*inbox*: if empty, only bypass *folder* to $MAIL or builtin default
[s-mailx.git] / cmd1.c
blob7c3a94f45edfc546658a376cad1c269146f505ee
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 - 2015 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. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
35 #undef n_FILE
36 #define n_FILE cmd1
38 #ifndef HAVE_AMALGAMATION
39 # include "nail.h"
40 #endif
42 static int _screen;
43 static sigjmp_buf _cmd1_pipestop;
44 static sigjmp_buf _cmd1_pipejmp;
46 static void _cmd1_onpipe(int signo);
47 static void _cmd1_brokpipe(int signo);
49 /* Prepare and print "[Message: xy]:" intro */
50 static void _show_msg_overview(FILE *obuf, struct message *mp, int msg_no);
52 /* ... And place the extracted date in `date' */
53 static void _parse_from_(struct message *mp, char date[FROM_DATEBUF]);
55 /* Print out the header of a specific message
56 * __hprf: handle *headline*
57 * __subject: return -1 if Subject: yet seen, otherwise smalloc()d Subject:
58 * __putindent: print out the indenting in threaded display */
59 static void _print_head(size_t yetprinted, size_t msgno, FILE *f,
60 bool_t threaded);
61 static void __hprf(size_t yetprinted, char const *fmt, size_t msgno,
62 FILE *f, bool_t threaded, char const *attrlist);
63 static char * __subject(struct message *mp, bool_t threaded,
64 size_t yetprinted);
65 static int __putindent(FILE *fp, struct message *mp, int maxwidth);
67 static int _dispc(struct message *mp, char const *a);
69 /* Shared `z' implementation */
70 static int a_cmd_scroll(char const *arg, bool_t onlynew);
72 /* Shared `headers' implementation */
73 static int _headers(int msgspec);
75 /* Show the requested messages */
76 static int _type1(int *msgvec, bool_t doign, bool_t dopage, bool_t dopipe,
77 bool_t dodecode, char *cmd, ui64_t *tstats);
79 /* Pipe the requested messages */
80 static int _pipe1(char *str, int doign);
82 static void
83 _cmd1_onpipe(int signo)
85 NYD_X; /* Signal handler */
86 UNUSED(signo);
87 siglongjmp(_cmd1_pipejmp, 1);
90 static void
91 _cmd1_brokpipe(int signo)
93 NYD_X; /* Signal handler */
94 UNUSED(signo);
95 siglongjmp(_cmd1_pipestop, 1);
98 static void
99 _show_msg_overview(FILE *obuf, struct message *mp, int msg_no)
101 char const *cpre = "", *csuf = "";
102 NYD_ENTER;
104 #ifdef HAVE_COLOUR
105 if (colour_table != NULL) {
106 struct str const *sp;
108 if ((sp = colour_get(COLOURSPEC_MSGINFO)) != NULL)
109 cpre = sp->s;
110 csuf = colour_get(COLOURSPEC_RESET)->s;
112 #endif
113 fprintf(obuf, _("%s[-- Message %2d -- %lu lines, %lu bytes --]:%s\n"),
114 cpre, msg_no, (ul_i)mp->m_lines, (ul_i)mp->m_size, csuf);
115 NYD_LEAVE;
118 static void
119 _parse_from_(struct message *mp, char date[FROM_DATEBUF]) /* TODO line pool */
121 FILE *ibuf;
122 int hlen;
123 char *hline = NULL;
124 size_t hsize = 0;
125 NYD_ENTER;
127 if ((ibuf = setinput(&mb, mp, NEED_HEADER)) != NULL &&
128 (hlen = readline_restart(ibuf, &hline, &hsize, 0)) > 0)
129 extract_date_from_from_(hline, hlen, date);
130 if (hline != NULL)
131 free(hline);
132 NYD_LEAVE;
135 static void
136 _print_head(size_t yetprinted, size_t msgno, FILE *f, bool_t threaded)
138 enum {attrlen = 14};
139 char attrlist[attrlen +1], *cp;
140 char const *fmt;
141 NYD_ENTER;
143 if ((cp = ok_vlook(attrlist)) != NULL) {
144 if (strlen(cp) == attrlen) {
145 memcpy(attrlist, cp, attrlen +1);
146 goto jattrok;
148 n_err(_("*attrlist* is not of the correct length, using builtin\n"));
151 if (ok_blook(bsdcompat) || ok_blook(bsdflags) || env_blook("SYSV3", FAL0)) {
152 char const bsdattr[attrlen +1] = "NU *HMFAT+-$~";
153 memcpy(attrlist, bsdattr, sizeof bsdattr);
154 } else {
155 char const pattr[attrlen +1] = "NUROSPMFAT+-$~";
156 memcpy(attrlist, pattr, sizeof pattr);
159 jattrok:
160 if ((fmt = ok_vlook(headline)) == NULL) {
161 fmt = ((ok_blook(bsdcompat) || ok_blook(bsdheadline))
162 ? "%>%a%m %-20f %16d %3l/%-5o %i%-S"
163 : "%>%a%m %-18f %16d %4l/%-5o %i%-s");
166 __hprf(yetprinted, fmt, msgno, f, threaded, attrlist);
167 NYD_LEAVE;
170 static void
171 __hprf(size_t yetprinted, char const *fmt, size_t msgno, FILE *f,
172 bool_t threaded, char const *attrlist)
174 char buf[16], datebuf[FROM_DATEBUF], *cp, *subjline;
175 char const *datefmt, *date, *name, *fp;
176 int i, n, s, wleft, subjlen;
177 struct message *mp;
178 time_t datet;
179 enum {
180 _NONE = 0,
181 _ISADDR = 1<<0,
182 _ISTO = 1<<1,
183 _IFMT = 1<<2,
184 _LOOP_MASK = (1<<3) - 1,
185 _SFMT = 1<<3
186 } flags = _NONE;
187 NYD_ENTER;
188 UNUSED(buf);
190 mp = message + msgno - 1;
191 datet = mp->m_time;
192 date = NULL;
194 datefmt = ok_vlook(datefield);
195 jredo:
196 if (datefmt != NULL) {
197 fp = hfield1("date", mp);/* TODO use m_date field! */
198 if (fp == NULL) {
199 datefmt = NULL;
200 goto jredo;
202 datet = rfctime(fp);
203 date = fakedate(datet);
204 fp = ok_vlook(datefield_markout_older);
205 i = (*datefmt != '\0');
206 if (fp != NULL)
207 i |= (*fp != '\0') ? 2 | 4 : 2; /* XXX no magics */
209 /* May we strftime(3)? */
210 if (i & (1 | 4))
211 memcpy(&time_current.tc_local, localtime(&datet),
212 sizeof time_current.tc_local);
214 if ((i & 2) && (datet > time_current.tc_time + DATE_SECSDAY ||
215 #define _6M ((DATE_DAYSYEAR / 2) * DATE_SECSDAY)
216 (datet + _6M < time_current.tc_time))) {
217 #undef _6M
218 if ((datefmt = (i & 4) ? fp : NULL) == NULL) {
219 memset(datebuf, ' ', FROM_DATEBUF); /* xxx ur */
220 memcpy(datebuf + 4, date + 4, 7);
221 datebuf[4 + 7] = ' ';
222 memcpy(datebuf + 4 + 7 + 1, date + 20, 4);
223 datebuf[4 + 7 + 1 + 4] = '\0';
224 date = datebuf;
226 } else if ((i & 1) == 0)
227 datefmt = NULL;
228 } else if (datet == (time_t)0 && !(mp->m_flag & MNOFROM)) {
229 /* TODO eliminate this path, query the FROM_ date in setptr(),
230 * TODO all other codepaths do so by themselves ALREADY ?????
231 * TODO assert(mp->m_time != 0);, then
232 * TODO ALSO changes behaviour of datefield_markout_older */
233 _parse_from_(mp, datebuf);
234 date = datebuf;
235 } else
236 date = fakedate(datet);
238 flags |= _ISADDR;
239 name = name1(mp, 0);
240 if (name != NULL && ok_blook(showto) && is_myname(skin(name))) {
241 if ((cp = hfield1("to", mp)) != NULL) {
242 name = cp;
243 flags |= _ISTO;
246 if (name == NULL) {
247 name = "";
248 flags &= ~_ISADDR;
250 if (flags & _ISADDR)
251 name = ok_blook(showname) ? realname(name) : prstr(skin(name));
253 subjline = NULL;
255 /* Detect the width of the non-format characters in *headline*;
256 * like that we can simply use putc() in the next loop, since we have
257 * already calculated their column widths (TODO it's sick) */
258 wleft = subjlen = scrnwidth;
260 for (fp = fmt; *fp != '\0'; ++fp) {
261 if (*fp == '%') {
262 if (*++fp == '-')
263 ++fp;
264 else if (*fp == '+')
265 ++fp;
266 if (digitchar(*fp)) {
267 n = 0;
269 n = 10*n + *fp - '0';
270 while (++fp, digitchar(*fp));
271 subjlen -= n;
273 if (*fp == 'i')
274 flags |= _IFMT;
276 if (*fp == '\0')
277 break;
278 } else {
279 #ifdef HAVE_WCWIDTH
280 if (mb_cur_max > 1) {
281 wchar_t wc;
282 if ((s = mbtowc(&wc, fp, mb_cur_max)) == -1)
283 n = s = 1;
284 else if ((n = wcwidth(wc)) == -1)
285 n = 1;
286 } else
287 #endif
288 n = s = 1;
289 subjlen -= n;
290 wleft -= n;
291 while (--s > 0)
292 ++fp;
296 /* Walk *headline*, producing output TODO not (really) MB safe */
297 for (fp = fmt; *fp != '\0'; ++fp) {
298 char c;
299 if ((c = *fp & 0xFF) != '%')
300 putc(c, f);
301 else {
302 flags &= _LOOP_MASK;
303 n = 0;
304 s = 1;
305 if (*++fp == '-') {
306 s = -1;
307 ++fp;
308 } else if (*fp == '+')
309 ++fp;
310 if (digitchar(*fp)) {
312 n = 10*n + *fp - '0';
313 while (++fp, digitchar(*fp));
315 if (*fp == '\0')
316 break;
318 n *= s;
319 switch ((c = *fp & 0xFF)) {
320 case '%':
321 goto jputc;
322 case '>':
323 case '<':
324 if (dot != mp)
325 c = ' ';
326 goto jputc;
327 case '$':
328 #ifdef HAVE_SPAM
329 if (n == 0)
330 n = 5;
331 if (UICMP(32, ABS(n), >, wleft))
332 n = (n < 0) ? -wleft : wleft;
333 snprintf(buf, sizeof buf, "%u.%02u",
334 (mp->m_spamscore >> 8), (mp->m_spamscore & 0xFF));
335 n = fprintf(f, "%*s", n, buf);
336 wleft = (n >= 0) ? wleft - n : 0;
337 break;
338 #else
339 c = '?';
340 goto jputc;
341 #endif
342 case 'a':
343 c = _dispc(mp, attrlist);
344 jputc:
345 if (UICMP(32, ABS(n), >, wleft))
346 n = (n < 0) ? -wleft : wleft;
347 n = fprintf(f, "%*c", n, c);
348 wleft = (n >= 0) ? wleft - n : 0;
349 break;
350 case 'd':
351 if (datefmt != NULL) {
352 i = strftime(datebuf, sizeof datebuf, datefmt,
353 &time_current.tc_local);
354 if (i != 0)
355 date = datebuf;
356 else
357 n_err(_("Ignored date format, it excesses the target "
358 "buffer (%lu bytes)\n"), (ul_i)sizeof(datebuf));
359 datefmt = NULL;
361 if (n == 0)
362 n = 16;
363 if (UICMP(32, ABS(n), >, wleft))
364 n = (n < 0) ? -wleft : wleft;
365 n = fprintf(f, "%*.*s", n, n, date);
366 wleft = (n >= 0) ? wleft - n : 0;
367 break;
368 case 'e':
369 if (n == 0)
370 n = 2;
371 if (UICMP(32, ABS(n), >, wleft))
372 n = (n < 0) ? -wleft : wleft;
373 n = fprintf(f, "%*u", n, (threaded == 1 ? mp->m_level : 0));
374 wleft = (n >= 0) ? wleft - n : 0;
375 break;
376 case 'f':
377 if (n == 0) {
378 n = 18;
379 if (s < 0)
380 n = -n;
382 i = ABS(n);
383 if (i > wleft) {
384 i = wleft;
385 n = (n < 0) ? -wleft : wleft;
387 if (flags & _ISTO) /* XXX tr()! */
388 i -= 3;
389 n = fprintf(f, "%s%s", ((flags & _ISTO) ? "To " : ""),
390 colalign(name, i, n, &wleft));
391 if (n < 0)
392 wleft = 0;
393 else if (flags & _ISTO)
394 wleft -= 3;
395 break;
396 case 'i':
397 if (threaded) {
398 n = __putindent(f, mp, MIN(wleft, scrnwidth - 60));
399 wleft = (n >= 0) ? wleft - n : 0;
401 break;
402 case 'l':
403 if (n == 0)
404 n = 4;
405 if (UICMP(32, ABS(n), >, wleft))
406 n = (n < 0) ? -wleft : wleft;
407 if (mp->m_xlines) {
408 n = fprintf(f, "%*ld", n, mp->m_xlines);
409 wleft = (n >= 0) ? wleft - n : 0;
410 } else {
411 n = ABS(n);
412 wleft -= n;
413 while (n-- != 0)
414 putc(' ', f);
416 break;
417 case 'm':
418 if (n == 0) {
419 n = 3;
420 if (threaded)
421 for (i = msgCount; i > 999; i /= 10)
422 ++n;
424 if (UICMP(32, ABS(n), >, wleft))
425 n = (n < 0) ? -wleft : wleft;
426 n = fprintf(f, "%*lu", n, (ul_i)msgno);
427 wleft = (n >= 0) ? wleft - n : 0;
428 break;
429 case 'o':
430 if (n == 0)
431 n = -5;
432 if (UICMP(32, ABS(n), >, wleft))
433 n = (n < 0) ? -wleft : wleft;
434 n = fprintf(f, "%*lu", n, (long)mp->m_xsize);
435 wleft = (n >= 0) ? wleft - n : 0;
436 break;
437 case 'S':
438 flags |= _SFMT;
439 /*FALLTHRU*/
440 case 's':
441 if (n == 0)
442 n = subjlen - 2;
443 if (n > 0 && s < 0)
444 n = -n;
445 if (subjlen > wleft)
446 subjlen = wleft;
447 if (UICMP(32, ABS(n), >, subjlen))
448 n = (n < 0) ? -subjlen : subjlen;
449 if (flags & _SFMT)
450 n -= (n < 0) ? -2 : 2;
451 if (n == 0)
452 break;
453 if (subjline == NULL)
454 subjline = __subject(mp, (threaded && (flags & _IFMT)),
455 yetprinted);
456 if (subjline == (char*)-1) {
457 n = fprintf(f, "%*s", n, "");
458 wleft = (n >= 0) ? wleft - n : 0;
459 } else {
460 n = fprintf(f, ((flags & _SFMT) ? "\"%s\"" : "%s"),
461 colalign(subjline, ABS(n), n, &wleft));
462 if (n < 0)
463 wleft = 0;
465 break;
466 case 'T': { /* Message recipient flags */
467 /* We never can reuse "name" since it's the full name */
468 struct name const *np = lextract(hfield1("to", mp), GTO | GSKIN);
469 c = ' ';
470 i = 0;
471 j_A_redo:
472 for (; np != NULL; np = np->n_flink) {
473 switch (is_mlist(np->n_name, FAL0)) {
474 case MLIST_SUBSCRIBED: c = 'S'; goto jputc;
475 case MLIST_KNOWN: c = 'L'; goto jputc;
476 case MLIST_OTHER:
477 default: break;
480 if (i != 0)
481 goto jputc;
482 ++i;
483 np = lextract(hfield1("cc", mp), GCC | GSKIN);
484 goto j_A_redo;
486 case 't':
487 if (n == 0) {
488 n = 3;
489 if (threaded)
490 for (i = msgCount; i > 999; i /= 10)
491 ++n;
493 if (UICMP(32, ABS(n), >, wleft))
494 n = (n < 0) ? -wleft : wleft;
495 n = fprintf(f, "%*lu", n,
496 (threaded ? (ul_i)mp->m_threadpos : (ul_i)msgno));
497 wleft = (n >= 0) ? wleft - n : 0;
498 break;
499 case 'U':
500 #ifdef HAVE_IMAP
501 if (n == 0)
502 n = 9;
503 if (UICMP(32, ABS(n), >, wleft))
504 n = (n < 0) ? -wleft : wleft;
505 n = fprintf(f, "%*lu", n, mp->m_uid);
506 wleft = (n >= 0) ? wleft - n : 0;
507 break;
508 #else
509 c = '?';
510 goto jputc;
511 #endif
512 default:
513 if (options & OPT_D_V)
514 n_err(_("Unkown *headline* format: \"%%%c\"\n"), c);
515 c = '?';
516 goto jputc;
519 if (wleft <= 0)
520 break;
523 putc('\n', f);
525 if (subjline != NULL && subjline != (char*)-1)
526 free(subjline);
527 NYD_LEAVE;
530 static char *
531 __subject(struct message *mp, bool_t threaded, size_t yetprinted)
533 struct str in, out;
534 char *rv = (char*)-1, *ms;
535 NYD_ENTER;
537 if ((ms = hfield1("subject", mp)) == NULL)
538 goto jleave;
540 in.l = strlen(in.s = ms);
541 mime_fromhdr(&in, &out, TD_ICONV | TD_ISPR);
542 rv = ms = out.s;
544 if (!threaded || mp->m_level == 0)
545 goto jleave;
547 /* In a display thread - check wether this message uses the same
548 * Subject: as it's parent or elder neighbour, suppress printing it if
549 * this is the case. To extend this a bit, ignore any leading Re: or
550 * Fwd: plus follow-up WS. Ignore invisible messages along the way */
551 ms = subject_re_trim(ms);
553 for (; (mp = prev_in_thread(mp)) != NULL && yetprinted-- > 0;) {
554 char *os;
556 if (visible(mp) && (os = hfield1("subject", mp)) != NULL) {
557 struct str oout;
558 int x;
560 in.l = strlen(in.s = os);
561 mime_fromhdr(&in, &oout, TD_ICONV | TD_ISPR);
562 x = asccasecmp(ms, subject_re_trim(oout.s));
563 free(oout.s);
565 if (!x) {
566 free(out.s);
567 rv = (char*)-1;
569 break;
572 jleave:
573 NYD_LEAVE;
574 return rv;
577 static int
578 __putindent(FILE *fp, struct message *mp, int maxwidth)/* XXX no magic consts */
580 struct message *mq;
581 int *us, indlvl, indw, i, important = MNEW | MFLAGGED;
582 char *cs;
583 NYD_ENTER;
585 if (mp->m_level == 0 || maxwidth == 0) {
586 indw = 0;
587 goto jleave;
590 cs = ac_alloc(mp->m_level);
591 us = ac_alloc(mp->m_level * sizeof *us);
593 i = mp->m_level - 1;
594 if (mp->m_younger && UICMP(32, i + 1, ==, mp->m_younger->m_level)) {
595 if (mp->m_parent && mp->m_parent->m_flag & important)
596 us[i] = mp->m_flag & important ? 0x2523 : 0x2520;
597 else
598 us[i] = mp->m_flag & important ? 0x251D : 0x251C;
599 cs[i] = '+';
600 } else {
601 if (mp->m_parent && mp->m_parent->m_flag & important)
602 us[i] = mp->m_flag & important ? 0x2517 : 0x2516;
603 else
604 us[i] = mp->m_flag & important ? 0x2515 : 0x2514;
605 cs[i] = '\\';
608 mq = mp->m_parent;
609 for (i = mp->m_level - 2; i >= 0; --i) {
610 if (mq) {
611 if (UICMP(32, i, >, mq->m_level - 1)) {
612 us[i] = cs[i] = ' ';
613 continue;
615 if (mq->m_younger) {
616 if (mq->m_parent && (mq->m_parent->m_flag & important))
617 us[i] = 0x2503;
618 else
619 us[i] = 0x2502;
620 cs[i] = '|';
621 } else
622 us[i] = cs[i] = ' ';
623 mq = mq->m_parent;
624 } else
625 us[i] = cs[i] = ' ';
628 --maxwidth;
629 for (indlvl = indw = 0; (ui8_t)indlvl < mp->m_level && indw < maxwidth;
630 ++indlvl) {
631 if (indw < maxwidth - 1)
632 indw += (int)putuc(us[indlvl], cs[indlvl] & 0xFF, fp);
633 else
634 indw += (int)putuc(0x21B8, '^', fp);
636 indw += putuc(0x25B8, '>', fp);
638 ac_free(us);
639 ac_free(cs);
640 jleave:
641 NYD_LEAVE;
642 return indw;
645 static int
646 _dispc(struct message *mp, char const *a)
648 int i = ' ';
649 NYD_ENTER;
651 if ((mp->m_flag & (MREAD | MNEW)) == MREAD)
652 i = a[3];
653 if ((mp->m_flag & (MREAD | MNEW)) == (MREAD | MNEW))
654 i = a[2];
655 if (mp->m_flag & MANSWERED)
656 i = a[8];
657 if (mp->m_flag & MDRAFTED)
658 i = a[9];
659 if ((mp->m_flag & (MREAD | MNEW)) == MNEW)
660 i = a[0];
661 if (!(mp->m_flag & (MREAD | MNEW)))
662 i = a[1];
663 if (mp->m_flag & MSPAM)
664 i = a[12];
665 if (mp->m_flag & MSPAMUNSURE)
666 i = a[13];
667 if (mp->m_flag & MSAVED)
668 i = a[4];
669 if (mp->m_flag & MPRESERVE)
670 i = a[5];
671 if (mp->m_flag & (MBOX | MBOXED))
672 i = a[6];
673 if (mp->m_flag & MFLAGGED)
674 i = a[7];
675 if (mb.mb_threaded == 1 && mp->m_collapsed > 0)
676 i = a[11];
677 if (mb.mb_threaded == 1 && mp->m_collapsed < 0)
678 i = a[10];
679 NYD_LEAVE;
680 return i;
683 static int
684 a_cmd_scroll(char const *arg, bool_t onlynew){
685 long l;
686 char *eptr;
687 bool_t isabs;
688 int msgspec, size, maxs;
689 NYD2_ENTER;
691 /* TODO scroll problem: we do not know whether + and $ have already reached
692 * TODO the last screen in threaded mode */
693 msgspec = onlynew ? -1 : 0;
694 size = screensize();
695 if((maxs = msgCount / size) > 0 && msgCount % size == 0)
696 --maxs;
698 switch(*arg){
699 case '\0':
700 ++_screen;
701 goto jfwd;
702 case '^':
703 if(arg[1] != '\0')
704 goto jerr;
705 if(_screen == 0)
706 goto jerrbwd;
707 _screen = 0;
708 break;
709 case '$':
710 if(arg[1] != '\0')
711 goto jerr;
712 if(_screen == maxs)
713 goto jerrfwd;
714 _screen = maxs;
715 break;
716 case '+':
717 if(arg[1] == '\0')
718 ++_screen;
719 else{
720 isabs = FAL0;
722 ++arg;
723 if(0){
724 case '1': case '2': case '3': case '4': case '5':
725 case '6': case '7': case '8': case '9': case '0':
726 isabs = TRU1;
728 l = strtol(arg, &eptr, 10);
729 if(*eptr != '\0')
730 goto jerr;
731 if(l > maxs - (isabs ? 0 : _screen))
732 goto jerrfwd;
733 _screen = isabs ? (int)l : _screen + l;
735 jfwd:
736 if(_screen > maxs){
737 jerrfwd:
738 _screen = maxs;
739 printf(_("On last screenful of messages\n"));
741 break;
743 case '-':
744 if(arg[1] == '\0')
745 --_screen;
746 else{
747 ++arg;
748 l = strtol(arg, &eptr, 10);
749 if(*eptr != '\0')
750 goto jerr;
751 if(l > _screen)
752 goto jerrbwd;
753 _screen -= l;
755 if(_screen < 0){
756 jerrbwd:
757 _screen = 0;
758 printf(_("On first screenful of messages\n"));
760 if(msgspec == -1)
761 msgspec = -2;
762 break;
763 default:
764 jerr:
765 n_err(_("Unrecognized scrolling command \"%s\"\n"), arg);
766 size = 1;
767 goto jleave;
770 size = _headers(msgspec);
771 jleave:
772 NYD2_LEAVE;
773 return size;
776 static int
777 _headers(int msgspec) /* FIXME rework v14.8; also: Neitzel mail, 2014-08-21 */
779 ui32_t flag;
780 int g, k, mesg, size, lastg = 1;
781 struct message *mp, *mq, *lastmq = NULL;
782 enum mflag fl = MNEW | MFLAGGED;
783 NYD_ENTER;
785 time_current_update(&time_current, FAL0);
787 flag = 0;
788 size = screensize();
789 if (_screen < 0)
790 _screen = 0;
791 #if 0 /* FIXME original code path */
792 k = _screen * size;
793 #else
794 if (msgspec <= 0)
795 k = _screen * size;
796 else
797 k = msgspec;
798 #endif
799 if (k >= msgCount)
800 k = msgCount - size;
801 if (k < 0)
802 k = 0;
804 if (mb.mb_threaded == 0) {
805 g = 0;
806 mq = message;
807 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
808 if (visible(mp)) {
809 if (g % size == 0)
810 mq = mp;
811 if (mp->m_flag & fl) {
812 lastg = g;
813 lastmq = mq;
815 if ((msgspec > 0 && PTRCMP(mp, ==, message + msgspec - 1)) ||
816 (msgspec == 0 && g == k) ||
817 (msgspec == -2 && g == k + size && lastmq) ||
818 (msgspec < 0 && g >= k && (mp->m_flag & fl) != 0))
819 break;
820 g++;
822 if (lastmq && (msgspec == -2 ||
823 (msgspec == -1 && PTRCMP(mp, ==, message + msgCount)))) {
824 g = lastg;
825 mq = lastmq;
827 _screen = g / size;
828 mp = mq;
829 mesg = (int)PTR2SIZE(mp - message);
830 if (PTRCMP(dot, !=, message + msgspec - 1)) { /* TODO really?? */
831 for (mq = mp; PTRCMP(mq, <, message + msgCount); ++mq)
832 if (visible(mq)) {
833 setdot(mq);
834 break;
837 #ifdef HAVE_IMAP
838 if (mb.mb_type == MB_IMAP)
839 imap_getheaders(mesg + 1, mesg + size);
840 #endif
841 srelax_hold();
842 for (; PTRCMP(mp, <, message + msgCount); ++mp) {
843 ++mesg;
844 if (!visible(mp))
845 continue;
846 if (UICMP(32, flag++, >=, size))
847 break;
848 _print_head(0, mesg, stdout, 0);
849 srelax();
851 srelax_rele();
852 } else { /* threaded */
853 g = 0;
854 mq = threadroot;
855 for (mp = threadroot; mp; mp = next_in_thread(mp))
856 if (visible(mp) &&
857 (mp->m_collapsed <= 0 ||
858 PTRCMP(mp, ==, message + msgspec - 1))) {
859 if (g % size == 0)
860 mq = mp;
861 if (mp->m_flag & fl) {
862 lastg = g;
863 lastmq = mq;
865 if ((msgspec > 0 && PTRCMP(mp, ==, message + msgspec - 1)) ||
866 (msgspec == 0 && g == k) ||
867 (msgspec == -2 && g == k + size && lastmq) ||
868 (msgspec < 0 && g >= k && (mp->m_flag & fl) != 0))
869 break;
870 g++;
872 if (lastmq && (msgspec == -2 ||
873 (msgspec == -1 && PTRCMP(mp, ==, message + msgCount)))) {
874 g = lastg;
875 mq = lastmq;
877 _screen = g / size;
878 mp = mq;
879 if (PTRCMP(dot, !=, message + msgspec - 1)) { /* TODO really?? */
880 for (mq = mp; mq; mq = next_in_thread(mq))
881 if (visible(mq) && mq->m_collapsed <= 0) {
882 setdot(mq);
883 break;
886 srelax_hold();
887 while (mp) {
888 if (visible(mp) &&
889 (mp->m_collapsed <= 0 ||
890 PTRCMP(mp, ==, message + msgspec - 1))) {
891 if (UICMP(32, flag++, >=, size))
892 break;
893 _print_head(flag - 1, PTR2SIZE(mp - message + 1), stdout,
894 mb.mb_threaded);
895 srelax();
897 mp = next_in_thread(mp);
899 srelax_rele();
902 if (!flag)
903 printf(_("No more mail.\n"));
904 NYD_LEAVE;
905 return !flag;
908 static int
909 _type1(int *msgvec, bool_t doign, bool_t dopage, bool_t dopipe,
910 bool_t dodecode, char *cmd, ui64_t *tstats)
912 ui64_t mstats[1];
913 int rv, *ip;
914 struct message *mp;
915 char const *cp;
916 FILE * volatile obuf;
917 bool_t volatile hadsig = FAL0, isrelax = FAL0;
918 NYD_ENTER;
919 {/* C89.. */
920 enum sendaction const action = ((dopipe && ok_blook(piperaw))
921 ? SEND_MBOX : dodecode
922 ? SEND_SHOW : doign
923 ? SEND_TODISP : SEND_TODISP_ALL);
924 bool_t const volatile formfeed = (dopipe && ok_blook(page));
925 obuf = stdout;
927 if (sigsetjmp(_cmd1_pipestop, 1)) {
928 hadsig = TRU1;
929 goto jclose_pipe;
932 if (dopipe) {
933 if ((cp = ok_vlook(SHELL)) == NULL)
934 cp = XSHELL;
935 if ((obuf = Popen(cmd, "w", cp, NULL, 1)) == NULL) {
936 n_perr(cmd, 0);
937 obuf = stdout;
938 } else
939 safe_signal(SIGPIPE, &_cmd1_brokpipe);
940 } else if ((options & OPT_TTYOUT) && (dopage ||
941 ((options & OPT_INTERACTIVE) && (cp = ok_vlook(crt)) != NULL))) {
942 char const *pager = NULL;
943 size_t nlines = 0;
945 if (!dopage) {
946 for (ip = msgvec; *ip && PTRCMP(ip - msgvec, <, msgCount); ++ip) {
947 mp = message + *ip - 1;
948 if (!(mp->m_have & HAVE_BODY))
949 if (get_body(mp) != OKAY) {
950 rv = 1;
951 goto jleave;
953 nlines += mp->m_lines + 1; /* Message info XXX and PARTS... */
957 /* >= not <: we return to the prompt */
958 if (dopage || UICMP(z, nlines, >=,
959 (*cp != '\0' ? atoi(cp) : realscreenheight))) {
960 char const *env_add[2];
961 pager = get_pager(env_add + 0);
962 env_add[1] = NULL;
963 obuf = Popen(pager, "w", NULL, env_add, 1);
964 if (obuf == NULL) {
965 n_perr(pager, 0);
966 obuf = stdout;
967 pager = NULL;
968 } else
969 safe_signal(SIGPIPE, &_cmd1_brokpipe);
971 #ifdef HAVE_COLOUR
972 if (IS_TTY_SESSION() && action != SEND_MBOX)
973 colour_table_create(pager != NULL); /* (salloc()s!) */
974 #endif
976 #ifdef HAVE_COLOUR
977 else if (IS_TTY_SESSION() && action != SEND_MBOX)
978 colour_table_create(FAL0); /* (salloc()s!) */
979 #endif
981 /*TODO unless we have our signal manager special care must be taken */
982 srelax_hold();
983 isrelax = TRU1;
984 for (ip = msgvec; *ip && PTRCMP(ip - msgvec, <, msgCount); ++ip) {
985 mp = message + *ip - 1;
986 touch(mp);
987 setdot(mp);
988 uncollapse1(mp, 1);
989 if (!dopipe && ip != msgvec)
990 fprintf(obuf, "\n");
991 if (action != SEND_MBOX)
992 _show_msg_overview(obuf, mp, *ip);
993 sendmp(mp, obuf, (doign ? ignore : NULL), NULL, action, mstats);
994 srelax();
995 if (formfeed) /* TODO a nicer way to separate piped messages! */
996 putc('\f', obuf);
997 if (tstats != NULL)
998 tstats[0] += mstats[0];
1000 srelax_rele();
1001 isrelax = FAL0;
1003 jclose_pipe:
1004 if (obuf != stdout) {
1005 /* Ignore SIGPIPE so it can't cause a duplicate close */
1006 safe_signal(SIGPIPE, SIG_IGN);
1007 if (hadsig && isrelax)
1008 srelax_rele();
1009 colour_reset(obuf); /* XXX hacky; only here because we still jump */
1010 Pclose(obuf, TRU1);
1011 safe_signal(SIGPIPE, dflpipe);
1013 rv = 0;
1015 jleave:
1016 NYD_LEAVE;
1017 return rv;
1020 static int
1021 _pipe1(char *str, int doign)
1023 ui64_t stats[1];
1024 char *cmd;
1025 int *msgvec, rv = 1;
1026 bool_t needs_list;
1027 NYD_ENTER;
1029 if ((cmd = laststring(str, &needs_list, TRU1)) == NULL) {
1030 cmd = ok_vlook(cmd);
1031 if (cmd == NULL || *cmd == '\0') {
1032 n_err(_("Variable *cmd* not set\n"));
1033 goto jleave;
1037 msgvec = salloc((msgCount + 2) * sizeof *msgvec);
1039 if (!needs_list) {
1040 *msgvec = first(0, MMNORM);
1041 if (*msgvec == 0) {
1042 if (pstate & PS_HOOK_MASK) {
1043 rv = 0;
1044 goto jleave;
1046 puts(_("No messages to pipe."));
1047 goto jleave;
1049 msgvec[1] = 0;
1050 } else if (getmsglist(str, msgvec, 0) < 0)
1051 goto jleave;
1052 if (*msgvec == 0) {
1053 if (pstate & PS_HOOK_MASK) {
1054 rv = 0;
1055 goto jleave;
1057 printf("No applicable messages.\n");
1058 goto jleave;
1061 printf(_("Pipe to: \"%s\"\n"), cmd);
1062 stats[0] = 0;
1063 if ((rv = _type1(msgvec, doign, FAL0, TRU1, FAL0, cmd, stats)) == 0)
1064 printf("\"%s\" %" PRIu64 " bytes\n", cmd, stats[0]);
1065 jleave:
1066 NYD_LEAVE;
1067 return rv;
1070 FL int
1071 c_cmdnotsupp(void *v) /* TODO -> lex.c */
1073 NYD_ENTER;
1074 UNUSED(v);
1075 n_err(_("The requested feature is not compiled in\n"));
1076 NYD_LEAVE;
1077 return 1;
1080 FL int
1081 c_headers(void *v)
1083 int rv;
1084 NYD_ENTER;
1086 rv = print_header_group((int*)v);
1087 NYD_LEAVE;
1088 return rv;
1091 FL int
1092 print_header_group(int *vector)
1094 int rv;
1095 NYD_ENTER;
1097 assert(vector != NULL && vector != (void*)-1);
1098 rv = _headers(vector[0]);
1099 NYD_LEAVE;
1100 return rv;
1103 FL int
1104 c_scroll(void *v)
1106 int rv;
1107 NYD_ENTER;
1109 rv = a_cmd_scroll(v, FAL0);
1110 NYD_LEAVE;
1111 return rv;
1114 FL int
1115 c_Scroll(void *v)
1117 int rv;
1118 NYD_ENTER;
1120 rv = a_cmd_scroll(v, TRU1);
1121 NYD_LEAVE;
1122 return rv;
1125 FL int
1126 c_from(void *v)
1128 int *msgvec = v, *ip, n;
1129 char *cp;
1130 FILE * volatile obuf;
1131 NYD_ENTER;
1133 time_current_update(&time_current, FAL0);
1134 obuf = stdout;
1136 /* TODO unfixable memory leaks still */
1137 if (IS_TTY_SESSION() && (cp = ok_vlook(crt)) != NULL) {
1138 for (n = 0, ip = msgvec; *ip != 0; ++ip)
1139 n++;
1140 if (n > (*cp == '\0' ? screensize() : atoi(cp)) + 3) {
1141 char const *p;
1142 if (sigsetjmp(_cmd1_pipejmp, 1))
1143 goto jendpipe;
1144 p = get_pager(NULL);
1145 if ((obuf = Popen(p, "w", NULL, NULL, 1)) == NULL) {
1146 n_perr(p, 0);
1147 obuf = stdout;
1148 cp = NULL;
1149 } else
1150 safe_signal(SIGPIPE, &_cmd1_onpipe);
1154 srelax_hold();
1155 for (n = 0, ip = msgvec; *ip != 0; ++ip) { /* TODO join into _print_head() */
1156 _print_head((size_t)n++, (size_t)*ip, obuf, mb.mb_threaded);
1157 srelax();
1159 srelax_rele();
1161 if (--ip >= msgvec)
1162 setdot(message + *ip - 1);
1164 jendpipe:
1165 if (obuf != stdout) {
1166 safe_signal(SIGPIPE, SIG_IGN);
1167 Pclose(obuf, TRU1);
1168 safe_signal(SIGPIPE, dflpipe);
1170 NYD_LEAVE;
1171 return 0;
1174 FL void
1175 print_headers(size_t bottom, size_t topx, bool_t only_marked)
1177 size_t printed;
1178 NYD_ENTER;
1180 #ifdef HAVE_IMAP
1181 if (mb.mb_type == MB_IMAP)
1182 imap_getheaders(bottom, topx);
1183 #endif
1184 time_current_update(&time_current, FAL0);
1186 srelax_hold();
1187 for (printed = 0; bottom <= topx; ++bottom) {
1188 struct message *mp = message + bottom - 1;
1189 if (only_marked) {
1190 if (!(mp->m_flag & MMARK))
1191 continue;
1192 } else if (!visible(mp))
1193 continue;
1194 _print_head(printed++, bottom, stdout, FAL0);
1195 srelax();
1197 srelax_rele();
1198 NYD_LEAVE;
1201 FL int
1202 c_pdot(void *v)
1204 NYD_ENTER;
1205 UNUSED(v);
1206 printf("%d\n", (int)PTR2SIZE(dot - message + 1));
1207 NYD_LEAVE;
1208 return 0;
1211 FL int
1212 c_more(void *v)
1214 int *msgvec = v, rv;
1215 NYD_ENTER;
1217 rv = _type1(msgvec, TRU1, TRU1, FAL0, FAL0, NULL, NULL);
1218 NYD_LEAVE;
1219 return rv;
1222 FL int
1223 c_More(void *v)
1225 int *msgvec = v, rv;
1226 NYD_ENTER;
1228 rv = _type1(msgvec, FAL0, TRU1, FAL0, FAL0, NULL, NULL);
1229 NYD_LEAVE;
1230 return rv;
1233 FL int
1234 c_type(void *v)
1236 int *msgvec = v, rv;
1237 NYD_ENTER;
1239 rv = _type1(msgvec, TRU1, FAL0, FAL0, FAL0, NULL, NULL);
1240 NYD_LEAVE;
1241 return rv;
1244 FL int
1245 c_Type(void *v)
1247 int *msgvec = v, rv;
1248 NYD_ENTER;
1250 rv = _type1(msgvec, FAL0, FAL0, FAL0, FAL0, NULL, NULL);
1251 NYD_LEAVE;
1252 return rv;
1255 FL int
1256 c_show(void *v)
1258 int *msgvec = v, rv;
1259 NYD_ENTER;
1261 rv = _type1(msgvec, FAL0, FAL0, FAL0, TRU1, NULL, NULL);
1262 NYD_LEAVE;
1263 return rv;
1266 FL int
1267 c_pipe(void *v)
1269 char *str = v;
1270 int rv;
1271 NYD_ENTER;
1273 rv = _pipe1(str, 1);
1274 NYD_LEAVE;
1275 return rv;
1278 FL int
1279 c_Pipe(void *v)
1281 char *str = v;
1282 int rv;
1283 NYD_ENTER;
1285 rv = _pipe1(str, 0);
1286 NYD_LEAVE;
1287 return rv;
1290 FL int
1291 c_top(void *v)
1293 int *msgvec = v, *ip, c, topl, lines, empty_last;
1294 struct message *mp;
1295 char *cp, *linebuf = NULL;
1296 size_t linesize = 0;
1297 FILE *ibuf;
1298 NYD_ENTER;
1300 topl = 5;
1301 cp = ok_vlook(toplines);
1302 if (cp != NULL) {
1303 topl = atoi(cp);
1304 if (topl < 0 || topl > 10000)
1305 topl = 5;
1308 #ifdef HAVE_COLOUR
1309 if (IS_TTY_SESSION())
1310 colour_table_create(FAL0); /* (salloc()s) */
1311 #endif
1312 empty_last = 1;
1313 for (ip = msgvec; *ip != 0 && UICMP(z, PTR2SIZE(ip - msgvec), <, msgCount);
1314 ++ip) {
1315 mp = message + *ip - 1;
1316 touch(mp);
1317 setdot(mp);
1318 pstate |= PS_DID_PRINT_DOT;
1319 if (!empty_last)
1320 printf("\n");
1321 _show_msg_overview(stdout, mp, *ip);
1322 if (mp->m_flag & MNOFROM)
1323 /* XXX c_top(): coloured output? */
1324 printf("From %s %s\n", fakefrom(mp), fakedate(mp->m_time));
1325 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL) { /* XXX could use TOP */
1326 v = NULL;
1327 break;
1329 c = mp->m_lines;
1330 for (lines = 0; lines < c && UICMP(32, lines, <=, topl); ++lines) {
1331 if (readline_restart(ibuf, &linebuf, &linesize, 0) < 0)
1332 break;
1333 puts(linebuf);
1335 for (cp = linebuf; *cp != '\0' && blankchar(*cp); ++cp)
1337 empty_last = (*cp == '\0');
1341 if (linebuf != NULL)
1342 free(linebuf);
1343 NYD_LEAVE;
1344 return (v != NULL);
1347 FL int
1348 c_stouch(void *v)
1350 int *msgvec = v, *ip;
1351 NYD_ENTER;
1353 for (ip = msgvec; *ip != 0; ++ip) {
1354 setdot(message + *ip - 1);
1355 dot->m_flag |= MTOUCH;
1356 dot->m_flag &= ~MPRESERVE;
1357 pstate |= PS_DID_PRINT_DOT;
1359 NYD_LEAVE;
1360 return 0;
1363 FL int
1364 c_mboxit(void *v)
1366 int *msgvec = v, *ip;
1367 NYD_ENTER;
1369 if (pstate & PS_EDIT) {
1370 n_err(_("`mbox' can only be used in a system mailbox\n")); /* TODO */
1371 goto jleave;
1374 for (ip = msgvec; *ip != 0; ++ip) {
1375 setdot(message + *ip - 1);
1376 dot->m_flag |= MTOUCH | MBOX;
1377 dot->m_flag &= ~MPRESERVE;
1378 pstate |= PS_DID_PRINT_DOT;
1380 jleave:
1381 NYD_LEAVE;
1382 return 0;
1385 FL int
1386 c_folders(void *v)
1388 char dirname[PATH_MAX], *name, **argv = v;
1389 char const *cmd;
1390 int rv = 1;
1391 NYD_ENTER;
1393 if (*argv) {
1394 name = expand(*argv);
1395 if (name == NULL)
1396 goto jleave;
1397 } else if (!getfold(dirname, sizeof dirname)) {
1398 n_err(_("No value set for \"folder\"\n"));
1399 goto jleave;
1400 } else
1401 name = dirname;
1403 if (which_protocol(name) == PROTO_IMAP) {
1404 #ifdef HAVE_IMAP
1405 imap_folders(name, *argv == NULL);
1406 #else
1407 rv = c_cmdnotsupp(NULL);
1408 #endif
1409 } else {
1410 if ((cmd = ok_vlook(LISTER)) == NULL)
1411 cmd = XLISTER;
1412 run_command(cmd, 0, -1, -1, name, NULL, NULL);
1414 jleave:
1415 NYD_LEAVE;
1416 return rv;
1419 /* s-it-mode */