C****Y BSD signal handling! Try improve ^C stability..
[s-mailx.git] / cmd-headers.c
blob06c3b25352d544ec3a183ca2181574b445d9839b
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[n_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[n_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[n_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 + n_DATE_SECSDAY ||
177 #define _6M ((n_DATE_DAYSYEAR / 2) * n_DATE_SECSDAY)
178 (datet + _6M < time_current.tc_time))) {
179 #undef _6M
180 if ((datefmt = (i & 4) ? fp : NULL) == NULL) {
181 memset(datebuf, ' ', n_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(n_COLOUR_IS_ACTIVE()){
262 if(flags & _ISDOT)
263 colo_tag = n_COLOUR_TAG_SUM_DOT;
264 cpen_bas = n_colour_pen_create(n_COLOUR_ID_SUM_HEADER, colo_tag);
265 n_colour_pen_put(cpen_new = cpen_cur = cpen_bas);
266 }else
267 cpen_new = cpen_bas = cpen_cur = NULL;
268 #endif
270 for (fp = fmt; *fp != '\0'; ++fp) {
271 char c;
273 if ((c = *fp & 0xFF) != '%') {
274 n_COLOUR(
275 if(n_COLOUR_IS_ACTIVE() && (cpen_new = cpen_bas) != cpen_cur)
276 n_colour_pen_put(cpen_cur = cpen_new);
278 putc(c, f);
279 continue;
282 flags &= _LOOP_MASK;
283 n = 0;
284 s = 1;
285 if ((c = *++fp) == '-') {
286 s = -1;
287 ++fp;
288 } else if (c == '+')
289 ++fp;
290 if (digitchar(*fp)) {
292 n = 10*n + *fp - '0';
293 while (++fp, digitchar(*fp));
296 if ((c = *fp & 0xFF) == '\0')
297 break;
298 n *= s;
300 cbuf[1] = '\0';
301 switch (c) {
302 case '%':
303 goto jputcb;
304 case '>':
305 case '<':
306 if (flags & _ISDOT) {
307 n_COLOUR(
308 if(n_COLOUR_IS_ACTIVE())
309 cpen_new = n_colour_pen_create(n_COLOUR_ID_SUM_DOTMARK,
310 colo_tag);
312 if (n_psonce & n_PSO_UNICODE) {
313 if (c == '>')
314 /* 25B8;BLACK RIGHT-POINTING SMALL TRIANGLE: â–¸ */
315 cbuf[1] = (char)0x96, cbuf[2] = (char)0xB8;
316 else
317 /* 25C2;BLACK LEFT-POINTING SMALL TRIANGLE: â—‚ */
318 cbuf[1] = (char)0x97, cbuf[2] = (char)0x82;
319 c = (char)0xE2;
320 cbuf[3] = '\0';
321 flags |= 2 << _PUTCB_UTF8_SHIFT;
323 } else
324 c = ' ';
325 goto jputcb;
326 case '$':
327 #ifdef HAVE_SPAM
328 if (n == 0)
329 n = 5;
330 if (UICMP(32, n_ABS(n), >, wleft))
331 n = (n < 0) ? -wleft : wleft;
332 snprintf(buf, sizeof buf, "%u.%02u",
333 (mp->m_spamscore >> 8), (mp->m_spamscore & 0xFF));
334 n = fprintf(f, "%*s", n, buf);
335 wleft = (n >= 0) ? wleft - n : 0;
336 break;
337 #else
338 c = '?';
339 goto jputcb;
340 #endif
341 case 'a':
342 c = _dispc(mp, attrlist);
343 jputcb:
344 #ifdef HAVE_COLOUR
345 if(n_COLOUR_IS_ACTIVE()){
346 if(cpen_new == cpen_cur)
347 cpen_new = cpen_bas;
348 if(cpen_new != cpen_cur)
349 n_colour_pen_put(cpen_cur = cpen_new);
351 #endif
352 if (UICMP(32, n_ABS(n), >, wleft))
353 n = (n < 0) ? -wleft : wleft;
354 cbuf[0] = c;
355 n = fprintf(f, "%*s", n, cbuf);
356 if (n >= 0) {
357 wleft -= n;
358 if ((n = (flags & _PUTCB_UTF8_MASK)) != 0) {
359 n >>= _PUTCB_UTF8_SHIFT;
360 wleft += n;
362 } else {
363 wleft = 0; /* TODO I/O error.. ? break? */
365 #ifdef HAVE_COLOUR
366 if(n_COLOUR_IS_ACTIVE() && (cpen_new = cpen_bas) != cpen_cur)
367 n_colour_pen_put(cpen_cur = cpen_new);
368 #endif
369 break;
370 case 'd':
371 if (datefmt != NULL) {
372 i = strftime(datebuf, sizeof datebuf, datefmt,
373 &time_current.tc_local);
374 if (i != 0)
375 date = datebuf;
376 else
377 n_err(_("Ignored date format, it excesses the target "
378 "buffer (%lu bytes)\n"), (ul_i)sizeof(datebuf));
379 datefmt = NULL;
381 if (n == 0)
382 n = 16;
383 if (UICMP(32, n_ABS(n), >, wleft))
384 n = (n < 0) ? -wleft : wleft;
385 n = fprintf(f, "%*.*s", n, n_ABS(n), date);
386 wleft = (n >= 0) ? wleft - n : 0;
387 break;
388 case 'e':
389 if (n == 0)
390 n = 2;
391 if (UICMP(32, n_ABS(n), >, wleft))
392 n = (n < 0) ? -wleft : wleft;
393 n = fprintf(f, "%*u", n, (threaded == 1 ? mp->m_level : 0));
394 wleft = (n >= 0) ? wleft - n : 0;
395 break;
396 case 'f':
397 if (n == 0) {
398 n = 18;
399 if (s < 0)
400 n = -n;
402 i = n_ABS(n);
403 if (i > wleft) {
404 i = wleft;
405 n = (n < 0) ? -wleft : wleft;
407 if (flags & _ISTO) /* XXX tr()! */
408 i -= 3;
409 n = fprintf(f, "%s%s", ((flags & _ISTO) ? "To " : n_empty),
410 colalign(name, i, n, &wleft));
411 if (n < 0)
412 wleft = 0;
413 else if (flags & _ISTO)
414 wleft -= 3;
415 break;
416 case 'i':
417 if (threaded) {
418 #ifdef HAVE_COLOUR
419 if(n_COLOUR_IS_ACTIVE()){
420 cpen_new = n_colour_pen_create(n_COLOUR_ID_SUM_THREAD, colo_tag);
421 if(cpen_new != cpen_cur)
422 n_colour_pen_put(cpen_cur = cpen_new);
424 #endif
425 n = __putindent(f, mp, n_MIN(wleft, (int)n_scrnwidth - 60));
426 wleft = (n >= 0) ? wleft - n : 0;
427 #ifdef HAVE_COLOUR
428 if(n_COLOUR_IS_ACTIVE() && (cpen_new = cpen_bas) != cpen_cur)
429 n_colour_pen_put(cpen_cur = cpen_new);
430 #endif
432 break;
433 case 'l':
434 if (n == 0)
435 n = 4;
436 if (UICMP(32, n_ABS(n), >, wleft))
437 n = (n < 0) ? -wleft : wleft;
438 if (mp->m_xlines) {
439 n = fprintf(f, "%*ld", n, mp->m_xlines);
440 wleft = (n >= 0) ? wleft - n : 0;
441 } else {
442 n = n_ABS(n);
443 wleft -= n;
444 while (n-- != 0)
445 putc(' ', f);
447 break;
448 case 'm':
449 if (n == 0) {
450 n = 3;
451 if (threaded)
452 for (i = msgCount; i > 999; i /= 10)
453 ++n;
455 if (UICMP(32, n_ABS(n), >, wleft))
456 n = (n < 0) ? -wleft : wleft;
457 n = fprintf(f, "%*lu", n, (ul_i)msgno);
458 wleft = (n >= 0) ? wleft - n : 0;
459 break;
460 case 'o':
461 if (n == 0)
462 n = -5;
463 if (UICMP(32, n_ABS(n), >, wleft))
464 n = (n < 0) ? -wleft : wleft;
465 n = fprintf(f, "%*lu", n, (ul_i)mp->m_xsize);
466 wleft = (n >= 0) ? wleft - n : 0;
467 break;
468 case 'S':
469 flags |= _SFMT;
470 /*FALLTHRU*/
471 case 's':
472 if (n == 0)
473 n = subjlen - 2;
474 if (n > 0 && s < 0)
475 n = -n;
476 if (subjlen > wleft)
477 subjlen = wleft;
478 if (UICMP(32, n_ABS(n), >, subjlen))
479 n = (n < 0) ? -subjlen : subjlen;
480 if (flags & _SFMT)
481 n -= (n < 0) ? -2 : 2;
482 if (n == 0)
483 break;
484 if (subjline == NULL)
485 subjline = __subject(mp, (threaded && (flags & _IFMT)), yetprinted);
486 if (subjline == (char*)-1) {
487 n = fprintf(f, "%*s", n, n_empty);
488 wleft = (n >= 0) ? wleft - n : 0;
489 } else {
490 n = fprintf(f, ((flags & _SFMT) ? "\"%s\"" : "%s"),
491 colalign(subjline, n_ABS(n), n, &wleft));
492 if (n < 0)
493 wleft = 0;
495 break;
496 case 'T': { /* Message recipient flags */
497 /* We never can reuse "name" since it's the full name */
498 struct name const *np = lextract(hfield1("to", mp), GTO | GSKIN);
499 c = ' ';
500 i = 0;
501 j_A_redo:
502 for (; np != NULL; np = np->n_flink) {
503 switch (is_mlist(np->n_name, FAL0)) {
504 case MLIST_SUBSCRIBED: c = 'S'; goto jputcb;
505 case MLIST_KNOWN: c = 'L'; goto jputcb;
506 case MLIST_OTHER:
507 default: break;
510 if (i != 0)
511 goto jputcb;
512 ++i;
513 np = lextract(hfield1("cc", mp), GCC | GSKIN);
514 goto j_A_redo;
516 case 't':
517 if (n == 0) {
518 n = 3;
519 if (threaded)
520 for (i = msgCount; i > 999; i /= 10)
521 ++n;
523 if (UICMP(32, n_ABS(n), >, wleft))
524 n = (n < 0) ? -wleft : wleft;
525 n = fprintf(f, "%*lu",
526 n, (threaded ? (ul_i)mp->m_threadpos : (ul_i)msgno));
527 wleft = (n >= 0) ? wleft - n : 0;
528 break;
529 default:
530 if (n_poption & n_PO_D_V)
531 n_err(_("Unkown *headline* format: %%%c\n"), c);
532 c = '?';
533 goto jputcb;
536 if (wleft <= 0)
537 break;
540 n_COLOUR( n_colour_reset(); )
541 putc('\n', f);
543 if (subjline != NULL && subjline != (char*)-1)
544 free(subjline);
545 NYD_LEAVE;
548 static char *
549 __subject(struct message *mp, bool_t threaded, size_t yetprinted)
551 struct str in, out;
552 char *rv, *ms;
553 NYD_ENTER;
555 rv = (char*)-1;
557 if ((ms = hfield1("subject", mp)) == NULL)
558 goto jleave;
560 in.l = strlen(in.s = ms);
561 mime_fromhdr(&in, &out, TD_ICONV | TD_ISPR);
562 rv = ms = out.s;
564 if (!threaded || mp->m_level == 0)
565 goto jleave;
567 /* In a display thread - check whether this message uses the same
568 * Subject: as it's parent or elder neighbour, suppress printing it if
569 * this is the case. To extend this a bit, ignore any leading Re: or
570 * Fwd: plus follow-up WS. Ignore invisible messages along the way */
571 ms = n_UNCONST(subject_re_trim(n_UNCONST(ms)));
573 for (; (mp = prev_in_thread(mp)) != NULL && yetprinted-- > 0;) {
574 char *os;
576 if (visible(mp) && (os = hfield1("subject", mp)) != NULL) {
577 struct str oout;
578 int x;
580 in.l = strlen(in.s = os);
581 mime_fromhdr(&in, &oout, TD_ICONV | TD_ISPR);
582 x = asccasecmp(ms, subject_re_trim(oout.s));
583 free(oout.s);
585 if (!x) {
586 free(out.s);
587 rv = (char*)-1;
589 break;
592 jleave:
593 NYD_LEAVE;
594 return rv;
597 static int
598 __putindent(FILE *fp, struct message *mp, int maxwidth)/* XXX no magic consts */
600 struct message *mq;
601 int *us, indlvl, indw, i, important = MNEW | MFLAGGED;
602 char *cs;
603 NYD_ENTER;
605 if (mp->m_level == 0 || maxwidth == 0) {
606 indw = 0;
607 goto jleave;
610 cs = ac_alloc(mp->m_level);
611 us = ac_alloc(mp->m_level * sizeof *us);
613 i = mp->m_level - 1;
614 if (mp->m_younger && UICMP(32, i + 1, ==, mp->m_younger->m_level)) {
615 if (mp->m_parent && mp->m_parent->m_flag & important)
616 us[i] = mp->m_flag & important ? 0x2523 : 0x2520;
617 else
618 us[i] = mp->m_flag & important ? 0x251D : 0x251C;
619 cs[i] = '+';
620 } else {
621 if (mp->m_parent && mp->m_parent->m_flag & important)
622 us[i] = mp->m_flag & important ? 0x2517 : 0x2516;
623 else
624 us[i] = mp->m_flag & important ? 0x2515 : 0x2514;
625 cs[i] = '\\';
628 mq = mp->m_parent;
629 for (i = mp->m_level - 2; i >= 0; --i) {
630 if (mq) {
631 if (UICMP(32, i, >, mq->m_level - 1)) {
632 us[i] = cs[i] = ' ';
633 continue;
635 if (mq->m_younger) {
636 if (mq->m_parent && (mq->m_parent->m_flag & important))
637 us[i] = 0x2503;
638 else
639 us[i] = 0x2502;
640 cs[i] = '|';
641 } else
642 us[i] = cs[i] = ' ';
643 mq = mq->m_parent;
644 } else
645 us[i] = cs[i] = ' ';
648 --maxwidth;
649 for (indlvl = indw = 0; (ui8_t)indlvl < mp->m_level && indw < maxwidth;
650 ++indlvl) {
651 if (indw < maxwidth - 1)
652 indw += (int)putuc(us[indlvl], cs[indlvl] & 0xFF, fp);
653 else
654 indw += (int)putuc(0x21B8, '^', fp);
656 indw += putuc(0x25B8, '>', fp);
658 ac_free(us);
659 ac_free(cs);
660 jleave:
661 NYD_LEAVE;
662 return indw;
665 static int
666 _dispc(struct message *mp, char const *a)
668 int i = ' ';
669 NYD_ENTER;
671 if ((mp->m_flag & (MREAD | MNEW)) == MREAD)
672 i = a[3];
673 if ((mp->m_flag & (MREAD | MNEW)) == (MREAD | MNEW))
674 i = a[2];
675 if (mp->m_flag & MANSWERED)
676 i = a[8];
677 if (mp->m_flag & MDRAFTED)
678 i = a[9];
679 if ((mp->m_flag & (MREAD | MNEW)) == MNEW)
680 i = a[0];
681 if (!(mp->m_flag & (MREAD | MNEW)))
682 i = a[1];
683 if (mp->m_flag & MSPAM)
684 i = a[12];
685 if (mp->m_flag & MSPAMUNSURE)
686 i = a[13];
687 if (mp->m_flag & MSAVED)
688 i = a[4];
689 if (mp->m_flag & MPRESERVE)
690 i = a[5];
691 if (mp->m_flag & (MBOX | MBOXED))
692 i = a[6];
693 if (mp->m_flag & MFLAGGED)
694 i = a[7];
695 if (mb.mb_threaded == 1 && mp->m_collapsed > 0)
696 i = a[11];
697 if (mb.mb_threaded == 1 && mp->m_collapsed < 0)
698 i = a[10];
699 NYD_LEAVE;
700 return i;
703 static int
704 a_cmd_scroll(char const *arg, bool_t onlynew){
705 siz_t l;
706 bool_t isabs;
707 int msgspec, size, maxs;
708 NYD2_ENTER;
710 /* TODO scroll problem: we do not know whether + and $ have already reached
711 * TODO the last screen in threaded mode */
712 msgspec = onlynew ? -1 : 0;
713 size = (int)/*TODO*/n_screensize();
714 if((maxs = msgCount / size) > 0 && msgCount % size == 0)
715 --maxs;
717 if(arg == NULL)
718 arg = n_empty;
719 switch(*arg){
720 case '\0':
721 ++_screen;
722 goto jfwd;
723 case '^':
724 if(arg[1] != '\0')
725 goto jerr;
726 if(_screen == 0)
727 goto jerrbwd;
728 _screen = 0;
729 break;
730 case '$':
731 if(arg[1] != '\0')
732 goto jerr;
733 if(_screen == maxs)
734 goto jerrfwd;
735 _screen = maxs;
736 break;
737 case '+':
738 if(arg[1] == '\0')
739 ++_screen;
740 else{
741 isabs = FAL0;
743 ++arg;
744 if(0){
745 case '1': case '2': case '3': case '4': case '5':
746 case '6': case '7': case '8': case '9': case '0':
747 isabs = TRU1;
749 if((n_idec_siz_cp(&l, arg, 0, NULL
750 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
751 ) != n_IDEC_STATE_CONSUMED)
752 goto jerr;
753 if(l > maxs - (isabs ? 0 : _screen))
754 goto jerrfwd;
755 _screen = isabs ? (int)l : _screen + l;
757 jfwd:
758 if(_screen > maxs){
759 jerrfwd:
760 _screen = maxs;
761 fprintf(n_stdout, _("On last screenful of messages\n"));
763 break;
765 case '-':
766 if(arg[1] == '\0')
767 --_screen;
768 else{
769 if((n_idec_siz_cp(&l, ++arg, 0, NULL
770 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
771 ) != n_IDEC_STATE_CONSUMED)
772 goto jerr;
773 if(l > _screen)
774 goto jerrbwd;
775 _screen -= l;
777 if(_screen < 0){
778 jerrbwd:
779 _screen = 0;
780 fprintf(n_stdout, _("On first screenful of messages\n"));
782 if(msgspec == -1)
783 msgspec = -2;
784 break;
785 default:
786 jerr:
787 n_err(_("Unrecognized scrolling command: %s\n"), arg);
788 size = 1;
789 goto jleave;
792 size = _headers(msgspec);
793 jleave:
794 NYD2_LEAVE;
795 return size;
798 static int
799 _headers(int msgspec) /* TODO rework v15 */
801 struct n_sigman sm;
802 bool_t volatile isrelax;
803 ui32_t volatile flag;
804 int g, k, mesg, size;
805 int volatile lastg = 1;
806 struct message *mp, *mq, *lastmq = NULL;
807 enum mflag fl = MNEW | MFLAGGED;
808 NYD_ENTER;
810 time_current_update(&time_current, FAL0);
812 flag = 0;
813 isrelax = FAL0;
814 n_SIGMAN_ENTER_SWITCH(&sm, n_SIGMAN_ALL) {
815 case 0:
816 break;
817 default:
818 goto jleave;
821 n_COLOUR( n_colour_env_create(n_COLOUR_CTX_SUM, n_stdout, FAL0); )
823 size = (int)/*TODO*/n_screensize();
824 if (_screen < 0)
825 _screen = 0;
826 #if 0 /* FIXME original code path */
827 k = _screen * size;
828 #else
829 if (msgspec <= 0)
830 k = _screen * size;
831 else
832 k = msgspec;
833 #endif
834 if (k >= msgCount)
835 k = msgCount - size;
836 if (k < 0)
837 k = 0;
839 if (mb.mb_threaded == 0) {
840 g = 0;
841 mq = message;
842 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
843 if (visible(mp)) {
844 if (g % size == 0)
845 mq = mp;
846 if (mp->m_flag & fl) {
847 lastg = g;
848 lastmq = mq;
850 if ((msgspec > 0 && PTRCMP(mp, ==, message + msgspec - 1)) ||
851 (msgspec == 0 && g == k) ||
852 (msgspec == -2 && g == k + size && lastmq) ||
853 (msgspec < 0 && g >= k && (mp->m_flag & fl) != 0))
854 break;
855 g++;
857 if (lastmq && (msgspec == -2 ||
858 (msgspec == -1 && PTRCMP(mp, ==, message + msgCount)))) {
859 g = lastg;
860 mq = lastmq;
862 _screen = g / size;
864 mp = mq;
865 mesg = (int)PTR2SIZE(mp - message);
866 if (PTRCMP(dot, !=, message + msgspec - 1)) { /* TODO really?? */
867 for (mq = mp; PTRCMP(mq, <, message + msgCount); ++mq)
868 if (visible(mq)) {
869 setdot(mq);
870 break;
874 srelax_hold();
875 isrelax = TRU1;
876 for (; PTRCMP(mp, <, message + msgCount); ++mp) {
877 ++mesg;
878 if (!visible(mp))
879 continue;
880 if (UICMP(32, flag++, >=, size))
881 break;
882 _print_head(0, mesg, n_stdout, 0);
883 srelax();
885 srelax_rele();
886 isrelax = FAL0;
887 } else { /* threaded */
888 g = 0;
889 mq = threadroot;
890 for (mp = threadroot; mp; mp = next_in_thread(mp))
891 if (visible(mp) &&
892 (mp->m_collapsed <= 0 ||
893 PTRCMP(mp, ==, message + msgspec - 1))) {
894 if (g % size == 0)
895 mq = mp;
896 if (mp->m_flag & fl) {
897 lastg = g;
898 lastmq = mq;
900 if ((msgspec > 0 && PTRCMP(mp, ==, message + msgspec - 1)) ||
901 (msgspec == 0 && g == k) ||
902 (msgspec == -2 && g == k + size && lastmq) ||
903 (msgspec < 0 && g >= k && (mp->m_flag & fl) != 0))
904 break;
905 g++;
907 if (lastmq && (msgspec == -2 ||
908 (msgspec == -1 && PTRCMP(mp, ==, message + msgCount)))) {
909 g = lastg;
910 mq = lastmq;
912 _screen = g / size;
913 mp = mq;
914 if (PTRCMP(dot, !=, message + msgspec - 1)) { /* TODO really?? */
915 for (mq = mp; mq; mq = next_in_thread(mq))
916 if (visible(mq) && mq->m_collapsed <= 0) {
917 setdot(mq);
918 break;
922 srelax_hold();
923 isrelax = TRU1;
924 while (mp) {
925 if (visible(mp) &&
926 (mp->m_collapsed <= 0 ||
927 PTRCMP(mp, ==, message + msgspec - 1))) {
928 if (UICMP(32, flag++, >=, size))
929 break;
930 _print_head(flag - 1, PTR2SIZE(mp - message + 1), n_stdout,
931 mb.mb_threaded);
932 srelax();
934 mp = next_in_thread(mp);
936 srelax_rele();
937 isrelax = FAL0;
940 if (!flag) {
941 fprintf(n_stdout, _("No more mail.\n"));
942 if (n_pstate & (n_PS_ROBOT | n_PS_HOOK_MASK))
943 flag = !flag;
946 n_sigman_cleanup_ping(&sm);
947 jleave:
948 if (isrelax)
949 srelax_rele();
950 n_COLOUR( n_colour_env_gut(); )
951 NYD_LEAVE;
952 n_sigman_leave(&sm, n_SIGMAN_VIPSIGS_NTTYOUT);
953 return !flag;
956 FL int
957 c_headers(void *v)
959 int rv;
960 NYD_ENTER;
962 rv = print_header_group((int*)v);
963 NYD_LEAVE;
964 return rv;
967 FL int
968 print_header_group(int *vector)
970 int rv;
971 NYD_ENTER;
973 assert(vector != NULL && vector != (void*)-1);
974 rv = _headers(vector[0]);
975 NYD_LEAVE;
976 return rv;
979 FL int
980 c_scroll(void *v)
982 int rv;
983 NYD_ENTER;
985 rv = a_cmd_scroll(*(char const**)v, FAL0);
986 NYD_LEAVE;
987 return rv;
990 FL int
991 c_Scroll(void *v)
993 int rv;
994 NYD_ENTER;
996 rv = a_cmd_scroll(*(char const**)v, TRU1);
997 NYD_LEAVE;
998 return rv;
1001 FL int
1002 c_dotmove(void *v)
1004 char const *args;
1005 int msgvec[2], rv;
1006 NYD_ENTER;
1008 if (*(args = v) == '\0' || args[1] != '\0') {
1009 jerr:
1010 n_err(_("Synopsis: dotmove: up <-> or down <+> by one message\n"));
1011 rv = 1;
1012 } else switch (args[0]) {
1013 case '-':
1014 case '+':
1015 if (msgCount == 0) {
1016 fprintf(n_stdout, _("At EOF\n"));
1017 rv = 0;
1018 } else if (getmsglist(n_UNCONST(/*TODO*/ args), msgvec, 0) > 0) {
1019 setdot(message + msgvec[0] - 1);
1020 msgvec[1] = 0;
1021 rv = c_headers(msgvec);
1022 } else
1023 rv = 1;
1024 break;
1025 default:
1026 goto jerr;
1028 NYD_LEAVE;
1029 return rv;
1032 FL int
1033 c_from(void *v)
1035 struct n_sigman sm;
1036 int *msgvec = v, *ip, n;
1037 char *cp;
1038 FILE * volatile obuf;
1039 bool_t volatile isrelax;
1040 NYD_ENTER;
1042 time_current_update(&time_current, FAL0);
1044 obuf = n_stdout;
1045 isrelax = FAL0;
1046 n_SIGMAN_ENTER_SWITCH(&sm, n_SIGMAN_ALL) {
1047 case 0:
1048 break;
1049 default:
1050 goto jleave;
1053 if (n_psonce & n_PSO_INTERACTIVE) {
1054 if ((cp = ok_vlook(crt)) != NULL) {
1055 uiz_t ib;
1057 for (n = 0, ip = msgvec; *ip != 0; ++ip)
1058 ++n;
1060 if(*cp == '\0')
1061 ib = n_screensize();
1062 else
1063 n_idec_uiz_cp(&ib, cp, 0, NULL);
1064 if (UICMP(z, n, >, ib) && (obuf = n_pager_open()) == NULL)
1065 obuf = n_stdout;
1068 n_COLOUR( n_colour_env_create(n_COLOUR_CTX_SUM, obuf, obuf != n_stdout); )
1070 /* Update dot before display so that the dotmark etc. are correct */
1071 for (ip = msgvec; *ip != 0; ++ip)
1073 if (--ip >= msgvec)
1074 setdot(message + *ip - 1);
1076 srelax_hold();
1077 isrelax = TRU1;
1078 for (n = 0, ip = msgvec; *ip != 0; ++ip) { /* TODO join into _print_head() */
1079 _print_head((size_t)n++, (size_t)*ip, obuf, mb.mb_threaded);
1080 srelax();
1082 srelax_rele();
1083 isrelax = FAL0;
1085 n_sigman_cleanup_ping(&sm);
1086 jleave:
1087 if (isrelax)
1088 srelax_rele();
1089 n_COLOUR( n_colour_env_gut(); )
1090 if (obuf != n_stdout)
1091 n_pager_close(obuf);
1092 NYD_LEAVE;
1093 n_sigman_leave(&sm, n_SIGMAN_VIPSIGS_NTTYOUT);
1094 return 0;
1097 FL void
1098 print_headers(size_t bottom, size_t topx, bool_t only_marked)
1100 struct n_sigman sm;
1101 bool_t volatile isrelax;
1102 size_t printed;
1103 NYD_ENTER;
1105 time_current_update(&time_current, FAL0);
1107 isrelax = FAL0;
1108 n_SIGMAN_ENTER_SWITCH(&sm, n_SIGMAN_ALL) {
1109 case 0:
1110 break;
1111 default:
1112 goto jleave;
1115 n_COLOUR( n_colour_env_create(n_COLOUR_CTX_SUM, n_stdout, FAL0); )
1117 srelax_hold();
1118 isrelax = TRU1;
1119 for (printed = 0; bottom <= topx; ++bottom) {
1120 struct message *mp = message + bottom - 1;
1121 if (only_marked) {
1122 if (!(mp->m_flag & MMARK))
1123 continue;
1124 } else if (!visible(mp))
1125 continue;
1126 _print_head(printed++, bottom, n_stdout, FAL0);
1127 srelax();
1129 srelax_rele();
1130 isrelax = FAL0;
1132 n_sigman_cleanup_ping(&sm);
1133 jleave:
1134 if (isrelax)
1135 srelax_rele();
1136 n_COLOUR( n_colour_env_gut(); )
1137 NYD_LEAVE;
1138 n_sigman_leave(&sm, n_SIGMAN_VIPSIGS_NTTYOUT);
1141 /* s-it-mode */