nail.1: mail,reply,etc.: some errors depend on *expandaddr*
[s-mailx.git] / cmd-headers.c
blobb4a9cb49ecacb5d2855fed8737cc3e07841df3e5
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 /* Print out the header of a specific message.
45 * time_current must be up-to-date when this is called.
46 * a_cmd__hprf: handle *headline*
47 * a_cmd__subject: -1 if Subject: yet seen, otherwise n_alloc()d Subject:
48 * a_cmd__putindent: print out the indenting in threaded display
49 * a_cmd__putuc: print out a Unicode character or a substitute for it, return
50 * 0 on error or wcwidth() (or 1) on success */
51 static void a_cmd_print_head(size_t yetprinted, size_t msgno, FILE *f,
52 bool_t threaded, bool_t subject_thread_compress);
54 static void a_cmd__hprf(size_t yetprinted, char const *fmt, size_t msgno,
55 FILE *f, bool_t threaded, bool_t subject_thread_compress,
56 char const *attrlist);
57 static char *a_cmd__subject(struct message *mp, bool_t threaded,
58 bool_t subject_thread_compress, size_t yetprinted);
59 static int a_cmd__putindent(FILE *fp, struct message *mp, int maxwidth);
60 static size_t a_cmd__putuc(int u, int c, FILE *fp);
61 static int a_cmd__dispc(struct message *mp, char const *a);
63 /* Shared `z' implementation */
64 static int a_cmd_scroll(char const *arg, bool_t onlynew);
66 /* Shared `headers' implementation */
67 static int _headers(int msgspec);
69 static void
70 a_cmd_print_head(size_t yetprinted, size_t msgno, FILE *f, bool_t threaded,
71 bool_t subject_thread_compress){
72 enum {attrlen = 14};
73 char attrlist[attrlen +1], *cp;
74 char const *fmt;
75 NYD2_ENTER;
77 if((cp = ok_vlook(attrlist)) != NULL){
78 if(strlen(cp) == attrlen){
79 memcpy(attrlist, cp, attrlen +1);
80 goto jattrok;
82 n_err(_("*attrlist* is not of the correct length, using built-in\n"));
85 if(ok_blook(bsdcompat) || ok_blook(bsdflags)){
86 char const bsdattr[attrlen +1] = "NU *HMFAT+-$~";
88 memcpy(attrlist, bsdattr, sizeof bsdattr);
89 }else if(ok_blook(SYSV3)){
90 char const bsdattr[attrlen +1] = "NU *HMFAT+-$~";
92 memcpy(attrlist, bsdattr, sizeof bsdattr);
93 n_OBSOLETE(_("*SYSV3*: please use *bsdcompat* or *bsdflags*, "
94 "or set *attrlist*"));
95 }else{
96 char const pattr[attrlen +1] = "NUROSPMFAT+-$~";
98 memcpy(attrlist, pattr, sizeof pattr);
101 jattrok:
102 if((fmt = ok_vlook(headline)) == NULL){
103 fmt = ((ok_blook(bsdcompat) || ok_blook(bsdheadline))
104 ? "%>%a%m %-20f %16d %4l/%-5o %i%-S"
105 : "%>%a%m %-18f %-16d %4l/%-5o %i%-s");
108 a_cmd__hprf(yetprinted, fmt, msgno, f, threaded, subject_thread_compress,
109 attrlist);
110 NYD2_LEAVE;
113 static void
114 a_cmd__hprf(size_t yetprinted, char const *fmt, size_t msgno, FILE *f,
115 bool_t threaded, bool_t subject_thread_compress, char const *attrlist)
117 char buf[16], cbuf[8], *cp, *subjline;
118 char const *date, *name, *fp, *color_tag;
119 int i, n, s, wleft, subjlen;
120 struct message *mp;
121 n_COLOUR( struct n_colour_pen *cpen_new COMMA *cpen_cur COMMA *cpen_bas; )
122 enum {
123 _NONE = 0,
124 _ISDOT = 1<<0,
125 _ISTO = 1<<1,
126 _IFMT = 1<<2,
127 _LOOP_MASK = (1<<4) - 1,
128 _SFMT = 1<<4, /* It is 'S' */
129 /* For the simple byte-based counts in wleft and n we sometimes need
130 * adjustments to compensate for additional bytes of UTF-8 sequences */
131 _PUTCB_UTF8_SHIFT = 5,
132 _PUTCB_UTF8_MASK = 3<<5
133 } flags = _NONE;
134 NYD2_ENTER;
135 n_UNUSED(buf);
137 if ((mp = message + msgno - 1) == dot)
138 flags = _ISDOT;
140 color_tag = NULL;
141 date = n_header_textual_date_info(mp, &color_tag);
142 /* C99 */{
143 bool_t isto;
145 n_header_textual_sender_info(mp, &cp, NULL, NULL, NULL, &isto);
146 name = cp;
147 if(isto)
148 flags |= _ISTO;
151 subjline = NULL;
153 /* Detect the width of the non-format characters in *headline*;
154 * like that we can simply use putc() in the next loop, since we have
155 * already calculated their column widths (TODO it's sick) */
156 wleft = subjlen = n_scrnwidth;
158 for (fp = fmt; *fp != '\0'; ++fp) {
159 if (*fp == '%') {
160 if (*++fp == '-')
161 ++fp;
162 else if (*fp == '+')
163 ++fp;
164 if (digitchar(*fp)) {
165 n = 0;
167 n = 10*n + *fp - '0';
168 while (++fp, digitchar(*fp));
169 subjlen -= n;
171 if (*fp == 'i')
172 flags |= _IFMT;
174 if (*fp == '\0')
175 break;
176 } else {
177 #ifdef HAVE_WCWIDTH
178 if (n_mb_cur_max > 1) {
179 wchar_t wc;
180 if ((s = mbtowc(&wc, fp, n_mb_cur_max)) == -1)
181 n = s = 1;
182 else if ((n = wcwidth(wc)) == -1)
183 n = 1;
184 } else
185 #endif
186 n = s = 1;
187 subjlen -= n;
188 wleft -= n;
189 while (--s > 0)
190 ++fp;
194 /* Walk *headline*, producing output TODO not (really) MB safe */
195 #ifdef HAVE_COLOUR
196 if(n_COLOUR_IS_ACTIVE()){
197 if(flags & _ISDOT)
198 color_tag = n_COLOUR_TAG_SUM_DOT;
199 cpen_bas = n_colour_pen_create(n_COLOUR_ID_SUM_HEADER, color_tag);
200 n_colour_pen_put(cpen_new = cpen_cur = cpen_bas);
201 }else
202 cpen_new = cpen_bas = cpen_cur = NULL;
203 #endif
205 for (fp = fmt; *fp != '\0'; ++fp) {
206 char c;
208 if ((c = *fp & 0xFF) != '%') {
209 n_COLOUR(
210 if(n_COLOUR_IS_ACTIVE() && (cpen_new = cpen_bas) != cpen_cur)
211 n_colour_pen_put(cpen_cur = cpen_new);
213 putc(c, f);
214 continue;
217 flags &= _LOOP_MASK;
218 n = 0;
219 s = 1;
220 if ((c = *++fp) == '-') {
221 s = -1;
222 ++fp;
223 } else if (c == '+')
224 ++fp;
225 if (digitchar(*fp)) {
227 n = 10*n + *fp - '0';
228 while (++fp, digitchar(*fp));
231 if ((c = *fp & 0xFF) == '\0')
232 break;
233 n *= s;
235 cbuf[1] = '\0';
236 switch (c) {
237 case '%':
238 goto jputcb;
239 case '>':
240 case '<':
241 if (flags & _ISDOT) {
242 n_COLOUR(
243 if(n_COLOUR_IS_ACTIVE())
244 cpen_new = n_colour_pen_create(n_COLOUR_ID_SUM_DOTMARK,
245 color_tag);
247 if((n_psonce & n_PSO_UNICODE) && !ok_blook(headline_plain)){
248 if (c == '>')
249 /* 25B8;BLACK RIGHT-POINTING SMALL TRIANGLE */
250 cbuf[1] = (char)0x96, cbuf[2] = (char)0xB8;
251 else
252 /* 25C2;BLACK LEFT-POINTING SMALL TRIANGLE */
253 cbuf[1] = (char)0x97, cbuf[2] = (char)0x82;
254 c = (char)0xE2;
255 cbuf[3] = '\0';
256 flags |= 2 << _PUTCB_UTF8_SHIFT;
258 } else
259 c = ' ';
260 goto jputcb;
261 case '$':
262 #ifdef HAVE_SPAM
263 if (n == 0)
264 n = 5;
265 if (UICMP(32, n_ABS(n), >, wleft))
266 n = (n < 0) ? -wleft : wleft;
267 snprintf(buf, sizeof buf, "%u.%02u",
268 (mp->m_spamscore >> 8), (mp->m_spamscore & 0xFF));
269 n = fprintf(f, "%*s", n, buf);
270 wleft = (n >= 0) ? wleft - n : 0;
271 break;
272 #else
273 c = '?';
274 goto jputcb;
275 #endif
276 case 'a':
277 c = a_cmd__dispc(mp, attrlist);
278 jputcb:
279 #ifdef HAVE_COLOUR
280 if(n_COLOUR_IS_ACTIVE()){
281 if(cpen_new == cpen_cur)
282 cpen_new = cpen_bas;
283 if(cpen_new != cpen_cur)
284 n_colour_pen_put(cpen_cur = cpen_new);
286 #endif
287 if (UICMP(32, n_ABS(n), >, wleft))
288 n = (n < 0) ? -wleft : wleft;
289 cbuf[0] = c;
290 n = fprintf(f, "%*s", n, cbuf);
291 if (n >= 0) {
292 wleft -= n;
293 if ((n = (flags & _PUTCB_UTF8_MASK)) != 0) {
294 n >>= _PUTCB_UTF8_SHIFT;
295 wleft += n;
297 } else {
298 wleft = 0; /* TODO I/O error.. ? break? */
300 #ifdef HAVE_COLOUR
301 if(n_COLOUR_IS_ACTIVE() && (cpen_new = cpen_bas) != cpen_cur)
302 n_colour_pen_put(cpen_cur = cpen_new);
303 #endif
304 break;
305 case 'd':
306 if (n == 0)
307 n = 16;
308 if (UICMP(32, n_ABS(n), >, wleft))
309 n = (n < 0) ? -wleft : wleft;
310 n = fprintf(f, "%*.*s", n, n_ABS(n), date);
311 wleft = (n >= 0) ? wleft - n : 0;
312 break;
313 case 'e':
314 if (n == 0)
315 n = 2;
316 if (UICMP(32, n_ABS(n), >, wleft))
317 n = (n < 0) ? -wleft : wleft;
318 n = fprintf(f, "%*u", n, (threaded == 1 ? mp->m_level : 0));
319 wleft = (n >= 0) ? wleft - n : 0;
320 break;
321 case 'f':
322 if (n == 0) {
323 n = 18;
324 if (s < 0)
325 n = -n;
327 i = n_ABS(n);
328 if (i > wleft) {
329 i = wleft;
330 n = (n < 0) ? -wleft : wleft;
332 if (flags & _ISTO) /* XXX tr()! */
333 i -= 3;
334 n = fprintf(f, "%s%s", ((flags & _ISTO) ? "To " : n_empty),
335 colalign(name, i, n, &wleft));
336 if (n < 0)
337 wleft = 0;
338 else if (flags & _ISTO)
339 wleft -= 3;
340 break;
341 case 'i':
342 if (threaded) {
343 #ifdef HAVE_COLOUR
344 if(n_COLOUR_IS_ACTIVE()){
345 cpen_new = n_colour_pen_create(n_COLOUR_ID_SUM_THREAD,
346 color_tag);
347 if(cpen_new != cpen_cur)
348 n_colour_pen_put(cpen_cur = cpen_new);
350 #endif
351 n = a_cmd__putindent(f, mp, n_MIN(wleft, (int)n_scrnwidth - 60));
352 wleft = (n >= 0) ? wleft - n : 0;
353 #ifdef HAVE_COLOUR
354 if(n_COLOUR_IS_ACTIVE() && (cpen_new = cpen_bas) != cpen_cur)
355 n_colour_pen_put(cpen_cur = cpen_new);
356 #endif
358 break;
359 case 'l':
360 if (n == 0)
361 n = 4;
362 if (UICMP(32, n_ABS(n), >, wleft))
363 n = (n < 0) ? -wleft : wleft;
364 if (mp->m_xlines) {
365 n = fprintf(f, "%*ld", n, mp->m_xlines);
366 wleft = (n >= 0) ? wleft - n : 0;
367 } else {
368 n = n_ABS(n);
369 wleft -= n;
370 while (n-- != 0)
371 putc(' ', f);
373 break;
374 case 'm':
375 if (n == 0) {
376 n = 3;
377 if (threaded)
378 for (i = msgCount; i > 999; i /= 10)
379 ++n;
381 if (UICMP(32, n_ABS(n), >, wleft))
382 n = (n < 0) ? -wleft : wleft;
383 n = fprintf(f, "%*lu", n, (ul_i)msgno);
384 wleft = (n >= 0) ? wleft - n : 0;
385 break;
386 case 'o':
387 if (n == 0)
388 n = -5;
389 if (UICMP(32, n_ABS(n), >, wleft))
390 n = (n < 0) ? -wleft : wleft;
391 n = fprintf(f, "%*lu", n, (ul_i)mp->m_xsize);
392 wleft = (n >= 0) ? wleft - n : 0;
393 break;
394 case 'S':
395 flags |= _SFMT;
396 /*FALLTHRU*/
397 case 's':
398 if (n == 0)
399 n = subjlen - 2;
400 if (n > 0 && s < 0)
401 n = -n;
402 if (subjlen > wleft)
403 subjlen = wleft;
404 if (UICMP(32, n_ABS(n), >, subjlen))
405 n = (n < 0) ? -subjlen : subjlen;
406 if (flags & _SFMT)
407 n -= (n < 0) ? -2 : 2;
408 if (n == 0)
409 break;
410 if (subjline == NULL)
411 subjline = a_cmd__subject(mp, (threaded && (flags & _IFMT)),
412 subject_thread_compress, yetprinted);
413 if (subjline == (char*)-1) {
414 n = fprintf(f, "%*s", n, n_empty);
415 wleft = (n >= 0) ? wleft - n : 0;
416 } else {
417 n = fprintf(f, ((flags & _SFMT) ? "\"%s\"" : "%s"),
418 colalign(subjline, n_ABS(n), n, &wleft));
419 if (n < 0)
420 wleft = 0;
422 break;
423 case 'T': /* Message recipient flags */
424 switch(is_mlist_mp(mp, MLIST_OTHER)){
425 case MLIST_OTHER: c = ' '; break;
426 case MLIST_KNOWN: c = 'l'; break;
427 case MLIST_SUBSCRIBED: c = 'L'; break;
429 goto jputcb;
430 case 't':
431 if (n == 0) {
432 n = 3;
433 if (threaded)
434 for (i = msgCount; i > 999; i /= 10)
435 ++n;
437 if (UICMP(32, n_ABS(n), >, wleft))
438 n = (n < 0) ? -wleft : wleft;
439 n = fprintf(f, "%*lu",
440 n, (threaded ? (ul_i)mp->m_threadpos : (ul_i)msgno));
441 wleft = (n >= 0) ? wleft - n : 0;
442 break;
443 case 'U':
444 #ifdef HAVE_IMAP
445 if (n == 0)
446 n = 9;
447 if (UICMP(32, n_ABS(n), >, wleft))
448 n = (n < 0) ? -wleft : wleft;
449 n = fprintf(f, "%*" PRIu64 , n, mp->m_uid);
450 wleft = (n >= 0) ? wleft - n : 0;
451 break;
452 #else
453 c = '0';
454 goto jputcb;
455 #endif
456 default:
457 if (n_poption & n_PO_D_V)
458 n_err(_("Unknown *headline* format: %%%c\n"), c);
459 c = '?';
460 goto jputcb;
463 if (wleft <= 0)
464 break;
467 n_COLOUR( n_colour_reset(); )
468 putc('\n', f);
470 if (subjline != NULL && subjline != (char*)-1)
471 n_free(subjline);
472 NYD2_LEAVE;
475 static char *
476 a_cmd__subject(struct message *mp, bool_t threaded,
477 bool_t subject_thread_compress, size_t yetprinted)
479 struct str in, out;
480 char *rv, *ms;
481 NYD2_ENTER;
483 rv = (char*)-1;
485 if ((ms = hfield1("subject", mp)) == NULL)
486 goto jleave;
488 in.l = strlen(in.s = ms);
489 mime_fromhdr(&in, &out, TD_ICONV | TD_ISPR);
490 rv = ms = out.s;
492 if (!threaded || !subject_thread_compress || mp->m_level == 0)
493 goto jleave;
495 /* In a display thread - check whether this message uses the same
496 * Subject: as it's parent or elder neighbour, suppress printing it if
497 * this is the case. To extend this a bit, ignore any leading Re: or
498 * Fwd: plus follow-up WS. Ignore invisible messages along the way */
499 ms = n_UNCONST(subject_re_trim(n_UNCONST(ms)));
501 for (; (mp = prev_in_thread(mp)) != NULL && yetprinted-- > 0;) {
502 char *os;
504 if (visible(mp) && (os = hfield1("subject", mp)) != NULL) {
505 struct str oout;
506 int x;
508 in.l = strlen(in.s = os);
509 mime_fromhdr(&in, &oout, TD_ICONV | TD_ISPR);
510 x = asccasecmp(ms, subject_re_trim(oout.s));
511 n_free(oout.s);
513 if (!x) {
514 n_free(out.s);
515 rv = (char*)-1;
517 break;
520 jleave:
521 NYD2_LEAVE;
522 return rv;
525 static int
526 a_cmd__putindent(FILE *fp, struct message *mp, int maxwidth)/* XXX magics */
528 struct message *mq;
529 int *us, indlvl, indw, i, important = MNEW | MFLAGGED;
530 char *cs;
531 NYD2_ENTER;
533 if (mp->m_level == 0 || maxwidth == 0) {
534 indw = 0;
535 goto jleave;
538 cs = n_lofi_alloc(mp->m_level);
539 us = n_lofi_alloc(mp->m_level * sizeof *us);
541 i = mp->m_level - 1;
542 if (mp->m_younger && UICMP(32, i + 1, ==, mp->m_younger->m_level)) {
543 if (mp->m_parent && mp->m_parent->m_flag & important)
544 us[i] = mp->m_flag & important ? 0x2523 : 0x2520;
545 else
546 us[i] = mp->m_flag & important ? 0x251D : 0x251C;
547 cs[i] = '+';
548 } else {
549 if (mp->m_parent && mp->m_parent->m_flag & important)
550 us[i] = mp->m_flag & important ? 0x2517 : 0x2516;
551 else
552 us[i] = mp->m_flag & important ? 0x2515 : 0x2514;
553 cs[i] = '\\';
556 mq = mp->m_parent;
557 for (i = mp->m_level - 2; i >= 0; --i) {
558 if (mq) {
559 if (UICMP(32, i, >, mq->m_level - 1)) {
560 us[i] = cs[i] = ' ';
561 continue;
563 if (mq->m_younger) {
564 if (mq->m_parent && (mq->m_parent->m_flag & important))
565 us[i] = 0x2503;
566 else
567 us[i] = 0x2502;
568 cs[i] = '|';
569 } else
570 us[i] = cs[i] = ' ';
571 mq = mq->m_parent;
572 } else
573 us[i] = cs[i] = ' ';
576 --maxwidth;
577 for (indlvl = indw = 0; (ui8_t)indlvl < mp->m_level && indw < maxwidth;
578 ++indlvl) {
579 if (indw < maxwidth - 1)
580 indw += (int)a_cmd__putuc(us[indlvl], cs[indlvl] & 0xFF, fp);
581 else
582 indw += (int)a_cmd__putuc(0x21B8, '^', fp);
584 indw += a_cmd__putuc(0x25B8, '>', fp);
586 n_lofi_free(us);
587 n_lofi_free(cs);
588 jleave:
589 NYD2_LEAVE;
590 return indw;
593 static size_t
594 a_cmd__putuc(int u, int c, FILE *fp){
595 size_t rv;
596 NYD2_ENTER;
597 n_UNUSED(u);
599 #ifdef HAVE_NATCH_CHAR
600 if((n_psonce & n_PSO_UNICODE) && (u & ~(wchar_t)0177) &&
601 !ok_blook(headline_plain)){
602 char mbb[MB_LEN_MAX];
603 int i, n;
605 if((n = wctomb(mbb, u)) > 0){
606 rv = wcwidth(u);
607 for(i = 0; i < n; ++i)
608 if(putc(mbb[i] & 0377, fp) == EOF){
609 rv = 0;
610 break;
612 }else if(n == 0)
613 rv = (putc('\0', fp) != EOF);
614 else
615 rv = 0;
616 }else
617 #endif
618 rv = (putc(c, fp) != EOF);
619 NYD2_LEAVE;
620 return rv;
623 static int
624 a_cmd__dispc(struct message *mp, char const *a)
626 int i = ' ';
627 NYD2_ENTER;
629 if ((mp->m_flag & (MREAD | MNEW)) == MREAD)
630 i = a[3];
631 if ((mp->m_flag & (MREAD | MNEW)) == (MREAD | MNEW))
632 i = a[2];
633 if (mp->m_flag & MANSWERED)
634 i = a[8];
635 if (mp->m_flag & MDRAFTED)
636 i = a[9];
637 if ((mp->m_flag & (MREAD | MNEW)) == MNEW)
638 i = a[0];
639 if (!(mp->m_flag & (MREAD | MNEW)))
640 i = a[1];
641 if (mp->m_flag & MSPAM)
642 i = a[12];
643 if (mp->m_flag & MSPAMUNSURE)
644 i = a[13];
645 if (mp->m_flag & MSAVED)
646 i = a[4];
647 if (mp->m_flag & MPRESERVE)
648 i = a[5];
649 if (mp->m_flag & (MBOX | MBOXED))
650 i = a[6];
651 if (mp->m_flag & MFLAGGED)
652 i = a[7];
653 if (mb.mb_threaded == 1 && mp->m_collapsed > 0)
654 i = a[11];
655 if (mb.mb_threaded == 1 && mp->m_collapsed < 0)
656 i = a[10];
657 NYD2_LEAVE;
658 return i;
661 static int
662 a_cmd_scroll(char const *arg, bool_t onlynew){
663 siz_t l;
664 bool_t isabs;
665 int msgspec, size, maxs;
666 NYD2_ENTER;
668 /* TODO scroll problem: we do not know whether + and $ have already reached
669 * TODO the last screen in threaded mode */
670 msgspec = onlynew ? -1 : 0;
671 size = (int)/*TODO*/n_screensize();
672 if((maxs = msgCount / size) > 0 && msgCount % size == 0)
673 --maxs;
675 if(arg == NULL)
676 arg = n_empty;
677 switch(*arg){
678 case '\0':
679 ++_screen;
680 goto jfwd;
681 case '^':
682 if(arg[1] != '\0')
683 goto jerr;
684 if(_screen == 0)
685 goto jerrbwd;
686 _screen = 0;
687 break;
688 case '$':
689 if(arg[1] != '\0')
690 goto jerr;
691 if(_screen == maxs)
692 goto jerrfwd;
693 _screen = maxs;
694 break;
695 case '+':
696 if(arg[1] == '\0')
697 ++_screen;
698 else{
699 isabs = FAL0;
701 ++arg;
702 if(0){
703 case '1': case '2': case '3': case '4': case '5':
704 case '6': case '7': case '8': case '9': case '0':
705 isabs = TRU1;
707 if((n_idec_siz_cp(&l, arg, 0, NULL
708 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
709 ) != n_IDEC_STATE_CONSUMED)
710 goto jerr;
711 if(l > maxs - (isabs ? 0 : _screen))
712 goto jerrfwd;
713 _screen = isabs ? (int)l : _screen + l;
715 jfwd:
716 if(_screen > maxs){
717 jerrfwd:
718 _screen = maxs;
719 fprintf(n_stdout, _("On last screenful of messages\n"));
721 break;
723 case '-':
724 if(arg[1] == '\0')
725 --_screen;
726 else{
727 if((n_idec_siz_cp(&l, ++arg, 0, NULL
728 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
729 ) != n_IDEC_STATE_CONSUMED)
730 goto jerr;
731 if(l > _screen)
732 goto jerrbwd;
733 _screen -= l;
735 if(_screen < 0){
736 jerrbwd:
737 _screen = 0;
738 fprintf(n_stdout, _("On first screenful of messages\n"));
740 if(msgspec == -1)
741 msgspec = -2;
742 break;
743 default:
744 jerr:
745 n_err(_("Unrecognized scrolling command: %s\n"), arg);
746 size = 1;
747 goto jleave;
750 size = _headers(msgspec);
751 jleave:
752 NYD2_LEAVE;
753 return size;
756 static int
757 _headers(int msgspec) /* TODO rework v15 */
759 bool_t needdot, showlast;
760 int g, k, mesg, size;
761 struct message *lastmq, *mp, *mq;
762 int volatile lastg;
763 ui32_t volatile flag;
764 enum mflag fl;
765 NYD_ENTER;
767 time_current_update(&time_current, FAL0);
769 fl = MNEW | MFLAGGED;
770 flag = 0;
771 lastg = 1;
772 lastmq = NULL;
774 size = (int)/*TODO*/n_screensize();
775 if (_screen < 0)
776 _screen = 0;
777 #if 0 /* FIXME original code path */
778 k = _screen * size;
779 #else
780 if (msgspec <= 0)
781 k = _screen * size;
782 else
783 k = msgspec;
784 #endif
785 if (k >= msgCount)
786 k = msgCount - size;
787 if (k < 0)
788 k = 0;
790 needdot = (msgspec <= 0) ? TRU1 : (dot != &message[msgspec - 1]);
791 showlast = ok_blook(showlast);
793 if (mb.mb_threaded == 0) {
794 g = 0;
795 mq = message;
796 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
797 if (visible(mp)) {
798 if (g % size == 0)
799 mq = mp;
800 if (mp->m_flag & fl) {
801 lastg = g;
802 lastmq = mq;
804 if ((msgspec > 0 && PTRCMP(mp, ==, message + msgspec - 1)) ||
805 (msgspec == 0 && g == k) ||
806 (msgspec == -2 && g == k + size && lastmq) ||
807 (msgspec < 0 && g >= k && (mp->m_flag & fl) != 0))
808 break;
809 g++;
811 if (lastmq && (msgspec == -2 ||
812 (msgspec == -1 && PTRCMP(mp, ==, message + msgCount)))) {
813 g = lastg;
814 mq = lastmq;
816 _screen = g / size;
817 mp = mq;
819 mesg = (int)PTR2SIZE(mp - message);
820 #ifdef HAVE_IMAP
821 if (mb.mb_type == MB_IMAP)
822 imap_getheaders(mesg + 1, mesg + size);
823 #endif
824 n_COLOUR( n_colour_env_create(n_COLOUR_CTX_SUM, n_stdout, FAL0); )
825 srelax_hold();
826 for(lastmq = NULL, mq = &message[msgCount]; mp < mq; lastmq = mp, ++mp){
827 ++mesg;
828 if (!visible(mp))
829 continue;
830 if (UICMP(32, flag, >=, size))
831 break;
832 if(needdot){
833 if(showlast){
834 if(UICMP(32, flag, ==, size - 1) || &mp[1] == mq)
835 goto jdot_unsort;
836 }else if(flag == 0){
837 jdot_unsort:
838 needdot = FAL0;
839 setdot(mp);
842 ++flag;
843 a_cmd_print_head(0, mesg, n_stdout, FAL0, FAL0);
844 srelax();
846 if(needdot && ok_blook(showlast)) /* xxx will not show */
847 setdot(lastmq);
848 srelax_rele();
849 n_COLOUR( n_colour_env_gut(); )
850 } else { /* threaded */
851 g = 0;
852 mq = threadroot;
853 for (mp = threadroot; mp; mp = next_in_thread(mp))
854 if (visible(mp) &&
855 (mp->m_collapsed <= 0 ||
856 PTRCMP(mp, ==, message + msgspec - 1))) {
857 if (g % size == 0)
858 mq = mp;
859 if (mp->m_flag & fl) {
860 lastg = g;
861 lastmq = mq;
863 if ((msgspec > 0 && PTRCMP(mp, ==, message + msgspec - 1)) ||
864 (msgspec == 0 && g == k) ||
865 (msgspec == -2 && g == k + size && lastmq) ||
866 (msgspec < 0 && g >= k && (mp->m_flag & fl) != 0))
867 break;
868 g++;
870 if (lastmq && (msgspec == -2 ||
871 (msgspec == -1 && PTRCMP(mp, ==, message + msgCount)))) {
872 g = lastg;
873 mq = lastmq;
875 _screen = g / size;
876 mp = mq;
878 n_COLOUR( n_colour_env_create(n_COLOUR_CTX_SUM, n_stdout, FAL0); )
879 srelax_hold();
880 for(lastmq = NULL; mp != NULL; lastmq = mp, mp = mq){
881 mq = next_in_thread(mp);
882 if (visible(mp) &&
883 (mp->m_collapsed <= 0 ||
884 PTRCMP(mp, ==, message + msgspec - 1))) {
885 if (UICMP(32, flag, >=, size))
886 break;
887 if(needdot){
888 if(showlast){
889 if(UICMP(32, flag, ==, size - 1) || mq == NULL)
890 goto jdot_sort;
891 }else if(flag == 0){
892 jdot_sort:
893 needdot = FAL0;
894 setdot(mp);
897 a_cmd_print_head(flag, PTR2SIZE(mp - message + 1), n_stdout,
898 mb.mb_threaded, TRU1);
899 ++flag;
900 srelax();
903 if(needdot && ok_blook(showlast)) /* xxx will not show */
904 setdot(lastmq);
905 srelax_rele();
906 n_COLOUR( n_colour_env_gut(); )
909 if (flag == 0) {
910 fprintf(n_stdout, _("No more mail.\n"));
911 if (n_pstate & (n_PS_ROBOT | n_PS_HOOK_MASK))
912 flag = !flag;
914 NYD_LEAVE;
915 return !flag;
918 FL int
919 c_headers(void *v)
921 int rv;
922 NYD_ENTER;
924 rv = print_header_group((int*)v);
925 NYD_LEAVE;
926 return rv;
929 FL int
930 print_header_group(int *vector)
932 int rv;
933 NYD_ENTER;
935 assert(vector != NULL && vector != (void*)-1);
936 rv = _headers(vector[0]);
937 NYD_LEAVE;
938 return rv;
941 FL int
942 c_scroll(void *v)
944 int rv;
945 NYD_ENTER;
947 rv = a_cmd_scroll(*(char const**)v, FAL0);
948 NYD_LEAVE;
949 return rv;
952 FL int
953 c_Scroll(void *v)
955 int rv;
956 NYD_ENTER;
958 rv = a_cmd_scroll(*(char const**)v, TRU1);
959 NYD_LEAVE;
960 return rv;
963 FL int
964 c_dotmove(void *v)
966 char const *args;
967 int msgvec[2], rv;
968 NYD_ENTER;
970 if (*(args = v) == '\0' || args[1] != '\0') {
971 jerr:
972 n_err(_("Synopsis: dotmove: up <-> or down <+> by one message\n"));
973 rv = 1;
974 } else switch (args[0]) {
975 case '-':
976 case '+':
977 if (msgCount == 0) {
978 fprintf(n_stdout, _("At EOF\n"));
979 rv = 0;
980 } else if (getmsglist(n_UNCONST(/*TODO*/ args), msgvec, 0) > 0) {
981 setdot(message + msgvec[0] - 1);
982 msgvec[1] = 0;
983 rv = c_headers(msgvec);
984 } else
985 rv = 1;
986 break;
987 default:
988 goto jerr;
990 NYD_LEAVE;
991 return rv;
994 FL int
995 c_from(void *vp)
997 int *msgvec, *ip, n;
998 char *cp;
999 FILE * volatile obuf;
1000 NYD_ENTER;
1002 if(*(msgvec = vp) == 0)
1003 goto jleave;
1005 time_current_update(&time_current, FAL0);
1007 obuf = n_stdout;
1009 if (n_psonce & n_PSO_INTERACTIVE) {
1010 if ((cp = ok_vlook(crt)) != NULL) {
1011 uiz_t ib;
1013 for (n = 0, ip = msgvec; *ip != 0; ++ip)
1014 ++n;
1016 if(*cp == '\0')
1017 ib = n_screensize();
1018 else
1019 n_idec_uiz_cp(&ib, cp, 0, NULL);
1020 if (UICMP(z, n, >, ib) && (obuf = n_pager_open()) == NULL)
1021 obuf = n_stdout;
1025 /* Update dot before display so that the dotmark etc. are correct */
1026 for (ip = msgvec; ip[1] != 0; ++ip)
1028 setdot(&message[(ok_blook(showlast) ? *ip : *msgvec) - 1]);
1030 n_COLOUR( n_colour_env_create(n_COLOUR_CTX_SUM, obuf, obuf != n_stdout); )
1031 srelax_hold();
1032 for (n = 0, ip = msgvec; *ip != 0; ++ip) { /* TODO join into _print_head() */
1033 a_cmd_print_head((size_t)n++, (size_t)*ip, obuf, mb.mb_threaded, FAL0);
1034 srelax();
1036 srelax_rele();
1037 n_COLOUR( n_colour_env_gut(); )
1039 if (obuf != n_stdout)
1040 n_pager_close(obuf);
1041 jleave:
1042 NYD_LEAVE;
1043 return 0;
1046 FL void
1047 print_headers(size_t bottom, size_t topx, bool_t only_marked,
1048 bool_t subject_thread_compress)
1050 size_t printed;
1051 NYD_ENTER;
1053 #ifdef HAVE_IMAP
1054 if (mb.mb_type == MB_IMAP)
1055 imap_getheaders(bottom, topx);
1056 #endif
1057 time_current_update(&time_current, FAL0);
1059 n_COLOUR( n_colour_env_create(n_COLOUR_CTX_SUM, n_stdout, FAL0); )
1060 srelax_hold();
1061 for (printed = 0; bottom <= topx; ++bottom) {
1062 struct message *mp = message + bottom - 1;
1063 if (only_marked) {
1064 if (!(mp->m_flag & MMARK))
1065 continue;
1066 } else if (!visible(mp))
1067 continue;
1068 a_cmd_print_head(printed++, bottom, n_stdout, FAL0,
1069 subject_thread_compress);
1070 srelax();
1072 srelax_rele();
1073 n_COLOUR( n_colour_env_gut(); )
1074 NYD_LEAVE;
1077 /* s-it-mode */