n_shexp_parse_token(): (finally) get rid of CC warning
[s-mailx.git] / cmd-headers.c
blobc55fb779fa548d82ff8f24dc8c5a214330f89bac
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 - 2018 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 built-in\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 = n_time_ctime(datet, NULL);
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 /* This localtime(3) should not fail since rfctime(3).. but .. */
174 struct tm *tmp;
175 time_t datet2;
177 /* TODO the datetime stuff is horror: mails should be parsed into
178 * TODO an object tree, and date: etc. have a datetime object, which
179 * TODO verifies upon parse time; then ALL occurrences of datetime are
180 * TODO valid all through the program; and: to_wire, to_user! */
181 datet2 = datet;
182 jredo_localtime:
183 if((tmp = localtime(&datet2)) == NULL){
184 datet2 = 0;
185 goto jredo_localtime;
187 memcpy(&time_current.tc_local, tmp, sizeof(*tmp));
190 if ((i & 2) &&
191 (UICMP(64,datet, >, time_current.tc_time + n_DATE_SECSDAY) ||
192 #define _6M ((n_DATE_DAYSYEAR / 2) * n_DATE_SECSDAY)
193 UICMP(64,datet + _6M, <, time_current.tc_time))) {
194 #undef _6M
195 if ((datefmt = (i & 4) ? fp : NULL) == NULL) {
196 memset(datebuf, ' ', n_FROM_DATEBUF); /* xxx ur */
197 memcpy(datebuf + 4, date + 4, 7);
198 datebuf[4 + 7] = ' ';
199 memcpy(datebuf + 4 + 7 + 1, date + 20, 4);
200 datebuf[4 + 7 + 1 + 4] = '\0';
201 date = datebuf;
203 n_COLOUR( colo_tag = n_COLOUR_TAG_SUM_OLDER; )
204 } else if ((i & 1) == 0)
205 datefmt = NULL;
206 } else if (datet == (time_t)0 && !(mp->m_flag & MNOFROM)) {
207 /* TODO eliminate this path, query the FROM_ date in setptr(),
208 * TODO all other codepaths do so by themselves ALREADY ?????
209 * TODO assert(mp->m_time != 0);, then
210 * TODO ALSO changes behaviour of datefield_markout_older */
211 _parse_from_(mp, datebuf);
212 date = datebuf;
213 } else
214 date = n_time_ctime(datet, NULL);
216 flags |= _ISADDR;
217 name = name1(mp, 0);
218 if (name != NULL && ok_blook(showto) && n_is_myname(skin(name))) {
219 if ((cp = hfield1("to", mp)) != NULL) {
220 name = cp;
221 flags |= _ISTO;
224 if (name == NULL) {
225 name = n_empty;
226 flags &= ~_ISADDR;
228 if (flags & _ISADDR)
229 name = ok_blook(showname) ? realname(name) : prstr(skin(name));
231 subjline = NULL;
233 /* Detect the width of the non-format characters in *headline*;
234 * like that we can simply use putc() in the next loop, since we have
235 * already calculated their column widths (TODO it's sick) */
236 wleft = subjlen = n_scrnwidth;
238 for (fp = fmt; *fp != '\0'; ++fp) {
239 if (*fp == '%') {
240 if (*++fp == '-')
241 ++fp;
242 else if (*fp == '+')
243 ++fp;
244 if (digitchar(*fp)) {
245 n = 0;
247 n = 10*n + *fp - '0';
248 while (++fp, digitchar(*fp));
249 subjlen -= n;
251 if (*fp == 'i')
252 flags |= _IFMT;
254 if (*fp == '\0')
255 break;
256 } else {
257 #ifdef HAVE_WCWIDTH
258 if (n_mb_cur_max > 1) {
259 wchar_t wc;
260 if ((s = mbtowc(&wc, fp, n_mb_cur_max)) == -1)
261 n = s = 1;
262 else if ((n = wcwidth(wc)) == -1)
263 n = 1;
264 } else
265 #endif
266 n = s = 1;
267 subjlen -= n;
268 wleft -= n;
269 while (--s > 0)
270 ++fp;
274 /* Walk *headline*, producing output TODO not (really) MB safe */
275 #ifdef HAVE_COLOUR
276 if(n_COLOUR_IS_ACTIVE()){
277 if(flags & _ISDOT)
278 colo_tag = n_COLOUR_TAG_SUM_DOT;
279 cpen_bas = n_colour_pen_create(n_COLOUR_ID_SUM_HEADER, colo_tag);
280 n_colour_pen_put(cpen_new = cpen_cur = cpen_bas);
281 }else
282 cpen_new = cpen_bas = cpen_cur = NULL;
283 #endif
285 for (fp = fmt; *fp != '\0'; ++fp) {
286 char c;
288 if ((c = *fp & 0xFF) != '%') {
289 n_COLOUR(
290 if(n_COLOUR_IS_ACTIVE() && (cpen_new = cpen_bas) != cpen_cur)
291 n_colour_pen_put(cpen_cur = cpen_new);
293 putc(c, f);
294 continue;
297 flags &= _LOOP_MASK;
298 n = 0;
299 s = 1;
300 if ((c = *++fp) == '-') {
301 s = -1;
302 ++fp;
303 } else if (c == '+')
304 ++fp;
305 if (digitchar(*fp)) {
307 n = 10*n + *fp - '0';
308 while (++fp, digitchar(*fp));
311 if ((c = *fp & 0xFF) == '\0')
312 break;
313 n *= s;
315 cbuf[1] = '\0';
316 switch (c) {
317 case '%':
318 goto jputcb;
319 case '>':
320 case '<':
321 if (flags & _ISDOT) {
322 n_COLOUR(
323 if(n_COLOUR_IS_ACTIVE())
324 cpen_new = n_colour_pen_create(n_COLOUR_ID_SUM_DOTMARK,
325 colo_tag);
327 if (n_psonce & n_PSO_UNICODE) {
328 if (c == '>')
329 /* 25B8;BLACK RIGHT-POINTING SMALL TRIANGLE: â–¸ */
330 cbuf[1] = (char)0x96, cbuf[2] = (char)0xB8;
331 else
332 /* 25C2;BLACK LEFT-POINTING SMALL TRIANGLE: â—‚ */
333 cbuf[1] = (char)0x97, cbuf[2] = (char)0x82;
334 c = (char)0xE2;
335 cbuf[3] = '\0';
336 flags |= 2 << _PUTCB_UTF8_SHIFT;
338 } else
339 c = ' ';
340 goto jputcb;
341 case '$':
342 #ifdef HAVE_SPAM
343 if (n == 0)
344 n = 5;
345 if (UICMP(32, n_ABS(n), >, wleft))
346 n = (n < 0) ? -wleft : wleft;
347 snprintf(buf, sizeof buf, "%u.%02u",
348 (mp->m_spamscore >> 8), (mp->m_spamscore & 0xFF));
349 n = fprintf(f, "%*s", n, buf);
350 wleft = (n >= 0) ? wleft - n : 0;
351 break;
352 #else
353 c = '?';
354 goto jputcb;
355 #endif
356 case 'a':
357 c = _dispc(mp, attrlist);
358 jputcb:
359 #ifdef HAVE_COLOUR
360 if(n_COLOUR_IS_ACTIVE()){
361 if(cpen_new == cpen_cur)
362 cpen_new = cpen_bas;
363 if(cpen_new != cpen_cur)
364 n_colour_pen_put(cpen_cur = cpen_new);
366 #endif
367 if (UICMP(32, n_ABS(n), >, wleft))
368 n = (n < 0) ? -wleft : wleft;
369 cbuf[0] = c;
370 n = fprintf(f, "%*s", n, cbuf);
371 if (n >= 0) {
372 wleft -= n;
373 if ((n = (flags & _PUTCB_UTF8_MASK)) != 0) {
374 n >>= _PUTCB_UTF8_SHIFT;
375 wleft += n;
377 } else {
378 wleft = 0; /* TODO I/O error.. ? break? */
380 #ifdef HAVE_COLOUR
381 if(n_COLOUR_IS_ACTIVE() && (cpen_new = cpen_bas) != cpen_cur)
382 n_colour_pen_put(cpen_cur = cpen_new);
383 #endif
384 break;
385 case 'd':
386 if (datefmt != NULL) {
387 i = strftime(datebuf, sizeof datebuf, datefmt,
388 &time_current.tc_local);
389 if (i != 0)
390 date = datebuf;
391 else
392 n_err(_("Ignoring date format, it is either empty or "
393 "excesses buffer size (%lu bytes)\n"),
394 (ul_i)sizeof(datebuf));
395 datefmt = NULL;
397 if (n == 0)
398 n = 16;
399 if (UICMP(32, n_ABS(n), >, wleft))
400 n = (n < 0) ? -wleft : wleft;
401 n = fprintf(f, "%*.*s", n, n_ABS(n), date);
402 wleft = (n >= 0) ? wleft - n : 0;
403 break;
404 case 'e':
405 if (n == 0)
406 n = 2;
407 if (UICMP(32, n_ABS(n), >, wleft))
408 n = (n < 0) ? -wleft : wleft;
409 n = fprintf(f, "%*u", n, (threaded == 1 ? mp->m_level : 0));
410 wleft = (n >= 0) ? wleft - n : 0;
411 break;
412 case 'f':
413 if (n == 0) {
414 n = 18;
415 if (s < 0)
416 n = -n;
418 i = n_ABS(n);
419 if (i > wleft) {
420 i = wleft;
421 n = (n < 0) ? -wleft : wleft;
423 if (flags & _ISTO) /* XXX tr()! */
424 i -= 3;
425 n = fprintf(f, "%s%s", ((flags & _ISTO) ? "To " : n_empty),
426 colalign(name, i, n, &wleft));
427 if (n < 0)
428 wleft = 0;
429 else if (flags & _ISTO)
430 wleft -= 3;
431 break;
432 case 'i':
433 if (threaded) {
434 #ifdef HAVE_COLOUR
435 if(n_COLOUR_IS_ACTIVE()){
436 cpen_new = n_colour_pen_create(n_COLOUR_ID_SUM_THREAD, colo_tag);
437 if(cpen_new != cpen_cur)
438 n_colour_pen_put(cpen_cur = cpen_new);
440 #endif
441 n = __putindent(f, mp, n_MIN(wleft, (int)n_scrnwidth - 60));
442 wleft = (n >= 0) ? wleft - n : 0;
443 #ifdef HAVE_COLOUR
444 if(n_COLOUR_IS_ACTIVE() && (cpen_new = cpen_bas) != cpen_cur)
445 n_colour_pen_put(cpen_cur = cpen_new);
446 #endif
448 break;
449 case 'l':
450 if (n == 0)
451 n = 4;
452 if (UICMP(32, n_ABS(n), >, wleft))
453 n = (n < 0) ? -wleft : wleft;
454 if (mp->m_xlines) {
455 n = fprintf(f, "%*ld", n, mp->m_xlines);
456 wleft = (n >= 0) ? wleft - n : 0;
457 } else {
458 n = n_ABS(n);
459 wleft -= n;
460 while (n-- != 0)
461 putc(' ', f);
463 break;
464 case 'm':
465 if (n == 0) {
466 n = 3;
467 if (threaded)
468 for (i = msgCount; i > 999; i /= 10)
469 ++n;
471 if (UICMP(32, n_ABS(n), >, wleft))
472 n = (n < 0) ? -wleft : wleft;
473 n = fprintf(f, "%*lu", n, (ul_i)msgno);
474 wleft = (n >= 0) ? wleft - n : 0;
475 break;
476 case 'o':
477 if (n == 0)
478 n = -5;
479 if (UICMP(32, n_ABS(n), >, wleft))
480 n = (n < 0) ? -wleft : wleft;
481 n = fprintf(f, "%*lu", n, (ul_i)mp->m_xsize);
482 wleft = (n >= 0) ? wleft - n : 0;
483 break;
484 case 'S':
485 flags |= _SFMT;
486 /*FALLTHRU*/
487 case 's':
488 if (n == 0)
489 n = subjlen - 2;
490 if (n > 0 && s < 0)
491 n = -n;
492 if (subjlen > wleft)
493 subjlen = wleft;
494 if (UICMP(32, n_ABS(n), >, subjlen))
495 n = (n < 0) ? -subjlen : subjlen;
496 if (flags & _SFMT)
497 n -= (n < 0) ? -2 : 2;
498 if (n == 0)
499 break;
500 if (subjline == NULL)
501 subjline = __subject(mp, (threaded && (flags & _IFMT)), yetprinted);
502 if (subjline == (char*)-1) {
503 n = fprintf(f, "%*s", n, n_empty);
504 wleft = (n >= 0) ? wleft - n : 0;
505 } else {
506 n = fprintf(f, ((flags & _SFMT) ? "\"%s\"" : "%s"),
507 colalign(subjline, n_ABS(n), n, &wleft));
508 if (n < 0)
509 wleft = 0;
511 break;
512 case 'T': /* Message recipient flags */
513 switch(is_mlist_mp(mp, MLIST_OTHER)){
514 case MLIST_OTHER: c = ' '; break;
515 case MLIST_KNOWN: c = 'l'; break;
516 case MLIST_SUBSCRIBED: c = 'L'; break;
518 goto jputcb;
519 case 't':
520 if (n == 0) {
521 n = 3;
522 if (threaded)
523 for (i = msgCount; i > 999; i /= 10)
524 ++n;
526 if (UICMP(32, n_ABS(n), >, wleft))
527 n = (n < 0) ? -wleft : wleft;
528 n = fprintf(f, "%*lu",
529 n, (threaded ? (ul_i)mp->m_threadpos : (ul_i)msgno));
530 wleft = (n >= 0) ? wleft - n : 0;
531 break;
532 case 'U':
533 #ifdef HAVE_IMAP
534 if (n == 0)
535 n = 9;
536 if (UICMP(32, n_ABS(n), >, wleft))
537 n = (n < 0) ? -wleft : wleft;
538 n = fprintf(f, "%*lu", n, mp->m_uid);
539 wleft = (n >= 0) ? wleft - n : 0;
540 break;
541 #else
542 c = '?';
543 goto jputcb;
544 #endif
545 default:
546 if (n_poption & n_PO_D_V)
547 n_err(_("Unknown *headline* format: %%%c\n"), c);
548 c = '?';
549 goto jputcb;
552 if (wleft <= 0)
553 break;
556 n_COLOUR( n_colour_reset(); )
557 putc('\n', f);
559 if (subjline != NULL && subjline != (char*)-1)
560 free(subjline);
561 NYD_LEAVE;
564 static char *
565 __subject(struct message *mp, bool_t threaded, size_t yetprinted)
567 struct str in, out;
568 char *rv, *ms;
569 NYD_ENTER;
571 rv = (char*)-1;
573 if ((ms = hfield1("subject", mp)) == NULL)
574 goto jleave;
576 in.l = strlen(in.s = ms);
577 mime_fromhdr(&in, &out, TD_ICONV | TD_ISPR);
578 rv = ms = out.s;
580 if (!threaded || mp->m_level == 0)
581 goto jleave;
583 /* In a display thread - check whether this message uses the same
584 * Subject: as it's parent or elder neighbour, suppress printing it if
585 * this is the case. To extend this a bit, ignore any leading Re: or
586 * Fwd: plus follow-up WS. Ignore invisible messages along the way */
587 ms = n_UNCONST(subject_re_trim(n_UNCONST(ms)));
589 for (; (mp = prev_in_thread(mp)) != NULL && yetprinted-- > 0;) {
590 char *os;
592 if (visible(mp) && (os = hfield1("subject", mp)) != NULL) {
593 struct str oout;
594 int x;
596 in.l = strlen(in.s = os);
597 mime_fromhdr(&in, &oout, TD_ICONV | TD_ISPR);
598 x = asccasecmp(ms, subject_re_trim(oout.s));
599 free(oout.s);
601 if (!x) {
602 free(out.s);
603 rv = (char*)-1;
605 break;
608 jleave:
609 NYD_LEAVE;
610 return rv;
613 static int
614 __putindent(FILE *fp, struct message *mp, int maxwidth)/* XXX no magic consts */
616 struct message *mq;
617 int *us, indlvl, indw, i, important = MNEW | MFLAGGED;
618 char *cs;
619 NYD_ENTER;
621 if (mp->m_level == 0 || maxwidth == 0) {
622 indw = 0;
623 goto jleave;
626 cs = ac_alloc(mp->m_level);
627 us = ac_alloc(mp->m_level * sizeof *us);
629 i = mp->m_level - 1;
630 if (mp->m_younger && UICMP(32, i + 1, ==, mp->m_younger->m_level)) {
631 if (mp->m_parent && mp->m_parent->m_flag & important)
632 us[i] = mp->m_flag & important ? 0x2523 : 0x2520;
633 else
634 us[i] = mp->m_flag & important ? 0x251D : 0x251C;
635 cs[i] = '+';
636 } else {
637 if (mp->m_parent && mp->m_parent->m_flag & important)
638 us[i] = mp->m_flag & important ? 0x2517 : 0x2516;
639 else
640 us[i] = mp->m_flag & important ? 0x2515 : 0x2514;
641 cs[i] = '\\';
644 mq = mp->m_parent;
645 for (i = mp->m_level - 2; i >= 0; --i) {
646 if (mq) {
647 if (UICMP(32, i, >, mq->m_level - 1)) {
648 us[i] = cs[i] = ' ';
649 continue;
651 if (mq->m_younger) {
652 if (mq->m_parent && (mq->m_parent->m_flag & important))
653 us[i] = 0x2503;
654 else
655 us[i] = 0x2502;
656 cs[i] = '|';
657 } else
658 us[i] = cs[i] = ' ';
659 mq = mq->m_parent;
660 } else
661 us[i] = cs[i] = ' ';
664 --maxwidth;
665 for (indlvl = indw = 0; (ui8_t)indlvl < mp->m_level && indw < maxwidth;
666 ++indlvl) {
667 if (indw < maxwidth - 1)
668 indw += (int)putuc(us[indlvl], cs[indlvl] & 0xFF, fp);
669 else
670 indw += (int)putuc(0x21B8, '^', fp);
672 indw += putuc(0x25B8, '>', fp);
674 ac_free(us);
675 ac_free(cs);
676 jleave:
677 NYD_LEAVE;
678 return indw;
681 static int
682 _dispc(struct message *mp, char const *a)
684 int i = ' ';
685 NYD_ENTER;
687 if ((mp->m_flag & (MREAD | MNEW)) == MREAD)
688 i = a[3];
689 if ((mp->m_flag & (MREAD | MNEW)) == (MREAD | MNEW))
690 i = a[2];
691 if (mp->m_flag & MANSWERED)
692 i = a[8];
693 if (mp->m_flag & MDRAFTED)
694 i = a[9];
695 if ((mp->m_flag & (MREAD | MNEW)) == MNEW)
696 i = a[0];
697 if (!(mp->m_flag & (MREAD | MNEW)))
698 i = a[1];
699 if (mp->m_flag & MSPAM)
700 i = a[12];
701 if (mp->m_flag & MSPAMUNSURE)
702 i = a[13];
703 if (mp->m_flag & MSAVED)
704 i = a[4];
705 if (mp->m_flag & MPRESERVE)
706 i = a[5];
707 if (mp->m_flag & (MBOX | MBOXED))
708 i = a[6];
709 if (mp->m_flag & MFLAGGED)
710 i = a[7];
711 if (mb.mb_threaded == 1 && mp->m_collapsed > 0)
712 i = a[11];
713 if (mb.mb_threaded == 1 && mp->m_collapsed < 0)
714 i = a[10];
715 NYD_LEAVE;
716 return i;
719 static int
720 a_cmd_scroll(char const *arg, bool_t onlynew){
721 siz_t l;
722 bool_t isabs;
723 int msgspec, size, maxs;
724 NYD2_ENTER;
726 /* TODO scroll problem: we do not know whether + and $ have already reached
727 * TODO the last screen in threaded mode */
728 msgspec = onlynew ? -1 : 0;
729 size = (int)/*TODO*/n_screensize();
730 if((maxs = msgCount / size) > 0 && msgCount % size == 0)
731 --maxs;
733 if(arg == NULL)
734 arg = n_empty;
735 switch(*arg){
736 case '\0':
737 ++_screen;
738 goto jfwd;
739 case '^':
740 if(arg[1] != '\0')
741 goto jerr;
742 if(_screen == 0)
743 goto jerrbwd;
744 _screen = 0;
745 break;
746 case '$':
747 if(arg[1] != '\0')
748 goto jerr;
749 if(_screen == maxs)
750 goto jerrfwd;
751 _screen = maxs;
752 break;
753 case '+':
754 if(arg[1] == '\0')
755 ++_screen;
756 else{
757 isabs = FAL0;
759 ++arg;
760 if(0){
761 case '1': case '2': case '3': case '4': case '5':
762 case '6': case '7': case '8': case '9': case '0':
763 isabs = TRU1;
765 if((n_idec_siz_cp(&l, arg, 0, NULL
766 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
767 ) != n_IDEC_STATE_CONSUMED)
768 goto jerr;
769 if(l > maxs - (isabs ? 0 : _screen))
770 goto jerrfwd;
771 _screen = isabs ? (int)l : _screen + l;
773 jfwd:
774 if(_screen > maxs){
775 jerrfwd:
776 _screen = maxs;
777 fprintf(n_stdout, _("On last screenful of messages\n"));
779 break;
781 case '-':
782 if(arg[1] == '\0')
783 --_screen;
784 else{
785 if((n_idec_siz_cp(&l, ++arg, 0, NULL
786 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
787 ) != n_IDEC_STATE_CONSUMED)
788 goto jerr;
789 if(l > _screen)
790 goto jerrbwd;
791 _screen -= l;
793 if(_screen < 0){
794 jerrbwd:
795 _screen = 0;
796 fprintf(n_stdout, _("On first screenful of messages\n"));
798 if(msgspec == -1)
799 msgspec = -2;
800 break;
801 default:
802 jerr:
803 n_err(_("Unrecognized scrolling command: %s\n"), arg);
804 size = 1;
805 goto jleave;
808 size = _headers(msgspec);
809 jleave:
810 NYD2_LEAVE;
811 return size;
814 static int
815 _headers(int msgspec) /* TODO rework v15 */
817 bool_t needdot, showlast;
818 int g, k, mesg, size;
819 struct message *lastmq, *mp, *mq;
820 int volatile lastg;
821 ui32_t volatile flag;
822 enum mflag fl;
823 NYD_ENTER;
825 time_current_update(&time_current, FAL0);
827 fl = MNEW | MFLAGGED;
828 flag = 0;
829 lastg = 1;
830 lastmq = NULL;
832 size = (int)/*TODO*/n_screensize();
833 if (_screen < 0)
834 _screen = 0;
835 #if 0 /* FIXME original code path */
836 k = _screen * size;
837 #else
838 if (msgspec <= 0)
839 k = _screen * size;
840 else
841 k = msgspec;
842 #endif
843 if (k >= msgCount)
844 k = msgCount - size;
845 if (k < 0)
846 k = 0;
848 needdot = (msgspec == 0) ? TRU1 : (dot != &message[msgspec - 1]);
849 showlast = ok_blook(showlast);
851 if (mb.mb_threaded == 0) {
852 g = 0;
853 mq = message;
854 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
855 if (visible(mp)) {
856 if (g % size == 0)
857 mq = mp;
858 if (mp->m_flag & fl) {
859 lastg = g;
860 lastmq = mq;
862 if ((msgspec > 0 && PTRCMP(mp, ==, message + msgspec - 1)) ||
863 (msgspec == 0 && g == k) ||
864 (msgspec == -2 && g == k + size && lastmq) ||
865 (msgspec < 0 && g >= k && (mp->m_flag & fl) != 0))
866 break;
867 g++;
869 if (lastmq && (msgspec == -2 ||
870 (msgspec == -1 && PTRCMP(mp, ==, message + msgCount)))) {
871 g = lastg;
872 mq = lastmq;
874 _screen = g / size;
875 mp = mq;
877 mesg = (int)PTR2SIZE(mp - message);
878 #ifdef HAVE_IMAP
879 if (mb.mb_type == MB_IMAP)
880 imap_getheaders(mesg + 1, mesg + size);
881 #endif
882 n_COLOUR( n_colour_env_create(n_COLOUR_CTX_SUM, n_stdout, FAL0); )
883 srelax_hold();
884 for(lastmq = NULL, mq = &message[msgCount]; mp < mq; lastmq = mp, ++mp){
885 ++mesg;
886 if (!visible(mp))
887 continue;
888 if (UICMP(32, flag, >=, size))
889 break;
890 if(needdot){
891 if(showlast){
892 if(UICMP(32, flag, ==, size - 1) || &mp[1] == mq)
893 goto jdot_unsort;
894 }else if(flag == 0){
895 jdot_unsort:
896 needdot = FAL0;
897 setdot(mp);
900 ++flag;
901 _print_head(0, mesg, n_stdout, 0);
902 srelax();
904 if(needdot && ok_blook(showlast)) /* xxx will not show */
905 setdot(lastmq);
906 srelax_rele();
907 n_COLOUR( n_colour_env_gut(); )
909 } else { /* threaded */
910 g = 0;
911 mq = threadroot;
912 for (mp = threadroot; mp; mp = next_in_thread(mp))
913 if (visible(mp) &&
914 (mp->m_collapsed <= 0 ||
915 PTRCMP(mp, ==, message + msgspec - 1))) {
916 if (g % size == 0)
917 mq = mp;
918 if (mp->m_flag & fl) {
919 lastg = g;
920 lastmq = mq;
922 if ((msgspec > 0 && PTRCMP(mp, ==, message + msgspec - 1)) ||
923 (msgspec == 0 && g == k) ||
924 (msgspec == -2 && g == k + size && lastmq) ||
925 (msgspec < 0 && g >= k && (mp->m_flag & fl) != 0))
926 break;
927 g++;
929 if (lastmq && (msgspec == -2 ||
930 (msgspec == -1 && PTRCMP(mp, ==, message + msgCount)))) {
931 g = lastg;
932 mq = lastmq;
934 _screen = g / size;
935 mp = mq;
937 n_COLOUR( n_colour_env_create(n_COLOUR_CTX_SUM, n_stdout, FAL0); )
938 srelax_hold();
939 for(lastmq = NULL; mp != NULL; lastmq = mp, mp = mq){
940 mq = next_in_thread(mp);
941 if (visible(mp) &&
942 (mp->m_collapsed <= 0 ||
943 PTRCMP(mp, ==, message + msgspec - 1))) {
944 if (UICMP(32, flag, >=, size))
945 break;
946 if(needdot){
947 if(showlast){
948 if(UICMP(32, flag, ==, size - 1) || mq == NULL)
949 goto jdot_sort;
950 }else if(flag == 0){
951 jdot_sort:
952 needdot = FAL0;
953 setdot(mp);
956 _print_head(flag, PTR2SIZE(mp - message + 1), n_stdout,
957 mb.mb_threaded);
958 ++flag;
959 srelax();
962 if(needdot && ok_blook(showlast)) /* xxx will not show */
963 setdot(lastmq);
964 srelax_rele();
965 n_COLOUR( n_colour_env_gut(); )
968 if (flag == 0) {
969 fprintf(n_stdout, _("No more mail.\n"));
970 if (n_pstate & (n_PS_ROBOT | n_PS_HOOK_MASK))
971 flag = !flag;
973 NYD_LEAVE;
974 return !flag;
977 FL int
978 c_headers(void *v)
980 int rv;
981 NYD_ENTER;
983 rv = print_header_group((int*)v);
984 NYD_LEAVE;
985 return rv;
988 FL int
989 print_header_group(int *vector)
991 int rv;
992 NYD_ENTER;
994 assert(vector != NULL && vector != (void*)-1);
995 rv = _headers(vector[0]);
996 NYD_LEAVE;
997 return rv;
1000 FL int
1001 c_scroll(void *v)
1003 int rv;
1004 NYD_ENTER;
1006 rv = a_cmd_scroll(*(char const**)v, FAL0);
1007 NYD_LEAVE;
1008 return rv;
1011 FL int
1012 c_Scroll(void *v)
1014 int rv;
1015 NYD_ENTER;
1017 rv = a_cmd_scroll(*(char const**)v, TRU1);
1018 NYD_LEAVE;
1019 return rv;
1022 FL int
1023 c_dotmove(void *v)
1025 char const *args;
1026 int msgvec[2], rv;
1027 NYD_ENTER;
1029 if (*(args = v) == '\0' || args[1] != '\0') {
1030 jerr:
1031 n_err(_("Synopsis: dotmove: up <-> or down <+> by one message\n"));
1032 rv = 1;
1033 } else switch (args[0]) {
1034 case '-':
1035 case '+':
1036 if (msgCount == 0) {
1037 fprintf(n_stdout, _("At EOF\n"));
1038 rv = 0;
1039 } else if (getmsglist(n_UNCONST(/*TODO*/ args), msgvec, 0) > 0) {
1040 setdot(message + msgvec[0] - 1);
1041 msgvec[1] = 0;
1042 rv = c_headers(msgvec);
1043 } else
1044 rv = 1;
1045 break;
1046 default:
1047 goto jerr;
1049 NYD_LEAVE;
1050 return rv;
1053 FL int
1054 c_from(void *vp)
1056 int *msgvec, *ip, n;
1057 char *cp;
1058 FILE * volatile obuf;
1059 NYD_ENTER;
1061 if(*(msgvec = vp) == 0)
1062 goto jleave;
1064 time_current_update(&time_current, FAL0);
1066 obuf = n_stdout;
1068 if (n_psonce & n_PSO_INTERACTIVE) {
1069 if ((cp = ok_vlook(crt)) != NULL) {
1070 uiz_t ib;
1072 for (n = 0, ip = msgvec; *ip != 0; ++ip)
1073 ++n;
1075 if(*cp == '\0')
1076 ib = n_screensize();
1077 else
1078 n_idec_uiz_cp(&ib, cp, 0, NULL);
1079 if (UICMP(z, n, >, ib) && (obuf = n_pager_open()) == NULL)
1080 obuf = n_stdout;
1084 /* Update dot before display so that the dotmark etc. are correct */
1085 for (ip = msgvec; ip[1] != 0; ++ip)
1087 setdot(&message[(ok_blook(showlast) ? *ip : *msgvec) - 1]);
1089 n_COLOUR( n_colour_env_create(n_COLOUR_CTX_SUM, obuf, obuf != n_stdout); )
1090 srelax_hold();
1091 for (n = 0, ip = msgvec; *ip != 0; ++ip) { /* TODO join into _print_head() */
1092 _print_head((size_t)n++, (size_t)*ip, obuf, mb.mb_threaded);
1093 srelax();
1095 srelax_rele();
1096 n_COLOUR( n_colour_env_gut(); )
1098 if (obuf != n_stdout)
1099 n_pager_close(obuf);
1100 jleave:
1101 NYD_LEAVE;
1102 return 0;
1105 FL void
1106 print_headers(size_t bottom, size_t topx, bool_t only_marked)
1108 size_t printed;
1109 NYD_ENTER;
1111 #ifdef HAVE_IMAP
1112 if (mb.mb_type == MB_IMAP)
1113 imap_getheaders(bottom, topx);
1114 #endif
1115 time_current_update(&time_current, FAL0);
1117 n_COLOUR( n_colour_env_create(n_COLOUR_CTX_SUM, n_stdout, FAL0); )
1118 srelax_hold();
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 n_COLOUR( n_colour_env_gut(); )
1131 NYD_LEAVE;
1134 /* s-it-mode */