cmd1.c revision 1.26 1 /* $NetBSD: cmd1.c,v 1.26 2006/10/21 21:37:20 christos 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.26 2006/10/21 21:37:20 christos Exp $");
38 #endif
39 #endif /* not lint */
40
41 #include "rcv.h"
42 #include "extern.h"
43 #ifdef MIME_SUPPORT
44 #include "mime.h"
45 #endif
46
47 /*
48 * Mail -- a mail program
49 *
50 * User commands.
51 */
52
53 /*
54 * Print the current active headings.
55 * Don't change dot if invoker didn't give an argument.
56 */
57
58 static int screen;
59
60 int
61 headers(void *v)
62 {
63 int *msgvec = v;
64 int n, mesg, flag;
65 struct message *mp;
66 int size;
67
68 size = screensize();
69 n = msgvec[0];
70 if (n != 0)
71 screen = (n-1)/size;
72 if (screen < 0)
73 screen = 0;
74 mp = &message[screen * size];
75 if (mp >= &message[msgCount])
76 mp = &message[msgCount - size];
77 if (mp < &message[0])
78 mp = &message[0];
79 flag = 0;
80 mesg = mp - &message[0];
81 if (dot != &message[n - 1])
82 dot = mp;
83 for (; mp < &message[msgCount]; mp++) {
84 mesg++;
85 if (mp->m_flag & MDELETED)
86 continue;
87 if (flag++ >= size)
88 break;
89 printhead(mesg);
90 }
91 if (flag == 0) {
92 (void)printf("No more mail.\n");
93 return(1);
94 }
95 return(0);
96 }
97
98 /*
99 * Scroll to the next/previous screen
100 */
101 int
102 scroll(void *v)
103 {
104 char *arg = v;
105 int s, size;
106 int cur[1];
107
108 cur[0] = 0;
109 size = screensize();
110 s = screen;
111 switch (*arg) {
112 case 0:
113 case '+':
114 s++;
115 if (s * size >= msgCount) {
116 (void)printf("On last screenful of messages\n");
117 return(0);
118 }
119 screen = s;
120 break;
121
122 case '-':
123 if (--s < 0) {
124 (void)printf("On first screenful of messages\n");
125 return(0);
126 }
127 screen = s;
128 break;
129
130 default:
131 (void)printf("Unrecognized scrolling command \"%s\"\n", arg);
132 return(1);
133 }
134 return(headers(cur));
135 }
136
137 /*
138 * Compute screen size.
139 */
140 int
141 screensize(void)
142 {
143 int s;
144 char *cp;
145
146 if ((cp = value("screen")) != NULL && (s = atoi(cp)) > 0)
147 return s;
148 return screenheight - 4;
149 }
150
151 /*
152 * Print out the headlines for each message
153 * in the passed message list.
154 */
155 int
156 from(void *v)
157 {
158 int *msgvec = v;
159 int *ip;
160
161 for (ip = msgvec; *ip != 0; ip++)
162 printhead(*ip);
163 if (--ip >= msgvec)
164 dot = &message[*ip - 1];
165 return(0);
166 }
167
168 /*
169 * Print out the header of a specific message.
170 * This is a slight improvement to the standard one.
171 */
172 void
173 printhead(int mesg)
174 {
175 struct message *mp;
176 char headline[LINESIZE], wcount[LINESIZE], *subjline, dispc, curind;
177 char pbuf[BUFSIZ];
178 struct headline hl;
179 int subjlen;
180 char *name;
181
182 mp = &message[mesg - 1];
183 (void)mail_readline(setinput(mp), headline, LINESIZE);
184 if ((subjline = hfield("subject", mp)) == NULL)
185 subjline = hfield("subj", mp);
186
187 /*
188 * Bletch!
189 */
190 curind = dot == mp ? '>' : ' ';
191 dispc = ' ';
192 if (mp->m_flag & MSAVED)
193 dispc = '*';
194 if (mp->m_flag & MPRESERVE)
195 dispc = 'P';
196 if ((mp->m_flag & (MREAD|MNEW)) == MNEW)
197 dispc = 'N';
198 if ((mp->m_flag & (MREAD|MNEW)) == 0)
199 dispc = 'U';
200 if (mp->m_flag & MBOX)
201 dispc = 'M';
202 parse(headline, &hl, pbuf);
203 (void)snprintf(wcount, LINESIZE, "%3ld/%-5llu", mp->m_blines,
204 (unsigned long long)mp->m_size);
205 subjlen = screenwidth - 50 - strlen(wcount);
206 name = value("show-rcpt") != NULL ?
207 skin(hfield("to", mp)) : nameof(mp, 0);
208 if (subjline == NULL || subjlen < 0) /* pretty pathetic */
209 (void)printf("%c%c%3d %-20.20s %16.16s %s\n",
210 curind, dispc, mesg, name, hl.l_date, wcount);
211 else
212 (void)printf("%c%c%3d %-20.20s %16.16s %s \"%.*s\"\n",
213 curind, dispc, mesg, name, hl.l_date, wcount,
214 subjlen, subjline);
215 }
216
217 /*
218 * Print out the value of dot.
219 */
220 int
221 /*ARGSUSED*/
222 pdot(void *v __unused)
223 {
224 (void)printf("%d\n", (int)(dot - &message[0] + 1));
225 return(0);
226 }
227
228 /*
229 * Print out all the possible commands.
230 */
231 int
232 /*ARGSUSED*/
233 pcmdlist(void *v __unused)
234 {
235 const struct cmd *cp;
236 int cc;
237
238 (void)printf("Commands are:\n");
239 for (cc = 0, cp = cmdtab; cp->c_name != NULL; cp++) {
240 cc += strlen(cp->c_name) + 2;
241 if (cc > 72) {
242 (void)printf("\n");
243 cc = strlen(cp->c_name) + 2;
244 }
245 if ((cp + 1)->c_name != NULL)
246 (void)printf("%s, ", cp->c_name);
247 else
248 (void)printf("%s\n", cp->c_name);
249 }
250 return(0);
251 }
252
253 #ifdef MIME_SUPPORT
254 static int
255 de_mime(const char *name)
256 {
257 const char *p;
258 const char *list;
259
260 #define DELIM " \t," /* list of string delimiters */
261
262 list = value(ENAME_MIME_DECODE_MSG);
263 if (list == NULL)
264 return 0;
265
266 if (list[0] == '\0')
267 return 1;
268
269 p = strcasestr(list, name);
270 if (p == NULL)
271 return 0;
272
273 return strchr(DELIM, p[strlen(name)]) && (
274 p == list || strchr(DELIM, p[-1]));
275
276 #undef DELIM
277 }
278 #endif /* MIME_SUPPORT */
279
280 /*
281 * Paginate messages, honor ignored fields.
282 */
283 int
284 more(void *v)
285 {
286 int *msgvec = v;
287 #ifdef MIME_SUPPORT
288 return type1(msgvec, 1, 1, de_mime("more"));
289 #else
290 return(type1(msgvec, 1, 1));
291 #endif
292
293 }
294
295 /*
296 * Paginate messages, even printing ignored fields.
297 */
298 int
299 More(void *v)
300 {
301 int *msgvec = v;
302
303 #ifdef MIME_SUPPORT
304 return type1(msgvec, 0, 1, de_mime("more"));
305 #else
306 return(type1(msgvec, 0, 1));
307 #endif
308 }
309
310 /*
311 * Type out messages, honor ignored fields.
312 */
313 int
314 type(void *v)
315 {
316 int *msgvec = v;
317
318 #ifdef MIME_SUPPORT
319 return type1(msgvec, 1, 0, de_mime("type"));
320 #else
321 return(type1(msgvec, 1, 0));
322 #endif
323 }
324
325 /*
326 * Type out messages, even printing ignored fields.
327 */
328 int
329 Type(void *v)
330 {
331 int *msgvec = v;
332
333 #ifdef MIME_SUPPORT
334 return type1(msgvec, 0, 0, de_mime("type"));
335 #else
336 return(type1(msgvec, 0, 0));
337 #endif
338 }
339
340
341 #ifdef MIME_SUPPORT
342 /*
343 * Paginate messages, honor ignored fields.
344 */
345 int
346 page(void *v)
347 {
348 int *msgvec = v;
349 #ifdef MIME_SUPPORT
350 return type1(msgvec, 1, 1, de_mime("page"));
351 #else
352 return(type1(msgvec, 1, 1));
353 #endif
354
355 }
356
357 /*
358 * Paginate messages, even printing ignored fields.
359 */
360 int
361 Page(void *v)
362 {
363 int *msgvec = v;
364
365 #ifdef MIME_SUPPORT
366 return type1(msgvec, 0, 1, de_mime("page"));
367 #else
368 return(type1(msgvec, 0, 1));
369 #endif
370 }
371
372 /*
373 * Type out messages, honor ignored fields.
374 */
375 int
376 print(void *v)
377 {
378 int *msgvec = v;
379
380 #ifdef MIME_SUPPORT
381 return type1(msgvec, 1, 0, de_mime("print"));
382 #else
383 return(type1(msgvec, 1, 0));
384 #endif
385 }
386
387 /*
388 * Type out messages, even printing ignored fields.
389 */
390 int
391 Print(void *v)
392 {
393 int *msgvec = v;
394
395 #ifdef MIME_SUPPORT
396 return type1(msgvec, 0, 0, de_mime("print"));
397 #else
398 return(type1(msgvec, 0, 0));
399 #endif
400 }
401
402 /*
403 * Identical to type(), but with opposite mime behavior.
404 */
405 int
406 view(void *v)
407 {
408 int *msgvec = v;
409 return type1(msgvec, 1, 0, !de_mime("print"));
410 }
411
412 /*
413 * Identical to Type(), but with opposite mime behavior.
414 */
415 int
416 View(void *v)
417 {
418 int *msgvec = v;
419
420 return type1(msgvec, 0, 0, !de_mime("print"));
421 }
422 #endif /* MIME_SUPPORT */
423
424 /*
425 * Type out the messages requested.
426 */
427 jmp_buf pipestop;
428 int
429 #ifdef MIME_SUPPORT
430 type1(int *msgvec, int doign, int dopage, int mime_decode)
431 #else
432 type1(int *msgvec, int doign, int dopage)
433 #endif
434 {
435 int *ip;
436 struct message *mp;
437 const char *cp;
438 int nlines;
439
440 /* Some volatile variables so longjmp will get the current not
441 * starting values. Note it is the variable that is volatile,
442 * not what it is pointing at! */
443 #ifdef MIME_SUPPORT
444 struct mime_info *volatile mip; /* avoid longjmp clobbering */
445 #endif
446 FILE *volatile obuf; /* avoid longjmp clobbering */
447
448 #ifdef MIME_SUPPORT
449 mip = NULL;
450 #endif
451 obuf = stdout;
452 if (setjmp(pipestop))
453 goto close_pipe;
454 if (value("interactive") != NULL &&
455 (dopage || (cp = value("crt")) != NULL)) {
456 nlines = 0;
457 if (!dopage) {
458 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++)
459 nlines += message[*ip - 1].m_blines;
460 }
461 if (dopage || nlines > (*cp ? atoi(cp) : realscreenheight)) {
462 cp = value("PAGER");
463 if (cp == NULL || *cp == '\0')
464 cp = _PATH_MORE;
465 obuf = Popen(cp, "w");
466 if (obuf == NULL) {
467 warn("%s", cp);
468 obuf = stdout;
469 } else
470 (void)signal(SIGPIPE, brokpipe);
471 }
472 }
473 for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) {
474 mp = &message[*ip - 1];
475 touch(mp);
476 dot = mp;
477 if (value("quiet") == NULL)
478 (void)fprintf(obuf, "Message %d:\n", *ip);
479 #ifdef MIME_SUPPORT
480 if (mime_decode)
481 mip = mime_decode_open(mp);
482 (void)mime_sendmessage(mp, obuf, doign ? ignore : 0, NULL, mip);
483 mime_decode_close(mip);
484 #else
485 (void)sendmessage(mp, obuf, doign ? ignore : 0, NULL);
486 #endif
487 }
488 close_pipe:
489 #ifdef MIME_SUPPORT
490 if (mip != NULL || obuf != stdout) {
491 /*
492 * Ignore SIGPIPE so it can't cause a duplicate close.
493 */
494 (void)signal(SIGPIPE, SIG_IGN);
495 mime_decode_close(mip);
496 if (obuf != stdout)
497 (void)Pclose(obuf);
498 (void)signal(SIGPIPE, SIG_DFL);
499
500 }
501 #else
502 if (obuf != stdout) {
503 /*
504 * Ignore SIGPIPE so it can't cause a duplicate close.
505 */
506 (void)signal(SIGPIPE, SIG_IGN);
507 (void)Pclose(obuf);
508 (void)signal(SIGPIPE, SIG_DFL);
509 }
510 #endif
511 return(0);
512 }
513
514 /*
515 * Respond to a broken pipe signal --
516 * probably caused by quitting more.
517 */
518 void
519 /*ARGSUSED*/
520 brokpipe(int signo __unused)
521 {
522 longjmp(pipestop, 1);
523 }
524
525 /*
526 * Pipe the current message buffer to a command.
527 */
528 int
529 pipecmd(void *v)
530 {
531 char *cmd = v;
532 FILE *volatile obuf; /* void longjmp clobbering - we want
533 the current value not start value */
534 if (dot == NULL) {
535 warn("pipcmd: no current message");
536 return 1;
537 }
538
539 obuf = stdout;
540 if (setjmp(pipestop))
541 goto close_pipe;
542
543 obuf = Popen(cmd, "w");
544 if (obuf == NULL) {
545 warn("pipecmd: Popen failed: %s", cmd);
546 return 1;
547 } else
548 (void)signal(SIGPIPE, brokpipe);
549
550 #ifdef MIME_SUPPORT
551 (void)sendmessage(dot, obuf, ignoreall, NULL, NULL);
552 #else
553 (void)sendmessage(dot, obuf, ignoreall, NULL);
554 #endif
555
556 close_pipe:
557 if (obuf != stdout) {
558 /*
559 * Ignore SIGPIPE so it can't cause a duplicate close.
560 */
561 (void)signal(SIGPIPE, SIG_IGN);
562 (void)Pclose(obuf);
563 (void)signal(SIGPIPE, SIG_DFL);
564 }
565 return 0;
566 }
567
568 /*
569 * Print the top so many lines of each desired message.
570 * The number of lines is taken from the variable "toplines"
571 * and defaults to 5.
572 */
573 int
574 top(void *v)
575 {
576 int *msgvec = v;
577 int *ip;
578 struct message *mp;
579 int c, topl, lines, lineb;
580 char *valtop, linebuf[LINESIZE];
581 FILE *ibuf;
582
583 topl = 5;
584 valtop = value("toplines");
585 if (valtop != NULL) {
586 topl = atoi(valtop);
587 if (topl < 0 || topl > 10000)
588 topl = 5;
589 }
590 lineb = 1;
591 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
592 mp = &message[*ip - 1];
593 touch(mp);
594 dot = mp;
595 if (value("quiet") == NULL)
596 (void)printf("Message %d:\n", *ip);
597 ibuf = setinput(mp);
598 c = mp->m_lines;
599 if (!lineb)
600 (void)printf("\n");
601 for (lines = 0; lines < c && lines <= topl; lines++) {
602 if (mail_readline(ibuf, linebuf, LINESIZE) < 0)
603 break;
604 (void)puts(linebuf);
605 lineb = blankline(linebuf);
606 }
607 }
608 return(0);
609 }
610
611 /*
612 * Touch all the given messages so that they will
613 * get mboxed.
614 */
615 int
616 stouch(void *v)
617 {
618 int *msgvec = v;
619 int *ip;
620
621 for (ip = msgvec; *ip != 0; ip++) {
622 dot = &message[*ip - 1];
623 dot->m_flag |= MTOUCH;
624 dot->m_flag &= ~MPRESERVE;
625 }
626 return(0);
627 }
628
629 /*
630 * Make sure all passed messages get mboxed.
631 */
632 int
633 mboxit(void *v)
634 {
635 int *msgvec = v;
636 int *ip;
637
638 for (ip = msgvec; *ip != 0; ip++) {
639 dot = &message[*ip - 1];
640 dot->m_flag |= MTOUCH|MBOX;
641 dot->m_flag &= ~MPRESERVE;
642 }
643 return(0);
644 }
645
646 /*
647 * List the folders the user currently has.
648 */
649 int
650 /*ARGSUSED*/
651 folders(void *v __unused)
652 {
653 char dirname[PATHSIZE];
654 const char *cmd;
655
656 if (getfold(dirname) < 0) {
657 (void)printf("No value set for \"folder\"\n");
658 return 1;
659 }
660 if ((cmd = value("LISTER")) == NULL)
661 cmd = "ls";
662 (void)run_command(cmd, 0, -1, -1, dirname, NULL);
663 return 0;
664 }
665
666 /*
667 * Update the mail file with any new messages that have
668 * come in since we started reading mail.
669 */
670 int
671 /*ARGSUSED*/
672 inc(void *v __unused)
673 {
674 int nmsg, mdot;
675
676 nmsg = incfile();
677
678 if (nmsg == 0) {
679 (void)printf("No new mail.\n");
680 } else if (nmsg > 0) {
681 mdot = newfileinfo(msgCount - nmsg);
682 dot = &message[mdot - 1];
683 } else {
684 (void)printf("\"inc\" command failed...\n");
685 }
686
687 return 0;
688 }
689