Add -O option to pass options through to MTA..
[s-mailx.git] / cmd1.c
blob7999c3d10b579467f3a6b110746173ad9017e334
1 /*
2 * Heirloom mailx - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 */
6 /*-
7 * Copyright (c) 1980, 1993
8 * The Regents of the University of California. All rights reserved.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
39 #ifndef lint
40 #ifdef DOSCCS
41 static char sccsid[] = "@(#)cmd1.c 2.97 (gritter) 6/16/07";
42 #endif
43 #endif /* not lint */
45 #include "rcv.h"
46 #include "extern.h"
47 #ifdef HAVE_WCWIDTH
48 #include <wchar.h>
49 #endif
52 * Mail -- a mail program
54 * User commands.
58 * Print the current active headings.
59 * Don't change dot if invoker didn't give an argument.
62 static int screen;
63 static void onpipe(int signo);
64 static int dispc(struct message *mp, const char *a);
65 static int scroll1(char *arg, int onlynew);
66 static void hprf(const char *fmt, int mesg, FILE *f, int threaded,
67 const char *attrlist);
68 static int putindent(FILE *fp, struct message *mp, int maxwidth);
69 static int type1(int *msgvec, int doign, int page, int pipe, int decode,
70 char *cmd, off_t *tstats);
71 static int pipe1(char *str, int doign);
72 void brokpipe(int signo);
74 char *
75 get_pager(void)
77 char *cp;
79 cp = value("PAGER");
80 if (cp == NULL || *cp == '\0')
81 cp = value("bsdcompat") ? "more" : "pg";
82 return cp;
85 int
86 headers(void *v)
88 int *msgvec = v;
89 int g, k, n, mesg, flag = 0, lastg = 1;
90 struct message *mp, *mq, *lastmq = NULL;
91 int size;
92 enum mflag fl = MNEW|MFLAGGED;
94 size = screensize();
95 n = msgvec[0]; /* n == {-2, -1, 0}: called from scroll() */
96 if (screen < 0)
97 screen = 0;
98 k = screen * size;
99 if (k >= msgCount)
100 k = msgCount - size;
101 if (k < 0)
102 k = 0;
103 if (mb.mb_threaded == 0) {
104 g = 0;
105 mq = &message[0];
106 for (mp = &message[0]; mp < &message[msgCount]; mp++)
107 if (visible(mp)) {
108 if (g % size == 0)
109 mq = mp;
110 if (mp->m_flag&fl) {
111 lastg = g;
112 lastmq = mq;
114 if (n>0 && mp==&message[n-1] ||
115 n==0 && g==k ||
116 n==-2 && g==k+size && lastmq ||
117 n<0 && g>=k && mp->m_flag&fl)
118 break;
119 g++;
121 if (lastmq && (n==-2 || n==-1 && mp==&message[msgCount])) {
122 g = lastg;
123 mq = lastmq;
125 screen = g / size;
126 mp = mq;
127 mesg = mp - &message[0];
128 if (dot != &message[n-1]) {
129 for (mq = mp; mq < &message[msgCount]; mq++)
130 if (visible(mq)) {
131 setdot(mq);
132 break;
135 if (mb.mb_type == MB_IMAP)
136 imap_getheaders(mesg+1, mesg + size);
137 for (; mp < &message[msgCount]; mp++) {
138 mesg++;
139 if (!visible(mp))
140 continue;
141 if (flag++ >= size)
142 break;
143 printhead(mesg, stdout, 0);
145 } else { /* threaded */
146 g = 0;
147 mq = threadroot;
148 for (mp = threadroot; mp; mp = next_in_thread(mp))
149 if (visible(mp) && (mp->m_collapsed <= 0 ||
150 mp == &message[n-1])) {
151 if (g % size == 0)
152 mq = mp;
153 if (mp->m_flag&fl) {
154 lastg = g;
155 lastmq = mq;
157 if (n>0 && mp==&message[n-1] ||
158 n==0 && g==k ||
159 n==-2 && g==k+size && lastmq ||
160 n<0 && g>=k && mp->m_flag&fl)
161 break;
162 g++;
164 if (lastmq && (n==-2 || n==-1 && mp==&message[msgCount])) {
165 g = lastg;
166 mq = lastmq;
168 screen = g / size;
169 mp = mq;
170 if (dot != &message[n-1]) {
171 for (mq = mp; mq; mq = next_in_thread(mq))
172 if (visible(mq) && mq->m_collapsed <= 0) {
173 setdot(mq);
174 break;
177 while (mp) {
178 if (visible(mp) && (mp->m_collapsed <= 0 ||
179 mp == &message[n-1])) {
180 if (flag++ >= size)
181 break;
182 printhead(mp - &message[0] + 1, stdout,
183 mb.mb_threaded);
185 mp = next_in_thread(mp);
188 if (flag == 0) {
189 printf(catgets(catd, CATSET, 6, "No more mail.\n"));
190 return(1);
192 return(0);
196 * Scroll to the next/previous screen
199 scroll(void *v)
201 return scroll1(v, 0);
205 Scroll(void *v)
207 return scroll1(v, 1);
210 static int
211 scroll1(char *arg, int onlynew)
213 int size;
214 int cur[1];
216 cur[0] = onlynew ? -1 : 0;
217 size = screensize();
218 switch (*arg) {
219 case '1': case '2': case '3': case '4': case '5':
220 case '6': case '7': case '8': case '9': case '0':
221 screen = atoi(arg);
222 goto scroll_forward;
223 case '\0':
224 screen++;
225 goto scroll_forward;
226 case '$':
227 screen = msgCount / size;
228 goto scroll_forward;
229 case '+':
230 if (arg[1] == '\0')
231 screen++;
232 else
233 screen += atoi(arg + 1);
234 scroll_forward:
235 if (screen * size > msgCount) {
236 screen = msgCount / size;
237 printf(catgets(catd, CATSET, 7,
238 "On last screenful of messages\n"));
240 break;
242 case '-':
243 if (arg[1] == '\0')
244 screen--;
245 else
246 screen -= atoi(arg + 1);
247 if (screen < 0) {
248 screen = 0;
249 printf(catgets(catd, CATSET, 8,
250 "On first screenful of messages\n"));
252 if (cur[0] == -1)
253 cur[0] = -2;
254 break;
256 default:
257 printf(catgets(catd, CATSET, 9,
258 "Unrecognized scrolling command \"%s\"\n"), arg);
259 return(1);
261 return(headers(cur));
265 * Compute screen size.
267 int
268 screensize(void)
270 int s;
271 char *cp;
273 if ((cp = value("screen")) != NULL && (s = atoi(cp)) > 0)
274 return s;
275 return scrnheight - 4;
278 static sigjmp_buf pipejmp;
280 /*ARGSUSED*/
281 static void
282 onpipe(int signo)
284 siglongjmp(pipejmp, 1);
288 * Print out the headlines for each message
289 * in the passed message list.
291 int
292 from(void *v)
294 int *msgvec = v;
295 int *ip, n;
296 FILE *obuf = stdout;
297 char *cp;
299 (void)&obuf;
300 (void)&cp;
301 if (is_a_tty[0] && is_a_tty[1] && (cp = value("crt")) != NULL) {
302 for (n = 0, ip = msgvec; *ip; ip++)
303 n++;
304 if (n > (*cp == '\0' ? screensize() : atoi(cp)) + 3) {
305 cp = get_pager();
306 if (sigsetjmp(pipejmp, 1))
307 goto endpipe;
308 if ((obuf = Popen(cp, "w", NULL, 1)) == NULL) {
309 perror(cp);
310 obuf = stdout;
311 } else
312 safe_signal(SIGPIPE, onpipe);
315 for (ip = msgvec; *ip != 0; ip++)
316 printhead(*ip, obuf, mb.mb_threaded);
317 if (--ip >= msgvec)
318 setdot(&message[*ip - 1]);
319 endpipe:
320 if (obuf != stdout) {
321 safe_signal(SIGPIPE, SIG_IGN);
322 Pclose(obuf);
323 safe_signal(SIGPIPE, dflpipe);
325 return(0);
328 static int
329 dispc(struct message *mp, const char *a)
331 int dispc = ' ';
334 * Bletch!
336 if ((mp->m_flag & (MREAD|MNEW)) == MREAD)
337 dispc = a[3];
338 if ((mp->m_flag & (MREAD|MNEW)) == (MREAD|MNEW))
339 dispc = a[2];
340 if (mp->m_flag & MANSWERED)
341 dispc = a[8];
342 if (mp->m_flag & MDRAFTED)
343 dispc = a[9];
344 if ((mp->m_flag & (MREAD|MNEW)) == MNEW)
345 dispc = a[0];
346 if ((mp->m_flag & (MREAD|MNEW)) == 0)
347 dispc = a[1];
348 if (mp->m_flag & MJUNK)
349 dispc = a[13];
350 if (mp->m_flag & MSAVED)
351 dispc = a[4];
352 if (mp->m_flag & MPRESERVE)
353 dispc = a[5];
354 if (mp->m_flag & (MBOX|MBOXED))
355 dispc = a[6];
356 if (mp->m_flag & MFLAGGED)
357 dispc = a[7];
358 if (mp->m_flag & MKILL)
359 dispc = a[10];
360 if (mb.mb_threaded == 1 && mp->m_collapsed > 0)
361 dispc = a[12];
362 if (mb.mb_threaded == 1 && mp->m_collapsed < 0)
363 dispc = a[11];
364 return dispc;
367 static void
368 hprf(const char *fmt, int mesg, FILE *f, int threaded, const char *attrlist)
370 struct message *mp = &message[mesg-1];
371 char *headline = NULL, *subjline, *name, *cp, *pbuf = NULL;
372 struct headline hl;
373 size_t headsize = 0;
374 const char *fp;
375 int B, c, i, n, s;
376 int headlen = 0;
377 struct str in, out;
378 int subjlen = scrnwidth, fromlen, isto = 0, isaddr = 0;
379 FILE *ibuf;
381 if ((mp->m_flag & MNOFROM) == 0) {
382 if ((ibuf = setinput(&mb, mp, NEED_HEADER)) == NULL)
383 return;
384 if ((headlen = readline(ibuf, &headline, &headsize)) < 0)
385 return;
387 if ((subjline = hfield("subject", mp)) == NULL)
388 subjline = hfield("subj", mp);
389 if (subjline == NULL) {
390 out.s = NULL;
391 out.l = 0;
392 } else {
393 in.s = subjline;
394 in.l = strlen(subjline);
395 mime_fromhdr(&in, &out, TD_ICONV | TD_ISPR);
396 subjline = out.s;
398 if ((mp->m_flag & MNOFROM) == 0) {
399 pbuf = ac_alloc(headlen + 1);
400 parse(headline, headlen, &hl, pbuf);
401 } else {
402 hl.l_from = /*fakefrom(mp);*/NULL;
403 hl.l_tty = NULL;
404 hl.l_date = fakedate(mp->m_time);
406 if (value("datefield") && (cp = hfield("date", mp)) != NULL)
407 hl.l_date = fakedate(rfctime(cp));
408 if (Iflag) {
409 if ((name = hfield("newsgroups", mp)) == NULL)
410 if ((name = hfield("article-id", mp)) == NULL)
411 name = "<>";
412 name = prstr(name);
413 } else if (value("show-rcpt") == NULL) {
414 name = name1(mp, 0);
415 isaddr = 1;
416 if (value("showto") && name && is_myname(skin(name))) {
417 if ((cp = hfield("to", mp)) != NULL) {
418 name = cp;
419 isto = 1;
422 } else {
423 isaddr = 1;
424 if ((name = hfield("to", mp)) != NULL)
425 isto = 1;
427 if (name == NULL) {
428 name = "";
429 isaddr = 0;
431 if (isaddr) {
432 if (value("showname"))
433 name = realname(name);
434 else {
435 name = prstr(skin(name));
438 for (fp = fmt; *fp; fp++) {
439 if (*fp == '%') {
440 if (*++fp == '-') {
441 fp++;
442 } else if (*fp == '+')
443 fp++;
444 while (digitchar(*fp&0377))
445 fp++;
446 if (*fp == '\0')
447 break;
448 } else {
449 #if defined (HAVE_MBTOWC) && defined (HAVE_WCWIDTH)
450 if (mb_cur_max > 1) {
451 wchar_t wc;
452 if ((s = mbtowc(&wc, fp, mb_cur_max)) < 0)
453 n = s = 1;
454 else {
455 if ((n = wcwidth(wc)) < 0)
456 n = 1;
458 } else
459 #endif /* HAVE_MBTOWC && HAVE_WCWIDTH */
461 n = s = 1;
463 subjlen -= n;
464 while (--s > 0)
465 fp++;
468 for (fp = fmt; *fp; fp++) {
469 if (*fp == '%') {
470 B = 0;
471 n = 0;
472 s = 1;
473 if (*++fp == '-') {
474 s = -1;
475 fp++;
476 } else if (*fp == '+')
477 fp++;
478 if (digitchar(*fp&0377)) {
480 n = 10*n + *fp - '0';
481 while (fp++, digitchar(*fp&0377));
483 if (*fp == '\0')
484 break;
485 n *= s;
486 switch (*fp) {
487 case '%':
488 putc('%', f);
489 subjlen--;
490 break;
491 case '>':
492 case '<':
493 c = dot == mp ? *fp&0377 : ' ';
494 putc(c, f);
495 subjlen--;
496 break;
497 case 'a':
498 c = dispc(mp, attrlist);
499 putc(c, f);
500 subjlen--;
501 break;
502 case 'm':
503 if (n == 0) {
504 n = 3;
505 if (threaded)
506 for (i=msgCount; i>999; i/=10)
507 n++;
509 subjlen -= fprintf(f, "%*d", n, mesg);
510 break;
511 case 'f':
512 if (n <= 0)
513 n = 18;
514 fromlen = n;
515 if (isto)
516 fromlen -= 3;
517 fprintf(f, "%s%s", isto ? "To " : "",
518 colalign(name, fromlen, 1));
519 subjlen -= n;
520 break;
521 case 'd':
522 if (n <= 0)
523 n = 16;
524 subjlen -= fprintf(f, "%*.*s", n, n, hl.l_date);
525 break;
526 case 'l':
527 if (n == 0)
528 n = 4;
529 if (mp->m_xlines)
530 subjlen -= fprintf(f, "%*ld", n,
531 mp->m_xlines);
532 else {
533 subjlen -= n;
534 while (n--)
535 putc(' ', f);
537 break;
538 case 'o':
539 if (n == 0)
540 n = -5;
541 subjlen -= fprintf(f, "%*lu", n,
542 (long)mp->m_xsize);
543 break;
544 case 'i':
545 if (threaded)
546 subjlen -= putindent(f, mp,
547 scrnwidth - 60);
548 break;
549 case 'S':
550 B = 1;
551 /*FALLTHRU*/
552 case 's':
553 n = n>0 ? n : subjlen - 2;
554 if (B)
555 n -= 2;
556 if (subjline != NULL && n >= 0) {
557 /* pretty pathetic */
558 fprintf(f, B ? "\"%s\"" : "%s",
559 colalign(subjline, n, 0));
561 break;
562 case 'U':
563 if (n == 0)
564 n = 9;
565 subjlen -= fprintf(f, "%*lu", n, mp->m_uid);
566 break;
567 case 'e':
568 if (n == 0)
569 n = 2;
570 subjlen -= fprintf(f, "%*u", n, threaded == 1 ?
571 mp->m_level : 0);
572 break;
573 case 't':
574 if (n == 0) {
575 n = 3;
576 if (threaded)
577 for (i=msgCount; i>999; i/=10)
578 n++;
580 fprintf(f, "%*ld", n, threaded ?
581 mp->m_threadpos : mesg);
582 subjlen -= n;
583 break;
584 case 'c':
585 if (n == 0)
586 n = 6;
587 subjlen -= fprintf(f, "%*g", n, mp->m_score);
588 break;
590 } else
591 putc(*fp&0377, f);
593 putc('\n', f);
594 if (out.s)
595 free(out.s);
596 if (headline)
597 free(headline);
598 if (pbuf)
599 ac_free(pbuf);
603 * Print out the indenting in threaded display.
605 static int
606 putindent(FILE *fp, struct message *mp, int maxwidth)
608 struct message *mq;
609 int indent, i;
610 int *us;
611 char *cs;
612 int important = MNEW|MFLAGGED;
614 if (mp->m_level == 0)
615 return 0;
616 cs = ac_alloc(mp->m_level);
617 us = ac_alloc(mp->m_level * sizeof *us);
618 i = mp->m_level - 1;
619 if (mp->m_younger && mp->m_younger->m_level == i + 1) {
620 if (mp->m_parent && mp->m_parent->m_flag & important)
621 us[i] = mp->m_flag & important ? 0x2523 : 0x2520;
622 else
623 us[i] = mp->m_flag & important ? 0x251D : 0x251C;
624 cs[i] = '+';
625 } else {
626 if (mp->m_parent && mp->m_parent->m_flag & important)
627 us[i] = mp->m_flag & important ? 0x2517 : 0x2516;
628 else
629 us[i] = mp->m_flag & important ? 0x2515 : 0x2514;
630 cs[i] = '\\';
632 mq = mp->m_parent;
633 for (i = mp->m_level - 2; i >= 0; i--) {
634 if (mq) {
635 if (i > mq->m_level - 1) {
636 us[i] = cs[i] = ' ';
637 continue;
639 if (mq->m_younger) {
640 if (mq->m_parent &&
641 mq->m_parent->m_flag&important)
642 us[i] = 0x2503;
643 else
644 us[i] = 0x2502;
645 cs[i] = '|';
646 } else
647 us[i] = cs[i] = ' ';
648 mq = mq->m_parent;
649 } else
650 us[i] = cs[i] = ' ';
652 for (indent = 0; indent < mp->m_level && indent < maxwidth; indent++) {
653 if (indent < maxwidth - 1)
654 putuc(us[indent], cs[indent] & 0377, fp);
655 else
656 putuc(0x21B8, '^', fp);
658 ac_free(us);
659 ac_free(cs);
660 return indent;
664 * Print out the header of a specific message.
665 * This is a slight improvement to the standard one.
667 void
668 printhead(int mesg, FILE *f, int threaded)
670 int bsdflags, bsdheadline, sz;
671 char *fmt, attrlist[30], *cp;
673 bsdflags = value("bsdcompat") != NULL || value("bsdflags") != NULL ||
674 getenv("SYSV3") != NULL;
675 strcpy(attrlist, bsdflags ? "NU *HMFATK+-J" : "NUROSPMFATK+-J");
676 if ((cp = value("attrlist")) != NULL) {
677 sz = strlen(cp);
678 if (sz > sizeof attrlist - 1)
679 sz = sizeof attrlist - 1;
680 memcpy(attrlist, cp, sz);
682 bsdheadline = value("bsdcompat") != NULL ||
683 value("bsdheadline") != NULL;
684 if ((fmt = value("headline")) == NULL)
685 fmt = bsdheadline ?
686 "%>%a%m %20f %16d %3l/%-5o %i%S" :
687 "%>%a%m %18f %16d %4l/%-5o %i%s";
688 hprf(fmt, mesg, f, threaded, attrlist);
692 * Print out the value of dot.
694 /*ARGSUSED*/
695 int
696 pdot(void *v)
698 printf(catgets(catd, CATSET, 13, "%d\n"),
699 (int)(dot - &message[0] + 1));
700 return(0);
704 * Print out all the possible commands.
706 /*ARGSUSED*/
707 int
708 pcmdlist(void *v)
710 extern const struct cmd cmdtab[];
711 const struct cmd *cp;
712 int cc;
714 printf(catgets(catd, CATSET, 14, "Commands are:\n"));
715 for (cc = 0, cp = cmdtab; cp->c_name != NULL; cp++) {
716 cc += strlen(cp->c_name) + 2;
717 if (cc > 72) {
718 printf("\n");
719 cc = strlen(cp->c_name) + 2;
721 if ((cp+1)->c_name != NULL)
722 printf(catgets(catd, CATSET, 15, "%s, "), cp->c_name);
723 else
724 printf("%s\n", cp->c_name);
726 return(0);
730 * Type out the messages requested.
732 static sigjmp_buf pipestop;
734 static int
735 type1(int *msgvec, int doign, int page, int pipe, int decode,
736 char *cmd, off_t *tstats)
738 int *ip;
739 struct message *mp;
740 char *cp;
741 int nlines;
742 off_t mstats[2];
744 * Must be static to become excluded from sigsetjmp().
746 static FILE *obuf;
747 #ifdef __GNUC__
748 /* Avoid longjmp clobbering */
749 (void) &cp;
750 (void) &cmd;
751 (void) &obuf;
752 #endif
754 obuf = stdout;
755 if (sigsetjmp(pipestop, 1))
756 goto close_pipe;
757 if (pipe) {
758 cp = value("SHELL");
759 if (cp == NULL)
760 cp = SHELL;
761 obuf = Popen(cmd, "w", cp, 1);
762 if (obuf == NULL) {
763 perror(cmd);
764 obuf = stdout;
765 } else {
766 safe_signal(SIGPIPE, brokpipe);
768 } else if (value("interactive") != NULL &&
769 (page || (cp = value("crt")) != NULL)) {
770 nlines = 0;
771 if (!page) {
772 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
773 if ((message[*ip-1].m_have & HAVE_BODY) == 0) {
774 if ((get_body(&message[*ip - 1])) !=
775 OKAY)
776 return 1;
778 nlines += message[*ip - 1].m_lines;
781 if (page || nlines > (*cp ? atoi(cp) : realscreenheight)) {
782 cp = get_pager();
783 obuf = Popen(cp, "w", NULL, 1);
784 if (obuf == NULL) {
785 perror(cp);
786 obuf = stdout;
787 } else
788 safe_signal(SIGPIPE, brokpipe);
791 for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) {
792 mp = &message[*ip - 1];
793 touch(mp);
794 setdot(mp);
795 uncollapse1(mp, 1);
796 if (value("quiet") == NULL)
797 fprintf(obuf, catgets(catd, CATSET, 17,
798 "Message %2d:\n"), *ip);
799 send(mp, obuf, doign ? ignore : 0, NULL,
800 pipe && value("piperaw") ? SEND_MBOX :
801 decode ? SEND_SHOW :
802 doign ? SEND_TODISP : SEND_TODISP_ALL,
803 mstats);
804 if (pipe && value("page")) {
805 putc('\f', obuf);
807 if (tstats) {
808 tstats[0] += mstats[0];
809 tstats[1] += mstats[1];
812 close_pipe:
813 if (obuf != stdout) {
815 * Ignore SIGPIPE so it can't cause a duplicate close.
817 safe_signal(SIGPIPE, SIG_IGN);
818 Pclose(obuf);
819 safe_signal(SIGPIPE, dflpipe);
821 return(0);
825 * Get the last, possibly quoted part of linebuf.
827 char *
828 laststring(char *linebuf, int *flag, int strip)
830 char *cp, *p;
831 char quoted;
833 *flag = 1;
834 cp = strlen(linebuf) + linebuf - 1;
837 * Strip away trailing blanks.
839 while (cp > linebuf && whitechar(*cp & 0377))
840 cp--;
841 *++cp = 0;
842 if (cp == linebuf) {
843 *flag = 0;
844 return NULL;
848 * Now search for the beginning of the command name.
850 quoted = *(cp - 1);
851 if (quoted == '\'' || quoted == '\"') {
852 cp--;
853 if (strip)
854 *cp = '\0';
855 cp--;
856 while (cp > linebuf) {
857 if (*cp != quoted) {
858 cp--;
859 } else if (*(cp - 1) != '\\') {
860 break;
861 } else {
862 p = --cp;
863 do {
864 *p = *(p + 1);
865 } while (*p++);
866 cp--;
869 if (cp == linebuf)
870 *flag = 0;
871 if (*cp == quoted) {
872 if (strip)
873 *cp++ = 0;
874 } else
875 *flag = 0;
876 } else {
877 while (cp > linebuf && !whitechar(*cp & 0377))
878 cp--;
879 if (whitechar(*cp & 0377))
880 *cp++ = 0;
881 else
882 *flag = 0;
884 if (*cp == '\0') {
885 return(NULL);
887 return(cp);
891 * Pipe the messages requested.
893 static int
894 pipe1(char *str, int doign)
896 char *cmd;
897 int f, *msgvec, ret;
898 off_t stats[2];
900 /*LINTED*/
901 msgvec = (int *)salloc((msgCount + 2) * sizeof *msgvec);
902 if ((cmd = laststring(str, &f, 1)) == NULL) {
903 cmd = value("cmd");
904 if (cmd == NULL || *cmd == '\0') {
905 fputs(catgets(catd, CATSET, 16,
906 "variable cmd not set\n"), stderr);
907 return 1;
910 if (!f) {
911 *msgvec = first(0, MMNORM);
912 if (*msgvec == 0) {
913 if (inhook)
914 return 0;
915 puts(catgets(catd, CATSET, 18, "No messages to pipe."));
916 return 1;
918 msgvec[1] = 0;
919 } else if (getmsglist(str, msgvec, 0) < 0)
920 return 1;
921 if (*msgvec == 0) {
922 if (inhook)
923 return 0;
924 printf("No applicable messages.\n");
925 return 1;
927 printf(catgets(catd, CATSET, 268, "Pipe to: \"%s\"\n"), cmd);
928 stats[0] = stats[1] = 0;
929 if ((ret = type1(msgvec, doign, 0, 1, 0, cmd, stats)) == 0) {
930 printf("\"%s\" ", cmd);
931 if (stats[0] >= 0)
932 printf("%lu", (long)stats[0]);
933 else
934 printf(catgets(catd, CATSET, 27, "binary"));
935 printf("/%lu\n", (long)stats[1]);
937 return ret;
941 * Paginate messages, honor ignored fields.
943 int
944 more(void *v)
946 int *msgvec = v;
947 return (type1(msgvec, 1, 1, 0, 0, NULL, NULL));
951 * Paginate messages, even printing ignored fields.
953 int
954 More(void *v)
956 int *msgvec = v;
958 return (type1(msgvec, 0, 1, 0, 0, NULL, NULL));
962 * Type out messages, honor ignored fields.
964 int
965 type(void *v)
967 int *msgvec = v;
969 return(type1(msgvec, 1, 0, 0, 0, NULL, NULL));
973 * Type out messages, even printing ignored fields.
975 int
976 Type(void *v)
978 int *msgvec = v;
980 return(type1(msgvec, 0, 0, 0, 0, NULL, NULL));
984 * Show MIME-encoded message text, including all fields.
987 show(void *v)
989 int *msgvec = v;
991 return(type1(msgvec, 0, 0, 0, 1, NULL, NULL));
995 * Pipe messages, honor ignored fields.
997 int
998 pipecmd(void *v)
1000 char *str = v;
1001 return(pipe1(str, 1));
1004 * Pipe messages, not respecting ignored fields.
1006 int
1007 Pipecmd(void *v)
1009 char *str = v;
1010 return(pipe1(str, 0));
1014 * Respond to a broken pipe signal --
1015 * probably caused by quitting more.
1017 /*ARGSUSED*/
1018 void
1019 brokpipe(int signo)
1021 siglongjmp(pipestop, 1);
1025 * Print the top so many lines of each desired message.
1026 * The number of lines is taken from the variable "toplines"
1027 * and defaults to 5.
1029 int
1030 top(void *v)
1032 int *msgvec = v;
1033 int *ip;
1034 struct message *mp;
1035 int c, topl, lines, lineb;
1036 char *valtop, *linebuf = NULL;
1037 size_t linesize;
1038 FILE *ibuf;
1040 topl = 5;
1041 valtop = value("toplines");
1042 if (valtop != NULL) {
1043 topl = atoi(valtop);
1044 if (topl < 0 || topl > 10000)
1045 topl = 5;
1047 lineb = 1;
1048 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
1049 mp = &message[*ip - 1];
1050 touch(mp);
1051 setdot(mp);
1052 did_print_dot = 1;
1053 if (value("quiet") == NULL)
1054 printf(catgets(catd, CATSET, 19,
1055 "Message %2d:\n"), *ip);
1056 if (mp->m_flag & MNOFROM)
1057 printf("From %s %s\n", fakefrom(mp),
1058 fakedate(mp->m_time));
1059 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL) /* XXX could use TOP */
1060 return 1;
1061 c = mp->m_lines;
1062 if (!lineb)
1063 printf("\n");
1064 for (lines = 0; lines < c && lines <= topl; lines++) {
1065 if (readline(ibuf, &linebuf, &linesize) < 0)
1066 break;
1067 puts(linebuf);
1068 lineb = blankline(linebuf);
1071 if (linebuf)
1072 free(linebuf);
1073 return(0);
1077 * Touch all the given messages so that they will
1078 * get mboxed.
1080 int
1081 stouch(void *v)
1083 int *msgvec = v;
1084 int *ip;
1086 for (ip = msgvec; *ip != 0; ip++) {
1087 setdot(&message[*ip-1]);
1088 dot->m_flag |= MTOUCH;
1089 dot->m_flag &= ~MPRESERVE;
1091 * POSIX interpretation necessary.
1093 did_print_dot = 1;
1095 return(0);
1099 * Make sure all passed messages get mboxed.
1101 int
1102 mboxit(void *v)
1104 int *msgvec = v;
1105 int *ip;
1107 for (ip = msgvec; *ip != 0; ip++) {
1108 setdot(&message[*ip-1]);
1109 dot->m_flag |= MTOUCH|MBOX;
1110 dot->m_flag &= ~MPRESERVE;
1112 * POSIX interpretation necessary.
1114 did_print_dot = 1;
1116 return(0);
1120 * List the folders the user currently has.
1122 int
1123 folders(void *v)
1125 char **argv = v;
1126 char dirname[PATHSIZE];
1127 char *cmd, *name;
1129 if (*argv)
1130 name = expand(*argv);
1131 else if (getfold(dirname, sizeof dirname) < 0) {
1132 printf(catgets(catd, CATSET, 20,
1133 "No value set for \"folder\"\n"));
1134 return 1;
1135 } else
1136 name = dirname;
1137 if (which_protocol(name) == PROTO_IMAP)
1138 imap_folders(name, *argv == NULL);
1139 else {
1140 if ((cmd = value("LISTER")) == NULL)
1141 cmd = "ls";
1142 run_command(cmd, 0, -1, -1, name, NULL, NULL);
1144 return 0;