nail.1: last fixes
[s-mailx.git] / cmd-headers.c
blobc9c1a238c0736cc5609e256cb526d3f83151a4df
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 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 = 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) &&
177 (UICMP(64,datet, >, time_current.tc_time + n_DATE_SECSDAY) ||
178 #define _6M ((n_DATE_DAYSYEAR / 2) * n_DATE_SECSDAY)
179 UICMP(64,datet + _6M, <, time_current.tc_time))) {
180 #undef _6M
181 if ((datefmt = (i & 4) ? fp : NULL) == NULL) {
182 memset(datebuf, ' ', n_FROM_DATEBUF); /* xxx ur */
183 memcpy(datebuf + 4, date + 4, 7);
184 datebuf[4 + 7] = ' ';
185 memcpy(datebuf + 4 + 7 + 1, date + 20, 4);
186 datebuf[4 + 7 + 1 + 4] = '\0';
187 date = datebuf;
189 n_COLOUR( colo_tag = n_COLOUR_TAG_SUM_OLDER; )
190 } else if ((i & 1) == 0)
191 datefmt = NULL;
192 } else if (datet == (time_t)0 && !(mp->m_flag & MNOFROM)) {
193 /* TODO eliminate this path, query the FROM_ date in setptr(),
194 * TODO all other codepaths do so by themselves ALREADY ?????
195 * TODO assert(mp->m_time != 0);, then
196 * TODO ALSO changes behaviour of datefield_markout_older */
197 _parse_from_(mp, datebuf);
198 date = datebuf;
199 } else
200 date = fakedate(datet);
202 flags |= _ISADDR;
203 name = name1(mp, 0);
204 if (name != NULL && ok_blook(showto) && n_is_myname(skin(name))) {
205 if ((cp = hfield1("to", mp)) != NULL) {
206 name = cp;
207 flags |= _ISTO;
210 if (name == NULL) {
211 name = n_empty;
212 flags &= ~_ISADDR;
214 if (flags & _ISADDR)
215 name = ok_blook(showname) ? realname(name) : prstr(skin(name));
217 subjline = NULL;
219 /* Detect the width of the non-format characters in *headline*;
220 * like that we can simply use putc() in the next loop, since we have
221 * already calculated their column widths (TODO it's sick) */
222 wleft = subjlen = n_scrnwidth;
224 for (fp = fmt; *fp != '\0'; ++fp) {
225 if (*fp == '%') {
226 if (*++fp == '-')
227 ++fp;
228 else if (*fp == '+')
229 ++fp;
230 if (digitchar(*fp)) {
231 n = 0;
233 n = 10*n + *fp - '0';
234 while (++fp, digitchar(*fp));
235 subjlen -= n;
237 if (*fp == 'i')
238 flags |= _IFMT;
240 if (*fp == '\0')
241 break;
242 } else {
243 #ifdef HAVE_WCWIDTH
244 if (n_mb_cur_max > 1) {
245 wchar_t wc;
246 if ((s = mbtowc(&wc, fp, n_mb_cur_max)) == -1)
247 n = s = 1;
248 else if ((n = wcwidth(wc)) == -1)
249 n = 1;
250 } else
251 #endif
252 n = s = 1;
253 subjlen -= n;
254 wleft -= n;
255 while (--s > 0)
256 ++fp;
260 /* Walk *headline*, producing output TODO not (really) MB safe */
261 #ifdef HAVE_COLOUR
262 if(n_COLOUR_IS_ACTIVE()){
263 if(flags & _ISDOT)
264 colo_tag = n_COLOUR_TAG_SUM_DOT;
265 cpen_bas = n_colour_pen_create(n_COLOUR_ID_SUM_HEADER, colo_tag);
266 n_colour_pen_put(cpen_new = cpen_cur = cpen_bas);
267 }else
268 cpen_new = cpen_bas = cpen_cur = NULL;
269 #endif
271 for (fp = fmt; *fp != '\0'; ++fp) {
272 char c;
274 if ((c = *fp & 0xFF) != '%') {
275 n_COLOUR(
276 if(n_COLOUR_IS_ACTIVE() && (cpen_new = cpen_bas) != cpen_cur)
277 n_colour_pen_put(cpen_cur = cpen_new);
279 putc(c, f);
280 continue;
283 flags &= _LOOP_MASK;
284 n = 0;
285 s = 1;
286 if ((c = *++fp) == '-') {
287 s = -1;
288 ++fp;
289 } else if (c == '+')
290 ++fp;
291 if (digitchar(*fp)) {
293 n = 10*n + *fp - '0';
294 while (++fp, digitchar(*fp));
297 if ((c = *fp & 0xFF) == '\0')
298 break;
299 n *= s;
301 cbuf[1] = '\0';
302 switch (c) {
303 case '%':
304 goto jputcb;
305 case '>':
306 case '<':
307 if (flags & _ISDOT) {
308 n_COLOUR(
309 if(n_COLOUR_IS_ACTIVE())
310 cpen_new = n_colour_pen_create(n_COLOUR_ID_SUM_DOTMARK,
311 colo_tag);
313 if (n_psonce & n_PSO_UNICODE) {
314 if (c == '>')
315 /* 25B8;BLACK RIGHT-POINTING SMALL TRIANGLE: â–¸ */
316 cbuf[1] = (char)0x96, cbuf[2] = (char)0xB8;
317 else
318 /* 25C2;BLACK LEFT-POINTING SMALL TRIANGLE: â—‚ */
319 cbuf[1] = (char)0x97, cbuf[2] = (char)0x82;
320 c = (char)0xE2;
321 cbuf[3] = '\0';
322 flags |= 2 << _PUTCB_UTF8_SHIFT;
324 } else
325 c = ' ';
326 goto jputcb;
327 case '$':
328 #ifdef HAVE_SPAM
329 if (n == 0)
330 n = 5;
331 if (UICMP(32, n_ABS(n), >, wleft))
332 n = (n < 0) ? -wleft : wleft;
333 snprintf(buf, sizeof buf, "%u.%02u",
334 (mp->m_spamscore >> 8), (mp->m_spamscore & 0xFF));
335 n = fprintf(f, "%*s", n, buf);
336 wleft = (n >= 0) ? wleft - n : 0;
337 break;
338 #else
339 c = '?';
340 goto jputcb;
341 #endif
342 case 'a':
343 c = _dispc(mp, attrlist);
344 jputcb:
345 #ifdef HAVE_COLOUR
346 if(n_COLOUR_IS_ACTIVE()){
347 if(cpen_new == cpen_cur)
348 cpen_new = cpen_bas;
349 if(cpen_new != cpen_cur)
350 n_colour_pen_put(cpen_cur = cpen_new);
352 #endif
353 if (UICMP(32, n_ABS(n), >, wleft))
354 n = (n < 0) ? -wleft : wleft;
355 cbuf[0] = c;
356 n = fprintf(f, "%*s", n, cbuf);
357 if (n >= 0) {
358 wleft -= n;
359 if ((n = (flags & _PUTCB_UTF8_MASK)) != 0) {
360 n >>= _PUTCB_UTF8_SHIFT;
361 wleft += n;
363 } else {
364 wleft = 0; /* TODO I/O error.. ? break? */
366 #ifdef HAVE_COLOUR
367 if(n_COLOUR_IS_ACTIVE() && (cpen_new = cpen_bas) != cpen_cur)
368 n_colour_pen_put(cpen_cur = cpen_new);
369 #endif
370 break;
371 case 'd':
372 if (datefmt != NULL) {
373 i = strftime(datebuf, sizeof datebuf, datefmt,
374 &time_current.tc_local);
375 if (i != 0)
376 date = datebuf;
377 else
378 n_err(_("Ignored date format, it excesses the target "
379 "buffer (%lu bytes)\n"), (ul_i)sizeof(datebuf));
380 datefmt = NULL;
382 if (n == 0)
383 n = 16;
384 if (UICMP(32, n_ABS(n), >, wleft))
385 n = (n < 0) ? -wleft : wleft;
386 n = fprintf(f, "%*.*s", n, n_ABS(n), date);
387 wleft = (n >= 0) ? wleft - n : 0;
388 break;
389 case 'e':
390 if (n == 0)
391 n = 2;
392 if (UICMP(32, n_ABS(n), >, wleft))
393 n = (n < 0) ? -wleft : wleft;
394 n = fprintf(f, "%*u", n, (threaded == 1 ? mp->m_level : 0));
395 wleft = (n >= 0) ? wleft - n : 0;
396 break;
397 case 'f':
398 if (n == 0) {
399 n = 18;
400 if (s < 0)
401 n = -n;
403 i = n_ABS(n);
404 if (i > wleft) {
405 i = wleft;
406 n = (n < 0) ? -wleft : wleft;
408 if (flags & _ISTO) /* XXX tr()! */
409 i -= 3;
410 n = fprintf(f, "%s%s", ((flags & _ISTO) ? "To " : n_empty),
411 colalign(name, i, n, &wleft));
412 if (n < 0)
413 wleft = 0;
414 else if (flags & _ISTO)
415 wleft -= 3;
416 break;
417 case 'i':
418 if (threaded) {
419 #ifdef HAVE_COLOUR
420 if(n_COLOUR_IS_ACTIVE()){
421 cpen_new = n_colour_pen_create(n_COLOUR_ID_SUM_THREAD, colo_tag);
422 if(cpen_new != cpen_cur)
423 n_colour_pen_put(cpen_cur = cpen_new);
425 #endif
426 n = __putindent(f, mp, n_MIN(wleft, (int)n_scrnwidth - 60));
427 wleft = (n >= 0) ? wleft - n : 0;
428 #ifdef HAVE_COLOUR
429 if(n_COLOUR_IS_ACTIVE() && (cpen_new = cpen_bas) != cpen_cur)
430 n_colour_pen_put(cpen_cur = cpen_new);
431 #endif
433 break;
434 case 'l':
435 if (n == 0)
436 n = 4;
437 if (UICMP(32, n_ABS(n), >, wleft))
438 n = (n < 0) ? -wleft : wleft;
439 if (mp->m_xlines) {
440 n = fprintf(f, "%*ld", n, mp->m_xlines);
441 wleft = (n >= 0) ? wleft - n : 0;
442 } else {
443 n = n_ABS(n);
444 wleft -= n;
445 while (n-- != 0)
446 putc(' ', f);
448 break;
449 case 'm':
450 if (n == 0) {
451 n = 3;
452 if (threaded)
453 for (i = msgCount; i > 999; i /= 10)
454 ++n;
456 if (UICMP(32, n_ABS(n), >, wleft))
457 n = (n < 0) ? -wleft : wleft;
458 n = fprintf(f, "%*lu", n, (ul_i)msgno);
459 wleft = (n >= 0) ? wleft - n : 0;
460 break;
461 case 'o':
462 if (n == 0)
463 n = -5;
464 if (UICMP(32, n_ABS(n), >, wleft))
465 n = (n < 0) ? -wleft : wleft;
466 n = fprintf(f, "%*lu", n, (ul_i)mp->m_xsize);
467 wleft = (n >= 0) ? wleft - n : 0;
468 break;
469 case 'S':
470 flags |= _SFMT;
471 /*FALLTHRU*/
472 case 's':
473 if (n == 0)
474 n = subjlen - 2;
475 if (n > 0 && s < 0)
476 n = -n;
477 if (subjlen > wleft)
478 subjlen = wleft;
479 if (UICMP(32, n_ABS(n), >, subjlen))
480 n = (n < 0) ? -subjlen : subjlen;
481 if (flags & _SFMT)
482 n -= (n < 0) ? -2 : 2;
483 if (n == 0)
484 break;
485 if (subjline == NULL)
486 subjline = __subject(mp, (threaded && (flags & _IFMT)), yetprinted);
487 if (subjline == (char*)-1) {
488 n = fprintf(f, "%*s", n, n_empty);
489 wleft = (n >= 0) ? wleft - n : 0;
490 } else {
491 n = fprintf(f, ((flags & _SFMT) ? "\"%s\"" : "%s"),
492 colalign(subjline, n_ABS(n), n, &wleft));
493 if (n < 0)
494 wleft = 0;
496 break;
497 case 'T': { /* Message recipient flags */
498 /* We never can reuse "name" since it's the full name */
499 struct name const *np = lextract(hfield1("to", mp), GTO | GSKIN);
500 c = ' ';
501 i = 0;
502 j_A_redo:
503 for (; np != NULL; np = np->n_flink) {
504 switch (is_mlist(np->n_name, FAL0)) {
505 case MLIST_SUBSCRIBED: c = 'S'; goto jputcb;
506 case MLIST_KNOWN: c = 'L'; goto jputcb;
507 case MLIST_OTHER:
508 default: break;
511 if (i != 0)
512 goto jputcb;
513 ++i;
514 np = lextract(hfield1("cc", mp), GCC | GSKIN);
515 goto j_A_redo;
517 case 't':
518 if (n == 0) {
519 n = 3;
520 if (threaded)
521 for (i = msgCount; i > 999; i /= 10)
522 ++n;
524 if (UICMP(32, n_ABS(n), >, wleft))
525 n = (n < 0) ? -wleft : wleft;
526 n = fprintf(f, "%*lu",
527 n, (threaded ? (ul_i)mp->m_threadpos : (ul_i)msgno));
528 wleft = (n >= 0) ? wleft - n : 0;
529 break;
530 case 'U':
531 #ifdef HAVE_IMAP
532 if (n == 0)
533 n = 9;
534 if (UICMP(32, n_ABS(n), >, wleft))
535 n = (n < 0) ? -wleft : wleft;
536 n = fprintf(f, "%*lu", n, mp->m_uid);
537 wleft = (n >= 0) ? wleft - n : 0;
538 break;
539 #else
540 c = '?';
541 goto jputcb;
542 #endif
543 default:
544 if (n_poption & n_PO_D_V)
545 n_err(_("Unkown *headline* format: %%%c\n"), c);
546 c = '?';
547 goto jputcb;
550 if (wleft <= 0)
551 break;
554 n_COLOUR( n_colour_reset(); )
555 putc('\n', f);
557 if (subjline != NULL && subjline != (char*)-1)
558 free(subjline);
559 NYD_LEAVE;
562 static char *
563 __subject(struct message *mp, bool_t threaded, size_t yetprinted)
565 struct str in, out;
566 char *rv, *ms;
567 NYD_ENTER;
569 rv = (char*)-1;
571 if ((ms = hfield1("subject", mp)) == NULL)
572 goto jleave;
574 in.l = strlen(in.s = ms);
575 mime_fromhdr(&in, &out, TD_ICONV | TD_ISPR);
576 rv = ms = out.s;
578 if (!threaded || mp->m_level == 0)
579 goto jleave;
581 /* In a display thread - check whether this message uses the same
582 * Subject: as it's parent or elder neighbour, suppress printing it if
583 * this is the case. To extend this a bit, ignore any leading Re: or
584 * Fwd: plus follow-up WS. Ignore invisible messages along the way */
585 ms = n_UNCONST(subject_re_trim(n_UNCONST(ms)));
587 for (; (mp = prev_in_thread(mp)) != NULL && yetprinted-- > 0;) {
588 char *os;
590 if (visible(mp) && (os = hfield1("subject", mp)) != NULL) {
591 struct str oout;
592 int x;
594 in.l = strlen(in.s = os);
595 mime_fromhdr(&in, &oout, TD_ICONV | TD_ISPR);
596 x = asccasecmp(ms, subject_re_trim(oout.s));
597 free(oout.s);
599 if (!x) {
600 free(out.s);
601 rv = (char*)-1;
603 break;
606 jleave:
607 NYD_LEAVE;
608 return rv;
611 static int
612 __putindent(FILE *fp, struct message *mp, int maxwidth)/* XXX no magic consts */
614 struct message *mq;
615 int *us, indlvl, indw, i, important = MNEW | MFLAGGED;
616 char *cs;
617 NYD_ENTER;
619 if (mp->m_level == 0 || maxwidth == 0) {
620 indw = 0;
621 goto jleave;
624 cs = ac_alloc(mp->m_level);
625 us = ac_alloc(mp->m_level * sizeof *us);
627 i = mp->m_level - 1;
628 if (mp->m_younger && UICMP(32, i + 1, ==, mp->m_younger->m_level)) {
629 if (mp->m_parent && mp->m_parent->m_flag & important)
630 us[i] = mp->m_flag & important ? 0x2523 : 0x2520;
631 else
632 us[i] = mp->m_flag & important ? 0x251D : 0x251C;
633 cs[i] = '+';
634 } else {
635 if (mp->m_parent && mp->m_parent->m_flag & important)
636 us[i] = mp->m_flag & important ? 0x2517 : 0x2516;
637 else
638 us[i] = mp->m_flag & important ? 0x2515 : 0x2514;
639 cs[i] = '\\';
642 mq = mp->m_parent;
643 for (i = mp->m_level - 2; i >= 0; --i) {
644 if (mq) {
645 if (UICMP(32, i, >, mq->m_level - 1)) {
646 us[i] = cs[i] = ' ';
647 continue;
649 if (mq->m_younger) {
650 if (mq->m_parent && (mq->m_parent->m_flag & important))
651 us[i] = 0x2503;
652 else
653 us[i] = 0x2502;
654 cs[i] = '|';
655 } else
656 us[i] = cs[i] = ' ';
657 mq = mq->m_parent;
658 } else
659 us[i] = cs[i] = ' ';
662 --maxwidth;
663 for (indlvl = indw = 0; (ui8_t)indlvl < mp->m_level && indw < maxwidth;
664 ++indlvl) {
665 if (indw < maxwidth - 1)
666 indw += (int)putuc(us[indlvl], cs[indlvl] & 0xFF, fp);
667 else
668 indw += (int)putuc(0x21B8, '^', fp);
670 indw += putuc(0x25B8, '>', fp);
672 ac_free(us);
673 ac_free(cs);
674 jleave:
675 NYD_LEAVE;
676 return indw;
679 static int
680 _dispc(struct message *mp, char const *a)
682 int i = ' ';
683 NYD_ENTER;
685 if ((mp->m_flag & (MREAD | MNEW)) == MREAD)
686 i = a[3];
687 if ((mp->m_flag & (MREAD | MNEW)) == (MREAD | MNEW))
688 i = a[2];
689 if (mp->m_flag & MANSWERED)
690 i = a[8];
691 if (mp->m_flag & MDRAFTED)
692 i = a[9];
693 if ((mp->m_flag & (MREAD | MNEW)) == MNEW)
694 i = a[0];
695 if (!(mp->m_flag & (MREAD | MNEW)))
696 i = a[1];
697 if (mp->m_flag & MSPAM)
698 i = a[12];
699 if (mp->m_flag & MSPAMUNSURE)
700 i = a[13];
701 if (mp->m_flag & MSAVED)
702 i = a[4];
703 if (mp->m_flag & MPRESERVE)
704 i = a[5];
705 if (mp->m_flag & (MBOX | MBOXED))
706 i = a[6];
707 if (mp->m_flag & MFLAGGED)
708 i = a[7];
709 if (mb.mb_threaded == 1 && mp->m_collapsed > 0)
710 i = a[11];
711 if (mb.mb_threaded == 1 && mp->m_collapsed < 0)
712 i = a[10];
713 NYD_LEAVE;
714 return i;
717 static int
718 a_cmd_scroll(char const *arg, bool_t onlynew){
719 siz_t l;
720 bool_t isabs;
721 int msgspec, size, maxs;
722 NYD2_ENTER;
724 /* TODO scroll problem: we do not know whether + and $ have already reached
725 * TODO the last screen in threaded mode */
726 msgspec = onlynew ? -1 : 0;
727 size = (int)/*TODO*/n_screensize();
728 if((maxs = msgCount / size) > 0 && msgCount % size == 0)
729 --maxs;
731 if(arg == NULL)
732 arg = n_empty;
733 switch(*arg){
734 case '\0':
735 ++_screen;
736 goto jfwd;
737 case '^':
738 if(arg[1] != '\0')
739 goto jerr;
740 if(_screen == 0)
741 goto jerrbwd;
742 _screen = 0;
743 break;
744 case '$':
745 if(arg[1] != '\0')
746 goto jerr;
747 if(_screen == maxs)
748 goto jerrfwd;
749 _screen = maxs;
750 break;
751 case '+':
752 if(arg[1] == '\0')
753 ++_screen;
754 else{
755 isabs = FAL0;
757 ++arg;
758 if(0){
759 case '1': case '2': case '3': case '4': case '5':
760 case '6': case '7': case '8': case '9': case '0':
761 isabs = TRU1;
763 if((n_idec_siz_cp(&l, arg, 0, NULL
764 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
765 ) != n_IDEC_STATE_CONSUMED)
766 goto jerr;
767 if(l > maxs - (isabs ? 0 : _screen))
768 goto jerrfwd;
769 _screen = isabs ? (int)l : _screen + l;
771 jfwd:
772 if(_screen > maxs){
773 jerrfwd:
774 _screen = maxs;
775 fprintf(n_stdout, _("On last screenful of messages\n"));
777 break;
779 case '-':
780 if(arg[1] == '\0')
781 --_screen;
782 else{
783 if((n_idec_siz_cp(&l, ++arg, 0, NULL
784 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
785 ) != n_IDEC_STATE_CONSUMED)
786 goto jerr;
787 if(l > _screen)
788 goto jerrbwd;
789 _screen -= l;
791 if(_screen < 0){
792 jerrbwd:
793 _screen = 0;
794 fprintf(n_stdout, _("On first screenful of messages\n"));
796 if(msgspec == -1)
797 msgspec = -2;
798 break;
799 default:
800 jerr:
801 n_err(_("Unrecognized scrolling command: %s\n"), arg);
802 size = 1;
803 goto jleave;
806 size = _headers(msgspec);
807 jleave:
808 NYD2_LEAVE;
809 return size;
812 static int
813 _headers(int msgspec) /* TODO rework v15 */
815 ui32_t volatile flag;
816 int g, k, mesg, size;
817 int volatile lastg = 1;
818 struct message *mp, *mq, *lastmq = NULL;
819 enum mflag fl = MNEW | MFLAGGED;
820 NYD_ENTER;
822 time_current_update(&time_current, FAL0);
823 flag = 0;
825 size = (int)/*TODO*/n_screensize();
826 if (_screen < 0)
827 _screen = 0;
828 #if 0 /* FIXME original code path */
829 k = _screen * size;
830 #else
831 if (msgspec <= 0)
832 k = _screen * size;
833 else
834 k = msgspec;
835 #endif
836 if (k >= msgCount)
837 k = msgCount - size;
838 if (k < 0)
839 k = 0;
841 if (mb.mb_threaded == 0) {
842 g = 0;
843 mq = message;
844 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
845 if (visible(mp)) {
846 if (g % size == 0)
847 mq = mp;
848 if (mp->m_flag & fl) {
849 lastg = g;
850 lastmq = mq;
852 if ((msgspec > 0 && PTRCMP(mp, ==, message + msgspec - 1)) ||
853 (msgspec == 0 && g == k) ||
854 (msgspec == -2 && g == k + size && lastmq) ||
855 (msgspec < 0 && g >= k && (mp->m_flag & fl) != 0))
856 break;
857 g++;
859 if (lastmq && (msgspec == -2 ||
860 (msgspec == -1 && PTRCMP(mp, ==, message + msgCount)))) {
861 g = lastg;
862 mq = lastmq;
864 _screen = g / size;
866 mp = mq;
867 mesg = (int)PTR2SIZE(mp - message);
868 if (PTRCMP(dot, !=, message + msgspec - 1)) { /* TODO really?? */
869 for (mq = mp; PTRCMP(mq, <, message + msgCount); ++mq)
870 if (visible(mq)) {
871 setdot(mq);
872 break;
876 #ifdef HAVE_IMAP
877 if (mb.mb_type == MB_IMAP)
878 imap_getheaders(mesg + 1, mesg + size);
879 #endif
880 n_COLOUR( n_colour_env_create(n_COLOUR_CTX_SUM, n_stdout, FAL0); )
881 srelax_hold();
882 for (; PTRCMP(mp, <, message + msgCount); ++mp) {
883 ++mesg;
884 if (!visible(mp))
885 continue;
886 if (UICMP(32, flag++, >=, size))
887 break;
888 _print_head(0, mesg, n_stdout, 0);
889 srelax();
891 srelax_rele();
892 n_COLOUR( n_colour_env_gut(); )
893 } else { /* threaded */
894 g = 0;
895 mq = threadroot;
896 for (mp = threadroot; mp; mp = next_in_thread(mp))
897 if (visible(mp) &&
898 (mp->m_collapsed <= 0 ||
899 PTRCMP(mp, ==, message + msgspec - 1))) {
900 if (g % size == 0)
901 mq = mp;
902 if (mp->m_flag & fl) {
903 lastg = g;
904 lastmq = mq;
906 if ((msgspec > 0 && PTRCMP(mp, ==, message + msgspec - 1)) ||
907 (msgspec == 0 && g == k) ||
908 (msgspec == -2 && g == k + size && lastmq) ||
909 (msgspec < 0 && g >= k && (mp->m_flag & fl) != 0))
910 break;
911 g++;
913 if (lastmq && (msgspec == -2 ||
914 (msgspec == -1 && PTRCMP(mp, ==, message + msgCount)))) {
915 g = lastg;
916 mq = lastmq;
918 _screen = g / size;
919 mp = mq;
920 if (PTRCMP(dot, !=, message + msgspec - 1)) { /* TODO really?? */
921 for (mq = mp; mq; mq = next_in_thread(mq))
922 if (visible(mq) && mq->m_collapsed <= 0) {
923 setdot(mq);
924 break;
928 n_COLOUR( n_colour_env_create(n_COLOUR_CTX_SUM, n_stdout, FAL0); )
929 srelax_hold();
930 while (mp) {
931 if (visible(mp) &&
932 (mp->m_collapsed <= 0 ||
933 PTRCMP(mp, ==, message + msgspec - 1))) {
934 if (UICMP(32, flag++, >=, size))
935 break;
936 _print_head(flag - 1, PTR2SIZE(mp - message + 1), n_stdout,
937 mb.mb_threaded);
938 srelax();
940 mp = next_in_thread(mp);
942 srelax_rele();
943 n_COLOUR( n_colour_env_gut(); )
946 if (!flag) {
947 fprintf(n_stdout, _("No more mail.\n"));
948 if (n_pstate & (n_PS_ROBOT | n_PS_HOOK_MASK))
949 flag = !flag;
951 NYD_LEAVE;
952 return !flag;
955 FL int
956 c_headers(void *v)
958 int rv;
959 NYD_ENTER;
961 rv = print_header_group((int*)v);
962 NYD_LEAVE;
963 return rv;
966 FL int
967 print_header_group(int *vector)
969 int rv;
970 NYD_ENTER;
972 assert(vector != NULL && vector != (void*)-1);
973 rv = _headers(vector[0]);
974 NYD_LEAVE;
975 return rv;
978 FL int
979 c_scroll(void *v)
981 int rv;
982 NYD_ENTER;
984 rv = a_cmd_scroll(*(char const**)v, FAL0);
985 NYD_LEAVE;
986 return rv;
989 FL int
990 c_Scroll(void *v)
992 int rv;
993 NYD_ENTER;
995 rv = a_cmd_scroll(*(char const**)v, TRU1);
996 NYD_LEAVE;
997 return rv;
1000 FL int
1001 c_dotmove(void *v)
1003 char const *args;
1004 int msgvec[2], rv;
1005 NYD_ENTER;
1007 if (*(args = v) == '\0' || args[1] != '\0') {
1008 jerr:
1009 n_err(_("Synopsis: dotmove: up <-> or down <+> by one message\n"));
1010 rv = 1;
1011 } else switch (args[0]) {
1012 case '-':
1013 case '+':
1014 if (msgCount == 0) {
1015 fprintf(n_stdout, _("At EOF\n"));
1016 rv = 0;
1017 } else if (getmsglist(n_UNCONST(/*TODO*/ args), msgvec, 0) > 0) {
1018 setdot(message + msgvec[0] - 1);
1019 msgvec[1] = 0;
1020 rv = c_headers(msgvec);
1021 } else
1022 rv = 1;
1023 break;
1024 default:
1025 goto jerr;
1027 NYD_LEAVE;
1028 return rv;
1031 FL int
1032 c_from(void *v)
1034 int *msgvec = v, *ip, n;
1035 char *cp;
1036 FILE * volatile obuf;
1037 NYD_ENTER;
1039 time_current_update(&time_current, FAL0);
1041 obuf = n_stdout;
1043 if (n_psonce & n_PSO_INTERACTIVE) {
1044 if ((cp = ok_vlook(crt)) != NULL) {
1045 uiz_t ib;
1047 for (n = 0, ip = msgvec; *ip != 0; ++ip)
1048 ++n;
1050 if(*cp == '\0')
1051 ib = n_screensize();
1052 else
1053 n_idec_uiz_cp(&ib, cp, 0, NULL);
1054 if (UICMP(z, n, >, ib) && (obuf = n_pager_open()) == NULL)
1055 obuf = n_stdout;
1059 /* Update dot before display so that the dotmark etc. are correct */
1060 for (ip = msgvec; *ip != 0; ++ip)
1062 if (--ip >= msgvec)
1063 setdot(message + *ip - 1);
1065 n_COLOUR( n_colour_env_create(n_COLOUR_CTX_SUM, obuf, obuf != n_stdout); )
1066 srelax_hold();
1067 for (n = 0, ip = msgvec; *ip != 0; ++ip) { /* TODO join into _print_head() */
1068 _print_head((size_t)n++, (size_t)*ip, obuf, mb.mb_threaded);
1069 srelax();
1071 srelax_rele();
1072 n_COLOUR( n_colour_env_gut(); )
1074 if (obuf != n_stdout)
1075 n_pager_close(obuf);
1076 NYD_LEAVE;
1077 return 0;
1080 FL void
1081 print_headers(size_t bottom, size_t topx, bool_t only_marked)
1083 size_t printed;
1084 NYD_ENTER;
1086 #ifdef HAVE_IMAP
1087 if (mb.mb_type == MB_IMAP)
1088 imap_getheaders(bottom, topx);
1089 #endif
1090 time_current_update(&time_current, FAL0);
1092 n_COLOUR( n_colour_env_create(n_COLOUR_CTX_SUM, n_stdout, FAL0); )
1093 srelax_hold();
1094 for (printed = 0; bottom <= topx; ++bottom) {
1095 struct message *mp = message + bottom - 1;
1096 if (only_marked) {
1097 if (!(mp->m_flag & MMARK))
1098 continue;
1099 } else if (!visible(mp))
1100 continue;
1101 _print_head(printed++, bottom, n_stdout, FAL0);
1102 srelax();
1104 srelax_rele();
1105 n_COLOUR( n_colour_env_gut(); )
1106 NYD_LEAVE;
1109 /* s-it-mode */