makeconfig: small tweaks
[s-mailx.git] / cmd1.c
blob64736530b3ee424ecff919d66d3b655412abe2da
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 *volatile 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 = hfield1("subject", mp)) == 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 = hfield1("date", mp)) != NULL)
407 hl.l_date = fakedate(rfctime(cp));
408 if (Iflag) {
409 if ((name = hfieldX("newsgroups", mp)) == NULL)
410 if ((name = hfieldX("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 = hfield1("to", mp)) != NULL) {
418 name = cp;
419 isto = 1;
422 } else {
423 isaddr = 1;
424 if ((name = hfield1("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 && (unsigned)i + 1 == mp->m_younger->m_level) {
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 ((unsigned)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; (unsigned)indent < mp->m_level && indent < maxwidth;
653 ++indent) {
654 if (indent < maxwidth - 1)
655 putuc(us[indent], cs[indent] & 0377, fp);
656 else
657 putuc(0x21B8, '^', fp);
659 ac_free(us);
660 ac_free(cs);
661 return indent;
665 * Print out the header of a specific message.
666 * This is a slight improvement to the standard one.
668 void
669 printhead(int mesg, FILE *f, int threaded)
671 int bsdflags, bsdheadline, sz;
672 char *fmt, attrlist[30], *cp;
674 bsdflags = value("bsdcompat") != NULL || value("bsdflags") != NULL ||
675 getenv("SYSV3") != NULL;
676 strcpy(attrlist, bsdflags ? "NU *HMFATK+-J" : "NUROSPMFATK+-J");
677 if ((cp = value("attrlist")) != NULL) {
678 sz = strlen(cp);
679 if (sz > (int)sizeof attrlist - 1)
680 sz = (int)sizeof attrlist - 1;
681 memcpy(attrlist, cp, sz);
683 bsdheadline = value("bsdcompat") != NULL ||
684 value("bsdheadline") != NULL;
685 if ((fmt = value("headline")) == NULL)
686 fmt = bsdheadline ?
687 "%>%a%m %20f %16d %3l/%-5o %i%S" :
688 "%>%a%m %18f %16d %4l/%-5o %i%s";
689 hprf(fmt, mesg, f, threaded, attrlist);
693 * Print out the value of dot.
695 /*ARGSUSED*/
696 int
697 pdot(void *v)
699 (void)v;
700 printf(catgets(catd, CATSET, 13, "%d\n"),
701 (int)(dot - &message[0] + 1));
702 return(0);
706 * Print out all the possible commands.
708 /*ARGSUSED*/
709 int
710 pcmdlist(void *v)
712 extern const struct cmd cmdtab[];
713 const struct cmd *cp;
714 int cc;
715 (void)v;
717 printf(catgets(catd, CATSET, 14, "Commands are:\n"));
718 for (cc = 0, cp = cmdtab; cp->c_name != NULL; cp++) {
719 cc += strlen(cp->c_name) + 2;
720 if (cc > 72) {
721 printf("\n");
722 cc = strlen(cp->c_name) + 2;
724 if ((cp+1)->c_name != NULL)
725 printf(catgets(catd, CATSET, 15, "%s, "), cp->c_name);
726 else
727 printf("%s\n", cp->c_name);
729 return(0);
733 * Type out the messages requested.
735 static sigjmp_buf pipestop;
737 static int
738 type1(int *msgvec, int doign, int page, int pipe, int decode,
739 char *cmd, off_t *tstats)
741 int *ip;
742 struct message *mp;
743 char *cp;
744 int nlines;
745 off_t mstats[2];
747 * Must be static to become excluded from sigsetjmp().
749 static FILE *obuf;
750 #ifdef __GNUC__
751 /* Avoid longjmp clobbering */
752 (void) &cp;
753 (void) &cmd;
754 (void) &obuf;
755 #endif
757 obuf = stdout;
758 if (sigsetjmp(pipestop, 1))
759 goto close_pipe;
760 if (pipe) {
761 cp = value("SHELL");
762 if (cp == NULL)
763 cp = SHELL;
764 obuf = Popen(cmd, "w", cp, 1);
765 if (obuf == NULL) {
766 perror(cmd);
767 obuf = stdout;
768 } else {
769 safe_signal(SIGPIPE, brokpipe);
771 } else if (value("interactive") != NULL &&
772 (page || (cp = value("crt")) != NULL)) {
773 nlines = 0;
774 if (!page) {
775 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
776 if ((message[*ip-1].m_have & HAVE_BODY) == 0) {
777 if ((get_body(&message[*ip - 1])) !=
778 OKAY)
779 return 1;
781 nlines += message[*ip - 1].m_lines;
784 if (page || nlines > (*cp ? atoi(cp) : realscreenheight)) {
785 cp = get_pager();
786 obuf = Popen(cp, "w", NULL, 1);
787 if (obuf == NULL) {
788 perror(cp);
789 obuf = stdout;
790 } else
791 safe_signal(SIGPIPE, brokpipe);
794 for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) {
795 mp = &message[*ip - 1];
796 touch(mp);
797 setdot(mp);
798 uncollapse1(mp, 1);
799 if (value("quiet") == NULL)
800 fprintf(obuf, catgets(catd, CATSET, 17,
801 "Message %2d:\n"), *ip);
802 send(mp, obuf, doign ? ignore : 0, NULL,
803 pipe && value("piperaw") ? SEND_MBOX :
804 decode ? SEND_SHOW :
805 doign ? SEND_TODISP : SEND_TODISP_ALL,
806 mstats);
807 if (pipe && value("page")) {
808 putc('\f', obuf);
810 if (tstats) {
811 tstats[0] += mstats[0];
812 tstats[1] += mstats[1];
815 close_pipe:
816 if (obuf != stdout) {
818 * Ignore SIGPIPE so it can't cause a duplicate close.
820 safe_signal(SIGPIPE, SIG_IGN);
821 Pclose(obuf);
822 safe_signal(SIGPIPE, dflpipe);
824 return(0);
828 * Get the last, possibly quoted part of linebuf.
830 char *
831 laststring(char *linebuf, int *flag, int strip)
833 char *cp, *p;
834 char quoted;
836 *flag = 1;
837 cp = strlen(linebuf) + linebuf - 1;
840 * Strip away trailing blanks.
842 while (cp > linebuf && whitechar(*cp & 0377))
843 cp--;
844 *++cp = 0;
845 if (cp == linebuf) {
846 *flag = 0;
847 return NULL;
851 * Now search for the beginning of the command name.
853 quoted = *(cp - 1);
854 if (quoted == '\'' || quoted == '\"') {
855 cp--;
856 if (strip)
857 *cp = '\0';
858 cp--;
859 while (cp > linebuf) {
860 if (*cp != quoted) {
861 cp--;
862 } else if (*(cp - 1) != '\\') {
863 break;
864 } else {
865 p = --cp;
866 do {
867 *p = *(p + 1);
868 } while (*p++);
869 cp--;
872 if (cp == linebuf)
873 *flag = 0;
874 if (*cp == quoted) {
875 if (strip)
876 *cp++ = 0;
877 } else
878 *flag = 0;
879 } else {
880 while (cp > linebuf && !whitechar(*cp & 0377))
881 cp--;
882 if (whitechar(*cp & 0377))
883 *cp++ = 0;
884 else
885 *flag = 0;
887 if (*cp == '\0') {
888 return(NULL);
890 return(cp);
894 * Pipe the messages requested.
896 static int
897 pipe1(char *str, int doign)
899 char *cmd;
900 int f, *msgvec, ret;
901 off_t stats[2];
903 /*LINTED*/
904 msgvec = (int *)salloc((msgCount + 2) * sizeof *msgvec);
905 if ((cmd = laststring(str, &f, 1)) == NULL) {
906 cmd = value("cmd");
907 if (cmd == NULL || *cmd == '\0') {
908 fputs(catgets(catd, CATSET, 16,
909 "variable cmd not set\n"), stderr);
910 return 1;
913 if (!f) {
914 *msgvec = first(0, MMNORM);
915 if (*msgvec == 0) {
916 if (inhook)
917 return 0;
918 puts(catgets(catd, CATSET, 18, "No messages to pipe."));
919 return 1;
921 msgvec[1] = 0;
922 } else if (getmsglist(str, msgvec, 0) < 0)
923 return 1;
924 if (*msgvec == 0) {
925 if (inhook)
926 return 0;
927 printf("No applicable messages.\n");
928 return 1;
930 printf(catgets(catd, CATSET, 268, "Pipe to: \"%s\"\n"), cmd);
931 stats[0] = stats[1] = 0;
932 if ((ret = type1(msgvec, doign, 0, 1, 0, cmd, stats)) == 0) {
933 printf("\"%s\" ", cmd);
934 if (stats[0] >= 0)
935 printf("%lu", (long)stats[0]);
936 else
937 printf(catgets(catd, CATSET, 27, "binary"));
938 printf("/%lu\n", (long)stats[1]);
940 return ret;
944 * Paginate messages, honor ignored fields.
946 int
947 more(void *v)
949 int *msgvec = v;
950 return (type1(msgvec, 1, 1, 0, 0, NULL, NULL));
954 * Paginate messages, even printing ignored fields.
956 int
957 More(void *v)
959 int *msgvec = v;
961 return (type1(msgvec, 0, 1, 0, 0, NULL, NULL));
965 * Type out messages, honor ignored fields.
967 int
968 type(void *v)
970 int *msgvec = v;
972 return(type1(msgvec, 1, 0, 0, 0, NULL, NULL));
976 * Type out messages, even printing ignored fields.
978 int
979 Type(void *v)
981 int *msgvec = v;
983 return(type1(msgvec, 0, 0, 0, 0, NULL, NULL));
987 * Show MIME-encoded message text, including all fields.
990 show(void *v)
992 int *msgvec = v;
994 return(type1(msgvec, 0, 0, 0, 1, NULL, NULL));
998 * Pipe messages, honor ignored fields.
1000 int
1001 pipecmd(void *v)
1003 char *str = v;
1004 return(pipe1(str, 1));
1007 * Pipe messages, not respecting ignored fields.
1009 int
1010 Pipecmd(void *v)
1012 char *str = v;
1013 return(pipe1(str, 0));
1017 * Respond to a broken pipe signal --
1018 * probably caused by quitting more.
1020 /*ARGSUSED*/
1021 void
1022 brokpipe(int signo)
1024 (void)signo;
1025 siglongjmp(pipestop, 1);
1029 * Print the top so many lines of each desired message.
1030 * The number of lines is taken from the variable "toplines"
1031 * and defaults to 5.
1033 int
1034 top(void *v)
1036 int *msgvec = v;
1037 int *ip;
1038 struct message *mp;
1039 int c, topl, lines, lineb;
1040 char *valtop, *linebuf = NULL;
1041 size_t linesize;
1042 FILE *ibuf;
1044 topl = 5;
1045 valtop = value("toplines");
1046 if (valtop != NULL) {
1047 topl = atoi(valtop);
1048 if (topl < 0 || topl > 10000)
1049 topl = 5;
1051 lineb = 1;
1052 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
1053 mp = &message[*ip - 1];
1054 touch(mp);
1055 setdot(mp);
1056 did_print_dot = 1;
1057 if (value("quiet") == NULL)
1058 printf(catgets(catd, CATSET, 19,
1059 "Message %2d:\n"), *ip);
1060 if (mp->m_flag & MNOFROM)
1061 printf("From %s %s\n", fakefrom(mp),
1062 fakedate(mp->m_time));
1063 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL) /* XXX could use TOP */
1064 return 1;
1065 c = mp->m_lines;
1066 if (!lineb)
1067 printf("\n");
1068 for (lines = 0; lines < c && lines <= topl; lines++) {
1069 if (readline(ibuf, &linebuf, &linesize) < 0)
1070 break;
1071 puts(linebuf);
1072 lineb = blankline(linebuf);
1075 if (linebuf)
1076 free(linebuf);
1077 return(0);
1081 * Touch all the given messages so that they will
1082 * get mboxed.
1084 int
1085 stouch(void *v)
1087 int *msgvec = v;
1088 int *ip;
1090 for (ip = msgvec; *ip != 0; ip++) {
1091 setdot(&message[*ip-1]);
1092 dot->m_flag |= MTOUCH;
1093 dot->m_flag &= ~MPRESERVE;
1095 * POSIX interpretation necessary.
1097 did_print_dot = 1;
1099 return(0);
1103 * Make sure all passed messages get mboxed.
1105 int
1106 mboxit(void *v)
1108 int *msgvec = v;
1109 int *ip;
1111 for (ip = msgvec; *ip != 0; ip++) {
1112 setdot(&message[*ip-1]);
1113 dot->m_flag |= MTOUCH|MBOX;
1114 dot->m_flag &= ~MPRESERVE;
1116 * POSIX interpretation necessary.
1118 did_print_dot = 1;
1120 return(0);
1124 * List the folders the user currently has.
1126 int
1127 folders(void *v)
1129 char **argv = v;
1130 char dirname[PATHSIZE];
1131 char *cmd, *name;
1133 if (*argv)
1134 name = expand(*argv);
1135 else if (getfold(dirname, sizeof dirname) < 0) {
1136 printf(catgets(catd, CATSET, 20,
1137 "No value set for \"folder\"\n"));
1138 return 1;
1139 } else
1140 name = dirname;
1141 if (which_protocol(name) == PROTO_IMAP)
1142 imap_folders(name, *argv == NULL);
1143 else {
1144 if ((cmd = value("LISTER")) == NULL)
1145 cmd = "ls";
1146 run_command(cmd, 0, -1, -1, name, NULL, NULL);
1148 return 0;