1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Header display, search, etc., related user commands.
3 *
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2020 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.
11 *
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.
23 *
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.
35 */
36 #undef su_FILE
37 #define su_FILE cmd_head
38 #define mx_SOURCE
39
40 #ifndef mx_HAVE_AMALGAMATION
41 # include "mx/nail.h"
42 #endif
43
44 #include <su/cs.h>
45 #include <su/icodec.h>
46 #include <su/mem.h>
47
48 #include "mx/cmd.h"
49 #include "mx/cmd-mlist.h"
50 #include "mx/colour.h"
51 #include "mx/termios.h"
52 #include "mx/ui-str.h"
53
54 /* TODO fake */
55 #include "su/code-in.h"
56
57 static int _screen;
58
59 /* Print out the header of a specific message.
60 * time_current must be up-to-date when this is called.
61 * a_chead__hprf: handle *headline*
62 * a_chead__subject: -1 if Subject: yet seen, otherwise n_alloc()d Subject:
63 * a_chead__putindent: print out the indenting in threaded display
64 * a_chead__putuc: print out a Unicode character or a substitute for it, return
65 * 0 on error or wcwidth() (or 1) on success */
66 static void a_chead_print_head(uz yetprinted, uz msgno, FILE *f,
67 boole threaded, boole subject_thread_compress);
68
69 static void a_chead__hprf(uz yetprinted, char const *fmt, uz msgno,
70 FILE *f, boole threaded, boole subject_thread_compress,
71 char const *attrlist);
72 static char *a_chead__subject(struct message *mp, boole threaded,
73 boole subject_thread_compress, uz yetprinted);
74 static int a_chead__putindent(FILE *fp, struct message *mp, int maxwidth);
75 static uz a_chead__putuc(int u, int c, FILE *fp);
76 static int a_chead__dispc(struct message *mp, char const *a);
77
78 /* Shared `z' implementation */
79 static int a_chead_scroll(char const *arg, boole onlynew);
80
81 /* Shared `headers' implementation */
82 static int _headers(int msgspec);
83
84 static void
a_chead_print_head(uz yetprinted,uz msgno,FILE * f,boole threaded,boole subject_thread_compress)85 a_chead_print_head(uz yetprinted, uz msgno, FILE *f, boole threaded,
86 boole subject_thread_compress){
87 enum {attrlen = 14};
88 char attrlist[attrlen +1], *cp;
89 char const *fmt;
90 NYD2_IN;
91
92 if((cp = ok_vlook(attrlist)) != NULL){
93 if(su_cs_len(cp) == attrlen){
94 su_mem_copy(attrlist, cp, attrlen +1);
95 goto jattrok;
96 }
97 n_err(_("*attrlist* is not of the correct length, using built-in\n"));
98 }
99
100 if(ok_blook(bsdcompat) || ok_blook(bsdflags)){
101 char const bsdattr[attrlen +1] = "NU *HMFAT+-$~";
102
103 su_mem_copy(attrlist, bsdattr, sizeof bsdattr);
104 }else if(ok_blook(SYSV3)){
105 char const bsdattr[attrlen +1] = "NU *HMFAT+-$~";
106
107 su_mem_copy(attrlist, bsdattr, sizeof bsdattr);
108 n_OBSOLETE(_("*SYSV3*: please use *bsdcompat* or *bsdflags*, "
109 "or set *attrlist*"));
110 }else{
111 char const pattr[attrlen +1] = "NUROSPMFAT+-$~";
112
113 su_mem_copy(attrlist, pattr, sizeof pattr);
114 }
115
116 jattrok:
117 if((fmt = ok_vlook(headline)) == NULL){
118 fmt = ((ok_blook(bsdcompat) || ok_blook(bsdheadline))
119 ? "%>%a%m %-20f %16d %4l/%-5o %i%-S"
120 : "%>%a%m %-18f %-16d %4l/%-5o %i%-s");
121 }
122
123 a_chead__hprf(yetprinted, fmt, msgno, f, threaded, subject_thread_compress,
124 attrlist);
125 NYD2_OU;
126 }
127
128 static void
a_chead__hprf(uz yetprinted,char const * fmt,uz msgno,FILE * f,boole threaded,boole subject_thread_compress,char const * attrlist)129 a_chead__hprf(uz yetprinted, char const *fmt, uz msgno, FILE *f,
130 boole threaded, boole subject_thread_compress, char const *attrlist)
131 {
132 char buf[16], cbuf[8], *cp, *subjline;
133 char const *date, *name, *fp, *color_tag;
134 int i, n, s, wleft, subjlen;
135 struct message *mp;
136 mx_COLOUR( struct mx_colour_pen *cpen_new su_COMMA
137 *cpen_cur su_COMMA *cpen_bas; )
138 enum {
139 _NONE = 0,
140 _ISDOT = 1<<0,
141 _ISTO = 1<<1,
142 _IFMT = 1<<2,
143 _LOOP_MASK = (1<<4) - 1,
144 _SFMT = 1<<4, /* It is 'S' */
145 /* For the simple byte-based counts in wleft and n we sometimes need
146 * adjustments to compensate for additional bytes of UTF-8 sequences */
147 _PUTCB_UTF8_SHIFT = 5,
148 _PUTCB_UTF8_MASK = 3<<5
149 } flags = _NONE;
150 NYD2_IN;
151 UNUSED(buf);
152
153 if ((mp = message + msgno - 1) == dot)
154 flags = _ISDOT;
155
156 color_tag = NULL;
157 date = n_header_textual_date_info(mp, &color_tag);
158 /* C99 */{
159 boole isto;
160
161 n_header_textual_sender_info(mp, &cp, NULL, NULL, NULL, &isto);
162 name = cp;
163 if(isto)
164 flags |= _ISTO;
165 }
166
167 subjline = NULL;
168
169 /* Detect the width of the non-format characters in *headline*;
170 * like that we can simply use putc() in the next loop, since we have
171 * already calculated their column widths (TODO it's sick) */
172 wleft = subjlen = mx_termios_dimen.tiosd_width;
173
174 for (fp = fmt; *fp != '\0'; ++fp) {
175 if (*fp == '%') {
176 if (*++fp == '-')
177 ++fp;
178 else if (*fp == '+')
179 ++fp;
180 if (su_cs_is_digit(*fp)) {
181 n = 0;
182 do
183 n = 10*n + *fp - '0';
184 while (++fp, su_cs_is_digit(*fp));
185 subjlen -= n;
186 }
187 if (*fp == 'i')
188 flags |= _IFMT;
189
190 if (*fp == '\0')
191 break;
192 } else {
193 #ifdef mx_HAVE_WCWIDTH
194 if (n_mb_cur_max > 1) {
195 wchar_t wc;
196 if ((s = mbtowc(&wc, fp, n_mb_cur_max)) == -1)
197 n = s = 1;
198 else if ((n = wcwidth(wc)) == -1)
199 n = 1;
200 } else
201 #endif
202 n = s = 1;
203 subjlen -= n;
204 wleft -= n;
205 while (--s > 0)
206 ++fp;
207 }
208 }
209
210 /* Walk *headline*, producing output TODO not (really) MB safe */
211 #ifdef mx_HAVE_COLOUR
212 if(mx_COLOUR_IS_ACTIVE()){
213 if(flags & _ISDOT)
214 color_tag = mx_COLOUR_TAG_SUM_DOT;
215 cpen_bas = mx_colour_pen_create(mx_COLOUR_ID_SUM_HEADER, color_tag);
216 mx_colour_pen_put(cpen_new = cpen_cur = cpen_bas);
217 }else
218 cpen_new = cpen_bas = cpen_cur = NULL;
219 #endif
220
221 for (fp = fmt; *fp != '\0'; ++fp) {
222 char c;
223
224 if ((c = *fp & 0xFF) != '%') {
225 mx_COLOUR(
226 if(mx_COLOUR_IS_ACTIVE() && (cpen_new = cpen_bas) != cpen_cur)
227 mx_colour_pen_put(cpen_cur = cpen_new);
228 );
229 putc(c, f);
230 continue;
231 }
232
233 flags &= _LOOP_MASK;
234 n = 0;
235 s = 1;
236 if ((c = *++fp) == '-') {
237 s = -1;
238 ++fp;
239 } else if (c == '+')
240 ++fp;
241 if (su_cs_is_digit(*fp)) {
242 do
243 n = 10*n + *fp - '0';
244 while (++fp, su_cs_is_digit(*fp));
245 }
246
247 if ((c = *fp & 0xFF) == '\0')
248 break;
249 n *= s;
250
251 cbuf[1] = '\0';
252 switch (c) {
253 case '%':
254 goto jputcb;
255 case '>':
256 case '<':
257 if (flags & _ISDOT) {
258 mx_COLOUR(
259 if(mx_COLOUR_IS_ACTIVE())
260 cpen_new = mx_colour_pen_create(mx_COLOUR_ID_SUM_DOTMARK,
261 color_tag);
262 );
263 if((n_psonce & n_PSO_UNICODE) && !ok_blook(headline_plain)){
264 if (c == '>')
265 /* 25B8;BLACK RIGHT-POINTING SMALL TRIANGLE */
266 cbuf[1] = (char)0x96, cbuf[2] = (char)0xB8;
267 else
268 /* 25C2;BLACK LEFT-POINTING SMALL TRIANGLE */
269 cbuf[1] = (char)0x97, cbuf[2] = (char)0x82;
270 c = (char)0xE2;
271 cbuf[3] = '\0';
272 flags |= 2 << _PUTCB_UTF8_SHIFT;
273 }
274 } else
275 c = ' ';
276 goto jputcb;
277 case '$':
278 #ifdef mx_HAVE_SPAM
279 if (n == 0)
280 n = 5;
281 if (UCMP(32, ABS(n), >, wleft))
282 wleft = 0;
283 else{
284 snprintf(buf, sizeof buf, "%u.%02u",
285 (mp->m_spamscore >> 8), (mp->m_spamscore & 0xFF));
286 n = fprintf(f, "%*s", n, buf);
287 wleft = (n >= 0) ? wleft - n : 0;
288 }
289 break;
290 #else
291 c = '?';
292 goto jputcb;
293 #endif
294 case 'a':
295 c = a_chead__dispc(mp, attrlist);
296 jputcb:
297 #ifdef mx_HAVE_COLOUR
298 if(mx_COLOUR_IS_ACTIVE()){
299 if(cpen_new == cpen_cur)
300 cpen_new = cpen_bas;
301 if(cpen_new != cpen_cur)
302 mx_colour_pen_put(cpen_cur = cpen_new);
303 }
304 #endif
305 if (UCMP(32, ABS(n), >, wleft))
306 n = (n < 0) ? -wleft : wleft;
307 cbuf[0] = c;
308 n = fprintf(f, "%*s", n, cbuf);
309 if (n >= 0) {
310 wleft -= n;
311 if ((n = (flags & _PUTCB_UTF8_MASK)) != 0) {
312 n >>= _PUTCB_UTF8_SHIFT;
313 wleft += n;
314 }
315 } else {
316 wleft = 0; /* TODO I/O error.. ? break? */
317 }
318 #ifdef mx_HAVE_COLOUR
319 if(mx_COLOUR_IS_ACTIVE() && (cpen_new = cpen_bas) != cpen_cur)
320 mx_colour_pen_put(cpen_cur = cpen_new);
321 #endif
322 break;
323 case 'd':
324 if (n == 0)
325 n = 16;
326 if (UCMP(32, ABS(n), >, wleft))
327 n = (n < 0) ? -wleft : wleft;
328 n = fprintf(f, "%*.*s", n, ABS(n), date);
329 wleft = (n >= 0) ? wleft - n : 0;
330 break;
331 case 'e':
332 if (n == 0)
333 n = 2;
334 if (UCMP(32, ABS(n), >, wleft))
335 wleft = 0;
336 else{
337 n = fprintf(f, "%*u", n, (threaded == 1 ? mp->m_level : 0));
338 wleft = (n >= 0) ? wleft - n : 0;
339 }
340 break;
341 case 'f':
342 if (n == 0) {
343 n = 18;
344 if (s < 0)
345 n = -n;
346 }
347 i = ABS(n);
348 if (i > wleft) {
349 i = wleft;
350 n = (n < 0) ? -wleft : wleft;
351 }
352 if (flags & _ISTO) {/* XXX tr()! */
353 if(wleft <= 3){
354 wleft = 0;
355 break;
356 }
357 i -= 3;
358 }
359 n = fprintf(f, "%s%s", ((flags & _ISTO) ? "To " : n_empty),
360 colalign(name, i, n, &wleft));
361 if (n < 0)
362 wleft = 0;
363 else if (flags & _ISTO)
364 wleft -= 3;
365 break;
366 case 'i':
367 if (threaded) {
368 #ifdef mx_HAVE_COLOUR
369 if(mx_COLOUR_IS_ACTIVE()){
370 cpen_new = mx_colour_pen_create(mx_COLOUR_ID_SUM_THREAD,
371 color_tag);
372 if(cpen_new != cpen_cur)
373 mx_colour_pen_put(cpen_cur = cpen_new);
374 }
375 #endif
376 n = a_chead__putindent(f, mp,
377 MIN(wleft, S(int,mx_termios_dimen.tiosd_width) - 60));
378 wleft = (n >= 0) ? wleft - n : 0;
379 #ifdef mx_HAVE_COLOUR
380 if(mx_COLOUR_IS_ACTIVE() && (cpen_new = cpen_bas) != cpen_cur)
381 mx_colour_pen_put(cpen_cur = cpen_new);
382 #endif
383 }
384 break;
385 case 'L': /* ML status */
386 jmlist: /* v15compat */
387 switch(mx_mlist_query_mp(mp, mx_MLIST_OTHER)){
388 case mx_MLIST_OTHER: c = ' '; break;
389 case mx_MLIST_KNOWN: c = 'l'; break;
390 case mx_MLIST_SUBSCRIBED: c = 'L'; break;
391 case mx_MLIST_POSSIBLY: c = 'P'; break;
392 }
393 goto jputcb;
394 case 'l':
395 if (n == 0)
396 n = 4;
397 if (UCMP(32, ABS(n), >, wleft))
398 wleft = 0;
399 else if (mp->m_xlines) {
400 n = fprintf(f, "%*ld", n, mp->m_xlines);
401 wleft = (n >= 0) ? wleft - n : 0;
402 } else {
403 n = ABS(n);
404 wleft -= n;
405 while (n-- != 0)
406 putc(' ', f);
407 }
408 break;
409 case 'm':
410 if (n == 0) {
411 n = 3;
412 if (threaded)
413 for (i = msgCount; i > 999; i /= 10)
414 ++n;
415 }
416 if (UCMP(32, ABS(n), >, wleft))
417 wleft = 0;
418 else{
419 n = fprintf(f, "%*lu", n, (ul)msgno);
420 wleft = (n >= 0) ? wleft - n : 0;
421 }
422 break;
423 case 'o':
424 if (n == 0)
425 n = -5;
426 if (UCMP(32, ABS(n), >, wleft))
427 wleft = 0;
428 else{
429 n = fprintf(f, "%*lu", n, (ul)mp->m_xsize);
430 wleft = (n >= 0) ? wleft - n : 0;
431 }
432 break;
433 case 'S':
434 flags |= _SFMT;
435 /*FALLTHRU*/
436 case 's':
437 if (n == 0)
438 n = subjlen - 2;
439 if (n > 0 && s < 0)
440 n = -n;
441 if (subjlen > wleft)
442 subjlen = wleft;
443 if (UCMP(32, ABS(n), >, subjlen))
444 n = (n < 0) ? -subjlen : subjlen;
445 if (flags & _SFMT)
446 n -= (n < 0) ? -2 : 2;
447 if (n == 0)
448 break;
449 if (subjline == NULL)
450 subjline = a_chead__subject(mp, (threaded && (flags & _IFMT)),
451 subject_thread_compress, yetprinted);
452 if (subjline == (char*)-1) {
453 n = fprintf(f, "%*s", n, n_empty);
454 wleft = (n >= 0) ? wleft - n : 0;
455 } else {
456 n = fprintf(f, ((flags & _SFMT) ? "\"%s\"" : "%s"),
457 colalign(subjline, ABS(n), n, &wleft));
458 if (n < 0)
459 wleft = 0;
460 }
461 break;
462 case 'T':
463 n_OBSOLETE("*headline*: please use %L not %T for mailing-list "
464 "status");
465 goto jmlist;
466 case 't':
467 if (n == 0) {
468 n = 3;
469 if (threaded)
470 for (i = msgCount; i > 999; i /= 10)
471 ++n;
472 }
473 if (UCMP(32, ABS(n), >, wleft))
474 wleft = 0;
475 else{
476 n = fprintf(f, "%*lu",
477 n, (threaded ? (ul)mp->m_threadpos : (ul)msgno));
478 wleft = (n >= 0) ? wleft - n : 0;
479 }
480 break;
481 case 'U':
482 #ifdef mx_HAVE_IMAP
483 if (n == 0)
484 n = 9;
485 if (UCMP(32, ABS(n), >, wleft))
486 wleft = 0;
487 else{
488 n = fprintf(f, "%*" PRIu64 , n, mp->m_uid);
489 wleft = (n >= 0) ? wleft - n : 0;
490 }
491 break;
492 #else
493 c = '0';
494 goto jputcb;
495 #endif
496 default:
497 if (n_poption & n_PO_D_V)
498 n_err(_("Unknown *headline* format: %%%c\n"), c);
499 c = '?';
500 goto jputcb;
501 }
502
503 if (wleft <= 0)
504 break;
505 }
506
507 mx_COLOUR( mx_colour_reset(); )
508 putc('\n', f);
509
510 if (subjline != NULL && subjline != (char*)-1)
511 n_free(subjline);
512 NYD2_OU;
513 }
514
515 static char *
a_chead__subject(struct message * mp,boole threaded,boole subject_thread_compress,uz yetprinted)516 a_chead__subject(struct message *mp, boole threaded,
517 boole subject_thread_compress, uz yetprinted)
518 {
519 struct str in, out;
520 char *rv, *ms;
521 NYD2_IN;
522
523 rv = (char*)-1;
524
525 if ((ms = hfield1("subject", mp)) == NULL)
526 goto jleave;
527
528 in.l = su_cs_len(in.s = ms);
529 mime_fromhdr(&in, &out, TD_ICONV | TD_ISPR);
530 rv = ms = out.s;
531
532 if (!threaded || !subject_thread_compress || mp->m_level == 0)
533 goto jleave;
534
535 /* In a display thread - check whether this message uses the same
536 * Subject: as it's parent or elder neighbour, suppress printing it if
537 * this is the case. To extend this a bit, ignore any leading Re: or
538 * Fwd: plus follow-up WS. Ignore invisible messages along the way */
539 ms = n_UNCONST(subject_re_trim(n_UNCONST(ms)));
540
541 for (; (mp = prev_in_thread(mp)) != NULL && yetprinted-- > 0;) {
542 char *os;
543
544 if (visible(mp) && (os = hfield1("subject", mp)) != NULL) {
545 struct str oout;
546 int x;
547
548 in.l = su_cs_len(in.s = os);
549 mime_fromhdr(&in, &oout, TD_ICONV | TD_ISPR);
550 x = su_cs_cmp_case(ms, subject_re_trim(oout.s));
551 n_free(oout.s);
552
553 if (!x) {
554 n_free(out.s);
555 rv = (char*)-1;
556 }
557 break;
558 }
559 }
560 jleave:
561 NYD2_OU;
562 return rv;
563 }
564
565 static int
a_chead__putindent(FILE * fp,struct message * mp,int maxwidth)566 a_chead__putindent(FILE *fp, struct message *mp, int maxwidth)/* XXX magics */
567 {
568 struct message *mq;
569 int *unis, indlvl, indw, i, important = MNEW | MFLAGGED;
570 char *cs;
571 NYD2_IN;
572
573 if (mp->m_level == 0 || maxwidth == 0) {
574 indw = 0;
575 goto jleave;
576 }
577
578 cs = n_lofi_alloc(mp->m_level);
579 unis = n_lofi_alloc(mp->m_level * sizeof *unis);
580
581 i = mp->m_level - 1;
582 if (mp->m_younger && UCMP(32, i + 1, ==, mp->m_younger->m_level)) {
583 if (mp->m_parent && mp->m_parent->m_flag & important)
584 unis[i] = mp->m_flag & important ? 0x2523 : 0x2520;
585 else
586 unis[i] = mp->m_flag & important ? 0x251D : 0x251C;
587 cs[i] = '+';
588 } else {
589 if (mp->m_parent && mp->m_parent->m_flag & important)
590 unis[i] = mp->m_flag & important ? 0x2517 : 0x2516;
591 else
592 unis[i] = mp->m_flag & important ? 0x2515 : 0x2514;
593 cs[i] = '\\';
594 }
595
596 mq = mp->m_parent;
597 for (i = mp->m_level - 2; i >= 0; --i) {
598 if (mq) {
599 if (UCMP(32, i, >, mq->m_level - 1)) {
600 unis[i] = cs[i] = ' ';
601 continue;
602 }
603 if (mq->m_younger) {
604 if (mq->m_parent && (mq->m_parent->m_flag & important))
605 unis[i] = 0x2503;
606 else
607 unis[i] = 0x2502;
608 cs[i] = '|';
609 } else
610 unis[i] = cs[i] = ' ';
611 mq = mq->m_parent;
612 } else
613 unis[i] = cs[i] = ' ';
614 }
615
616 --maxwidth;
617 for (indlvl = indw = 0; (u8)indlvl < mp->m_level && indw < maxwidth;
618 ++indlvl) {
619 if (indw < maxwidth - 1)
620 indw += (int)a_chead__putuc(unis[indlvl], cs[indlvl] & 0xFF, fp);
621 else
622 indw += (int)a_chead__putuc(0x21B8, '^', fp);
623 }
624 indw += a_chead__putuc(0x25B8, '>', fp);
625
626 n_lofi_free(unis);
627 n_lofi_free(cs);
628 jleave:
629 NYD2_OU;
630 return indw;
631 }
632
633 static uz
a_chead__putuc(int u,int c,FILE * fp)634 a_chead__putuc(int u, int c, FILE *fp){
635 uz rv;
636 NYD2_IN;
637 UNUSED(u);
638
639 #ifdef mx_HAVE_NATCH_CHAR
640 if((n_psonce & n_PSO_UNICODE) && (u & ~(wchar_t)0177) &&
641 !ok_blook(headline_plain)){
642 char mbb[MB_LEN_MAX];
643 int i, n;
644
645 if((n = wctomb(mbb, u)) > 0){
646 rv = wcwidth(u);
647 for(i = 0; i < n; ++i)
648 if(putc(mbb[i] & 0377, fp) == EOF){
649 rv = 0;
650 break;
651 }
652 }else if(n == 0)
653 rv = (putc('\0', fp) != EOF);
654 else
655 rv = 0;
656 }else
657 #endif
658 rv = (putc(c, fp) != EOF);
659 NYD2_OU;
660 return rv;
661 }
662
663 static int
a_chead__dispc(struct message * mp,char const * a)664 a_chead__dispc(struct message *mp, char const *a)
665 {
666 int i = ' ';
667 NYD2_IN;
668
669 if ((mp->m_flag & (MREAD | MNEW)) == MREAD)
670 i = a[3];
671 if ((mp->m_flag & (MREAD | MNEW)) == (MREAD | MNEW))
672 i = a[2];
673 if (mp->m_flag & MANSWERED)
674 i = a[8];
675 if (mp->m_flag & MDRAFTED)
676 i = a[9];
677 if ((mp->m_flag & (MREAD | MNEW)) == MNEW)
678 i = a[0];
679 if (!(mp->m_flag & (MREAD | MNEW)))
680 i = a[1];
681 if (mp->m_flag & MSPAM)
682 i = a[12];
683 if (mp->m_flag & MSPAMUNSURE)
684 i = a[13];
685 if (mp->m_flag & MSAVED)
686 i = a[4];
687 if (mp->m_flag & MPRESERVE)
688 i = a[5];
689 if (mp->m_flag & (MBOX | MBOXED))
690 i = a[6];
691 if (mp->m_flag & MFLAGGED)
692 i = a[7];
693 if (mb.mb_threaded == 1) { /* TODO bad, and m_collapsed is weird */
694 /* TODO So this does not work because of weird thread handling and
695 * TODO intermixing view and controller except when run via -L from
696 * TODO command line; in general these flags should go and we need
697 * TODO specific *headline* formats which always work and indicate
698 * TODO whether a message is in a thread, the head of a subthread etc. */
699 if (mp->m_collapsed > 0)
700 i = a[11];
701 else if (mp->m_collapsed < 0)
702 i = a[10];
703 }
704 NYD2_OU;
705 return i;
706 }
707
708 static int
a_chead_scroll(char const * arg,boole onlynew)709 a_chead_scroll(char const *arg, boole onlynew){
710 sz l;
711 boole isabs;
712 int msgspec, size, maxs;
713 NYD2_IN;
714
715 /* TODO scroll problem: we do not know whether + and $ have already reached
716 * TODO the last screen in threaded mode */
717 msgspec = onlynew ? -1 : 0;
718 size = (int)/*TODO*/n_screensize();
719 if((maxs = msgCount / size) > 0 && msgCount % size == 0)
720 --maxs;
721
722 if(arg == NULL)
723 arg = n_empty;
724 switch(*arg){
725 case '\0':
726 ++_screen;
727 goto jfwd;
728 case '^':
729 if(arg[1] != '\0')
730 goto jerr;
731 if(_screen == 0)
732 goto jerrbwd;
733 _screen = 0;
734 break;
735 case '$':
736 if(arg[1] != '\0')
737 goto jerr;
738 if(_screen == maxs)
739 goto jerrfwd;
740 _screen = maxs;
741 break;
742 case '+':
743 if(arg[1] == '\0')
744 ++_screen;
745 else{
746 isabs = FAL0;
747
748 ++arg;
749 if(0){
750 case '1': case '2': case '3': case '4': case '5':
751 case '6': case '7': case '8': case '9': case '0':
752 isabs = TRU1;
753 }
754 if((su_idec_sz_cp(&l, arg, 0, NULL
755 ) & (su_IDEC_STATE_EMASK | su_IDEC_STATE_CONSUMED)
756 ) != su_IDEC_STATE_CONSUMED)
757 goto jerr;
758 if(l > maxs - (isabs ? 0 : _screen))
759 goto jerrfwd;
760 _screen = isabs ? (int)l : _screen + l;
761 }
762 jfwd:
763 if(_screen > maxs){
764 jerrfwd:
765 _screen = maxs;
766 fprintf(n_stdout, _("On last screenful of messages\n"));
767 }
768 break;
769
770 case '-':
771 if(arg[1] == '\0')
772 --_screen;
773 else{
774 if((su_idec_sz_cp(&l, ++arg, 0, NULL
775 ) & (su_IDEC_STATE_EMASK | su_IDEC_STATE_CONSUMED)
776 ) != su_IDEC_STATE_CONSUMED)
777 goto jerr;
778 if(l > _screen)
779 goto jerrbwd;
780 _screen -= l;
781 }
782 if(_screen < 0){
783 jerrbwd:
784 _screen = 0;
785 fprintf(n_stdout, _("On first screenful of messages\n"));
786 }
787 if(msgspec == -1)
788 msgspec = -2;
789 break;
790 default:
791 jerr:
792 n_err(_("Unrecognized scrolling command: %s\n"), arg);
793 size = 1;
794 goto jleave;
795 }
796
797 size = _headers(msgspec);
798 jleave:
799 NYD2_OU;
800 return size;
801 }
802
803 static int
_headers(int msgspec)804 _headers(int msgspec) /* TODO rework v15 */
805 {
806 boole needdot, showlast;
807 int g, k, mesg, size;
808 struct message *lastmq, *mp, *mq;
809 int volatile lastg;
810 u32 volatile flag;
811 enum mflag fl;
812 NYD_IN;
813
814 time_current_update(&time_current, FAL0);
815
816 fl = MNEW | MFLAGGED;
817 flag = 0;
818 lastg = 1;
819 lastmq = NULL;
820
821 size = (int)/*TODO*/n_screensize();
822 if (_screen < 0)
823 _screen = 0;
824 #if 0 /* FIXME original code path */
825 k = _screen * size;
826 #else
827 if (msgspec <= 0)
828 k = _screen * size;
829 else
830 k = msgspec;
831 #endif
832 if (k >= msgCount)
833 k = msgCount - size;
834 if (k < 0)
835 k = 0;
836
837 needdot = (msgspec <= 0) ? TRU1 : (dot != &message[msgspec - 1]);
838 showlast = ok_blook(showlast);
839
840 if (mb.mb_threaded == 0) {
841 g = 0;
842 mq = message;
843 for (mp = message; PCMP(mp, <, message + msgCount); ++mp)
844 if (visible(mp)) {
845 if (g % size == 0)
846 mq = mp;
847 if (mp->m_flag & fl) {
848 lastg = g;
849 lastmq = mq;
850 }
851 if ((msgspec > 0 && PCMP(mp, ==, message + msgspec - 1)) ||
852 (msgspec == 0 && g == k) ||
853 (msgspec == -2 && g == k + size && lastmq) ||
854 (msgspec < 0 && g >= k && (mp->m_flag & fl) != 0))
855 break;
856 g++;
857 }
858 if (lastmq && (msgspec == -2 ||
859 (msgspec == -1 && PCMP(mp, ==, message + msgCount)))) {
860 g = lastg;
861 mq = lastmq;
862 }
863 _screen = g / size;
864 mp = mq;
865
866 mesg = (int)P2UZ(mp - message);
867 #ifdef mx_HAVE_IMAP
868 if (mb.mb_type == MB_IMAP)
869 imap_getheaders(mesg + 1, mesg + size);
870 #endif
871 mx_COLOUR( mx_colour_env_create(mx_COLOUR_CTX_SUM, n_stdout, FAL0); )
872 n_autorec_relax_create();
873 for(lastmq = NULL, mq = &message[msgCount]; mp < mq; lastmq = mp, ++mp){
874 ++mesg;
875 if (!visible(mp))
876 continue;
877 if (UCMP(32, flag, >=, size))
878 break;
879 if(needdot){
880 if(showlast){
881 if(UCMP(32, flag, ==, size - 1) || &mp[1] == mq)
882 goto jdot_unsort;
883 }else if(flag == 0){
884 jdot_unsort:
885 needdot = FAL0;
886 setdot(mp);
887 }
888 }
889 ++flag;
890 a_chead_print_head(0, mesg, n_stdout, FAL0, FAL0);
891 n_autorec_relax_unroll();
892 }
893 if(needdot && ok_blook(showlast)) /* xxx will not show */
894 setdot(lastmq);
895 n_autorec_relax_gut();
896 mx_COLOUR( mx_colour_env_gut(); )
897 } else { /* threaded */
898 g = 0;
899 mq = threadroot;
900 for (mp = threadroot; mp; mp = next_in_thread(mp)){
901 /* TODO thread handling needs rewrite, m_collapsed must go */
902 if (visible(mp) &&
903 (mp->m_collapsed <= 0 ||
904 PCMP(mp, ==, message + msgspec - 1))) {
905 if (g % size == 0)
906 mq = mp;
907 if (mp->m_flag & fl) {
908 lastg = g;
909 lastmq = mq;
910 }
911 if ((msgspec > 0 && PCMP(mp, ==, message + msgspec - 1)) ||
912 (msgspec == 0 && g == k) ||
913 (msgspec == -2 && g == k + size && lastmq) ||
914 (msgspec < 0 && g >= k && (mp->m_flag & fl) != 0))
915 break;
916 g++;
917 }
918 }
919 if (lastmq && (msgspec == -2 ||
920 (msgspec == -1 && PCMP(mp, ==, message + msgCount)))) {
921 g = lastg;
922 mq = lastmq;
923 }
924 _screen = g / size;
925 mp = mq;
926
927 mx_COLOUR( mx_colour_env_create(mx_COLOUR_CTX_SUM, n_stdout, FAL0); )
928 n_autorec_relax_create();
929 for(lastmq = NULL; mp != NULL; lastmq = mp, mp = mq){
930 mq = next_in_thread(mp);
931 if (visible(mp) &&
932 (mp->m_collapsed <= 0 ||
933 PCMP(mp, ==, message + msgspec - 1))) {
934 if (UCMP(32, flag, >=, size))
935 break;
936 if(needdot){
937 if(showlast){
938 if(UCMP(32, flag, ==, size - 1) || mq == NULL)
939 goto jdot_sort;
940 }else if(flag == 0){
941 jdot_sort:
942 needdot = FAL0;
943 setdot(mp);
944 }
945 }
946 a_chead_print_head(flag, P2UZ(mp - message + 1), n_stdout,
947 mb.mb_threaded, TRU1);
948 ++flag;
949 n_autorec_relax_unroll();
950 }
951 }
952 if(needdot && ok_blook(showlast)) /* xxx will not show */
953 setdot(lastmq);
954 n_autorec_relax_gut();
955 mx_COLOUR( mx_colour_env_gut(); )
956 }
957
958 if (flag == 0) {
959 fprintf(n_stdout, _("No more mail.\n"));
960 if (n_pstate & (n_PS_ROBOT | n_PS_HOOK_MASK))
961 flag = !flag;
962 }
963 NYD_OU;
964 return !flag;
965 }
966
967 FL int
c_headers(void * v)968 c_headers(void *v)
969 {
970 int rv;
971 NYD_IN;
972
973 rv = print_header_group((int*)v);
974 NYD_OU;
975 return rv;
976 }
977
978 FL int
print_header_group(int * vector)979 print_header_group(int *vector)
980 {
981 int rv;
982 NYD_IN;
983
984 ASSERT(vector != NULL && vector != (void*)-1);
985 rv = _headers(vector[0]);
986 NYD_OU;
987 return rv;
988 }
989
990 FL int
c_scroll(void * v)991 c_scroll(void *v)
992 {
993 int rv;
994 NYD_IN;
995
996 rv = a_chead_scroll(*(char const**)v, FAL0);
997 NYD_OU;
998 return rv;
999 }
1000
1001 FL int
c_Scroll(void * v)1002 c_Scroll(void *v)
1003 {
1004 int rv;
1005 NYD_IN;
1006
1007 rv = a_chead_scroll(*(char const**)v, TRU1);
1008 NYD_OU;
1009 return rv;
1010 }
1011
1012 FL int
c_dotmove(void * v)1013 c_dotmove(void *v)
1014 {
1015 char const *args;
1016 int msgvec[2], rv;
1017 NYD_IN;
1018
1019 if (*(args = v) == '\0' || args[1] != '\0') {
1020 jerr:
1021 mx_cmd_print_synopsis(mx_cmd_firstfit("dotmove"), NIL);
1022 rv = 1;
1023 } else switch (args[0]) {
1024 case '-':
1025 case '+':
1026 if (msgCount == 0) {
1027 fprintf(n_stdout, _("At EOF\n"));
1028 rv = 0;
1029 } else if (n_getmsglist(n_UNCONST(/*TODO*/args), msgvec, 0, NULL) > 0) {
1030 setdot(message + msgvec[0] - 1);
1031 msgvec[1] = 0;
1032 rv = c_headers(msgvec);
1033 } else
1034 rv = 1;
1035 break;
1036 default:
1037 goto jerr;
1038 }
1039 NYD_OU;
1040 return rv;
1041 }
1042
1043 FL int
c_from(void * vp)1044 c_from(void *vp)
1045 {
1046 int *msgvec, *ip, n;
1047 char *cp;
1048 FILE * volatile obuf;
1049 NYD_IN;
1050
1051 if(*(msgvec = vp) == 0)
1052 goto jleave;
1053
1054 time_current_update(&time_current, FAL0);
1055
1056 obuf = n_stdout;
1057
1058 if (n_psonce & n_PSO_INTERACTIVE) {
1059 if ((cp = ok_vlook(crt)) != NULL) {
1060 uz ib;
1061
1062 for (n = 0, ip = msgvec; *ip != 0; ++ip)
1063 ++n;
1064
1065 if(*cp == '\0')
1066 ib = n_screensize();
1067 else
1068 su_idec_uz_cp(&ib, cp, 0, NULL);
1069 if (UCMP(z, n, >, ib) && (obuf = mx_pager_open()) == NULL)
1070 obuf = n_stdout;
1071 }
1072 }
1073
1074 /* Update dot before display so that the dotmark etc. are correct */
1075 for (ip = msgvec; ip[1] != 0; ++ip)
1076 ;
1077 setdot(&message[(ok_blook(showlast) ? *ip : *msgvec) - 1]);
1078
1079 mx_COLOUR( mx_colour_env_create(mx_COLOUR_CTX_SUM, obuf,
1080 (obuf != n_stdout)); )
1081 n_autorec_relax_create();
1082 for(n = 0, ip = msgvec; *ip != 0; ++ip){ /* TODO join into _print_head() */
1083 a_chead_print_head((uz)n++, S(uz,*ip), obuf, mb.mb_threaded, FAL0);
1084 n_autorec_relax_unroll();
1085 }
1086 n_autorec_relax_gut();
1087 mx_COLOUR( mx_colour_env_gut(); )
1088
1089 if(obuf != n_stdout)
1090 mx_pager_close(obuf);
1091 else
1092 clearerr(obuf);
1093
1094 jleave:
1095 NYD_OU;
1096 return 0;
1097 }
1098
1099 FL void
print_headers(int const * msgvec,boole only_marked,boole subject_thread_compress)1100 print_headers(int const *msgvec, boole only_marked,
1101 boole subject_thread_compress)
1102 {
1103 uz printed;
1104 NYD_IN;
1105
1106 time_current_update(&time_current, FAL0);
1107
1108 mx_COLOUR( mx_colour_env_create(mx_COLOUR_CTX_SUM, n_stdout, FAL0); )
1109 n_autorec_relax_create();
1110 for(printed = 0; *msgvec != 0; ++msgvec) {
1111 struct message *mp = message + *msgvec - 1;
1112 if (only_marked) {
1113 if (!(mp->m_flag & MMARK))
1114 continue;
1115 } else if (!visible(mp))
1116 continue;
1117 a_chead_print_head(printed++, *msgvec, n_stdout, mb.mb_threaded,
1118 subject_thread_compress);
1119 n_autorec_relax_unroll();
1120 }
1121 n_autorec_relax_gut();
1122 mx_COLOUR( mx_colour_env_gut(); )
1123 NYD_OU;
1124 }
1125
1126 #include "su/code-ou.h"
1127 /* s-it-mode */
1128