Fix QP encoding canary violation (Peter Hofmann)..
[s-mailx.git] / cmd1.c
blob455558eae6fcf75f291d8c4a1d44cb71d038ddf4
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 - 2014 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
44 static int _screen;
45 static sigjmp_buf _cmd1_pipestop;
46 static sigjmp_buf _cmd1_pipejmp;
48 static void _cmd1_onpipe(int signo);
49 static void _cmd1_brokpipe(int signo);
51 /* Prepare and print "[Message: xy]:" intro */
52 static void _show_msg_overview(FILE *obuf, struct message *mp, int msg_no);
54 /* ... And place the extracted date in `date' */
55 static void _parse_from_(struct message *mp, char date[FROM_DATEBUF]);
57 /* Print out the header of a specific message
58 * __hprf: handle *headline*
59 * __subject: Subject:, but return NULL if threaded and Subject: yet seen
60 * __putindent: print out the indenting in threaded display */
61 static void _print_head(size_t yetprinted, size_t msgno, FILE *f,
62 bool_t threaded);
63 static void __hprf(size_t yetprinted, char const *fmt, size_t msgno,
64 FILE *f, bool_t threaded, char const *attrlist);
65 static char * __subject(struct message *mp, bool_t threaded,
66 size_t yetprinted);
67 static int __putindent(FILE *fp, struct message *mp, int maxwidth);
69 static int _dispc(struct message *mp, char const *a);
70 static int _scroll1(char *arg, int onlynew);
72 /* Show the requested messages */
73 static int _type1(int *msgvec, bool_t doign, bool_t dopage, bool_t dopipe,
74 bool_t dodecode, char *cmd, off_t *tstats);
76 /* Pipe the requested messages */
77 static int _pipe1(char *str, int doign);
79 static void
80 _cmd1_onpipe(int signo)
82 NYD_X; /* Signal handler */
83 UNUSED(signo);
84 siglongjmp(_cmd1_pipejmp, 1);
87 static void
88 _cmd1_brokpipe(int signo)
90 NYD_X; /* Signal handler */
91 UNUSED(signo);
92 siglongjmp(_cmd1_pipestop, 1);
95 static void
96 _show_msg_overview(FILE *obuf, struct message *mp, int msg_no)
98 char const *cpre = "", *csuf = "";
99 NYD_ENTER;
101 #ifdef HAVE_COLOUR
102 if (colour_table != NULL) {
103 struct str const *sp;
105 if ((sp = colour_get(COLOURSPEC_MSGINFO)) != NULL)
106 cpre = sp->s;
107 csuf = colour_get(COLOURSPEC_RESET)->s;
109 #endif
110 fprintf(obuf, _("%s[-- Message %2d -- %lu lines, %lu bytes --]:%s\n"),
111 cpre, msg_no, (ul_it)mp->m_lines, (ul_it)mp->m_size, csuf);
112 NYD_LEAVE;
115 static void
116 _parse_from_(struct message *mp, char date[FROM_DATEBUF]) /* TODO line pool */
118 FILE *ibuf;
119 int hlen;
120 char *hline = NULL;
121 size_t hsize = 0;
122 NYD_ENTER;
124 if ((ibuf = setinput(&mb, mp, NEED_HEADER)) != NULL &&
125 (hlen = readline_restart(ibuf, &hline, &hsize, 0)) > 0)
126 extract_date_from_from_(hline, hlen, date);
127 if (hline != NULL)
128 free(hline);
129 NYD_LEAVE;
132 static void
133 _print_head(size_t yetprinted, size_t msgno, FILE *f, bool_t threaded)
135 enum {attrlen = 13};
136 char attrlist[attrlen +1], *cp;
137 char const *fmt;
138 NYD_ENTER;
140 if ((cp = ok_vlook(attrlist)) != NULL) {
141 if (strlen(cp) == attrlen) {
142 memcpy(attrlist, cp, attrlen +1);
143 goto jattrok;
145 fprintf(stderr, _(
146 "The value of *attrlist* is not of the correct length\n"));
148 if (ok_blook(bsdcompat) || ok_blook(bsdflags) ||
149 getenv("SYSV3") != NULL) {
150 char const bsdattr[attrlen +1] = "NU *HMFAT+-$";
151 memcpy(attrlist, bsdattr, sizeof bsdattr);
152 } else {
153 char const pattr[attrlen +1] = "NUROSPMFAT+-$";
154 memcpy(attrlist, pattr, sizeof pattr);
156 jattrok:
157 if ((fmt = ok_vlook(headline)) == NULL) {
158 fmt = ((ok_blook(bsdcompat) || ok_blook(bsdheadline))
159 ? "%>%a%m %-20f %16d %3l/%-5o %i%-S"
160 : "%>%a%m %-18f %16d %4l/%-5o %i%-s");
163 __hprf(yetprinted, fmt, msgno, f, threaded, attrlist);
164 NYD_LEAVE;
167 static void
168 __hprf(size_t yetprinted, char const *fmt, size_t msgno, FILE *f,
169 bool_t threaded, char const *attrlist)
171 char datebuf[FROM_DATEBUF], *cp, *subjline;
172 char const *datefmt, *date, *name, *fp;
173 int i, n, s, wleft, subjlen;
174 struct message *mp;
175 time_t datet;
176 enum {
177 _NONE = 0,
178 _ISADDR = 1<<0,
179 _ISTO = 1<<1,
180 _IFMT = 1<<2,
181 _LOOP_MASK = (1<<3) - 1,
182 _SFMT = 1<<3
183 } flags = _NONE;
184 NYD_ENTER;
186 mp = message + msgno - 1;
187 datet = mp->m_time;
188 date = NULL;
190 datefmt = ok_vlook(datefield);
191 jredo:
192 if (datefmt != NULL) {
193 fp = hfield1("date", mp);/* TODO use m_date field! */
194 if (fp == NULL) {
195 datefmt = NULL;
196 goto jredo;
198 datet = rfctime(fp);
199 date = fakedate(datet);
200 fp = ok_vlook(datefield_markout_older);
201 i = (*datefmt != '\0');
202 if (fp != NULL)
203 i |= (*fp != '\0') ? 2 | 4 : 2; /* XXX no magics */
205 /* May we strftime(3)? */
206 if (i & (1 | 4))
207 memcpy(&time_current.tc_local, localtime(&datet),
208 sizeof time_current.tc_local);
210 if ((i & 2) && (datet > time_current.tc_time + DATE_SECSDAY ||
211 #define _6M ((DATE_DAYSYEAR / 2) * DATE_SECSDAY)
212 (datet + _6M < time_current.tc_time))) {
213 #undef _6M
214 if ((datefmt = (i & 4) ? fp : NULL) == NULL) {
215 memset(datebuf, ' ', FROM_DATEBUF); /* xxx ur */
216 memcpy(datebuf + 4, date + 4, 7);
217 datebuf[4 + 7] = ' ';
218 memcpy(datebuf + 4 + 7 + 1, date + 20, 4);
219 datebuf[4 + 7 + 1 + 4] = '\0';
220 date = datebuf;
222 } else if ((i & 1) == 0)
223 datefmt = NULL;
224 } else if (datet == (time_t)0 && !(mp->m_flag & MNOFROM)) {
225 /* TODO eliminate this path, query the FROM_ date in setptr(),
226 * TODO all other codepaths do so by themselves ALREADY ?????
227 * TODO assert(mp->m_time != 0);, then
228 * TODO ALSO changes behaviour of datefield_markout_older */
229 _parse_from_(mp, datebuf);
230 date = datebuf;
231 } else
232 date = fakedate(datet);
234 flags |= _ISADDR;
235 name = name1(mp, 0);
236 if (name != NULL && ok_blook(showto) && is_myname(skin(name))) {
237 if ((cp = hfield1("to", mp)) != NULL) {
238 name = cp;
239 flags |= _ISTO;
242 if (name == NULL) {
243 name = "";
244 flags &= ~_ISADDR;
246 if (flags & _ISADDR)
247 name = ok_blook(showname) ? realname(name) : prstr(skin(name));
249 subjline = NULL;
251 /* Detect the width of the non-format characters in *headline*;
252 * like that we can simply use putc() in the next loop, since we have
253 * already calculated their column widths (TODO it's sick) */
254 wleft = subjlen = scrnwidth;
256 for (fp = fmt; *fp != '\0'; ++fp) {
257 if (*fp == '%') {
258 if (*++fp == '-')
259 ++fp;
260 else if (*fp == '+')
261 ++fp;
262 if (digitchar(*fp)) {
263 n = 0;
265 n = 10*n + *fp - '0';
266 while (++fp, digitchar(*fp));
267 subjlen -= n;
269 if (*fp == 'i')
270 flags |= _IFMT;
272 if (*fp == '\0')
273 break;
274 } else {
275 #ifdef HAVE_WCWIDTH
276 if (mb_cur_max > 1) {
277 wchar_t wc;
278 if ((s = mbtowc(&wc, fp, mb_cur_max)) == -1)
279 n = s = 1;
280 else if ((n = wcwidth(wc)) == -1)
281 n = 1;
282 } else
283 #endif
284 n = s = 1;
285 subjlen -= n;
286 wleft -= n;
287 while (--s > 0)
288 ++fp;
292 /* Walk *headline*, producing output TODO not (really) MB safe */
293 for (fp = fmt; *fp != '\0'; ++fp) {
294 char c;
295 if ((c = *fp & 0xFF) != '%')
296 putc(c, f);
297 else {
298 flags &= _LOOP_MASK;
299 n = 0;
300 s = 1;
301 if (*++fp == '-') {
302 s = -1;
303 ++fp;
304 } else if (*fp == '+')
305 ++fp;
306 if (digitchar(*fp)) {
308 n = 10*n + *fp - '0';
309 while (++fp, digitchar(*fp));
311 if (*fp == '\0')
312 break;
314 n *= s;
315 switch ((c = *fp & 0xFF)) {
316 case '%':
317 goto jputc;
318 case '>':
319 case '<':
320 if (dot != mp)
321 c = ' ';
322 goto jputc;
323 case '$':
324 #ifdef HAVE_SPAM
325 if (n == 0)
326 n = 4;
327 if (UICMP(32, ABS(n), >, wleft))
328 n = (n < 0) ? -wleft : wleft;
329 { char buf[16];
330 snprintf(buf, sizeof buf, "%u.%u",
331 (mp->m_spamscore >> 8), (mp->m_spamscore & 0xFF));
332 n = fprintf(f, "%*s", n, buf);
333 wleft = (n >= 0) ? wleft - n : 0;
335 #else
336 c = '?';
337 goto jputc;
338 #endif
339 case 'a':
340 c = _dispc(mp, attrlist);
341 jputc:
342 if (UICMP(32, ABS(n), >, wleft))
343 n = (n < 0) ? -wleft : wleft;
344 n = fprintf(f, "%*c", n, c);
345 wleft = (n >= 0) ? wleft - n : 0;
346 break;
347 case 'd':
348 if (datefmt != NULL) {
349 i = strftime(datebuf, sizeof datebuf, datefmt,
350 &time_current.tc_local);
351 if (i != 0)
352 date = datebuf;
353 else
354 fprintf(stderr, _(
355 "Ignored date format, it excesses the target buffer "
356 "(%lu bytes)\n"), (ul_it)sizeof datebuf);
357 datefmt = NULL;
359 if (n == 0)
360 n = 16;
361 if (UICMP(32, ABS(n), >, wleft))
362 n = (n < 0) ? -wleft : wleft;
363 n = fprintf(f, "%*.*s", n, n, date);
364 wleft = (n >= 0) ? wleft - n : 0;
365 break;
366 case 'e':
367 if (n == 0)
368 n = 2;
369 if (UICMP(32, ABS(n), >, wleft))
370 n = (n < 0) ? -wleft : wleft;
371 n = fprintf(f, "%*u", n, (threaded == 1 ? mp->m_level : 0));
372 wleft = (n >= 0) ? wleft - n : 0;
373 break;
374 case 'f':
375 if (n == 0) {
376 n = 18;
377 if (s < 0)
378 n = -n;
380 i = ABS(n);
381 if (i > wleft) {
382 i = wleft;
383 n = (n < 0) ? -wleft : wleft;
385 if (flags & _ISTO) /* XXX tr()! */
386 i -= 3;
387 n = fprintf(f, "%s%s", ((flags & _ISTO) ? "To " : ""),
388 colalign(name, i, n, &wleft));
389 if (n < 0)
390 wleft = 0;
391 else if (flags & _ISTO)
392 wleft -= 3;
393 break;
394 case 'i':
395 if (threaded) {
396 n = __putindent(f, mp, MIN(wleft, scrnwidth - 60));
397 wleft = (n >= 0) ? wleft - n : 0;
399 break;
400 case 'l':
401 if (n == 0)
402 n = 4;
403 if (UICMP(32, ABS(n), >, wleft))
404 n = (n < 0) ? -wleft : wleft;
405 if (mp->m_xlines) {
406 n = fprintf(f, "%*ld", n, mp->m_xlines);
407 wleft = (n >= 0) ? wleft - n : 0;
408 } else {
409 n = ABS(n);
410 wleft -= n;
411 while (n-- != 0)
412 putc(' ', f);
414 break;
415 case 'm':
416 if (n == 0) {
417 n = 3;
418 if (threaded)
419 for (i = msgCount; i > 999; i /= 10)
420 ++n;
422 if (UICMP(32, ABS(n), >, wleft))
423 n = (n < 0) ? -wleft : wleft;
424 n = fprintf(f, "%*lu", n, (ul_it)msgno);
425 wleft = (n >= 0) ? wleft - n : 0;
426 break;
427 case 'o':
428 if (n == 0)
429 n = -5;
430 if (UICMP(32, ABS(n), >, wleft))
431 n = (n < 0) ? -wleft : wleft;
432 n = fprintf(f, "%*lu", n, (long)mp->m_xsize);
433 wleft = (n >= 0) ? wleft - n : 0;
434 break;
435 case 'S':
436 flags |= _SFMT;
437 /*FALLTHRU*/
438 case 's':
439 if (n == 0)
440 n = subjlen - 2;
441 if (n > 0 && s < 0)
442 n = -n;
443 if (subjlen > wleft)
444 subjlen = wleft;
445 if (UICMP(32, ABS(n), >, subjlen))
446 n = (n < 0) ? -subjlen : subjlen;
447 if (flags & _SFMT)
448 n -= (n < 0) ? -2 : 2;
449 if (n == 0)
450 break;
451 if (subjline == NULL)
452 subjline = __subject(mp, (threaded && (flags & _IFMT)),
453 yetprinted);
454 if (subjline == (char*)-1) {
455 n = fprintf(f, "%*s", n, "");
456 wleft = (n >= 0) ? wleft - n : 0;
457 } else {
458 n = fprintf(f, ((flags & _SFMT) ? "\"%s\"" : "%s"),
459 colalign(subjline, ABS(n), n, &wleft));
460 if (n < 0)
461 wleft = 0;
463 break;
464 case 't':
465 if (n == 0) {
466 n = 3;
467 if (threaded)
468 for (i = msgCount; i > 999; i /= 10)
469 ++n;
471 if (UICMP(32, ABS(n), >, wleft))
472 n = (n < 0) ? -wleft : wleft;
473 n = fprintf(f, "%*lu", n,
474 (threaded ? (ul_it)mp->m_threadpos : (ul_it)msgno));
475 wleft = (n >= 0) ? wleft - n : 0;
476 break;
477 case 'U':
478 #ifdef HAVE_IMAP
479 if (n == 0)
480 n = 9;
481 if (UICMP(32, ABS(n), >, wleft))
482 n = (n < 0) ? -wleft : wleft;
483 n = fprintf(f, "%*lu", n, mp->m_uid);
484 wleft = (n >= 0) ? wleft - n : 0;
485 break;
486 #else
487 c = '?';
488 goto jputc;
489 #endif
492 if (wleft <= 0)
493 break;
496 putc('\n', f);
498 if (subjline != NULL && subjline != (char*)-1)
499 free(subjline);
500 NYD_LEAVE;
503 static char *
504 __subject(struct message *mp, bool_t threaded, size_t yetprinted)
506 /* XXX NOTE: because of efficiency reasons we simply ignore any encoded
507 * XXX parts and use ASCII case-insensitive comparison */
508 struct str in, out;
509 struct message *xmp;
510 char *rv = (char*)-1, *ms, *mso, *os;
511 NYD_ENTER;
513 if ((ms = hfield1("subject", mp)) == NULL)
514 goto jleave;
516 if (!threaded || mp->m_level == 0)
517 goto jconv;
519 /* In a display thread - check wether this message uses the same
520 * Subject: as it's parent or elder neighbour, suppress printing it if
521 * this is the case. To extend this a bit, ignore any leading Re: or
522 * Fwd: plus follow-up WS. Ignore invisible messages along the way */
523 mso = subject_re_trim(ms);
524 for (xmp = mp; (xmp = prev_in_thread(xmp)) != NULL && yetprinted-- > 0;)
525 if (visible(xmp) && (os = hfield1("subject", xmp)) != NULL &&
526 !asccasecmp(mso, subject_re_trim(os)))
527 goto jleave;
528 jconv:
529 in.s = ms;
530 in.l = strlen(ms);
531 mime_fromhdr(&in, &out, TD_ICONV | TD_ISPR);
532 rv = out.s;
533 jleave:
534 NYD_LEAVE;
535 return rv;
538 static int
539 __putindent(FILE *fp, struct message *mp, int maxwidth)/* XXX no magic consts */
541 struct message *mq;
542 int *us, indlvl, indw, i, important = MNEW | MFLAGGED;
543 char *cs;
544 NYD_ENTER;
546 if (mp->m_level == 0 || maxwidth == 0) {
547 indw = 0;
548 goto jleave;
551 cs = ac_alloc(mp->m_level);
552 us = ac_alloc(mp->m_level * sizeof *us);
554 i = mp->m_level - 1;
555 if (mp->m_younger && UICMP(32, i + 1, ==, mp->m_younger->m_level)) {
556 if (mp->m_parent && mp->m_parent->m_flag & important)
557 us[i] = mp->m_flag & important ? 0x2523 : 0x2520;
558 else
559 us[i] = mp->m_flag & important ? 0x251D : 0x251C;
560 cs[i] = '+';
561 } else {
562 if (mp->m_parent && mp->m_parent->m_flag & important)
563 us[i] = mp->m_flag & important ? 0x2517 : 0x2516;
564 else
565 us[i] = mp->m_flag & important ? 0x2515 : 0x2514;
566 cs[i] = '\\';
569 mq = mp->m_parent;
570 for (i = mp->m_level - 2; i >= 0; --i) {
571 if (mq) {
572 if (UICMP(32, i, >, mq->m_level - 1)) {
573 us[i] = cs[i] = ' ';
574 continue;
576 if (mq->m_younger) {
577 if (mq->m_parent && (mq->m_parent->m_flag & important))
578 us[i] = 0x2503;
579 else
580 us[i] = 0x2502;
581 cs[i] = '|';
582 } else
583 us[i] = cs[i] = ' ';
584 mq = mq->m_parent;
585 } else
586 us[i] = cs[i] = ' ';
589 --maxwidth;
590 for (indlvl = indw = 0; (ui8_t)indlvl < mp->m_level && indw < maxwidth;
591 ++indlvl) {
592 if (indw < maxwidth - 1)
593 indw += (int)putuc(us[indlvl], cs[indlvl] & 0xFF, fp);
594 else
595 indw += (int)putuc(0x21B8, '^', fp);
597 indw += (/*putuc(0x261E, fp)*/putc('>', fp) != EOF);
599 ac_free(us);
600 ac_free(cs);
601 jleave:
602 NYD_LEAVE;
603 return indw;
606 static int
607 _dispc(struct message *mp, char const *a)
609 int i = ' ';
610 NYD_ENTER;
612 if ((mp->m_flag & (MREAD | MNEW)) == MREAD)
613 i = a[3];
614 if ((mp->m_flag & (MREAD | MNEW)) == (MREAD | MNEW))
615 i = a[2];
616 if (mp->m_flag & MANSWERED)
617 i = a[8];
618 if (mp->m_flag & MDRAFTED)
619 i = a[9];
620 if ((mp->m_flag & (MREAD | MNEW)) == MNEW)
621 i = a[0];
622 if (!(mp->m_flag & (MREAD | MNEW)))
623 i = a[1];
624 if (mp->m_flag & MSPAM)
625 i = a[12];
626 if (mp->m_flag & MSAVED)
627 i = a[4];
628 if (mp->m_flag & MPRESERVE)
629 i = a[5];
630 if (mp->m_flag & (MBOX | MBOXED))
631 i = a[6];
632 if (mp->m_flag & MFLAGGED)
633 i = a[7];
634 if (mb.mb_threaded == 1 && mp->m_collapsed > 0)
635 i = a[11];
636 if (mb.mb_threaded == 1 && mp->m_collapsed < 0)
637 i = a[10];
638 NYD_LEAVE;
639 return i;
642 static int
643 _scroll1(char *arg, int onlynew)
645 int cur[1], size;
646 NYD_ENTER;
648 cur[0] = onlynew ? -1 : 0;
649 size = screensize();
650 switch (*arg) {
651 case '1': case '2': case '3': case '4': case '5':
652 case '6': case '7': case '8': case '9': case '0':
653 _screen = atoi(arg);
654 goto jscroll_forward;
655 case '\0':
656 ++_screen;
657 goto jscroll_forward;
658 case '$':
659 _screen = msgCount / size;
660 goto jscroll_forward;
661 case '+':
662 if (arg[1] == '\0')
663 ++_screen;
664 else
665 _screen += atoi(arg + 1);
666 jscroll_forward:
667 if (_screen * size > msgCount) {
668 _screen = msgCount / size;
669 printf(_("On last screenful of messages\n"));
671 break;
673 case '-':
674 if (arg[1] == '\0')
675 --_screen;
676 else
677 _screen -= atoi(arg + 1);
678 if (_screen < 0) {
679 _screen = 0;
680 printf(_("On first screenful of messages\n"));
682 if (cur[0] == -1)
683 cur[0] = -2;
684 break;
686 default:
687 fprintf(stderr, _("Unrecognized scrolling command \"%s\"\n"), arg);
688 size = 1;
689 goto jleave;
691 size = c_headers(cur);
692 jleave:
693 NYD_LEAVE;
694 return size;
697 static int
698 _type1(int *msgvec, bool_t doign, bool_t dopage, bool_t dopipe,
699 bool_t dodecode, char *cmd, off_t *tstats)
701 off_t mstats[2];
702 int rv, *ip;
703 struct message *mp;
704 char const *cp;
705 FILE * volatile obuf;
706 bool_t volatile hadsig = FAL0, isrelax = FAL0;
707 NYD_ENTER;
708 {/* C89.. */
709 enum sendaction const action = ((dopipe && ok_blook(piperaw))
710 ? SEND_MBOX : dodecode
711 ? SEND_SHOW : doign
712 ? SEND_TODISP : SEND_TODISP_ALL);
713 bool_t const volatile formfeed = (dopipe && ok_blook(page));
714 obuf = stdout;
716 if (sigsetjmp(_cmd1_pipestop, 1)) {
717 hadsig = TRU1;
718 goto jclose_pipe;
721 if (dopipe) {
722 if ((cp = ok_vlook(SHELL)) == NULL)
723 cp = XSHELL;
724 if ((obuf = Popen(cmd, "w", cp, NULL, 1)) == NULL) {
725 perror(cmd);
726 obuf = stdout;
727 } else
728 safe_signal(SIGPIPE, &_cmd1_brokpipe);
729 } else if ((options & OPT_TTYOUT) &&
730 (dopage || (cp = ok_vlook(crt)) != NULL)) {
731 char const *pager = NULL;
732 size_t nlines = 0;
734 if (!dopage) {
735 for (ip = msgvec; *ip && PTRCMP(ip - msgvec, <, msgCount); ++ip) {
736 mp = message + *ip - 1;
737 if (!(mp->m_have & HAVE_BODY))
738 if (get_body(mp) != OKAY) {
739 rv = 1;
740 goto jleave;
742 nlines += mp->m_lines + 1; /* Message info XXX and PARTS... */
746 /* `>=' not `<': we return to the prompt */
747 if (dopage || UICMP(z, nlines, >=,
748 (*cp != '\0' ? atoi(cp) : realscreenheight))) {
749 char const *env_add[2];
750 pager = get_pager(env_add + 0);
751 env_add[1] = NULL;
752 obuf = Popen(pager, "w", NULL, env_add, 1);
753 if (obuf == NULL) {
754 perror(pager);
755 obuf = stdout;
756 pager = NULL;
757 } else
758 safe_signal(SIGPIPE, &_cmd1_brokpipe);
760 #ifdef HAVE_COLOUR
761 if (action != SEND_MBOX)
762 colour_table_create(pager != NULL); /* (salloc()s!) */
763 #endif
765 #ifdef HAVE_COLOUR
766 else if ((options & OPT_TTYOUT) && action != SEND_MBOX)
767 colour_table_create(FAL0); /* (salloc()s!) */
768 #endif
770 /*TODO unless we have our signal manager special care must be taken */
771 srelax_hold();
772 isrelax = TRU1;
773 for (ip = msgvec; *ip && PTRCMP(ip - msgvec, <, msgCount); ++ip) {
774 mp = message + *ip - 1;
775 touch(mp);
776 setdot(mp);
777 uncollapse1(mp, 1);
778 if (!dopipe) {
779 if (ip != msgvec)
780 fprintf(obuf, "\n");
781 if (action != SEND_MBOX)
782 _show_msg_overview(obuf, mp, *ip);
784 sendmp(mp, obuf, (doign ? ignore : NULL), NULL, action, mstats);
785 srelax();
786 if (formfeed) /* TODO a nicer way to separate piped messages! */
787 putc('\f', obuf);
788 if (tstats) {
789 tstats[0] += mstats[0];
790 tstats[1] += mstats[1];
793 srelax_rele();
794 isrelax = FAL0;
796 jclose_pipe:
797 if (obuf != stdout) {
798 /* Ignore SIGPIPE so it can't cause a duplicate close */
799 safe_signal(SIGPIPE, SIG_IGN);
800 if (hadsig && isrelax)
801 srelax_rele();
802 colour_reset(obuf); /* XXX hacky; only here because we still jump */
803 Pclose(obuf, TRU1);
804 safe_signal(SIGPIPE, dflpipe);
806 rv = 0;
808 jleave:
809 NYD_LEAVE;
810 return rv;
813 static int
814 _pipe1(char *str, int doign)
816 off_t stats[2];
817 char *cmd;
818 int *msgvec, rv = 1;
819 bool_t needs_list;
820 NYD_ENTER;
822 if ((cmd = laststring(str, &needs_list, TRU1)) == NULL) {
823 cmd = ok_vlook(cmd);
824 if (cmd == NULL || *cmd == '\0') {
825 fputs(_("variable cmd not set\n"), stderr);
826 goto jleave;
830 msgvec = salloc((msgCount + 2) * sizeof *msgvec);
832 if (!needs_list) {
833 *msgvec = first(0, MMNORM);
834 if (*msgvec == 0) {
835 if (inhook) {
836 rv = 0;
837 goto jleave;
839 puts(_("No messages to pipe."));
840 goto jleave;
842 msgvec[1] = 0;
843 } else if (getmsglist(str, msgvec, 0) < 0)
844 goto jleave;
845 if (*msgvec == 0) {
846 if (inhook) {
847 rv = 0;
848 goto jleave;
850 printf("No applicable messages.\n");
851 goto jleave;
854 printf(_("Pipe to: \"%s\"\n"), cmd);
855 stats[0] = stats[1] = 0;
856 if ((rv = _type1(msgvec, doign, FAL0, TRU1, FAL0, cmd, stats)) == 0) {
857 printf("\"%s\" ", cmd);
858 if (stats[0] >= 0)
859 printf("%lu", (long)stats[0]);
860 else
861 printf(_("binary"));
862 printf("/%lu\n", (long)stats[1]);
864 jleave:
865 NYD_LEAVE;
866 return rv;
869 FL int
870 c_cmdnotsupp(void *v) /* TODO -> lex.c */
872 NYD_ENTER;
873 UNUSED(v);
874 fprintf(stderr, _("The requested feature is not compiled in\n"));
875 NYD_LEAVE;
876 return 1;
879 FL int
880 c_headers(void *v)
882 ui32_t flag;
883 int *msgvec = v, g, k, n, mesg, size, lastg = 1;
884 struct message *mp, *mq, *lastmq = NULL;
885 enum mflag fl = MNEW | MFLAGGED;
886 NYD_ENTER;
888 time_current_update(&time_current, FAL0);
890 flag = 0;
891 size = screensize();
892 n = msgvec[0]; /* n == {-2, -1, 0}: called from scroll() */
893 if (_screen < 0)
894 _screen = 0;
895 k = _screen * size;
896 if (k >= msgCount)
897 k = msgCount - size;
898 if (k < 0)
899 k = 0;
901 if (mb.mb_threaded == 0) {
902 g = 0;
903 mq = message;
904 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
905 if (visible(mp)) {
906 if (g % size == 0)
907 mq = mp;
908 if (mp->m_flag & fl) {
909 lastg = g;
910 lastmq = mq;
912 if ((n > 0 && PTRCMP(mp, ==, message + n - 1)) ||
913 (n == 0 && g == k) || (n == -2 && g == k + size && lastmq) ||
914 (n < 0 && g >= k && (mp->m_flag & fl) != 0))
915 break;
916 g++;
918 if (lastmq && (n == -2 ||
919 (n == -1 && PTRCMP(mp, ==, message + msgCount)))) {
920 g = lastg;
921 mq = lastmq;
923 _screen = g / size;
924 mp = mq;
925 mesg = (int)PTR2SIZE(mp - message);
926 if (PTRCMP(dot, !=, message + n - 1)) {
927 for (mq = mp; PTRCMP(mq, <, message + msgCount); ++mq)
928 if (visible(mq)) {
929 setdot(mq);
930 break;
933 #ifdef HAVE_IMAP
934 if (mb.mb_type == MB_IMAP)
935 imap_getheaders(mesg + 1, mesg + size);
936 #endif
937 srelax_hold();
938 for (; PTRCMP(mp, <, message + msgCount); ++mp) {
939 ++mesg;
940 if (!visible(mp))
941 continue;
942 if (UICMP(32, flag++, >=, size))
943 break;
944 _print_head(0, mesg, stdout, 0);
945 srelax();
947 srelax_rele();
948 } else { /* threaded */
949 g = 0;
950 mq = threadroot;
951 for (mp = threadroot; mp; mp = next_in_thread(mp))
952 if (visible(mp) &&
953 (mp->m_collapsed <= 0 || PTRCMP(mp, ==, message + n - 1))) {
954 if (g % size == 0)
955 mq = mp;
956 if (mp->m_flag & fl) {
957 lastg = g;
958 lastmq = mq;
960 if ((n > 0 && PTRCMP(mp, ==, message + n - 1)) ||
961 (n == 0 && g == k) || (n == -2 && g == k + size && lastmq) ||
962 (n < 0 && g >= k && (mp->m_flag & fl) != 0))
963 break;
964 g++;
966 if (lastmq && (n == -2 ||
967 (n == -1 && PTRCMP(mp, ==, message + msgCount)))) {
968 g = lastg;
969 mq = lastmq;
971 _screen = g / size;
972 mp = mq;
973 if (PTRCMP(dot, !=, message + n - 1)) {
974 for (mq = mp; mq; mq = next_in_thread(mq))
975 if (visible(mq) && mq->m_collapsed <= 0) {
976 setdot(mq);
977 break;
980 srelax_hold();
981 while (mp) {
982 if (visible(mp) &&
983 (mp->m_collapsed <= 0 || PTRCMP(mp, ==, message + n - 1))) {
984 if (UICMP(32, flag++, >=, size))
985 break;
986 _print_head(flag - 1, PTR2SIZE(mp - message + 1), stdout,
987 mb.mb_threaded);
988 srelax();
990 mp = next_in_thread(mp);
992 srelax_rele();
995 if (!flag)
996 printf(_("No more mail.\n"));
997 NYD_LEAVE;
998 return !flag;
1001 FL int
1002 c_scroll(void *v)
1004 int rv;
1005 NYD_ENTER;
1007 rv = _scroll1(v, 0);
1008 NYD_LEAVE;
1009 return rv;
1012 FL int
1013 c_Scroll(void *v)
1015 int rv;
1016 NYD_ENTER;
1018 rv = _scroll1(v, 1);
1019 NYD_LEAVE;
1020 return rv;
1023 FL int
1024 c_from(void *v)
1026 int *msgvec = v, *ip, n;
1027 char *cp;
1028 FILE * volatile obuf;
1029 NYD_ENTER;
1031 time_current_update(&time_current, FAL0);
1032 obuf = stdout;
1034 /* TODO unfixable memory leaks still */
1035 if (IS_TTY_SESSION() && (cp = ok_vlook(crt)) != NULL) {
1036 for (n = 0, ip = msgvec; *ip != 0; ++ip)
1037 n++;
1038 if (n > (*cp == '\0' ? screensize() : atoi(cp)) + 3) {
1039 char const *p;
1040 if (sigsetjmp(_cmd1_pipejmp, 1))
1041 goto jendpipe;
1042 p = get_pager(NULL);
1043 if ((obuf = Popen(p, "w", NULL, NULL, 1)) == NULL) {
1044 perror(p);
1045 obuf = stdout;
1046 cp = NULL;
1047 } else
1048 safe_signal(SIGPIPE, &_cmd1_onpipe);
1052 for (n = 0, ip = msgvec; *ip != 0; ++ip) /* TODO join into _print_head() */
1053 _print_head((size_t)n++, (size_t)*ip, obuf, mb.mb_threaded);
1054 if (--ip >= msgvec)
1055 setdot(message + *ip - 1);
1057 jendpipe:
1058 if (obuf != stdout) {
1059 safe_signal(SIGPIPE, SIG_IGN);
1060 Pclose(obuf, TRU1);
1061 safe_signal(SIGPIPE, dflpipe);
1063 NYD_LEAVE;
1064 return 0;
1067 FL void
1068 print_headers(size_t bottom, size_t topx, bool_t only_marked)
1070 size_t printed;
1071 NYD_ENTER;
1073 #ifdef HAVE_IMAP
1074 if (mb.mb_type == MB_IMAP)
1075 imap_getheaders(bottom, topx);
1076 #endif
1077 time_current_update(&time_current, FAL0);
1079 for (printed = 0; bottom <= topx; ++bottom) {
1080 struct message *mp = message + bottom - 1;
1081 if (only_marked) {
1082 if (!(mp->m_flag & MMARK))
1083 continue;
1084 } else if (!visible(mp))
1085 continue;
1086 _print_head(printed++, bottom, stdout, FAL0);
1088 NYD_LEAVE;
1091 FL int
1092 c_pdot(void *v)
1094 NYD_ENTER;
1095 UNUSED(v);
1096 printf("%d\n", (int)PTR2SIZE(dot - message + 1));
1097 NYD_LEAVE;
1098 return 0;
1101 FL int
1102 c_more(void *v)
1104 int *msgvec = v, rv;
1105 NYD_ENTER;
1107 rv = _type1(msgvec, TRU1, TRU1, FAL0, FAL0, NULL, NULL);
1108 NYD_LEAVE;
1109 return rv;
1112 FL int
1113 c_More(void *v)
1115 int *msgvec = v, rv;
1116 NYD_ENTER;
1118 rv = _type1(msgvec, FAL0, TRU1, FAL0, FAL0, NULL, NULL);
1119 NYD_LEAVE;
1120 return rv;
1123 FL int
1124 c_type(void *v)
1126 int *msgvec = v, rv;
1127 NYD_ENTER;
1129 rv = _type1(msgvec, TRU1, FAL0, FAL0, FAL0, NULL, NULL);
1130 NYD_LEAVE;
1131 return rv;
1134 FL int
1135 c_Type(void *v)
1137 int *msgvec = v, rv;
1138 NYD_ENTER;
1140 rv = _type1(msgvec, FAL0, FAL0, FAL0, FAL0, NULL, NULL);
1141 NYD_LEAVE;
1142 return rv;
1145 FL int
1146 c_show(void *v)
1148 int *msgvec = v, rv;
1149 NYD_ENTER;
1151 rv = _type1(msgvec, FAL0, FAL0, FAL0, TRU1, NULL, NULL);
1152 NYD_LEAVE;
1153 return rv;
1156 FL int
1157 c_pipe(void *v)
1159 char *str = v;
1160 int rv;
1161 NYD_ENTER;
1163 rv = _pipe1(str, 1);
1164 NYD_LEAVE;
1165 return rv;
1168 FL int
1169 c_Pipe(void *v)
1171 char *str = v;
1172 int rv;
1173 NYD_ENTER;
1175 rv = _pipe1(str, 0);
1176 NYD_LEAVE;
1177 return rv;
1180 FL int
1181 c_top(void *v)
1183 int *msgvec = v, *ip, c, topl, lines, empty_last;
1184 struct message *mp;
1185 char *cp, *linebuf = NULL;
1186 size_t linesize = 0;
1187 FILE *ibuf;
1188 NYD_ENTER;
1190 topl = 5;
1191 cp = ok_vlook(toplines);
1192 if (cp != NULL) {
1193 topl = atoi(cp);
1194 if (topl < 0 || topl > 10000)
1195 topl = 5;
1198 #ifdef HAVE_COLOUR
1199 if (options & OPT_TTYOUT)
1200 colour_table_create(FAL0); /* (salloc()s) */
1201 #endif
1202 empty_last = 1;
1203 for (ip = msgvec; *ip != 0 && UICMP(z, PTR2SIZE(ip - msgvec), <, msgCount);
1204 ++ip) {
1205 mp = message + *ip - 1;
1206 touch(mp);
1207 setdot(mp);
1208 did_print_dot = TRU1;
1209 if (!empty_last)
1210 printf("\n");
1211 _show_msg_overview(stdout, mp, *ip);
1212 if (mp->m_flag & MNOFROM)
1213 /* XXX c_top(): coloured output? */
1214 printf("From %s %s\n", fakefrom(mp), fakedate(mp->m_time));
1215 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL) { /* XXX could use TOP */
1216 v = NULL;
1217 break;
1219 c = mp->m_lines;
1220 for (lines = 0; lines < c && UICMP(32, lines, <=, topl); ++lines) {
1221 if (readline_restart(ibuf, &linebuf, &linesize, 0) < 0)
1222 break;
1223 puts(linebuf);
1225 for (cp = linebuf; *cp != '\0' && blankchar(*cp); ++cp)
1227 empty_last = (*cp == '\0');
1231 if (linebuf != NULL)
1232 free(linebuf);
1233 NYD_LEAVE;
1234 return (v != NULL);
1237 FL int
1238 c_stouch(void *v)
1240 int *msgvec = v, *ip;
1241 NYD_ENTER;
1243 for (ip = msgvec; *ip != 0; ++ip) {
1244 setdot(message + *ip - 1);
1245 dot->m_flag |= MTOUCH;
1246 dot->m_flag &= ~MPRESERVE;
1247 did_print_dot = TRU1;
1249 NYD_LEAVE;
1250 return 0;
1253 FL int
1254 c_mboxit(void *v)
1256 int *msgvec = v, *ip;
1257 NYD_ENTER;
1259 for (ip = msgvec; *ip != 0; ++ip) {
1260 setdot(message + *ip - 1);
1261 dot->m_flag |= MTOUCH | MBOX;
1262 dot->m_flag &= ~MPRESERVE;
1263 did_print_dot = TRU1;
1265 NYD_LEAVE;
1266 return 0;
1269 FL int
1270 c_folders(void *v)
1272 char dirname[PATH_MAX], *name, **argv = v;
1273 char const *cmd;
1274 int rv = 1;
1275 NYD_ENTER;
1277 if (*argv) {
1278 name = expand(*argv);
1279 if (name == NULL)
1280 goto jleave;
1281 } else if (!getfold(dirname, sizeof dirname)) {
1282 fprintf(stderr, _("No value set for \"folder\"\n"));
1283 goto jleave;
1284 } else
1285 name = dirname;
1287 if (which_protocol(name) == PROTO_IMAP) {
1288 #ifdef HAVE_IMAP
1289 imap_folders(name, *argv == NULL);
1290 #else
1291 rv = c_cmdnotsupp(NULL);
1292 #endif
1293 } else {
1294 if ((cmd = ok_vlook(LISTER)) == NULL)
1295 cmd = XLISTER;
1296 run_command(cmd, 0, -1, -1, name, NULL, NULL);
1298 jleave:
1299 NYD_LEAVE;
1300 return rv;
1303 /* s-it-mode */