Isolate iconv(3) usage in a single place..
[s-mailx.git] / cmd1.c
blob8185d691d9b4d98a6c76aa2d0ad72c3c783531ac
1 /*
2 * Heirloom mailx - 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 #ifndef lint
41 #ifdef DOSCCS
42 static char sccsid[] = "@(#)cmd1.c 2.97 (gritter) 6/16/07";
43 #endif
44 #endif /* not lint */
46 #include "rcv.h"
47 #include "extern.h"
48 #ifdef HAVE_WCWIDTH
49 #include <wchar.h>
50 #endif
53 * Mail -- a mail program
55 * User commands.
59 * Print the current active headings.
60 * Don't change dot if invoker didn't give an argument.
63 static int screen;
64 static void onpipe(int signo);
65 static int dispc(struct message *mp, const char *a);
66 static int scroll1(char *arg, int onlynew);
67 static void hprf(const char *fmt, int mesg, FILE *f, int threaded,
68 const char *attrlist);
69 static int putindent(FILE *fp, struct message *mp, int maxwidth);
70 static int type1(int *msgvec, int doign, int page, int pipe, int decode,
71 char *cmd, off_t *tstats);
72 static int pipe1(char *str, int doign);
73 void brokpipe(int signo);
75 char *
76 get_pager(void)
78 char *cp;
80 cp = value("PAGER");
81 if (cp == NULL || *cp == '\0')
82 cp = value("bsdcompat") ? "more" : "pg";
83 return cp;
86 int
87 headers(void *v)
89 int *msgvec = v;
90 int g, k, n, mesg, flag = 0, lastg = 1;
91 struct message *mp, *mq, *lastmq = NULL;
92 int size;
93 enum mflag fl = MNEW|MFLAGGED;
95 size = screensize();
96 n = msgvec[0]; /* n == {-2, -1, 0}: called from scroll() */
97 if (screen < 0)
98 screen = 0;
99 k = screen * size;
100 if (k >= msgCount)
101 k = msgCount - size;
102 if (k < 0)
103 k = 0;
104 if (mb.mb_threaded == 0) {
105 g = 0;
106 mq = &message[0];
107 for (mp = &message[0]; mp < &message[msgCount]; mp++)
108 if (visible(mp)) {
109 if (g % size == 0)
110 mq = mp;
111 if (mp->m_flag&fl) {
112 lastg = g;
113 lastmq = mq;
115 if ((n > 0 && mp == &message[n-1]) ||
116 (n == 0 && g == k) ||
117 (n == -2 && g == k + size &&
118 lastmq) ||
119 (n < 0 && g >= k &&
120 (mp->m_flag & fl) != 0))
121 break;
122 g++;
124 if (lastmq && (n==-2 || (n==-1 && mp == &message[msgCount]))) {
125 g = lastg;
126 mq = lastmq;
128 screen = g / size;
129 mp = mq;
130 mesg = mp - &message[0];
131 if (dot != &message[n-1]) {
132 for (mq = mp; mq < &message[msgCount]; mq++)
133 if (visible(mq)) {
134 setdot(mq);
135 break;
138 if (mb.mb_type == MB_IMAP)
139 imap_getheaders(mesg+1, mesg + size);
140 for (; mp < &message[msgCount]; mp++) {
141 mesg++;
142 if (!visible(mp))
143 continue;
144 if (flag++ >= size)
145 break;
146 printhead(mesg, stdout, 0);
148 } else { /* threaded */
149 g = 0;
150 mq = threadroot;
151 for (mp = threadroot; mp; mp = next_in_thread(mp))
152 if (visible(mp) && (mp->m_collapsed <= 0 ||
153 mp == &message[n-1])) {
154 if (g % size == 0)
155 mq = mp;
156 if (mp->m_flag&fl) {
157 lastg = g;
158 lastmq = mq;
160 if ((n > 0 && mp == &message[n-1]) ||
161 (n == 0 && g == k) ||
162 (n == -2 && g == k + size &&
163 lastmq) ||
164 (n < 0 && g >= k &&
165 (mp->m_flag & fl) != 0))
166 break;
167 g++;
169 if (lastmq && (n==-2 || (n==-1 && mp==&message[msgCount]))) {
170 g = lastg;
171 mq = lastmq;
173 screen = g / size;
174 mp = mq;
175 if (dot != &message[n-1]) {
176 for (mq = mp; mq; mq = next_in_thread(mq))
177 if (visible(mq) && mq->m_collapsed <= 0) {
178 setdot(mq);
179 break;
182 while (mp) {
183 if (visible(mp) && (mp->m_collapsed <= 0 ||
184 mp == &message[n-1])) {
185 if (flag++ >= size)
186 break;
187 printhead(mp - &message[0] + 1, stdout,
188 mb.mb_threaded);
190 mp = next_in_thread(mp);
193 if (flag == 0) {
194 printf(catgets(catd, CATSET, 6, "No more mail.\n"));
195 return(1);
197 return(0);
201 * Scroll to the next/previous screen
204 scroll(void *v)
206 return scroll1(v, 0);
210 Scroll(void *v)
212 return scroll1(v, 1);
215 static int
216 scroll1(char *arg, int onlynew)
218 int size;
219 int cur[1];
221 cur[0] = onlynew ? -1 : 0;
222 size = screensize();
223 switch (*arg) {
224 case '1': case '2': case '3': case '4': case '5':
225 case '6': case '7': case '8': case '9': case '0':
226 screen = atoi(arg);
227 goto scroll_forward;
228 case '\0':
229 screen++;
230 goto scroll_forward;
231 case '$':
232 screen = msgCount / size;
233 goto scroll_forward;
234 case '+':
235 if (arg[1] == '\0')
236 screen++;
237 else
238 screen += atoi(arg + 1);
239 scroll_forward:
240 if (screen * size > msgCount) {
241 screen = msgCount / size;
242 printf(catgets(catd, CATSET, 7,
243 "On last screenful of messages\n"));
245 break;
247 case '-':
248 if (arg[1] == '\0')
249 screen--;
250 else
251 screen -= atoi(arg + 1);
252 if (screen < 0) {
253 screen = 0;
254 printf(catgets(catd, CATSET, 8,
255 "On first screenful of messages\n"));
257 if (cur[0] == -1)
258 cur[0] = -2;
259 break;
261 default:
262 printf(catgets(catd, CATSET, 9,
263 "Unrecognized scrolling command \"%s\"\n"), arg);
264 return(1);
266 return(headers(cur));
270 * Compute screen size.
272 int
273 screensize(void)
275 int s;
276 char *cp;
278 if ((cp = value("screen")) != NULL && (s = atoi(cp)) > 0)
279 return s;
280 return scrnheight - 4;
283 static sigjmp_buf pipejmp;
285 /*ARGSUSED*/
286 static void
287 onpipe(int signo)
289 (void)signo;
290 siglongjmp(pipejmp, 1);
294 * Print out the headlines for each message
295 * in the passed message list.
297 int
298 from(void *v)
300 int *msgvec = v;
301 int *ip, n;
302 FILE *obuf = stdout;
303 char *cp;
305 if (is_a_tty[0] && is_a_tty[1] && (cp = value("crt")) != NULL) {
306 for (n = 0, ip = msgvec; *ip; ip++)
307 n++;
308 if (n > (*cp == '\0' ? screensize() : atoi(cp)) + 3) {
309 cp = get_pager();
310 /* TODO should be below the Popen?
311 * TODO Problem: Popen doesn't encapsulate it,
312 * TODO may leave child running if fdopen() fails!
313 * TODO even more such stuff in this file! */
314 if (sigsetjmp(pipejmp, 1))
315 goto endpipe;
316 if ((obuf = Popen(cp, "w", NULL, 1)) == NULL) {
317 perror(cp);
318 obuf = stdout;
319 } else
320 safe_signal(SIGPIPE, onpipe);
323 for (ip = msgvec; *ip != 0; ip++)
324 printhead(*ip, obuf, mb.mb_threaded);
325 if (--ip >= msgvec)
326 setdot(&message[*ip - 1]);
327 endpipe:
328 if (obuf != stdout) {
329 safe_signal(SIGPIPE, SIG_IGN);
330 Pclose(obuf);
331 safe_signal(SIGPIPE, dflpipe);
333 return(0);
336 static int
337 dispc(struct message *mp, const char *a)
339 int dispc = ' ';
342 * Bletch!
344 if ((mp->m_flag & (MREAD|MNEW)) == MREAD)
345 dispc = a[3];
346 if ((mp->m_flag & (MREAD|MNEW)) == (MREAD|MNEW))
347 dispc = a[2];
348 if (mp->m_flag & MANSWERED)
349 dispc = a[8];
350 if (mp->m_flag & MDRAFTED)
351 dispc = a[9];
352 if ((mp->m_flag & (MREAD|MNEW)) == MNEW)
353 dispc = a[0];
354 if ((mp->m_flag & (MREAD|MNEW)) == 0)
355 dispc = a[1];
356 if (mp->m_flag & MJUNK)
357 dispc = a[13];
358 if (mp->m_flag & MSAVED)
359 dispc = a[4];
360 if (mp->m_flag & MPRESERVE)
361 dispc = a[5];
362 if (mp->m_flag & (MBOX|MBOXED))
363 dispc = a[6];
364 if (mp->m_flag & MFLAGGED)
365 dispc = a[7];
366 if (mp->m_flag & MKILL)
367 dispc = a[10];
368 if (mb.mb_threaded == 1 && mp->m_collapsed > 0)
369 dispc = a[12];
370 if (mb.mb_threaded == 1 && mp->m_collapsed < 0)
371 dispc = a[11];
372 return dispc;
375 static void
376 hprf(const char *fmt, int mesg, FILE *f, int threaded, const char *attrlist)
378 struct message *mp = &message[mesg-1];
379 char *headline = NULL, *subjline, *name, *cp, *pbuf = NULL;
380 struct headline hl;
381 size_t headsize = 0;
382 const char *fp;
383 int B, c, i, n, s;
384 int headlen = 0;
385 struct str in, out;
386 int subjlen = scrnwidth, fromlen, isto = 0, isaddr = 0;
387 FILE *ibuf;
389 if ((mp->m_flag & MNOFROM) == 0) {
390 if ((ibuf = setinput(&mb, mp, NEED_HEADER)) == NULL)
391 return;
392 if ((headlen = readline(ibuf, &headline, &headsize)) < 0)
393 return;
395 if ((subjline = hfield("subject", mp)) == NULL)
396 subjline = hfield("subj", mp);
397 if (subjline == NULL) {
398 out.s = NULL;
399 out.l = 0;
400 } else {
401 in.s = subjline;
402 in.l = strlen(subjline);
403 mime_fromhdr(&in, &out, TD_ICONV | TD_ISPR);
404 subjline = out.s;
406 if ((mp->m_flag & MNOFROM) == 0) {
407 pbuf = ac_alloc(headlen + 1);
408 parse(headline, headlen, &hl, pbuf);
409 } else {
410 hl.l_from = /*fakefrom(mp);*/NULL;
411 hl.l_tty = NULL;
412 hl.l_date = fakedate(mp->m_time);
414 if (value("datefield") && (cp = hfield("date", mp)) != NULL)
415 hl.l_date = fakedate(rfctime(cp));
416 if (Iflag) {
417 if ((name = hfield("newsgroups", mp)) == NULL)
418 if ((name = hfield("article-id", mp)) == NULL)
419 name = "<>";
420 name = prstr(name);
421 } else if (value("show-rcpt") == NULL) {
422 name = name1(mp, 0);
423 isaddr = 1;
424 if (value("showto") && name && is_myname(skin(name))) {
425 if ((cp = hfield("to", mp)) != NULL) {
426 name = cp;
427 isto = 1;
430 } else {
431 isaddr = 1;
432 if ((name = hfield("to", mp)) != NULL)
433 isto = 1;
435 if (name == NULL) {
436 name = "";
437 isaddr = 0;
439 if (isaddr) {
440 if (value("showname"))
441 name = realname(name);
442 else {
443 name = prstr(skin(name));
446 for (fp = fmt; *fp; fp++) {
447 if (*fp == '%') {
448 if (*++fp == '-') {
449 fp++;
450 } else if (*fp == '+')
451 fp++;
452 while (digitchar(*fp&0377))
453 fp++;
454 if (*fp == '\0')
455 break;
456 } else {
457 #if defined (HAVE_MBTOWC) && defined (HAVE_WCWIDTH)
458 if (mb_cur_max > 1) {
459 wchar_t wc;
460 if ((s = mbtowc(&wc, fp, mb_cur_max)) < 0)
461 n = s = 1;
462 else {
463 if ((n = wcwidth(wc)) < 0)
464 n = 1;
466 } else
467 #endif /* HAVE_MBTOWC && HAVE_WCWIDTH */
469 n = s = 1;
471 subjlen -= n;
472 while (--s > 0)
473 fp++;
476 for (fp = fmt; *fp; fp++) {
477 if (*fp == '%') {
478 B = 0;
479 n = 0;
480 s = 1;
481 if (*++fp == '-') {
482 s = -1;
483 fp++;
484 } else if (*fp == '+')
485 fp++;
486 if (digitchar(*fp&0377)) {
488 n = 10*n + *fp - '0';
489 while (fp++, digitchar(*fp&0377));
491 if (*fp == '\0')
492 break;
493 n *= s;
494 switch (*fp) {
495 case '%':
496 putc('%', f);
497 subjlen--;
498 break;
499 case '>':
500 case '<':
501 c = dot == mp ? *fp&0377 : ' ';
502 putc(c, f);
503 subjlen--;
504 break;
505 case 'a':
506 c = dispc(mp, attrlist);
507 putc(c, f);
508 subjlen--;
509 break;
510 case 'm':
511 if (n == 0) {
512 n = 3;
513 if (threaded)
514 for (i=msgCount; i>999; i/=10)
515 n++;
517 subjlen -= fprintf(f, "%*d", n, mesg);
518 break;
519 case 'f':
520 if (n <= 0)
521 n = 18;
522 fromlen = n;
523 if (isto)
524 fromlen -= 3;
525 fprintf(f, "%s%s", isto ? "To " : "",
526 colalign(name, fromlen, 1));
527 subjlen -= n;
528 break;
529 case 'd':
530 if (n <= 0)
531 n = 16;
532 subjlen -= fprintf(f, "%*.*s", n, n, hl.l_date);
533 break;
534 case 'l':
535 if (n == 0)
536 n = 4;
537 if (mp->m_xlines)
538 subjlen -= fprintf(f, "%*ld", n,
539 mp->m_xlines);
540 else {
541 subjlen -= n;
542 while (n--)
543 putc(' ', f);
545 break;
546 case 'o':
547 if (n == 0)
548 n = -5;
549 subjlen -= fprintf(f, "%*lu", n,
550 (long)mp->m_xsize);
551 break;
552 case 'i':
553 if (threaded)
554 subjlen -= putindent(f, mp,
555 scrnwidth - 60);
556 break;
557 case 'S':
558 B = 1;
559 /*FALLTHRU*/
560 case 's':
561 n = n>0 ? n : subjlen - 2;
562 if (B)
563 n -= 2;
564 if (subjline != NULL && n >= 0) {
565 /* pretty pathetic */
566 fprintf(f, B ? "\"%s\"" : "%s",
567 colalign(subjline, n, 0));
569 break;
570 case 'U':
571 if (n == 0)
572 n = 9;
573 subjlen -= fprintf(f, "%*lu", n, mp->m_uid);
574 break;
575 case 'e':
576 if (n == 0)
577 n = 2;
578 subjlen -= fprintf(f, "%*u", n, threaded == 1 ?
579 mp->m_level : 0);
580 break;
581 case 't':
582 if (n == 0) {
583 n = 3;
584 if (threaded)
585 for (i=msgCount; i>999; i/=10)
586 n++;
588 fprintf(f, "%*ld", n, threaded ?
589 mp->m_threadpos : mesg);
590 subjlen -= n;
591 break;
592 case 'c':
593 if (n == 0)
594 n = 6;
595 subjlen -= fprintf(f, "%*g", n, mp->m_score);
596 break;
598 } else
599 putc(*fp&0377, f);
601 putc('\n', f);
602 if (out.s)
603 free(out.s);
604 if (headline)
605 free(headline);
606 if (pbuf)
607 ac_free(pbuf);
611 * Print out the indenting in threaded display.
613 static int
614 putindent(FILE *fp, struct message *mp, int maxwidth)
616 struct message *mq;
617 int indent, i;
618 int *us;
619 char *cs;
620 int important = MNEW|MFLAGGED;
622 if (mp->m_level == 0)
623 return 0;
624 cs = ac_alloc(mp->m_level);
625 us = ac_alloc(mp->m_level * sizeof *us);
626 i = mp->m_level - 1;
627 if (mp->m_younger && (unsigned)i + 1 == mp->m_younger->m_level) {
628 if (mp->m_parent && mp->m_parent->m_flag & important)
629 us[i] = mp->m_flag & important ? 0x2523 : 0x2520;
630 else
631 us[i] = mp->m_flag & important ? 0x251D : 0x251C;
632 cs[i] = '+';
633 } else {
634 if (mp->m_parent && mp->m_parent->m_flag & important)
635 us[i] = mp->m_flag & important ? 0x2517 : 0x2516;
636 else
637 us[i] = mp->m_flag & important ? 0x2515 : 0x2514;
638 cs[i] = '\\';
640 mq = mp->m_parent;
641 for (i = mp->m_level - 2; i >= 0; i--) {
642 if (mq) {
643 if ((unsigned)i > mq->m_level - 1) {
644 us[i] = cs[i] = ' ';
645 continue;
647 if (mq->m_younger) {
648 if (mq->m_parent &&
649 mq->m_parent->m_flag&important)
650 us[i] = 0x2503;
651 else
652 us[i] = 0x2502;
653 cs[i] = '|';
654 } else
655 us[i] = cs[i] = ' ';
656 mq = mq->m_parent;
657 } else
658 us[i] = cs[i] = ' ';
660 for (indent = 0; (unsigned)indent < mp->m_level && indent < maxwidth;
661 ++indent) {
662 if (indent < maxwidth - 1)
663 putuc(us[indent], cs[indent] & 0377, fp);
664 else
665 putuc(0x21B8, '^', fp);
667 ac_free(us);
668 ac_free(cs);
669 return indent;
673 * Print out the header of a specific message.
674 * This is a slight improvement to the standard one.
676 void
677 printhead(int mesg, FILE *f, int threaded)
679 int bsdflags, bsdheadline, sz;
680 char *fmt, attrlist[30], *cp;
682 bsdflags = value("bsdcompat") != NULL || value("bsdflags") != NULL ||
683 getenv("SYSV3") != NULL;
684 strcpy(attrlist, bsdflags ? "NU *HMFATK+-J" : "NUROSPMFATK+-J");
685 if ((cp = value("attrlist")) != NULL) {
686 sz = strlen(cp);
687 if (sz > (int)sizeof attrlist - 1)
688 sz = (int)sizeof attrlist - 1;
689 memcpy(attrlist, cp, sz);
691 bsdheadline = value("bsdcompat") != NULL ||
692 value("bsdheadline") != NULL;
693 if ((fmt = value("headline")) == NULL)
694 fmt = bsdheadline ?
695 "%>%a%m %20f %16d %3l/%-5o %i%S" :
696 "%>%a%m %18f %16d %4l/%-5o %i%s";
697 hprf(fmt, mesg, f, threaded, attrlist);
701 * Print out the value of dot.
703 /*ARGSUSED*/
704 int
705 pdot(void *v)
707 (void)v;
708 printf(catgets(catd, CATSET, 13, "%d\n"),
709 (int)(dot - &message[0] + 1));
710 return(0);
714 * Print out all the possible commands.
716 /*ARGSUSED*/
717 int
718 pcmdlist(void *v)
720 extern const struct cmd cmdtab[];
721 const struct cmd *cp;
722 int cc;
723 (void)v;
725 printf(catgets(catd, CATSET, 14, "Commands are:\n"));
726 for (cc = 0, cp = cmdtab; cp->c_name != NULL; cp++) {
727 cc += strlen(cp->c_name) + 2;
728 if (cc > 72) {
729 printf("\n");
730 cc = strlen(cp->c_name) + 2;
732 if ((cp+1)->c_name != NULL)
733 printf(catgets(catd, CATSET, 15, "%s, "), cp->c_name);
734 else
735 printf("%s\n", cp->c_name);
737 return(0);
741 * Type out the messages requested.
743 static sigjmp_buf pipestop;
745 static int
746 type1(int *msgvec, int doign, int page, int pipe, int decode,
747 char *cmd, off_t *tstats)
749 int *ip;
750 struct message *mp;
751 char *cp;
752 int nlines;
753 off_t mstats[2];
755 * Must be static to become excluded from sigsetjmp().
757 static FILE *obuf;
758 #ifdef __GNUC__
759 /* Avoid longjmp clobbering */
760 (void) &cp;
761 (void) &cmd;
762 (void) &obuf;
763 #endif
765 obuf = stdout;
766 if (sigsetjmp(pipestop, 1))
767 goto close_pipe;
768 if (pipe) {
769 cp = value("SHELL");
770 if (cp == NULL)
771 cp = SHELL;
772 obuf = Popen(cmd, "w", cp, 1);
773 if (obuf == NULL) {
774 perror(cmd);
775 obuf = stdout;
776 } else {
777 safe_signal(SIGPIPE, brokpipe);
779 } else if (value("interactive") != NULL &&
780 (page || (cp = value("crt")) != NULL)) {
781 nlines = 0;
782 if (!page) {
783 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
784 if ((message[*ip-1].m_have & HAVE_BODY) == 0) {
785 if ((get_body(&message[*ip - 1])) !=
786 OKAY)
787 return 1;
789 nlines += message[*ip - 1].m_lines;
792 if (page || nlines > (*cp ? atoi(cp) : realscreenheight)) {
793 cp = get_pager();
794 obuf = Popen(cp, "w", NULL, 1);
795 if (obuf == NULL) {
796 perror(cp);
797 obuf = stdout;
798 } else
799 safe_signal(SIGPIPE, brokpipe);
802 for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) {
803 mp = &message[*ip - 1];
804 touch(mp);
805 setdot(mp);
806 uncollapse1(mp, 1);
807 if (value("quiet") == NULL)
808 fprintf(obuf, catgets(catd, CATSET, 17,
809 "Message %2d:\n"), *ip);
810 send(mp, obuf, doign ? ignore : 0, NULL,
811 pipe && value("piperaw") ? SEND_MBOX :
812 decode ? SEND_SHOW :
813 doign ? SEND_TODISP : SEND_TODISP_ALL,
814 mstats);
815 if (pipe && value("page")) {
816 putc('\f', obuf);
818 if (tstats) {
819 tstats[0] += mstats[0];
820 tstats[1] += mstats[1];
823 close_pipe:
824 if (obuf != stdout) {
826 * Ignore SIGPIPE so it can't cause a duplicate close.
828 safe_signal(SIGPIPE, SIG_IGN);
829 Pclose(obuf);
830 safe_signal(SIGPIPE, dflpipe);
832 return(0);
836 * Get the last, possibly quoted part of linebuf.
838 char *
839 laststring(char *linebuf, int *flag, int strip)
841 char *cp, *p;
842 char quoted;
844 *flag = 1;
845 cp = strlen(linebuf) + linebuf - 1;
848 * Strip away trailing blanks.
850 while (cp > linebuf && whitechar(*cp & 0377))
851 cp--;
852 *++cp = 0;
853 if (cp == linebuf) {
854 *flag = 0;
855 return NULL;
859 * Now search for the beginning of the command name.
861 quoted = *(cp - 1);
862 if (quoted == '\'' || quoted == '\"') {
863 cp--;
864 if (strip)
865 *cp = '\0';
866 cp--;
867 while (cp > linebuf) {
868 if (*cp != quoted) {
869 cp--;
870 } else if (*(cp - 1) != '\\') {
871 break;
872 } else {
873 p = --cp;
874 do {
875 *p = *(p + 1);
876 } while (*p++);
877 cp--;
880 if (cp == linebuf)
881 *flag = 0;
882 if (*cp == quoted) {
883 if (strip)
884 *cp++ = 0;
885 } else
886 *flag = 0;
887 } else {
888 while (cp > linebuf && !whitechar(*cp & 0377))
889 cp--;
890 if (whitechar(*cp & 0377))
891 *cp++ = 0;
892 else
893 *flag = 0;
895 if (*cp == '\0') {
896 return(NULL);
898 return(cp);
902 * Pipe the messages requested.
904 static int
905 pipe1(char *str, int doign)
907 char *cmd;
908 int f, *msgvec, ret;
909 off_t stats[2];
911 /*LINTED*/
912 msgvec = (int *)salloc((msgCount + 2) * sizeof *msgvec);
913 if ((cmd = laststring(str, &f, 1)) == NULL) {
914 cmd = value("cmd");
915 if (cmd == NULL || *cmd == '\0') {
916 fputs(catgets(catd, CATSET, 16,
917 "variable cmd not set\n"), stderr);
918 return 1;
921 if (!f) {
922 *msgvec = first(0, MMNORM);
923 if (*msgvec == 0) {
924 if (inhook)
925 return 0;
926 puts(catgets(catd, CATSET, 18, "No messages to pipe."));
927 return 1;
929 msgvec[1] = 0;
930 } else if (getmsglist(str, msgvec, 0) < 0)
931 return 1;
932 if (*msgvec == 0) {
933 if (inhook)
934 return 0;
935 printf("No applicable messages.\n");
936 return 1;
938 printf(catgets(catd, CATSET, 268, "Pipe to: \"%s\"\n"), cmd);
939 stats[0] = stats[1] = 0;
940 if ((ret = type1(msgvec, doign, 0, 1, 0, cmd, stats)) == 0) {
941 printf("\"%s\" ", cmd);
942 if (stats[0] >= 0)
943 printf("%lu", (long)stats[0]);
944 else
945 printf(catgets(catd, CATSET, 27, "binary"));
946 printf("/%lu\n", (long)stats[1]);
948 return ret;
952 * Paginate messages, honor ignored fields.
954 int
955 more(void *v)
957 int *msgvec = v;
958 return (type1(msgvec, 1, 1, 0, 0, NULL, NULL));
962 * Paginate messages, even printing ignored fields.
964 int
965 More(void *v)
967 int *msgvec = v;
969 return (type1(msgvec, 0, 1, 0, 0, NULL, NULL));
973 * Type out messages, honor ignored fields.
975 int
976 type(void *v)
978 int *msgvec = v;
980 return(type1(msgvec, 1, 0, 0, 0, NULL, NULL));
984 * Type out messages, even printing ignored fields.
986 int
987 Type(void *v)
989 int *msgvec = v;
991 return(type1(msgvec, 0, 0, 0, 0, NULL, NULL));
995 * Show MIME-encoded message text, including all fields.
998 show(void *v)
1000 int *msgvec = v;
1002 return(type1(msgvec, 0, 0, 0, 1, NULL, NULL));
1006 * Pipe messages, honor ignored fields.
1008 int
1009 pipecmd(void *v)
1011 char *str = v;
1012 return(pipe1(str, 1));
1015 * Pipe messages, not respecting ignored fields.
1017 int
1018 Pipecmd(void *v)
1020 char *str = v;
1021 return(pipe1(str, 0));
1025 * Respond to a broken pipe signal --
1026 * probably caused by quitting more.
1028 /*ARGSUSED*/
1029 void
1030 brokpipe(int signo)
1032 (void)signo;
1033 siglongjmp(pipestop, 1);
1037 * Print the top so many lines of each desired message.
1038 * The number of lines is taken from the variable "toplines"
1039 * and defaults to 5.
1041 int
1042 top(void *v)
1044 int *msgvec = v;
1045 int *ip;
1046 struct message *mp;
1047 int c, topl, lines, lineb;
1048 char *valtop, *linebuf = NULL;
1049 size_t linesize;
1050 FILE *ibuf;
1052 topl = 5;
1053 valtop = value("toplines");
1054 if (valtop != NULL) {
1055 topl = atoi(valtop);
1056 if (topl < 0 || topl > 10000)
1057 topl = 5;
1059 lineb = 1;
1060 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
1061 mp = &message[*ip - 1];
1062 touch(mp);
1063 setdot(mp);
1064 did_print_dot = 1;
1065 if (value("quiet") == NULL)
1066 printf(catgets(catd, CATSET, 19,
1067 "Message %2d:\n"), *ip);
1068 if (mp->m_flag & MNOFROM)
1069 printf("From %s %s\n", fakefrom(mp),
1070 fakedate(mp->m_time));
1071 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL) /* XXX could use TOP */
1072 return 1;
1073 c = mp->m_lines;
1074 if (!lineb)
1075 printf("\n");
1076 for (lines = 0; lines < c && lines <= topl; lines++) {
1077 if (readline(ibuf, &linebuf, &linesize) < 0)
1078 break;
1079 puts(linebuf);
1080 lineb = blankline(linebuf);
1083 if (linebuf)
1084 free(linebuf);
1085 return(0);
1089 * Touch all the given messages so that they will
1090 * get mboxed.
1092 int
1093 stouch(void *v)
1095 int *msgvec = v;
1096 int *ip;
1098 for (ip = msgvec; *ip != 0; ip++) {
1099 setdot(&message[*ip-1]);
1100 dot->m_flag |= MTOUCH;
1101 dot->m_flag &= ~MPRESERVE;
1103 * POSIX interpretation necessary.
1105 did_print_dot = 1;
1107 return(0);
1111 * Make sure all passed messages get mboxed.
1113 int
1114 mboxit(void *v)
1116 int *msgvec = v;
1117 int *ip;
1119 for (ip = msgvec; *ip != 0; ip++) {
1120 setdot(&message[*ip-1]);
1121 dot->m_flag |= MTOUCH|MBOX;
1122 dot->m_flag &= ~MPRESERVE;
1124 * POSIX interpretation necessary.
1126 did_print_dot = 1;
1128 return(0);
1132 * List the folders the user currently has.
1134 int
1135 folders(void *v)
1137 char **argv = v;
1138 char dirname[PATHSIZE];
1139 char *cmd, *name;
1141 if (*argv)
1142 name = expand(*argv);
1143 else if (getfold(dirname, sizeof dirname) < 0) {
1144 printf(catgets(catd, CATSET, 20,
1145 "No value set for \"folder\"\n"));
1146 return 1;
1147 } else
1148 name = dirname;
1149 if (which_protocol(name) == PROTO_IMAP)
1150 imap_folders(name, *argv == NULL);
1151 else {
1152 if ((cmd = value("LISTER")) == NULL)
1153 cmd = "ls";
1154 run_command(cmd, 0, -1, -1, name, NULL, NULL);
1156 return 0;