~<: add "- [HERE-delimiter]" for pasting etc. (Ralph Corderoy)
[s-mailx.git] / cmd_headers.c
blob0925de9c6638609e3a037e958c590782e42b3f94
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 switch(*arg){
710 case '\0':
711 ++_screen;
712 goto jfwd;
713 case '^':
714 if(arg[1] != '\0')
715 goto jerr;
716 if(_screen == 0)
717 goto jerrbwd;
718 _screen = 0;
719 break;
720 case '$':
721 if(arg[1] != '\0')
722 goto jerr;
723 if(_screen == maxs)
724 goto jerrfwd;
725 _screen = maxs;
726 break;
727 case '+':
728 if(arg[1] == '\0')
729 ++_screen;
730 else{
731 isabs = FAL0;
733 ++arg;
734 if(0){
735 case '1': case '2': case '3': case '4': case '5':
736 case '6': case '7': case '8': case '9': case '0':
737 isabs = TRU1;
739 if((n_idec_siz_cp(&l, arg, 0, NULL
740 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
741 ) != n_IDEC_STATE_CONSUMED)
742 goto jerr;
743 if(l > maxs - (isabs ? 0 : _screen))
744 goto jerrfwd;
745 _screen = isabs ? (int)l : _screen + l;
747 jfwd:
748 if(_screen > maxs){
749 jerrfwd:
750 _screen = maxs;
751 fprintf(n_stdout, _("On last screenful of messages\n"));
753 break;
755 case '-':
756 if(arg[1] == '\0')
757 --_screen;
758 else{
759 if((n_idec_siz_cp(&l, ++arg, 0, NULL
760 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
761 ) != n_IDEC_STATE_CONSUMED)
762 goto jerr;
763 if(l > _screen)
764 goto jerrbwd;
765 _screen -= l;
767 if(_screen < 0){
768 jerrbwd:
769 _screen = 0;
770 fprintf(n_stdout, _("On first screenful of messages\n"));
772 if(msgspec == -1)
773 msgspec = -2;
774 break;
775 default:
776 jerr:
777 n_err(_("Unrecognized scrolling command: %s\n"), arg);
778 size = 1;
779 goto jleave;
782 size = _headers(msgspec);
783 jleave:
784 NYD2_LEAVE;
785 return size;
788 static int
789 _headers(int msgspec) /* TODO rework v15 */
791 struct n_sigman sm;
792 bool_t volatile isrelax;
793 ui32_t volatile flag;
794 int g, k, mesg, size;
795 int volatile lastg = 1;
796 struct message *mp, *mq, *lastmq = NULL;
797 enum mflag fl = MNEW | MFLAGGED;
798 NYD_ENTER;
800 time_current_update(&time_current, FAL0);
802 flag = 0;
803 isrelax = FAL0;
804 n_SIGMAN_ENTER_SWITCH(&sm, n_SIGMAN_ALL) {
805 case 0:
806 break;
807 default:
808 goto jleave;
811 #ifdef HAVE_COLOUR
812 if (n_psonce & n_PSO_INTERACTIVE)
813 n_colour_env_create(n_COLOUR_CTX_SUM, FAL0);
814 #endif
816 size = (int)/*TODO*/n_screensize();
817 if (_screen < 0)
818 _screen = 0;
819 #if 0 /* FIXME original code path */
820 k = _screen * size;
821 #else
822 if (msgspec <= 0)
823 k = _screen * size;
824 else
825 k = msgspec;
826 #endif
827 if (k >= msgCount)
828 k = msgCount - size;
829 if (k < 0)
830 k = 0;
832 if (mb.mb_threaded == 0) {
833 g = 0;
834 mq = message;
835 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
836 if (visible(mp)) {
837 if (g % size == 0)
838 mq = mp;
839 if (mp->m_flag & fl) {
840 lastg = g;
841 lastmq = mq;
843 if ((msgspec > 0 && PTRCMP(mp, ==, message + msgspec - 1)) ||
844 (msgspec == 0 && g == k) ||
845 (msgspec == -2 && g == k + size && lastmq) ||
846 (msgspec < 0 && g >= k && (mp->m_flag & fl) != 0))
847 break;
848 g++;
850 if (lastmq && (msgspec == -2 ||
851 (msgspec == -1 && PTRCMP(mp, ==, message + msgCount)))) {
852 g = lastg;
853 mq = lastmq;
855 _screen = g / size;
857 mp = mq;
858 mesg = (int)PTR2SIZE(mp - message);
859 if (PTRCMP(dot, !=, message + msgspec - 1)) { /* TODO really?? */
860 for (mq = mp; PTRCMP(mq, <, message + msgCount); ++mq)
861 if (visible(mq)) {
862 setdot(mq);
863 break;
867 srelax_hold();
868 isrelax = TRU1;
869 for (; PTRCMP(mp, <, message + msgCount); ++mp) {
870 ++mesg;
871 if (!visible(mp))
872 continue;
873 if (UICMP(32, flag++, >=, size))
874 break;
875 _print_head(0, mesg, n_stdout, 0);
876 srelax();
878 srelax_rele();
879 isrelax = FAL0;
880 } else { /* threaded */
881 g = 0;
882 mq = threadroot;
883 for (mp = threadroot; mp; mp = next_in_thread(mp))
884 if (visible(mp) &&
885 (mp->m_collapsed <= 0 ||
886 PTRCMP(mp, ==, message + msgspec - 1))) {
887 if (g % size == 0)
888 mq = mp;
889 if (mp->m_flag & fl) {
890 lastg = g;
891 lastmq = mq;
893 if ((msgspec > 0 && PTRCMP(mp, ==, message + msgspec - 1)) ||
894 (msgspec == 0 && g == k) ||
895 (msgspec == -2 && g == k + size && lastmq) ||
896 (msgspec < 0 && g >= k && (mp->m_flag & fl) != 0))
897 break;
898 g++;
900 if (lastmq && (msgspec == -2 ||
901 (msgspec == -1 && PTRCMP(mp, ==, message + msgCount)))) {
902 g = lastg;
903 mq = lastmq;
905 _screen = g / size;
906 mp = mq;
907 if (PTRCMP(dot, !=, message + msgspec - 1)) { /* TODO really?? */
908 for (mq = mp; mq; mq = next_in_thread(mq))
909 if (visible(mq) && mq->m_collapsed <= 0) {
910 setdot(mq);
911 break;
915 srelax_hold();
916 isrelax = TRU1;
917 while (mp) {
918 if (visible(mp) &&
919 (mp->m_collapsed <= 0 ||
920 PTRCMP(mp, ==, message + msgspec - 1))) {
921 if (UICMP(32, flag++, >=, size))
922 break;
923 _print_head(flag - 1, PTR2SIZE(mp - message + 1), n_stdout,
924 mb.mb_threaded);
925 srelax();
927 mp = next_in_thread(mp);
929 srelax_rele();
930 isrelax = FAL0;
933 if (!flag) {
934 fprintf(n_stdout, _("No more mail.\n"));
935 if (n_pstate & (n_PS_ROBOT | n_PS_HOOK_MASK))
936 flag = !flag;
939 n_sigman_cleanup_ping(&sm);
940 jleave:
941 if (isrelax)
942 srelax_rele();
943 n_COLOUR( n_colour_env_gut((sm.sm_signo != SIGPIPE) ? n_stdout : NULL); )
944 NYD_LEAVE;
945 n_sigman_leave(&sm, n_SIGMAN_VIPSIGS_NTTYOUT);
946 return !flag;
949 FL int
950 c_headers(void *v)
952 int rv;
953 NYD_ENTER;
955 rv = print_header_group((int*)v);
956 NYD_LEAVE;
957 return rv;
960 FL int
961 print_header_group(int *vector)
963 int rv;
964 NYD_ENTER;
966 assert(vector != NULL && vector != (void*)-1);
967 rv = _headers(vector[0]);
968 NYD_LEAVE;
969 return rv;
972 FL int
973 c_scroll(void *v)
975 int rv;
976 NYD_ENTER;
978 rv = a_cmd_scroll(v, FAL0);
979 NYD_LEAVE;
980 return rv;
983 FL int
984 c_Scroll(void *v)
986 int rv;
987 NYD_ENTER;
989 rv = a_cmd_scroll(v, TRU1);
990 NYD_LEAVE;
991 return rv;
994 FL int
995 c_dotmove(void *v)
997 char const *args;
998 int msgvec[2], rv;
999 NYD_ENTER;
1001 if (*(args = v) == '\0' || args[1] != '\0') {
1002 jerr:
1003 n_err(_("Synopsis: dotmove: up <-> or down <+> by one message\n"));
1004 rv = 1;
1005 } else switch (args[0]) {
1006 case '-':
1007 case '+':
1008 if (msgCount == 0) {
1009 fprintf(n_stdout, _("At EOF\n"));
1010 rv = 0;
1011 } else if (getmsglist(n_UNCONST(/*TODO*/ args), msgvec, 0) > 0) {
1012 setdot(message + msgvec[0] - 1);
1013 msgvec[1] = 0;
1014 rv = c_headers(msgvec);
1015 } else
1016 rv = 1;
1017 break;
1018 default:
1019 goto jerr;
1021 NYD_LEAVE;
1022 return rv;
1025 FL int
1026 c_from(void *v)
1028 struct n_sigman sm;
1029 int *msgvec = v, *ip, n;
1030 char *cp;
1031 FILE * volatile obuf;
1032 bool_t volatile isrelax;
1033 NYD_ENTER;
1035 time_current_update(&time_current, FAL0);
1037 obuf = n_stdout;
1038 isrelax = FAL0;
1039 n_SIGMAN_ENTER_SWITCH(&sm, n_SIGMAN_ALL) {
1040 case 0:
1041 break;
1042 default:
1043 goto jleave;
1046 if (n_psonce & n_PSO_INTERACTIVE) {
1047 if ((cp = ok_vlook(crt)) != NULL) {
1048 uiz_t ib;
1050 for (n = 0, ip = msgvec; *ip != 0; ++ip)
1051 ++n;
1053 if(*cp == '\0')
1054 ib = n_screensize();
1055 else
1056 n_idec_uiz_cp(&ib, cp, 0, NULL);
1057 if (UICMP(z, n, >, ib) && (obuf = n_pager_open()) == NULL)
1058 obuf = n_stdout;
1060 n_COLOUR( n_colour_env_create(n_COLOUR_CTX_SUM, obuf != n_stdout); )
1063 /* Update dot before display so that the dotmark etc. are correct */
1064 for (ip = msgvec; *ip != 0; ++ip)
1066 if (--ip >= msgvec)
1067 setdot(message + *ip - 1);
1069 srelax_hold();
1070 isrelax = TRU1;
1071 for (n = 0, ip = msgvec; *ip != 0; ++ip) { /* TODO join into _print_head() */
1072 _print_head((size_t)n++, (size_t)*ip, obuf, mb.mb_threaded);
1073 srelax();
1075 srelax_rele();
1076 isrelax = FAL0;
1078 n_sigman_cleanup_ping(&sm);
1079 jleave:
1080 if (isrelax)
1081 srelax_rele();
1082 n_COLOUR( n_colour_env_gut((sm.sm_signo != SIGPIPE) ? obuf : NULL); )
1083 if (obuf != n_stdout)
1084 n_pager_close(obuf);
1085 NYD_LEAVE;
1086 n_sigman_leave(&sm, n_SIGMAN_VIPSIGS_NTTYOUT);
1087 return 0;
1090 FL void
1091 print_headers(size_t bottom, size_t topx, bool_t only_marked)
1093 struct n_sigman sm;
1094 bool_t volatile isrelax;
1095 size_t printed;
1096 NYD_ENTER;
1098 time_current_update(&time_current, FAL0);
1100 isrelax = FAL0;
1101 n_SIGMAN_ENTER_SWITCH(&sm, n_SIGMAN_ALL) {
1102 case 0:
1103 break;
1104 default:
1105 goto jleave;
1108 #ifdef HAVE_COLOUR
1109 if (n_psonce & n_PSO_INTERACTIVE)
1110 n_colour_env_create(n_COLOUR_CTX_SUM, FAL0);
1111 #endif
1113 srelax_hold();
1114 isrelax = TRU1;
1115 for (printed = 0; bottom <= topx; ++bottom) {
1116 struct message *mp = message + bottom - 1;
1117 if (only_marked) {
1118 if (!(mp->m_flag & MMARK))
1119 continue;
1120 } else if (!visible(mp))
1121 continue;
1122 _print_head(printed++, bottom, n_stdout, FAL0);
1123 srelax();
1125 srelax_rele();
1126 isrelax = FAL0;
1128 n_sigman_cleanup_ping(&sm);
1129 jleave:
1130 if (isrelax)
1131 srelax_rele();
1132 n_COLOUR( n_colour_env_gut((sm.sm_signo != SIGPIPE) ? n_stdout : NULL); )
1133 NYD_LEAVE;
1134 n_sigman_leave(&sm, n_SIGMAN_VIPSIGS_NTTYOUT);
1137 /* s-it-mode */