popen.c: some cleanup, use other wait_child() from now on
[s-mailx.git] / cmd1.c
blob95579514fcf440711943c23ea3e4371d43583c58
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 #ifndef HAVE_AMALGAMATION
41 # include "nail.h"
42 #endif
45 * Print the current active headings.
46 * Don't change dot if invoker didn't give an argument.
49 static int screen;
51 /* Prepare and print "[Message: xy]:" intro */
52 static void _show_msg_overview(struct message *mp, int msg_no, FILE *obuf);
54 static void _cmd1_onpipe(int signo);
55 static int _dispc(struct message *mp, const char *a);
56 static int scroll1(char *arg, int onlynew);
58 /* ... And place the extracted date in `date' */
59 static void _parse_from_(struct message *mp, char date[FROM_DATEBUF]);
61 /* Get the Subject:, but return NULL if in threaded mode and the message
62 * printed before was in the same thread and had the same subject */
63 static char * _get_subject(struct message *mp, bool_t threaded);
64 static char * __subject_trim(char *s);
66 static void hprf(const char *fmt, int mesg, FILE *f, int threaded,
67 const char *attrlist);
68 static int putindent(FILE *fp, struct message *mp, int maxwidth);
69 static int _type1(int *msgvec, bool_t doign, bool_t dopage, bool_t dopipe,
70 bool_t dodecode, char *cmd, off_t *tstats);
71 static int pipe1(char *str, int doign);
73 static void
74 _show_msg_overview(struct message *mp, int msg_no, FILE *obuf)
76 fprintf(obuf, tr(17, "[-- Message %2d -- %lu lines, %lu bytes --]:\n"),
77 msg_no, (ul_it)mp->m_lines, (ul_it)mp->m_size);
80 FL int
81 ccmdnotsupp(void *v)
83 (void)v;
84 fprintf(stderr, tr(10, "The requested feature is not compiled in\n"));
85 return (1);
88 FL char const *
89 get_pager(void)
91 char const *cp;
93 cp = value("PAGER");
94 if (cp == NULL || *cp == '\0')
95 cp = PAGER;
96 return cp;
99 FL int
100 headers(void *v)
102 ui32_t flag;
103 int *msgvec = v, g, k, n, mesg, size, lastg = 1;
104 struct message *mp, *mq, *lastmq = NULL;
105 enum mflag fl = MNEW|MFLAGGED;
107 time_current_update(&time_current, FAL0);
109 flag = 0;
110 size = screensize();
111 n = msgvec[0]; /* n == {-2, -1, 0}: called from scroll() */
112 if (screen < 0)
113 screen = 0;
114 k = screen * size;
115 if (k >= msgCount)
116 k = msgCount - size;
117 if (k < 0)
118 k = 0;
120 if (mb.mb_threaded == 0) {
121 g = 0;
122 mq = &message[0];
123 for (mp = &message[0]; mp < &message[msgCount]; mp++)
124 if (visible(mp)) {
125 if (g % size == 0)
126 mq = mp;
127 if (mp->m_flag&fl) {
128 lastg = g;
129 lastmq = mq;
131 if ((n > 0 && mp == &message[n-1]) ||
132 (n == 0 && g == k) ||
133 (n == -2 && g == k + size &&
134 lastmq) ||
135 (n < 0 && g >= k &&
136 (mp->m_flag & fl) != 0))
137 break;
138 g++;
140 if (lastmq && (n==-2 || (n==-1 && mp == &message[msgCount]))) {
141 g = lastg;
142 mq = lastmq;
144 screen = g / size;
145 mp = mq;
146 mesg = mp - &message[0];
147 if (dot != &message[n-1]) {
148 for (mq = mp; mq < &message[msgCount]; mq++)
149 if (visible(mq)) {
150 setdot(mq);
151 break;
154 #ifdef HAVE_IMAP
155 if (mb.mb_type == MB_IMAP)
156 imap_getheaders(mesg+1, mesg + size);
157 #endif
158 srelax_hold();
159 for (; mp < &message[msgCount]; mp++) {
160 mesg++;
161 if (!visible(mp))
162 continue;
163 if (UICMP(32, flag++, >=, size))
164 break;
165 printhead(mesg, stdout, 0);
166 srelax();
168 srelax_rele();
169 } else { /* threaded */
170 g = 0;
171 mq = threadroot;
172 for (mp = threadroot; mp; mp = next_in_thread(mp))
173 if (visible(mp) && (mp->m_collapsed <= 0 ||
174 mp == &message[n-1])) {
175 if (g % size == 0)
176 mq = mp;
177 if (mp->m_flag&fl) {
178 lastg = g;
179 lastmq = mq;
181 if ((n > 0 && mp == &message[n-1]) ||
182 (n == 0 && g == k) ||
183 (n == -2 && g == k + size &&
184 lastmq) ||
185 (n < 0 && g >= k &&
186 (mp->m_flag & fl) != 0))
187 break;
188 g++;
190 if (lastmq && (n==-2 || (n==-1 && mp==&message[msgCount]))) {
191 g = lastg;
192 mq = lastmq;
194 screen = g / size;
195 mp = mq;
196 if (dot != &message[n-1]) {
197 for (mq = mp; mq; mq = next_in_thread(mq))
198 if (visible(mq) && mq->m_collapsed <= 0) {
199 setdot(mq);
200 break;
203 srelax_hold();
204 while (mp) {
205 if (visible(mp) && (mp->m_collapsed <= 0 ||
206 mp == &message[n-1])) {
207 if (UICMP(32, flag++, >=, size))
208 break;
209 printhead(mp - &message[0] + 1, stdout,
210 mb.mb_threaded);
211 srelax();
213 mp = next_in_thread(mp);
215 srelax_rele();
217 if (!flag)
218 printf(tr(6, "No more mail.\n"));
219 return !flag;
223 * Scroll to the next/previous screen
225 FL int
226 scroll(void *v)
228 return scroll1(v, 0);
231 FL int
232 Scroll(void *v)
234 return scroll1(v, 1);
237 static int
238 scroll1(char *arg, int onlynew)
240 int size;
241 int cur[1];
243 cur[0] = onlynew ? -1 : 0;
244 size = screensize();
245 switch (*arg) {
246 case '1': case '2': case '3': case '4': case '5':
247 case '6': case '7': case '8': case '9': case '0':
248 screen = atoi(arg);
249 goto scroll_forward;
250 case '\0':
251 screen++;
252 goto scroll_forward;
253 case '$':
254 screen = msgCount / size;
255 goto scroll_forward;
256 case '+':
257 if (arg[1] == '\0')
258 screen++;
259 else
260 screen += atoi(arg + 1);
261 scroll_forward:
262 if (screen * size > msgCount) {
263 screen = msgCount / size;
264 printf(tr(7, "On last screenful of messages\n"));
266 break;
268 case '-':
269 if (arg[1] == '\0')
270 screen--;
271 else
272 screen -= atoi(arg + 1);
273 if (screen < 0) {
274 screen = 0;
275 printf(tr(8, "On first screenful of messages\n"));
277 if (cur[0] == -1)
278 cur[0] = -2;
279 break;
281 default:
282 printf(tr(9, "Unrecognized scrolling command \"%s\"\n"), arg);
283 return(1);
285 return(headers(cur));
289 * Compute screen size.
291 FL int
292 screensize(void)
294 int s;
295 char *cp;
297 if ((cp = value("screen")) != NULL && (s = atoi(cp)) > 0)
298 return s;
299 return scrnheight - 4;
302 static sigjmp_buf _cmd1_pipejmp;
304 /*ARGSUSED*/
305 static void
306 _cmd1_onpipe(int signo)
308 UNUSED(signo);
309 siglongjmp(_cmd1_pipejmp, 1);
313 * Print out the headlines for each message
314 * in the passed message list.
316 FL int
317 from(void *v)
319 int *msgvec = v, *ip, n;
320 char *cp;
321 FILE *volatile obuf = stdout;
323 time_current_update(&time_current, FAL0);
325 /* TODO unfixable memory leaks still */
326 if (IS_TTY_SESSION() && (cp = value("crt")) != NULL) {
327 for (n = 0, ip = msgvec; *ip; ip++)
328 n++;
329 if (n > (*cp == '\0' ? screensize() : atoi((char*)cp)) + 3) {
330 char const *p;
331 if (sigsetjmp(_cmd1_pipejmp, 1))
332 goto endpipe;
333 p = get_pager();
334 if ((obuf = Popen(p, "w", NULL, 1)) == NULL) {
335 perror(p);
336 obuf = stdout;
337 cp=NULL;
338 } else
339 safe_signal(SIGPIPE, _cmd1_onpipe);
342 for (ip = msgvec; *ip != 0; ip++)
343 printhead(*ip, obuf, mb.mb_threaded);
344 if (--ip >= msgvec)
345 setdot(&message[*ip - 1]);
346 endpipe:
347 if (obuf != stdout) {
348 safe_signal(SIGPIPE, SIG_IGN);
349 Pclose(obuf, TRU1);
350 safe_signal(SIGPIPE, dflpipe);
352 return(0);
355 static int
356 _dispc(struct message *mp, const char *a)
358 int i = ' ';
361 * Bletch!
363 if ((mp->m_flag & (MREAD|MNEW)) == MREAD)
364 i = a[3];
365 if ((mp->m_flag & (MREAD|MNEW)) == (MREAD|MNEW))
366 i = a[2];
367 if (mp->m_flag & MANSWERED)
368 i = a[8];
369 if (mp->m_flag & MDRAFTED)
370 i = a[9];
371 if ((mp->m_flag & (MREAD|MNEW)) == MNEW)
372 i = a[0];
373 if ((mp->m_flag & (MREAD|MNEW)) == 0)
374 i = a[1];
375 if (mp->m_flag & MSPAM)
376 i = a[12];
377 if (mp->m_flag & MSAVED)
378 i = a[4];
379 if (mp->m_flag & MPRESERVE)
380 i = a[5];
381 if (mp->m_flag & (MBOX|MBOXED))
382 i = a[6];
383 if (mp->m_flag & MFLAGGED)
384 i = a[7];
385 if (mb.mb_threaded == 1 && mp->m_collapsed > 0)
386 i = a[11];
387 if (mb.mb_threaded == 1 && mp->m_collapsed < 0)
388 i = a[10];
389 return i;
392 static void
393 _parse_from_(struct message *mp, char date[FROM_DATEBUF])
395 FILE *ibuf;
396 int hlen;
397 char *hline = NULL;
398 size_t hsize = 0;
400 if ((ibuf = setinput(&mb, mp, NEED_HEADER)) != NULL &&
401 (hlen = readline_restart(ibuf, &hline, &hsize, 0)) > 0)
402 (void)extract_date_from_from_(hline, hlen, date);
403 if (hline != NULL)
404 free(hline);
407 static char *
408 __subject_trim(char *s)
410 while (*s != '\0') {
411 while (spacechar(*s))
412 ++s;
413 if (is_asccaseprefix("re:", s)) {
414 s += 3;
415 continue;
417 if (is_asccaseprefix("fwd:", s)) {
418 s += 4;
419 continue;
421 break;
423 return s;
426 static char *
427 _get_subject(struct message *mp, bool_t threaded)
429 struct str in, out;
430 char *rv = (char*)-1, *ms, *mso, *os;
432 if ((ms = hfield1("subject", mp)) == NULL)
433 goto jleave;
435 if (! threaded || mp->m_level == 0)
436 goto jconv;
438 /* In a display thread - check wether this message uses the same
439 * Subject: as it's parent or elder neighbour, suppress printing it if
440 * this is the case. To extend this a bit, ignore any leading Re: or
441 * Fwd: plus follow-up WS; XXX NOTE: because of efficiency reasons we
442 * XXX simply ignore any encoded parts and use ASCII case-insensitive
443 * XXX comparison */
444 mso = __subject_trim(ms);
446 if (mp->m_elder != NULL &&
447 (os = hfield1("subject", mp->m_elder)) != NULL &&
448 asccasecmp(mso, __subject_trim(os)) == 0)
449 goto jleave;
451 if (mp->m_parent != NULL &&
452 (os = hfield1("subject", mp->m_parent)) != NULL &&
453 asccasecmp(mso, __subject_trim(os)) == 0)
454 goto jleave;
456 jconv:
457 in.s = ms;
458 in.l = strlen(ms);
459 mime_fromhdr(&in, &out, TD_ICONV | TD_ISPR);
460 rv = out.s;
461 jleave:
462 return rv;
465 static void
466 hprf(const char *fmt, int mesg, FILE *f, int threaded, const char *attrlist)
468 char datebuf[FROM_DATEBUF], *cp, *subjline;
469 char const *datefmt, *date, *name, *fp;
470 int B, c, i, n, s, wleft, subjlen, isto = 0, isaddr = 0;
471 struct message *mp = &message[mesg - 1];
472 time_t datet = mp->m_time;
474 date = NULL;
475 if ((datefmt = value("datefield")) != NULL) {
476 fp = hfield1("date", mp);/* TODO use m_date field! */
477 if (fp == NULL) {
478 datefmt = NULL;
479 goto jdate_set;
481 datet = rfctime(fp);
482 date = fakedate(datet);
483 fp = value("datefield-markout-older");
484 i = (*datefmt != '\0');
485 if (fp != NULL)
486 i |= (*fp != '\0') ? 2 | 4 : 2;
487 /* May we strftime(3)? */
488 if (i & (1 | 4))
489 memcpy(&time_current.tc_local, localtime(&datet),
490 sizeof time_current.tc_local);
491 if ((i & 2) &&
492 /* TODO *datefield-markout-older* we accept
493 * TODO one day in the future, should be UTC
494 * TODO offset only? and Stephen Isard had
495 * TODO one week once he proposed the patch! */
496 (datet > time_current.tc_time + DATE_SECSDAY ||
497 #define _6M ((DATE_DAYSYEAR / 2) * DATE_SECSDAY)
498 (datet + _6M < time_current.tc_time))) {
499 #undef _6M
500 if ((datefmt = (i & 4) ? fp : NULL) == NULL) {
501 memset(datebuf, ' ', FROM_DATEBUF); /* xxx ur */
502 memcpy(datebuf + 4, date + 4, 7);
503 datebuf[4 + 7] = ' ';
504 memcpy(datebuf + 4 + 7 + 1, date + 20, 4);
505 datebuf[4 + 7 + 1 + 4] = '\0';
506 date = datebuf;
508 } else if ((i & 1) == 0)
509 datefmt = NULL;
510 } else if (datet == (time_t)0 && (mp->m_flag & MNOFROM) == 0) {
511 /* TODO eliminate this path, query the FROM_ date in setptr(),
512 * TODO all other codepaths do so by themselves ALREADY ?????
513 * TODO assert(mp->m_time != 0);, then
514 * TODO ALSO changes behaviour of markout-non-current */
515 _parse_from_(mp, datebuf);
516 date = datebuf;
517 } else {
518 jdate_set:
519 date = fakedate(datet);
522 isaddr = 1;
523 name = name1(mp, 0);
524 if (name != NULL && value("showto") && is_myname(skin(name))) {
525 if ((cp = hfield1("to", mp)) != NULL) {
526 name = cp;
527 isto = 1;
530 if (name == NULL) {
531 name = "";
532 isaddr = 0;
534 if (isaddr) {
535 if (value("showname"))
536 name = realname(name);
537 else {
538 name = prstr(skin(name));
542 subjline = NULL;
544 /* Detect the width of the non-format characters in *headline*;
545 * like that we can simply use putc() in the next loop, since we have
546 * already calculated their column widths (TODO it's sick) */
547 wleft =
548 subjlen = scrnwidth;
550 for (fp = fmt; *fp; ++fp) {
551 if (*fp == '%') {
552 if (*++fp == '-') {
553 ++fp;
554 } else if (*fp == '+')
555 ++fp;
556 if (digitchar(*fp)) {
557 n = 0;
559 n = 10*n + *fp - '0';
560 while (++fp, digitchar(*fp));
561 subjlen -= n;
564 if (*fp == '\0')
565 break;
566 } else {
567 #if defined HAVE_MBTOWC && defined HAVE_WCWIDTH
568 if (mb_cur_max > 1) {
569 wchar_t wc;
570 if ((s = mbtowc(&wc, fp, mb_cur_max)) < 0)
571 n = s = 1;
572 else if ((n = wcwidth(wc)) < 0)
573 n = 1;
574 } else
575 #endif
576 n = s = 1;
577 subjlen -= n;
578 wleft -= n;
579 while (--s > 0)
580 ++fp;
584 /* Walk *headline*, producing output */
585 for (fp = fmt; *fp; ++fp) {
586 if ((c = *fp & 0xFF) == '%') {
587 B = 0;
588 n = 0;
589 s = 1;
590 if (*++fp == '-') {
591 s = -1;
592 ++fp;
593 } else if (*fp == '+')
594 ++fp;
595 if (digitchar(*fp)) {
597 n = 10*n + *fp - '0';
598 while (++fp, digitchar(*fp));
600 if (*fp == '\0')
601 break;
603 n *= s;
604 switch ((c = *fp & 0xFF)) {
605 case '%':
606 goto jputc;
607 case '>':
608 case '<':
609 if (dot != mp)
610 c = ' ';
611 goto jputc;
612 case 'a':
613 c = _dispc(mp, attrlist);
614 jputc:
615 if (UICMP(32, ABS(n), >, wleft))
616 n = (n < 0) ? -wleft : wleft;
617 n = fprintf(f, "%*c", n, c);
618 wleft = (n >= 0) ? wleft - n : 0;
619 break;
620 case 'm':
621 if (n == 0) {
622 n = 3;
623 if (threaded)
624 for (i=msgCount; i>999; i/=10)
625 n++;
627 if (UICMP(32, ABS(n), >, wleft))
628 n = (n < 0) ? -wleft : wleft;
629 n = fprintf(f, "%*d", n, mesg);
630 wleft = (n >= 0) ? wleft - n : 0;
631 break;
632 case 'f':
633 if (n == 0) {
634 n = 18;
635 if (s < 0)
636 n = -n;
638 i = ABS(n);
639 if (i > wleft) {
640 i = wleft;
641 n = (n < 0) ? -wleft : wleft;
643 if (isto) /* XXX tr()! */
644 i -= 3;
645 n = fprintf(f, "%s%s", (isto ? "To " : ""),
646 colalign(name, i, n, &wleft));
647 if (n < 0)
648 wleft = 0;
649 else if (isto)
650 wleft -= 3;
651 break;
652 case 'd':
653 if (datefmt != NULL) {
654 i = strftime(datebuf, sizeof datebuf,
655 datefmt,
656 &time_current.tc_local);
657 if (i != 0)
658 date = datebuf;
659 else
660 fprintf(stderr, tr(174,
661 "Ignored date format, "
662 "it excesses the "
663 "target buffer "
664 "(%lu bytes)\n"),
665 (ul_it)sizeof datebuf);
666 datefmt = NULL;
668 if (n == 0)
669 n = 16;
670 if (UICMP(32, ABS(n), >, wleft))
671 n = (n < 0) ? -wleft : wleft;
672 n = fprintf(f, "%*.*s", n, n, date);
673 wleft = (n >= 0) ? wleft - n : 0;
674 break;
675 case 'l':
676 if (n == 0)
677 n = 4;
678 if (UICMP(32, ABS(n), >, wleft))
679 n = (n < 0) ? -wleft : wleft;
680 if (mp->m_xlines) {
681 n = fprintf(f, "%*ld", n, mp->m_xlines);
682 wleft = (n >= 0) ? wleft - n : 0;
683 } else {
684 n = ABS(n);
685 wleft -= n;
686 while (n-- != 0)
687 putc(' ', f);
689 break;
690 case 'o':
691 if (n == 0)
692 n = -5;
693 if (UICMP(32, ABS(n), >, wleft))
694 n = (n < 0) ? -wleft : wleft;
695 n = fprintf(f, "%*lu", n, (long)mp->m_xsize);
696 wleft = (n >= 0) ? wleft - n : 0;
697 break;
698 case 'i':
699 if (threaded) {
700 n = putindent(f, mp, MIN(wleft,
701 scrnwidth - 60));
702 wleft = (n >= 0) ? wleft - n : 0;
704 break;
705 case 'S':
706 B = 1;
707 /*FALLTHRU*/
708 case 's':
709 if (n == 0)
710 n = subjlen - 2;
711 if (n > 0 && s < 0)
712 n = -n;
713 if (subjlen > wleft)
714 subjlen = wleft;
715 if (UICMP(32, ABS(n), >, subjlen))
716 n = (n < 0) ? -subjlen : subjlen;
717 if (B)
718 n -= (n < 0) ? -2 : 2;
719 if (n == 0)
720 break;
721 if (subjline == NULL)
722 subjline = _get_subject(mp, threaded);
723 if (subjline == (char*)-1) {
724 n = fprintf(f, "%*s", n, "");
725 wleft = (n >= 0) ? wleft-n : 0;
726 } else {
727 n = fprintf(f, (B ? "\"%s\"" : "%s"),
728 colalign(subjline, ABS(n), n,
729 &wleft));
730 if (n < 0)
731 wleft = 0;
733 break;
734 case 'U':
735 #ifdef HAVE_IMAP
736 if (n == 0)
737 n = 9;
738 if (UICMP(32, ABS(n), >, wleft))
739 n = (n < 0) ? -wleft : wleft;
740 n = fprintf(f, "%*lu", n, mp->m_uid);
741 wleft = (n >= 0) ? wleft - n : 0;
742 break;
743 #else
744 c = '?';
745 goto jputc;
746 #endif
747 case 'e':
748 if (n == 0)
749 n = 2;
750 if (UICMP(32, ABS(n), >, wleft))
751 n = (n < 0) ? -wleft : wleft;
752 n = fprintf(f, "%*u", n,
753 threaded == 1 ? mp->m_level : 0);
754 wleft = (n >= 0) ? wleft - n : 0;
755 break;
756 case 't':
757 if (n == 0) {
758 n = 3;
759 if (threaded)
760 for (i=msgCount; i>999; i/=10)
761 n++;
763 if (UICMP(32, ABS(n), >, wleft))
764 n = (n < 0) ? -wleft : wleft;
765 n = fprintf(f, "%*ld", n,
766 threaded ? mp->m_threadpos : mesg);
767 wleft = (n >= 0) ? wleft - n : 0;
768 break;
769 case '$':
770 #ifdef HAVE_SPAM
771 if (n == 0)
772 n = 4;
773 if (UICMP(32, ABS(n), >, wleft))
774 n = (n < 0) ? -wleft : wleft;
775 { char buf[16];
776 snprintf(buf, sizeof buf, "%u.%u",
777 (mp->m_spamscore >> 8),
778 (mp->m_spamscore & 0xFF));
779 n = fprintf(f, "%*s", n, buf);
780 wleft = (n >= 0) ? wleft - n : 0;
782 #else
783 c = '?';
784 goto jputc;
785 #endif
788 if (wleft <= 0)
789 break;
790 } else
791 putc(c, f);
793 putc('\n', f);
795 if (subjline != NULL && subjline != (char*)-1)
796 free(subjline);
800 * Print out the indenting in threaded display.
802 static int
803 putindent(FILE *fp, struct message *mp, int maxwidth)/* XXX no magic consts */
805 struct message *mq;
806 int *us, indlvl, indw, i, important = MNEW|MFLAGGED;
807 char *cs;
809 if (mp->m_level == 0 || maxwidth == 0)
810 return 0;
811 cs = ac_alloc(mp->m_level);
812 us = ac_alloc(mp->m_level * sizeof *us);
814 i = mp->m_level - 1;
815 if (mp->m_younger && UICMP(32, i + 1, ==, mp->m_younger->m_level)) {
816 if (mp->m_parent && mp->m_parent->m_flag & important)
817 us[i] = mp->m_flag & important ? 0x2523 : 0x2520;
818 else
819 us[i] = mp->m_flag & important ? 0x251D : 0x251C;
820 cs[i] = '+';
821 } else {
822 if (mp->m_parent && mp->m_parent->m_flag & important)
823 us[i] = mp->m_flag & important ? 0x2517 : 0x2516;
824 else
825 us[i] = mp->m_flag & important ? 0x2515 : 0x2514;
826 cs[i] = '\\';
829 mq = mp->m_parent;
830 for (i = mp->m_level - 2; i >= 0; i--) {
831 if (mq) {
832 if (UICMP(32, i, >, mq->m_level - 1)) {
833 us[i] = cs[i] = ' ';
834 continue;
836 if (mq->m_younger) {
837 if (mq->m_parent &&
838 mq->m_parent->m_flag&important)
839 us[i] = 0x2503;
840 else
841 us[i] = 0x2502;
842 cs[i] = '|';
843 } else
844 us[i] = cs[i] = ' ';
845 mq = mq->m_parent;
846 } else
847 us[i] = cs[i] = ' ';
850 --maxwidth;
851 for (indlvl = indw = 0; (uc_it)indlvl < mp->m_level &&
852 indw < maxwidth; ++indlvl) {
853 if (indw < maxwidth - 1)
854 indw += (int)putuc(us[indlvl], cs[indlvl] & 0377, fp);
855 else
856 indw += (int)putuc(0x21B8, '^', fp);
858 indw += /*putuc(0x261E, fp)*/putc('>', fp) != EOF;
860 ac_free(us);
861 ac_free(cs);
862 return indw;
865 FL void
866 printhead(int mesg, FILE *f, int threaded)
868 int bsdflags, bsdheadline, sz;
869 char attrlist[30], *cp;
870 char const *fmt;
872 bsdflags = value("bsdcompat") != NULL || value("bsdflags") != NULL ||
873 getenv("SYSV3") != NULL;
874 strcpy(attrlist, bsdflags ? "NU *HMFAT+-$" : "NUROSPMFAT+-$");
875 if ((cp = value("attrlist")) != NULL) {
876 sz = strlen(cp);
877 if (UICMP(32, sz, >, sizeof attrlist - 1))
878 sz = (int)sizeof attrlist - 1;
879 memcpy(attrlist, cp, sz);
881 bsdheadline = value("bsdcompat") != NULL ||
882 value("bsdheadline") != NULL;
883 if ((fmt = value("headline")) == NULL)
884 fmt = bsdheadline ?
885 "%>%a%m %-20f %16d %3l/%-5o %i%-S" :
886 "%>%a%m %-18f %16d %4l/%-5o %i%-s";
887 hprf(fmt, mesg, f, threaded, attrlist);
891 * Print out the value of dot.
893 /*ARGSUSED*/
894 FL int
895 pdot(void *v)
897 (void)v;
898 printf("%d\n", (int)(dot - &message[0] + 1));
899 return(0);
903 * Type out the messages requested.
905 static sigjmp_buf pipestop;
907 /*ARGSUSED*/
908 static void
909 brokpipe(int signo)
911 (void)signo;
912 siglongjmp(pipestop, 1);
915 static int
916 _type1(int *msgvec, bool_t doign, bool_t dopage, bool_t dopipe,
917 bool_t dodecode, char *cmd, off_t *tstats)
919 off_t mstats[2];
920 int *ip;
921 struct message *mp;
922 char const *cp;
923 FILE * volatile obuf;
925 obuf = stdout;
926 if (sigsetjmp(pipestop, 1))
927 goto close_pipe;
928 if (dopipe) {
929 if ((cp = value("SHELL")) == NULL)
930 cp = SHELL;
931 if ((obuf = Popen(cmd, "w", cp, 1)) == NULL) {
932 perror(cmd);
933 obuf = stdout;
934 } else
935 safe_signal(SIGPIPE, brokpipe);
936 } else if ((options & OPT_TTYOUT) &&
937 (dopage || (cp = value("crt")) != NULL)) {
938 long nlines = 0;
939 if (!dopage) {
940 for (ip = msgvec; *ip &&
941 PTRCMP(ip - msgvec, <, msgCount);
942 ++ip) {
943 if (!(message[*ip - 1].m_have & HAVE_BODY)) {
944 if ((get_body(&message[*ip - 1])) !=
945 OKAY)
946 return 1;
948 nlines += message[*ip - 1].m_lines;
951 if (dopage || nlines > (*cp ? atoi(cp) : realscreenheight)) {
952 char const *p = get_pager();
953 if ((obuf = Popen(p, "w", NULL, 1)) == NULL) {
954 perror(p);
955 obuf = stdout;
956 } else
957 safe_signal(SIGPIPE, brokpipe);
961 /* This may jump, in which case srelax_rele() wouldn't be called, but
962 * it shouldn't matter, because we -- then -- directly reenter the
963 * lex.c:commands() loop, which sreset()s */
964 srelax_hold();
965 for (ip = msgvec; *ip && PTRCMP(ip - msgvec, <, msgCount); ++ip) {
966 mp = &message[*ip - 1];
967 touch(mp);
968 setdot(mp);
969 uncollapse1(mp, 1);
970 if (!dopipe && ip != msgvec)
971 fprintf(obuf, "\n");
972 _show_msg_overview(mp, *ip, obuf);
973 sendmp(mp, obuf, (doign ? ignore : 0), NULL,
974 ((dopipe && boption("piperaw"))
975 ? SEND_MBOX : dodecode
976 ? SEND_SHOW : doign
977 ? SEND_TODISP : SEND_TODISP_ALL),
978 mstats);
979 srelax();
980 if (dopipe && boption("page"))
981 putc('\f', obuf);
982 if (tstats) {
983 tstats[0] += mstats[0];
984 tstats[1] += mstats[1];
987 srelax_rele();
988 close_pipe:
989 if (obuf != stdout) {
990 /* Ignore SIGPIPE so it can't cause a duplicate close */
991 safe_signal(SIGPIPE, SIG_IGN);
992 Pclose(obuf, TRU1);
993 safe_signal(SIGPIPE, dflpipe);
995 return 0;
999 * Pipe the messages requested.
1001 static int
1002 pipe1(char *str, int doign)
1004 char *cmd;
1005 int *msgvec, ret;
1006 off_t stats[2];
1007 bool_t f;
1009 /*LINTED*/
1010 msgvec = (int *)salloc((msgCount + 2) * sizeof *msgvec);
1011 if ((cmd = laststring(str, &f, 1)) == NULL) {
1012 cmd = value("cmd");
1013 if (cmd == NULL || *cmd == '\0') {
1014 fputs(tr(16, "variable cmd not set\n"), stderr);
1015 return 1;
1018 if (!f) {
1019 *msgvec = first(0, MMNORM);
1020 if (*msgvec == 0) {
1021 if (inhook)
1022 return 0;
1023 puts(tr(18, "No messages to pipe."));
1024 return 1;
1026 msgvec[1] = 0;
1027 } else if (getmsglist(str, msgvec, 0) < 0)
1028 return 1;
1029 if (*msgvec == 0) {
1030 if (inhook)
1031 return 0;
1032 printf("No applicable messages.\n");
1033 return 1;
1035 printf(tr(268, "Pipe to: \"%s\"\n"), cmd);
1036 stats[0] = stats[1] = 0;
1037 if ((ret = _type1(msgvec, doign, FAL0, TRU1, FAL0, cmd, stats)) == 0) {
1038 printf("\"%s\" ", cmd);
1039 if (stats[0] >= 0)
1040 printf("%lu", (long)stats[0]);
1041 else
1042 printf(tr(27, "binary"));
1043 printf("/%lu\n", (long)stats[1]);
1045 return ret;
1049 * Paginate messages, honor ignored fields.
1051 FL int
1052 more(void *v)
1054 int *msgvec = v;
1056 return _type1(msgvec, TRU1, TRU1, FAL0, FAL0, NULL, NULL);
1060 * Paginate messages, even printing ignored fields.
1062 FL int
1063 More(void *v)
1065 int *msgvec = v;
1067 return _type1(msgvec, FAL0, TRU1, FAL0, FAL0, NULL, NULL);
1071 * Type out messages, honor ignored fields.
1073 FL int
1074 type(void *v)
1076 int *msgvec = v;
1078 return _type1(msgvec, TRU1, FAL0, FAL0, FAL0, NULL, NULL);
1082 * Type out messages, even printing ignored fields.
1084 FL int
1085 Type(void *v)
1087 int *msgvec = v;
1089 return _type1(msgvec, FAL0, FAL0, FAL0, FAL0, NULL, NULL);
1093 * Show MIME-encoded message text, including all fields.
1095 FL int
1096 show(void *v)
1098 int *msgvec = v;
1100 return _type1(msgvec, FAL0, FAL0, FAL0, TRU1, NULL, NULL);
1104 * Pipe messages, honor ignored fields.
1106 FL int
1107 pipecmd(void *v)
1109 char *str = v;
1110 return(pipe1(str, 1));
1113 * Pipe messages, not respecting ignored fields.
1115 FL int
1116 Pipecmd(void *v)
1118 char *str = v;
1119 return(pipe1(str, 0));
1123 * Print the top so many lines of each desired message.
1124 * The number of lines is taken from the variable "toplines"
1125 * and defaults to 5.
1127 FL int
1128 top(void *v)
1130 int *msgvec = v, *ip, c, topl, lines, empty_last;
1131 struct message *mp;
1132 char *cp, *linebuf = NULL;
1133 size_t linesize;
1134 FILE *ibuf;
1136 topl = 5;
1137 cp = value("toplines");
1138 if (cp != NULL) {
1139 topl = atoi(cp);
1140 if (topl < 0 || topl > 10000)
1141 topl = 5;
1143 empty_last = 1;
1144 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
1145 mp = &message[*ip - 1];
1146 touch(mp);
1147 setdot(mp);
1148 did_print_dot = TRU1;
1149 if (! empty_last)
1150 printf("\n");
1151 _show_msg_overview(mp, *ip, stdout);
1152 if (mp->m_flag & MNOFROM)
1153 printf("From %s %s\n", fakefrom(mp),
1154 fakedate(mp->m_time));
1155 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL) { /* XXX could use TOP */
1156 v = NULL;
1157 break;
1159 c = mp->m_lines;
1160 for (lines = 0; lines < c && UICMP(32, lines, <=, topl);
1161 ++lines) {
1162 if (readline_restart(ibuf, &linebuf, &linesize, 0) < 0)
1163 break;
1164 puts(linebuf);
1166 for (cp = linebuf; *cp && blankchar(*cp); ++cp)
1168 empty_last = (*cp == '\0');
1172 if (linebuf != NULL)
1173 free(linebuf);
1174 return (v != NULL);
1178 * Touch all the given messages so that they will
1179 * get mboxed.
1181 FL int
1182 stouch(void *v)
1184 int *msgvec = v;
1185 int *ip;
1187 for (ip = msgvec; *ip != 0; ip++) {
1188 setdot(&message[*ip-1]);
1189 dot->m_flag |= MTOUCH;
1190 dot->m_flag &= ~MPRESERVE;
1192 * POSIX interpretation necessary.
1194 did_print_dot = TRU1;
1196 return(0);
1200 * Make sure all passed messages get mboxed.
1202 FL int
1203 mboxit(void *v)
1205 int *msgvec = v;
1206 int *ip;
1208 for (ip = msgvec; *ip != 0; ip++) {
1209 setdot(&message[*ip-1]);
1210 dot->m_flag |= MTOUCH|MBOX;
1211 dot->m_flag &= ~MPRESERVE;
1213 * POSIX interpretation necessary.
1215 did_print_dot = TRU1;
1217 return(0);
1221 * List the folders the user currently has.
1223 FL int
1224 folders(void *v)
1226 char dirname[MAXPATHLEN], *name, **argv = v;
1227 char const *cmd;
1229 if (*argv) {
1230 name = expand(*argv);
1231 if (name == NULL)
1232 return 1;
1233 } else if (! getfold(dirname, sizeof dirname)) {
1234 fprintf(stderr, tr(20, "No value set for \"folder\"\n"));
1235 return 1;
1236 } else
1237 name = dirname;
1239 if (which_protocol(name) == PROTO_IMAP) {
1240 #ifdef HAVE_IMAP
1241 imap_folders(name, *argv == NULL);
1242 #else
1243 return ccmdnotsupp(NULL);
1244 #endif
1245 } else {
1246 if ((cmd = value("LISTER")) == NULL)
1247 cmd = LISTER;
1248 run_command(cmd, 0, -1, -1, name, NULL, NULL);
1250 return 0;