Fix `headers' command documentation+ (Jan Chaloupka)..
[s-mailx.git] / cmd1.c
blob23fc64417943ded241a1652152bf7096ceadb6a9
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);
71 /* Shared `z' implementation */
72 static int _scroll1(char *arg, int onlynew);
74 /* Shared `headers' implementation */
75 static int _headers(int msgspec);
77 /* Show the requested messages */
78 static int _type1(int *msgvec, bool_t doign, bool_t dopage, bool_t dopipe,
79 bool_t dodecode, char *cmd, off_t *tstats);
81 /* Pipe the requested messages */
82 static int _pipe1(char *str, int doign);
84 static void
85 _cmd1_onpipe(int signo)
87 NYD_X; /* Signal handler */
88 UNUSED(signo);
89 siglongjmp(_cmd1_pipejmp, 1);
92 static void
93 _cmd1_brokpipe(int signo)
95 NYD_X; /* Signal handler */
96 UNUSED(signo);
97 siglongjmp(_cmd1_pipestop, 1);
100 static void
101 _show_msg_overview(FILE *obuf, struct message *mp, int msg_no)
103 char const *cpre = "", *csuf = "";
104 NYD_ENTER;
106 #ifdef HAVE_COLOUR
107 if (colour_table != NULL) {
108 struct str const *sp;
110 if ((sp = colour_get(COLOURSPEC_MSGINFO)) != NULL)
111 cpre = sp->s;
112 csuf = colour_get(COLOURSPEC_RESET)->s;
114 #endif
115 fprintf(obuf, _("%s[-- Message %2d -- %lu lines, %lu bytes --]:%s\n"),
116 cpre, msg_no, (ul_it)mp->m_lines, (ul_it)mp->m_size, csuf);
117 NYD_LEAVE;
120 static void
121 _parse_from_(struct message *mp, char date[FROM_DATEBUF]) /* TODO line pool */
123 FILE *ibuf;
124 int hlen;
125 char *hline = NULL;
126 size_t hsize = 0;
127 NYD_ENTER;
129 if ((ibuf = setinput(&mb, mp, NEED_HEADER)) != NULL &&
130 (hlen = readline_restart(ibuf, &hline, &hsize, 0)) > 0)
131 extract_date_from_from_(hline, hlen, date);
132 if (hline != NULL)
133 free(hline);
134 NYD_LEAVE;
137 static void
138 _print_head(size_t yetprinted, size_t msgno, FILE *f, bool_t threaded)
140 enum {attrlen = 13};
141 char attrlist[attrlen +1], *cp;
142 char const *fmt;
143 NYD_ENTER;
145 if ((cp = ok_vlook(attrlist)) != NULL) {
146 if (strlen(cp) == attrlen) {
147 memcpy(attrlist, cp, attrlen +1);
148 goto jattrok;
150 fprintf(stderr, _(
151 "The value of *attrlist* is not of the correct length\n"));
153 if (ok_blook(bsdcompat) || ok_blook(bsdflags) ||
154 getenv("SYSV3") != NULL) {
155 char const bsdattr[attrlen +1] = "NU *HMFAT+-$";
156 memcpy(attrlist, bsdattr, sizeof bsdattr);
157 } else {
158 char const pattr[attrlen +1] = "NUROSPMFAT+-$";
159 memcpy(attrlist, pattr, sizeof pattr);
161 jattrok:
162 if ((fmt = ok_vlook(headline)) == NULL) {
163 fmt = ((ok_blook(bsdcompat) || ok_blook(bsdheadline))
164 ? "%>%a%m %-20f %16d %3l/%-5o %i%-S"
165 : "%>%a%m %-18f %16d %4l/%-5o %i%-s");
168 __hprf(yetprinted, fmt, msgno, f, threaded, attrlist);
169 NYD_LEAVE;
172 static void
173 __hprf(size_t yetprinted, char const *fmt, size_t msgno, FILE *f,
174 bool_t threaded, char const *attrlist)
176 char datebuf[FROM_DATEBUF], *cp, *subjline;
177 char const *datefmt, *date, *name, *fp;
178 int i, n, s, wleft, subjlen;
179 struct message *mp;
180 time_t datet;
181 enum {
182 _NONE = 0,
183 _ISADDR = 1<<0,
184 _ISTO = 1<<1,
185 _IFMT = 1<<2,
186 _LOOP_MASK = (1<<3) - 1,
187 _SFMT = 1<<3
188 } flags = _NONE;
189 NYD_ENTER;
191 mp = message + msgno - 1;
192 datet = mp->m_time;
193 date = NULL;
195 datefmt = ok_vlook(datefield);
196 jredo:
197 if (datefmt != NULL) {
198 fp = hfield1("date", mp);/* TODO use m_date field! */
199 if (fp == NULL) {
200 datefmt = NULL;
201 goto jredo;
203 datet = rfctime(fp);
204 date = fakedate(datet);
205 fp = ok_vlook(datefield_markout_older);
206 i = (*datefmt != '\0');
207 if (fp != NULL)
208 i |= (*fp != '\0') ? 2 | 4 : 2; /* XXX no magics */
210 /* May we strftime(3)? */
211 if (i & (1 | 4))
212 memcpy(&time_current.tc_local, localtime(&datet),
213 sizeof time_current.tc_local);
215 if ((i & 2) && (datet > time_current.tc_time + DATE_SECSDAY ||
216 #define _6M ((DATE_DAYSYEAR / 2) * DATE_SECSDAY)
217 (datet + _6M < time_current.tc_time))) {
218 #undef _6M
219 if ((datefmt = (i & 4) ? fp : NULL) == NULL) {
220 memset(datebuf, ' ', FROM_DATEBUF); /* xxx ur */
221 memcpy(datebuf + 4, date + 4, 7);
222 datebuf[4 + 7] = ' ';
223 memcpy(datebuf + 4 + 7 + 1, date + 20, 4);
224 datebuf[4 + 7 + 1 + 4] = '\0';
225 date = datebuf;
227 } else if ((i & 1) == 0)
228 datefmt = NULL;
229 } else if (datet == (time_t)0 && !(mp->m_flag & MNOFROM)) {
230 /* TODO eliminate this path, query the FROM_ date in setptr(),
231 * TODO all other codepaths do so by themselves ALREADY ?????
232 * TODO assert(mp->m_time != 0);, then
233 * TODO ALSO changes behaviour of datefield_markout_older */
234 _parse_from_(mp, datebuf);
235 date = datebuf;
236 } else
237 date = fakedate(datet);
239 flags |= _ISADDR;
240 name = name1(mp, 0);
241 if (name != NULL && ok_blook(showto) && is_myname(skin(name))) {
242 if ((cp = hfield1("to", mp)) != NULL) {
243 name = cp;
244 flags |= _ISTO;
247 if (name == NULL) {
248 name = "";
249 flags &= ~_ISADDR;
251 if (flags & _ISADDR)
252 name = ok_blook(showname) ? realname(name) : prstr(skin(name));
254 subjline = NULL;
256 /* Detect the width of the non-format characters in *headline*;
257 * like that we can simply use putc() in the next loop, since we have
258 * already calculated their column widths (TODO it's sick) */
259 wleft = subjlen = scrnwidth;
261 for (fp = fmt; *fp != '\0'; ++fp) {
262 if (*fp == '%') {
263 if (*++fp == '-')
264 ++fp;
265 else if (*fp == '+')
266 ++fp;
267 if (digitchar(*fp)) {
268 n = 0;
270 n = 10*n + *fp - '0';
271 while (++fp, digitchar(*fp));
272 subjlen -= n;
274 if (*fp == 'i')
275 flags |= _IFMT;
277 if (*fp == '\0')
278 break;
279 } else {
280 #ifdef HAVE_WCWIDTH
281 if (mb_cur_max > 1) {
282 wchar_t wc;
283 if ((s = mbtowc(&wc, fp, mb_cur_max)) == -1)
284 n = s = 1;
285 else if ((n = wcwidth(wc)) == -1)
286 n = 1;
287 } else
288 #endif
289 n = s = 1;
290 subjlen -= n;
291 wleft -= n;
292 while (--s > 0)
293 ++fp;
297 /* Walk *headline*, producing output TODO not (really) MB safe */
298 for (fp = fmt; *fp != '\0'; ++fp) {
299 char c;
300 if ((c = *fp & 0xFF) != '%')
301 putc(c, f);
302 else {
303 flags &= _LOOP_MASK;
304 n = 0;
305 s = 1;
306 if (*++fp == '-') {
307 s = -1;
308 ++fp;
309 } else if (*fp == '+')
310 ++fp;
311 if (digitchar(*fp)) {
313 n = 10*n + *fp - '0';
314 while (++fp, digitchar(*fp));
316 if (*fp == '\0')
317 break;
319 n *= s;
320 switch ((c = *fp & 0xFF)) {
321 case '%':
322 goto jputc;
323 case '>':
324 case '<':
325 if (dot != mp)
326 c = ' ';
327 goto jputc;
328 case '$':
329 #ifdef HAVE_SPAM
330 if (n == 0)
331 n = 4;
332 if (UICMP(32, ABS(n), >, wleft))
333 n = (n < 0) ? -wleft : wleft;
334 { char buf[16];
335 snprintf(buf, sizeof buf, "%u.%u",
336 (mp->m_spamscore >> 8), (mp->m_spamscore & 0xFF));
337 n = fprintf(f, "%*s", n, buf);
338 wleft = (n >= 0) ? wleft - n : 0;
340 #else
341 c = '?';
342 goto jputc;
343 #endif
344 case 'a':
345 c = _dispc(mp, attrlist);
346 jputc:
347 if (UICMP(32, ABS(n), >, wleft))
348 n = (n < 0) ? -wleft : wleft;
349 n = fprintf(f, "%*c", n, c);
350 wleft = (n >= 0) ? wleft - n : 0;
351 break;
352 case 'd':
353 if (datefmt != NULL) {
354 i = strftime(datebuf, sizeof datebuf, datefmt,
355 &time_current.tc_local);
356 if (i != 0)
357 date = datebuf;
358 else
359 fprintf(stderr, _(
360 "Ignored date format, it excesses the target buffer "
361 "(%lu bytes)\n"), (ul_it)sizeof datebuf);
362 datefmt = NULL;
364 if (n == 0)
365 n = 16;
366 if (UICMP(32, ABS(n), >, wleft))
367 n = (n < 0) ? -wleft : wleft;
368 n = fprintf(f, "%*.*s", n, n, date);
369 wleft = (n >= 0) ? wleft - n : 0;
370 break;
371 case 'e':
372 if (n == 0)
373 n = 2;
374 if (UICMP(32, ABS(n), >, wleft))
375 n = (n < 0) ? -wleft : wleft;
376 n = fprintf(f, "%*u", n, (threaded == 1 ? mp->m_level : 0));
377 wleft = (n >= 0) ? wleft - n : 0;
378 break;
379 case 'f':
380 if (n == 0) {
381 n = 18;
382 if (s < 0)
383 n = -n;
385 i = ABS(n);
386 if (i > wleft) {
387 i = wleft;
388 n = (n < 0) ? -wleft : wleft;
390 if (flags & _ISTO) /* XXX tr()! */
391 i -= 3;
392 n = fprintf(f, "%s%s", ((flags & _ISTO) ? "To " : ""),
393 colalign(name, i, n, &wleft));
394 if (n < 0)
395 wleft = 0;
396 else if (flags & _ISTO)
397 wleft -= 3;
398 break;
399 case 'i':
400 if (threaded) {
401 n = __putindent(f, mp, MIN(wleft, scrnwidth - 60));
402 wleft = (n >= 0) ? wleft - n : 0;
404 break;
405 case 'l':
406 if (n == 0)
407 n = 4;
408 if (UICMP(32, ABS(n), >, wleft))
409 n = (n < 0) ? -wleft : wleft;
410 if (mp->m_xlines) {
411 n = fprintf(f, "%*ld", n, mp->m_xlines);
412 wleft = (n >= 0) ? wleft - n : 0;
413 } else {
414 n = ABS(n);
415 wleft -= n;
416 while (n-- != 0)
417 putc(' ', f);
419 break;
420 case 'm':
421 if (n == 0) {
422 n = 3;
423 if (threaded)
424 for (i = msgCount; i > 999; i /= 10)
425 ++n;
427 if (UICMP(32, ABS(n), >, wleft))
428 n = (n < 0) ? -wleft : wleft;
429 n = fprintf(f, "%*lu", n, (ul_it)msgno);
430 wleft = (n >= 0) ? wleft - n : 0;
431 break;
432 case 'o':
433 if (n == 0)
434 n = -5;
435 if (UICMP(32, ABS(n), >, wleft))
436 n = (n < 0) ? -wleft : wleft;
437 n = fprintf(f, "%*lu", n, (long)mp->m_xsize);
438 wleft = (n >= 0) ? wleft - n : 0;
439 break;
440 case 'S':
441 flags |= _SFMT;
442 /*FALLTHRU*/
443 case 's':
444 if (n == 0)
445 n = subjlen - 2;
446 if (n > 0 && s < 0)
447 n = -n;
448 if (subjlen > wleft)
449 subjlen = wleft;
450 if (UICMP(32, ABS(n), >, subjlen))
451 n = (n < 0) ? -subjlen : subjlen;
452 if (flags & _SFMT)
453 n -= (n < 0) ? -2 : 2;
454 if (n == 0)
455 break;
456 if (subjline == NULL)
457 subjline = __subject(mp, (threaded && (flags & _IFMT)),
458 yetprinted);
459 if (subjline == (char*)-1) {
460 n = fprintf(f, "%*s", n, "");
461 wleft = (n >= 0) ? wleft - n : 0;
462 } else {
463 n = fprintf(f, ((flags & _SFMT) ? "\"%s\"" : "%s"),
464 colalign(subjline, ABS(n), n, &wleft));
465 if (n < 0)
466 wleft = 0;
468 break;
469 case 't':
470 if (n == 0) {
471 n = 3;
472 if (threaded)
473 for (i = msgCount; i > 999; i /= 10)
474 ++n;
476 if (UICMP(32, ABS(n), >, wleft))
477 n = (n < 0) ? -wleft : wleft;
478 n = fprintf(f, "%*lu", n,
479 (threaded ? (ul_it)mp->m_threadpos : (ul_it)msgno));
480 wleft = (n >= 0) ? wleft - n : 0;
481 break;
482 case 'U':
483 #ifdef HAVE_IMAP
484 if (n == 0)
485 n = 9;
486 if (UICMP(32, ABS(n), >, wleft))
487 n = (n < 0) ? -wleft : wleft;
488 n = fprintf(f, "%*lu", n, mp->m_uid);
489 wleft = (n >= 0) ? wleft - n : 0;
490 break;
491 #else
492 c = '?';
493 goto jputc;
494 #endif
497 if (wleft <= 0)
498 break;
501 putc('\n', f);
503 if (subjline != NULL && subjline != (char*)-1)
504 free(subjline);
505 NYD_LEAVE;
508 static char *
509 __subject(struct message *mp, bool_t threaded, size_t yetprinted)
511 /* XXX NOTE: because of efficiency reasons we simply ignore any encoded
512 * XXX parts and use ASCII case-insensitive comparison */
513 struct str in, out;
514 struct message *xmp;
515 char *rv = (char*)-1, *ms, *mso, *os;
516 NYD_ENTER;
518 if ((ms = hfield1("subject", mp)) == NULL)
519 goto jleave;
521 if (!threaded || mp->m_level == 0)
522 goto jconv;
524 /* In a display thread - check wether this message uses the same
525 * Subject: as it's parent or elder neighbour, suppress printing it if
526 * this is the case. To extend this a bit, ignore any leading Re: or
527 * Fwd: plus follow-up WS. Ignore invisible messages along the way */
528 mso = subject_re_trim(ms);
529 for (xmp = mp; (xmp = prev_in_thread(xmp)) != NULL && yetprinted-- > 0;)
530 if (visible(xmp) && (os = hfield1("subject", xmp)) != NULL &&
531 !asccasecmp(mso, subject_re_trim(os)))
532 goto jleave;
533 jconv:
534 in.s = ms;
535 in.l = strlen(ms);
536 mime_fromhdr(&in, &out, TD_ICONV | TD_ISPR);
537 rv = out.s;
538 jleave:
539 NYD_LEAVE;
540 return rv;
543 static int
544 __putindent(FILE *fp, struct message *mp, int maxwidth)/* XXX no magic consts */
546 struct message *mq;
547 int *us, indlvl, indw, i, important = MNEW | MFLAGGED;
548 char *cs;
549 NYD_ENTER;
551 if (mp->m_level == 0 || maxwidth == 0) {
552 indw = 0;
553 goto jleave;
556 cs = ac_alloc(mp->m_level);
557 us = ac_alloc(mp->m_level * sizeof *us);
559 i = mp->m_level - 1;
560 if (mp->m_younger && UICMP(32, i + 1, ==, mp->m_younger->m_level)) {
561 if (mp->m_parent && mp->m_parent->m_flag & important)
562 us[i] = mp->m_flag & important ? 0x2523 : 0x2520;
563 else
564 us[i] = mp->m_flag & important ? 0x251D : 0x251C;
565 cs[i] = '+';
566 } else {
567 if (mp->m_parent && mp->m_parent->m_flag & important)
568 us[i] = mp->m_flag & important ? 0x2517 : 0x2516;
569 else
570 us[i] = mp->m_flag & important ? 0x2515 : 0x2514;
571 cs[i] = '\\';
574 mq = mp->m_parent;
575 for (i = mp->m_level - 2; i >= 0; --i) {
576 if (mq) {
577 if (UICMP(32, i, >, mq->m_level - 1)) {
578 us[i] = cs[i] = ' ';
579 continue;
581 if (mq->m_younger) {
582 if (mq->m_parent && (mq->m_parent->m_flag & important))
583 us[i] = 0x2503;
584 else
585 us[i] = 0x2502;
586 cs[i] = '|';
587 } else
588 us[i] = cs[i] = ' ';
589 mq = mq->m_parent;
590 } else
591 us[i] = cs[i] = ' ';
594 --maxwidth;
595 for (indlvl = indw = 0; (ui8_t)indlvl < mp->m_level && indw < maxwidth;
596 ++indlvl) {
597 if (indw < maxwidth - 1)
598 indw += (int)putuc(us[indlvl], cs[indlvl] & 0xFF, fp);
599 else
600 indw += (int)putuc(0x21B8, '^', fp);
602 indw += (/*putuc(0x261E, fp)*/putc('>', fp) != EOF);
604 ac_free(us);
605 ac_free(cs);
606 jleave:
607 NYD_LEAVE;
608 return indw;
611 static int
612 _dispc(struct message *mp, char const *a)
614 int i = ' ';
615 NYD_ENTER;
617 if ((mp->m_flag & (MREAD | MNEW)) == MREAD)
618 i = a[3];
619 if ((mp->m_flag & (MREAD | MNEW)) == (MREAD | MNEW))
620 i = a[2];
621 if (mp->m_flag & MANSWERED)
622 i = a[8];
623 if (mp->m_flag & MDRAFTED)
624 i = a[9];
625 if ((mp->m_flag & (MREAD | MNEW)) == MNEW)
626 i = a[0];
627 if (!(mp->m_flag & (MREAD | MNEW)))
628 i = a[1];
629 if (mp->m_flag & MSPAM)
630 i = a[12];
631 if (mp->m_flag & MSAVED)
632 i = a[4];
633 if (mp->m_flag & MPRESERVE)
634 i = a[5];
635 if (mp->m_flag & (MBOX | MBOXED))
636 i = a[6];
637 if (mp->m_flag & MFLAGGED)
638 i = a[7];
639 if (mb.mb_threaded == 1 && mp->m_collapsed > 0)
640 i = a[11];
641 if (mb.mb_threaded == 1 && mp->m_collapsed < 0)
642 i = a[10];
643 NYD_LEAVE;
644 return i;
647 static int
648 _scroll1(char *arg, int onlynew)
650 int msgspec, size;
651 NYD_ENTER;
653 msgspec = onlynew ? -1 : 0;
654 size = screensize();
656 if (arg[0] != '\0' && arg[1] != '\0')
657 goto jerr;
658 switch (*arg) {
659 case '1': case '2': case '3': case '4': case '5':
660 case '6': case '7': case '8': case '9': case '0':
661 _screen = atoi(arg);
662 goto jscroll_forward;
663 case '\0':
664 ++_screen;
665 goto jscroll_forward;
666 case '$':
667 _screen = msgCount / size;
668 goto jscroll_forward;
669 case '+':
670 if (arg[1] == '\0')
671 ++_screen;
672 else
673 _screen += atoi(arg + 1);
674 jscroll_forward:
675 if (_screen * size > msgCount) {
676 _screen = msgCount / size;
677 printf(_("On last screenful of messages\n"));
679 break;
680 case '-':
681 if (arg[1] == '\0')
682 --_screen;
683 else
684 _screen -= atoi(arg + 1);
685 if (_screen < 0) {
686 _screen = 0;
687 printf(_("On first screenful of messages\n"));
689 if (msgspec == -1)
690 msgspec = -2;
691 break;
692 default:
693 jerr:
694 fprintf(stderr, _("Unrecognized scrolling command \"%s\"\n"), arg);
695 size = 1;
696 goto jleave;
699 size = _headers(msgspec);
700 jleave:
701 NYD_LEAVE;
702 return size;
705 static int
706 _headers(int msgspec) /* FIXME rework v14.8; also: Neitzel mail, 2014-08-21 */
708 ui32_t flag;
709 int g, k, mesg, size, lastg = 1;
710 struct message *mp, *mq, *lastmq = NULL;
711 enum mflag fl = MNEW | MFLAGGED;
712 NYD_ENTER;
714 time_current_update(&time_current, FAL0);
716 flag = 0;
717 size = screensize();
718 if (_screen < 0)
719 _screen = 0;
720 #if 0 /* FIXME original code path */
721 k = _screen * size;
722 #else
723 if (msgspec <= 0)
724 k = _screen * size;
725 else
726 k = msgspec;
727 #endif
728 if (k >= msgCount)
729 k = msgCount - size;
730 if (k < 0)
731 k = 0;
733 if (mb.mb_threaded == 0) {
734 g = 0;
735 mq = message;
736 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
737 if (visible(mp)) {
738 if (g % size == 0)
739 mq = mp;
740 if (mp->m_flag & fl) {
741 lastg = g;
742 lastmq = mq;
744 if ((msgspec > 0 && PTRCMP(mp, ==, message + msgspec - 1)) ||
745 (msgspec == 0 && g == k) ||
746 (msgspec == -2 && g == k + size && lastmq) ||
747 (msgspec < 0 && g >= k && (mp->m_flag & fl) != 0))
748 break;
749 g++;
751 if (lastmq && (msgspec == -2 ||
752 (msgspec == -1 && PTRCMP(mp, ==, message + msgCount)))) {
753 g = lastg;
754 mq = lastmq;
756 _screen = g / size;
757 mp = mq;
758 mesg = (int)PTR2SIZE(mp - message);
759 if (PTRCMP(dot, !=, message + msgspec - 1)) { /* TODO really?? */
760 for (mq = mp; PTRCMP(mq, <, message + msgCount); ++mq)
761 if (visible(mq)) {
762 setdot(mq);
763 break;
766 #ifdef HAVE_IMAP
767 if (mb.mb_type == MB_IMAP)
768 imap_getheaders(mesg + 1, mesg + size);
769 #endif
770 srelax_hold();
771 for (; PTRCMP(mp, <, message + msgCount); ++mp) {
772 ++mesg;
773 if (!visible(mp))
774 continue;
775 if (UICMP(32, flag++, >=, size))
776 break;
777 _print_head(0, mesg, stdout, 0);
778 srelax();
780 srelax_rele();
781 } else { /* threaded */
782 g = 0;
783 mq = threadroot;
784 for (mp = threadroot; mp; mp = next_in_thread(mp))
785 if (visible(mp) &&
786 (mp->m_collapsed <= 0 ||
787 PTRCMP(mp, ==, message + msgspec - 1))) {
788 if (g % size == 0)
789 mq = mp;
790 if (mp->m_flag & fl) {
791 lastg = g;
792 lastmq = mq;
794 if ((msgspec > 0 && PTRCMP(mp, ==, message + msgspec - 1)) ||
795 (msgspec == 0 && g == k) ||
796 (msgspec == -2 && g == k + size && lastmq) ||
797 (msgspec < 0 && g >= k && (mp->m_flag & fl) != 0))
798 break;
799 g++;
801 if (lastmq && (msgspec == -2 ||
802 (msgspec == -1 && PTRCMP(mp, ==, message + msgCount)))) {
803 g = lastg;
804 mq = lastmq;
806 _screen = g / size;
807 mp = mq;
808 if (PTRCMP(dot, !=, message + msgspec - 1)) { /* TODO really?? */
809 for (mq = mp; mq; mq = next_in_thread(mq))
810 if (visible(mq) && mq->m_collapsed <= 0) {
811 setdot(mq);
812 break;
815 srelax_hold();
816 while (mp) {
817 if (visible(mp) &&
818 (mp->m_collapsed <= 0 ||
819 PTRCMP(mp, ==, message + msgspec - 1))) {
820 if (UICMP(32, flag++, >=, size))
821 break;
822 _print_head(flag - 1, PTR2SIZE(mp - message + 1), stdout,
823 mb.mb_threaded);
824 srelax();
826 mp = next_in_thread(mp);
828 srelax_rele();
831 if (!flag)
832 printf(_("No more mail.\n"));
833 NYD_LEAVE;
834 return !flag;
837 static int
838 _type1(int *msgvec, bool_t doign, bool_t dopage, bool_t dopipe,
839 bool_t dodecode, char *cmd, off_t *tstats)
841 off_t mstats[2];
842 int rv, *ip;
843 struct message *mp;
844 char const *cp;
845 FILE * volatile obuf;
846 bool_t volatile hadsig = FAL0, isrelax = FAL0;
847 NYD_ENTER;
848 {/* C89.. */
849 enum sendaction const action = ((dopipe && ok_blook(piperaw))
850 ? SEND_MBOX : dodecode
851 ? SEND_SHOW : doign
852 ? SEND_TODISP : SEND_TODISP_ALL);
853 bool_t const volatile formfeed = (dopipe && ok_blook(page));
854 obuf = stdout;
856 if (sigsetjmp(_cmd1_pipestop, 1)) {
857 hadsig = TRU1;
858 goto jclose_pipe;
861 if (dopipe) {
862 if ((cp = ok_vlook(SHELL)) == NULL)
863 cp = XSHELL;
864 if ((obuf = Popen(cmd, "w", cp, NULL, 1)) == NULL) {
865 perror(cmd);
866 obuf = stdout;
867 } else
868 safe_signal(SIGPIPE, &_cmd1_brokpipe);
869 } else if ((options & OPT_TTYOUT) &&
870 (dopage || (cp = ok_vlook(crt)) != NULL)) {
871 char const *pager = NULL;
872 size_t nlines = 0;
874 if (!dopage) {
875 for (ip = msgvec; *ip && PTRCMP(ip - msgvec, <, msgCount); ++ip) {
876 mp = message + *ip - 1;
877 if (!(mp->m_have & HAVE_BODY))
878 if (get_body(mp) != OKAY) {
879 rv = 1;
880 goto jleave;
882 nlines += mp->m_lines + 1; /* Message info XXX and PARTS... */
886 /* `>=' not `<': we return to the prompt */
887 if (dopage || UICMP(z, nlines, >=,
888 (*cp != '\0' ? atoi(cp) : realscreenheight))) {
889 char const *env_add[2];
890 pager = get_pager(env_add + 0);
891 env_add[1] = NULL;
892 obuf = Popen(pager, "w", NULL, env_add, 1);
893 if (obuf == NULL) {
894 perror(pager);
895 obuf = stdout;
896 pager = NULL;
897 } else
898 safe_signal(SIGPIPE, &_cmd1_brokpipe);
900 #ifdef HAVE_COLOUR
901 if (action != SEND_MBOX)
902 colour_table_create(pager != NULL); /* (salloc()s!) */
903 #endif
905 #ifdef HAVE_COLOUR
906 else if ((options & OPT_TTYOUT) && action != SEND_MBOX)
907 colour_table_create(FAL0); /* (salloc()s!) */
908 #endif
910 /*TODO unless we have our signal manager special care must be taken */
911 srelax_hold();
912 isrelax = TRU1;
913 for (ip = msgvec; *ip && PTRCMP(ip - msgvec, <, msgCount); ++ip) {
914 mp = message + *ip - 1;
915 touch(mp);
916 setdot(mp);
917 uncollapse1(mp, 1);
918 if (!dopipe) {
919 if (ip != msgvec)
920 fprintf(obuf, "\n");
921 if (action != SEND_MBOX)
922 _show_msg_overview(obuf, mp, *ip);
924 sendmp(mp, obuf, (doign ? ignore : NULL), NULL, action, mstats);
925 srelax();
926 if (formfeed) /* TODO a nicer way to separate piped messages! */
927 putc('\f', obuf);
928 if (tstats) {
929 tstats[0] += mstats[0];
930 tstats[1] += mstats[1];
933 srelax_rele();
934 isrelax = FAL0;
936 jclose_pipe:
937 if (obuf != stdout) {
938 /* Ignore SIGPIPE so it can't cause a duplicate close */
939 safe_signal(SIGPIPE, SIG_IGN);
940 if (hadsig && isrelax)
941 srelax_rele();
942 colour_reset(obuf); /* XXX hacky; only here because we still jump */
943 Pclose(obuf, TRU1);
944 safe_signal(SIGPIPE, dflpipe);
946 rv = 0;
948 jleave:
949 NYD_LEAVE;
950 return rv;
953 static int
954 _pipe1(char *str, int doign)
956 off_t stats[2];
957 char *cmd;
958 int *msgvec, rv = 1;
959 bool_t needs_list;
960 NYD_ENTER;
962 if ((cmd = laststring(str, &needs_list, TRU1)) == NULL) {
963 cmd = ok_vlook(cmd);
964 if (cmd == NULL || *cmd == '\0') {
965 fputs(_("variable cmd not set\n"), stderr);
966 goto jleave;
970 msgvec = salloc((msgCount + 2) * sizeof *msgvec);
972 if (!needs_list) {
973 *msgvec = first(0, MMNORM);
974 if (*msgvec == 0) {
975 if (inhook) {
976 rv = 0;
977 goto jleave;
979 puts(_("No messages to pipe."));
980 goto jleave;
982 msgvec[1] = 0;
983 } else if (getmsglist(str, msgvec, 0) < 0)
984 goto jleave;
985 if (*msgvec == 0) {
986 if (inhook) {
987 rv = 0;
988 goto jleave;
990 printf("No applicable messages.\n");
991 goto jleave;
994 printf(_("Pipe to: \"%s\"\n"), cmd);
995 stats[0] = stats[1] = 0;
996 if ((rv = _type1(msgvec, doign, FAL0, TRU1, FAL0, cmd, stats)) == 0) {
997 printf("\"%s\" ", cmd);
998 if (stats[0] >= 0)
999 printf("%lu", (long)stats[0]);
1000 else
1001 printf(_("binary"));
1002 printf("/%lu\n", (long)stats[1]);
1004 jleave:
1005 NYD_LEAVE;
1006 return rv;
1009 FL int
1010 c_cmdnotsupp(void *v) /* TODO -> lex.c */
1012 NYD_ENTER;
1013 UNUSED(v);
1014 fprintf(stderr, _("The requested feature is not compiled in\n"));
1015 NYD_LEAVE;
1016 return 1;
1019 FL int
1020 c_headers(void *v)
1022 int rv;
1023 NYD_ENTER;
1025 rv = print_header_group((int*)v);
1026 NYD_LEAVE;
1027 return rv;
1030 FL int
1031 print_header_group(int *vector)
1033 int rv;
1034 NYD_ENTER;
1036 assert(vector != NULL && vector != (void*)-1);
1037 rv = _headers(vector[0]);
1038 NYD_LEAVE;
1039 return rv;
1042 FL int
1043 c_scroll(void *v)
1045 int rv;
1046 NYD_ENTER;
1048 rv = _scroll1(v, 0);
1049 NYD_LEAVE;
1050 return rv;
1053 FL int
1054 c_Scroll(void *v)
1056 int rv;
1057 NYD_ENTER;
1059 rv = _scroll1(v, 1);
1060 NYD_LEAVE;
1061 return rv;
1064 FL int
1065 c_from(void *v)
1067 int *msgvec = v, *ip, n;
1068 char *cp;
1069 FILE * volatile obuf;
1070 NYD_ENTER;
1072 time_current_update(&time_current, FAL0);
1073 obuf = stdout;
1075 /* TODO unfixable memory leaks still */
1076 if (IS_TTY_SESSION() && (cp = ok_vlook(crt)) != NULL) {
1077 for (n = 0, ip = msgvec; *ip != 0; ++ip)
1078 n++;
1079 if (n > (*cp == '\0' ? screensize() : atoi(cp)) + 3) {
1080 char const *p;
1081 if (sigsetjmp(_cmd1_pipejmp, 1))
1082 goto jendpipe;
1083 p = get_pager(NULL);
1084 if ((obuf = Popen(p, "w", NULL, NULL, 1)) == NULL) {
1085 perror(p);
1086 obuf = stdout;
1087 cp = NULL;
1088 } else
1089 safe_signal(SIGPIPE, &_cmd1_onpipe);
1093 for (n = 0, ip = msgvec; *ip != 0; ++ip) /* TODO join into _print_head() */
1094 _print_head((size_t)n++, (size_t)*ip, obuf, mb.mb_threaded);
1095 if (--ip >= msgvec)
1096 setdot(message + *ip - 1);
1098 jendpipe:
1099 if (obuf != stdout) {
1100 safe_signal(SIGPIPE, SIG_IGN);
1101 Pclose(obuf, TRU1);
1102 safe_signal(SIGPIPE, dflpipe);
1104 NYD_LEAVE;
1105 return 0;
1108 FL void
1109 print_headers(size_t bottom, size_t topx, bool_t only_marked)
1111 size_t printed;
1112 NYD_ENTER;
1114 #ifdef HAVE_IMAP
1115 if (mb.mb_type == MB_IMAP)
1116 imap_getheaders(bottom, topx);
1117 #endif
1118 time_current_update(&time_current, FAL0);
1120 for (printed = 0; bottom <= topx; ++bottom) {
1121 struct message *mp = message + bottom - 1;
1122 if (only_marked) {
1123 if (!(mp->m_flag & MMARK))
1124 continue;
1125 } else if (!visible(mp))
1126 continue;
1127 _print_head(printed++, bottom, stdout, FAL0);
1129 NYD_LEAVE;
1132 FL int
1133 c_pdot(void *v)
1135 NYD_ENTER;
1136 UNUSED(v);
1137 printf("%d\n", (int)PTR2SIZE(dot - message + 1));
1138 NYD_LEAVE;
1139 return 0;
1142 FL int
1143 c_more(void *v)
1145 int *msgvec = v, rv;
1146 NYD_ENTER;
1148 rv = _type1(msgvec, TRU1, TRU1, FAL0, FAL0, NULL, NULL);
1149 NYD_LEAVE;
1150 return rv;
1153 FL int
1154 c_More(void *v)
1156 int *msgvec = v, rv;
1157 NYD_ENTER;
1159 rv = _type1(msgvec, FAL0, TRU1, FAL0, FAL0, NULL, NULL);
1160 NYD_LEAVE;
1161 return rv;
1164 FL int
1165 c_type(void *v)
1167 int *msgvec = v, rv;
1168 NYD_ENTER;
1170 rv = _type1(msgvec, TRU1, FAL0, FAL0, FAL0, NULL, NULL);
1171 NYD_LEAVE;
1172 return rv;
1175 FL int
1176 c_Type(void *v)
1178 int *msgvec = v, rv;
1179 NYD_ENTER;
1181 rv = _type1(msgvec, FAL0, FAL0, FAL0, FAL0, NULL, NULL);
1182 NYD_LEAVE;
1183 return rv;
1186 FL int
1187 c_show(void *v)
1189 int *msgvec = v, rv;
1190 NYD_ENTER;
1192 rv = _type1(msgvec, FAL0, FAL0, FAL0, TRU1, NULL, NULL);
1193 NYD_LEAVE;
1194 return rv;
1197 FL int
1198 c_pipe(void *v)
1200 char *str = v;
1201 int rv;
1202 NYD_ENTER;
1204 rv = _pipe1(str, 1);
1205 NYD_LEAVE;
1206 return rv;
1209 FL int
1210 c_Pipe(void *v)
1212 char *str = v;
1213 int rv;
1214 NYD_ENTER;
1216 rv = _pipe1(str, 0);
1217 NYD_LEAVE;
1218 return rv;
1221 FL int
1222 c_top(void *v)
1224 int *msgvec = v, *ip, c, topl, lines, empty_last;
1225 struct message *mp;
1226 char *cp, *linebuf = NULL;
1227 size_t linesize = 0;
1228 FILE *ibuf;
1229 NYD_ENTER;
1231 topl = 5;
1232 cp = ok_vlook(toplines);
1233 if (cp != NULL) {
1234 topl = atoi(cp);
1235 if (topl < 0 || topl > 10000)
1236 topl = 5;
1239 #ifdef HAVE_COLOUR
1240 if (options & OPT_TTYOUT)
1241 colour_table_create(FAL0); /* (salloc()s) */
1242 #endif
1243 empty_last = 1;
1244 for (ip = msgvec; *ip != 0 && UICMP(z, PTR2SIZE(ip - msgvec), <, msgCount);
1245 ++ip) {
1246 mp = message + *ip - 1;
1247 touch(mp);
1248 setdot(mp);
1249 did_print_dot = TRU1;
1250 if (!empty_last)
1251 printf("\n");
1252 _show_msg_overview(stdout, mp, *ip);
1253 if (mp->m_flag & MNOFROM)
1254 /* XXX c_top(): coloured output? */
1255 printf("From %s %s\n", fakefrom(mp), fakedate(mp->m_time));
1256 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL) { /* XXX could use TOP */
1257 v = NULL;
1258 break;
1260 c = mp->m_lines;
1261 for (lines = 0; lines < c && UICMP(32, lines, <=, topl); ++lines) {
1262 if (readline_restart(ibuf, &linebuf, &linesize, 0) < 0)
1263 break;
1264 puts(linebuf);
1266 for (cp = linebuf; *cp != '\0' && blankchar(*cp); ++cp)
1268 empty_last = (*cp == '\0');
1272 if (linebuf != NULL)
1273 free(linebuf);
1274 NYD_LEAVE;
1275 return (v != NULL);
1278 FL int
1279 c_stouch(void *v)
1281 int *msgvec = v, *ip;
1282 NYD_ENTER;
1284 for (ip = msgvec; *ip != 0; ++ip) {
1285 setdot(message + *ip - 1);
1286 dot->m_flag |= MTOUCH;
1287 dot->m_flag &= ~MPRESERVE;
1288 did_print_dot = TRU1;
1290 NYD_LEAVE;
1291 return 0;
1294 FL int
1295 c_mboxit(void *v)
1297 int *msgvec = v, *ip;
1298 NYD_ENTER;
1300 for (ip = msgvec; *ip != 0; ++ip) {
1301 setdot(message + *ip - 1);
1302 dot->m_flag |= MTOUCH | MBOX;
1303 dot->m_flag &= ~MPRESERVE;
1304 did_print_dot = TRU1;
1306 NYD_LEAVE;
1307 return 0;
1310 FL int
1311 c_folders(void *v)
1313 char dirname[PATH_MAX], *name, **argv = v;
1314 char const *cmd;
1315 int rv = 1;
1316 NYD_ENTER;
1318 if (*argv) {
1319 name = expand(*argv);
1320 if (name == NULL)
1321 goto jleave;
1322 } else if (!getfold(dirname, sizeof dirname)) {
1323 fprintf(stderr, _("No value set for \"folder\"\n"));
1324 goto jleave;
1325 } else
1326 name = dirname;
1328 if (which_protocol(name) == PROTO_IMAP) {
1329 #ifdef HAVE_IMAP
1330 imap_folders(name, *argv == NULL);
1331 #else
1332 rv = c_cmdnotsupp(NULL);
1333 #endif
1334 } else {
1335 if ((cmd = ok_vlook(LISTER)) == NULL)
1336 cmd = XLISTER;
1337 run_command(cmd, 0, -1, -1, name, NULL, NULL);
1339 jleave:
1340 NYD_LEAVE;
1341 return rv;
1344 /* s-it-mode */