make-config.in: complete path (leftover of [807f64e2], 2015-12-26!)
[s-mailx.git] / cmd-head.c
blob9cb76d19c253f72a2c81ebb4f93592f8d4b0e344
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 * SPDX-License-Identifier: BSD-3-Clause
7 */
8 /*
9 * Copyright (c) 1980, 1993
10 * The Regents of the University of California. All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
36 #undef n_FILE
37 #define n_FILE cmd_head
39 #ifndef HAVE_AMALGAMATION
40 # include "nail.h"
41 #endif
43 static int _screen;
45 /* Print out the header of a specific message.
46 * time_current must be up-to-date when this is called.
47 * a_chead__hprf: handle *headline*
48 * a_chead__subject: -1 if Subject: yet seen, otherwise n_alloc()d Subject:
49 * a_chead__putindent: print out the indenting in threaded display
50 * a_chead__putuc: print out a Unicode character or a substitute for it, return
51 * 0 on error or wcwidth() (or 1) on success */
52 static void a_chead_print_head(size_t yetprinted, size_t msgno, FILE *f,
53 bool_t threaded, bool_t subject_thread_compress);
55 static void a_chead__hprf(size_t yetprinted, char const *fmt, size_t msgno,
56 FILE *f, bool_t threaded, bool_t subject_thread_compress,
57 char const *attrlist);
58 static char *a_chead__subject(struct message *mp, bool_t threaded,
59 bool_t subject_thread_compress, size_t yetprinted);
60 static int a_chead__putindent(FILE *fp, struct message *mp, int maxwidth);
61 static size_t a_chead__putuc(int u, int c, FILE *fp);
62 static int a_chead__dispc(struct message *mp, char const *a);
64 /* Shared `z' implementation */
65 static int a_chead_scroll(char const *arg, bool_t onlynew);
67 /* Shared `headers' implementation */
68 static int _headers(int msgspec);
70 static void
71 a_chead_print_head(size_t yetprinted, size_t msgno, FILE *f, bool_t threaded,
72 bool_t subject_thread_compress){
73 enum {attrlen = 14};
74 char attrlist[attrlen +1], *cp;
75 char const *fmt;
76 NYD2_ENTER;
78 if((cp = ok_vlook(attrlist)) != NULL){
79 if(strlen(cp) == attrlen){
80 memcpy(attrlist, cp, attrlen +1);
81 goto jattrok;
83 n_err(_("*attrlist* is not of the correct length, using built-in\n"));
86 if(ok_blook(bsdcompat) || ok_blook(bsdflags)){
87 char const bsdattr[attrlen +1] = "NU *HMFAT+-$~";
89 memcpy(attrlist, bsdattr, sizeof bsdattr);
90 }else if(ok_blook(SYSV3)){
91 char const bsdattr[attrlen +1] = "NU *HMFAT+-$~";
93 memcpy(attrlist, bsdattr, sizeof bsdattr);
94 n_OBSOLETE(_("*SYSV3*: please use *bsdcompat* or *bsdflags*, "
95 "or set *attrlist*"));
96 }else{
97 char const pattr[attrlen +1] = "NUROSPMFAT+-$~";
99 memcpy(attrlist, pattr, sizeof pattr);
102 jattrok:
103 if((fmt = ok_vlook(headline)) == NULL){
104 fmt = ((ok_blook(bsdcompat) || ok_blook(bsdheadline))
105 ? "%>%a%m %-20f %16d %4l/%-5o %i%-S"
106 : "%>%a%m %-18f %-16d %4l/%-5o %i%-s");
109 a_chead__hprf(yetprinted, fmt, msgno, f, threaded, subject_thread_compress,
110 attrlist);
111 NYD2_LEAVE;
114 static void
115 a_chead__hprf(size_t yetprinted, char const *fmt, size_t msgno, FILE *f,
116 bool_t threaded, bool_t subject_thread_compress, char const *attrlist)
118 char buf[16], cbuf[8], *cp, *subjline;
119 char const *date, *name, *fp, *color_tag;
120 int i, n, s, wleft, subjlen;
121 struct message *mp;
122 n_COLOUR( struct n_colour_pen *cpen_new COMMA *cpen_cur COMMA *cpen_bas; )
123 enum {
124 _NONE = 0,
125 _ISDOT = 1<<0,
126 _ISTO = 1<<1,
127 _IFMT = 1<<2,
128 _LOOP_MASK = (1<<4) - 1,
129 _SFMT = 1<<4, /* It is 'S' */
130 /* For the simple byte-based counts in wleft and n we sometimes need
131 * adjustments to compensate for additional bytes of UTF-8 sequences */
132 _PUTCB_UTF8_SHIFT = 5,
133 _PUTCB_UTF8_MASK = 3<<5
134 } flags = _NONE;
135 NYD2_ENTER;
136 n_UNUSED(buf);
138 if ((mp = message + msgno - 1) == dot)
139 flags = _ISDOT;
141 color_tag = NULL;
142 date = n_header_textual_date_info(mp, &color_tag);
143 /* C99 */{
144 bool_t isto;
146 n_header_textual_sender_info(mp, &cp, NULL, NULL, NULL, &isto);
147 name = cp;
148 if(isto)
149 flags |= _ISTO;
152 subjline = NULL;
154 /* Detect the width of the non-format characters in *headline*;
155 * like that we can simply use putc() in the next loop, since we have
156 * already calculated their column widths (TODO it's sick) */
157 wleft = subjlen = n_scrnwidth;
159 for (fp = fmt; *fp != '\0'; ++fp) {
160 if (*fp == '%') {
161 if (*++fp == '-')
162 ++fp;
163 else if (*fp == '+')
164 ++fp;
165 if (digitchar(*fp)) {
166 n = 0;
168 n = 10*n + *fp - '0';
169 while (++fp, digitchar(*fp));
170 subjlen -= n;
172 if (*fp == 'i')
173 flags |= _IFMT;
175 if (*fp == '\0')
176 break;
177 } else {
178 #ifdef HAVE_WCWIDTH
179 if (n_mb_cur_max > 1) {
180 wchar_t wc;
181 if ((s = mbtowc(&wc, fp, n_mb_cur_max)) == -1)
182 n = s = 1;
183 else if ((n = wcwidth(wc)) == -1)
184 n = 1;
185 } else
186 #endif
187 n = s = 1;
188 subjlen -= n;
189 wleft -= n;
190 while (--s > 0)
191 ++fp;
195 /* Walk *headline*, producing output TODO not (really) MB safe */
196 #ifdef HAVE_COLOUR
197 if(n_COLOUR_IS_ACTIVE()){
198 if(flags & _ISDOT)
199 color_tag = n_COLOUR_TAG_SUM_DOT;
200 cpen_bas = n_colour_pen_create(n_COLOUR_ID_SUM_HEADER, color_tag);
201 n_colour_pen_put(cpen_new = cpen_cur = cpen_bas);
202 }else
203 cpen_new = cpen_bas = cpen_cur = NULL;
204 #endif
206 for (fp = fmt; *fp != '\0'; ++fp) {
207 char c;
209 if ((c = *fp & 0xFF) != '%') {
210 n_COLOUR(
211 if(n_COLOUR_IS_ACTIVE() && (cpen_new = cpen_bas) != cpen_cur)
212 n_colour_pen_put(cpen_cur = cpen_new);
214 putc(c, f);
215 continue;
218 flags &= _LOOP_MASK;
219 n = 0;
220 s = 1;
221 if ((c = *++fp) == '-') {
222 s = -1;
223 ++fp;
224 } else if (c == '+')
225 ++fp;
226 if (digitchar(*fp)) {
228 n = 10*n + *fp - '0';
229 while (++fp, digitchar(*fp));
232 if ((c = *fp & 0xFF) == '\0')
233 break;
234 n *= s;
236 cbuf[1] = '\0';
237 switch (c) {
238 case '%':
239 goto jputcb;
240 case '>':
241 case '<':
242 if (flags & _ISDOT) {
243 n_COLOUR(
244 if(n_COLOUR_IS_ACTIVE())
245 cpen_new = n_colour_pen_create(n_COLOUR_ID_SUM_DOTMARK,
246 color_tag);
248 if((n_psonce & n_PSO_UNICODE) && !ok_blook(headline_plain)){
249 if (c == '>')
250 /* 25B8;BLACK RIGHT-POINTING SMALL TRIANGLE */
251 cbuf[1] = (char)0x96, cbuf[2] = (char)0xB8;
252 else
253 /* 25C2;BLACK LEFT-POINTING SMALL TRIANGLE */
254 cbuf[1] = (char)0x97, cbuf[2] = (char)0x82;
255 c = (char)0xE2;
256 cbuf[3] = '\0';
257 flags |= 2 << _PUTCB_UTF8_SHIFT;
259 } else
260 c = ' ';
261 goto jputcb;
262 case '$':
263 #ifdef HAVE_SPAM
264 if (n == 0)
265 n = 5;
266 if (UICMP(32, n_ABS(n), >, wleft))
267 n = (n < 0) ? -wleft : wleft;
268 snprintf(buf, sizeof buf, "%u.%02u",
269 (mp->m_spamscore >> 8), (mp->m_spamscore & 0xFF));
270 n = fprintf(f, "%*s", n, buf);
271 wleft = (n >= 0) ? wleft - n : 0;
272 break;
273 #else
274 c = '?';
275 goto jputcb;
276 #endif
277 case 'a':
278 c = a_chead__dispc(mp, attrlist);
279 jputcb:
280 #ifdef HAVE_COLOUR
281 if(n_COLOUR_IS_ACTIVE()){
282 if(cpen_new == cpen_cur)
283 cpen_new = cpen_bas;
284 if(cpen_new != cpen_cur)
285 n_colour_pen_put(cpen_cur = cpen_new);
287 #endif
288 if (UICMP(32, n_ABS(n), >, wleft))
289 n = (n < 0) ? -wleft : wleft;
290 cbuf[0] = c;
291 n = fprintf(f, "%*s", n, cbuf);
292 if (n >= 0) {
293 wleft -= n;
294 if ((n = (flags & _PUTCB_UTF8_MASK)) != 0) {
295 n >>= _PUTCB_UTF8_SHIFT;
296 wleft += n;
298 } else {
299 wleft = 0; /* TODO I/O error.. ? break? */
301 #ifdef HAVE_COLOUR
302 if(n_COLOUR_IS_ACTIVE() && (cpen_new = cpen_bas) != cpen_cur)
303 n_colour_pen_put(cpen_cur = cpen_new);
304 #endif
305 break;
306 case 'd':
307 if (n == 0)
308 n = 16;
309 if (UICMP(32, n_ABS(n), >, wleft))
310 n = (n < 0) ? -wleft : wleft;
311 n = fprintf(f, "%*.*s", n, n_ABS(n), date);
312 wleft = (n >= 0) ? wleft - n : 0;
313 break;
314 case 'e':
315 if (n == 0)
316 n = 2;
317 if (UICMP(32, n_ABS(n), >, wleft))
318 n = (n < 0) ? -wleft : wleft;
319 n = fprintf(f, "%*u", n, (threaded == 1 ? mp->m_level : 0));
320 wleft = (n >= 0) ? wleft - n : 0;
321 break;
322 case 'f':
323 if (n == 0) {
324 n = 18;
325 if (s < 0)
326 n = -n;
328 i = n_ABS(n);
329 if (i > wleft) {
330 i = wleft;
331 n = (n < 0) ? -wleft : wleft;
333 if (flags & _ISTO) /* XXX tr()! */
334 i -= 3;
335 n = fprintf(f, "%s%s", ((flags & _ISTO) ? "To " : n_empty),
336 colalign(name, i, n, &wleft));
337 if (n < 0)
338 wleft = 0;
339 else if (flags & _ISTO)
340 wleft -= 3;
341 break;
342 case 'i':
343 if (threaded) {
344 #ifdef HAVE_COLOUR
345 if(n_COLOUR_IS_ACTIVE()){
346 cpen_new = n_colour_pen_create(n_COLOUR_ID_SUM_THREAD,
347 color_tag);
348 if(cpen_new != cpen_cur)
349 n_colour_pen_put(cpen_cur = cpen_new);
351 #endif
352 n = a_chead__putindent(f, mp, n_MIN(wleft, (int)n_scrnwidth - 60));
353 wleft = (n >= 0) ? wleft - n : 0;
354 #ifdef HAVE_COLOUR
355 if(n_COLOUR_IS_ACTIVE() && (cpen_new = cpen_bas) != cpen_cur)
356 n_colour_pen_put(cpen_cur = cpen_new);
357 #endif
359 break;
360 case 'l':
361 if (n == 0)
362 n = 4;
363 if (UICMP(32, n_ABS(n), >, wleft))
364 n = (n < 0) ? -wleft : wleft;
365 if (mp->m_xlines) {
366 n = fprintf(f, "%*ld", n, mp->m_xlines);
367 wleft = (n >= 0) ? wleft - n : 0;
368 } else {
369 n = n_ABS(n);
370 wleft -= n;
371 while (n-- != 0)
372 putc(' ', f);
374 break;
375 case 'm':
376 if (n == 0) {
377 n = 3;
378 if (threaded)
379 for (i = msgCount; i > 999; i /= 10)
380 ++n;
382 if (UICMP(32, n_ABS(n), >, wleft))
383 n = (n < 0) ? -wleft : wleft;
384 n = fprintf(f, "%*lu", n, (ul_i)msgno);
385 wleft = (n >= 0) ? wleft - n : 0;
386 break;
387 case 'o':
388 if (n == 0)
389 n = -5;
390 if (UICMP(32, n_ABS(n), >, wleft))
391 n = (n < 0) ? -wleft : wleft;
392 n = fprintf(f, "%*lu", n, (ul_i)mp->m_xsize);
393 wleft = (n >= 0) ? wleft - n : 0;
394 break;
395 case 'S':
396 flags |= _SFMT;
397 /*FALLTHRU*/
398 case 's':
399 if (n == 0)
400 n = subjlen - 2;
401 if (n > 0 && s < 0)
402 n = -n;
403 if (subjlen > wleft)
404 subjlen = wleft;
405 if (UICMP(32, n_ABS(n), >, subjlen))
406 n = (n < 0) ? -subjlen : subjlen;
407 if (flags & _SFMT)
408 n -= (n < 0) ? -2 : 2;
409 if (n == 0)
410 break;
411 if (subjline == NULL)
412 subjline = a_chead__subject(mp, (threaded && (flags & _IFMT)),
413 subject_thread_compress, yetprinted);
414 if (subjline == (char*)-1) {
415 n = fprintf(f, "%*s", n, n_empty);
416 wleft = (n >= 0) ? wleft - n : 0;
417 } else {
418 n = fprintf(f, ((flags & _SFMT) ? "\"%s\"" : "%s"),
419 colalign(subjline, n_ABS(n), n, &wleft));
420 if (n < 0)
421 wleft = 0;
423 break;
424 case 'T': /* Message recipient flags */
425 switch(is_mlist_mp(mp, MLIST_OTHER)){
426 case MLIST_OTHER: c = ' '; break;
427 case MLIST_KNOWN: c = 'l'; break;
428 case MLIST_SUBSCRIBED: c = 'L'; break;
430 goto jputcb;
431 case 't':
432 if (n == 0) {
433 n = 3;
434 if (threaded)
435 for (i = msgCount; i > 999; i /= 10)
436 ++n;
438 if (UICMP(32, n_ABS(n), >, wleft))
439 n = (n < 0) ? -wleft : wleft;
440 n = fprintf(f, "%*lu",
441 n, (threaded ? (ul_i)mp->m_threadpos : (ul_i)msgno));
442 wleft = (n >= 0) ? wleft - n : 0;
443 break;
444 case 'U':
445 #ifdef HAVE_IMAP
446 if (n == 0)
447 n = 9;
448 if (UICMP(32, n_ABS(n), >, wleft))
449 n = (n < 0) ? -wleft : wleft;
450 n = fprintf(f, "%*" PRIu64 , n, mp->m_uid);
451 wleft = (n >= 0) ? wleft - n : 0;
452 break;
453 #else
454 c = '0';
455 goto jputcb;
456 #endif
457 default:
458 if (n_poption & n_PO_D_V)
459 n_err(_("Unknown *headline* format: %%%c\n"), c);
460 c = '?';
461 goto jputcb;
464 if (wleft <= 0)
465 break;
468 n_COLOUR( n_colour_reset(); )
469 putc('\n', f);
471 if (subjline != NULL && subjline != (char*)-1)
472 n_free(subjline);
473 NYD2_LEAVE;
476 static char *
477 a_chead__subject(struct message *mp, bool_t threaded,
478 bool_t subject_thread_compress, size_t yetprinted)
480 struct str in, out;
481 char *rv, *ms;
482 NYD2_ENTER;
484 rv = (char*)-1;
486 if ((ms = hfield1("subject", mp)) == NULL)
487 goto jleave;
489 in.l = strlen(in.s = ms);
490 mime_fromhdr(&in, &out, TD_ICONV | TD_ISPR);
491 rv = ms = out.s;
493 if (!threaded || !subject_thread_compress || mp->m_level == 0)
494 goto jleave;
496 /* In a display thread - check whether this message uses the same
497 * Subject: as it's parent or elder neighbour, suppress printing it if
498 * this is the case. To extend this a bit, ignore any leading Re: or
499 * Fwd: plus follow-up WS. Ignore invisible messages along the way */
500 ms = n_UNCONST(subject_re_trim(n_UNCONST(ms)));
502 for (; (mp = prev_in_thread(mp)) != NULL && yetprinted-- > 0;) {
503 char *os;
505 if (visible(mp) && (os = hfield1("subject", mp)) != NULL) {
506 struct str oout;
507 int x;
509 in.l = strlen(in.s = os);
510 mime_fromhdr(&in, &oout, TD_ICONV | TD_ISPR);
511 x = asccasecmp(ms, subject_re_trim(oout.s));
512 n_free(oout.s);
514 if (!x) {
515 n_free(out.s);
516 rv = (char*)-1;
518 break;
521 jleave:
522 NYD2_LEAVE;
523 return rv;
526 static int
527 a_chead__putindent(FILE *fp, struct message *mp, int maxwidth)/* XXX magics */
529 struct message *mq;
530 int *us, indlvl, indw, i, important = MNEW | MFLAGGED;
531 char *cs;
532 NYD2_ENTER;
534 if (mp->m_level == 0 || maxwidth == 0) {
535 indw = 0;
536 goto jleave;
539 cs = n_lofi_alloc(mp->m_level);
540 us = n_lofi_alloc(mp->m_level * sizeof *us);
542 i = mp->m_level - 1;
543 if (mp->m_younger && UICMP(32, i + 1, ==, mp->m_younger->m_level)) {
544 if (mp->m_parent && mp->m_parent->m_flag & important)
545 us[i] = mp->m_flag & important ? 0x2523 : 0x2520;
546 else
547 us[i] = mp->m_flag & important ? 0x251D : 0x251C;
548 cs[i] = '+';
549 } else {
550 if (mp->m_parent && mp->m_parent->m_flag & important)
551 us[i] = mp->m_flag & important ? 0x2517 : 0x2516;
552 else
553 us[i] = mp->m_flag & important ? 0x2515 : 0x2514;
554 cs[i] = '\\';
557 mq = mp->m_parent;
558 for (i = mp->m_level - 2; i >= 0; --i) {
559 if (mq) {
560 if (UICMP(32, i, >, mq->m_level - 1)) {
561 us[i] = cs[i] = ' ';
562 continue;
564 if (mq->m_younger) {
565 if (mq->m_parent && (mq->m_parent->m_flag & important))
566 us[i] = 0x2503;
567 else
568 us[i] = 0x2502;
569 cs[i] = '|';
570 } else
571 us[i] = cs[i] = ' ';
572 mq = mq->m_parent;
573 } else
574 us[i] = cs[i] = ' ';
577 --maxwidth;
578 for (indlvl = indw = 0; (ui8_t)indlvl < mp->m_level && indw < maxwidth;
579 ++indlvl) {
580 if (indw < maxwidth - 1)
581 indw += (int)a_chead__putuc(us[indlvl], cs[indlvl] & 0xFF, fp);
582 else
583 indw += (int)a_chead__putuc(0x21B8, '^', fp);
585 indw += a_chead__putuc(0x25B8, '>', fp);
587 n_lofi_free(us);
588 n_lofi_free(cs);
589 jleave:
590 NYD2_LEAVE;
591 return indw;
594 static size_t
595 a_chead__putuc(int u, int c, FILE *fp){
596 size_t rv;
597 NYD2_ENTER;
598 n_UNUSED(u);
600 #ifdef HAVE_NATCH_CHAR
601 if((n_psonce & n_PSO_UNICODE) && (u & ~(wchar_t)0177) &&
602 !ok_blook(headline_plain)){
603 char mbb[MB_LEN_MAX];
604 int i, n;
606 if((n = wctomb(mbb, u)) > 0){
607 rv = wcwidth(u);
608 for(i = 0; i < n; ++i)
609 if(putc(mbb[i] & 0377, fp) == EOF){
610 rv = 0;
611 break;
613 }else if(n == 0)
614 rv = (putc('\0', fp) != EOF);
615 else
616 rv = 0;
617 }else
618 #endif
619 rv = (putc(c, fp) != EOF);
620 NYD2_LEAVE;
621 return rv;
624 static int
625 a_chead__dispc(struct message *mp, char const *a)
627 int i = ' ';
628 NYD2_ENTER;
630 if ((mp->m_flag & (MREAD | MNEW)) == MREAD)
631 i = a[3];
632 if ((mp->m_flag & (MREAD | MNEW)) == (MREAD | MNEW))
633 i = a[2];
634 if (mp->m_flag & MANSWERED)
635 i = a[8];
636 if (mp->m_flag & MDRAFTED)
637 i = a[9];
638 if ((mp->m_flag & (MREAD | MNEW)) == MNEW)
639 i = a[0];
640 if (!(mp->m_flag & (MREAD | MNEW)))
641 i = a[1];
642 if (mp->m_flag & MSPAM)
643 i = a[12];
644 if (mp->m_flag & MSPAMUNSURE)
645 i = a[13];
646 if (mp->m_flag & MSAVED)
647 i = a[4];
648 if (mp->m_flag & MPRESERVE)
649 i = a[5];
650 if (mp->m_flag & (MBOX | MBOXED))
651 i = a[6];
652 if (mp->m_flag & MFLAGGED)
653 i = a[7];
654 if (mb.mb_threaded == 1) { /* TODO bad, and m_collapsed is weird */
655 /* TODO So this does not work because of weird thread handling and
656 * TODO intermixing view and controller except when run via -L from
657 * TODO command line; in general these flags should go and we need
658 * TODO specific *headline* formats which always work and indicate
659 * TODO whether a message is in a thread, the head of a subthread etc. */
660 if (mp->m_collapsed > 0)
661 i = a[11];
662 else if (mp->m_collapsed < 0)
663 i = a[10];
665 NYD2_LEAVE;
666 return i;
669 static int
670 a_chead_scroll(char const *arg, bool_t onlynew){
671 siz_t l;
672 bool_t isabs;
673 int msgspec, size, maxs;
674 NYD2_ENTER;
676 /* TODO scroll problem: we do not know whether + and $ have already reached
677 * TODO the last screen in threaded mode */
678 msgspec = onlynew ? -1 : 0;
679 size = (int)/*TODO*/n_screensize();
680 if((maxs = msgCount / size) > 0 && msgCount % size == 0)
681 --maxs;
683 if(arg == NULL)
684 arg = n_empty;
685 switch(*arg){
686 case '\0':
687 ++_screen;
688 goto jfwd;
689 case '^':
690 if(arg[1] != '\0')
691 goto jerr;
692 if(_screen == 0)
693 goto jerrbwd;
694 _screen = 0;
695 break;
696 case '$':
697 if(arg[1] != '\0')
698 goto jerr;
699 if(_screen == maxs)
700 goto jerrfwd;
701 _screen = maxs;
702 break;
703 case '+':
704 if(arg[1] == '\0')
705 ++_screen;
706 else{
707 isabs = FAL0;
709 ++arg;
710 if(0){
711 case '1': case '2': case '3': case '4': case '5':
712 case '6': case '7': case '8': case '9': case '0':
713 isabs = TRU1;
715 if((n_idec_siz_cp(&l, arg, 0, NULL
716 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
717 ) != n_IDEC_STATE_CONSUMED)
718 goto jerr;
719 if(l > maxs - (isabs ? 0 : _screen))
720 goto jerrfwd;
721 _screen = isabs ? (int)l : _screen + l;
723 jfwd:
724 if(_screen > maxs){
725 jerrfwd:
726 _screen = maxs;
727 fprintf(n_stdout, _("On last screenful of messages\n"));
729 break;
731 case '-':
732 if(arg[1] == '\0')
733 --_screen;
734 else{
735 if((n_idec_siz_cp(&l, ++arg, 0, NULL
736 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
737 ) != n_IDEC_STATE_CONSUMED)
738 goto jerr;
739 if(l > _screen)
740 goto jerrbwd;
741 _screen -= l;
743 if(_screen < 0){
744 jerrbwd:
745 _screen = 0;
746 fprintf(n_stdout, _("On first screenful of messages\n"));
748 if(msgspec == -1)
749 msgspec = -2;
750 break;
751 default:
752 jerr:
753 n_err(_("Unrecognized scrolling command: %s\n"), arg);
754 size = 1;
755 goto jleave;
758 size = _headers(msgspec);
759 jleave:
760 NYD2_LEAVE;
761 return size;
764 static int
765 _headers(int msgspec) /* TODO rework v15 */
767 bool_t needdot, showlast;
768 int g, k, mesg, size;
769 struct message *lastmq, *mp, *mq;
770 int volatile lastg;
771 ui32_t volatile flag;
772 enum mflag fl;
773 NYD_ENTER;
775 time_current_update(&time_current, FAL0);
777 fl = MNEW | MFLAGGED;
778 flag = 0;
779 lastg = 1;
780 lastmq = NULL;
782 size = (int)/*TODO*/n_screensize();
783 if (_screen < 0)
784 _screen = 0;
785 #if 0 /* FIXME original code path */
786 k = _screen * size;
787 #else
788 if (msgspec <= 0)
789 k = _screen * size;
790 else
791 k = msgspec;
792 #endif
793 if (k >= msgCount)
794 k = msgCount - size;
795 if (k < 0)
796 k = 0;
798 needdot = (msgspec <= 0) ? TRU1 : (dot != &message[msgspec - 1]);
799 showlast = ok_blook(showlast);
801 if (mb.mb_threaded == 0) {
802 g = 0;
803 mq = message;
804 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
805 if (visible(mp)) {
806 if (g % size == 0)
807 mq = mp;
808 if (mp->m_flag & fl) {
809 lastg = g;
810 lastmq = mq;
812 if ((msgspec > 0 && PTRCMP(mp, ==, message + msgspec - 1)) ||
813 (msgspec == 0 && g == k) ||
814 (msgspec == -2 && g == k + size && lastmq) ||
815 (msgspec < 0 && g >= k && (mp->m_flag & fl) != 0))
816 break;
817 g++;
819 if (lastmq && (msgspec == -2 ||
820 (msgspec == -1 && PTRCMP(mp, ==, message + msgCount)))) {
821 g = lastg;
822 mq = lastmq;
824 _screen = g / size;
825 mp = mq;
827 mesg = (int)PTR2SIZE(mp - message);
828 #ifdef HAVE_IMAP
829 if (mb.mb_type == MB_IMAP)
830 imap_getheaders(mesg + 1, mesg + size);
831 #endif
832 n_COLOUR( n_colour_env_create(n_COLOUR_CTX_SUM, n_stdout, FAL0); )
833 srelax_hold();
834 for(lastmq = NULL, mq = &message[msgCount]; mp < mq; lastmq = mp, ++mp){
835 ++mesg;
836 if (!visible(mp))
837 continue;
838 if (UICMP(32, flag, >=, size))
839 break;
840 if(needdot){
841 if(showlast){
842 if(UICMP(32, flag, ==, size - 1) || &mp[1] == mq)
843 goto jdot_unsort;
844 }else if(flag == 0){
845 jdot_unsort:
846 needdot = FAL0;
847 setdot(mp);
850 ++flag;
851 a_chead_print_head(0, mesg, n_stdout, FAL0, FAL0);
852 srelax();
854 if(needdot && ok_blook(showlast)) /* xxx will not show */
855 setdot(lastmq);
856 srelax_rele();
857 n_COLOUR( n_colour_env_gut(); )
858 } else { /* threaded */
859 g = 0;
860 mq = threadroot;
861 for (mp = threadroot; mp; mp = next_in_thread(mp)){
862 /* TODO thread handling needs rewrite, m_collapsed must go */
863 if (visible(mp) &&
864 (mp->m_collapsed <= 0 ||
865 PTRCMP(mp, ==, message + msgspec - 1))) {
866 if (g % size == 0)
867 mq = mp;
868 if (mp->m_flag & fl) {
869 lastg = g;
870 lastmq = mq;
872 if ((msgspec > 0 && PTRCMP(mp, ==, message + msgspec - 1)) ||
873 (msgspec == 0 && g == k) ||
874 (msgspec == -2 && g == k + size && lastmq) ||
875 (msgspec < 0 && g >= k && (mp->m_flag & fl) != 0))
876 break;
877 g++;
880 if (lastmq && (msgspec == -2 ||
881 (msgspec == -1 && PTRCMP(mp, ==, message + msgCount)))) {
882 g = lastg;
883 mq = lastmq;
885 _screen = g / size;
886 mp = mq;
888 n_COLOUR( n_colour_env_create(n_COLOUR_CTX_SUM, n_stdout, FAL0); )
889 srelax_hold();
890 for(lastmq = NULL; mp != NULL; lastmq = mp, mp = mq){
891 mq = next_in_thread(mp);
892 if (visible(mp) &&
893 (mp->m_collapsed <= 0 ||
894 PTRCMP(mp, ==, message + msgspec - 1))) {
895 if (UICMP(32, flag, >=, size))
896 break;
897 if(needdot){
898 if(showlast){
899 if(UICMP(32, flag, ==, size - 1) || mq == NULL)
900 goto jdot_sort;
901 }else if(flag == 0){
902 jdot_sort:
903 needdot = FAL0;
904 setdot(mp);
907 a_chead_print_head(flag, PTR2SIZE(mp - message + 1), n_stdout,
908 mb.mb_threaded, TRU1);
909 ++flag;
910 srelax();
913 if(needdot && ok_blook(showlast)) /* xxx will not show */
914 setdot(lastmq);
915 srelax_rele();
916 n_COLOUR( n_colour_env_gut(); )
919 if (flag == 0) {
920 fprintf(n_stdout, _("No more mail.\n"));
921 if (n_pstate & (n_PS_ROBOT | n_PS_HOOK_MASK))
922 flag = !flag;
924 NYD_LEAVE;
925 return !flag;
928 FL int
929 c_headers(void *v)
931 int rv;
932 NYD_ENTER;
934 rv = print_header_group((int*)v);
935 NYD_LEAVE;
936 return rv;
939 FL int
940 print_header_group(int *vector)
942 int rv;
943 NYD_ENTER;
945 assert(vector != NULL && vector != (void*)-1);
946 rv = _headers(vector[0]);
947 NYD_LEAVE;
948 return rv;
951 FL int
952 c_scroll(void *v)
954 int rv;
955 NYD_ENTER;
957 rv = a_chead_scroll(*(char const**)v, FAL0);
958 NYD_LEAVE;
959 return rv;
962 FL int
963 c_Scroll(void *v)
965 int rv;
966 NYD_ENTER;
968 rv = a_chead_scroll(*(char const**)v, TRU1);
969 NYD_LEAVE;
970 return rv;
973 FL int
974 c_dotmove(void *v)
976 char const *args;
977 int msgvec[2], rv;
978 NYD_ENTER;
980 if (*(args = v) == '\0' || args[1] != '\0') {
981 jerr:
982 n_err(_("Synopsis: dotmove: up <-> or down <+> by one message\n"));
983 rv = 1;
984 } else switch (args[0]) {
985 case '-':
986 case '+':
987 if (msgCount == 0) {
988 fprintf(n_stdout, _("At EOF\n"));
989 rv = 0;
990 } else if (n_getmsglist(n_UNCONST(/*TODO*/args), msgvec, 0, NULL) > 0) {
991 setdot(message + msgvec[0] - 1);
992 msgvec[1] = 0;
993 rv = c_headers(msgvec);
994 } else
995 rv = 1;
996 break;
997 default:
998 goto jerr;
1000 NYD_LEAVE;
1001 return rv;
1004 FL int
1005 c_from(void *vp)
1007 int *msgvec, *ip, n;
1008 char *cp;
1009 FILE * volatile obuf;
1010 NYD_ENTER;
1012 if(*(msgvec = vp) == 0)
1013 goto jleave;
1015 time_current_update(&time_current, FAL0);
1017 obuf = n_stdout;
1019 if (n_psonce & n_PSO_INTERACTIVE) {
1020 if ((cp = ok_vlook(crt)) != NULL) {
1021 uiz_t ib;
1023 for (n = 0, ip = msgvec; *ip != 0; ++ip)
1024 ++n;
1026 if(*cp == '\0')
1027 ib = n_screensize();
1028 else
1029 n_idec_uiz_cp(&ib, cp, 0, NULL);
1030 if (UICMP(z, n, >, ib) && (obuf = n_pager_open()) == NULL)
1031 obuf = n_stdout;
1035 /* Update dot before display so that the dotmark etc. are correct */
1036 for (ip = msgvec; ip[1] != 0; ++ip)
1038 setdot(&message[(ok_blook(showlast) ? *ip : *msgvec) - 1]);
1040 n_COLOUR( n_colour_env_create(n_COLOUR_CTX_SUM, obuf, obuf != n_stdout); )
1041 srelax_hold();
1042 for (n = 0, ip = msgvec; *ip != 0; ++ip) { /* TODO join into _print_head() */
1043 a_chead_print_head((size_t)n++, (size_t)*ip, obuf, mb.mb_threaded, FAL0);
1044 srelax();
1046 srelax_rele();
1047 n_COLOUR( n_colour_env_gut(); )
1049 if (obuf != n_stdout)
1050 n_pager_close(obuf);
1051 jleave:
1052 NYD_LEAVE;
1053 return 0;
1056 FL void
1057 print_headers(int const *msgvec, bool_t only_marked,
1058 bool_t subject_thread_compress)
1060 size_t printed;
1061 NYD_ENTER;
1063 time_current_update(&time_current, FAL0);
1065 n_COLOUR( n_colour_env_create(n_COLOUR_CTX_SUM, n_stdout, FAL0); )
1066 srelax_hold();
1067 for(printed = 0; *msgvec != 0; ++msgvec) {
1068 struct message *mp = message + *msgvec - 1;
1069 if (only_marked) {
1070 if (!(mp->m_flag & MMARK))
1071 continue;
1072 } else if (!visible(mp))
1073 continue;
1074 a_chead_print_head(printed++, *msgvec, n_stdout, mb.mb_threaded,
1075 subject_thread_compress);
1076 srelax();
1078 srelax_rele();
1079 n_COLOUR( n_colour_env_gut(); )
1080 NYD_LEAVE;
1083 /* s-it-mode */