head.c:a_head_idna_apply(): FIX IDNA result length calculation!
[s-mailx.git] / cmd_headers.c
blobb4341ad56c89bc82c1ae97100ed9dbcafcca6905
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 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 %3l/%-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 = 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 (mb_cur_max > 1) {
244 wchar_t wc;
245 if ((s = mbtowc(&wc, fp, 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 (options & OPT_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, 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, 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 (options & OPT_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 = (char*)-1, *ms;
545 NYD_ENTER;
547 if ((ms = hfield1("subject", mp)) == NULL)
548 goto jleave;
550 in.l = strlen(in.s = ms);
551 mime_fromhdr(&in, &out, TD_ICONV | TD_ISPR);
552 rv = ms = out.s;
554 if (!threaded || mp->m_level == 0)
555 goto jleave;
557 /* In a display thread - check whether this message uses the same
558 * Subject: as it's parent or elder neighbour, suppress printing it if
559 * this is the case. To extend this a bit, ignore any leading Re: or
560 * Fwd: plus follow-up WS. Ignore invisible messages along the way */
561 ms = subject_re_trim(ms);
563 for (; (mp = prev_in_thread(mp)) != NULL && yetprinted-- > 0;) {
564 char *os;
566 if (visible(mp) && (os = hfield1("subject", mp)) != NULL) {
567 struct str oout;
568 int x;
570 in.l = strlen(in.s = os);
571 mime_fromhdr(&in, &oout, TD_ICONV | TD_ISPR);
572 x = asccasecmp(ms, subject_re_trim(oout.s));
573 free(oout.s);
575 if (!x) {
576 free(out.s);
577 rv = (char*)-1;
579 break;
582 jleave:
583 NYD_LEAVE;
584 return rv;
587 static int
588 __putindent(FILE *fp, struct message *mp, int maxwidth)/* XXX no magic consts */
590 struct message *mq;
591 int *us, indlvl, indw, i, important = MNEW | MFLAGGED;
592 char *cs;
593 NYD_ENTER;
595 if (mp->m_level == 0 || maxwidth == 0) {
596 indw = 0;
597 goto jleave;
600 cs = ac_alloc(mp->m_level);
601 us = ac_alloc(mp->m_level * sizeof *us);
603 i = mp->m_level - 1;
604 if (mp->m_younger && UICMP(32, i + 1, ==, mp->m_younger->m_level)) {
605 if (mp->m_parent && mp->m_parent->m_flag & important)
606 us[i] = mp->m_flag & important ? 0x2523 : 0x2520;
607 else
608 us[i] = mp->m_flag & important ? 0x251D : 0x251C;
609 cs[i] = '+';
610 } else {
611 if (mp->m_parent && mp->m_parent->m_flag & important)
612 us[i] = mp->m_flag & important ? 0x2517 : 0x2516;
613 else
614 us[i] = mp->m_flag & important ? 0x2515 : 0x2514;
615 cs[i] = '\\';
618 mq = mp->m_parent;
619 for (i = mp->m_level - 2; i >= 0; --i) {
620 if (mq) {
621 if (UICMP(32, i, >, mq->m_level - 1)) {
622 us[i] = cs[i] = ' ';
623 continue;
625 if (mq->m_younger) {
626 if (mq->m_parent && (mq->m_parent->m_flag & important))
627 us[i] = 0x2503;
628 else
629 us[i] = 0x2502;
630 cs[i] = '|';
631 } else
632 us[i] = cs[i] = ' ';
633 mq = mq->m_parent;
634 } else
635 us[i] = cs[i] = ' ';
638 --maxwidth;
639 for (indlvl = indw = 0; (ui8_t)indlvl < mp->m_level && indw < maxwidth;
640 ++indlvl) {
641 if (indw < maxwidth - 1)
642 indw += (int)putuc(us[indlvl], cs[indlvl] & 0xFF, fp);
643 else
644 indw += (int)putuc(0x21B8, '^', fp);
646 indw += putuc(0x25B8, '>', fp);
648 ac_free(us);
649 ac_free(cs);
650 jleave:
651 NYD_LEAVE;
652 return indw;
655 static int
656 _dispc(struct message *mp, char const *a)
658 int i = ' ';
659 NYD_ENTER;
661 if ((mp->m_flag & (MREAD | MNEW)) == MREAD)
662 i = a[3];
663 if ((mp->m_flag & (MREAD | MNEW)) == (MREAD | MNEW))
664 i = a[2];
665 if (mp->m_flag & MANSWERED)
666 i = a[8];
667 if (mp->m_flag & MDRAFTED)
668 i = a[9];
669 if ((mp->m_flag & (MREAD | MNEW)) == MNEW)
670 i = a[0];
671 if (!(mp->m_flag & (MREAD | MNEW)))
672 i = a[1];
673 if (mp->m_flag & MSPAM)
674 i = a[12];
675 if (mp->m_flag & MSPAMUNSURE)
676 i = a[13];
677 if (mp->m_flag & MSAVED)
678 i = a[4];
679 if (mp->m_flag & MPRESERVE)
680 i = a[5];
681 if (mp->m_flag & (MBOX | MBOXED))
682 i = a[6];
683 if (mp->m_flag & MFLAGGED)
684 i = a[7];
685 if (mb.mb_threaded == 1 && mp->m_collapsed > 0)
686 i = a[11];
687 if (mb.mb_threaded == 1 && mp->m_collapsed < 0)
688 i = a[10];
689 NYD_LEAVE;
690 return i;
693 static int
694 a_cmd_scroll(char const *arg, bool_t onlynew){
695 long l;
696 char *eptr;
697 bool_t isabs;
698 int msgspec, size, maxs;
699 NYD2_ENTER;
701 /* TODO scroll problem: we do not know whether + and $ have already reached
702 * TODO the last screen in threaded mode */
703 msgspec = onlynew ? -1 : 0;
704 size = screensize();
705 if((maxs = msgCount / size) > 0 && msgCount % size == 0)
706 --maxs;
708 switch(*arg){
709 case '\0':
710 ++_screen;
711 goto jfwd;
712 case '^':
713 if(arg[1] != '\0')
714 goto jerr;
715 if(_screen == 0)
716 goto jerrbwd;
717 _screen = 0;
718 break;
719 case '$':
720 if(arg[1] != '\0')
721 goto jerr;
722 if(_screen == maxs)
723 goto jerrfwd;
724 _screen = maxs;
725 break;
726 case '+':
727 if(arg[1] == '\0')
728 ++_screen;
729 else{
730 isabs = FAL0;
732 ++arg;
733 if(0){
734 case '1': case '2': case '3': case '4': case '5':
735 case '6': case '7': case '8': case '9': case '0':
736 isabs = TRU1;
738 l = strtol(arg, &eptr, 10);
739 if(*eptr != '\0')
740 goto jerr;
741 if(l > maxs - (isabs ? 0 : _screen))
742 goto jerrfwd;
743 _screen = isabs ? (int)l : _screen + l;
745 jfwd:
746 if(_screen > maxs){
747 jerrfwd:
748 _screen = maxs;
749 printf(_("On last screenful of messages\n"));
751 break;
753 case '-':
754 if(arg[1] == '\0')
755 --_screen;
756 else{
757 ++arg;
758 l = strtol(arg, &eptr, 10);
759 if(*eptr != '\0')
760 goto jerr;
761 if(l > _screen)
762 goto jerrbwd;
763 _screen -= l;
765 if(_screen < 0){
766 jerrbwd:
767 _screen = 0;
768 printf(_("On first screenful of messages\n"));
770 if(msgspec == -1)
771 msgspec = -2;
772 break;
773 default:
774 jerr:
775 n_err(_("Unrecognized scrolling command: %s\n"), arg);
776 size = 1;
777 goto jleave;
780 size = _headers(msgspec);
781 jleave:
782 NYD2_LEAVE;
783 return size;
786 static int
787 _headers(int msgspec) /* TODO rework v15 */
789 struct n_sigman sm;
790 bool_t volatile isrelax;
791 ui32_t volatile flag;
792 int g, k, mesg, size;
793 int volatile lastg = 1;
794 struct message *mp, *mq, *lastmq = NULL;
795 enum mflag fl = MNEW | MFLAGGED;
796 NYD_ENTER;
798 time_current_update(&time_current, FAL0);
800 flag = 0;
801 isrelax = FAL0;
802 n_SIGMAN_ENTER_SWITCH(&sm, n_SIGMAN_ALL) {
803 case 0:
804 break;
805 default:
806 goto jleave;
809 #ifdef HAVE_COLOUR
810 if (options & OPT_INTERACTIVE)
811 n_colour_env_create(n_COLOUR_CTX_SUM, FAL0);
812 #endif
814 size = screensize();
815 if (_screen < 0)
816 _screen = 0;
817 #if 0 /* FIXME original code path */
818 k = _screen * size;
819 #else
820 if (msgspec <= 0)
821 k = _screen * size;
822 else
823 k = msgspec;
824 #endif
825 if (k >= msgCount)
826 k = msgCount - size;
827 if (k < 0)
828 k = 0;
830 if (mb.mb_threaded == 0) {
831 g = 0;
832 mq = message;
833 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
834 if (visible(mp)) {
835 if (g % size == 0)
836 mq = mp;
837 if (mp->m_flag & fl) {
838 lastg = g;
839 lastmq = mq;
841 if ((msgspec > 0 && PTRCMP(mp, ==, message + msgspec - 1)) ||
842 (msgspec == 0 && g == k) ||
843 (msgspec == -2 && g == k + size && lastmq) ||
844 (msgspec < 0 && g >= k && (mp->m_flag & fl) != 0))
845 break;
846 g++;
848 if (lastmq && (msgspec == -2 ||
849 (msgspec == -1 && PTRCMP(mp, ==, message + msgCount)))) {
850 g = lastg;
851 mq = lastmq;
853 _screen = g / size;
855 mp = mq;
856 mesg = (int)PTR2SIZE(mp - message);
857 if (PTRCMP(dot, !=, message + msgspec - 1)) { /* TODO really?? */
858 for (mq = mp; PTRCMP(mq, <, message + msgCount); ++mq)
859 if (visible(mq)) {
860 setdot(mq);
861 break;
865 srelax_hold();
866 isrelax = TRU1;
867 for (; PTRCMP(mp, <, message + msgCount); ++mp) {
868 ++mesg;
869 if (!visible(mp))
870 continue;
871 if (UICMP(32, flag++, >=, size))
872 break;
873 _print_head(0, mesg, stdout, 0);
874 srelax();
876 srelax_rele();
877 isrelax = FAL0;
878 } else { /* threaded */
879 g = 0;
880 mq = threadroot;
881 for (mp = threadroot; mp; mp = next_in_thread(mp))
882 if (visible(mp) &&
883 (mp->m_collapsed <= 0 ||
884 PTRCMP(mp, ==, message + msgspec - 1))) {
885 if (g % size == 0)
886 mq = mp;
887 if (mp->m_flag & fl) {
888 lastg = g;
889 lastmq = mq;
891 if ((msgspec > 0 && PTRCMP(mp, ==, message + msgspec - 1)) ||
892 (msgspec == 0 && g == k) ||
893 (msgspec == -2 && g == k + size && lastmq) ||
894 (msgspec < 0 && g >= k && (mp->m_flag & fl) != 0))
895 break;
896 g++;
898 if (lastmq && (msgspec == -2 ||
899 (msgspec == -1 && PTRCMP(mp, ==, message + msgCount)))) {
900 g = lastg;
901 mq = lastmq;
903 _screen = g / size;
904 mp = mq;
905 if (PTRCMP(dot, !=, message + msgspec - 1)) { /* TODO really?? */
906 for (mq = mp; mq; mq = next_in_thread(mq))
907 if (visible(mq) && mq->m_collapsed <= 0) {
908 setdot(mq);
909 break;
913 srelax_hold();
914 isrelax = TRU1;
915 while (mp) {
916 if (visible(mp) &&
917 (mp->m_collapsed <= 0 ||
918 PTRCMP(mp, ==, message + msgspec - 1))) {
919 if (UICMP(32, flag++, >=, size))
920 break;
921 _print_head(flag - 1, PTR2SIZE(mp - message + 1), stdout,
922 mb.mb_threaded);
923 srelax();
925 mp = next_in_thread(mp);
927 srelax_rele();
928 isrelax = FAL0;
931 if (!flag) {
932 printf(_("No more mail.\n"));
933 if (pstate & (PS_HOOK_MASK | PS_ROBOT))
934 flag = !flag;
937 n_sigman_cleanup_ping(&sm);
938 jleave:
939 if (isrelax)
940 srelax_rele();
941 n_COLOUR( n_colour_env_gut((sm.sm_signo != SIGPIPE) ? stdout : NULL); )
942 NYD_LEAVE;
943 n_sigman_leave(&sm, n_SIGMAN_VIPSIGS_NTTYOUT);
944 return !flag;
947 FL int
948 c_headers(void *v)
950 int rv;
951 NYD_ENTER;
953 rv = print_header_group((int*)v);
954 NYD_LEAVE;
955 return rv;
958 FL int
959 print_header_group(int *vector)
961 int rv;
962 NYD_ENTER;
964 assert(vector != NULL && vector != (void*)-1);
965 rv = _headers(vector[0]);
966 NYD_LEAVE;
967 return rv;
970 FL int
971 c_scroll(void *v)
973 int rv;
974 NYD_ENTER;
976 rv = a_cmd_scroll(v, FAL0);
977 NYD_LEAVE;
978 return rv;
981 FL int
982 c_Scroll(void *v)
984 int rv;
985 NYD_ENTER;
987 rv = a_cmd_scroll(v, TRU1);
988 NYD_LEAVE;
989 return rv;
992 FL int
993 c_dotmove(void *v)
995 char const *args;
996 int msgvec[2], rv;
997 NYD_ENTER;
999 if (*(args = v) == '\0' || args[1] != '\0') {
1000 jerr:
1001 n_err(_("Synopsis: dotmove: up <-> or down <+> by one message\n"));
1002 rv = 1;
1003 } else switch (args[0]) {
1004 case '-':
1005 case '+':
1006 if (msgCount == 0) {
1007 printf(_("At EOF\n"));
1008 rv = 0;
1009 } else if (getmsglist(n_UNCONST(/*TODO*/ args), msgvec, 0) > 0) {
1010 setdot(message + msgvec[0] - 1);
1011 msgvec[1] = 0;
1012 rv = c_headers(msgvec);
1013 } else
1014 rv = 1;
1015 break;
1016 default:
1017 goto jerr;
1019 NYD_LEAVE;
1020 return rv;
1023 FL int
1024 c_from(void *v)
1026 struct n_sigman sm;
1027 int *msgvec = v, *ip, n;
1028 char *cp;
1029 FILE * volatile obuf;
1030 bool_t volatile isrelax;
1031 NYD_ENTER;
1033 time_current_update(&time_current, FAL0);
1035 obuf = stdout;
1036 isrelax = FAL0;
1037 n_SIGMAN_ENTER_SWITCH(&sm, n_SIGMAN_ALL) {
1038 case 0:
1039 break;
1040 default:
1041 goto jleave;
1044 if (options & OPT_INTERACTIVE) {
1045 if ((cp = ok_vlook(crt)) != NULL) {
1046 for (n = 0, ip = msgvec; *ip != 0; ++ip)
1047 ++n;
1048 if (UICMP(z, n, >, (*cp == '\0'
1049 ? (size_t)screensize() : strtoul(cp, NULL, 0)) + 3) &&
1050 (obuf = n_pager_open()) == NULL)
1051 obuf = stdout;
1053 n_COLOUR( n_colour_env_create(n_COLOUR_CTX_SUM, obuf != stdout); )
1056 /* Update dot before display so that the dotmark etc. are correct */
1057 for (ip = msgvec; *ip != 0; ++ip)
1059 if (--ip >= msgvec)
1060 setdot(message + *ip - 1);
1062 srelax_hold();
1063 isrelax = TRU1;
1064 for (n = 0, ip = msgvec; *ip != 0; ++ip) { /* TODO join into _print_head() */
1065 _print_head((size_t)n++, (size_t)*ip, obuf, mb.mb_threaded);
1066 srelax();
1068 srelax_rele();
1069 isrelax = FAL0;
1071 n_sigman_cleanup_ping(&sm);
1072 jleave:
1073 if (isrelax)
1074 srelax_rele();
1075 n_COLOUR( n_colour_env_gut((sm.sm_signo != SIGPIPE) ? obuf : NULL); )
1076 if (obuf != stdout)
1077 n_pager_close(obuf);
1078 NYD_LEAVE;
1079 n_sigman_leave(&sm, n_SIGMAN_VIPSIGS_NTTYOUT);
1080 return 0;
1083 FL void
1084 print_headers(size_t bottom, size_t topx, bool_t only_marked)
1086 struct n_sigman sm;
1087 bool_t volatile isrelax;
1088 size_t printed;
1089 NYD_ENTER;
1091 time_current_update(&time_current, FAL0);
1093 isrelax = FAL0;
1094 n_SIGMAN_ENTER_SWITCH(&sm, n_SIGMAN_ALL) {
1095 case 0:
1096 break;
1097 default:
1098 goto jleave;
1101 #ifdef HAVE_COLOUR
1102 if (options & OPT_INTERACTIVE)
1103 n_colour_env_create(n_COLOUR_CTX_SUM, FAL0);
1104 #endif
1106 srelax_hold();
1107 isrelax = TRU1;
1108 for (printed = 0; bottom <= topx; ++bottom) {
1109 struct message *mp = message + bottom - 1;
1110 if (only_marked) {
1111 if (!(mp->m_flag & MMARK))
1112 continue;
1113 } else if (!visible(mp))
1114 continue;
1115 _print_head(printed++, bottom, stdout, FAL0);
1116 srelax();
1118 srelax_rele();
1119 isrelax = FAL0;
1121 n_sigman_cleanup_ping(&sm);
1122 jleave:
1123 if (isrelax)
1124 srelax_rele();
1125 n_COLOUR( n_colour_env_gut((sm.sm_signo != SIGPIPE) ? stdout : NULL); )
1126 NYD_LEAVE;
1127 n_sigman_leave(&sm, n_SIGMAN_VIPSIGS_NTTYOUT);
1130 /* s-it-mode */