Rename occurrences of USE_ to HAVE_ (again)
[s-mailx.git] / cmd1.c
blob3dcc56e07c1f88691babf64b621bcdeeebbdd820
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ User commands.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2013 Steffen "Daode" Nurpmeso <sdaoden@users.sf.net>.
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"
42 #ifdef HAVE_WCWIDTH
43 # include <wchar.h>
44 #endif
46 #include "extern.h"
49 * Print the current active headings.
50 * Don't change dot if invoker didn't give an argument.
53 static int screen;
55 /* Prepare and print "[Message: xy]:" intro */
56 static void _show_msg_overview(struct message *mp, int msg_no, FILE *obuf);
58 static void onpipe(int signo);
59 static int dispc(struct message *mp, const char *a);
60 static int scroll1(char *arg, int onlynew);
62 static void _parse_head(struct message *mp, char date[FROM_DATEBUF]);
63 static void hprf(const char *fmt, int mesg, FILE *f, int threaded,
64 const char *attrlist);
65 static int putindent(FILE *fp, struct message *mp, int maxwidth);
66 static int type1(int *msgvec, int doign, int page, int pipe, int decode,
67 char *cmd, off_t *tstats);
68 static int pipe1(char *str, int doign);
70 static void
71 _show_msg_overview(struct message *mp, int msg_no, FILE *obuf)
73 fprintf(obuf, tr(17, "[-- Message %2d -- %lu lines, %lu bytes --]:\n"),
74 msg_no, (ul_it)mp->m_lines, (ul_it)mp->m_size);
77 int
78 ccmdnotsupp(void *v)
80 (void)v;
81 fprintf(stderr, tr(10, "The requested feature is not compiled in\n"));
82 return (1);
85 char const *
86 get_pager(void)
88 char const *cp;
90 cp = value("PAGER");
91 if (cp == NULL || *cp == '\0')
92 cp = PAGER;
93 return cp;
96 int
97 headers(void *v)
99 int *msgvec = v;
100 int g, k, n, mesg, flag = 0, lastg = 1;
101 struct message *mp, *mq, *lastmq = NULL;
102 int size;
103 enum mflag fl = MNEW|MFLAGGED;
105 time_current_update(&time_current, FAL0);
107 size = screensize();
108 n = msgvec[0]; /* n == {-2, -1, 0}: called from scroll() */
109 if (screen < 0)
110 screen = 0;
111 k = screen * size;
112 if (k >= msgCount)
113 k = msgCount - size;
114 if (k < 0)
115 k = 0;
116 if (mb.mb_threaded == 0) {
117 g = 0;
118 mq = &message[0];
119 for (mp = &message[0]; mp < &message[msgCount]; mp++)
120 if (visible(mp)) {
121 if (g % size == 0)
122 mq = mp;
123 if (mp->m_flag&fl) {
124 lastg = g;
125 lastmq = mq;
127 if ((n > 0 && mp == &message[n-1]) ||
128 (n == 0 && g == k) ||
129 (n == -2 && g == k + size &&
130 lastmq) ||
131 (n < 0 && g >= k &&
132 (mp->m_flag & fl) != 0))
133 break;
134 g++;
136 if (lastmq && (n==-2 || (n==-1 && mp == &message[msgCount]))) {
137 g = lastg;
138 mq = lastmq;
140 screen = g / size;
141 mp = mq;
142 mesg = mp - &message[0];
143 if (dot != &message[n-1]) {
144 for (mq = mp; mq < &message[msgCount]; mq++)
145 if (visible(mq)) {
146 setdot(mq);
147 break;
150 #ifdef HAVE_IMAP
151 if (mb.mb_type == MB_IMAP)
152 imap_getheaders(mesg+1, mesg + size);
153 #endif
154 for (; mp < &message[msgCount]; mp++) {
155 mesg++;
156 if (!visible(mp))
157 continue;
158 if (flag++ >= size)
159 break;
160 printhead(mesg, stdout, 0);
162 } else { /* threaded */
163 g = 0;
164 mq = threadroot;
165 for (mp = threadroot; mp; mp = next_in_thread(mp))
166 if (visible(mp) && (mp->m_collapsed <= 0 ||
167 mp == &message[n-1])) {
168 if (g % size == 0)
169 mq = mp;
170 if (mp->m_flag&fl) {
171 lastg = g;
172 lastmq = mq;
174 if ((n > 0 && mp == &message[n-1]) ||
175 (n == 0 && g == k) ||
176 (n == -2 && g == k + size &&
177 lastmq) ||
178 (n < 0 && g >= k &&
179 (mp->m_flag & fl) != 0))
180 break;
181 g++;
183 if (lastmq && (n==-2 || (n==-1 && mp==&message[msgCount]))) {
184 g = lastg;
185 mq = lastmq;
187 screen = g / size;
188 mp = mq;
189 if (dot != &message[n-1]) {
190 for (mq = mp; mq; mq = next_in_thread(mq))
191 if (visible(mq) && mq->m_collapsed <= 0) {
192 setdot(mq);
193 break;
196 while (mp) {
197 if (visible(mp) && (mp->m_collapsed <= 0 ||
198 mp == &message[n-1])) {
199 if (flag++ >= size)
200 break;
201 printhead(mp - &message[0] + 1, stdout,
202 mb.mb_threaded);
204 mp = next_in_thread(mp);
207 if (flag == 0) {
208 printf(tr(6, "No more mail.\n"));
209 return(1);
211 return(0);
215 * Scroll to the next/previous screen
218 scroll(void *v)
220 return scroll1(v, 0);
224 Scroll(void *v)
226 return scroll1(v, 1);
229 static int
230 scroll1(char *arg, int onlynew)
232 int size;
233 int cur[1];
235 cur[0] = onlynew ? -1 : 0;
236 size = screensize();
237 switch (*arg) {
238 case '1': case '2': case '3': case '4': case '5':
239 case '6': case '7': case '8': case '9': case '0':
240 screen = atoi(arg);
241 goto scroll_forward;
242 case '\0':
243 screen++;
244 goto scroll_forward;
245 case '$':
246 screen = msgCount / size;
247 goto scroll_forward;
248 case '+':
249 if (arg[1] == '\0')
250 screen++;
251 else
252 screen += atoi(arg + 1);
253 scroll_forward:
254 if (screen * size > msgCount) {
255 screen = msgCount / size;
256 printf(catgets(catd, CATSET, 7,
257 "On last screenful of messages\n"));
259 break;
261 case '-':
262 if (arg[1] == '\0')
263 screen--;
264 else
265 screen -= atoi(arg + 1);
266 if (screen < 0) {
267 screen = 0;
268 printf(catgets(catd, CATSET, 8,
269 "On first screenful of messages\n"));
271 if (cur[0] == -1)
272 cur[0] = -2;
273 break;
275 default:
276 printf(catgets(catd, CATSET, 9,
277 "Unrecognized scrolling command \"%s\"\n"), arg);
278 return(1);
280 return(headers(cur));
284 * Compute screen size.
286 int
287 screensize(void)
289 int s;
290 char *cp;
292 if ((cp = value("screen")) != NULL && (s = atoi(cp)) > 0)
293 return s;
294 return scrnheight - 4;
297 static sigjmp_buf pipejmp;
299 /*ARGSUSED*/
300 static void
301 onpipe(int signo)
303 (void)signo;
304 siglongjmp(pipejmp, 1);
308 * Print out the headlines for each message
309 * in the passed message list.
311 int
312 from(void *v)
314 int *msgvec = v, *ip, n;
315 char *cp;
316 FILE *volatile obuf = stdout;
318 time_current_update(&time_current, FAL0);
320 /* TODO unfixable memory leaks still */
321 if (IS_TTY_SESSION() && (cp = value("crt")) != NULL) {
322 for (n = 0, ip = msgvec; *ip; ip++)
323 n++;
324 if (n > (*cp == '\0' ? screensize() : atoi((char*)cp)) + 3) {
325 char const *p;
326 if (sigsetjmp(pipejmp, 1))
327 goto endpipe;
328 p = get_pager();
329 if ((obuf = Popen(p, "w", NULL, 1)) == NULL) {
330 perror(p);
331 obuf = stdout;
332 cp=NULL;
333 } else
334 safe_signal(SIGPIPE, onpipe);
337 for (ip = msgvec; *ip != 0; ip++)
338 printhead(*ip, obuf, mb.mb_threaded);
339 if (--ip >= msgvec)
340 setdot(&message[*ip - 1]);
341 endpipe:
342 if (obuf != stdout) {
343 safe_signal(SIGPIPE, SIG_IGN);
344 Pclose(obuf, TRU1);
345 safe_signal(SIGPIPE, dflpipe);
347 return(0);
350 static int
351 dispc(struct message *mp, const char *a)
353 int dispc = ' ';
356 * Bletch!
358 if ((mp->m_flag & (MREAD|MNEW)) == MREAD)
359 dispc = a[3];
360 if ((mp->m_flag & (MREAD|MNEW)) == (MREAD|MNEW))
361 dispc = a[2];
362 if (mp->m_flag & MANSWERED)
363 dispc = a[8];
364 if (mp->m_flag & MDRAFTED)
365 dispc = a[9];
366 if ((mp->m_flag & (MREAD|MNEW)) == MNEW)
367 dispc = a[0];
368 if ((mp->m_flag & (MREAD|MNEW)) == 0)
369 dispc = a[1];
370 if (mp->m_flag & MSPAM)
371 dispc = a[12];
372 if (mp->m_flag & MSAVED)
373 dispc = a[4];
374 if (mp->m_flag & MPRESERVE)
375 dispc = a[5];
376 if (mp->m_flag & (MBOX|MBOXED))
377 dispc = a[6];
378 if (mp->m_flag & MFLAGGED)
379 dispc = a[7];
380 if (mb.mb_threaded == 1 && mp->m_collapsed > 0)
381 dispc = a[11];
382 if (mb.mb_threaded == 1 && mp->m_collapsed < 0)
383 dispc = a[10];
384 return dispc;
387 static void
388 _parse_head(struct message *mp, char date[FROM_DATEBUF])
390 FILE *ibuf;
391 int hlen;
392 char *hline = NULL;
393 size_t hsize = 0;
395 if ((ibuf = setinput(&mb, mp, NEED_HEADER)) != NULL &&
396 (hlen = readline_restart(ibuf, &hline, &hsize, 0)) > 0)
397 (void)extract_date_from_from_(hline, hlen, date);
398 if (hline != NULL)
399 free(hline);
402 static void
403 hprf(const char *fmt, int mesg, FILE *f, int threaded, const char *attrlist)
405 char datebuf[FROM_DATEBUF], *subjline, *cp;
406 struct str in, out;
407 char const *datefmt, *date, *name, *fp;
408 int B, c, i, n, s, fromlen, subjlen = scrnwidth, isto = 0, isaddr = 0;
409 struct message *mp = &message[mesg - 1];
410 time_t datet = mp->m_time;
412 date = NULL;
413 if ((datefmt = value("datefield")) != NULL) {
414 fp = hfield1("date", mp);/* TODO use m_date field! */
415 if (fp == NULL) {
416 datefmt = NULL;
417 goto jdate_set;
419 datet = rfctime(fp);
420 date = fakedate(datet);
421 fp = value("datefield-markout-older");
422 i = (*datefmt != '\0');
423 if (fp != NULL)
424 i |= (*fp != '\0') ? 2 | 4 : 2;
425 /* May we strftime(3)? */
426 if (i & (1 | 4))
427 memcpy(&time_current.tc_local, localtime(&datet),
428 sizeof time_current.tc_local);
429 if ((i & 2) && (datet > time_current.tc_time ||
430 #define _6M ((DATE_DAYSYEAR / 2) * DATE_SECSDAY)
431 (datet + _6M < time_current.tc_time))) {
432 #undef _6M
433 if ((datefmt = (i & 4) ? fp : NULL) == NULL) {
434 memset(datebuf, ' ', FROM_DATEBUF); /* xxx ur */
435 memcpy(datebuf + 4, date + 4, 7);
436 datebuf[4 + 7] = ' ';
437 memcpy(datebuf + 4 + 7 + 1, date + 20, 4);
438 datebuf[4 + 7 + 1 + 4] = '\0';
439 date = datebuf;
441 } else if ((i & 1) == 0)
442 datefmt = NULL;
443 } else if (datet == (time_t)0 && (mp->m_flag & MNOFROM) == 0) {
444 /* TODO eliminate this path, query the FROM_ date in setptr(),
445 * TODO all other codepaths do so by themselves ALREADY ?????
446 * TODO assert(mp->m_time != 0);, then
447 * TODO ALSO changes behaviour of markout-non-current */
448 _parse_head(mp, datebuf);
449 date = datebuf;
450 } else {
451 jdate_set:
452 date = fakedate(datet);
455 out.s = NULL;
456 out.l = 0;
457 if ((subjline = hfield1("subject", mp)) != NULL) {
458 in.s = subjline;
459 in.l = strlen(subjline);
460 mime_fromhdr(&in, &out, TD_ICONV | TD_ISPR);
461 subjline = out.s;
464 isaddr = 1;
465 name = name1(mp, 0);
466 if (name != NULL && value("showto") && is_myname(skin(name))) {
467 if ((cp = hfield1("to", mp)) != NULL) {
468 name = cp;
469 isto = 1;
472 if (name == NULL) {
473 name = "";
474 isaddr = 0;
476 if (isaddr) {
477 if (value("showname"))
478 name = realname(name);
479 else {
480 name = prstr(skin(name));
484 for (fp = fmt; *fp; fp++) {
485 if (*fp == '%') {
486 if (*++fp == '-') {
487 fp++;
488 } else if (*fp == '+')
489 fp++;
490 while (digitchar(*fp))
491 fp++;
492 if (*fp == '\0')
493 break;
494 } else {
495 #if defined (HAVE_MBTOWC) && defined (HAVE_WCWIDTH)
496 if (mb_cur_max > 1) {
497 wchar_t wc;
498 if ((s = mbtowc(&wc, fp, mb_cur_max)) < 0)
499 n = s = 1;
500 else {
501 if ((n = wcwidth(wc)) < 0)
502 n = 1;
504 } else
505 #endif /* HAVE_MBTOWC && HAVE_WCWIDTH */
507 n = s = 1;
509 subjlen -= n;
510 while (--s > 0)
511 fp++;
515 for (fp = fmt; *fp; fp++) {
516 if ((c = *fp & 0xFF) == '%') {
517 B = 0;
518 n = 0;
519 s = 1;
520 if (*++fp == '-') {
521 s = -1;
522 fp++;
523 } else if (*fp == '+')
524 fp++;
525 if (digitchar(*fp)) {
527 n = 10*n + *fp - '0';
528 while (fp++, digitchar(*fp));
530 if (*fp == '\0')
531 break;
532 n *= s;
533 switch ((c = *fp & 0xFF)) {
534 case '%':
535 goto jputc;
536 case '>':
537 case '<':
538 if (dot != mp)
539 c = ' ';
540 goto jputc;
541 case 'a':
542 c = dispc(mp, attrlist);
543 jputc:
544 n = fprintf(f, "%*c", n, c);
545 if (n >= 0)
546 subjlen -= n;
547 break;
548 case 'm':
549 if (n == 0) {
550 n = 3;
551 if (threaded)
552 for (i=msgCount; i>999; i/=10)
553 n++;
555 subjlen -= fprintf(f, "%*d", n, mesg);
556 break;
557 case 'f':
558 if (n == 0) {
559 n = 18;
560 if (s < 0)
561 n = -n;
563 fromlen = ABS(n);
564 if (isto) /* XXX tr()! */
565 fromlen -= 3;
566 subjlen -= fprintf(f, "%s%s", isto ? "To " : "",
567 colalign(name, fromlen, n));
568 break;
569 case 'd':
570 if (datefmt != NULL) {
571 i = strftime(datebuf, sizeof datebuf,
572 datefmt,
573 &time_current.tc_local);
574 if (i != 0)
575 date = datebuf;
576 else
577 fprintf(stderr, tr(174,
578 "Ignored date format, "
579 "it excesses the "
580 "target buffer "
581 "(%lu bytes)\n"),
582 (ul_it)sizeof datebuf);
583 datefmt = NULL;
585 if (n == 0)
586 n = 16;
587 subjlen -= fprintf(f, "%*.*s", n, n, date);
588 break;
589 case 'l':
590 if (n == 0)
591 n = 4;
592 if (mp->m_xlines)
593 subjlen -= fprintf(f, "%*ld", n,
594 mp->m_xlines);
595 else {
596 n = ABS(n);
597 subjlen -= n;
598 while (n--)
599 putc(' ', f);
601 break;
602 case 'o':
603 if (n == 0)
604 n = -5;
605 subjlen -= fprintf(f, "%*lu", n,
606 (long)mp->m_xsize);
607 break;
608 case 'i':
609 if (threaded)
610 subjlen -= putindent(f, mp,
611 scrnwidth - 60);
612 break;
613 case 'S':
614 B = 1;
615 /*FALLTHRU*/
616 case 's':
617 if (n == 0)
618 n = subjlen - 2;
619 if (n > 0 && s < 0)
620 n = -n;
621 if (B)
622 n -= (n < 0) ? -2 : 2;
623 if (subjline != NULL && n != 0) {
624 /* pretty pathetic */
625 fprintf(f, B ? "\"%s\"" : "%s",
626 colalign(subjline, ABS(n), n));
628 break;
629 case 'U':
630 #ifdef HAVE_IMAP
631 if (n == 0)
632 n = 9;
633 subjlen -= fprintf(f, "%*lu", n, mp->m_uid);
634 break;
635 #else
636 c = '?';
637 goto jputc;
638 #endif
639 case 'e':
640 if (n == 0)
641 n = 2;
642 subjlen -= fprintf(f, "%*u", n, threaded == 1 ?
643 mp->m_level : 0);
644 break;
645 case 't':
646 if (n == 0) {
647 n = 3;
648 if (threaded)
649 for (i=msgCount; i>999; i/=10)
650 n++;
652 subjlen -= fprintf(f, "%*ld", n, threaded ?
653 mp->m_threadpos : mesg);
654 break;
655 case '$':
656 #ifdef HAVE_SPAM
657 if (n == 0)
658 n = 4;
659 { char buf[16];
660 snprintf(buf, sizeof buf, "%u.%u",
661 (mp->m_spamscore >> 8),
662 (mp->m_spamscore & 0xFF));
663 subjlen -= fprintf(f, "%*s",
664 n, buf);
666 #else
667 c = '?';
668 goto jputc;
669 #endif
671 } else
672 putc(c, f);
674 putc('\n', f);
676 if (out.s)
677 free(out.s);
681 * Print out the indenting in threaded display.
683 static int
684 putindent(FILE *fp, struct message *mp, int maxwidth)
686 struct message *mq;
687 int indent, i;
688 int *us;
689 char *cs;
690 int important = MNEW|MFLAGGED;
692 if (mp->m_level == 0)
693 return 0;
694 cs = ac_alloc(mp->m_level);
695 us = ac_alloc(mp->m_level * sizeof *us);
696 i = mp->m_level - 1;
697 if (mp->m_younger && (unsigned)i + 1 == mp->m_younger->m_level) {
698 if (mp->m_parent && mp->m_parent->m_flag & important)
699 us[i] = mp->m_flag & important ? 0x2523 : 0x2520;
700 else
701 us[i] = mp->m_flag & important ? 0x251D : 0x251C;
702 cs[i] = '+';
703 } else {
704 if (mp->m_parent && mp->m_parent->m_flag & important)
705 us[i] = mp->m_flag & important ? 0x2517 : 0x2516;
706 else
707 us[i] = mp->m_flag & important ? 0x2515 : 0x2514;
708 cs[i] = '\\';
710 mq = mp->m_parent;
711 for (i = mp->m_level - 2; i >= 0; i--) {
712 if (mq) {
713 if ((unsigned)i > mq->m_level - 1) {
714 us[i] = cs[i] = ' ';
715 continue;
717 if (mq->m_younger) {
718 if (mq->m_parent &&
719 mq->m_parent->m_flag&important)
720 us[i] = 0x2503;
721 else
722 us[i] = 0x2502;
723 cs[i] = '|';
724 } else
725 us[i] = cs[i] = ' ';
726 mq = mq->m_parent;
727 } else
728 us[i] = cs[i] = ' ';
730 for (indent = 0; (unsigned)indent < mp->m_level && indent < maxwidth;
731 ++indent) {
732 if (indent < maxwidth - 1)
733 putuc(us[indent], cs[indent] & 0377, fp);
734 else
735 putuc(0x21B8, '^', fp);
737 ac_free(us);
738 ac_free(cs);
739 return indent;
742 void
743 printhead(int mesg, FILE *f, int threaded)
745 int bsdflags, bsdheadline, sz;
746 char attrlist[30], *cp;
747 char const *fmt;
749 bsdflags = value("bsdcompat") != NULL || value("bsdflags") != NULL ||
750 getenv("SYSV3") != NULL;
751 strcpy(attrlist, bsdflags ? "NU *HMFAT+-$" : "NUROSPMFAT+-$");
752 if ((cp = value("attrlist")) != NULL) {
753 sz = strlen(cp);
754 if (sz > (int)sizeof attrlist - 1)
755 sz = (int)sizeof attrlist - 1;
756 memcpy(attrlist, cp, sz);
758 bsdheadline = value("bsdcompat") != NULL ||
759 value("bsdheadline") != NULL;
760 if ((fmt = value("headline")) == NULL)
761 fmt = bsdheadline ?
762 "%>%a%m %-20f %16d %3l/%-5o %i%-S" :
763 "%>%a%m %-18f %16d %4l/%-5o %i%-s";
764 hprf(fmt, mesg, f, threaded, attrlist);
768 * Print out the value of dot.
770 /*ARGSUSED*/
771 int
772 pdot(void *v)
774 (void)v;
775 printf(catgets(catd, CATSET, 13, "%d\n"),
776 (int)(dot - &message[0] + 1));
777 return(0);
781 * Print out all the possible commands.
784 static int
785 _pcmd_cmp(void const *s1, void const *s2)
787 struct cmd const *const*c1 = s1, *const*c2 = s2;
788 return (strcmp((*c1)->c_name, (*c2)->c_name));
791 /*ARGSUSED*/
792 int
793 pcmdlist(void *v)
795 extern struct cmd const cmdtab[];
796 struct cmd const **cpa, *cp, **cursor;
797 size_t i;
798 (void)v;
800 for (i = 0; cmdtab[i].c_name != NULL; ++i)
802 ++i;
803 cpa = ac_alloc(sizeof(cp) * i);
805 for (i = 0; (cp = cmdtab + i)->c_name != NULL; ++i)
806 cpa[i] = cp;
807 cpa[i] = NULL;
809 qsort(cpa, i, sizeof(cp), &_pcmd_cmp);
811 printf(tr(14, "Commands are:\n"));
812 for (i = 0, cursor = cpa; (cp = *cursor++) != NULL;) {
813 size_t j;
814 if (cp->c_func == &ccmdnotsupp)
815 continue;
816 j = strlen(cp->c_name) + 2;
817 if ((i += j) > 72) {
818 i = j;
819 printf("\n");
821 printf((*cursor != NULL ? "%s, " : "%s\n"), cp->c_name);
824 ac_free(cpa);
825 return (0);
829 * Type out the messages requested.
831 static sigjmp_buf pipestop;
833 /*ARGSUSED*/
834 static void
835 brokpipe(int signo)
837 (void)signo;
838 siglongjmp(pipestop, 1);
841 static int
842 type1(int *msgvec, int doign, int page, int pipe, int decode,
843 char *cmd, off_t *tstats)
845 int *ip;
846 struct message *mp;
847 char const *cp;
848 int nlines;
849 off_t mstats[2];
850 FILE *volatile obuf;
852 obuf = stdout;
853 if (sigsetjmp(pipestop, 1))
854 goto close_pipe;
855 if (pipe) {
856 cp = value("SHELL");
857 if (cp == NULL)
858 cp = SHELL;
859 obuf = Popen(cmd, "w", cp, 1);
860 if (obuf == NULL) {
861 perror(cmd);
862 obuf = stdout;
863 } else {
864 safe_signal(SIGPIPE, brokpipe);
866 } else if ((options & OPT_TTYOUT) &&
867 (page || (cp = value("crt")) != NULL)) {
868 nlines = 0;
869 if (!page) {
870 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
871 if ((message[*ip-1].m_have & HAVE_BODY) == 0) {
872 if ((get_body(&message[*ip - 1])) !=
873 OKAY)
874 return 1;
876 nlines += message[*ip - 1].m_lines;
879 if (page || nlines > (*cp ? atoi(cp) : realscreenheight)) {
880 char const *p = get_pager();
881 obuf = Popen(p, "w", NULL, 1);
882 if (obuf == NULL) {
883 perror(p);
884 obuf = stdout;
885 } else
886 safe_signal(SIGPIPE, brokpipe);
889 for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) {
890 mp = &message[*ip - 1];
891 touch(mp);
892 setdot(mp);
893 uncollapse1(mp, 1);
894 if (! pipe && ip != msgvec)
895 fprintf(obuf, "\n");
896 _show_msg_overview(mp, *ip, obuf);
897 send(mp, obuf, doign ? ignore : 0, NULL,
898 pipe && value("piperaw") ? SEND_MBOX :
899 decode ? SEND_SHOW :
900 doign ? SEND_TODISP : SEND_TODISP_ALL,
901 mstats);
902 if (pipe && value("page")) {
903 putc('\f', obuf);
905 if (tstats) {
906 tstats[0] += mstats[0];
907 tstats[1] += mstats[1];
910 close_pipe:
911 if (obuf != stdout) {
913 * Ignore SIGPIPE so it can't cause a duplicate close.
915 safe_signal(SIGPIPE, SIG_IGN);
916 Pclose(obuf, TRU1);
917 safe_signal(SIGPIPE, dflpipe);
919 return(0);
923 * Get the last, possibly quoted part of linebuf.
925 char *
926 laststring(char *linebuf, int *flag, int strip)
928 char *cp, *p;
929 char quoted;
931 *flag = 1;
932 cp = strlen(linebuf) + linebuf - 1;
935 * Strip away trailing blanks.
937 while (cp > linebuf && whitechar(*cp & 0377))
938 cp--;
939 *++cp = 0;
940 if (cp == linebuf) {
941 *flag = 0;
942 return NULL;
946 * Now search for the beginning of the command name.
948 quoted = *(cp - 1);
949 if (quoted == '\'' || quoted == '\"') {
950 cp--;
951 if (strip)
952 *cp = '\0';
953 cp--;
954 while (cp > linebuf) {
955 if (*cp != quoted) {
956 cp--;
957 } else if (*(cp - 1) != '\\') {
958 break;
959 } else {
960 p = --cp;
961 do {
962 *p = *(p + 1);
963 } while (*p++);
964 cp--;
967 if (cp == linebuf)
968 *flag = 0;
969 if (*cp == quoted) {
970 if (strip)
971 *cp++ = 0;
972 } else
973 *flag = 0;
974 } else {
975 while (cp > linebuf && !whitechar(*cp & 0377))
976 cp--;
977 if (whitechar(*cp & 0377))
978 *cp++ = 0;
979 else
980 *flag = 0;
982 if (*cp == '\0') {
983 return(NULL);
985 return(cp);
989 * Pipe the messages requested.
991 static int
992 pipe1(char *str, int doign)
994 char *cmd;
995 int f, *msgvec, ret;
996 off_t stats[2];
998 /*LINTED*/
999 msgvec = (int *)salloc((msgCount + 2) * sizeof *msgvec);
1000 if ((cmd = laststring(str, &f, 1)) == NULL) {
1001 cmd = value("cmd");
1002 if (cmd == NULL || *cmd == '\0') {
1003 fputs(catgets(catd, CATSET, 16,
1004 "variable cmd not set\n"), stderr);
1005 return 1;
1008 if (!f) {
1009 *msgvec = first(0, MMNORM);
1010 if (*msgvec == 0) {
1011 if (inhook)
1012 return 0;
1013 puts(catgets(catd, CATSET, 18, "No messages to pipe."));
1014 return 1;
1016 msgvec[1] = 0;
1017 } else if (getmsglist(str, msgvec, 0) < 0)
1018 return 1;
1019 if (*msgvec == 0) {
1020 if (inhook)
1021 return 0;
1022 printf("No applicable messages.\n");
1023 return 1;
1025 printf(catgets(catd, CATSET, 268, "Pipe to: \"%s\"\n"), cmd);
1026 stats[0] = stats[1] = 0;
1027 if ((ret = type1(msgvec, doign, 0, 1, 0, cmd, stats)) == 0) {
1028 printf("\"%s\" ", cmd);
1029 if (stats[0] >= 0)
1030 printf("%lu", (long)stats[0]);
1031 else
1032 printf(catgets(catd, CATSET, 27, "binary"));
1033 printf("/%lu\n", (long)stats[1]);
1035 return ret;
1039 * Paginate messages, honor ignored fields.
1041 int
1042 more(void *v)
1044 int *msgvec = v;
1045 return (type1(msgvec, 1, 1, 0, 0, NULL, NULL));
1049 * Paginate messages, even printing ignored fields.
1051 int
1052 More(void *v)
1054 int *msgvec = v;
1056 return (type1(msgvec, 0, 1, 0, 0, NULL, NULL));
1060 * Type out messages, honor ignored fields.
1062 int
1063 type(void *v)
1065 int *msgvec = v;
1067 return(type1(msgvec, 1, 0, 0, 0, NULL, NULL));
1071 * Type out messages, even printing ignored fields.
1073 int
1074 Type(void *v)
1076 int *msgvec = v;
1078 return(type1(msgvec, 0, 0, 0, 0, NULL, NULL));
1082 * Show MIME-encoded message text, including all fields.
1085 show(void *v)
1087 int *msgvec = v;
1089 return(type1(msgvec, 0, 0, 0, 1, NULL, NULL));
1093 * Pipe messages, honor ignored fields.
1095 int
1096 pipecmd(void *v)
1098 char *str = v;
1099 return(pipe1(str, 1));
1102 * Pipe messages, not respecting ignored fields.
1104 int
1105 Pipecmd(void *v)
1107 char *str = v;
1108 return(pipe1(str, 0));
1112 * Print the top so many lines of each desired message.
1113 * The number of lines is taken from the variable "toplines"
1114 * and defaults to 5.
1116 int
1117 top(void *v)
1119 int *msgvec = v, *ip, c, topl, lines, empty_last;
1120 struct message *mp;
1121 char *cp, *linebuf = NULL;
1122 size_t linesize;
1123 FILE *ibuf;
1125 topl = 5;
1126 cp = value("toplines");
1127 if (cp != NULL) {
1128 topl = atoi(cp);
1129 if (topl < 0 || topl > 10000)
1130 topl = 5;
1132 empty_last = 1;
1133 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
1134 mp = &message[*ip - 1];
1135 touch(mp);
1136 setdot(mp);
1137 did_print_dot = TRU1;
1138 if (! empty_last)
1139 printf("\n");
1140 _show_msg_overview(mp, *ip, stdout);
1141 if (mp->m_flag & MNOFROM)
1142 printf("From %s %s\n", fakefrom(mp),
1143 fakedate(mp->m_time));
1144 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL) { /* XXX could use TOP */
1145 v = NULL;
1146 break;
1148 c = mp->m_lines;
1149 for (lines = 0; lines < c && lines <= topl; lines++) {
1150 if (readline_restart(ibuf, &linebuf, &linesize, 0) < 0)
1151 break;
1152 puts(linebuf);
1154 for (cp = linebuf; *cp && blankchar(*cp); ++cp)
1156 empty_last = (*cp == '\0');
1160 if (linebuf != NULL)
1161 free(linebuf);
1162 return (v != NULL);
1166 * Touch all the given messages so that they will
1167 * get mboxed.
1169 int
1170 stouch(void *v)
1172 int *msgvec = v;
1173 int *ip;
1175 for (ip = msgvec; *ip != 0; ip++) {
1176 setdot(&message[*ip-1]);
1177 dot->m_flag |= MTOUCH;
1178 dot->m_flag &= ~MPRESERVE;
1180 * POSIX interpretation necessary.
1182 did_print_dot = TRU1;
1184 return(0);
1188 * Make sure all passed messages get mboxed.
1190 int
1191 mboxit(void *v)
1193 int *msgvec = v;
1194 int *ip;
1196 for (ip = msgvec; *ip != 0; ip++) {
1197 setdot(&message[*ip-1]);
1198 dot->m_flag |= MTOUCH|MBOX;
1199 dot->m_flag &= ~MPRESERVE;
1201 * POSIX interpretation necessary.
1203 did_print_dot = TRU1;
1205 return(0);
1209 * List the folders the user currently has.
1211 int
1212 folders(void *v)
1214 char dirname[MAXPATHLEN], *name, **argv = v;
1215 char const *cmd;
1217 if (*argv) {
1218 name = expand(*argv);
1219 if (name == NULL)
1220 return 1;
1221 } else if (! getfold(dirname, sizeof dirname)) {
1222 fprintf(stderr, tr(20, "No value set for \"folder\"\n"));
1223 return 1;
1224 } else
1225 name = dirname;
1227 if (which_protocol(name) == PROTO_IMAP) {
1228 #ifdef HAVE_IMAP
1229 imap_folders(name, *argv == NULL);
1230 #else
1231 return ccmdnotsupp(NULL);
1232 #endif
1233 } else {
1234 if ((cmd = value("LISTER")) == NULL)
1235 cmd = LISTER;
1236 run_command(cmd, 0, -1, -1, name, NULL, NULL);
1238 return 0;