Fix [fa8ea474] and -uninstall script with DESTDIR (Paride Legovini)
[s-mailx.git] / cmd-head.c
blob79a83f4e32afdb0119ea0e6b4a296c6c96c040a0
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 wleft = 0;
268 else{
269 snprintf(buf, sizeof buf, "%u.%02u",
270 (mp->m_spamscore >> 8), (mp->m_spamscore & 0xFF));
271 n = fprintf(f, "%*s", n, buf);
272 wleft = (n >= 0) ? wleft - n : 0;
274 break;
275 #else
276 c = '?';
277 goto jputcb;
278 #endif
279 case 'a':
280 c = a_chead__dispc(mp, attrlist);
281 jputcb:
282 #ifdef HAVE_COLOUR
283 if(n_COLOUR_IS_ACTIVE()){
284 if(cpen_new == cpen_cur)
285 cpen_new = cpen_bas;
286 if(cpen_new != cpen_cur)
287 n_colour_pen_put(cpen_cur = cpen_new);
289 #endif
290 if (UICMP(32, n_ABS(n), >, wleft))
291 n = (n < 0) ? -wleft : wleft;
292 cbuf[0] = c;
293 n = fprintf(f, "%*s", n, cbuf);
294 if (n >= 0) {
295 wleft -= n;
296 if ((n = (flags & _PUTCB_UTF8_MASK)) != 0) {
297 n >>= _PUTCB_UTF8_SHIFT;
298 wleft += n;
300 } else {
301 wleft = 0; /* TODO I/O error.. ? break? */
303 #ifdef HAVE_COLOUR
304 if(n_COLOUR_IS_ACTIVE() && (cpen_new = cpen_bas) != cpen_cur)
305 n_colour_pen_put(cpen_cur = cpen_new);
306 #endif
307 break;
308 case 'd':
309 if (n == 0)
310 n = 16;
311 if (UICMP(32, n_ABS(n), >, wleft))
312 n = (n < 0) ? -wleft : wleft;
313 n = fprintf(f, "%*.*s", n, n_ABS(n), date);
314 wleft = (n >= 0) ? wleft - n : 0;
315 break;
316 case 'e':
317 if (n == 0)
318 n = 2;
319 if (UICMP(32, n_ABS(n), >, wleft))
320 wleft = 0;
321 else{
322 n = fprintf(f, "%*u", n, (threaded == 1 ? mp->m_level : 0));
323 wleft = (n >= 0) ? wleft - n : 0;
325 break;
326 case 'f':
327 if (n == 0) {
328 n = 18;
329 if (s < 0)
330 n = -n;
332 i = n_ABS(n);
333 if (i > wleft) {
334 i = wleft;
335 n = (n < 0) ? -wleft : wleft;
337 if (flags & _ISTO) {/* XXX tr()! */
338 if(wleft <= 3){
339 wleft = 0;
340 break;
342 i -= 3;
344 n = fprintf(f, "%s%s", ((flags & _ISTO) ? "To " : n_empty),
345 colalign(name, i, n, &wleft));
346 if (n < 0)
347 wleft = 0;
348 else if (flags & _ISTO)
349 wleft -= 3;
350 break;
351 case 'i':
352 if (threaded) {
353 #ifdef HAVE_COLOUR
354 if(n_COLOUR_IS_ACTIVE()){
355 cpen_new = n_colour_pen_create(n_COLOUR_ID_SUM_THREAD,
356 color_tag);
357 if(cpen_new != cpen_cur)
358 n_colour_pen_put(cpen_cur = cpen_new);
360 #endif
361 n = a_chead__putindent(f, mp, n_MIN(wleft, (int)n_scrnwidth - 60));
362 wleft = (n >= 0) ? wleft - n : 0;
363 #ifdef HAVE_COLOUR
364 if(n_COLOUR_IS_ACTIVE() && (cpen_new = cpen_bas) != cpen_cur)
365 n_colour_pen_put(cpen_cur = cpen_new);
366 #endif
368 break;
369 case 'l':
370 if (n == 0)
371 n = 4;
372 if (UICMP(32, n_ABS(n), >, wleft))
373 wleft = 0;
374 else if (mp->m_xlines) {
375 n = fprintf(f, "%*ld", n, mp->m_xlines);
376 wleft = (n >= 0) ? wleft - n : 0;
377 } else {
378 n = n_ABS(n);
379 wleft -= n;
380 while (n-- != 0)
381 putc(' ', f);
383 break;
384 case 'm':
385 if (n == 0) {
386 n = 3;
387 if (threaded)
388 for (i = msgCount; i > 999; i /= 10)
389 ++n;
391 if (UICMP(32, n_ABS(n), >, wleft))
392 wleft = 0;
393 else{
394 n = fprintf(f, "%*lu", n, (ul_i)msgno);
395 wleft = (n >= 0) ? wleft - n : 0;
397 break;
398 case 'o':
399 if (n == 0)
400 n = -5;
401 if (UICMP(32, n_ABS(n), >, wleft))
402 wleft = 0;
403 else{
404 n = fprintf(f, "%*lu", n, (ul_i)mp->m_xsize);
405 wleft = (n >= 0) ? wleft - n : 0;
407 break;
408 case 'S':
409 flags |= _SFMT;
410 /*FALLTHRU*/
411 case 's':
412 if (n == 0)
413 n = subjlen - 2;
414 if (n > 0 && s < 0)
415 n = -n;
416 if (subjlen > wleft)
417 subjlen = wleft;
418 if (UICMP(32, n_ABS(n), >, subjlen))
419 n = (n < 0) ? -subjlen : subjlen;
420 if (flags & _SFMT)
421 n -= (n < 0) ? -2 : 2;
422 if (n == 0)
423 break;
424 if (subjline == NULL)
425 subjline = a_chead__subject(mp, (threaded && (flags & _IFMT)),
426 subject_thread_compress, yetprinted);
427 if (subjline == (char*)-1) {
428 n = fprintf(f, "%*s", n, n_empty);
429 wleft = (n >= 0) ? wleft - n : 0;
430 } else {
431 n = fprintf(f, ((flags & _SFMT) ? "\"%s\"" : "%s"),
432 colalign(subjline, n_ABS(n), n, &wleft));
433 if (n < 0)
434 wleft = 0;
436 break;
437 case 'T': /* Message recipient flags */
438 switch(is_mlist_mp(mp, MLIST_OTHER)){
439 case MLIST_OTHER: c = ' '; break;
440 case MLIST_KNOWN: c = 'l'; break;
441 case MLIST_SUBSCRIBED: c = 'L'; break;
443 goto jputcb;
444 case 't':
445 if (n == 0) {
446 n = 3;
447 if (threaded)
448 for (i = msgCount; i > 999; i /= 10)
449 ++n;
451 if (UICMP(32, n_ABS(n), >, wleft))
452 wleft = 0;
453 else{
454 n = fprintf(f, "%*lu",
455 n, (threaded ? (ul_i)mp->m_threadpos : (ul_i)msgno));
456 wleft = (n >= 0) ? wleft - n : 0;
458 break;
459 case 'U':
460 #ifdef HAVE_IMAP
461 if (n == 0)
462 n = 9;
463 if (UICMP(32, n_ABS(n), >, wleft))
464 wleft = 0;
465 else{
466 n = fprintf(f, "%*" PRIu64 , n, mp->m_uid);
467 wleft = (n >= 0) ? wleft - n : 0;
469 break;
470 #else
471 c = '0';
472 goto jputcb;
473 #endif
474 default:
475 if (n_poption & n_PO_D_V)
476 n_err(_("Unknown *headline* format: %%%c\n"), c);
477 c = '?';
478 goto jputcb;
481 if (wleft <= 0)
482 break;
485 n_COLOUR( n_colour_reset(); )
486 putc('\n', f);
488 if (subjline != NULL && subjline != (char*)-1)
489 n_free(subjline);
490 NYD2_LEAVE;
493 static char *
494 a_chead__subject(struct message *mp, bool_t threaded,
495 bool_t subject_thread_compress, size_t yetprinted)
497 struct str in, out;
498 char *rv, *ms;
499 NYD2_ENTER;
501 rv = (char*)-1;
503 if ((ms = hfield1("subject", mp)) == NULL)
504 goto jleave;
506 in.l = strlen(in.s = ms);
507 mime_fromhdr(&in, &out, TD_ICONV | TD_ISPR);
508 rv = ms = out.s;
510 if (!threaded || !subject_thread_compress || mp->m_level == 0)
511 goto jleave;
513 /* In a display thread - check whether this message uses the same
514 * Subject: as it's parent or elder neighbour, suppress printing it if
515 * this is the case. To extend this a bit, ignore any leading Re: or
516 * Fwd: plus follow-up WS. Ignore invisible messages along the way */
517 ms = n_UNCONST(subject_re_trim(n_UNCONST(ms)));
519 for (; (mp = prev_in_thread(mp)) != NULL && yetprinted-- > 0;) {
520 char *os;
522 if (visible(mp) && (os = hfield1("subject", mp)) != NULL) {
523 struct str oout;
524 int x;
526 in.l = strlen(in.s = os);
527 mime_fromhdr(&in, &oout, TD_ICONV | TD_ISPR);
528 x = asccasecmp(ms, subject_re_trim(oout.s));
529 n_free(oout.s);
531 if (!x) {
532 n_free(out.s);
533 rv = (char*)-1;
535 break;
538 jleave:
539 NYD2_LEAVE;
540 return rv;
543 static int
544 a_chead__putindent(FILE *fp, struct message *mp, int maxwidth)/* XXX magics */
546 struct message *mq;
547 int *us, indlvl, indw, i, important = MNEW | MFLAGGED;
548 char *cs;
549 NYD2_ENTER;
551 if (mp->m_level == 0 || maxwidth == 0) {
552 indw = 0;
553 goto jleave;
556 cs = n_lofi_alloc(mp->m_level);
557 us = n_lofi_alloc(mp->m_level * sizeof *us);
559 i = mp->m_level - 1;
560 if (mp->m_younger && UICMP(32, i + 1, ==, mp->m_younger->m_level)) {
561 if (mp->m_parent && mp->m_parent->m_flag & important)
562 us[i] = mp->m_flag & important ? 0x2523 : 0x2520;
563 else
564 us[i] = mp->m_flag & important ? 0x251D : 0x251C;
565 cs[i] = '+';
566 } else {
567 if (mp->m_parent && mp->m_parent->m_flag & important)
568 us[i] = mp->m_flag & important ? 0x2517 : 0x2516;
569 else
570 us[i] = mp->m_flag & important ? 0x2515 : 0x2514;
571 cs[i] = '\\';
574 mq = mp->m_parent;
575 for (i = mp->m_level - 2; i >= 0; --i) {
576 if (mq) {
577 if (UICMP(32, i, >, mq->m_level - 1)) {
578 us[i] = cs[i] = ' ';
579 continue;
581 if (mq->m_younger) {
582 if (mq->m_parent && (mq->m_parent->m_flag & important))
583 us[i] = 0x2503;
584 else
585 us[i] = 0x2502;
586 cs[i] = '|';
587 } else
588 us[i] = cs[i] = ' ';
589 mq = mq->m_parent;
590 } else
591 us[i] = cs[i] = ' ';
594 --maxwidth;
595 for (indlvl = indw = 0; (ui8_t)indlvl < mp->m_level && indw < maxwidth;
596 ++indlvl) {
597 if (indw < maxwidth - 1)
598 indw += (int)a_chead__putuc(us[indlvl], cs[indlvl] & 0xFF, fp);
599 else
600 indw += (int)a_chead__putuc(0x21B8, '^', fp);
602 indw += a_chead__putuc(0x25B8, '>', fp);
604 n_lofi_free(us);
605 n_lofi_free(cs);
606 jleave:
607 NYD2_LEAVE;
608 return indw;
611 static size_t
612 a_chead__putuc(int u, int c, FILE *fp){
613 size_t rv;
614 NYD2_ENTER;
615 n_UNUSED(u);
617 #ifdef HAVE_NATCH_CHAR
618 if((n_psonce & n_PSO_UNICODE) && (u & ~(wchar_t)0177) &&
619 !ok_blook(headline_plain)){
620 char mbb[MB_LEN_MAX];
621 int i, n;
623 if((n = wctomb(mbb, u)) > 0){
624 rv = wcwidth(u);
625 for(i = 0; i < n; ++i)
626 if(putc(mbb[i] & 0377, fp) == EOF){
627 rv = 0;
628 break;
630 }else if(n == 0)
631 rv = (putc('\0', fp) != EOF);
632 else
633 rv = 0;
634 }else
635 #endif
636 rv = (putc(c, fp) != EOF);
637 NYD2_LEAVE;
638 return rv;
641 static int
642 a_chead__dispc(struct message *mp, char const *a)
644 int i = ' ';
645 NYD2_ENTER;
647 if ((mp->m_flag & (MREAD | MNEW)) == MREAD)
648 i = a[3];
649 if ((mp->m_flag & (MREAD | MNEW)) == (MREAD | MNEW))
650 i = a[2];
651 if (mp->m_flag & MANSWERED)
652 i = a[8];
653 if (mp->m_flag & MDRAFTED)
654 i = a[9];
655 if ((mp->m_flag & (MREAD | MNEW)) == MNEW)
656 i = a[0];
657 if (!(mp->m_flag & (MREAD | MNEW)))
658 i = a[1];
659 if (mp->m_flag & MSPAM)
660 i = a[12];
661 if (mp->m_flag & MSPAMUNSURE)
662 i = a[13];
663 if (mp->m_flag & MSAVED)
664 i = a[4];
665 if (mp->m_flag & MPRESERVE)
666 i = a[5];
667 if (mp->m_flag & (MBOX | MBOXED))
668 i = a[6];
669 if (mp->m_flag & MFLAGGED)
670 i = a[7];
671 if (mb.mb_threaded == 1) { /* TODO bad, and m_collapsed is weird */
672 /* TODO So this does not work because of weird thread handling and
673 * TODO intermixing view and controller except when run via -L from
674 * TODO command line; in general these flags should go and we need
675 * TODO specific *headline* formats which always work and indicate
676 * TODO whether a message is in a thread, the head of a subthread etc. */
677 if (mp->m_collapsed > 0)
678 i = a[11];
679 else if (mp->m_collapsed < 0)
680 i = a[10];
682 NYD2_LEAVE;
683 return i;
686 static int
687 a_chead_scroll(char const *arg, bool_t onlynew){
688 siz_t l;
689 bool_t isabs;
690 int msgspec, size, maxs;
691 NYD2_ENTER;
693 /* TODO scroll problem: we do not know whether + and $ have already reached
694 * TODO the last screen in threaded mode */
695 msgspec = onlynew ? -1 : 0;
696 size = (int)/*TODO*/n_screensize();
697 if((maxs = msgCount / size) > 0 && msgCount % size == 0)
698 --maxs;
700 if(arg == NULL)
701 arg = n_empty;
702 switch(*arg){
703 case '\0':
704 ++_screen;
705 goto jfwd;
706 case '^':
707 if(arg[1] != '\0')
708 goto jerr;
709 if(_screen == 0)
710 goto jerrbwd;
711 _screen = 0;
712 break;
713 case '$':
714 if(arg[1] != '\0')
715 goto jerr;
716 if(_screen == maxs)
717 goto jerrfwd;
718 _screen = maxs;
719 break;
720 case '+':
721 if(arg[1] == '\0')
722 ++_screen;
723 else{
724 isabs = FAL0;
726 ++arg;
727 if(0){
728 case '1': case '2': case '3': case '4': case '5':
729 case '6': case '7': case '8': case '9': case '0':
730 isabs = TRU1;
732 if((n_idec_siz_cp(&l, arg, 0, NULL
733 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
734 ) != n_IDEC_STATE_CONSUMED)
735 goto jerr;
736 if(l > maxs - (isabs ? 0 : _screen))
737 goto jerrfwd;
738 _screen = isabs ? (int)l : _screen + l;
740 jfwd:
741 if(_screen > maxs){
742 jerrfwd:
743 _screen = maxs;
744 fprintf(n_stdout, _("On last screenful of messages\n"));
746 break;
748 case '-':
749 if(arg[1] == '\0')
750 --_screen;
751 else{
752 if((n_idec_siz_cp(&l, ++arg, 0, NULL
753 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
754 ) != n_IDEC_STATE_CONSUMED)
755 goto jerr;
756 if(l > _screen)
757 goto jerrbwd;
758 _screen -= l;
760 if(_screen < 0){
761 jerrbwd:
762 _screen = 0;
763 fprintf(n_stdout, _("On first screenful of messages\n"));
765 if(msgspec == -1)
766 msgspec = -2;
767 break;
768 default:
769 jerr:
770 n_err(_("Unrecognized scrolling command: %s\n"), arg);
771 size = 1;
772 goto jleave;
775 size = _headers(msgspec);
776 jleave:
777 NYD2_LEAVE;
778 return size;
781 static int
782 _headers(int msgspec) /* TODO rework v15 */
784 bool_t needdot, showlast;
785 int g, k, mesg, size;
786 struct message *lastmq, *mp, *mq;
787 int volatile lastg;
788 ui32_t volatile flag;
789 enum mflag fl;
790 NYD_ENTER;
792 time_current_update(&time_current, FAL0);
794 fl = MNEW | MFLAGGED;
795 flag = 0;
796 lastg = 1;
797 lastmq = NULL;
799 size = (int)/*TODO*/n_screensize();
800 if (_screen < 0)
801 _screen = 0;
802 #if 0 /* FIXME original code path */
803 k = _screen * size;
804 #else
805 if (msgspec <= 0)
806 k = _screen * size;
807 else
808 k = msgspec;
809 #endif
810 if (k >= msgCount)
811 k = msgCount - size;
812 if (k < 0)
813 k = 0;
815 needdot = (msgspec <= 0) ? TRU1 : (dot != &message[msgspec - 1]);
816 showlast = ok_blook(showlast);
818 if (mb.mb_threaded == 0) {
819 g = 0;
820 mq = message;
821 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
822 if (visible(mp)) {
823 if (g % size == 0)
824 mq = mp;
825 if (mp->m_flag & fl) {
826 lastg = g;
827 lastmq = mq;
829 if ((msgspec > 0 && PTRCMP(mp, ==, message + msgspec - 1)) ||
830 (msgspec == 0 && g == k) ||
831 (msgspec == -2 && g == k + size && lastmq) ||
832 (msgspec < 0 && g >= k && (mp->m_flag & fl) != 0))
833 break;
834 g++;
836 if (lastmq && (msgspec == -2 ||
837 (msgspec == -1 && PTRCMP(mp, ==, message + msgCount)))) {
838 g = lastg;
839 mq = lastmq;
841 _screen = g / size;
842 mp = mq;
844 mesg = (int)PTR2SIZE(mp - message);
845 #ifdef HAVE_IMAP
846 if (mb.mb_type == MB_IMAP)
847 imap_getheaders(mesg + 1, mesg + size);
848 #endif
849 n_COLOUR( n_colour_env_create(n_COLOUR_CTX_SUM, n_stdout, FAL0); )
850 srelax_hold();
851 for(lastmq = NULL, mq = &message[msgCount]; mp < mq; lastmq = mp, ++mp){
852 ++mesg;
853 if (!visible(mp))
854 continue;
855 if (UICMP(32, flag, >=, size))
856 break;
857 if(needdot){
858 if(showlast){
859 if(UICMP(32, flag, ==, size - 1) || &mp[1] == mq)
860 goto jdot_unsort;
861 }else if(flag == 0){
862 jdot_unsort:
863 needdot = FAL0;
864 setdot(mp);
867 ++flag;
868 a_chead_print_head(0, mesg, n_stdout, FAL0, FAL0);
869 srelax();
871 if(needdot && ok_blook(showlast)) /* xxx will not show */
872 setdot(lastmq);
873 srelax_rele();
874 n_COLOUR( n_colour_env_gut(); )
875 } else { /* threaded */
876 g = 0;
877 mq = threadroot;
878 for (mp = threadroot; mp; mp = next_in_thread(mp)){
879 /* TODO thread handling needs rewrite, m_collapsed must go */
880 if (visible(mp) &&
881 (mp->m_collapsed <= 0 ||
882 PTRCMP(mp, ==, message + msgspec - 1))) {
883 if (g % size == 0)
884 mq = mp;
885 if (mp->m_flag & fl) {
886 lastg = g;
887 lastmq = mq;
889 if ((msgspec > 0 && PTRCMP(mp, ==, message + msgspec - 1)) ||
890 (msgspec == 0 && g == k) ||
891 (msgspec == -2 && g == k + size && lastmq) ||
892 (msgspec < 0 && g >= k && (mp->m_flag & fl) != 0))
893 break;
894 g++;
897 if (lastmq && (msgspec == -2 ||
898 (msgspec == -1 && PTRCMP(mp, ==, message + msgCount)))) {
899 g = lastg;
900 mq = lastmq;
902 _screen = g / size;
903 mp = mq;
905 n_COLOUR( n_colour_env_create(n_COLOUR_CTX_SUM, n_stdout, FAL0); )
906 srelax_hold();
907 for(lastmq = NULL; mp != NULL; lastmq = mp, mp = mq){
908 mq = next_in_thread(mp);
909 if (visible(mp) &&
910 (mp->m_collapsed <= 0 ||
911 PTRCMP(mp, ==, message + msgspec - 1))) {
912 if (UICMP(32, flag, >=, size))
913 break;
914 if(needdot){
915 if(showlast){
916 if(UICMP(32, flag, ==, size - 1) || mq == NULL)
917 goto jdot_sort;
918 }else if(flag == 0){
919 jdot_sort:
920 needdot = FAL0;
921 setdot(mp);
924 a_chead_print_head(flag, PTR2SIZE(mp - message + 1), n_stdout,
925 mb.mb_threaded, TRU1);
926 ++flag;
927 srelax();
930 if(needdot && ok_blook(showlast)) /* xxx will not show */
931 setdot(lastmq);
932 srelax_rele();
933 n_COLOUR( n_colour_env_gut(); )
936 if (flag == 0) {
937 fprintf(n_stdout, _("No more mail.\n"));
938 if (n_pstate & (n_PS_ROBOT | n_PS_HOOK_MASK))
939 flag = !flag;
941 NYD_LEAVE;
942 return !flag;
945 FL int
946 c_headers(void *v)
948 int rv;
949 NYD_ENTER;
951 rv = print_header_group((int*)v);
952 NYD_LEAVE;
953 return rv;
956 FL int
957 print_header_group(int *vector)
959 int rv;
960 NYD_ENTER;
962 assert(vector != NULL && vector != (void*)-1);
963 rv = _headers(vector[0]);
964 NYD_LEAVE;
965 return rv;
968 FL int
969 c_scroll(void *v)
971 int rv;
972 NYD_ENTER;
974 rv = a_chead_scroll(*(char const**)v, FAL0);
975 NYD_LEAVE;
976 return rv;
979 FL int
980 c_Scroll(void *v)
982 int rv;
983 NYD_ENTER;
985 rv = a_chead_scroll(*(char const**)v, TRU1);
986 NYD_LEAVE;
987 return rv;
990 FL int
991 c_dotmove(void *v)
993 char const *args;
994 int msgvec[2], rv;
995 NYD_ENTER;
997 if (*(args = v) == '\0' || args[1] != '\0') {
998 jerr:
999 n_err(_("Synopsis: dotmove: up <-> or down <+> by one message\n"));
1000 rv = 1;
1001 } else switch (args[0]) {
1002 case '-':
1003 case '+':
1004 if (msgCount == 0) {
1005 fprintf(n_stdout, _("At EOF\n"));
1006 rv = 0;
1007 } else if (n_getmsglist(n_UNCONST(/*TODO*/args), msgvec, 0, NULL) > 0) {
1008 setdot(message + msgvec[0] - 1);
1009 msgvec[1] = 0;
1010 rv = c_headers(msgvec);
1011 } else
1012 rv = 1;
1013 break;
1014 default:
1015 goto jerr;
1017 NYD_LEAVE;
1018 return rv;
1021 FL int
1022 c_from(void *vp)
1024 int *msgvec, *ip, n;
1025 char *cp;
1026 FILE * volatile obuf;
1027 NYD_ENTER;
1029 if(*(msgvec = vp) == 0)
1030 goto jleave;
1032 time_current_update(&time_current, FAL0);
1034 obuf = n_stdout;
1036 if (n_psonce & n_PSO_INTERACTIVE) {
1037 if ((cp = ok_vlook(crt)) != NULL) {
1038 uiz_t ib;
1040 for (n = 0, ip = msgvec; *ip != 0; ++ip)
1041 ++n;
1043 if(*cp == '\0')
1044 ib = n_screensize();
1045 else
1046 n_idec_uiz_cp(&ib, cp, 0, NULL);
1047 if (UICMP(z, n, >, ib) && (obuf = n_pager_open()) == NULL)
1048 obuf = n_stdout;
1052 /* Update dot before display so that the dotmark etc. are correct */
1053 for (ip = msgvec; ip[1] != 0; ++ip)
1055 setdot(&message[(ok_blook(showlast) ? *ip : *msgvec) - 1]);
1057 n_COLOUR( n_colour_env_create(n_COLOUR_CTX_SUM, obuf, obuf != n_stdout); )
1058 srelax_hold();
1059 for (n = 0, ip = msgvec; *ip != 0; ++ip) { /* TODO join into _print_head() */
1060 a_chead_print_head((size_t)n++, (size_t)*ip, obuf, mb.mb_threaded, FAL0);
1061 srelax();
1063 srelax_rele();
1064 n_COLOUR( n_colour_env_gut(); )
1066 if (obuf != n_stdout)
1067 n_pager_close(obuf);
1068 jleave:
1069 NYD_LEAVE;
1070 return 0;
1073 FL void
1074 print_headers(int const *msgvec, bool_t only_marked,
1075 bool_t subject_thread_compress)
1077 size_t printed;
1078 NYD_ENTER;
1080 time_current_update(&time_current, FAL0);
1082 n_COLOUR( n_colour_env_create(n_COLOUR_CTX_SUM, n_stdout, FAL0); )
1083 srelax_hold();
1084 for(printed = 0; *msgvec != 0; ++msgvec) {
1085 struct message *mp = message + *msgvec - 1;
1086 if (only_marked) {
1087 if (!(mp->m_flag & MMARK))
1088 continue;
1089 } else if (!visible(mp))
1090 continue;
1091 a_chead_print_head(printed++, *msgvec, n_stdout, mb.mb_threaded,
1092 subject_thread_compress);
1093 srelax();
1095 srelax_rele();
1096 n_COLOUR( n_colour_env_gut(); )
1097 NYD_LEAVE;
1100 /* s-it-mode */