*Really* adapt Heirloom mailx(1) as S-nail(1)..
[s-mailx.git] / cmd1.c
blob2e9c7db6cbd2c6e9e42e181ee88b5d9db96f1763
1 /*
2 * S-nail - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 Steffen "Daode" Nurpmeso.
6 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
40 #include "rcv.h"
41 #include "extern.h"
42 #ifdef HAVE_WCWIDTH
43 #include <wchar.h>
44 #endif
47 * Mail -- a mail program
49 * User commands.
53 * Print the current active headings.
54 * Don't change dot if invoker didn't give an argument.
57 static int screen;
58 static void onpipe(int signo);
59 static int dispc(struct message *mp, const char *a);
60 static int scroll1(char *arg, int onlynew);
61 static void hprf(const char *fmt, int mesg, FILE *f, int threaded,
62 const char *attrlist);
63 static int putindent(FILE *fp, struct message *mp, int maxwidth);
64 static int type1(int *msgvec, int doign, int page, int pipe, int decode,
65 char *cmd, off_t *tstats);
66 static int pipe1(char *str, int doign);
67 void brokpipe(int signo);
69 char *
70 get_pager(void)
72 char *cp;
74 cp = value("PAGER");
75 if (cp == NULL || *cp == '\0')
76 cp = value("bsdcompat") ? "more" : "pg";
77 return cp;
80 int
81 headers(void *v)
83 int *msgvec = v;
84 int g, k, n, mesg, flag = 0, lastg = 1;
85 struct message *mp, *mq, *lastmq = NULL;
86 int size;
87 enum mflag fl = MNEW|MFLAGGED;
89 size = screensize();
90 n = msgvec[0]; /* n == {-2, -1, 0}: called from scroll() */
91 if (screen < 0)
92 screen = 0;
93 k = screen * size;
94 if (k >= msgCount)
95 k = msgCount - size;
96 if (k < 0)
97 k = 0;
98 if (mb.mb_threaded == 0) {
99 g = 0;
100 mq = &message[0];
101 for (mp = &message[0]; mp < &message[msgCount]; mp++)
102 if (visible(mp)) {
103 if (g % size == 0)
104 mq = mp;
105 if (mp->m_flag&fl) {
106 lastg = g;
107 lastmq = mq;
109 if ((n > 0 && mp == &message[n-1]) ||
110 (n == 0 && g == k) ||
111 (n == -2 && g == k + size &&
112 lastmq) ||
113 (n < 0 && g >= k &&
114 (mp->m_flag & fl) != 0))
115 break;
116 g++;
118 if (lastmq && (n==-2 || (n==-1 && mp == &message[msgCount]))) {
119 g = lastg;
120 mq = lastmq;
122 screen = g / size;
123 mp = mq;
124 mesg = mp - &message[0];
125 if (dot != &message[n-1]) {
126 for (mq = mp; mq < &message[msgCount]; mq++)
127 if (visible(mq)) {
128 setdot(mq);
129 break;
132 if (mb.mb_type == MB_IMAP)
133 imap_getheaders(mesg+1, mesg + size);
134 for (; mp < &message[msgCount]; mp++) {
135 mesg++;
136 if (!visible(mp))
137 continue;
138 if (flag++ >= size)
139 break;
140 printhead(mesg, stdout, 0);
142 } else { /* threaded */
143 g = 0;
144 mq = threadroot;
145 for (mp = threadroot; mp; mp = next_in_thread(mp))
146 if (visible(mp) && (mp->m_collapsed <= 0 ||
147 mp == &message[n-1])) {
148 if (g % size == 0)
149 mq = mp;
150 if (mp->m_flag&fl) {
151 lastg = g;
152 lastmq = mq;
154 if ((n > 0 && mp == &message[n-1]) ||
155 (n == 0 && g == k) ||
156 (n == -2 && g == k + size &&
157 lastmq) ||
158 (n < 0 && g >= k &&
159 (mp->m_flag & fl) != 0))
160 break;
161 g++;
163 if (lastmq && (n==-2 || (n==-1 && mp==&message[msgCount]))) {
164 g = lastg;
165 mq = lastmq;
167 screen = g / size;
168 mp = mq;
169 if (dot != &message[n-1]) {
170 for (mq = mp; mq; mq = next_in_thread(mq))
171 if (visible(mq) && mq->m_collapsed <= 0) {
172 setdot(mq);
173 break;
176 while (mp) {
177 if (visible(mp) && (mp->m_collapsed <= 0 ||
178 mp == &message[n-1])) {
179 if (flag++ >= size)
180 break;
181 printhead(mp - &message[0] + 1, stdout,
182 mb.mb_threaded);
184 mp = next_in_thread(mp);
187 if (flag == 0) {
188 printf(catgets(catd, CATSET, 6, "No more mail.\n"));
189 return(1);
191 return(0);
195 * Scroll to the next/previous screen
198 scroll(void *v)
200 return scroll1(v, 0);
204 Scroll(void *v)
206 return scroll1(v, 1);
209 static int
210 scroll1(char *arg, int onlynew)
212 int size;
213 int cur[1];
215 cur[0] = onlynew ? -1 : 0;
216 size = screensize();
217 switch (*arg) {
218 case '1': case '2': case '3': case '4': case '5':
219 case '6': case '7': case '8': case '9': case '0':
220 screen = atoi(arg);
221 goto scroll_forward;
222 case '\0':
223 screen++;
224 goto scroll_forward;
225 case '$':
226 screen = msgCount / size;
227 goto scroll_forward;
228 case '+':
229 if (arg[1] == '\0')
230 screen++;
231 else
232 screen += atoi(arg + 1);
233 scroll_forward:
234 if (screen * size > msgCount) {
235 screen = msgCount / size;
236 printf(catgets(catd, CATSET, 7,
237 "On last screenful of messages\n"));
239 break;
241 case '-':
242 if (arg[1] == '\0')
243 screen--;
244 else
245 screen -= atoi(arg + 1);
246 if (screen < 0) {
247 screen = 0;
248 printf(catgets(catd, CATSET, 8,
249 "On first screenful of messages\n"));
251 if (cur[0] == -1)
252 cur[0] = -2;
253 break;
255 default:
256 printf(catgets(catd, CATSET, 9,
257 "Unrecognized scrolling command \"%s\"\n"), arg);
258 return(1);
260 return(headers(cur));
264 * Compute screen size.
266 int
267 screensize(void)
269 int s;
270 char *cp;
272 if ((cp = value("screen")) != NULL && (s = atoi(cp)) > 0)
273 return s;
274 return scrnheight - 4;
277 static sigjmp_buf pipejmp;
279 /*ARGSUSED*/
280 static void
281 onpipe(int signo)
283 (void)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 if (is_a_tty[0] && is_a_tty[1] && (cp = value("crt")) != NULL) {
300 for (n = 0, ip = msgvec; *ip; ip++)
301 n++;
302 if (n > (*cp == '\0' ? screensize() : atoi(cp)) + 3) {
303 cp = get_pager();
304 /* TODO should be below the Popen?
305 * TODO Problem: Popen doesn't encapsulate it,
306 * TODO may leave child running if fdopen() fails!
307 * TODO even more such stuff in this file! */
308 if (sigsetjmp(pipejmp, 1))
309 goto endpipe;
310 if ((obuf = Popen(cp, "w", NULL, 1)) == NULL) {
311 perror(cp);
312 obuf = stdout;
313 } else
314 safe_signal(SIGPIPE, onpipe);
317 for (ip = msgvec; *ip != 0; ip++)
318 printhead(*ip, obuf, mb.mb_threaded);
319 if (--ip >= msgvec)
320 setdot(&message[*ip - 1]);
321 endpipe:
322 if (obuf != stdout) {
323 safe_signal(SIGPIPE, SIG_IGN);
324 Pclose(obuf);
325 safe_signal(SIGPIPE, dflpipe);
327 return(0);
330 static int
331 dispc(struct message *mp, const char *a)
333 int dispc = ' ';
336 * Bletch!
338 if ((mp->m_flag & (MREAD|MNEW)) == MREAD)
339 dispc = a[3];
340 if ((mp->m_flag & (MREAD|MNEW)) == (MREAD|MNEW))
341 dispc = a[2];
342 if (mp->m_flag & MANSWERED)
343 dispc = a[8];
344 if (mp->m_flag & MDRAFTED)
345 dispc = a[9];
346 if ((mp->m_flag & (MREAD|MNEW)) == MNEW)
347 dispc = a[0];
348 if ((mp->m_flag & (MREAD|MNEW)) == 0)
349 dispc = a[1];
350 if (mp->m_flag & MJUNK)
351 dispc = a[13];
352 if (mp->m_flag & MSAVED)
353 dispc = a[4];
354 if (mp->m_flag & MPRESERVE)
355 dispc = a[5];
356 if (mp->m_flag & (MBOX|MBOXED))
357 dispc = a[6];
358 if (mp->m_flag & MFLAGGED)
359 dispc = a[7];
360 if (mp->m_flag & MKILL)
361 dispc = a[10];
362 if (mb.mb_threaded == 1 && mp->m_collapsed > 0)
363 dispc = a[12];
364 if (mb.mb_threaded == 1 && mp->m_collapsed < 0)
365 dispc = a[11];
366 return dispc;
369 static void
370 hprf(const char *fmt, int mesg, FILE *f, int threaded, const char *attrlist)
372 struct message *mp = &message[mesg-1];
373 char *headline = NULL, *subjline, *name, *cp, *pbuf = NULL;
374 struct headline hl;
375 size_t headsize = 0;
376 const char *fp;
377 int B, c, i, n, s;
378 int headlen = 0;
379 struct str in, out;
380 int subjlen = scrnwidth, fromlen, isto = 0, isaddr = 0;
381 FILE *ibuf;
383 if ((mp->m_flag & MNOFROM) == 0) {
384 if ((ibuf = setinput(&mb, mp, NEED_HEADER)) == NULL)
385 return;
386 if ((headlen = readline(ibuf, &headline, &headsize)) < 0)
387 return;
389 if ((subjline = hfield("subject", mp)) == NULL)
390 subjline = hfield("subj", mp);
391 if (subjline == NULL) {
392 out.s = NULL;
393 out.l = 0;
394 } else {
395 in.s = subjline;
396 in.l = strlen(subjline);
397 mime_fromhdr(&in, &out, TD_ICONV | TD_ISPR);
398 subjline = out.s;
400 if ((mp->m_flag & MNOFROM) == 0) {
401 pbuf = ac_alloc(headlen + 1);
402 parse(headline, headlen, &hl, pbuf);
403 } else {
404 hl.l_from = /*fakefrom(mp);*/NULL;
405 hl.l_tty = NULL;
406 hl.l_date = fakedate(mp->m_time);
408 if (value("datefield") && (cp = hfield("date", mp)) != NULL)
409 hl.l_date = fakedate(rfctime(cp));
410 if (Iflag) {
411 if ((name = hfield("newsgroups", mp)) == NULL)
412 if ((name = hfield("article-id", mp)) == NULL)
413 name = "<>";
414 name = prstr(name);
415 } else if (value("show-rcpt") == NULL) {
416 name = name1(mp, 0);
417 isaddr = 1;
418 if (value("showto") && name && is_myname(skin(name))) {
419 if ((cp = hfield("to", mp)) != NULL) {
420 name = cp;
421 isto = 1;
424 } else {
425 isaddr = 1;
426 if ((name = hfield("to", mp)) != NULL)
427 isto = 1;
429 if (name == NULL) {
430 name = "";
431 isaddr = 0;
433 if (isaddr) {
434 if (value("showname"))
435 name = realname(name);
436 else {
437 name = prstr(skin(name));
440 for (fp = fmt; *fp; fp++) {
441 if (*fp == '%') {
442 if (*++fp == '-') {
443 fp++;
444 } else if (*fp == '+')
445 fp++;
446 while (digitchar(*fp&0377))
447 fp++;
448 if (*fp == '\0')
449 break;
450 } else {
451 #if defined (HAVE_MBTOWC) && defined (HAVE_WCWIDTH)
452 if (mb_cur_max > 1) {
453 wchar_t wc;
454 if ((s = mbtowc(&wc, fp, mb_cur_max)) < 0)
455 n = s = 1;
456 else {
457 if ((n = wcwidth(wc)) < 0)
458 n = 1;
460 } else
461 #endif /* HAVE_MBTOWC && HAVE_WCWIDTH */
463 n = s = 1;
465 subjlen -= n;
466 while (--s > 0)
467 fp++;
470 for (fp = fmt; *fp; fp++) {
471 if (*fp == '%') {
472 B = 0;
473 n = 0;
474 s = 1;
475 if (*++fp == '-') {
476 s = -1;
477 fp++;
478 } else if (*fp == '+')
479 fp++;
480 if (digitchar(*fp&0377)) {
482 n = 10*n + *fp - '0';
483 while (fp++, digitchar(*fp&0377));
485 if (*fp == '\0')
486 break;
487 n *= s;
488 switch (*fp) {
489 case '%':
490 putc('%', f);
491 subjlen--;
492 break;
493 case '>':
494 case '<':
495 c = dot == mp ? *fp&0377 : ' ';
496 putc(c, f);
497 subjlen--;
498 break;
499 case 'a':
500 c = dispc(mp, attrlist);
501 putc(c, f);
502 subjlen--;
503 break;
504 case 'm':
505 if (n == 0) {
506 n = 3;
507 if (threaded)
508 for (i=msgCount; i>999; i/=10)
509 n++;
511 subjlen -= fprintf(f, "%*d", n, mesg);
512 break;
513 case 'f':
514 if (n <= 0)
515 n = 18;
516 fromlen = n;
517 if (isto)
518 fromlen -= 3;
519 fprintf(f, "%s%s", isto ? "To " : "",
520 colalign(name, fromlen, 1));
521 subjlen -= n;
522 break;
523 case 'd':
524 if (n <= 0)
525 n = 16;
526 subjlen -= fprintf(f, "%*.*s", n, n, hl.l_date);
527 break;
528 case 'l':
529 if (n == 0)
530 n = 4;
531 if (mp->m_xlines)
532 subjlen -= fprintf(f, "%*ld", n,
533 mp->m_xlines);
534 else {
535 subjlen -= n;
536 while (n--)
537 putc(' ', f);
539 break;
540 case 'o':
541 if (n == 0)
542 n = -5;
543 subjlen -= fprintf(f, "%*lu", n,
544 (long)mp->m_xsize);
545 break;
546 case 'i':
547 if (threaded)
548 subjlen -= putindent(f, mp,
549 scrnwidth - 60);
550 break;
551 case 'S':
552 B = 1;
553 /*FALLTHRU*/
554 case 's':
555 n = n>0 ? n : subjlen - 2;
556 if (B)
557 n -= 2;
558 if (subjline != NULL && n >= 0) {
559 /* pretty pathetic */
560 fprintf(f, B ? "\"%s\"" : "%s",
561 colalign(subjline, n, 0));
563 break;
564 case 'U':
565 if (n == 0)
566 n = 9;
567 subjlen -= fprintf(f, "%*lu", n, mp->m_uid);
568 break;
569 case 'e':
570 if (n == 0)
571 n = 2;
572 subjlen -= fprintf(f, "%*u", n, threaded == 1 ?
573 mp->m_level : 0);
574 break;
575 case 't':
576 if (n == 0) {
577 n = 3;
578 if (threaded)
579 for (i=msgCount; i>999; i/=10)
580 n++;
582 fprintf(f, "%*ld", n, threaded ?
583 mp->m_threadpos : mesg);
584 subjlen -= n;
585 break;
586 case 'c':
587 if (n == 0)
588 n = 6;
589 subjlen -= fprintf(f, "%*g", n, mp->m_score);
590 break;
592 } else
593 putc(*fp&0377, f);
595 putc('\n', f);
596 if (out.s)
597 free(out.s);
598 if (headline)
599 free(headline);
600 if (pbuf)
601 ac_free(pbuf);
605 * Print out the indenting in threaded display.
607 static int
608 putindent(FILE *fp, struct message *mp, int maxwidth)
610 struct message *mq;
611 int indent, i;
612 int *us;
613 char *cs;
614 int important = MNEW|MFLAGGED;
616 if (mp->m_level == 0)
617 return 0;
618 cs = ac_alloc(mp->m_level);
619 us = ac_alloc(mp->m_level * sizeof *us);
620 i = mp->m_level - 1;
621 if (mp->m_younger && (unsigned)i + 1 == mp->m_younger->m_level) {
622 if (mp->m_parent && mp->m_parent->m_flag & important)
623 us[i] = mp->m_flag & important ? 0x2523 : 0x2520;
624 else
625 us[i] = mp->m_flag & important ? 0x251D : 0x251C;
626 cs[i] = '+';
627 } else {
628 if (mp->m_parent && mp->m_parent->m_flag & important)
629 us[i] = mp->m_flag & important ? 0x2517 : 0x2516;
630 else
631 us[i] = mp->m_flag & important ? 0x2515 : 0x2514;
632 cs[i] = '\\';
634 mq = mp->m_parent;
635 for (i = mp->m_level - 2; i >= 0; i--) {
636 if (mq) {
637 if ((unsigned)i > mq->m_level - 1) {
638 us[i] = cs[i] = ' ';
639 continue;
641 if (mq->m_younger) {
642 if (mq->m_parent &&
643 mq->m_parent->m_flag&important)
644 us[i] = 0x2503;
645 else
646 us[i] = 0x2502;
647 cs[i] = '|';
648 } else
649 us[i] = cs[i] = ' ';
650 mq = mq->m_parent;
651 } else
652 us[i] = cs[i] = ' ';
654 for (indent = 0; (unsigned)indent < mp->m_level && indent < maxwidth;
655 ++indent) {
656 if (indent < maxwidth - 1)
657 putuc(us[indent], cs[indent] & 0377, fp);
658 else
659 putuc(0x21B8, '^', fp);
661 ac_free(us);
662 ac_free(cs);
663 return indent;
667 * Print out the header of a specific message.
668 * This is a slight improvement to the standard one.
670 void
671 printhead(int mesg, FILE *f, int threaded)
673 int bsdflags, bsdheadline, sz;
674 char *fmt, attrlist[30], *cp;
676 bsdflags = value("bsdcompat") != NULL || value("bsdflags") != NULL ||
677 getenv("SYSV3") != NULL;
678 strcpy(attrlist, bsdflags ? "NU *HMFATK+-J" : "NUROSPMFATK+-J");
679 if ((cp = value("attrlist")) != NULL) {
680 sz = strlen(cp);
681 if (sz > (int)sizeof attrlist - 1)
682 sz = (int)sizeof attrlist - 1;
683 memcpy(attrlist, cp, sz);
685 bsdheadline = value("bsdcompat") != NULL ||
686 value("bsdheadline") != NULL;
687 if ((fmt = value("headline")) == NULL)
688 fmt = bsdheadline ?
689 "%>%a%m %20f %16d %3l/%-5o %i%S" :
690 "%>%a%m %18f %16d %4l/%-5o %i%s";
691 hprf(fmt, mesg, f, threaded, attrlist);
695 * Print out the value of dot.
697 /*ARGSUSED*/
698 int
699 pdot(void *v)
701 (void)v;
702 printf(catgets(catd, CATSET, 13, "%d\n"),
703 (int)(dot - &message[0] + 1));
704 return(0);
708 * Print out all the possible commands.
710 /*ARGSUSED*/
711 int
712 pcmdlist(void *v)
714 extern const struct cmd cmdtab[];
715 const struct cmd *cp;
716 int cc;
717 (void)v;
719 printf(catgets(catd, CATSET, 14, "Commands are:\n"));
720 for (cc = 0, cp = cmdtab; cp->c_name != NULL; cp++) {
721 cc += strlen(cp->c_name) + 2;
722 if (cc > 72) {
723 printf("\n");
724 cc = strlen(cp->c_name) + 2;
726 if ((cp+1)->c_name != NULL)
727 printf(catgets(catd, CATSET, 15, "%s, "), cp->c_name);
728 else
729 printf("%s\n", cp->c_name);
731 return(0);
735 * Type out the messages requested.
737 static sigjmp_buf pipestop;
739 static int
740 type1(int *msgvec, int doign, int page, int pipe, int decode,
741 char *cmd, off_t *tstats)
743 int *ip;
744 struct message *mp;
745 char *cp;
746 int nlines;
747 off_t mstats[2];
749 * Must be static to become excluded from sigsetjmp().
751 static FILE *obuf;
752 #ifdef __GNUC__
753 /* Avoid longjmp clobbering */
754 (void) &cp;
755 (void) &cmd;
756 (void) &obuf;
757 #endif
759 obuf = stdout;
760 if (sigsetjmp(pipestop, 1))
761 goto close_pipe;
762 if (pipe) {
763 cp = value("SHELL");
764 if (cp == NULL)
765 cp = SHELL;
766 obuf = Popen(cmd, "w", cp, 1);
767 if (obuf == NULL) {
768 perror(cmd);
769 obuf = stdout;
770 } else {
771 safe_signal(SIGPIPE, brokpipe);
773 } else if (value("interactive") != NULL &&
774 (page || (cp = value("crt")) != NULL)) {
775 nlines = 0;
776 if (!page) {
777 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
778 if ((message[*ip-1].m_have & HAVE_BODY) == 0) {
779 if ((get_body(&message[*ip - 1])) !=
780 OKAY)
781 return 1;
783 nlines += message[*ip - 1].m_lines;
786 if (page || nlines > (*cp ? atoi(cp) : realscreenheight)) {
787 cp = get_pager();
788 obuf = Popen(cp, "w", NULL, 1);
789 if (obuf == NULL) {
790 perror(cp);
791 obuf = stdout;
792 } else
793 safe_signal(SIGPIPE, brokpipe);
796 for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) {
797 mp = &message[*ip - 1];
798 touch(mp);
799 setdot(mp);
800 uncollapse1(mp, 1);
801 if (value("quiet") == NULL)
802 fprintf(obuf, catgets(catd, CATSET, 17,
803 "Message %2d:\n"), *ip);
804 send(mp, obuf, doign ? ignore : 0, NULL,
805 pipe && value("piperaw") ? SEND_MBOX :
806 decode ? SEND_SHOW :
807 doign ? SEND_TODISP : SEND_TODISP_ALL,
808 mstats);
809 if (pipe && value("page")) {
810 putc('\f', obuf);
812 if (tstats) {
813 tstats[0] += mstats[0];
814 tstats[1] += mstats[1];
817 close_pipe:
818 if (obuf != stdout) {
820 * Ignore SIGPIPE so it can't cause a duplicate close.
822 safe_signal(SIGPIPE, SIG_IGN);
823 Pclose(obuf);
824 safe_signal(SIGPIPE, dflpipe);
826 return(0);
830 * Get the last, possibly quoted part of linebuf.
832 char *
833 laststring(char *linebuf, int *flag, int strip)
835 char *cp, *p;
836 char quoted;
838 *flag = 1;
839 cp = strlen(linebuf) + linebuf - 1;
842 * Strip away trailing blanks.
844 while (cp > linebuf && whitechar(*cp & 0377))
845 cp--;
846 *++cp = 0;
847 if (cp == linebuf) {
848 *flag = 0;
849 return NULL;
853 * Now search for the beginning of the command name.
855 quoted = *(cp - 1);
856 if (quoted == '\'' || quoted == '\"') {
857 cp--;
858 if (strip)
859 *cp = '\0';
860 cp--;
861 while (cp > linebuf) {
862 if (*cp != quoted) {
863 cp--;
864 } else if (*(cp - 1) != '\\') {
865 break;
866 } else {
867 p = --cp;
868 do {
869 *p = *(p + 1);
870 } while (*p++);
871 cp--;
874 if (cp == linebuf)
875 *flag = 0;
876 if (*cp == quoted) {
877 if (strip)
878 *cp++ = 0;
879 } else
880 *flag = 0;
881 } else {
882 while (cp > linebuf && !whitechar(*cp & 0377))
883 cp--;
884 if (whitechar(*cp & 0377))
885 *cp++ = 0;
886 else
887 *flag = 0;
889 if (*cp == '\0') {
890 return(NULL);
892 return(cp);
896 * Pipe the messages requested.
898 static int
899 pipe1(char *str, int doign)
901 char *cmd;
902 int f, *msgvec, ret;
903 off_t stats[2];
905 /*LINTED*/
906 msgvec = (int *)salloc((msgCount + 2) * sizeof *msgvec);
907 if ((cmd = laststring(str, &f, 1)) == NULL) {
908 cmd = value("cmd");
909 if (cmd == NULL || *cmd == '\0') {
910 fputs(catgets(catd, CATSET, 16,
911 "variable cmd not set\n"), stderr);
912 return 1;
915 if (!f) {
916 *msgvec = first(0, MMNORM);
917 if (*msgvec == 0) {
918 if (inhook)
919 return 0;
920 puts(catgets(catd, CATSET, 18, "No messages to pipe."));
921 return 1;
923 msgvec[1] = 0;
924 } else if (getmsglist(str, msgvec, 0) < 0)
925 return 1;
926 if (*msgvec == 0) {
927 if (inhook)
928 return 0;
929 printf("No applicable messages.\n");
930 return 1;
932 printf(catgets(catd, CATSET, 268, "Pipe to: \"%s\"\n"), cmd);
933 stats[0] = stats[1] = 0;
934 if ((ret = type1(msgvec, doign, 0, 1, 0, cmd, stats)) == 0) {
935 printf("\"%s\" ", cmd);
936 if (stats[0] >= 0)
937 printf("%lu", (long)stats[0]);
938 else
939 printf(catgets(catd, CATSET, 27, "binary"));
940 printf("/%lu\n", (long)stats[1]);
942 return ret;
946 * Paginate messages, honor ignored fields.
948 int
949 more(void *v)
951 int *msgvec = v;
952 return (type1(msgvec, 1, 1, 0, 0, NULL, NULL));
956 * Paginate messages, even printing ignored fields.
958 int
959 More(void *v)
961 int *msgvec = v;
963 return (type1(msgvec, 0, 1, 0, 0, NULL, NULL));
967 * Type out messages, honor ignored fields.
969 int
970 type(void *v)
972 int *msgvec = v;
974 return(type1(msgvec, 1, 0, 0, 0, NULL, NULL));
978 * Type out messages, even printing ignored fields.
980 int
981 Type(void *v)
983 int *msgvec = v;
985 return(type1(msgvec, 0, 0, 0, 0, NULL, NULL));
989 * Show MIME-encoded message text, including all fields.
992 show(void *v)
994 int *msgvec = v;
996 return(type1(msgvec, 0, 0, 0, 1, NULL, NULL));
1000 * Pipe messages, honor ignored fields.
1002 int
1003 pipecmd(void *v)
1005 char *str = v;
1006 return(pipe1(str, 1));
1009 * Pipe messages, not respecting ignored fields.
1011 int
1012 Pipecmd(void *v)
1014 char *str = v;
1015 return(pipe1(str, 0));
1019 * Respond to a broken pipe signal --
1020 * probably caused by quitting more.
1022 /*ARGSUSED*/
1023 void
1024 brokpipe(int signo)
1026 (void)signo;
1027 siglongjmp(pipestop, 1);
1031 * Print the top so many lines of each desired message.
1032 * The number of lines is taken from the variable "toplines"
1033 * and defaults to 5.
1035 int
1036 top(void *v)
1038 int *msgvec = v;
1039 int *ip;
1040 struct message *mp;
1041 int c, topl, lines, lineb;
1042 char *valtop, *linebuf = NULL;
1043 size_t linesize;
1044 FILE *ibuf;
1046 topl = 5;
1047 valtop = value("toplines");
1048 if (valtop != NULL) {
1049 topl = atoi(valtop);
1050 if (topl < 0 || topl > 10000)
1051 topl = 5;
1053 lineb = 1;
1054 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
1055 mp = &message[*ip - 1];
1056 touch(mp);
1057 setdot(mp);
1058 did_print_dot = 1;
1059 if (value("quiet") == NULL)
1060 printf(catgets(catd, CATSET, 19,
1061 "Message %2d:\n"), *ip);
1062 if (mp->m_flag & MNOFROM)
1063 printf("From %s %s\n", fakefrom(mp),
1064 fakedate(mp->m_time));
1065 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL) /* XXX could use TOP */
1066 return 1;
1067 c = mp->m_lines;
1068 if (!lineb)
1069 printf("\n");
1070 for (lines = 0; lines < c && lines <= topl; lines++) {
1071 if (readline(ibuf, &linebuf, &linesize) < 0)
1072 break;
1073 puts(linebuf);
1074 lineb = blankline(linebuf);
1077 if (linebuf)
1078 free(linebuf);
1079 return(0);
1083 * Touch all the given messages so that they will
1084 * get mboxed.
1086 int
1087 stouch(void *v)
1089 int *msgvec = v;
1090 int *ip;
1092 for (ip = msgvec; *ip != 0; ip++) {
1093 setdot(&message[*ip-1]);
1094 dot->m_flag |= MTOUCH;
1095 dot->m_flag &= ~MPRESERVE;
1097 * POSIX interpretation necessary.
1099 did_print_dot = 1;
1101 return(0);
1105 * Make sure all passed messages get mboxed.
1107 int
1108 mboxit(void *v)
1110 int *msgvec = v;
1111 int *ip;
1113 for (ip = msgvec; *ip != 0; ip++) {
1114 setdot(&message[*ip-1]);
1115 dot->m_flag |= MTOUCH|MBOX;
1116 dot->m_flag &= ~MPRESERVE;
1118 * POSIX interpretation necessary.
1120 did_print_dot = 1;
1122 return(0);
1126 * List the folders the user currently has.
1128 int
1129 folders(void *v)
1131 char **argv = v;
1132 char dirname[PATHSIZE];
1133 char *cmd, *name;
1135 if (*argv)
1136 name = expand(*argv);
1137 else if (getfold(dirname, sizeof dirname) < 0) {
1138 printf(catgets(catd, CATSET, 20,
1139 "No value set for \"folder\"\n"));
1140 return 1;
1141 } else
1142 name = dirname;
1143 if (which_protocol(name) == PROTO_IMAP)
1144 imap_folders(name, *argv == NULL);
1145 else {
1146 if ((cmd = value("LISTER")) == NULL)
1147 cmd = "ls";
1148 run_command(cmd, 0, -1, -1, name, NULL, NULL);
1150 return 0;