Do not use non-compiler-builtin alloca(3)s..
[s-mailx.git] / cmd1.c
blob87064aa9b3144265b6b16efc769efe5070bcb966
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, *ip, n;
295 FILE *volatile obuf = stdout;
296 char volatile *cp;
298 if (is_a_tty[0] && is_a_tty[1] && (cp = value("crt")) != NULL) {
299 for (n = 0, ip = msgvec; *ip; ip++)
300 n++;
301 if (n > (*cp == '\0' ? screensize() : atoi((char*)cp)) + 3) {
302 cp = get_pager();
303 if (sigsetjmp(pipejmp, 1))
304 goto endpipe;
305 if ((obuf = Popen((char*)cp, "w", NULL, 1)) == NULL) {
306 perror((char*)cp);
307 obuf = stdout;
308 cp=NULL;
309 } else
310 safe_signal(SIGPIPE, onpipe);
313 for (ip = msgvec; *ip != 0; ip++)
314 printhead(*ip, obuf, mb.mb_threaded);
315 if (--ip >= msgvec)
316 setdot(&message[*ip - 1]);
317 endpipe:
318 if (obuf != stdout) {
319 safe_signal(SIGPIPE, SIG_IGN);
320 Pclose(obuf);
321 safe_signal(SIGPIPE, dflpipe);
323 return(0);
326 static int
327 dispc(struct message *mp, const char *a)
329 int dispc = ' ';
332 * Bletch!
334 if ((mp->m_flag & (MREAD|MNEW)) == MREAD)
335 dispc = a[3];
336 if ((mp->m_flag & (MREAD|MNEW)) == (MREAD|MNEW))
337 dispc = a[2];
338 if (mp->m_flag & MANSWERED)
339 dispc = a[8];
340 if (mp->m_flag & MDRAFTED)
341 dispc = a[9];
342 if ((mp->m_flag & (MREAD|MNEW)) == MNEW)
343 dispc = a[0];
344 if ((mp->m_flag & (MREAD|MNEW)) == 0)
345 dispc = a[1];
346 if (mp->m_flag & MJUNK)
347 dispc = a[13];
348 if (mp->m_flag & MSAVED)
349 dispc = a[4];
350 if (mp->m_flag & MPRESERVE)
351 dispc = a[5];
352 if (mp->m_flag & (MBOX|MBOXED))
353 dispc = a[6];
354 if (mp->m_flag & MFLAGGED)
355 dispc = a[7];
356 if (mp->m_flag & MKILL)
357 dispc = a[10];
358 if (mb.mb_threaded == 1 && mp->m_collapsed > 0)
359 dispc = a[12];
360 if (mb.mb_threaded == 1 && mp->m_collapsed < 0)
361 dispc = a[11];
362 return dispc;
365 static void
366 hprf(const char *fmt, int mesg, FILE *f, int threaded, const char *attrlist)
368 struct message *mp = &message[mesg-1];
369 char *headline = NULL, *subjline, *name, *cp, *pbuf = NULL;
370 struct headline hl;
371 size_t headsize = 0;
372 const char *fp;
373 int B, c, i, n, s;
374 int headlen = 0;
375 struct str in, out;
376 int subjlen = scrnwidth, fromlen, isto = 0, isaddr = 0;
377 FILE *ibuf;
379 if ((mp->m_flag & MNOFROM) == 0) {
380 if ((ibuf = setinput(&mb, mp, NEED_HEADER)) == NULL)
381 return;
382 if ((headlen = readline(ibuf, &headline, &headsize)) < 0)
383 return;
385 if ((subjline = hfield1("subject", mp)) == NULL) {
386 out.s = NULL;
387 out.l = 0;
388 } else {
389 in.s = subjline;
390 in.l = strlen(subjline);
391 mime_fromhdr(&in, &out, TD_ICONV | TD_ISPR);
392 subjline = out.s;
394 if ((mp->m_flag & MNOFROM) == 0) {
395 pbuf = ac_alloc(headlen + 1);
396 parse(headline, headlen, &hl, pbuf);
397 } else {
398 hl.l_from = /*fakefrom(mp);*/NULL;
399 hl.l_tty = NULL;
400 hl.l_date = fakedate(mp->m_time);
402 if (value("datefield") && (cp = hfield1("date", mp)) != NULL)
403 hl.l_date = fakedate(rfctime(cp));
404 if (Iflag) {
405 if ((name = hfieldX("newsgroups", mp)) == NULL)
406 if ((name = hfieldX("article-id", mp)) == NULL)
407 name = "<>";
408 name = prstr(name);
409 } else if (value("show-rcpt") == NULL) {
410 name = name1(mp, 0);
411 isaddr = 1;
412 if (value("showto") && name && is_myname(skin(name))) {
413 if ((cp = hfield1("to", mp)) != NULL) {
414 name = cp;
415 isto = 1;
418 } else {
419 isaddr = 1;
420 if ((name = hfield1("to", mp)) != NULL)
421 isto = 1;
423 if (name == NULL) {
424 name = "";
425 isaddr = 0;
427 if (isaddr) {
428 if (value("showname"))
429 name = realname(name);
430 else {
431 name = prstr(skin(name));
434 for (fp = fmt; *fp; fp++) {
435 if (*fp == '%') {
436 if (*++fp == '-') {
437 fp++;
438 } else if (*fp == '+')
439 fp++;
440 while (digitchar(*fp&0377))
441 fp++;
442 if (*fp == '\0')
443 break;
444 } else {
445 #if defined (HAVE_MBTOWC) && defined (HAVE_WCWIDTH)
446 if (mb_cur_max > 1) {
447 wchar_t wc;
448 if ((s = mbtowc(&wc, fp, mb_cur_max)) < 0)
449 n = s = 1;
450 else {
451 if ((n = wcwidth(wc)) < 0)
452 n = 1;
454 } else
455 #endif /* HAVE_MBTOWC && HAVE_WCWIDTH */
457 n = s = 1;
459 subjlen -= n;
460 while (--s > 0)
461 fp++;
464 for (fp = fmt; *fp; fp++) {
465 if (*fp == '%') {
466 B = 0;
467 n = 0;
468 s = 1;
469 if (*++fp == '-') {
470 s = -1;
471 fp++;
472 } else if (*fp == '+')
473 fp++;
474 if (digitchar(*fp&0377)) {
476 n = 10*n + *fp - '0';
477 while (fp++, digitchar(*fp&0377));
479 if (*fp == '\0')
480 break;
481 n *= s;
482 switch (*fp) {
483 case '%':
484 putc('%', f);
485 subjlen--;
486 break;
487 case '>':
488 case '<':
489 c = dot == mp ? *fp&0377 : ' ';
490 putc(c, f);
491 subjlen--;
492 break;
493 case 'a':
494 c = dispc(mp, attrlist);
495 putc(c, f);
496 subjlen--;
497 break;
498 case 'm':
499 if (n == 0) {
500 n = 3;
501 if (threaded)
502 for (i=msgCount; i>999; i/=10)
503 n++;
505 subjlen -= fprintf(f, "%*d", n, mesg);
506 break;
507 case 'f':
508 if (n <= 0)
509 n = 18;
510 fromlen = n;
511 if (isto)
512 fromlen -= 3;
513 fprintf(f, "%s%s", isto ? "To " : "",
514 colalign(name, fromlen, 1));
515 subjlen -= n;
516 break;
517 case 'd':
518 if (n <= 0)
519 n = 16;
520 subjlen -= fprintf(f, "%*.*s", n, n, hl.l_date);
521 break;
522 case 'l':
523 if (n == 0)
524 n = 4;
525 if (mp->m_xlines)
526 subjlen -= fprintf(f, "%*ld", n,
527 mp->m_xlines);
528 else {
529 subjlen -= n;
530 while (n--)
531 putc(' ', f);
533 break;
534 case 'o':
535 if (n == 0)
536 n = -5;
537 subjlen -= fprintf(f, "%*lu", n,
538 (long)mp->m_xsize);
539 break;
540 case 'i':
541 if (threaded)
542 subjlen -= putindent(f, mp,
543 scrnwidth - 60);
544 break;
545 case 'S':
546 B = 1;
547 /*FALLTHRU*/
548 case 's':
549 n = n>0 ? n : subjlen - 2;
550 if (B)
551 n -= 2;
552 if (subjline != NULL && n >= 0) {
553 /* pretty pathetic */
554 fprintf(f, B ? "\"%s\"" : "%s",
555 colalign(subjline, n, 0));
557 break;
558 case 'U':
559 if (n == 0)
560 n = 9;
561 subjlen -= fprintf(f, "%*lu", n, mp->m_uid);
562 break;
563 case 'e':
564 if (n == 0)
565 n = 2;
566 subjlen -= fprintf(f, "%*u", n, threaded == 1 ?
567 mp->m_level : 0);
568 break;
569 case 't':
570 if (n == 0) {
571 n = 3;
572 if (threaded)
573 for (i=msgCount; i>999; i/=10)
574 n++;
576 fprintf(f, "%*ld", n, threaded ?
577 mp->m_threadpos : mesg);
578 subjlen -= n;
579 break;
580 case 'c':
581 if (n == 0)
582 n = 6;
583 subjlen -= fprintf(f, "%*g", n, mp->m_score);
584 break;
586 } else
587 putc(*fp&0377, f);
589 putc('\n', f);
590 if (out.s)
591 free(out.s);
592 if (headline)
593 free(headline);
594 if (pbuf)
595 ac_free(pbuf);
599 * Print out the indenting in threaded display.
601 static int
602 putindent(FILE *fp, struct message *mp, int maxwidth)
604 struct message *mq;
605 int indent, i;
606 int *us;
607 char *cs;
608 int important = MNEW|MFLAGGED;
610 if (mp->m_level == 0)
611 return 0;
612 cs = ac_alloc(mp->m_level);
613 us = ac_alloc(mp->m_level * sizeof *us);
614 i = mp->m_level - 1;
615 if (mp->m_younger && (unsigned)i + 1 == mp->m_younger->m_level) {
616 if (mp->m_parent && mp->m_parent->m_flag & important)
617 us[i] = mp->m_flag & important ? 0x2523 : 0x2520;
618 else
619 us[i] = mp->m_flag & important ? 0x251D : 0x251C;
620 cs[i] = '+';
621 } else {
622 if (mp->m_parent && mp->m_parent->m_flag & important)
623 us[i] = mp->m_flag & important ? 0x2517 : 0x2516;
624 else
625 us[i] = mp->m_flag & important ? 0x2515 : 0x2514;
626 cs[i] = '\\';
628 mq = mp->m_parent;
629 for (i = mp->m_level - 2; i >= 0; i--) {
630 if (mq) {
631 if ((unsigned)i > mq->m_level - 1) {
632 us[i] = cs[i] = ' ';
633 continue;
635 if (mq->m_younger) {
636 if (mq->m_parent &&
637 mq->m_parent->m_flag&important)
638 us[i] = 0x2503;
639 else
640 us[i] = 0x2502;
641 cs[i] = '|';
642 } else
643 us[i] = cs[i] = ' ';
644 mq = mq->m_parent;
645 } else
646 us[i] = cs[i] = ' ';
648 for (indent = 0; (unsigned)indent < mp->m_level && indent < maxwidth;
649 ++indent) {
650 if (indent < maxwidth - 1)
651 putuc(us[indent], cs[indent] & 0377, fp);
652 else
653 putuc(0x21B8, '^', fp);
655 ac_free(us);
656 ac_free(cs);
657 return indent;
661 * Print out the header of a specific message.
662 * This is a slight improvement to the standard one.
664 void
665 printhead(int mesg, FILE *f, int threaded)
667 int bsdflags, bsdheadline, sz;
668 char *fmt, attrlist[30], *cp;
670 bsdflags = value("bsdcompat") != NULL || value("bsdflags") != NULL ||
671 getenv("SYSV3") != NULL;
672 strcpy(attrlist, bsdflags ? "NU *HMFATK+-J" : "NUROSPMFATK+-J");
673 if ((cp = value("attrlist")) != NULL) {
674 sz = strlen(cp);
675 if (sz > (int)sizeof attrlist - 1)
676 sz = (int)sizeof attrlist - 1;
677 memcpy(attrlist, cp, sz);
679 bsdheadline = value("bsdcompat") != NULL ||
680 value("bsdheadline") != NULL;
681 if ((fmt = value("headline")) == NULL)
682 fmt = bsdheadline ?
683 "%>%a%m %20f %16d %3l/%-5o %i%S" :
684 "%>%a%m %18f %16d %4l/%-5o %i%s";
685 hprf(fmt, mesg, f, threaded, attrlist);
689 * Print out the value of dot.
691 /*ARGSUSED*/
692 int
693 pdot(void *v)
695 (void)v;
696 printf(catgets(catd, CATSET, 13, "%d\n"),
697 (int)(dot - &message[0] + 1));
698 return(0);
702 * Print out all the possible commands.
704 /*ARGSUSED*/
705 int
706 pcmdlist(void *v)
708 extern const struct cmd cmdtab[];
709 const struct cmd *cp;
710 int cc;
711 (void)v;
713 printf(catgets(catd, CATSET, 14, "Commands are:\n"));
714 for (cc = 0, cp = cmdtab; cp->c_name != NULL; cp++) {
715 cc += strlen(cp->c_name) + 2;
716 if (cc > 72) {
717 printf("\n");
718 cc = strlen(cp->c_name) + 2;
720 if ((cp+1)->c_name != NULL)
721 printf(catgets(catd, CATSET, 15, "%s, "), cp->c_name);
722 else
723 printf("%s\n", cp->c_name);
725 return(0);
729 * Type out the messages requested.
731 static sigjmp_buf pipestop;
733 static int
734 type1(int *msgvec, int doign, int page, int pipe, int decode,
735 char *cmd, off_t *tstats)
737 int *ip;
738 struct message *mp;
739 char *cp;
740 int nlines;
741 off_t mstats[2];
743 * Must be static to become excluded from sigsetjmp().
745 static FILE *obuf;
746 #ifdef __GNUC__
747 /* Avoid longjmp clobbering */
748 (void) &cp;
749 (void) &cmd;
750 (void) &obuf;
751 #endif
753 obuf = stdout;
754 if (sigsetjmp(pipestop, 1))
755 goto close_pipe;
756 if (pipe) {
757 cp = value("SHELL");
758 if (cp == NULL)
759 cp = SHELL;
760 obuf = Popen(cmd, "w", cp, 1);
761 if (obuf == NULL) {
762 perror(cmd);
763 obuf = stdout;
764 } else {
765 safe_signal(SIGPIPE, brokpipe);
767 } else if (value("interactive") != NULL &&
768 (page || (cp = value("crt")) != NULL)) {
769 nlines = 0;
770 if (!page) {
771 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
772 if ((message[*ip-1].m_have & HAVE_BODY) == 0) {
773 if ((get_body(&message[*ip - 1])) !=
774 OKAY)
775 return 1;
777 nlines += message[*ip - 1].m_lines;
780 if (page || nlines > (*cp ? atoi(cp) : realscreenheight)) {
781 cp = get_pager();
782 obuf = Popen(cp, "w", NULL, 1);
783 if (obuf == NULL) {
784 perror(cp);
785 obuf = stdout;
786 } else
787 safe_signal(SIGPIPE, brokpipe);
790 for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) {
791 mp = &message[*ip - 1];
792 touch(mp);
793 setdot(mp);
794 uncollapse1(mp, 1);
795 if (value("quiet") == NULL)
796 fprintf(obuf, catgets(catd, CATSET, 17,
797 "Message %2d:\n"), *ip);
798 send(mp, obuf, doign ? ignore : 0, NULL,
799 pipe && value("piperaw") ? SEND_MBOX :
800 decode ? SEND_SHOW :
801 doign ? SEND_TODISP : SEND_TODISP_ALL,
802 mstats);
803 if (pipe && value("page")) {
804 putc('\f', obuf);
806 if (tstats) {
807 tstats[0] += mstats[0];
808 tstats[1] += mstats[1];
811 close_pipe:
812 if (obuf != stdout) {
814 * Ignore SIGPIPE so it can't cause a duplicate close.
816 safe_signal(SIGPIPE, SIG_IGN);
817 Pclose(obuf);
818 safe_signal(SIGPIPE, dflpipe);
820 return(0);
824 * Get the last, possibly quoted part of linebuf.
826 char *
827 laststring(char *linebuf, int *flag, int strip)
829 char *cp, *p;
830 char quoted;
832 *flag = 1;
833 cp = strlen(linebuf) + linebuf - 1;
836 * Strip away trailing blanks.
838 while (cp > linebuf && whitechar(*cp & 0377))
839 cp--;
840 *++cp = 0;
841 if (cp == linebuf) {
842 *flag = 0;
843 return NULL;
847 * Now search for the beginning of the command name.
849 quoted = *(cp - 1);
850 if (quoted == '\'' || quoted == '\"') {
851 cp--;
852 if (strip)
853 *cp = '\0';
854 cp--;
855 while (cp > linebuf) {
856 if (*cp != quoted) {
857 cp--;
858 } else if (*(cp - 1) != '\\') {
859 break;
860 } else {
861 p = --cp;
862 do {
863 *p = *(p + 1);
864 } while (*p++);
865 cp--;
868 if (cp == linebuf)
869 *flag = 0;
870 if (*cp == quoted) {
871 if (strip)
872 *cp++ = 0;
873 } else
874 *flag = 0;
875 } else {
876 while (cp > linebuf && !whitechar(*cp & 0377))
877 cp--;
878 if (whitechar(*cp & 0377))
879 *cp++ = 0;
880 else
881 *flag = 0;
883 if (*cp == '\0') {
884 return(NULL);
886 return(cp);
890 * Pipe the messages requested.
892 static int
893 pipe1(char *str, int doign)
895 char *cmd;
896 int f, *msgvec, ret;
897 off_t stats[2];
899 /*LINTED*/
900 msgvec = (int *)salloc((msgCount + 2) * sizeof *msgvec);
901 if ((cmd = laststring(str, &f, 1)) == NULL) {
902 cmd = value("cmd");
903 if (cmd == NULL || *cmd == '\0') {
904 fputs(catgets(catd, CATSET, 16,
905 "variable cmd not set\n"), stderr);
906 return 1;
909 if (!f) {
910 *msgvec = first(0, MMNORM);
911 if (*msgvec == 0) {
912 if (inhook)
913 return 0;
914 puts(catgets(catd, CATSET, 18, "No messages to pipe."));
915 return 1;
917 msgvec[1] = 0;
918 } else if (getmsglist(str, msgvec, 0) < 0)
919 return 1;
920 if (*msgvec == 0) {
921 if (inhook)
922 return 0;
923 printf("No applicable messages.\n");
924 return 1;
926 printf(catgets(catd, CATSET, 268, "Pipe to: \"%s\"\n"), cmd);
927 stats[0] = stats[1] = 0;
928 if ((ret = type1(msgvec, doign, 0, 1, 0, cmd, stats)) == 0) {
929 printf("\"%s\" ", cmd);
930 if (stats[0] >= 0)
931 printf("%lu", (long)stats[0]);
932 else
933 printf(catgets(catd, CATSET, 27, "binary"));
934 printf("/%lu\n", (long)stats[1]);
936 return ret;
940 * Paginate messages, honor ignored fields.
942 int
943 more(void *v)
945 int *msgvec = v;
946 return (type1(msgvec, 1, 1, 0, 0, NULL, NULL));
950 * Paginate messages, even printing ignored fields.
952 int
953 More(void *v)
955 int *msgvec = v;
957 return (type1(msgvec, 0, 1, 0, 0, NULL, NULL));
961 * Type out messages, honor ignored fields.
963 int
964 type(void *v)
966 int *msgvec = v;
968 return(type1(msgvec, 1, 0, 0, 0, NULL, NULL));
972 * Type out messages, even printing ignored fields.
974 int
975 Type(void *v)
977 int *msgvec = v;
979 return(type1(msgvec, 0, 0, 0, 0, NULL, NULL));
983 * Show MIME-encoded message text, including all fields.
986 show(void *v)
988 int *msgvec = v;
990 return(type1(msgvec, 0, 0, 0, 1, NULL, NULL));
994 * Pipe messages, honor ignored fields.
996 int
997 pipecmd(void *v)
999 char *str = v;
1000 return(pipe1(str, 1));
1003 * Pipe messages, not respecting ignored fields.
1005 int
1006 Pipecmd(void *v)
1008 char *str = v;
1009 return(pipe1(str, 0));
1013 * Respond to a broken pipe signal --
1014 * probably caused by quitting more.
1016 /*ARGSUSED*/
1017 void
1018 brokpipe(int signo)
1020 (void)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 dirname[PATHSIZE], *name, *cmd, **argv = v;
1127 if (*argv && (name = expand(*argv)) == NULL)
1128 return (1);
1129 else if (getfold(dirname, sizeof dirname) < 0) {
1130 fprintf(stderr, tr(20, "No value set for \"folder\"\n"));
1131 return (1);
1132 } else
1133 name = dirname;
1135 if (which_protocol(name) == PROTO_IMAP)
1136 imap_folders(name, *argv == NULL);
1137 else {
1138 if ((cmd = value("LISTER")) == NULL)
1139 cmd = "ls";
1140 run_command(cmd, 0, -1, -1, name, NULL, NULL);
1142 return (0);