openssl.c, catd/en_US: remove some useless strings
[s-mailx.git] / cmd1.c
blob9ef8c78cc8bba36582a61ef09e0a1f0600df2840
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 "nail.h"
43 * Print the current active headings.
44 * Don't change dot if invoker didn't give an argument.
47 static int screen;
49 /* Prepare and print "[Message: xy]:" intro */
50 static void _show_msg_overview(struct message *mp, int msg_no, FILE *obuf);
52 static void onpipe(int signo);
53 static int _dispc(struct message *mp, const char *a);
54 static int scroll1(char *arg, int onlynew);
56 /* ... And place the extracted date in `date' */
57 static void _parse_from_(struct message *mp, char date[FROM_DATEBUF]);
59 /* Get the Subject:, but return NULL if in threaded mode and the message
60 * printed before was in the same thread and had the same subject */
61 static char * _get_subject(struct message *mp, bool_t threaded);
62 static char * __subject_trim(char *s);
64 static void hprf(const char *fmt, int mesg, FILE *f, int threaded,
65 const char *attrlist);
66 static int putindent(FILE *fp, struct message *mp, int maxwidth);
67 static int type1(int *msgvec, int doign, int page, int pipe, int decode,
68 char *cmd, off_t *tstats);
69 static int pipe1(char *str, int doign);
71 static void
72 _show_msg_overview(struct message *mp, int msg_no, FILE *obuf)
74 fprintf(obuf, tr(17, "[-- Message %2d -- %lu lines, %lu bytes --]:\n"),
75 msg_no, (ul_it)mp->m_lines, (ul_it)mp->m_size);
78 int
79 ccmdnotsupp(void *v)
81 (void)v;
82 fprintf(stderr, tr(10, "The requested feature is not compiled in\n"));
83 return (1);
86 char const *
87 get_pager(void)
89 char const *cp;
91 cp = value("PAGER");
92 if (cp == NULL || *cp == '\0')
93 cp = PAGER;
94 return cp;
97 int
98 headers(void *v)
100 int *msgvec = v;
101 int g, k, n, mesg, flag = 0, lastg = 1;
102 struct message *mp, *mq, *lastmq = NULL;
103 int size;
104 enum mflag fl = MNEW|MFLAGGED;
106 time_current_update(&time_current, FAL0);
108 size = screensize();
109 n = msgvec[0]; /* n == {-2, -1, 0}: called from scroll() */
110 if (screen < 0)
111 screen = 0;
112 k = screen * size;
113 if (k >= msgCount)
114 k = msgCount - size;
115 if (k < 0)
116 k = 0;
117 if (mb.mb_threaded == 0) {
118 g = 0;
119 mq = &message[0];
120 for (mp = &message[0]; mp < &message[msgCount]; mp++)
121 if (visible(mp)) {
122 if (g % size == 0)
123 mq = mp;
124 if (mp->m_flag&fl) {
125 lastg = g;
126 lastmq = mq;
128 if ((n > 0 && mp == &message[n-1]) ||
129 (n == 0 && g == k) ||
130 (n == -2 && g == k + size &&
131 lastmq) ||
132 (n < 0 && g >= k &&
133 (mp->m_flag & fl) != 0))
134 break;
135 g++;
137 if (lastmq && (n==-2 || (n==-1 && mp == &message[msgCount]))) {
138 g = lastg;
139 mq = lastmq;
141 screen = g / size;
142 mp = mq;
143 mesg = mp - &message[0];
144 if (dot != &message[n-1]) {
145 for (mq = mp; mq < &message[msgCount]; mq++)
146 if (visible(mq)) {
147 setdot(mq);
148 break;
151 #ifdef HAVE_IMAP
152 if (mb.mb_type == MB_IMAP)
153 imap_getheaders(mesg+1, mesg + size);
154 #endif
155 for (; mp < &message[msgCount]; mp++) {
156 mesg++;
157 if (!visible(mp))
158 continue;
159 if (flag++ >= size)
160 break;
161 printhead(mesg, stdout, 0);
163 } else { /* threaded */
164 g = 0;
165 mq = threadroot;
166 for (mp = threadroot; mp; mp = next_in_thread(mp))
167 if (visible(mp) && (mp->m_collapsed <= 0 ||
168 mp == &message[n-1])) {
169 if (g % size == 0)
170 mq = mp;
171 if (mp->m_flag&fl) {
172 lastg = g;
173 lastmq = mq;
175 if ((n > 0 && mp == &message[n-1]) ||
176 (n == 0 && g == k) ||
177 (n == -2 && g == k + size &&
178 lastmq) ||
179 (n < 0 && g >= k &&
180 (mp->m_flag & fl) != 0))
181 break;
182 g++;
184 if (lastmq && (n==-2 || (n==-1 && mp==&message[msgCount]))) {
185 g = lastg;
186 mq = lastmq;
188 screen = g / size;
189 mp = mq;
190 if (dot != &message[n-1]) {
191 for (mq = mp; mq; mq = next_in_thread(mq))
192 if (visible(mq) && mq->m_collapsed <= 0) {
193 setdot(mq);
194 break;
197 while (mp) {
198 if (visible(mp) && (mp->m_collapsed <= 0 ||
199 mp == &message[n-1])) {
200 if (flag++ >= size)
201 break;
202 printhead(mp - &message[0] + 1, stdout,
203 mb.mb_threaded);
205 mp = next_in_thread(mp);
208 if (flag == 0) {
209 printf(tr(6, "No more mail.\n"));
210 return(1);
212 return(0);
216 * Scroll to the next/previous screen
219 scroll(void *v)
221 return scroll1(v, 0);
225 Scroll(void *v)
227 return scroll1(v, 1);
230 static int
231 scroll1(char *arg, int onlynew)
233 int size;
234 int cur[1];
236 cur[0] = onlynew ? -1 : 0;
237 size = screensize();
238 switch (*arg) {
239 case '1': case '2': case '3': case '4': case '5':
240 case '6': case '7': case '8': case '9': case '0':
241 screen = atoi(arg);
242 goto scroll_forward;
243 case '\0':
244 screen++;
245 goto scroll_forward;
246 case '$':
247 screen = msgCount / size;
248 goto scroll_forward;
249 case '+':
250 if (arg[1] == '\0')
251 screen++;
252 else
253 screen += atoi(arg + 1);
254 scroll_forward:
255 if (screen * size > msgCount) {
256 screen = msgCount / size;
257 printf(tr(7, "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(tr(8, "On first screenful of messages\n"));
270 if (cur[0] == -1)
271 cur[0] = -2;
272 break;
274 default:
275 printf(tr(9, "Unrecognized scrolling command \"%s\"\n"), arg);
276 return(1);
278 return(headers(cur));
282 * Compute screen size.
284 int
285 screensize(void)
287 int s;
288 char *cp;
290 if ((cp = value("screen")) != NULL && (s = atoi(cp)) > 0)
291 return s;
292 return scrnheight - 4;
295 static sigjmp_buf pipejmp;
297 /*ARGSUSED*/
298 static void
299 onpipe(int signo)
301 (void)signo;
302 siglongjmp(pipejmp, 1);
306 * Print out the headlines for each message
307 * in the passed message list.
309 int
310 from(void *v)
312 int *msgvec = v, *ip, n;
313 char *cp;
314 FILE *volatile obuf = stdout;
316 time_current_update(&time_current, FAL0);
318 /* TODO unfixable memory leaks still */
319 if (IS_TTY_SESSION() && (cp = value("crt")) != NULL) {
320 for (n = 0, ip = msgvec; *ip; ip++)
321 n++;
322 if (n > (*cp == '\0' ? screensize() : atoi((char*)cp)) + 3) {
323 char const *p;
324 if (sigsetjmp(pipejmp, 1))
325 goto endpipe;
326 p = get_pager();
327 if ((obuf = Popen(p, "w", NULL, 1)) == NULL) {
328 perror(p);
329 obuf = stdout;
330 cp=NULL;
331 } else
332 safe_signal(SIGPIPE, onpipe);
335 for (ip = msgvec; *ip != 0; ip++)
336 printhead(*ip, obuf, mb.mb_threaded);
337 if (--ip >= msgvec)
338 setdot(&message[*ip - 1]);
339 endpipe:
340 if (obuf != stdout) {
341 safe_signal(SIGPIPE, SIG_IGN);
342 Pclose(obuf, TRU1);
343 safe_signal(SIGPIPE, dflpipe);
345 return(0);
348 static int
349 _dispc(struct message *mp, const char *a)
351 int i = ' ';
354 * Bletch!
356 if ((mp->m_flag & (MREAD|MNEW)) == MREAD)
357 i = a[3];
358 if ((mp->m_flag & (MREAD|MNEW)) == (MREAD|MNEW))
359 i = a[2];
360 if (mp->m_flag & MANSWERED)
361 i = a[8];
362 if (mp->m_flag & MDRAFTED)
363 i = a[9];
364 if ((mp->m_flag & (MREAD|MNEW)) == MNEW)
365 i = a[0];
366 if ((mp->m_flag & (MREAD|MNEW)) == 0)
367 i = a[1];
368 if (mp->m_flag & MSPAM)
369 i = a[12];
370 if (mp->m_flag & MSAVED)
371 i = a[4];
372 if (mp->m_flag & MPRESERVE)
373 i = a[5];
374 if (mp->m_flag & (MBOX|MBOXED))
375 i = a[6];
376 if (mp->m_flag & MFLAGGED)
377 i = a[7];
378 if (mb.mb_threaded == 1 && mp->m_collapsed > 0)
379 i = a[11];
380 if (mb.mb_threaded == 1 && mp->m_collapsed < 0)
381 i = a[10];
382 return i;
385 static void
386 _parse_from_(struct message *mp, char date[FROM_DATEBUF])
388 FILE *ibuf;
389 int hlen;
390 char *hline = NULL;
391 size_t hsize = 0;
393 if ((ibuf = setinput(&mb, mp, NEED_HEADER)) != NULL &&
394 (hlen = readline_restart(ibuf, &hline, &hsize, 0)) > 0)
395 (void)extract_date_from_from_(hline, hlen, date);
396 if (hline != NULL)
397 free(hline);
400 static char *
401 __subject_trim(char *s)
403 while (*s != '\0') {
404 while (spacechar(*s))
405 ++s;
406 if (is_asccaseprefix("re:", s)) {
407 s += 3;
408 continue;
410 if (is_asccaseprefix("fwd:", s)) {
411 s += 4;
412 continue;
414 break;
416 return s;
419 static char *
420 _get_subject(struct message *mp, bool_t threaded)
422 struct str in, out;
423 char *rv = (char*)-1, *ms, *mso, *os;
425 if ((ms = hfield1("subject", mp)) == NULL)
426 goto jleave;
428 if (! threaded || mp->m_level == 0)
429 goto jconv;
431 /* In a display thread - check wether this message uses the same
432 * Subject: as it's parent or elder neighbour, suppress printing it if
433 * this is the case. To extend this a bit, ignore any leading Re: or
434 * Fwd: plus follow-up WS; XXX NOTE: because of efficiency reasons we
435 * XXX simply ignore any encoded parts and use ASCII case-insensitive
436 * XXX comparison */
437 mso = __subject_trim(ms);
439 if (mp->m_elder != NULL &&
440 (os = hfield1("subject", mp->m_elder)) != NULL &&
441 asccasecmp(mso, __subject_trim(os)) == 0)
442 goto jleave;
444 if (mp->m_parent != NULL &&
445 (os = hfield1("subject", mp->m_parent)) != NULL &&
446 asccasecmp(mso, __subject_trim(os)) == 0)
447 goto jleave;
449 jconv:
450 in.s = ms;
451 in.l = strlen(ms);
452 mime_fromhdr(&in, &out, TD_ICONV | TD_ISPR);
453 rv = out.s;
454 jleave:
455 return rv;
458 static void
459 hprf(const char *fmt, int mesg, FILE *f, int threaded, const char *attrlist)
461 char datebuf[FROM_DATEBUF], *cp, *subjline;
462 char const *datefmt, *date, *name, *fp;
463 int B, c, i, n, s, wleft, subjlen, isto = 0, isaddr = 0;
464 struct message *mp = &message[mesg - 1];
465 time_t datet = mp->m_time;
467 date = NULL;
468 if ((datefmt = value("datefield")) != NULL) {
469 fp = hfield1("date", mp);/* TODO use m_date field! */
470 if (fp == NULL) {
471 datefmt = NULL;
472 goto jdate_set;
474 datet = rfctime(fp);
475 date = fakedate(datet);
476 fp = value("datefield-markout-older");
477 i = (*datefmt != '\0');
478 if (fp != NULL)
479 i |= (*fp != '\0') ? 2 | 4 : 2;
480 /* May we strftime(3)? */
481 if (i & (1 | 4))
482 memcpy(&time_current.tc_local, localtime(&datet),
483 sizeof time_current.tc_local);
484 if ((i & 2) &&
485 /* TODO *datefield-markout-older* we accept
486 * TODO one day in the future, should be UTC
487 * TODO offset only? and Stephen Isard had
488 * TODO one week once he proposed the patch! */
489 (datet > time_current.tc_time + DATE_SECSDAY ||
490 #define _6M ((DATE_DAYSYEAR / 2) * DATE_SECSDAY)
491 (datet + _6M < time_current.tc_time))) {
492 #undef _6M
493 if ((datefmt = (i & 4) ? fp : NULL) == NULL) {
494 memset(datebuf, ' ', FROM_DATEBUF); /* xxx ur */
495 memcpy(datebuf + 4, date + 4, 7);
496 datebuf[4 + 7] = ' ';
497 memcpy(datebuf + 4 + 7 + 1, date + 20, 4);
498 datebuf[4 + 7 + 1 + 4] = '\0';
499 date = datebuf;
501 } else if ((i & 1) == 0)
502 datefmt = NULL;
503 } else if (datet == (time_t)0 && (mp->m_flag & MNOFROM) == 0) {
504 /* TODO eliminate this path, query the FROM_ date in setptr(),
505 * TODO all other codepaths do so by themselves ALREADY ?????
506 * TODO assert(mp->m_time != 0);, then
507 * TODO ALSO changes behaviour of markout-non-current */
508 _parse_from_(mp, datebuf);
509 date = datebuf;
510 } else {
511 jdate_set:
512 date = fakedate(datet);
515 isaddr = 1;
516 name = name1(mp, 0);
517 if (name != NULL && value("showto") && is_myname(skin(name))) {
518 if ((cp = hfield1("to", mp)) != NULL) {
519 name = cp;
520 isto = 1;
523 if (name == NULL) {
524 name = "";
525 isaddr = 0;
527 if (isaddr) {
528 if (value("showname"))
529 name = realname(name);
530 else {
531 name = prstr(skin(name));
535 subjline = NULL;
537 /* Detect the width of the non-format characters in *headline*;
538 * like that we can simply use putc() in the next loop, since we have
539 * already calculated their column widths (TODO it's sick) */
540 wleft =
541 subjlen = scrnwidth;
543 for (fp = fmt; *fp; ++fp) {
544 if (*fp == '%') {
545 if (*++fp == '-') {
546 ++fp;
547 } else if (*fp == '+')
548 ++fp;
549 if (digitchar(*fp)) {
550 n = 0;
552 n = 10*n + *fp - '0';
553 while (++fp, digitchar(*fp));
554 subjlen -= n;
557 if (*fp == '\0')
558 break;
559 } else {
560 #if defined HAVE_MBTOWC && defined HAVE_WCWIDTH
561 if (mb_cur_max > 1) {
562 wchar_t wc;
563 if ((s = mbtowc(&wc, fp, mb_cur_max)) < 0)
564 n = s = 1;
565 else if ((n = wcwidth(wc)) < 0)
566 n = 1;
567 } else
568 #endif
569 n = s = 1;
570 subjlen -= n;
571 wleft -= n;
572 while (--s > 0)
573 ++fp;
577 /* Walk *headline*, producing output */
578 for (fp = fmt; *fp; ++fp) {
579 if ((c = *fp & 0xFF) == '%') {
580 B = 0;
581 n = 0;
582 s = 1;
583 if (*++fp == '-') {
584 s = -1;
585 ++fp;
586 } else if (*fp == '+')
587 ++fp;
588 if (digitchar(*fp)) {
590 n = 10*n + *fp - '0';
591 while (++fp, digitchar(*fp));
593 if (*fp == '\0')
594 break;
596 n *= s;
597 switch ((c = *fp & 0xFF)) {
598 case '%':
599 goto jputc;
600 case '>':
601 case '<':
602 if (dot != mp)
603 c = ' ';
604 goto jputc;
605 case 'a':
606 c = _dispc(mp, attrlist);
607 jputc:
608 if (ABS(n) > wleft)
609 n = (n < 0) ? -wleft : wleft;
610 n = fprintf(f, "%*c", n, c);
611 wleft = (n >= 0) ? wleft - n : 0;
612 break;
613 case 'm':
614 if (n == 0) {
615 n = 3;
616 if (threaded)
617 for (i=msgCount; i>999; i/=10)
618 n++;
620 if (ABS(n) > wleft)
621 n = (n < 0) ? -wleft : wleft;
622 n = fprintf(f, "%*d", n, mesg);
623 wleft = (n >= 0) ? wleft - n : 0;
624 break;
625 case 'f':
626 if (n == 0) {
627 n = 18;
628 if (s < 0)
629 n = -n;
631 i = ABS(n);
632 if (i > wleft) {
633 i = wleft;
634 n = (n < 0) ? -wleft : wleft;
636 if (isto) /* XXX tr()! */
637 i -= 3;
638 n = fprintf(f, "%s%s", (isto ? "To " : ""),
639 colalign(name, i, n, &wleft));
640 if (n < 0)
641 wleft = 0;
642 else if (isto)
643 wleft -= 3;
644 break;
645 case 'd':
646 if (datefmt != NULL) {
647 i = strftime(datebuf, sizeof datebuf,
648 datefmt,
649 &time_current.tc_local);
650 if (i != 0)
651 date = datebuf;
652 else
653 fprintf(stderr, tr(174,
654 "Ignored date format, "
655 "it excesses the "
656 "target buffer "
657 "(%lu bytes)\n"),
658 (ul_it)sizeof datebuf);
659 datefmt = NULL;
661 if (n == 0)
662 n = 16;
663 if (ABS(n) > wleft)
664 n = (n < 0) ? -wleft : wleft;
665 n = fprintf(f, "%*.*s", n, n, date);
666 wleft = (n >= 0) ? wleft - n : 0;
667 break;
668 case 'l':
669 if (n == 0)
670 n = 4;
671 if (ABS(n) > wleft)
672 n = (n < 0) ? -wleft : wleft;
673 if (mp->m_xlines) {
674 n = fprintf(f, "%*ld", n, mp->m_xlines);
675 wleft = (n >= 0) ? wleft - n : 0;
676 } else {
677 n = ABS(n);
678 wleft -= n;
679 while (n-- != 0)
680 putc(' ', f);
682 break;
683 case 'o':
684 if (n == 0)
685 n = -5;
686 if (ABS(n) > wleft)
687 n = (n < 0) ? -wleft : wleft;
688 n = fprintf(f, "%*lu", n, (long)mp->m_xsize);
689 wleft = (n >= 0) ? wleft - n : 0;
690 break;
691 case 'i':
692 if (threaded) {
693 n = putindent(f, mp, MIN(wleft,
694 scrnwidth - 60));
695 wleft = (n >= 0) ? wleft - n : 0;
697 break;
698 case 'S':
699 B = 1;
700 /*FALLTHRU*/
701 case 's':
702 if (n == 0)
703 n = subjlen - 2;
704 if (n > 0 && s < 0)
705 n = -n;
706 if (subjlen > wleft)
707 subjlen = wleft;
708 if (ABS(n) > subjlen)
709 n = (n < 0) ? -subjlen : subjlen;
710 if (B)
711 n -= (n < 0) ? -2 : 2;
712 if (n == 0)
713 break;
714 if (subjline == NULL)
715 subjline = _get_subject(mp, threaded);
716 if (subjline == (char*)-1) {
717 n = fprintf(f, "%*s", n, "");
718 wleft = (n >= 0) ? wleft-n : 0;
719 } else {
720 n = fprintf(f, (B ? "\"%s\"" : "%s"),
721 colalign(subjline, ABS(n), n,
722 &wleft));
723 if (n < 0)
724 wleft = 0;
726 break;
727 case 'U':
728 #ifdef HAVE_IMAP
729 if (n == 0)
730 n = 9;
731 if (ABS(n) > wleft)
732 n = (n < 0) ? -wleft : wleft;
733 n = fprintf(f, "%*lu", n, mp->m_uid);
734 wleft = (n >= 0) ? wleft - n : 0;
735 break;
736 #else
737 c = '?';
738 goto jputc;
739 #endif
740 case 'e':
741 if (n == 0)
742 n = 2;
743 if (ABS(n) > wleft)
744 n = (n < 0) ? -wleft : wleft;
745 n = fprintf(f, "%*u", n,
746 threaded == 1 ? mp->m_level : 0);
747 wleft = (n >= 0) ? wleft - n : 0;
748 break;
749 case 't':
750 if (n == 0) {
751 n = 3;
752 if (threaded)
753 for (i=msgCount; i>999; i/=10)
754 n++;
756 if (ABS(n) > wleft)
757 n = (n < 0) ? -wleft : wleft;
758 n = fprintf(f, "%*ld", n,
759 threaded ? mp->m_threadpos : mesg);
760 wleft = (n >= 0) ? wleft - n : 0;
761 break;
762 case '$':
763 #ifdef HAVE_SPAM
764 if (n == 0)
765 n = 4;
766 if (ABS(n) > wleft)
767 n = (n < 0) ? -wleft : wleft;
768 { char buf[16];
769 snprintf(buf, sizeof buf, "%u.%u",
770 (mp->m_spamscore >> 8),
771 (mp->m_spamscore & 0xFF));
772 n = fprintf(f, "%*s", n, buf);
773 wleft = (n >= 0) ? wleft - n : 0;
775 #else
776 c = '?';
777 goto jputc;
778 #endif
781 if (wleft <= 0)
782 break;
783 } else
784 putc(c, f);
786 putc('\n', f);
788 if (subjline != NULL && subjline != (char*)-1)
789 free(subjline);
793 * Print out the indenting in threaded display.
795 static int
796 putindent(FILE *fp, struct message *mp, int maxwidth)/* XXX no magic consts */
798 struct message *mq;
799 int *us, indlvl, indw, i, important = MNEW|MFLAGGED;
800 char *cs;
802 if (mp->m_level == 0 || maxwidth == 0)
803 return 0;
804 cs = ac_alloc(mp->m_level);
805 us = ac_alloc(mp->m_level * sizeof *us);
807 i = mp->m_level - 1;
808 if (mp->m_younger && (unsigned)i + 1 == mp->m_younger->m_level) {
809 if (mp->m_parent && mp->m_parent->m_flag & important)
810 us[i] = mp->m_flag & important ? 0x2523 : 0x2520;
811 else
812 us[i] = mp->m_flag & important ? 0x251D : 0x251C;
813 cs[i] = '+';
814 } else {
815 if (mp->m_parent && mp->m_parent->m_flag & important)
816 us[i] = mp->m_flag & important ? 0x2517 : 0x2516;
817 else
818 us[i] = mp->m_flag & important ? 0x2515 : 0x2514;
819 cs[i] = '\\';
822 mq = mp->m_parent;
823 for (i = mp->m_level - 2; i >= 0; i--) {
824 if (mq) {
825 if ((unsigned)i > mq->m_level - 1) {
826 us[i] = cs[i] = ' ';
827 continue;
829 if (mq->m_younger) {
830 if (mq->m_parent &&
831 mq->m_parent->m_flag&important)
832 us[i] = 0x2503;
833 else
834 us[i] = 0x2502;
835 cs[i] = '|';
836 } else
837 us[i] = cs[i] = ' ';
838 mq = mq->m_parent;
839 } else
840 us[i] = cs[i] = ' ';
843 --maxwidth;
844 for (indlvl = indw = 0; (uc_it)indlvl < mp->m_level &&
845 indw < maxwidth; ++indlvl) {
846 if (indw < maxwidth - 1)
847 indw += (int)putuc(us[indlvl], cs[indlvl] & 0377, fp);
848 else
849 indw += (int)putuc(0x21B8, '^', fp);
851 indw += /*putuc(0x261E, fp)*/putc('>', fp) != EOF;
853 ac_free(us);
854 ac_free(cs);
855 return indw;
858 void
859 printhead(int mesg, FILE *f, int threaded)
861 int bsdflags, bsdheadline, sz;
862 char attrlist[30], *cp;
863 char const *fmt;
865 bsdflags = value("bsdcompat") != NULL || value("bsdflags") != NULL ||
866 getenv("SYSV3") != NULL;
867 strcpy(attrlist, bsdflags ? "NU *HMFAT+-$" : "NUROSPMFAT+-$");
868 if ((cp = value("attrlist")) != NULL) {
869 sz = strlen(cp);
870 if (sz > (int)sizeof attrlist - 1)
871 sz = (int)sizeof attrlist - 1;
872 memcpy(attrlist, cp, sz);
874 bsdheadline = value("bsdcompat") != NULL ||
875 value("bsdheadline") != NULL;
876 if ((fmt = value("headline")) == NULL)
877 fmt = bsdheadline ?
878 "%>%a%m %-20f %16d %3l/%-5o %i%-S" :
879 "%>%a%m %-18f %16d %4l/%-5o %i%-s";
880 hprf(fmt, mesg, f, threaded, attrlist);
884 * Print out the value of dot.
886 /*ARGSUSED*/
887 int
888 pdot(void *v)
890 (void)v;
891 printf("%d\n", (int)(dot - &message[0] + 1));
892 return(0);
896 * Type out the messages requested.
898 static sigjmp_buf pipestop;
900 /*ARGSUSED*/
901 static void
902 brokpipe(int signo)
904 (void)signo;
905 siglongjmp(pipestop, 1);
908 static int
909 type1(int *msgvec, int doign, int page, int pipe, int decode,
910 char *cmd, off_t *tstats)
912 int *ip;
913 struct message *mp;
914 char const *cp;
915 int nlines;
916 off_t mstats[2];
917 FILE *volatile obuf;
919 obuf = stdout;
920 if (sigsetjmp(pipestop, 1))
921 goto close_pipe;
922 if (pipe) {
923 cp = value("SHELL");
924 if (cp == NULL)
925 cp = SHELL;
926 obuf = Popen(cmd, "w", cp, 1);
927 if (obuf == NULL) {
928 perror(cmd);
929 obuf = stdout;
930 } else {
931 safe_signal(SIGPIPE, brokpipe);
933 } else if ((options & OPT_TTYOUT) &&
934 (page || (cp = value("crt")) != NULL)) {
935 nlines = 0;
936 if (!page) {
937 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
938 if ((message[*ip-1].m_have & HAVE_BODY) == 0) {
939 if ((get_body(&message[*ip - 1])) !=
940 OKAY)
941 return 1;
943 nlines += message[*ip - 1].m_lines;
946 if (page || nlines > (*cp ? atoi(cp) : realscreenheight)) {
947 char const *p = get_pager();
948 obuf = Popen(p, "w", NULL, 1);
949 if (obuf == NULL) {
950 perror(p);
951 obuf = stdout;
952 } else
953 safe_signal(SIGPIPE, brokpipe);
956 for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) {
957 mp = &message[*ip - 1];
958 touch(mp);
959 setdot(mp);
960 uncollapse1(mp, 1);
961 if (! pipe && ip != msgvec)
962 fprintf(obuf, "\n");
963 _show_msg_overview(mp, *ip, obuf);
964 sendmp(mp, obuf, doign ? ignore : 0, NULL,
965 pipe && value("piperaw") ? SEND_MBOX :
966 decode ? SEND_SHOW :
967 doign ? SEND_TODISP : SEND_TODISP_ALL,
968 mstats);
969 if (pipe && value("page")) {
970 putc('\f', obuf);
972 if (tstats) {
973 tstats[0] += mstats[0];
974 tstats[1] += mstats[1];
977 close_pipe:
978 if (obuf != stdout) {
980 * Ignore SIGPIPE so it can't cause a duplicate close.
982 safe_signal(SIGPIPE, SIG_IGN);
983 Pclose(obuf, TRU1);
984 safe_signal(SIGPIPE, dflpipe);
986 return(0);
990 * Get the last, possibly quoted part of linebuf.
992 char *
993 laststring(char *linebuf, int *flag, int strip)
995 char *cp, *p;
996 char quoted;
998 *flag = 1;
999 cp = strlen(linebuf) + linebuf - 1;
1002 * Strip away trailing blanks.
1004 while (cp > linebuf && whitechar(*cp & 0377))
1005 cp--;
1006 *++cp = 0;
1007 if (cp == linebuf) {
1008 *flag = 0;
1009 return NULL;
1013 * Now search for the beginning of the command name.
1015 quoted = *(cp - 1);
1016 if (quoted == '\'' || quoted == '\"') {
1017 cp--;
1018 if (strip)
1019 *cp = '\0';
1020 cp--;
1021 while (cp > linebuf) {
1022 if (*cp != quoted) {
1023 cp--;
1024 } else if (*(cp - 1) != '\\') {
1025 break;
1026 } else {
1027 p = --cp;
1028 do {
1029 *p = *(p + 1);
1030 } while (*p++);
1031 cp--;
1034 if (cp == linebuf)
1035 *flag = 0;
1036 if (*cp == quoted) {
1037 if (strip)
1038 *cp++ = 0;
1039 } else
1040 *flag = 0;
1041 } else {
1042 while (cp > linebuf && !whitechar(*cp & 0377))
1043 cp--;
1044 if (whitechar(*cp & 0377))
1045 *cp++ = 0;
1046 else
1047 *flag = 0;
1049 if (*cp == '\0') {
1050 return(NULL);
1052 return(cp);
1056 * Pipe the messages requested.
1058 static int
1059 pipe1(char *str, int doign)
1061 char *cmd;
1062 int f, *msgvec, ret;
1063 off_t stats[2];
1065 /*LINTED*/
1066 msgvec = (int *)salloc((msgCount + 2) * sizeof *msgvec);
1067 if ((cmd = laststring(str, &f, 1)) == NULL) {
1068 cmd = value("cmd");
1069 if (cmd == NULL || *cmd == '\0') {
1070 fputs(tr(16, "variable cmd not set\n"), stderr);
1071 return 1;
1074 if (!f) {
1075 *msgvec = first(0, MMNORM);
1076 if (*msgvec == 0) {
1077 if (inhook)
1078 return 0;
1079 puts(tr(18, "No messages to pipe."));
1080 return 1;
1082 msgvec[1] = 0;
1083 } else if (getmsglist(str, msgvec, 0) < 0)
1084 return 1;
1085 if (*msgvec == 0) {
1086 if (inhook)
1087 return 0;
1088 printf("No applicable messages.\n");
1089 return 1;
1091 printf(tr(268, "Pipe to: \"%s\"\n"), cmd);
1092 stats[0] = stats[1] = 0;
1093 if ((ret = type1(msgvec, doign, 0, 1, 0, cmd, stats)) == 0) {
1094 printf("\"%s\" ", cmd);
1095 if (stats[0] >= 0)
1096 printf("%lu", (long)stats[0]);
1097 else
1098 printf(tr(27, "binary"));
1099 printf("/%lu\n", (long)stats[1]);
1101 return ret;
1105 * Paginate messages, honor ignored fields.
1107 int
1108 more(void *v)
1110 int *msgvec = v;
1111 return (type1(msgvec, 1, 1, 0, 0, NULL, NULL));
1115 * Paginate messages, even printing ignored fields.
1117 int
1118 More(void *v)
1120 int *msgvec = v;
1122 return (type1(msgvec, 0, 1, 0, 0, NULL, NULL));
1126 * Type out messages, honor ignored fields.
1128 int
1129 type(void *v)
1131 int *msgvec = v;
1133 return(type1(msgvec, 1, 0, 0, 0, NULL, NULL));
1137 * Type out messages, even printing ignored fields.
1139 int
1140 Type(void *v)
1142 int *msgvec = v;
1144 return(type1(msgvec, 0, 0, 0, 0, NULL, NULL));
1148 * Show MIME-encoded message text, including all fields.
1151 show(void *v)
1153 int *msgvec = v;
1155 return(type1(msgvec, 0, 0, 0, 1, NULL, NULL));
1159 * Pipe messages, honor ignored fields.
1161 int
1162 pipecmd(void *v)
1164 char *str = v;
1165 return(pipe1(str, 1));
1168 * Pipe messages, not respecting ignored fields.
1170 int
1171 Pipecmd(void *v)
1173 char *str = v;
1174 return(pipe1(str, 0));
1178 * Print the top so many lines of each desired message.
1179 * The number of lines is taken from the variable "toplines"
1180 * and defaults to 5.
1182 int
1183 top(void *v)
1185 int *msgvec = v, *ip, c, topl, lines, empty_last;
1186 struct message *mp;
1187 char *cp, *linebuf = NULL;
1188 size_t linesize;
1189 FILE *ibuf;
1191 topl = 5;
1192 cp = value("toplines");
1193 if (cp != NULL) {
1194 topl = atoi(cp);
1195 if (topl < 0 || topl > 10000)
1196 topl = 5;
1198 empty_last = 1;
1199 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
1200 mp = &message[*ip - 1];
1201 touch(mp);
1202 setdot(mp);
1203 did_print_dot = TRU1;
1204 if (! empty_last)
1205 printf("\n");
1206 _show_msg_overview(mp, *ip, stdout);
1207 if (mp->m_flag & MNOFROM)
1208 printf("From %s %s\n", fakefrom(mp),
1209 fakedate(mp->m_time));
1210 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL) { /* XXX could use TOP */
1211 v = NULL;
1212 break;
1214 c = mp->m_lines;
1215 for (lines = 0; lines < c && lines <= topl; lines++) {
1216 if (readline_restart(ibuf, &linebuf, &linesize, 0) < 0)
1217 break;
1218 puts(linebuf);
1220 for (cp = linebuf; *cp && blankchar(*cp); ++cp)
1222 empty_last = (*cp == '\0');
1226 if (linebuf != NULL)
1227 free(linebuf);
1228 return (v != NULL);
1232 * Touch all the given messages so that they will
1233 * get mboxed.
1235 int
1236 stouch(void *v)
1238 int *msgvec = v;
1239 int *ip;
1241 for (ip = msgvec; *ip != 0; ip++) {
1242 setdot(&message[*ip-1]);
1243 dot->m_flag |= MTOUCH;
1244 dot->m_flag &= ~MPRESERVE;
1246 * POSIX interpretation necessary.
1248 did_print_dot = TRU1;
1250 return(0);
1254 * Make sure all passed messages get mboxed.
1256 int
1257 mboxit(void *v)
1259 int *msgvec = v;
1260 int *ip;
1262 for (ip = msgvec; *ip != 0; ip++) {
1263 setdot(&message[*ip-1]);
1264 dot->m_flag |= MTOUCH|MBOX;
1265 dot->m_flag &= ~MPRESERVE;
1267 * POSIX interpretation necessary.
1269 did_print_dot = TRU1;
1271 return(0);
1275 * List the folders the user currently has.
1277 int
1278 folders(void *v)
1280 char dirname[MAXPATHLEN], *name, **argv = v;
1281 char const *cmd;
1283 if (*argv) {
1284 name = expand(*argv);
1285 if (name == NULL)
1286 return 1;
1287 } else if (! getfold(dirname, sizeof dirname)) {
1288 fprintf(stderr, tr(20, "No value set for \"folder\"\n"));
1289 return 1;
1290 } else
1291 name = dirname;
1293 if (which_protocol(name) == PROTO_IMAP) {
1294 #ifdef HAVE_IMAP
1295 imap_folders(name, *argv == NULL);
1296 #else
1297 return ccmdnotsupp(NULL);
1298 #endif
1299 } else {
1300 if ((cmd = value("LISTER")) == NULL)
1301 cmd = LISTER;
1302 run_command(cmd, 0, -1, -1, name, NULL, NULL);
1304 return 0;