a_go_evaluate(): fix un/signed comparison
[s-mailx.git] / cmd-headers.c
blob2c1a62cf56531a595d08e50666a1d6c85e981f94
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) { /* TODO bad, and m_collapsed is weird */
654 /* TODO So this does not work because of weird thread handling and
655 * TODO intermixing view and controller except when run via -L from
656 * TODO command line; in general these flags should go and we need
657 * TODO specific *headline* formats which always work and indicate
658 * TODO whether a message is in a thread, the head of a subthread etc. */
659 if (mp->m_collapsed > 0)
660 i = a[11];
661 else if (mp->m_collapsed < 0)
662 i = a[10];
664 NYD2_LEAVE;
665 return i;
668 static int
669 a_cmd_scroll(char const *arg, bool_t onlynew){
670 siz_t l;
671 bool_t isabs;
672 int msgspec, size, maxs;
673 NYD2_ENTER;
675 /* TODO scroll problem: we do not know whether + and $ have already reached
676 * TODO the last screen in threaded mode */
677 msgspec = onlynew ? -1 : 0;
678 size = (int)/*TODO*/n_screensize();
679 if((maxs = msgCount / size) > 0 && msgCount % size == 0)
680 --maxs;
682 if(arg == NULL)
683 arg = n_empty;
684 switch(*arg){
685 case '\0':
686 ++_screen;
687 goto jfwd;
688 case '^':
689 if(arg[1] != '\0')
690 goto jerr;
691 if(_screen == 0)
692 goto jerrbwd;
693 _screen = 0;
694 break;
695 case '$':
696 if(arg[1] != '\0')
697 goto jerr;
698 if(_screen == maxs)
699 goto jerrfwd;
700 _screen = maxs;
701 break;
702 case '+':
703 if(arg[1] == '\0')
704 ++_screen;
705 else{
706 isabs = FAL0;
708 ++arg;
709 if(0){
710 case '1': case '2': case '3': case '4': case '5':
711 case '6': case '7': case '8': case '9': case '0':
712 isabs = TRU1;
714 if((n_idec_siz_cp(&l, arg, 0, NULL
715 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
716 ) != n_IDEC_STATE_CONSUMED)
717 goto jerr;
718 if(l > maxs - (isabs ? 0 : _screen))
719 goto jerrfwd;
720 _screen = isabs ? (int)l : _screen + l;
722 jfwd:
723 if(_screen > maxs){
724 jerrfwd:
725 _screen = maxs;
726 fprintf(n_stdout, _("On last screenful of messages\n"));
728 break;
730 case '-':
731 if(arg[1] == '\0')
732 --_screen;
733 else{
734 if((n_idec_siz_cp(&l, ++arg, 0, NULL
735 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
736 ) != n_IDEC_STATE_CONSUMED)
737 goto jerr;
738 if(l > _screen)
739 goto jerrbwd;
740 _screen -= l;
742 if(_screen < 0){
743 jerrbwd:
744 _screen = 0;
745 fprintf(n_stdout, _("On first screenful of messages\n"));
747 if(msgspec == -1)
748 msgspec = -2;
749 break;
750 default:
751 jerr:
752 n_err(_("Unrecognized scrolling command: %s\n"), arg);
753 size = 1;
754 goto jleave;
757 size = _headers(msgspec);
758 jleave:
759 NYD2_LEAVE;
760 return size;
763 static int
764 _headers(int msgspec) /* TODO rework v15 */
766 bool_t needdot, showlast;
767 int g, k, mesg, size;
768 struct message *lastmq, *mp, *mq;
769 int volatile lastg;
770 ui32_t volatile flag;
771 enum mflag fl;
772 NYD_ENTER;
774 time_current_update(&time_current, FAL0);
776 fl = MNEW | MFLAGGED;
777 flag = 0;
778 lastg = 1;
779 lastmq = NULL;
781 size = (int)/*TODO*/n_screensize();
782 if (_screen < 0)
783 _screen = 0;
784 #if 0 /* FIXME original code path */
785 k = _screen * size;
786 #else
787 if (msgspec <= 0)
788 k = _screen * size;
789 else
790 k = msgspec;
791 #endif
792 if (k >= msgCount)
793 k = msgCount - size;
794 if (k < 0)
795 k = 0;
797 needdot = (msgspec <= 0) ? TRU1 : (dot != &message[msgspec - 1]);
798 showlast = ok_blook(showlast);
800 if (mb.mb_threaded == 0) {
801 g = 0;
802 mq = message;
803 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
804 if (visible(mp)) {
805 if (g % size == 0)
806 mq = mp;
807 if (mp->m_flag & fl) {
808 lastg = g;
809 lastmq = mq;
811 if ((msgspec > 0 && PTRCMP(mp, ==, message + msgspec - 1)) ||
812 (msgspec == 0 && g == k) ||
813 (msgspec == -2 && g == k + size && lastmq) ||
814 (msgspec < 0 && g >= k && (mp->m_flag & fl) != 0))
815 break;
816 g++;
818 if (lastmq && (msgspec == -2 ||
819 (msgspec == -1 && PTRCMP(mp, ==, message + msgCount)))) {
820 g = lastg;
821 mq = lastmq;
823 _screen = g / size;
824 mp = mq;
826 mesg = (int)PTR2SIZE(mp - message);
827 #ifdef HAVE_IMAP
828 if (mb.mb_type == MB_IMAP)
829 imap_getheaders(mesg + 1, mesg + size);
830 #endif
831 n_COLOUR( n_colour_env_create(n_COLOUR_CTX_SUM, n_stdout, FAL0); )
832 srelax_hold();
833 for(lastmq = NULL, mq = &message[msgCount]; mp < mq; lastmq = mp, ++mp){
834 ++mesg;
835 if (!visible(mp))
836 continue;
837 if (UICMP(32, flag, >=, size))
838 break;
839 if(needdot){
840 if(showlast){
841 if(UICMP(32, flag, ==, size - 1) || &mp[1] == mq)
842 goto jdot_unsort;
843 }else if(flag == 0){
844 jdot_unsort:
845 needdot = FAL0;
846 setdot(mp);
849 ++flag;
850 a_cmd_print_head(0, mesg, n_stdout, FAL0, FAL0);
851 srelax();
853 if(needdot && ok_blook(showlast)) /* xxx will not show */
854 setdot(lastmq);
855 srelax_rele();
856 n_COLOUR( n_colour_env_gut(); )
857 } else { /* threaded */
858 g = 0;
859 mq = threadroot;
860 for (mp = threadroot; mp; mp = next_in_thread(mp)){
861 /* TODO thread handling needs rewrite, m_collapsed must go */
862 if (visible(mp) &&
863 (mp->m_collapsed <= 0 ||
864 PTRCMP(mp, ==, message + msgspec - 1))) {
865 if (g % size == 0)
866 mq = mp;
867 if (mp->m_flag & fl) {
868 lastg = g;
869 lastmq = mq;
871 if ((msgspec > 0 && PTRCMP(mp, ==, message + msgspec - 1)) ||
872 (msgspec == 0 && g == k) ||
873 (msgspec == -2 && g == k + size && lastmq) ||
874 (msgspec < 0 && g >= k && (mp->m_flag & fl) != 0))
875 break;
876 g++;
879 if (lastmq && (msgspec == -2 ||
880 (msgspec == -1 && PTRCMP(mp, ==, message + msgCount)))) {
881 g = lastg;
882 mq = lastmq;
884 _screen = g / size;
885 mp = mq;
887 n_COLOUR( n_colour_env_create(n_COLOUR_CTX_SUM, n_stdout, FAL0); )
888 srelax_hold();
889 for(lastmq = NULL; mp != NULL; lastmq = mp, mp = mq){
890 mq = next_in_thread(mp);
891 if (visible(mp) &&
892 (mp->m_collapsed <= 0 ||
893 PTRCMP(mp, ==, message + msgspec - 1))) {
894 if (UICMP(32, flag, >=, size))
895 break;
896 if(needdot){
897 if(showlast){
898 if(UICMP(32, flag, ==, size - 1) || mq == NULL)
899 goto jdot_sort;
900 }else if(flag == 0){
901 jdot_sort:
902 needdot = FAL0;
903 setdot(mp);
906 a_cmd_print_head(flag, PTR2SIZE(mp - message + 1), n_stdout,
907 mb.mb_threaded, TRU1);
908 ++flag;
909 srelax();
912 if(needdot && ok_blook(showlast)) /* xxx will not show */
913 setdot(lastmq);
914 srelax_rele();
915 n_COLOUR( n_colour_env_gut(); )
918 if (flag == 0) {
919 fprintf(n_stdout, _("No more mail.\n"));
920 if (n_pstate & (n_PS_ROBOT | n_PS_HOOK_MASK))
921 flag = !flag;
923 NYD_LEAVE;
924 return !flag;
927 FL int
928 c_headers(void *v)
930 int rv;
931 NYD_ENTER;
933 rv = print_header_group((int*)v);
934 NYD_LEAVE;
935 return rv;
938 FL int
939 print_header_group(int *vector)
941 int rv;
942 NYD_ENTER;
944 assert(vector != NULL && vector != (void*)-1);
945 rv = _headers(vector[0]);
946 NYD_LEAVE;
947 return rv;
950 FL int
951 c_scroll(void *v)
953 int rv;
954 NYD_ENTER;
956 rv = a_cmd_scroll(*(char const**)v, FAL0);
957 NYD_LEAVE;
958 return rv;
961 FL int
962 c_Scroll(void *v)
964 int rv;
965 NYD_ENTER;
967 rv = a_cmd_scroll(*(char const**)v, TRU1);
968 NYD_LEAVE;
969 return rv;
972 FL int
973 c_dotmove(void *v)
975 char const *args;
976 int msgvec[2], rv;
977 NYD_ENTER;
979 if (*(args = v) == '\0' || args[1] != '\0') {
980 jerr:
981 n_err(_("Synopsis: dotmove: up <-> or down <+> by one message\n"));
982 rv = 1;
983 } else switch (args[0]) {
984 case '-':
985 case '+':
986 if (msgCount == 0) {
987 fprintf(n_stdout, _("At EOF\n"));
988 rv = 0;
989 } else if (n_getmsglist(n_UNCONST(/*TODO*/args), msgvec, 0, NULL) > 0) {
990 setdot(message + msgvec[0] - 1);
991 msgvec[1] = 0;
992 rv = c_headers(msgvec);
993 } else
994 rv = 1;
995 break;
996 default:
997 goto jerr;
999 NYD_LEAVE;
1000 return rv;
1003 FL int
1004 c_from(void *vp)
1006 int *msgvec, *ip, n;
1007 char *cp;
1008 FILE * volatile obuf;
1009 NYD_ENTER;
1011 if(*(msgvec = vp) == 0)
1012 goto jleave;
1014 time_current_update(&time_current, FAL0);
1016 obuf = n_stdout;
1018 if (n_psonce & n_PSO_INTERACTIVE) {
1019 if ((cp = ok_vlook(crt)) != NULL) {
1020 uiz_t ib;
1022 for (n = 0, ip = msgvec; *ip != 0; ++ip)
1023 ++n;
1025 if(*cp == '\0')
1026 ib = n_screensize();
1027 else
1028 n_idec_uiz_cp(&ib, cp, 0, NULL);
1029 if (UICMP(z, n, >, ib) && (obuf = n_pager_open()) == NULL)
1030 obuf = n_stdout;
1034 /* Update dot before display so that the dotmark etc. are correct */
1035 for (ip = msgvec; ip[1] != 0; ++ip)
1037 setdot(&message[(ok_blook(showlast) ? *ip : *msgvec) - 1]);
1039 n_COLOUR( n_colour_env_create(n_COLOUR_CTX_SUM, obuf, obuf != n_stdout); )
1040 srelax_hold();
1041 for (n = 0, ip = msgvec; *ip != 0; ++ip) { /* TODO join into _print_head() */
1042 a_cmd_print_head((size_t)n++, (size_t)*ip, obuf, mb.mb_threaded, FAL0);
1043 srelax();
1045 srelax_rele();
1046 n_COLOUR( n_colour_env_gut(); )
1048 if (obuf != n_stdout)
1049 n_pager_close(obuf);
1050 jleave:
1051 NYD_LEAVE;
1052 return 0;
1055 FL void
1056 print_headers(int const *msgvec, bool_t only_marked,
1057 bool_t subject_thread_compress)
1059 size_t printed;
1060 NYD_ENTER;
1062 time_current_update(&time_current, FAL0);
1064 n_COLOUR( n_colour_env_create(n_COLOUR_CTX_SUM, n_stdout, FAL0); )
1065 srelax_hold();
1066 for(printed = 0; *msgvec != 0; ++msgvec) {
1067 struct message *mp = message + *msgvec - 1;
1068 if (only_marked) {
1069 if (!(mp->m_flag & MMARK))
1070 continue;
1071 } else if (!visible(mp))
1072 continue;
1073 a_cmd_print_head(printed++, *msgvec, n_stdout, mb.mb_threaded,
1074 subject_thread_compress);
1075 srelax();
1077 srelax_rele();
1078 n_COLOUR( n_colour_env_gut(); )
1079 NYD_LEAVE;
1082 /* s-it-mode */