Implement `xcall' stack-avoidance optimization
[s-mailx.git] / cmd-headers.c
blob6d02ecfc8c6d0caa614168d2779f695464cf114a
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Header display, search, etc., related user commands.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
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 cmd_headers
38 #ifndef HAVE_AMALGAMATION
39 # include "nail.h"
40 #endif
42 static int _screen;
44 /* ... And place the extracted date in `date' */
45 static void _parse_from_(struct message *mp, char date[FROM_DATEBUF]);
47 /* Print out the header of a specific message
48 * __hprf: handle *headline*
49 * __subject: return -1 if Subject: yet seen, otherwise smalloc()d Subject:
50 * __putindent: print out the indenting in threaded display */
51 static void _print_head(size_t yetprinted, size_t msgno, FILE *f,
52 bool_t threaded);
53 static void __hprf(size_t yetprinted, char const *fmt, size_t msgno,
54 FILE *f, bool_t threaded, char const *attrlist);
55 static char * __subject(struct message *mp, bool_t threaded,
56 size_t yetprinted);
57 static int __putindent(FILE *fp, struct message *mp, int maxwidth);
59 static int _dispc(struct message *mp, char const *a);
61 /* Shared `z' implementation */
62 static int a_cmd_scroll(char const *arg, bool_t onlynew);
64 /* Shared `headers' implementation */
65 static int _headers(int msgspec);
67 static void
68 _parse_from_(struct message *mp, char date[FROM_DATEBUF]) /* TODO line pool */
70 FILE *ibuf;
71 int hlen;
72 char *hline = NULL;
73 size_t hsize = 0;
74 NYD_ENTER;
76 if ((ibuf = setinput(&mb, mp, NEED_HEADER)) != NULL &&
77 (hlen = readline_restart(ibuf, &hline, &hsize, 0)) > 0)
78 extract_date_from_from_(hline, hlen, date);
79 if (hline != NULL)
80 free(hline);
81 NYD_LEAVE;
84 static void
85 _print_head(size_t yetprinted, size_t msgno, FILE *f, bool_t threaded)
87 enum {attrlen = 14};
88 char attrlist[attrlen +1], *cp;
89 char const *fmt;
90 NYD_ENTER;
92 if ((cp = ok_vlook(attrlist)) != NULL) {
93 if (strlen(cp) == attrlen) {
94 memcpy(attrlist, cp, attrlen +1);
95 goto jattrok;
97 n_err(_("*attrlist* is not of the correct length, using builtin\n"));
100 if (ok_blook(bsdcompat) || ok_blook(bsdflags)) {
101 char const bsdattr[attrlen +1] = "NU *HMFAT+-$~";
102 memcpy(attrlist, bsdattr, sizeof bsdattr);
103 } else if (ok_blook(SYSV3)) {
104 char const bsdattr[attrlen +1] = "NU *HMFAT+-$~";
105 memcpy(attrlist, bsdattr, sizeof bsdattr);
106 n_OBSOLETE(_("*SYSV3*: please use *bsdcompat* or *bsdflags*, "
107 "or set *attrlist*"));
108 } else {
109 char const pattr[attrlen +1] = "NUROSPMFAT+-$~";
110 memcpy(attrlist, pattr, sizeof pattr);
113 jattrok:
114 if ((fmt = ok_vlook(headline)) == NULL) {
115 fmt = ((ok_blook(bsdcompat) || ok_blook(bsdheadline))
116 ? "%>%a%m %-20f %16d %4l/%-5o %i%-S"
117 : "%>%a%m %-18f %-16d %4l/%-5o %i%-s");
120 __hprf(yetprinted, fmt, msgno, f, threaded, attrlist);
121 NYD_LEAVE;
124 static void
125 __hprf(size_t yetprinted, char const *fmt, size_t msgno, FILE *f,
126 bool_t threaded, char const *attrlist)
128 char buf[16], datebuf[FROM_DATEBUF], cbuf[8], *cp, *subjline;
129 char const *datefmt, *date, *name, *fp n_COLOUR( COMMA *colo_tag );
130 int i, n, s, wleft, subjlen;
131 struct message *mp;
132 time_t datet;
133 n_COLOUR( struct n_colour_pen *cpen_new COMMA *cpen_cur COMMA *cpen_bas; )
134 enum {
135 _NONE = 0,
136 _ISDOT = 1<<0,
137 _ISADDR = 1<<1,
138 _ISTO = 1<<2,
139 _IFMT = 1<<3,
140 _LOOP_MASK = (1<<4) - 1,
141 _SFMT = 1<<4, /* It is 'S' */
142 /* For the simple byte-based counts in wleft and n we sometimes need
143 * adjustments to compensate for additional bytes of UTF-8 sequences */
144 _PUTCB_UTF8_SHIFT = 5,
145 _PUTCB_UTF8_MASK = 3<<5
146 } flags = _NONE;
147 NYD_ENTER;
148 n_UNUSED(buf);
150 if ((mp = message + msgno - 1) == dot)
151 flags = _ISDOT;
152 datet = mp->m_time;
153 date = NULL;
154 n_COLOUR( colo_tag = NULL; )
156 datefmt = ok_vlook(datefield);
157 jredo:
158 if (datefmt != NULL) {
159 fp = hfield1("date", mp);/* TODO use m_date field! */
160 if (fp == NULL) {
161 datefmt = NULL;
162 goto jredo;
164 datet = rfctime(fp);
165 date = fakedate(datet);
166 fp = ok_vlook(datefield_markout_older);
167 i = (*datefmt != '\0');
168 if (fp != NULL)
169 i |= (*fp != '\0') ? 2 | 4 : 2; /* XXX no magics */
171 /* May we strftime(3)? */
172 if (i & (1 | 4))
173 memcpy(&time_current.tc_local, localtime(&datet),
174 sizeof time_current.tc_local);
176 if ((i & 2) && (datet > time_current.tc_time + DATE_SECSDAY ||
177 #define _6M ((DATE_DAYSYEAR / 2) * DATE_SECSDAY)
178 (datet + _6M < time_current.tc_time))) {
179 #undef _6M
180 if ((datefmt = (i & 4) ? fp : NULL) == NULL) {
181 memset(datebuf, ' ', FROM_DATEBUF); /* xxx ur */
182 memcpy(datebuf + 4, date + 4, 7);
183 datebuf[4 + 7] = ' ';
184 memcpy(datebuf + 4 + 7 + 1, date + 20, 4);
185 datebuf[4 + 7 + 1 + 4] = '\0';
186 date = datebuf;
188 n_COLOUR( colo_tag = n_COLOUR_TAG_SUM_OLDER; )
189 } else if ((i & 1) == 0)
190 datefmt = NULL;
191 } else if (datet == (time_t)0 && !(mp->m_flag & MNOFROM)) {
192 /* TODO eliminate this path, query the FROM_ date in setptr(),
193 * TODO all other codepaths do so by themselves ALREADY ?????
194 * TODO assert(mp->m_time != 0);, then
195 * TODO ALSO changes behaviour of datefield_markout_older */
196 _parse_from_(mp, datebuf);
197 date = datebuf;
198 } else
199 date = fakedate(datet);
201 flags |= _ISADDR;
202 name = name1(mp, 0);
203 if (name != NULL && ok_blook(showto) && is_myname(skin(name))) {
204 if ((cp = hfield1("to", mp)) != NULL) {
205 name = cp;
206 flags |= _ISTO;
209 if (name == NULL) {
210 name = n_empty;
211 flags &= ~_ISADDR;
213 if (flags & _ISADDR)
214 name = ok_blook(showname) ? realname(name) : prstr(skin(name));
216 subjline = NULL;
218 /* Detect the width of the non-format characters in *headline*;
219 * like that we can simply use putc() in the next loop, since we have
220 * already calculated their column widths (TODO it's sick) */
221 wleft = subjlen = n_scrnwidth;
223 for (fp = fmt; *fp != '\0'; ++fp) {
224 if (*fp == '%') {
225 if (*++fp == '-')
226 ++fp;
227 else if (*fp == '+')
228 ++fp;
229 if (digitchar(*fp)) {
230 n = 0;
232 n = 10*n + *fp - '0';
233 while (++fp, digitchar(*fp));
234 subjlen -= n;
236 if (*fp == 'i')
237 flags |= _IFMT;
239 if (*fp == '\0')
240 break;
241 } else {
242 #ifdef HAVE_WCWIDTH
243 if (n_mb_cur_max > 1) {
244 wchar_t wc;
245 if ((s = mbtowc(&wc, fp, n_mb_cur_max)) == -1)
246 n = s = 1;
247 else if ((n = wcwidth(wc)) == -1)
248 n = 1;
249 } else
250 #endif
251 n = s = 1;
252 subjlen -= n;
253 wleft -= n;
254 while (--s > 0)
255 ++fp;
259 /* Walk *headline*, producing output TODO not (really) MB safe */
260 #ifdef HAVE_COLOUR
261 if (flags & _ISDOT)
262 colo_tag = n_COLOUR_TAG_SUM_DOT;
263 cpen_bas = n_colour_pen_create(n_COLOUR_ID_SUM_HEADER, colo_tag);
264 n_colour_pen_put(cpen_new = cpen_cur = cpen_bas, f);
265 #endif
267 for (fp = fmt; *fp != '\0'; ++fp) {
268 char c;
270 if ((c = *fp & 0xFF) != '%') {
271 #ifdef HAVE_COLOUR
272 if ((cpen_new = cpen_bas) != cpen_cur)
273 n_colour_pen_put(cpen_cur = cpen_new, f);
274 #endif
275 putc(c, f);
276 continue;
279 flags &= _LOOP_MASK;
280 n = 0;
281 s = 1;
282 if ((c = *++fp) == '-') {
283 s = -1;
284 ++fp;
285 } else if (c == '+')
286 ++fp;
287 if (digitchar(*fp)) {
289 n = 10*n + *fp - '0';
290 while (++fp, digitchar(*fp));
293 if ((c = *fp & 0xFF) == '\0')
294 break;
295 n *= s;
297 cbuf[1] = '\0';
298 switch (c) {
299 case '%':
300 goto jputcb;
301 case '>':
302 case '<':
303 if (flags & _ISDOT) {
304 n_COLOUR( cpen_new = n_colour_pen_create(n_COLOUR_ID_SUM_DOTMARK,
305 colo_tag); );
306 if (n_psonce & n_PSO_UNICODE) {
307 if (c == '>')
308 /* 25B8;BLACK RIGHT-POINTING SMALL TRIANGLE: â–¸ */
309 cbuf[1] = (char)0x96, cbuf[2] = (char)0xB8;
310 else
311 /* 25C2;BLACK LEFT-POINTING SMALL TRIANGLE: â—‚ */
312 cbuf[1] = (char)0x97, cbuf[2] = (char)0x82;
313 c = (char)0xE2;
314 cbuf[3] = '\0';
315 flags |= 2 << _PUTCB_UTF8_SHIFT;
317 } else
318 c = ' ';
319 goto jputcb;
320 case '$':
321 #ifdef HAVE_SPAM
322 if (n == 0)
323 n = 5;
324 if (UICMP(32, n_ABS(n), >, wleft))
325 n = (n < 0) ? -wleft : wleft;
326 snprintf(buf, sizeof buf, "%u.%02u",
327 (mp->m_spamscore >> 8), (mp->m_spamscore & 0xFF));
328 n = fprintf(f, "%*s", n, buf);
329 wleft = (n >= 0) ? wleft - n : 0;
330 break;
331 #else
332 c = '?';
333 goto jputcb;
334 #endif
335 case 'a':
336 c = _dispc(mp, attrlist);
337 jputcb:
338 #ifdef HAVE_COLOUR
339 if (cpen_new == cpen_cur)
340 cpen_new = cpen_bas;
341 if (cpen_new != cpen_cur)
342 n_colour_pen_put(cpen_cur = cpen_new, f);
343 #endif
344 if (UICMP(32, n_ABS(n), >, wleft))
345 n = (n < 0) ? -wleft : wleft;
346 cbuf[0] = c;
347 n = fprintf(f, "%*s", n, cbuf);
348 if (n >= 0) {
349 wleft -= n;
350 if ((n = (flags & _PUTCB_UTF8_MASK)) != 0) {
351 n >>= _PUTCB_UTF8_SHIFT;
352 wleft += n;
354 } else {
355 wleft = 0; /* TODO I/O error.. ? break? */
357 #ifdef HAVE_COLOUR
358 if ((cpen_new = cpen_bas) != cpen_cur)
359 n_colour_pen_put(cpen_cur = cpen_new, f);
360 #endif
361 break;
362 case 'd':
363 if (datefmt != NULL) {
364 i = strftime(datebuf, sizeof datebuf, datefmt,
365 &time_current.tc_local);
366 if (i != 0)
367 date = datebuf;
368 else
369 n_err(_("Ignored date format, it excesses the target "
370 "buffer (%lu bytes)\n"), (ul_i)sizeof(datebuf));
371 datefmt = NULL;
373 if (n == 0)
374 n = 16;
375 if (UICMP(32, n_ABS(n), >, wleft))
376 n = (n < 0) ? -wleft : wleft;
377 n = fprintf(f, "%*.*s", n, n_ABS(n), date);
378 wleft = (n >= 0) ? wleft - n : 0;
379 break;
380 case 'e':
381 if (n == 0)
382 n = 2;
383 if (UICMP(32, n_ABS(n), >, wleft))
384 n = (n < 0) ? -wleft : wleft;
385 n = fprintf(f, "%*u", n, (threaded == 1 ? mp->m_level : 0));
386 wleft = (n >= 0) ? wleft - n : 0;
387 break;
388 case 'f':
389 if (n == 0) {
390 n = 18;
391 if (s < 0)
392 n = -n;
394 i = n_ABS(n);
395 if (i > wleft) {
396 i = wleft;
397 n = (n < 0) ? -wleft : wleft;
399 if (flags & _ISTO) /* XXX tr()! */
400 i -= 3;
401 n = fprintf(f, "%s%s", ((flags & _ISTO) ? "To " : n_empty),
402 colalign(name, i, n, &wleft));
403 if (n < 0)
404 wleft = 0;
405 else if (flags & _ISTO)
406 wleft -= 3;
407 break;
408 case 'i':
409 if (threaded) {
410 #ifdef HAVE_COLOUR
411 cpen_new = n_colour_pen_create(n_COLOUR_ID_SUM_THREAD, colo_tag);
412 if (cpen_new != cpen_cur)
413 n_colour_pen_put(cpen_cur = cpen_new, f);
414 #endif
415 n = __putindent(f, mp, n_MIN(wleft, (int)n_scrnwidth - 60));
416 wleft = (n >= 0) ? wleft - n : 0;
417 #ifdef HAVE_COLOUR
418 if ((cpen_new = cpen_bas) != cpen_cur)
419 n_colour_pen_put(cpen_cur = cpen_new, f);
420 #endif
422 break;
423 case 'l':
424 if (n == 0)
425 n = 4;
426 if (UICMP(32, n_ABS(n), >, wleft))
427 n = (n < 0) ? -wleft : wleft;
428 if (mp->m_xlines) {
429 n = fprintf(f, "%*ld", n, mp->m_xlines);
430 wleft = (n >= 0) ? wleft - n : 0;
431 } else {
432 n = n_ABS(n);
433 wleft -= n;
434 while (n-- != 0)
435 putc(' ', f);
437 break;
438 case 'm':
439 if (n == 0) {
440 n = 3;
441 if (threaded)
442 for (i = msgCount; i > 999; i /= 10)
443 ++n;
445 if (UICMP(32, n_ABS(n), >, wleft))
446 n = (n < 0) ? -wleft : wleft;
447 n = fprintf(f, "%*lu", n, (ul_i)msgno);
448 wleft = (n >= 0) ? wleft - n : 0;
449 break;
450 case 'o':
451 if (n == 0)
452 n = -5;
453 if (UICMP(32, n_ABS(n), >, wleft))
454 n = (n < 0) ? -wleft : wleft;
455 n = fprintf(f, "%*lu", n, (ul_i)mp->m_xsize);
456 wleft = (n >= 0) ? wleft - n : 0;
457 break;
458 case 'S':
459 flags |= _SFMT;
460 /*FALLTHRU*/
461 case 's':
462 if (n == 0)
463 n = subjlen - 2;
464 if (n > 0 && s < 0)
465 n = -n;
466 if (subjlen > wleft)
467 subjlen = wleft;
468 if (UICMP(32, n_ABS(n), >, subjlen))
469 n = (n < 0) ? -subjlen : subjlen;
470 if (flags & _SFMT)
471 n -= (n < 0) ? -2 : 2;
472 if (n == 0)
473 break;
474 if (subjline == NULL)
475 subjline = __subject(mp, (threaded && (flags & _IFMT)), yetprinted);
476 if (subjline == (char*)-1) {
477 n = fprintf(f, "%*s", n, n_empty);
478 wleft = (n >= 0) ? wleft - n : 0;
479 } else {
480 n = fprintf(f, ((flags & _SFMT) ? "\"%s\"" : "%s"),
481 colalign(subjline, n_ABS(n), n, &wleft));
482 if (n < 0)
483 wleft = 0;
485 break;
486 case 'T': { /* Message recipient flags */
487 /* We never can reuse "name" since it's the full name */
488 struct name const *np = lextract(hfield1("to", mp), GTO | GSKIN);
489 c = ' ';
490 i = 0;
491 j_A_redo:
492 for (; np != NULL; np = np->n_flink) {
493 switch (is_mlist(np->n_name, FAL0)) {
494 case MLIST_SUBSCRIBED: c = 'S'; goto jputcb;
495 case MLIST_KNOWN: c = 'L'; goto jputcb;
496 case MLIST_OTHER:
497 default: break;
500 if (i != 0)
501 goto jputcb;
502 ++i;
503 np = lextract(hfield1("cc", mp), GCC | GSKIN);
504 goto j_A_redo;
506 case 't':
507 if (n == 0) {
508 n = 3;
509 if (threaded)
510 for (i = msgCount; i > 999; i /= 10)
511 ++n;
513 if (UICMP(32, n_ABS(n), >, wleft))
514 n = (n < 0) ? -wleft : wleft;
515 n = fprintf(f, "%*lu",
516 n, (threaded ? (ul_i)mp->m_threadpos : (ul_i)msgno));
517 wleft = (n >= 0) ? wleft - n : 0;
518 break;
519 default:
520 if (n_poption & n_PO_D_V)
521 n_err(_("Unkown *headline* format: %%%c\n"), c);
522 c = '?';
523 goto jputcb;
526 if (wleft <= 0)
527 break;
530 #ifdef HAVE_COLOUR
531 n_colour_reset(f);
532 #endif
533 putc('\n', f);
535 if (subjline != NULL && subjline != (char*)-1)
536 free(subjline);
537 NYD_LEAVE;
540 static char *
541 __subject(struct message *mp, bool_t threaded, size_t yetprinted)
543 struct str in, out;
544 char *rv, *ms;
545 NYD_ENTER;
547 rv = (char*)-1;
549 if ((ms = hfield1("subject", mp)) == NULL)
550 goto jleave;
552 in.l = strlen(in.s = ms);
553 mime_fromhdr(&in, &out, TD_ICONV | TD_ISPR);
554 rv = ms = out.s;
556 if (!threaded || mp->m_level == 0)
557 goto jleave;
559 /* In a display thread - check whether this message uses the same
560 * Subject: as it's parent or elder neighbour, suppress printing it if
561 * this is the case. To extend this a bit, ignore any leading Re: or
562 * Fwd: plus follow-up WS. Ignore invisible messages along the way */
563 ms = n_UNCONST(subject_re_trim(n_UNCONST(ms)));
565 for (; (mp = prev_in_thread(mp)) != NULL && yetprinted-- > 0;) {
566 char *os;
568 if (visible(mp) && (os = hfield1("subject", mp)) != NULL) {
569 struct str oout;
570 int x;
572 in.l = strlen(in.s = os);
573 mime_fromhdr(&in, &oout, TD_ICONV | TD_ISPR);
574 x = asccasecmp(ms, subject_re_trim(oout.s));
575 free(oout.s);
577 if (!x) {
578 free(out.s);
579 rv = (char*)-1;
581 break;
584 jleave:
585 NYD_LEAVE;
586 return rv;
589 static int
590 __putindent(FILE *fp, struct message *mp, int maxwidth)/* XXX no magic consts */
592 struct message *mq;
593 int *us, indlvl, indw, i, important = MNEW | MFLAGGED;
594 char *cs;
595 NYD_ENTER;
597 if (mp->m_level == 0 || maxwidth == 0) {
598 indw = 0;
599 goto jleave;
602 cs = ac_alloc(mp->m_level);
603 us = ac_alloc(mp->m_level * sizeof *us);
605 i = mp->m_level - 1;
606 if (mp->m_younger && UICMP(32, i + 1, ==, mp->m_younger->m_level)) {
607 if (mp->m_parent && mp->m_parent->m_flag & important)
608 us[i] = mp->m_flag & important ? 0x2523 : 0x2520;
609 else
610 us[i] = mp->m_flag & important ? 0x251D : 0x251C;
611 cs[i] = '+';
612 } else {
613 if (mp->m_parent && mp->m_parent->m_flag & important)
614 us[i] = mp->m_flag & important ? 0x2517 : 0x2516;
615 else
616 us[i] = mp->m_flag & important ? 0x2515 : 0x2514;
617 cs[i] = '\\';
620 mq = mp->m_parent;
621 for (i = mp->m_level - 2; i >= 0; --i) {
622 if (mq) {
623 if (UICMP(32, i, >, mq->m_level - 1)) {
624 us[i] = cs[i] = ' ';
625 continue;
627 if (mq->m_younger) {
628 if (mq->m_parent && (mq->m_parent->m_flag & important))
629 us[i] = 0x2503;
630 else
631 us[i] = 0x2502;
632 cs[i] = '|';
633 } else
634 us[i] = cs[i] = ' ';
635 mq = mq->m_parent;
636 } else
637 us[i] = cs[i] = ' ';
640 --maxwidth;
641 for (indlvl = indw = 0; (ui8_t)indlvl < mp->m_level && indw < maxwidth;
642 ++indlvl) {
643 if (indw < maxwidth - 1)
644 indw += (int)putuc(us[indlvl], cs[indlvl] & 0xFF, fp);
645 else
646 indw += (int)putuc(0x21B8, '^', fp);
648 indw += putuc(0x25B8, '>', fp);
650 ac_free(us);
651 ac_free(cs);
652 jleave:
653 NYD_LEAVE;
654 return indw;
657 static int
658 _dispc(struct message *mp, char const *a)
660 int i = ' ';
661 NYD_ENTER;
663 if ((mp->m_flag & (MREAD | MNEW)) == MREAD)
664 i = a[3];
665 if ((mp->m_flag & (MREAD | MNEW)) == (MREAD | MNEW))
666 i = a[2];
667 if (mp->m_flag & MANSWERED)
668 i = a[8];
669 if (mp->m_flag & MDRAFTED)
670 i = a[9];
671 if ((mp->m_flag & (MREAD | MNEW)) == MNEW)
672 i = a[0];
673 if (!(mp->m_flag & (MREAD | MNEW)))
674 i = a[1];
675 if (mp->m_flag & MSPAM)
676 i = a[12];
677 if (mp->m_flag & MSPAMUNSURE)
678 i = a[13];
679 if (mp->m_flag & MSAVED)
680 i = a[4];
681 if (mp->m_flag & MPRESERVE)
682 i = a[5];
683 if (mp->m_flag & (MBOX | MBOXED))
684 i = a[6];
685 if (mp->m_flag & MFLAGGED)
686 i = a[7];
687 if (mb.mb_threaded == 1 && mp->m_collapsed > 0)
688 i = a[11];
689 if (mb.mb_threaded == 1 && mp->m_collapsed < 0)
690 i = a[10];
691 NYD_LEAVE;
692 return i;
695 static int
696 a_cmd_scroll(char const *arg, bool_t onlynew){
697 siz_t l;
698 bool_t isabs;
699 int msgspec, size, maxs;
700 NYD2_ENTER;
702 /* TODO scroll problem: we do not know whether + and $ have already reached
703 * TODO the last screen in threaded mode */
704 msgspec = onlynew ? -1 : 0;
705 size = (int)/*TODO*/n_screensize();
706 if((maxs = msgCount / size) > 0 && msgCount % size == 0)
707 --maxs;
709 if(arg == NULL)
710 arg = n_empty;
711 switch(*arg){
712 case '\0':
713 ++_screen;
714 goto jfwd;
715 case '^':
716 if(arg[1] != '\0')
717 goto jerr;
718 if(_screen == 0)
719 goto jerrbwd;
720 _screen = 0;
721 break;
722 case '$':
723 if(arg[1] != '\0')
724 goto jerr;
725 if(_screen == maxs)
726 goto jerrfwd;
727 _screen = maxs;
728 break;
729 case '+':
730 if(arg[1] == '\0')
731 ++_screen;
732 else{
733 isabs = FAL0;
735 ++arg;
736 if(0){
737 case '1': case '2': case '3': case '4': case '5':
738 case '6': case '7': case '8': case '9': case '0':
739 isabs = TRU1;
741 if((n_idec_siz_cp(&l, arg, 0, NULL
742 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
743 ) != n_IDEC_STATE_CONSUMED)
744 goto jerr;
745 if(l > maxs - (isabs ? 0 : _screen))
746 goto jerrfwd;
747 _screen = isabs ? (int)l : _screen + l;
749 jfwd:
750 if(_screen > maxs){
751 jerrfwd:
752 _screen = maxs;
753 fprintf(n_stdout, _("On last screenful of messages\n"));
755 break;
757 case '-':
758 if(arg[1] == '\0')
759 --_screen;
760 else{
761 if((n_idec_siz_cp(&l, ++arg, 0, NULL
762 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
763 ) != n_IDEC_STATE_CONSUMED)
764 goto jerr;
765 if(l > _screen)
766 goto jerrbwd;
767 _screen -= l;
769 if(_screen < 0){
770 jerrbwd:
771 _screen = 0;
772 fprintf(n_stdout, _("On first screenful of messages\n"));
774 if(msgspec == -1)
775 msgspec = -2;
776 break;
777 default:
778 jerr:
779 n_err(_("Unrecognized scrolling command: %s\n"), arg);
780 size = 1;
781 goto jleave;
784 size = _headers(msgspec);
785 jleave:
786 NYD2_LEAVE;
787 return size;
790 static int
791 _headers(int msgspec) /* TODO rework v15 */
793 struct n_sigman sm;
794 bool_t volatile isrelax;
795 ui32_t volatile flag;
796 int g, k, mesg, size;
797 int volatile lastg = 1;
798 struct message *mp, *mq, *lastmq = NULL;
799 enum mflag fl = MNEW | MFLAGGED;
800 NYD_ENTER;
802 time_current_update(&time_current, FAL0);
804 flag = 0;
805 isrelax = FAL0;
806 n_SIGMAN_ENTER_SWITCH(&sm, n_SIGMAN_ALL) {
807 case 0:
808 break;
809 default:
810 goto jleave;
813 #ifdef HAVE_COLOUR
814 if (n_psonce & n_PSO_INTERACTIVE)
815 n_colour_env_create(n_COLOUR_CTX_SUM, FAL0);
816 #endif
818 size = (int)/*TODO*/n_screensize();
819 if (_screen < 0)
820 _screen = 0;
821 #if 0 /* FIXME original code path */
822 k = _screen * size;
823 #else
824 if (msgspec <= 0)
825 k = _screen * size;
826 else
827 k = msgspec;
828 #endif
829 if (k >= msgCount)
830 k = msgCount - size;
831 if (k < 0)
832 k = 0;
834 if (mb.mb_threaded == 0) {
835 g = 0;
836 mq = message;
837 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
838 if (visible(mp)) {
839 if (g % size == 0)
840 mq = mp;
841 if (mp->m_flag & fl) {
842 lastg = g;
843 lastmq = mq;
845 if ((msgspec > 0 && PTRCMP(mp, ==, message + msgspec - 1)) ||
846 (msgspec == 0 && g == k) ||
847 (msgspec == -2 && g == k + size && lastmq) ||
848 (msgspec < 0 && g >= k && (mp->m_flag & fl) != 0))
849 break;
850 g++;
852 if (lastmq && (msgspec == -2 ||
853 (msgspec == -1 && PTRCMP(mp, ==, message + msgCount)))) {
854 g = lastg;
855 mq = lastmq;
857 _screen = g / size;
859 mp = mq;
860 mesg = (int)PTR2SIZE(mp - message);
861 if (PTRCMP(dot, !=, message + msgspec - 1)) { /* TODO really?? */
862 for (mq = mp; PTRCMP(mq, <, message + msgCount); ++mq)
863 if (visible(mq)) {
864 setdot(mq);
865 break;
869 srelax_hold();
870 isrelax = TRU1;
871 for (; PTRCMP(mp, <, message + msgCount); ++mp) {
872 ++mesg;
873 if (!visible(mp))
874 continue;
875 if (UICMP(32, flag++, >=, size))
876 break;
877 _print_head(0, mesg, n_stdout, 0);
878 srelax();
880 srelax_rele();
881 isrelax = FAL0;
882 } else { /* threaded */
883 g = 0;
884 mq = threadroot;
885 for (mp = threadroot; mp; mp = next_in_thread(mp))
886 if (visible(mp) &&
887 (mp->m_collapsed <= 0 ||
888 PTRCMP(mp, ==, message + msgspec - 1))) {
889 if (g % size == 0)
890 mq = mp;
891 if (mp->m_flag & fl) {
892 lastg = g;
893 lastmq = mq;
895 if ((msgspec > 0 && PTRCMP(mp, ==, message + msgspec - 1)) ||
896 (msgspec == 0 && g == k) ||
897 (msgspec == -2 && g == k + size && lastmq) ||
898 (msgspec < 0 && g >= k && (mp->m_flag & fl) != 0))
899 break;
900 g++;
902 if (lastmq && (msgspec == -2 ||
903 (msgspec == -1 && PTRCMP(mp, ==, message + msgCount)))) {
904 g = lastg;
905 mq = lastmq;
907 _screen = g / size;
908 mp = mq;
909 if (PTRCMP(dot, !=, message + msgspec - 1)) { /* TODO really?? */
910 for (mq = mp; mq; mq = next_in_thread(mq))
911 if (visible(mq) && mq->m_collapsed <= 0) {
912 setdot(mq);
913 break;
917 srelax_hold();
918 isrelax = TRU1;
919 while (mp) {
920 if (visible(mp) &&
921 (mp->m_collapsed <= 0 ||
922 PTRCMP(mp, ==, message + msgspec - 1))) {
923 if (UICMP(32, flag++, >=, size))
924 break;
925 _print_head(flag - 1, PTR2SIZE(mp - message + 1), n_stdout,
926 mb.mb_threaded);
927 srelax();
929 mp = next_in_thread(mp);
931 srelax_rele();
932 isrelax = FAL0;
935 if (!flag) {
936 fprintf(n_stdout, _("No more mail.\n"));
937 if (n_pstate & (n_PS_ROBOT | n_PS_HOOK_MASK))
938 flag = !flag;
941 n_sigman_cleanup_ping(&sm);
942 jleave:
943 if (isrelax)
944 srelax_rele();
945 n_COLOUR( n_colour_env_gut((sm.sm_signo != SIGPIPE) ? n_stdout : NULL); )
946 NYD_LEAVE;
947 n_sigman_leave(&sm, n_SIGMAN_VIPSIGS_NTTYOUT);
948 return !flag;
951 FL int
952 c_headers(void *v)
954 int rv;
955 NYD_ENTER;
957 rv = print_header_group((int*)v);
958 NYD_LEAVE;
959 return rv;
962 FL int
963 print_header_group(int *vector)
965 int rv;
966 NYD_ENTER;
968 assert(vector != NULL && vector != (void*)-1);
969 rv = _headers(vector[0]);
970 NYD_LEAVE;
971 return rv;
974 FL int
975 c_scroll(void *v)
977 int rv;
978 NYD_ENTER;
980 rv = a_cmd_scroll(*(char const**)v, FAL0);
981 NYD_LEAVE;
982 return rv;
985 FL int
986 c_Scroll(void *v)
988 int rv;
989 NYD_ENTER;
991 rv = a_cmd_scroll(*(char const**)v, TRU1);
992 NYD_LEAVE;
993 return rv;
996 FL int
997 c_dotmove(void *v)
999 char const *args;
1000 int msgvec[2], rv;
1001 NYD_ENTER;
1003 if (*(args = v) == '\0' || args[1] != '\0') {
1004 jerr:
1005 n_err(_("Synopsis: dotmove: up <-> or down <+> by one message\n"));
1006 rv = 1;
1007 } else switch (args[0]) {
1008 case '-':
1009 case '+':
1010 if (msgCount == 0) {
1011 fprintf(n_stdout, _("At EOF\n"));
1012 rv = 0;
1013 } else if (getmsglist(n_UNCONST(/*TODO*/ args), msgvec, 0) > 0) {
1014 setdot(message + msgvec[0] - 1);
1015 msgvec[1] = 0;
1016 rv = c_headers(msgvec);
1017 } else
1018 rv = 1;
1019 break;
1020 default:
1021 goto jerr;
1023 NYD_LEAVE;
1024 return rv;
1027 FL int
1028 c_from(void *v)
1030 struct n_sigman sm;
1031 int *msgvec = v, *ip, n;
1032 char *cp;
1033 FILE * volatile obuf;
1034 bool_t volatile isrelax;
1035 NYD_ENTER;
1037 time_current_update(&time_current, FAL0);
1039 obuf = n_stdout;
1040 isrelax = FAL0;
1041 n_SIGMAN_ENTER_SWITCH(&sm, n_SIGMAN_ALL) {
1042 case 0:
1043 break;
1044 default:
1045 goto jleave;
1048 if (n_psonce & n_PSO_INTERACTIVE) {
1049 if ((cp = ok_vlook(crt)) != NULL) {
1050 uiz_t ib;
1052 for (n = 0, ip = msgvec; *ip != 0; ++ip)
1053 ++n;
1055 if(*cp == '\0')
1056 ib = n_screensize();
1057 else
1058 n_idec_uiz_cp(&ib, cp, 0, NULL);
1059 if (UICMP(z, n, >, ib) && (obuf = n_pager_open()) == NULL)
1060 obuf = n_stdout;
1062 n_COLOUR( n_colour_env_create(n_COLOUR_CTX_SUM, obuf != n_stdout); )
1065 /* Update dot before display so that the dotmark etc. are correct */
1066 for (ip = msgvec; *ip != 0; ++ip)
1068 if (--ip >= msgvec)
1069 setdot(message + *ip - 1);
1071 srelax_hold();
1072 isrelax = TRU1;
1073 for (n = 0, ip = msgvec; *ip != 0; ++ip) { /* TODO join into _print_head() */
1074 _print_head((size_t)n++, (size_t)*ip, obuf, mb.mb_threaded);
1075 srelax();
1077 srelax_rele();
1078 isrelax = FAL0;
1080 n_sigman_cleanup_ping(&sm);
1081 jleave:
1082 if (isrelax)
1083 srelax_rele();
1084 n_COLOUR( n_colour_env_gut((sm.sm_signo != SIGPIPE) ? obuf : NULL); )
1085 if (obuf != n_stdout)
1086 n_pager_close(obuf);
1087 NYD_LEAVE;
1088 n_sigman_leave(&sm, n_SIGMAN_VIPSIGS_NTTYOUT);
1089 return 0;
1092 FL void
1093 print_headers(size_t bottom, size_t topx, bool_t only_marked)
1095 struct n_sigman sm;
1096 bool_t volatile isrelax;
1097 size_t printed;
1098 NYD_ENTER;
1100 time_current_update(&time_current, FAL0);
1102 isrelax = FAL0;
1103 n_SIGMAN_ENTER_SWITCH(&sm, n_SIGMAN_ALL) {
1104 case 0:
1105 break;
1106 default:
1107 goto jleave;
1110 #ifdef HAVE_COLOUR
1111 if (n_psonce & n_PSO_INTERACTIVE)
1112 n_colour_env_create(n_COLOUR_CTX_SUM, FAL0);
1113 #endif
1115 srelax_hold();
1116 isrelax = TRU1;
1117 for (printed = 0; bottom <= topx; ++bottom) {
1118 struct message *mp = message + bottom - 1;
1119 if (only_marked) {
1120 if (!(mp->m_flag & MMARK))
1121 continue;
1122 } else if (!visible(mp))
1123 continue;
1124 _print_head(printed++, bottom, n_stdout, FAL0);
1125 srelax();
1127 srelax_rele();
1128 isrelax = FAL0;
1130 n_sigman_cleanup_ping(&sm);
1131 jleave:
1132 if (isrelax)
1133 srelax_rele();
1134 n_COLOUR( n_colour_env_gut((sm.sm_signo != SIGPIPE) ? n_stdout : NULL); )
1135 NYD_LEAVE;
1136 n_sigman_leave(&sm, n_SIGMAN_VIPSIGS_NTTYOUT);
1139 /* s-it-mode */