cmd1.c revision 1.21 1 /* $NetBSD: cmd1.c,v 1.21 2003/08/07 11:14:35 agc Exp $ */
2
3 /*-
4 * Copyright (c) 1980, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)cmd1.c 8.2 (Berkeley) 4/20/95";
36 #else
37 __RCSID("$NetBSD: cmd1.c,v 1.21 2003/08/07 11:14:35 agc Exp $");
38 #endif
39 #endif /* not lint */
40
41 #include "rcv.h"
42 #include "extern.h"
43
44 /*
45 * Mail -- a mail program
46 *
47 * User commands.
48 */
49 extern const struct cmd cmdtab[];
50
51 /*
52 * Print the current active headings.
53 * Don't change dot if invoker didn't give an argument.
54 */
55
56 static int screen;
57
58 int
59 headers(void *v)
60 {
61 int *msgvec = v;
62 int n, mesg, flag;
63 struct message *mp;
64 int size;
65
66 size = screensize();
67 n = msgvec[0];
68 if (n != 0)
69 screen = (n-1)/size;
70 if (screen < 0)
71 screen = 0;
72 mp = &message[screen * size];
73 if (mp >= &message[msgCount])
74 mp = &message[msgCount - size];
75 if (mp < &message[0])
76 mp = &message[0];
77 flag = 0;
78 mesg = mp - &message[0];
79 if (dot != &message[n-1])
80 dot = mp;
81 for (; mp < &message[msgCount]; mp++) {
82 mesg++;
83 if (mp->m_flag & MDELETED)
84 continue;
85 if (flag++ >= size)
86 break;
87 printhead(mesg);
88 }
89 if (flag == 0) {
90 printf("No more mail.\n");
91 return(1);
92 }
93 return(0);
94 }
95
96 /*
97 * Scroll to the next/previous screen
98 */
99 int
100 scroll(void *v)
101 {
102 char *arg = v;
103 int s, size;
104 int cur[1];
105
106 cur[0] = 0;
107 size = screensize();
108 s = screen;
109 switch (*arg) {
110 case 0:
111 case '+':
112 s++;
113 if (s * size >= msgCount) {
114 printf("On last screenful of messages\n");
115 return(0);
116 }
117 screen = s;
118 break;
119
120 case '-':
121 if (--s < 0) {
122 printf("On first screenful of messages\n");
123 return(0);
124 }
125 screen = s;
126 break;
127
128 default:
129 printf("Unrecognized scrolling command \"%s\"\n", arg);
130 return(1);
131 }
132 return(headers(cur));
133 }
134
135 /*
136 * Compute screen size.
137 */
138 int
139 screensize(void)
140 {
141 int s;
142 char *cp;
143
144 if ((cp = value("screen")) != NULL && (s = atoi(cp)) > 0)
145 return s;
146 return screenheight - 4;
147 }
148
149 /*
150 * Print out the headlines for each message
151 * in the passed message list.
152 */
153 int
154 from(void *v)
155 {
156 int *msgvec = v;
157 int *ip;
158
159 for (ip = msgvec; *ip != 0; ip++)
160 printhead(*ip);
161 if (--ip >= msgvec)
162 dot = &message[*ip - 1];
163 return(0);
164 }
165
166 /*
167 * Print out the header of a specific message.
168 * This is a slight improvement to the standard one.
169 */
170 void
171 printhead(int mesg)
172 {
173 struct message *mp;
174 char headline[LINESIZE], wcount[LINESIZE], *subjline, dispc, curind;
175 char pbuf[BUFSIZ];
176 struct headline hl;
177 int subjlen;
178 char *name;
179
180 mp = &message[mesg-1];
181 (void)readline(setinput(mp), headline, LINESIZE);
182 if ((subjline = hfield("subject", mp)) == NULL)
183 subjline = hfield("subj", mp);
184 /*
185 * Bletch!
186 */
187 curind = dot == mp ? '>' : ' ';
188 dispc = ' ';
189 if (mp->m_flag & MSAVED)
190 dispc = '*';
191 if (mp->m_flag & MPRESERVE)
192 dispc = 'P';
193 if ((mp->m_flag & (MREAD|MNEW)) == MNEW)
194 dispc = 'N';
195 if ((mp->m_flag & (MREAD|MNEW)) == 0)
196 dispc = 'U';
197 if (mp->m_flag & MBOX)
198 dispc = 'M';
199 parse(headline, &hl, pbuf);
200 snprintf(wcount, LINESIZE, "%3ld/%-5ld", mp->m_blines, mp->m_size);
201 subjlen = screenwidth - 50 - strlen(wcount);
202 name = value("show-rcpt") != NULL ?
203 skin(hfield("to", mp)) : nameof(mp, 0);
204 if (subjline == NULL || subjlen < 0) /* pretty pathetic */
205 printf("%c%c%3d %-20.20s %16.16s %s\n",
206 curind, dispc, mesg, name, hl.l_date, wcount);
207 else
208 printf("%c%c%3d %-20.20s %16.16s %s \"%.*s\"\n",
209 curind, dispc, mesg, name, hl.l_date, wcount,
210 subjlen, subjline);
211 }
212
213 /*
214 * Print out the value of dot.
215 */
216 int
217 pdot(void *v)
218 {
219 printf("%d\n", (int)(dot - &message[0] + 1));
220 return(0);
221 }
222
223 /*
224 * Print out all the possible commands.
225 */
226 int
227 pcmdlist(void *v)
228 {
229 const struct cmd *cp;
230 int cc;
231
232 printf("Commands are:\n");
233 for (cc = 0, cp = cmdtab; cp->c_name != NULL; cp++) {
234 cc += strlen(cp->c_name) + 2;
235 if (cc > 72) {
236 printf("\n");
237 cc = strlen(cp->c_name) + 2;
238 }
239 if ((cp+1)->c_name != NULL)
240 printf("%s, ", cp->c_name);
241 else
242 printf("%s\n", cp->c_name);
243 }
244 return(0);
245 }
246
247 /*
248 * Paginate messages, honor ignored fields.
249 */
250 int
251 more(void *v)
252 {
253 int *msgvec = v;
254 return (type1(msgvec, 1, 1));
255 }
256
257 /*
258 * Paginate messages, even printing ignored fields.
259 */
260 int
261 More(void *v)
262 {
263 int *msgvec = v;
264
265 return (type1(msgvec, 0, 1));
266 }
267
268 /*
269 * Type out messages, honor ignored fields.
270 */
271 int
272 type(void *v)
273 {
274 int *msgvec = v;
275
276 return(type1(msgvec, 1, 0));
277 }
278
279 /*
280 * Type out messages, even printing ignored fields.
281 */
282 int
283 Type(void *v)
284 {
285 int *msgvec = v;
286
287 return(type1(msgvec, 0, 0));
288 }
289
290 /*
291 * Type out the messages requested.
292 */
293 jmp_buf pipestop;
294 int
295 type1(int *msgvec, int doign, int page)
296 {
297 int *ip;
298 struct message *mp;
299 char *cp;
300 int nlines;
301 FILE *obuf;
302 #if __GNUC__
303 /* Avoid longjmp clobbering */
304 (void)&cp;
305 (void)&obuf;
306 #endif
307
308 obuf = stdout;
309 if (setjmp(pipestop))
310 goto close_pipe;
311 if (value("interactive") != NULL &&
312 (page || (cp = value("crt")) != NULL)) {
313 nlines = 0;
314 if (!page) {
315 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++)
316 nlines += message[*ip - 1].m_blines;
317 }
318 if (page || nlines > (*cp ? atoi(cp) : realscreenheight)) {
319 cp = value("PAGER");
320 if (cp == NULL || *cp == '\0')
321 cp = _PATH_MORE;
322 obuf = Popen(cp, "w");
323 if (obuf == NULL) {
324 warn("%s", cp);
325 obuf = stdout;
326 } else
327 signal(SIGPIPE, brokpipe);
328 }
329 }
330 for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) {
331 mp = &message[*ip - 1];
332 touch(mp);
333 dot = mp;
334 if (value("quiet") == NULL)
335 fprintf(obuf, "Message %d:\n", *ip);
336 (void)sendmessage(mp, obuf, doign ? ignore : 0, NULL);
337 }
338 close_pipe:
339 if (obuf != stdout) {
340 /*
341 * Ignore SIGPIPE so it can't cause a duplicate close.
342 */
343 signal(SIGPIPE, SIG_IGN);
344 Pclose(obuf);
345 signal(SIGPIPE, SIG_DFL);
346 }
347 return(0);
348 }
349
350 /*
351 * Respond to a broken pipe signal --
352 * probably caused by quitting more.
353 */
354 void
355 brokpipe(int signo)
356 {
357 longjmp(pipestop, 1);
358 }
359
360 /*
361 * Print the top so many lines of each desired message.
362 * The number of lines is taken from the variable "toplines"
363 * and defaults to 5.
364 */
365 int
366 top(void *v)
367 {
368 int *msgvec = v;
369 int *ip;
370 struct message *mp;
371 int c, topl, lines, lineb;
372 char *valtop, linebuf[LINESIZE];
373 FILE *ibuf;
374
375 topl = 5;
376 valtop = value("toplines");
377 if (valtop != NULL) {
378 topl = atoi(valtop);
379 if (topl < 0 || topl > 10000)
380 topl = 5;
381 }
382 lineb = 1;
383 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
384 mp = &message[*ip - 1];
385 touch(mp);
386 dot = mp;
387 if (value("quiet") == NULL)
388 printf("Message %d:\n", *ip);
389 ibuf = setinput(mp);
390 c = mp->m_lines;
391 if (!lineb)
392 printf("\n");
393 for (lines = 0; lines < c && lines <= topl; lines++) {
394 if (readline(ibuf, linebuf, LINESIZE) < 0)
395 break;
396 puts(linebuf);
397 lineb = blankline(linebuf);
398 }
399 }
400 return(0);
401 }
402
403 /*
404 * Touch all the given messages so that they will
405 * get mboxed.
406 */
407 int
408 stouch(void *v)
409 {
410 int *msgvec = v;
411 int *ip;
412
413 for (ip = msgvec; *ip != 0; ip++) {
414 dot = &message[*ip-1];
415 dot->m_flag |= MTOUCH;
416 dot->m_flag &= ~MPRESERVE;
417 }
418 return(0);
419 }
420
421 /*
422 * Make sure all passed messages get mboxed.
423 */
424 int
425 mboxit(void *v)
426 {
427 int *msgvec = v;
428 int *ip;
429
430 for (ip = msgvec; *ip != 0; ip++) {
431 dot = &message[*ip-1];
432 dot->m_flag |= MTOUCH|MBOX;
433 dot->m_flag &= ~MPRESERVE;
434 }
435 return(0);
436 }
437
438 /*
439 * List the folders the user currently has.
440 */
441 int
442 folders(void *v)
443 {
444 char dirname[PATHSIZE];
445 char *cmd;
446
447 if (getfold(dirname) < 0) {
448 printf("No value set for \"folder\"\n");
449 return 1;
450 }
451 if ((cmd = value("LISTER")) == NULL)
452 cmd = "ls";
453 (void)run_command(cmd, 0, -1, -1, dirname, NULL);
454 return 0;
455 }
456
457 /*
458 * Update the mail file with any new messages that have
459 * come in since we started reading mail.
460 */
461 int
462 inc(void *v)
463 {
464 int nmsg, mdot;
465
466 nmsg = incfile();
467
468 if (nmsg == 0) {
469 printf("No new mail.\n");
470 } else if (nmsg > 0) {
471 mdot = newfileinfo(msgCount - nmsg);
472 dot = &message[mdot - 1];
473 } else {
474 printf("\"inc\" command failed...\n");
475 }
476
477 return 0;
478 }
479