Merge branch 'topic/nyd'
[s-mailx.git] / cmd1.c
blob507f6060634cbf6d779cedafb0fed1e17d842f63
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ User commands.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2014 Steffen "Daode" Nurpmeso <sdaoden@users.sf.net>.
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. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
40 #ifndef HAVE_AMALGAMATION
41 # include "nail.h"
42 #endif
44 static int _screen;
45 static sigjmp_buf _cmd1_pipestop;
46 static sigjmp_buf _cmd1_pipejmp;
48 static void _cmd1_onpipe(int signo);
49 static void _cmd1_brokpipe(int signo);
51 /* Prepare and print "[Message: xy]:" intro */
52 static void _show_msg_overview(FILE *obuf, struct message *mp, int msg_no);
54 /* ... And place the extracted date in `date' */
55 static void _parse_from_(struct message *mp, char date[FROM_DATEBUF]);
57 /* Print out the header of a specific message
58 * __hprf: handle *headline*
59 * __subject: Subject:, but return NULL if threaded and Subject: yet seen
60 * __putindent: print out the indenting in threaded display */
61 static void _print_head(size_t yetprinted, size_t msgno, FILE *f,
62 bool_t threaded);
63 static void __hprf(size_t yetprinted, char const *fmt, size_t msgno,
64 FILE *f, bool_t threaded, char const *attrlist);
65 static char * __subject(struct message *mp, bool_t threaded,
66 size_t yetprinted);
67 static char * __subject_trim(char *s);
68 static int __putindent(FILE *fp, struct message *mp, int maxwidth);
70 static int _dispc(struct message *mp, char const *a);
71 static int _scroll1(char *arg, int onlynew);
73 /* Show the requested messages */
74 static int _type1(int *msgvec, bool_t doign, bool_t dopage, bool_t dopipe,
75 bool_t dodecode, char *cmd, off_t *tstats);
77 /* Pipe the requested messages */
78 static int _pipe1(char *str, int doign);
80 static void
81 _cmd1_onpipe(int signo)
83 NYD_X; /* Signal handler */
84 UNUSED(signo);
85 siglongjmp(_cmd1_pipejmp, 1);
88 static void
89 _cmd1_brokpipe(int signo)
91 NYD_X; /* Signal handler */
92 UNUSED(signo);
93 siglongjmp(_cmd1_pipestop, 1);
96 static void
97 _show_msg_overview(FILE *obuf, struct message *mp, int msg_no)
99 char const *cpre = "", *csuf = "";
100 NYD_ENTER;
102 #ifdef HAVE_COLOUR
103 if (colour_table != NULL) {
104 struct str const *sp;
106 if ((sp = colour_get(COLOURSPEC_MSGINFO)) != NULL)
107 cpre = sp->s;
108 csuf = colour_get(COLOURSPEC_RESET)->s;
110 #endif
111 fprintf(obuf, tr(17, "%s[-- Message %2d -- %lu lines, %lu bytes --]:%s\n"),
112 cpre, msg_no, (ul_it)mp->m_lines, (ul_it)mp->m_size, csuf);
113 NYD_LEAVE;
116 static void
117 _parse_from_(struct message *mp, char date[FROM_DATEBUF])
119 FILE *ibuf;
120 int hlen;
121 char *hline = NULL;
122 size_t hsize = 0;
123 NYD_ENTER;
125 if ((ibuf = setinput(&mb, mp, NEED_HEADER)) != NULL &&
126 (hlen = readline_restart(ibuf, &hline, &hsize, 0)) > 0)
127 extract_date_from_from_(hline, hlen, date);
128 if (hline != NULL)
129 free(hline);
130 NYD_LEAVE;
133 static void
134 _print_head(size_t yetprinted, size_t msgno, FILE *f, bool_t threaded)
136 char attrlist[30], *cp;
137 char const *fmt;
138 NYD_ENTER;
140 if ((cp = ok_vlook(attrlist)) != NULL) {
141 size_t i = strlen(cp);
142 if (UICMP(32, i, >, sizeof attrlist - 1))
143 i = (int)sizeof attrlist - 1;
144 memcpy(attrlist, cp, i);
145 } else if (ok_blook(bsdcompat) || ok_blook(bsdflags) ||
146 getenv("SYSV3") != NULL) {
147 char const bsdattr[] = "NU *HMFAT+-$";
148 memcpy(attrlist, bsdattr, sizeof bsdattr - 1);
149 } else {
150 char const pattr[] = "NUROSPMFAT+-$";
151 memcpy(attrlist, pattr, sizeof pattr - 1);
154 if ((fmt = ok_vlook(headline)) == NULL) {
155 fmt = ((ok_blook(bsdcompat) || ok_blook(bsdheadline))
156 ? "%>%a%m %-20f %16d %3l/%-5o %i%-S"
157 : "%>%a%m %-18f %16d %4l/%-5o %i%-s");
160 __hprf(yetprinted, fmt, msgno, f, threaded, attrlist);
161 NYD_LEAVE;
164 static void
165 __hprf(size_t yetprinted, char const *fmt, size_t msgno, FILE *f,
166 bool_t threaded, char const *attrlist)
168 char datebuf[FROM_DATEBUF], *cp, *subjline;
169 char const *datefmt, *date, *name, *fp;
170 int B, c, i, n, s, wleft, subjlen, isto = 0, isaddr = 0;
171 struct message *mp;
172 time_t datet;
173 NYD_ENTER;
175 mp = &message[msgno - 1];
176 datet = mp->m_time;
177 date = NULL;
179 datefmt = ok_vlook(datefield);
180 jredo:
181 if (datefmt != NULL) {
182 fp = hfield1("date", mp);/* TODO use m_date field! */
183 if (fp == NULL) {
184 datefmt = NULL;
185 goto jredo;
187 datet = rfctime(fp);
188 date = fakedate(datet);
189 fp = ok_vlook(datefield_markout_older);
190 i = (*datefmt != '\0');
191 if (fp != NULL)
192 i |= (*fp != '\0') ? 2 | 4 : 2;
194 /* May we strftime(3)? */
195 if (i & (1 | 4))
196 memcpy(&time_current.tc_local, localtime(&datet),
197 sizeof time_current.tc_local);
199 if ((i & 2) && (datet > time_current.tc_time + DATE_SECSDAY ||
200 #define _6M ((DATE_DAYSYEAR / 2) * DATE_SECSDAY)
201 (datet + _6M < time_current.tc_time))) {
202 #undef _6M
203 if ((datefmt = (i & 4) ? fp : NULL) == NULL) {
204 memset(datebuf, ' ', FROM_DATEBUF); /* xxx ur */
205 memcpy(datebuf + 4, date + 4, 7);
206 datebuf[4 + 7] = ' ';
207 memcpy(datebuf + 4 + 7 + 1, date + 20, 4);
208 datebuf[4 + 7 + 1 + 4] = '\0';
209 date = datebuf;
211 } else if ((i & 1) == 0)
212 datefmt = NULL;
213 } else if (datet == (time_t)0 && !(mp->m_flag & MNOFROM)) {
214 /* TODO eliminate this path, query the FROM_ date in setptr(),
215 * TODO all other codepaths do so by themselves ALREADY ?????
216 * TODO assert(mp->m_time != 0);, then
217 * TODO ALSO changes behaviour of datefield_markout_older */
218 _parse_from_(mp, datebuf);
219 date = datebuf;
220 } else
221 date = fakedate(datet);
223 isaddr = 1;
224 name = name1(mp, 0);
225 if (name != NULL && ok_blook(showto) && is_myname(skin(name))) {
226 if ((cp = hfield1("to", mp)) != NULL) {
227 name = cp;
228 isto = 1;
231 if (name == NULL) {
232 name = "";
233 isaddr = 0;
235 if (isaddr)
236 name = ok_blook(showname) ? realname(name) : prstr(skin(name));
238 subjline = NULL;
240 /* Detect the width of the non-format characters in *headline*;
241 * like that we can simply use putc() in the next loop, since we have
242 * already calculated their column widths (TODO it's sick) */
243 wleft = subjlen = scrnwidth;
245 for (fp = fmt; *fp != '\0'; ++fp) {
246 if (*fp == '%') {
247 if (*++fp == '-')
248 ++fp;
249 else if (*fp == '+')
250 ++fp;
251 if (digitchar(*fp)) {
252 n = 0;
254 n = 10*n + *fp - '0';
255 while (++fp, digitchar(*fp));
256 subjlen -= n;
259 if (*fp == '\0')
260 break;
261 } else {
262 #ifdef HAVE_WCWIDTH
263 if (mb_cur_max > 1) {
264 wchar_t wc;
265 if ((s = mbtowc(&wc, fp, mb_cur_max)) < 0)
266 n = s = 1;
267 else if ((n = wcwidth(wc)) < 0)
268 n = 1;
269 } else
270 #endif
271 n = s = 1;
272 subjlen -= n;
273 wleft -= n;
274 while (--s > 0)
275 ++fp;
279 /* Walk *headline*, producing output */
280 for (fp = fmt; *fp != '\0'; ++fp) {
281 if ((c = *fp & 0xFF) == '%') {
282 B = 0;
283 n = 0;
284 s = 1;
285 if (*++fp == '-') {
286 s = -1;
287 ++fp;
288 } else if (*fp == '+')
289 ++fp;
290 if (digitchar(*fp)) {
292 n = 10*n + *fp - '0';
293 while (++fp, digitchar(*fp));
295 if (*fp == '\0')
296 break;
298 n *= s;
299 switch ((c = *fp & 0xFF)) {
300 case '%':
301 goto jputc;
302 case '>':
303 case '<':
304 if (dot != mp)
305 c = ' ';
306 goto jputc;
307 case 'a':
308 c = _dispc(mp, attrlist);
309 jputc:
310 if (UICMP(32, ABS(n), >, wleft))
311 n = (n < 0) ? -wleft : wleft;
312 n = fprintf(f, "%*c", n, c);
313 wleft = (n >= 0) ? wleft - n : 0;
314 break;
315 case 'm':
316 if (n == 0) {
317 n = 3;
318 if (threaded)
319 for (i = msgCount; i > 999; i /= 10)
320 ++n;
322 if (UICMP(32, ABS(n), >, wleft))
323 n = (n < 0) ? -wleft : wleft;
324 n = fprintf(f, "%*lu", n, (ul_it)msgno);
325 wleft = (n >= 0) ? wleft - n : 0;
326 break;
327 case 'f':
328 if (n == 0) {
329 n = 18;
330 if (s < 0)
331 n = -n;
333 i = ABS(n);
334 if (i > wleft) {
335 i = wleft;
336 n = (n < 0) ? -wleft : wleft;
338 if (isto) /* XXX tr()! */
339 i -= 3;
340 n = fprintf(f, "%s%s", (isto ? "To " : ""),
341 colalign(name, i, n, &wleft));
342 if (n < 0)
343 wleft = 0;
344 else if (isto)
345 wleft -= 3;
346 break;
347 case 'd':
348 if (datefmt != NULL) {
349 i = strftime(datebuf, sizeof datebuf, datefmt,
350 &time_current.tc_local);
351 if (i != 0)
352 date = datebuf;
353 else
354 fprintf(stderr, tr(174,
355 "Ignored date format, it excesses the target buffer "
356 "(%lu bytes)\n"), (ul_it)sizeof datebuf);
357 datefmt = NULL;
359 if (n == 0)
360 n = 16;
361 if (UICMP(32, ABS(n), >, wleft))
362 n = (n < 0) ? -wleft : wleft;
363 n = fprintf(f, "%*.*s", n, n, date);
364 wleft = (n >= 0) ? wleft - n : 0;
365 break;
366 case 'l':
367 if (n == 0)
368 n = 4;
369 if (UICMP(32, ABS(n), >, wleft))
370 n = (n < 0) ? -wleft : wleft;
371 if (mp->m_xlines) {
372 n = fprintf(f, "%*ld", n, mp->m_xlines);
373 wleft = (n >= 0) ? wleft - n : 0;
374 } else {
375 n = ABS(n);
376 wleft -= n;
377 while (n-- != 0)
378 putc(' ', f);
380 break;
381 case 'o':
382 if (n == 0)
383 n = -5;
384 if (UICMP(32, ABS(n), >, wleft))
385 n = (n < 0) ? -wleft : wleft;
386 n = fprintf(f, "%*lu", n, (long)mp->m_xsize);
387 wleft = (n >= 0) ? wleft - n : 0;
388 break;
389 case 'i':
390 if (threaded) {
391 n = __putindent(f, mp, MIN(wleft, scrnwidth - 60));
392 wleft = (n >= 0) ? wleft - n : 0;
394 break;
395 case 'S':
396 B = 1;
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, ABS(n), >, subjlen))
406 n = (n < 0) ? -subjlen : subjlen;
407 if (B)
408 n -= (n < 0) ? -2 : 2;
409 if (n == 0)
410 break;
411 if (subjline == NULL)
412 subjline = __subject(mp, threaded, yetprinted);
413 if (subjline == (char*)-1) {
414 n = fprintf(f, "%*s", n, "");
415 wleft = (n >= 0) ? wleft - n : 0;
416 } else {
417 n = fprintf(f, (B ? "\"%s\"" : "%s"),
418 colalign(subjline, ABS(n), n, &wleft));
419 if (n < 0)
420 wleft = 0;
422 break;
423 case 'U':
424 #ifdef HAVE_IMAP
425 if (n == 0)
426 n = 9;
427 if (UICMP(32, ABS(n), >, wleft))
428 n = (n < 0) ? -wleft : wleft;
429 n = fprintf(f, "%*lu", n, mp->m_uid);
430 wleft = (n >= 0) ? wleft - n : 0;
431 break;
432 #else
433 c = '?';
434 goto jputc;
435 #endif
436 case 'e':
437 if (n == 0)
438 n = 2;
439 if (UICMP(32, ABS(n), >, wleft))
440 n = (n < 0) ? -wleft : wleft;
441 n = fprintf(f, "%*u", n, (threaded == 1 ? mp->m_level : 0));
442 wleft = (n >= 0) ? wleft - n : 0;
443 break;
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, ABS(n), >, wleft))
452 n = (n < 0) ? -wleft : wleft;
453 n = fprintf(f, "%*lu", n,
454 (threaded ? (ul_it)mp->m_threadpos : (ul_it)msgno));
455 wleft = (n >= 0) ? wleft - n : 0;
456 break;
457 case '$':
458 #ifdef HAVE_SPAM
459 if (n == 0)
460 n = 4;
461 if (UICMP(32, ABS(n), >, wleft))
462 n = (n < 0) ? -wleft : wleft;
463 { char buf[16];
464 snprintf(buf, sizeof buf, "%u.%u",
465 (mp->m_spamscore >> 8), (mp->m_spamscore & 0xFF));
466 n = fprintf(f, "%*s", n, buf);
467 wleft = (n >= 0) ? wleft - n : 0;
469 #else
470 c = '?';
471 goto jputc;
472 #endif
475 if (wleft <= 0)
476 break;
477 } else
478 putc(c, f);
480 putc('\n', f);
482 if (subjline != NULL && subjline != (char*)-1)
483 free(subjline);
484 NYD_LEAVE;
487 static char *
488 __subject_trim(char *s)
490 struct {
491 ui8_t len;
492 char dat[7];
493 } const *pp, ignored[] = { /* TODO make ignore list configurable */
494 { 3, "re:" }, { 4, "fwd:" },
495 { 3, "aw:" }, { 5, "antw:" },
496 { 0, "" }
498 NYD_ENTER;
500 jouter:
501 while (*s != '\0') {
502 while (spacechar(*s))
503 ++s;
504 /* TODO While it is maybe ok not to MIME decode these, we
505 * TODO should skip =?..?= at the beginning? */
506 for (pp = ignored; pp->len > 0; ++pp)
507 if (is_asccaseprefix(pp->dat, s)) {
508 s += pp->len;
509 goto jouter;
511 break;
513 NYD_LEAVE;
514 return s;
517 static char *
518 __subject(struct message *mp, bool_t threaded, size_t yetprinted)
520 /* XXX NOTE: because of efficiency reasons we simply ignore any encoded
521 * XXX parts and use ASCII case-insensitive comparison */
522 struct str in, out;
523 struct message *xmp;
524 char *rv = (char*)-1, *ms, *mso, *os;
525 NYD_ENTER;
527 if ((ms = hfield1("subject", mp)) == NULL)
528 goto jleave;
530 if (!threaded || mp->m_level == 0)
531 goto jconv;
533 /* In a display thread - check wether this message uses the same
534 * Subject: as it's parent or elder neighbour, suppress printing it if
535 * this is the case. To extend this a bit, ignore any leading Re: or
536 * Fwd: plus follow-up WS. Ignore invisible messages along the way */
537 mso = __subject_trim(ms);
538 for (xmp = mp; (xmp = prev_in_thread(xmp)) != NULL && yetprinted-- > 0;)
539 if (visible(xmp) && (os = hfield1("subject", xmp)) != NULL &&
540 asccasecmp(mso, __subject_trim(os)) == 0)
541 goto jleave;
542 jconv:
543 in.s = ms;
544 in.l = strlen(ms);
545 mime_fromhdr(&in, &out, TD_ICONV | TD_ISPR);
546 rv = out.s;
547 jleave:
548 NYD_LEAVE;
549 return rv;
552 static int
553 __putindent(FILE *fp, struct message *mp, int maxwidth)/* XXX no magic consts */
555 struct message *mq;
556 int *us, indlvl, indw, i, important = MNEW | MFLAGGED;
557 char *cs;
558 NYD_ENTER;
560 if (mp->m_level == 0 || maxwidth == 0) {
561 indw = 0;
562 goto jleave;
565 cs = ac_alloc(mp->m_level);
566 us = ac_alloc(mp->m_level * sizeof *us);
568 i = mp->m_level - 1;
569 if (mp->m_younger && UICMP(32, i + 1, ==, mp->m_younger->m_level)) {
570 if (mp->m_parent && mp->m_parent->m_flag & important)
571 us[i] = mp->m_flag & important ? 0x2523 : 0x2520;
572 else
573 us[i] = mp->m_flag & important ? 0x251D : 0x251C;
574 cs[i] = '+';
575 } else {
576 if (mp->m_parent && mp->m_parent->m_flag & important)
577 us[i] = mp->m_flag & important ? 0x2517 : 0x2516;
578 else
579 us[i] = mp->m_flag & important ? 0x2515 : 0x2514;
580 cs[i] = '\\';
583 mq = mp->m_parent;
584 for (i = mp->m_level - 2; i >= 0; --i) {
585 if (mq) {
586 if (UICMP(32, i, >, mq->m_level - 1)) {
587 us[i] = cs[i] = ' ';
588 continue;
590 if (mq->m_younger) {
591 if (mq->m_parent && (mq->m_parent->m_flag & important))
592 us[i] = 0x2503;
593 else
594 us[i] = 0x2502;
595 cs[i] = '|';
596 } else
597 us[i] = cs[i] = ' ';
598 mq = mq->m_parent;
599 } else
600 us[i] = cs[i] = ' ';
603 --maxwidth;
604 for (indlvl = indw = 0; (uc_it)indlvl < mp->m_level && indw < maxwidth;
605 ++indlvl) {
606 if (indw < maxwidth - 1)
607 indw += (int)putuc(us[indlvl], cs[indlvl] & 0377, fp);
608 else
609 indw += (int)putuc(0x21B8, '^', fp);
611 indw += (/*putuc(0x261E, fp)*/putc('>', fp) != EOF);
613 ac_free(us);
614 ac_free(cs);
615 jleave:
616 NYD_LEAVE;
617 return indw;
620 static int
621 _dispc(struct message *mp, char const *a)
623 int i = ' ';
624 NYD_ENTER;
626 if ((mp->m_flag & (MREAD | MNEW)) == MREAD)
627 i = a[3];
628 if ((mp->m_flag & (MREAD | MNEW)) == (MREAD | MNEW))
629 i = a[2];
630 if (mp->m_flag & MANSWERED)
631 i = a[8];
632 if (mp->m_flag & MDRAFTED)
633 i = a[9];
634 if ((mp->m_flag & (MREAD | MNEW)) == MNEW)
635 i = a[0];
636 if ((mp->m_flag & (MREAD | MNEW)) == 0)
637 i = a[1];
638 if (mp->m_flag & MSPAM)
639 i = a[12];
640 if (mp->m_flag & MSAVED)
641 i = a[4];
642 if (mp->m_flag & MPRESERVE)
643 i = a[5];
644 if (mp->m_flag & (MBOX | MBOXED))
645 i = a[6];
646 if (mp->m_flag & MFLAGGED)
647 i = a[7];
648 if (mb.mb_threaded == 1 && mp->m_collapsed > 0)
649 i = a[11];
650 if (mb.mb_threaded == 1 && mp->m_collapsed < 0)
651 i = a[10];
652 NYD_LEAVE;
653 return i;
656 static int
657 _scroll1(char *arg, int onlynew)
659 int cur[1], size;
660 NYD_ENTER;
662 cur[0] = onlynew ? -1 : 0;
663 size = screensize();
664 switch (*arg) {
665 case '1': case '2': case '3': case '4': case '5':
666 case '6': case '7': case '8': case '9': case '0':
667 _screen = atoi(arg);
668 goto jscroll_forward;
669 case '\0':
670 ++_screen;
671 goto jscroll_forward;
672 case '$':
673 _screen = msgCount / size;
674 goto jscroll_forward;
675 case '+':
676 if (arg[1] == '\0')
677 ++_screen;
678 else
679 _screen += atoi(arg + 1);
680 jscroll_forward:
681 if (_screen * size > msgCount) {
682 _screen = msgCount / size;
683 printf(tr(7, "On last screenful of messages\n"));
685 break;
687 case '-':
688 if (arg[1] == '\0')
689 --_screen;
690 else
691 _screen -= atoi(arg + 1);
692 if (_screen < 0) {
693 _screen = 0;
694 printf(tr(8, "On first screenful of messages\n"));
696 if (cur[0] == -1)
697 cur[0] = -2;
698 break;
700 default:
701 printf(tr(9, "Unrecognized scrolling command \"%s\"\n"), arg);
702 size = 1;
703 goto jleave;
705 size = headers(cur);
706 jleave:
707 NYD_LEAVE;
708 return size;
711 static int
712 _type1(int *msgvec, bool_t doign, bool_t dopage, bool_t dopipe,
713 bool_t dodecode, char *cmd, off_t *tstats)
715 off_t mstats[2];
716 int rv, *ip;
717 struct message *mp;
718 char const *cp;
719 FILE * volatile obuf;
720 NYD_ENTER;
722 enum sendaction const action = ((dopipe && ok_blook(piperaw))
723 ? SEND_MBOX : dodecode
724 ? SEND_SHOW : doign
725 ? SEND_TODISP : SEND_TODISP_ALL);
726 bool_t const volatile formfeed = (dopipe && ok_blook(page));
727 obuf = stdout;
729 if (sigsetjmp(_cmd1_pipestop, 1))
730 goto jclose_pipe;
731 if (dopipe) {
732 if ((cp = ok_vlook(SHELL)) == NULL)
733 cp = XSHELL;
734 if ((obuf = Popen(cmd, "w", cp, 1)) == NULL) {
735 perror(cmd);
736 obuf = stdout;
737 } else
738 safe_signal(SIGPIPE, &_cmd1_brokpipe);
739 } else if ((options & OPT_TTYOUT) &&
740 (dopage || (cp = ok_vlook(crt)) != NULL)) {
741 char const *pager = NULL;
742 size_t nlines = 0;
743 if (!dopage) {
744 for (ip = msgvec; *ip && PTRCMP(ip - msgvec, <, msgCount); ++ip) {
745 mp = &message[*ip - 1];
746 if (!(mp->m_have & HAVE_BODY))
747 if (get_body(mp) != OKAY) {
748 rv = 1;
749 goto jleave;
751 nlines += mp->m_lines;
754 if (dopage || UICMP(z, nlines, >,
755 (*cp != '\0' ? atoi(cp) : realscreenheight))) {
756 pager = get_pager();
757 #ifdef HAVE_SETENV
758 if ((cp = getenv("LESS")) == NULL) /* XXX not here! */
759 setenv("LESS", "FRXi", 0); /* XXX add env. */
760 #endif
761 obuf = Popen(pager, "w", NULL, 1);
762 #ifdef HAVE_SETENV
763 if (cp == NULL)
764 unsetenv("LESS"); /* XXX to Popen() etc.?!! */
765 #endif
766 if (obuf == NULL) {
767 perror(pager);
768 obuf = stdout;
769 pager = NULL;
770 } else
771 safe_signal(SIGPIPE, &_cmd1_brokpipe);
773 #ifdef HAVE_COLOUR
774 if (action != SEND_MBOX)
775 colour_table_create(pager); /* (salloc()s!) */
776 #endif
778 #ifdef HAVE_COLOUR
779 else if ((options & OPT_TTYOUT) && action != SEND_MBOX)
780 colour_table_create(NULL); /* (salloc()s!) */
781 #endif
783 /* This may jump, in which case srelax_rele() wouldn't be called, but
784 * it shouldn't matter, because we -- then -- directly reenter the
785 * lex.c:commands() loop, which sreset()s */
786 srelax_hold();
787 for (ip = msgvec; *ip && PTRCMP(ip - msgvec, <, msgCount); ++ip) {
788 mp = &message[*ip - 1];
789 touch(mp);
790 setdot(mp);
791 uncollapse1(mp, 1);
792 if (!dopipe) {
793 if (ip != msgvec)
794 fprintf(obuf, "\n");
795 if (action != SEND_MBOX)
796 _show_msg_overview(obuf, mp, *ip);
798 sendmp(mp, obuf, (doign ? ignore : NULL), NULL, action, mstats);
799 srelax();
800 if (formfeed)
801 putc('\f', obuf);
802 if (tstats) {
803 tstats[0] += mstats[0];
804 tstats[1] += mstats[1];
807 srelax_rele();
808 jclose_pipe:
809 if (obuf != stdout) {
810 /* Ignore SIGPIPE so it can't cause a duplicate close */
811 safe_signal(SIGPIPE, SIG_IGN);
812 colour_reset(obuf); /* XXX hacky; only here because we still jump */
813 Pclose(obuf, TRU1);
814 safe_signal(SIGPIPE, dflpipe);
816 rv = 0;
818 jleave:
819 NYD_LEAVE;
820 return rv;
823 static int
824 _pipe1(char *str, int doign)
826 off_t stats[2];
827 char *cmd;
828 int *msgvec, rv = 1;
829 bool_t needs_list;
830 NYD_ENTER;
832 if ((cmd = laststring(str, &needs_list, 1)) == NULL) {
833 cmd = ok_vlook(cmd);
834 if (cmd == NULL || *cmd == '\0') {
835 fputs(tr(16, "variable cmd not set\n"), stderr);
836 goto jleave;
840 msgvec = salloc((msgCount + 2) * sizeof *msgvec);
842 if (!needs_list) {
843 *msgvec = first(0, MMNORM);
844 if (*msgvec == 0) {
845 if (inhook) {
846 rv = 0;
847 goto jleave;
849 puts(tr(18, "No messages to pipe."));
850 goto jleave;
852 msgvec[1] = 0;
853 } else if (getmsglist(str, msgvec, 0) < 0)
854 goto jleave;
855 if (*msgvec == 0) {
856 if (inhook) {
857 rv = 0;
858 goto jleave;
860 printf("No applicable messages.\n");
861 goto jleave;
864 printf(tr(268, "Pipe to: \"%s\"\n"), cmd);
865 stats[0] = stats[1] = 0;
866 if ((rv = _type1(msgvec, doign, FAL0, TRU1, FAL0, cmd, stats)) == 0) {
867 printf("\"%s\" ", cmd);
868 if (stats[0] >= 0)
869 printf("%lu", (long)stats[0]);
870 else
871 printf(tr(27, "binary"));
872 printf("/%lu\n", (long)stats[1]);
874 jleave:
875 NYD_LEAVE;
876 return rv;
879 FL int
880 ccmdnotsupp(void *v) /* TODO -> lex.c */
882 NYD_ENTER;
883 UNUSED(v);
884 fprintf(stderr, tr(10, "The requested feature is not compiled in\n"));
885 NYD_LEAVE;
886 return 1;
889 FL int
890 headers(void *v)
892 ui32_t flag;
893 int *msgvec = v, g, k, n, mesg, size, lastg = 1;
894 struct message *mp, *mq, *lastmq = NULL;
895 enum mflag fl = MNEW | MFLAGGED;
896 NYD_ENTER;
898 time_current_update(&time_current, FAL0);
900 flag = 0;
901 size = screensize();
902 n = msgvec[0]; /* n == {-2, -1, 0}: called from scroll() */
903 if (_screen < 0)
904 _screen = 0;
905 k = _screen * size;
906 if (k >= msgCount)
907 k = msgCount - size;
908 if (k < 0)
909 k = 0;
911 if (mb.mb_threaded == 0) {
912 g = 0;
913 mq = message;
914 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
915 if (visible(mp)) {
916 if (g % size == 0)
917 mq = mp;
918 if (mp->m_flag & fl) {
919 lastg = g;
920 lastmq = mq;
922 if ((n > 0 && mp == &message[n - 1]) || (n == 0 && g == k) ||
923 (n == -2 && g == k + size && lastmq) ||
924 (n < 0 && g >= k && (mp->m_flag & fl) != 0))
925 break;
926 g++;
928 if (lastmq && (n == -2 || (n == -1 && mp == &message[msgCount]))) {
929 g = lastg;
930 mq = lastmq;
932 _screen = g / size;
933 mp = mq;
934 mesg = (int)PTR2SIZE(mp - message);
935 if (dot != &message[n - 1]) {
936 for (mq = mp; PTRCMP(mq, <, message + msgCount); ++mq)
937 if (visible(mq)) {
938 setdot(mq);
939 break;
942 #ifdef HAVE_IMAP
943 if (mb.mb_type == MB_IMAP)
944 imap_getheaders(mesg + 1, mesg + size);
945 #endif
946 srelax_hold();
947 for (; PTRCMP(mp, <, message + msgCount); ++mp) {
948 ++mesg;
949 if (!visible(mp))
950 continue;
951 if (UICMP(32, flag++, >=, size))
952 break;
953 _print_head(0, mesg, stdout, 0);
954 srelax();
956 srelax_rele();
957 } else { /* threaded */
958 g = 0;
959 mq = threadroot;
960 for (mp = threadroot; mp; mp = next_in_thread(mp))
961 if (visible(mp) && (mp->m_collapsed <= 0 || mp == &message[n - 1])) {
962 if (g % size == 0)
963 mq = mp;
964 if (mp->m_flag & fl) {
965 lastg = g;
966 lastmq = mq;
968 if ((n > 0 && mp == &message[n - 1]) || (n == 0 && g == k) ||
969 (n == -2 && g == k + size && lastmq) ||
970 (n < 0 && g >= k && (mp->m_flag & fl) != 0))
971 break;
972 g++;
974 if (lastmq && (n == -2 || (n == -1 && mp == &message[msgCount]))) {
975 g = lastg;
976 mq = lastmq;
978 _screen = g / size;
979 mp = mq;
980 if (dot != &message[n - 1]) {
981 for (mq = mp; mq; mq = next_in_thread(mq))
982 if (visible(mq) && mq->m_collapsed <= 0) {
983 setdot(mq);
984 break;
987 srelax_hold();
988 while (mp) {
989 if (visible(mp) && (mp->m_collapsed <= 0 || mp == &message[n - 1])) {
990 if (UICMP(32, flag++, >=, size))
991 break;
992 _print_head(flag - 1, PTR2SIZE(mp - message + 1), stdout,
993 mb.mb_threaded);
994 srelax();
996 mp = next_in_thread(mp);
998 srelax_rele();
1001 if (!flag)
1002 printf(tr(6, "No more mail.\n"));
1003 NYD_LEAVE;
1004 return !flag;
1007 FL int
1008 scroll(void *v)
1010 int rv;
1011 NYD_ENTER;
1013 rv = _scroll1(v, 0);
1014 NYD_LEAVE;
1015 return rv;
1018 FL int
1019 Scroll(void *v)
1021 int rv;
1022 NYD_ENTER;
1024 rv = _scroll1(v, 1);
1025 NYD_LEAVE;
1026 return rv;
1029 FL int
1030 from(void *v)
1032 int *msgvec = v, *ip, n;
1033 char *cp;
1034 FILE * volatile obuf;
1035 NYD_ENTER;
1037 time_current_update(&time_current, FAL0);
1038 obuf = stdout;
1040 /* TODO unfixable memory leaks still */
1041 if (IS_TTY_SESSION() && (cp = ok_vlook(crt)) != NULL) {
1042 for (n = 0, ip = msgvec; *ip != 0; ++ip)
1043 n++;
1044 if (n > (*cp == '\0' ? screensize() : atoi(cp)) + 3) {
1045 char const *p;
1046 if (sigsetjmp(_cmd1_pipejmp, 1))
1047 goto jendpipe;
1048 p = get_pager();
1049 if ((obuf = Popen(p, "w", NULL, 1)) == NULL) {
1050 perror(p);
1051 obuf = stdout;
1052 cp=NULL;
1053 } else
1054 safe_signal(SIGPIPE, &_cmd1_onpipe);
1058 for (n = 0, ip = msgvec; *ip != 0; ++ip) /* TODO join into _print_head() */
1059 _print_head((size_t)n++, (size_t)*ip, obuf, mb.mb_threaded);
1060 if (--ip >= msgvec)
1061 setdot(&message[*ip - 1]);
1063 jendpipe:
1064 if (obuf != stdout) {
1065 safe_signal(SIGPIPE, SIG_IGN);
1066 Pclose(obuf, TRU1);
1067 safe_signal(SIGPIPE, dflpipe);
1069 NYD_LEAVE;
1070 return 0;
1073 FL void
1074 print_headers(size_t bottom, size_t topx)
1076 size_t printed;
1077 NYD_ENTER;
1079 #ifdef HAVE_IMAP
1080 if (mb.mb_type == MB_IMAP)
1081 imap_getheaders(bottom, topx);
1082 #endif
1083 time_current_update(&time_current, FAL0);
1085 for (printed = 0; bottom <= topx; ++bottom) /* TODO join into _print_head()*/
1086 if (visible(&message[bottom - 1]))
1087 _print_head(printed++, bottom, stdout, FAL0);
1088 NYD_LEAVE;
1091 FL int
1092 pdot(void *v)
1094 NYD_ENTER;
1095 UNUSED(v);
1096 printf("%d\n", (int)PTR2SIZE(dot - message + 1));
1097 NYD_LEAVE;
1098 return 0;
1101 FL int
1102 more(void *v)
1104 int *msgvec = v, rv;
1105 NYD_ENTER;
1107 rv = _type1(msgvec, TRU1, TRU1, FAL0, FAL0, NULL, NULL);
1108 NYD_LEAVE;
1109 return rv;
1112 FL int
1113 More(void *v)
1115 int *msgvec = v, rv;
1116 NYD_ENTER;
1118 rv = _type1(msgvec, FAL0, TRU1, FAL0, FAL0, NULL, NULL);
1119 NYD_LEAVE;
1120 return rv;
1123 FL int
1124 type(void *v)
1126 int *msgvec = v, rv;
1127 NYD_ENTER;
1129 rv = _type1(msgvec, TRU1, FAL0, FAL0, FAL0, NULL, NULL);
1130 NYD_LEAVE;
1131 return rv;
1134 FL int
1135 Type(void *v)
1137 int *msgvec = v, rv;
1138 NYD_ENTER;
1140 rv = _type1(msgvec, FAL0, FAL0, FAL0, FAL0, NULL, NULL);
1141 NYD_LEAVE;
1142 return rv;
1145 FL int
1146 show(void *v)
1148 int *msgvec = v, rv;
1149 NYD_ENTER;
1151 rv = _type1(msgvec, FAL0, FAL0, FAL0, TRU1, NULL, NULL);
1152 NYD_LEAVE;
1153 return rv;
1156 FL int
1157 pipecmd(void *v)
1159 char *str = v;
1160 int rv;
1161 NYD_ENTER;
1163 rv = _pipe1(str, 1);
1164 NYD_LEAVE;
1165 return rv;
1168 FL int
1169 Pipecmd(void *v)
1171 char *str = v;
1172 int rv;
1173 NYD_ENTER;
1175 rv = _pipe1(str, 0);
1176 NYD_LEAVE;
1177 return rv;
1180 FL int
1181 top(void *v)
1183 int *msgvec = v, *ip, c, topl, lines, empty_last;
1184 struct message *mp;
1185 char *cp, *linebuf = NULL;
1186 size_t linesize = 0;
1187 FILE *ibuf;
1188 NYD_ENTER;
1190 topl = 5;
1191 cp = ok_vlook(toplines);
1192 if (cp != NULL) {
1193 topl = atoi(cp);
1194 if (topl < 0 || topl > 10000)
1195 topl = 5;
1198 #ifdef HAVE_COLOUR
1199 if (options & OPT_TTYOUT)
1200 colour_table_create(NULL); /* (salloc()s) */
1201 #endif
1202 empty_last = 1;
1203 for (ip = msgvec; *ip != 0 && UICMP(z, PTR2SIZE(ip - msgvec), <, msgCount);
1204 ++ip) {
1205 mp = &message[*ip - 1];
1206 touch(mp);
1207 setdot(mp);
1208 did_print_dot = TRU1;
1209 if (!empty_last)
1210 printf("\n");
1211 _show_msg_overview(stdout, mp, *ip);
1212 if (mp->m_flag & MNOFROM)
1213 /* XXX top(): coloured output? */
1214 printf("From %s %s\n", fakefrom(mp), fakedate(mp->m_time));
1215 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL) { /* XXX could use TOP */
1216 v = NULL;
1217 break;
1219 c = mp->m_lines;
1220 for (lines = 0; lines < c && UICMP(32, lines, <=, topl); ++lines) {
1221 if (readline_restart(ibuf, &linebuf, &linesize, 0) < 0)
1222 break;
1223 puts(linebuf);
1225 for (cp = linebuf; *cp && blankchar(*cp); ++cp)
1227 empty_last = (*cp == '\0');
1231 if (linebuf != NULL)
1232 free(linebuf);
1233 NYD_LEAVE;
1234 return (v != NULL);
1237 FL int
1238 stouch(void *v)
1240 int *msgvec = v, *ip;
1241 NYD_ENTER;
1243 for (ip = msgvec; *ip != 0; ++ip) {
1244 setdot(&message[*ip - 1]);
1245 dot->m_flag |= MTOUCH;
1246 dot->m_flag &= ~MPRESERVE;
1247 did_print_dot = TRU1;
1249 NYD_LEAVE;
1250 return 0;
1253 FL int
1254 mboxit(void *v)
1256 int *msgvec = v, *ip;
1257 NYD_ENTER;
1259 for (ip = msgvec; *ip != 0; ++ip) {
1260 setdot(&message[*ip - 1]);
1261 dot->m_flag |= MTOUCH | MBOX;
1262 dot->m_flag &= ~MPRESERVE;
1263 did_print_dot = TRU1;
1265 NYD_LEAVE;
1266 return 0;
1269 FL int
1270 folders(void *v)
1272 char dirname[PATH_MAX], *name, **argv = v;
1273 char const *cmd;
1274 int rv = 1;
1275 NYD_ENTER;
1277 if (*argv) {
1278 name = expand(*argv);
1279 if (name == NULL)
1280 goto jleave;
1281 } else if (!getfold(dirname, sizeof dirname)) {
1282 fprintf(stderr, tr(20, "No value set for \"folder\"\n"));
1283 goto jleave;
1284 } else
1285 name = dirname;
1287 if (which_protocol(name) == PROTO_IMAP) {
1288 #ifdef HAVE_IMAP
1289 imap_folders(name, *argv == NULL);
1290 #else
1291 rv = ccmdnotsupp(NULL);
1292 #endif
1293 } else {
1294 if ((cmd = ok_vlook(LISTER)) == NULL)
1295 cmd = XLISTER;
1296 run_command(cmd, 0, -1, -1, name, NULL, NULL);
1298 jleave:
1299 NYD_LEAVE;
1300 return rv;
1303 /* vim:set fenc=utf-8:s-it-mode */