mk-conf.sh, mk-mk.in: use variables for *all* utilities
[s-mailx.git] / cmd1.c
blobb53da97a53174cff52dd90cceb1ea08be58650f8
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 - 2013 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
45 * Print the current active headings.
46 * Don't change dot if invoker didn't give an argument.
49 static int screen;
51 /* Prepare and print "[Message: xy]:" intro */
52 static void _show_msg_overview(struct message *mp, int msg_no, FILE *obuf);
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, int msgno, FILE *f,
62 bool_t threaded);
63 static void __hprf(size_t yetprinted, const char *fmt, int mesg, FILE *f,
64 bool_t threaded, const char *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 void _cmd1_onpipe(int signo);
71 static int _dispc(struct message *mp, const char *a);
72 static int scroll1(char *arg, int onlynew);
74 static int _type1(int *msgvec, bool_t doign, bool_t dopage, bool_t dopipe,
75 bool_t dodecode, char *cmd, off_t *tstats);
76 static int pipe1(char *str, int doign);
78 static void
79 _show_msg_overview(struct message *mp, int msg_no, FILE *obuf)
81 fprintf(obuf, tr(17, "[-- Message %2d -- %lu lines, %lu bytes --]:\n"),
82 msg_no, (ul_it)mp->m_lines, (ul_it)mp->m_size);
85 static void
86 _parse_from_(struct message *mp, char date[FROM_DATEBUF])
88 FILE *ibuf;
89 int hlen;
90 char *hline = NULL;
91 size_t hsize = 0;
93 if ((ibuf = setinput(&mb, mp, NEED_HEADER)) != NULL &&
94 (hlen = readline_restart(ibuf, &hline, &hsize, 0)) > 0)
95 (void)extract_date_from_from_(hline, hlen, date);
96 if (hline != NULL)
97 free(hline);
100 static void
101 _print_head(size_t yetprinted, int msgno, FILE *f, bool_t threaded)
103 char attrlist[30], *cp;
104 char const *fmt;
106 if ((cp = voption("attrlist")) != NULL) {
107 size_t i = strlen(cp);
108 if (UICMP(32, i, >, sizeof attrlist - 1))
109 i = (int)sizeof attrlist - 1;
110 memcpy(attrlist, cp, i);
111 } else if (boption("bsdcompat") || boption("bsdflags") ||
112 getenv("SYSV3") != NULL) {
113 char const bsdattr[] = "NU *HMFAT+-$";
114 memcpy(attrlist, bsdattr, sizeof bsdattr - 1);
115 } else {
116 char const pattr[] = "NUROSPMFAT+-$";
117 memcpy(attrlist, pattr, sizeof pattr - 1);
120 if ((fmt = voption("headline")) == NULL) {
121 fmt = ((boption("bsdcompat") || boption("bsdheadline"))
122 ? "%>%a%m %-20f %16d %3l/%-5o %i%-S"
123 : "%>%a%m %-18f %16d %4l/%-5o %i%-s");
126 __hprf(yetprinted, fmt, msgno, f, threaded, attrlist);
129 static void
130 __hprf(size_t yetprinted, char const *fmt, int msgno, FILE *f, bool_t threaded,
131 char const *attrlist)
133 char datebuf[FROM_DATEBUF], *cp, *subjline;
134 char const *datefmt, *date, *name, *fp;
135 int B, c, i, n, s, wleft, subjlen, isto = 0, isaddr = 0;
136 struct message *mp = &message[msgno - 1];
137 time_t datet = mp->m_time;
139 date = NULL;
140 if ((datefmt = value("datefield")) != NULL) {
141 fp = hfield1("date", mp);/* TODO use m_date field! */
142 if (fp == NULL) {
143 datefmt = NULL;
144 goto jdate_set;
146 datet = rfctime(fp);
147 date = fakedate(datet);
148 fp = value("datefield-markout-older");
149 i = (*datefmt != '\0');
150 if (fp != NULL)
151 i |= (*fp != '\0') ? 2 | 4 : 2;
152 /* May we strftime(3)? */
153 if (i & (1 | 4))
154 memcpy(&time_current.tc_local, localtime(&datet),
155 sizeof time_current.tc_local);
156 if ((i & 2) &&
157 /* TODO *datefield-markout-older* we accept
158 * TODO one day in the future, should be UTC
159 * TODO offset only? and Stephen Isard had
160 * TODO one week once he proposed the patch! */
161 (datet > time_current.tc_time + DATE_SECSDAY ||
162 #define _6M ((DATE_DAYSYEAR / 2) * DATE_SECSDAY)
163 (datet + _6M < time_current.tc_time))) {
164 #undef _6M
165 if ((datefmt = (i & 4) ? fp : NULL) == NULL) {
166 memset(datebuf, ' ', FROM_DATEBUF); /* xxx ur */
167 memcpy(datebuf + 4, date + 4, 7);
168 datebuf[4 + 7] = ' ';
169 memcpy(datebuf + 4 + 7 + 1, date + 20, 4);
170 datebuf[4 + 7 + 1 + 4] = '\0';
171 date = datebuf;
173 } else if ((i & 1) == 0)
174 datefmt = NULL;
175 } else if (datet == (time_t)0 && (mp->m_flag & MNOFROM) == 0) {
176 /* TODO eliminate this path, query the FROM_ date in setptr(),
177 * TODO all other codepaths do so by themselves ALREADY ?????
178 * TODO assert(mp->m_time != 0);, then
179 * TODO ALSO changes behaviour of markout-non-current */
180 _parse_from_(mp, datebuf);
181 date = datebuf;
182 } else {
183 jdate_set:
184 date = fakedate(datet);
187 isaddr = 1;
188 name = name1(mp, 0);
189 if (name != NULL && value("showto") && is_myname(skin(name))) {
190 if ((cp = hfield1("to", mp)) != NULL) {
191 name = cp;
192 isto = 1;
195 if (name == NULL) {
196 name = "";
197 isaddr = 0;
199 if (isaddr) {
200 if (value("showname"))
201 name = realname(name);
202 else {
203 name = prstr(skin(name));
207 subjline = NULL;
209 /* Detect the width of the non-format characters in *headline*;
210 * like that we can simply use putc() in the next loop, since we have
211 * already calculated their column widths (TODO it's sick) */
212 wleft =
213 subjlen = scrnwidth;
215 for (fp = fmt; *fp; ++fp) {
216 if (*fp == '%') {
217 if (*++fp == '-') {
218 ++fp;
219 } else if (*fp == '+')
220 ++fp;
221 if (digitchar(*fp)) {
222 n = 0;
224 n = 10*n + *fp - '0';
225 while (++fp, digitchar(*fp));
226 subjlen -= n;
229 if (*fp == '\0')
230 break;
231 } else {
232 #ifdef HAVE_WCWIDTH
233 if (mb_cur_max > 1) {
234 wchar_t wc;
235 if ((s = mbtowc(&wc, fp, mb_cur_max)) < 0)
236 n = s = 1;
237 else if ((n = wcwidth(wc)) < 0)
238 n = 1;
239 } else
240 #endif
241 n = s = 1;
242 subjlen -= n;
243 wleft -= n;
244 while (--s > 0)
245 ++fp;
249 /* Walk *headline*, producing output */
250 for (fp = fmt; *fp; ++fp) {
251 if ((c = *fp & 0xFF) == '%') {
252 B = 0;
253 n = 0;
254 s = 1;
255 if (*++fp == '-') {
256 s = -1;
257 ++fp;
258 } else if (*fp == '+')
259 ++fp;
260 if (digitchar(*fp)) {
262 n = 10*n + *fp - '0';
263 while (++fp, digitchar(*fp));
265 if (*fp == '\0')
266 break;
268 n *= s;
269 switch ((c = *fp & 0xFF)) {
270 case '%':
271 goto jputc;
272 case '>':
273 case '<':
274 if (dot != mp)
275 c = ' ';
276 goto jputc;
277 case 'a':
278 c = _dispc(mp, attrlist);
279 jputc:
280 if (UICMP(32, ABS(n), >, wleft))
281 n = (n < 0) ? -wleft : wleft;
282 n = fprintf(f, "%*c", n, c);
283 wleft = (n >= 0) ? wleft - n : 0;
284 break;
285 case 'm':
286 if (n == 0) {
287 n = 3;
288 if (threaded)
289 for (i=msgCount; i>999; i/=10)
290 n++;
292 if (UICMP(32, ABS(n), >, wleft))
293 n = (n < 0) ? -wleft : wleft;
294 n = fprintf(f, "%*d", n, msgno);
295 wleft = (n >= 0) ? wleft - n : 0;
296 break;
297 case 'f':
298 if (n == 0) {
299 n = 18;
300 if (s < 0)
301 n = -n;
303 i = ABS(n);
304 if (i > wleft) {
305 i = wleft;
306 n = (n < 0) ? -wleft : wleft;
308 if (isto) /* XXX tr()! */
309 i -= 3;
310 n = fprintf(f, "%s%s", (isto ? "To " : ""),
311 colalign(name, i, n, &wleft));
312 if (n < 0)
313 wleft = 0;
314 else if (isto)
315 wleft -= 3;
316 break;
317 case 'd':
318 if (datefmt != NULL) {
319 i = strftime(datebuf, sizeof datebuf,
320 datefmt,
321 &time_current.tc_local);
322 if (i != 0)
323 date = datebuf;
324 else
325 fprintf(stderr, tr(174,
326 "Ignored date format, "
327 "it excesses the "
328 "target buffer "
329 "(%lu bytes)\n"),
330 (ul_it)sizeof datebuf);
331 datefmt = NULL;
333 if (n == 0)
334 n = 16;
335 if (UICMP(32, ABS(n), >, wleft))
336 n = (n < 0) ? -wleft : wleft;
337 n = fprintf(f, "%*.*s", n, n, date);
338 wleft = (n >= 0) ? wleft - n : 0;
339 break;
340 case 'l':
341 if (n == 0)
342 n = 4;
343 if (UICMP(32, ABS(n), >, wleft))
344 n = (n < 0) ? -wleft : wleft;
345 if (mp->m_xlines) {
346 n = fprintf(f, "%*ld", n, mp->m_xlines);
347 wleft = (n >= 0) ? wleft - n : 0;
348 } else {
349 n = ABS(n);
350 wleft -= n;
351 while (n-- != 0)
352 putc(' ', f);
354 break;
355 case 'o':
356 if (n == 0)
357 n = -5;
358 if (UICMP(32, ABS(n), >, wleft))
359 n = (n < 0) ? -wleft : wleft;
360 n = fprintf(f, "%*lu", n, (long)mp->m_xsize);
361 wleft = (n >= 0) ? wleft - n : 0;
362 break;
363 case 'i':
364 if (threaded) {
365 n = __putindent(f, mp, MIN(wleft,
366 scrnwidth - 60));
367 wleft = (n >= 0) ? wleft - n : 0;
369 break;
370 case 'S':
371 B = 1;
372 /*FALLTHRU*/
373 case 's':
374 if (n == 0)
375 n = subjlen - 2;
376 if (n > 0 && s < 0)
377 n = -n;
378 if (subjlen > wleft)
379 subjlen = wleft;
380 if (UICMP(32, ABS(n), >, subjlen))
381 n = (n < 0) ? -subjlen : subjlen;
382 if (B)
383 n -= (n < 0) ? -2 : 2;
384 if (n == 0)
385 break;
386 if (subjline == NULL)
387 subjline = __subject(mp, threaded,
388 yetprinted);
389 if (subjline == (char*)-1) {
390 n = fprintf(f, "%*s", n, "");
391 wleft = (n >= 0) ? wleft-n : 0;
392 } else {
393 n = fprintf(f, (B ? "\"%s\"" : "%s"),
394 colalign(subjline, ABS(n), n,
395 &wleft));
396 if (n < 0)
397 wleft = 0;
399 break;
400 case 'U':
401 #ifdef HAVE_IMAP
402 if (n == 0)
403 n = 9;
404 if (UICMP(32, ABS(n), >, wleft))
405 n = (n < 0) ? -wleft : wleft;
406 n = fprintf(f, "%*lu", n, mp->m_uid);
407 wleft = (n >= 0) ? wleft - n : 0;
408 break;
409 #else
410 c = '?';
411 goto jputc;
412 #endif
413 case 'e':
414 if (n == 0)
415 n = 2;
416 if (UICMP(32, ABS(n), >, wleft))
417 n = (n < 0) ? -wleft : wleft;
418 n = fprintf(f, "%*u", n,
419 threaded == 1 ? mp->m_level : 0);
420 wleft = (n >= 0) ? wleft - n : 0;
421 break;
422 case 't':
423 if (n == 0) {
424 n = 3;
425 if (threaded)
426 for (i=msgCount; i>999; i/=10)
427 n++;
429 if (UICMP(32, ABS(n), >, wleft))
430 n = (n < 0) ? -wleft : wleft;
431 n = fprintf(f, "%*ld", n,
432 threaded ? mp->m_threadpos : msgno);
433 wleft = (n >= 0) ? wleft - n : 0;
434 break;
435 case '$':
436 #ifdef HAVE_SPAM
437 if (n == 0)
438 n = 4;
439 if (UICMP(32, ABS(n), >, wleft))
440 n = (n < 0) ? -wleft : wleft;
441 { char buf[16];
442 snprintf(buf, sizeof buf, "%u.%u",
443 (mp->m_spamscore >> 8),
444 (mp->m_spamscore & 0xFF));
445 n = fprintf(f, "%*s", n, buf);
446 wleft = (n >= 0) ? wleft - n : 0;
448 #else
449 c = '?';
450 goto jputc;
451 #endif
454 if (wleft <= 0)
455 break;
456 } else
457 putc(c, f);
459 putc('\n', f);
461 if (subjline != NULL && subjline != (char*)-1)
462 free(subjline);
465 static char *
466 __subject_trim(char *s)
468 struct {
469 ui8_t len;
470 char dat[7];
471 } const *pp, ignored[] = { /* TODO make ignore list configurable */
472 { 3, "re:" }, { 4, "fwd:" },
473 { 3, "aw:" }, { 5, "antw:" },
474 { 0, "" }
476 jouter:
477 while (*s != '\0') {
478 while (spacechar(*s))
479 ++s;
480 /* TODO While it is maybe ok not to MIME decode these, we
481 * TODO should skip =?..?= at the beginning? */
482 for (pp = ignored; pp->len > 0; ++pp)
483 if (is_asccaseprefix(pp->dat, s)) {
484 s += pp->len;
485 goto jouter;
487 break;
489 return s;
492 static char *
493 __subject(struct message *mp, bool_t threaded, size_t yetprinted)
495 /* XXX NOTE: because of efficiency reasons we simply ignore any encoded
496 * XXX parts and use ASCII case-insensitive comparison */
497 struct str in, out;
498 struct message *xmp;
499 char *rv = (char*)-1, *ms, *mso, *os;
501 if ((ms = hfield1("subject", mp)) == NULL)
502 goto jleave;
504 if (!threaded || mp->m_level == 0)
505 goto jconv;
507 /* In a display thread - check wether this message uses the same
508 * Subject: as it's parent or elder neighbour, suppress printing it if
509 * this is the case. To extend this a bit, ignore any leading Re: or
510 * Fwd: plus follow-up WS. Ignore invisible messages along the way */
511 mso = __subject_trim(ms);
512 for (xmp = mp; (xmp = prev_in_thread(xmp)) != NULL && yetprinted-- > 0;)
513 if (visible(xmp) && (os = hfield1("subject", xmp)) != NULL &&
514 asccasecmp(mso, __subject_trim(os)) == 0)
515 goto jleave;
516 jconv:
517 in.s = ms;
518 in.l = strlen(ms);
519 mime_fromhdr(&in, &out, TD_ICONV | TD_ISPR);
520 rv = out.s;
521 jleave:
522 return rv;
525 static int
526 __putindent(FILE *fp, struct message *mp, int maxwidth)/* XXX no magic consts */
528 struct message *mq;
529 int *us, indlvl, indw, i, important = MNEW|MFLAGGED;
530 char *cs;
532 if (mp->m_level == 0 || maxwidth == 0)
533 return 0;
534 cs = ac_alloc(mp->m_level);
535 us = ac_alloc(mp->m_level * sizeof *us);
537 i = mp->m_level - 1;
538 if (mp->m_younger && UICMP(32, i + 1, ==, mp->m_younger->m_level)) {
539 if (mp->m_parent && mp->m_parent->m_flag & important)
540 us[i] = mp->m_flag & important ? 0x2523 : 0x2520;
541 else
542 us[i] = mp->m_flag & important ? 0x251D : 0x251C;
543 cs[i] = '+';
544 } else {
545 if (mp->m_parent && mp->m_parent->m_flag & important)
546 us[i] = mp->m_flag & important ? 0x2517 : 0x2516;
547 else
548 us[i] = mp->m_flag & important ? 0x2515 : 0x2514;
549 cs[i] = '\\';
552 mq = mp->m_parent;
553 for (i = mp->m_level - 2; i >= 0; i--) {
554 if (mq) {
555 if (UICMP(32, i, >, mq->m_level - 1)) {
556 us[i] = cs[i] = ' ';
557 continue;
559 if (mq->m_younger) {
560 if (mq->m_parent &&
561 mq->m_parent->m_flag&important)
562 us[i] = 0x2503;
563 else
564 us[i] = 0x2502;
565 cs[i] = '|';
566 } else
567 us[i] = cs[i] = ' ';
568 mq = mq->m_parent;
569 } else
570 us[i] = cs[i] = ' ';
573 --maxwidth;
574 for (indlvl = indw = 0; (uc_it)indlvl < mp->m_level &&
575 indw < maxwidth; ++indlvl) {
576 if (indw < maxwidth - 1)
577 indw += (int)putuc(us[indlvl], cs[indlvl] & 0377, fp);
578 else
579 indw += (int)putuc(0x21B8, '^', fp);
581 indw += /*putuc(0x261E, fp)*/putc('>', fp) != EOF;
583 ac_free(us);
584 ac_free(cs);
585 return indw;
588 FL int
589 ccmdnotsupp(void *v) /* TODO -> lex.c */
591 (void)v;
592 fprintf(stderr, tr(10, "The requested feature is not compiled in\n"));
593 return (1);
596 FL char const *
597 get_pager(void)
599 char const *cp;
601 cp = value("PAGER");
602 if (cp == NULL || *cp == '\0')
603 cp = PAGER;
604 return cp;
607 FL int
608 headers(void *v)
610 ui32_t flag;
611 int *msgvec = v, g, k, n, mesg, size, lastg = 1;
612 struct message *mp, *mq, *lastmq = NULL;
613 enum mflag fl = MNEW|MFLAGGED;
615 time_current_update(&time_current, FAL0);
617 flag = 0;
618 size = screensize();
619 n = msgvec[0]; /* n == {-2, -1, 0}: called from scroll() */
620 if (screen < 0)
621 screen = 0;
622 k = screen * size;
623 if (k >= msgCount)
624 k = msgCount - size;
625 if (k < 0)
626 k = 0;
628 if (mb.mb_threaded == 0) {
629 g = 0;
630 mq = &message[0];
631 for (mp = &message[0]; mp < &message[msgCount]; mp++)
632 if (visible(mp)) {
633 if (g % size == 0)
634 mq = mp;
635 if (mp->m_flag&fl) {
636 lastg = g;
637 lastmq = mq;
639 if ((n > 0 && mp == &message[n-1]) ||
640 (n == 0 && g == k) ||
641 (n == -2 && g == k + size &&
642 lastmq) ||
643 (n < 0 && g >= k &&
644 (mp->m_flag & fl) != 0))
645 break;
646 g++;
648 if (lastmq && (n==-2 || (n==-1 && mp == &message[msgCount]))) {
649 g = lastg;
650 mq = lastmq;
652 screen = g / size;
653 mp = mq;
654 mesg = mp - &message[0];
655 if (dot != &message[n-1]) {
656 for (mq = mp; mq < &message[msgCount]; mq++)
657 if (visible(mq)) {
658 setdot(mq);
659 break;
662 #ifdef HAVE_IMAP
663 if (mb.mb_type == MB_IMAP)
664 imap_getheaders(mesg+1, mesg + size);
665 #endif
666 srelax_hold();
667 for (; mp < &message[msgCount]; mp++) {
668 mesg++;
669 if (!visible(mp))
670 continue;
671 if (UICMP(32, flag++, >=, size))
672 break;
673 _print_head(0, mesg, stdout, 0);
674 srelax();
676 srelax_rele();
677 } else { /* threaded */
678 g = 0;
679 mq = threadroot;
680 for (mp = threadroot; mp; mp = next_in_thread(mp))
681 if (visible(mp) && (mp->m_collapsed <= 0 ||
682 mp == &message[n-1])) {
683 if (g % size == 0)
684 mq = mp;
685 if (mp->m_flag&fl) {
686 lastg = g;
687 lastmq = mq;
689 if ((n > 0 && mp == &message[n-1]) ||
690 (n == 0 && g == k) ||
691 (n == -2 && g == k + size &&
692 lastmq) ||
693 (n < 0 && g >= k &&
694 (mp->m_flag & fl) != 0))
695 break;
696 g++;
698 if (lastmq && (n==-2 || (n==-1 && mp==&message[msgCount]))) {
699 g = lastg;
700 mq = lastmq;
702 screen = g / size;
703 mp = mq;
704 if (dot != &message[n-1]) {
705 for (mq = mp; mq; mq = next_in_thread(mq))
706 if (visible(mq) && mq->m_collapsed <= 0) {
707 setdot(mq);
708 break;
711 srelax_hold();
712 while (mp) {
713 if (visible(mp) && (mp->m_collapsed <= 0 ||
714 mp == &message[n-1])) {
715 if (UICMP(32, flag++, >=, size))
716 break;
717 _print_head(flag - 1, mp - &message[0] + 1,
718 stdout, mb.mb_threaded);
719 srelax();
721 mp = next_in_thread(mp);
723 srelax_rele();
725 if (!flag)
726 printf(tr(6, "No more mail.\n"));
727 return !flag;
731 * Scroll to the next/previous screen
733 FL int
734 scroll(void *v)
736 return scroll1(v, 0);
739 FL int
740 Scroll(void *v)
742 return scroll1(v, 1);
745 static int
746 scroll1(char *arg, int onlynew)
748 int size;
749 int cur[1];
751 cur[0] = onlynew ? -1 : 0;
752 size = screensize();
753 switch (*arg) {
754 case '1': case '2': case '3': case '4': case '5':
755 case '6': case '7': case '8': case '9': case '0':
756 screen = atoi(arg);
757 goto scroll_forward;
758 case '\0':
759 screen++;
760 goto scroll_forward;
761 case '$':
762 screen = msgCount / size;
763 goto scroll_forward;
764 case '+':
765 if (arg[1] == '\0')
766 screen++;
767 else
768 screen += atoi(arg + 1);
769 scroll_forward:
770 if (screen * size > msgCount) {
771 screen = msgCount / size;
772 printf(tr(7, "On last screenful of messages\n"));
774 break;
776 case '-':
777 if (arg[1] == '\0')
778 screen--;
779 else
780 screen -= atoi(arg + 1);
781 if (screen < 0) {
782 screen = 0;
783 printf(tr(8, "On first screenful of messages\n"));
785 if (cur[0] == -1)
786 cur[0] = -2;
787 break;
789 default:
790 printf(tr(9, "Unrecognized scrolling command \"%s\"\n"), arg);
791 return(1);
793 return(headers(cur));
797 * Compute screen size.
799 FL int
800 screensize(void)
802 int s;
803 char *cp;
805 if ((cp = value("screen")) != NULL && (s = atoi(cp)) > 0)
806 return s;
807 return scrnheight - 4;
810 static sigjmp_buf _cmd1_pipejmp;
812 /*ARGSUSED*/
813 static void
814 _cmd1_onpipe(int signo)
816 UNUSED(signo);
817 siglongjmp(_cmd1_pipejmp, 1);
821 * Print out the headlines for each message
822 * in the passed message list.
824 FL int
825 from(void *v)
827 int *msgvec = v, *ip, n;
828 char *cp;
829 FILE *volatile obuf = stdout;
831 time_current_update(&time_current, FAL0);
833 /* TODO unfixable memory leaks still */
834 if (IS_TTY_SESSION() && (cp = value("crt")) != NULL) {
835 for (n = 0, ip = msgvec; *ip; ip++)
836 n++;
837 if (n > (*cp == '\0' ? screensize() : atoi((char*)cp)) + 3) {
838 char const *p;
839 if (sigsetjmp(_cmd1_pipejmp, 1))
840 goto endpipe;
841 p = get_pager();
842 if ((obuf = Popen(p, "w", NULL, 1)) == NULL) {
843 perror(p);
844 obuf = stdout;
845 cp=NULL;
846 } else
847 safe_signal(SIGPIPE, _cmd1_onpipe);
850 for (n = 0, ip = msgvec; *ip != 0; ip++)
851 _print_head((size_t)n++, *ip, obuf, mb.mb_threaded);
852 if (--ip >= msgvec)
853 setdot(&message[*ip - 1]);
854 endpipe:
855 if (obuf != stdout) {
856 safe_signal(SIGPIPE, SIG_IGN);
857 Pclose(obuf, TRU1);
858 safe_signal(SIGPIPE, dflpipe);
860 return(0);
863 static int
864 _dispc(struct message *mp, const char *a)
866 int i = ' ';
869 * Bletch!
871 if ((mp->m_flag & (MREAD|MNEW)) == MREAD)
872 i = a[3];
873 if ((mp->m_flag & (MREAD|MNEW)) == (MREAD|MNEW))
874 i = a[2];
875 if (mp->m_flag & MANSWERED)
876 i = a[8];
877 if (mp->m_flag & MDRAFTED)
878 i = a[9];
879 if ((mp->m_flag & (MREAD|MNEW)) == MNEW)
880 i = a[0];
881 if ((mp->m_flag & (MREAD|MNEW)) == 0)
882 i = a[1];
883 if (mp->m_flag & MSPAM)
884 i = a[12];
885 if (mp->m_flag & MSAVED)
886 i = a[4];
887 if (mp->m_flag & MPRESERVE)
888 i = a[5];
889 if (mp->m_flag & (MBOX|MBOXED))
890 i = a[6];
891 if (mp->m_flag & MFLAGGED)
892 i = a[7];
893 if (mb.mb_threaded == 1 && mp->m_collapsed > 0)
894 i = a[11];
895 if (mb.mb_threaded == 1 && mp->m_collapsed < 0)
896 i = a[10];
897 return i;
900 FL void
901 print_headers(size_t bottom, size_t topx)
903 size_t printed;
905 #ifdef HAVE_IMAP
906 if (mb.mb_type == MB_IMAP)
907 imap_getheaders(bottom, topx);
908 #endif
909 time_current_update(&time_current, FAL0);
911 for (printed = 0; bottom <= topx; ++bottom)
912 if (visible(&message[bottom - 1]))
913 _print_head(printed++, bottom, stdout, 0);
917 * Print out the value of dot.
919 /*ARGSUSED*/
920 FL int
921 pdot(void *v)
923 (void)v;
924 printf("%d\n", (int)(dot - &message[0] + 1));
925 return(0);
929 * Type out the messages requested.
931 static sigjmp_buf pipestop;
933 /*ARGSUSED*/
934 static void
935 brokpipe(int signo)
937 (void)signo;
938 siglongjmp(pipestop, 1);
941 static int
942 _type1(int *msgvec, bool_t doign, bool_t dopage, bool_t dopipe,
943 bool_t dodecode, char *cmd, off_t *tstats)
945 off_t mstats[2];
946 int *ip;
947 struct message *mp;
948 char const *cp;
949 FILE * volatile obuf;
951 obuf = stdout;
952 if (sigsetjmp(pipestop, 1))
953 goto close_pipe;
954 if (dopipe) {
955 if ((cp = value("SHELL")) == NULL)
956 cp = SHELL;
957 if ((obuf = Popen(cmd, "w", cp, 1)) == NULL) {
958 perror(cmd);
959 obuf = stdout;
960 } else
961 safe_signal(SIGPIPE, brokpipe);
962 } else if ((options & OPT_TTYOUT) &&
963 (dopage || (cp = value("crt")) != NULL)) {
964 long nlines = 0;
965 if (!dopage) {
966 for (ip = msgvec; *ip &&
967 PTRCMP(ip - msgvec, <, msgCount);
968 ++ip) {
969 if (!(message[*ip - 1].m_have & HAVE_BODY)) {
970 if ((get_body(&message[*ip - 1])) !=
971 OKAY)
972 return 1;
974 nlines += message[*ip - 1].m_lines;
977 if (dopage || nlines > (*cp ? atoi(cp) : realscreenheight)) {
978 char const *p = get_pager();
979 if ((obuf = Popen(p, "w", NULL, 1)) == NULL) {
980 perror(p);
981 obuf = stdout;
982 } else
983 safe_signal(SIGPIPE, brokpipe);
987 /* This may jump, in which case srelax_rele() wouldn't be called, but
988 * it shouldn't matter, because we -- then -- directly reenter the
989 * lex.c:commands() loop, which sreset()s */
990 srelax_hold();
991 for (ip = msgvec; *ip && PTRCMP(ip - msgvec, <, msgCount); ++ip) {
992 mp = &message[*ip - 1];
993 touch(mp);
994 setdot(mp);
995 uncollapse1(mp, 1);
996 if (!dopipe && ip != msgvec)
997 fprintf(obuf, "\n");
998 _show_msg_overview(mp, *ip, obuf);
999 sendmp(mp, obuf, (doign ? ignore : 0), NULL,
1000 ((dopipe && boption("piperaw"))
1001 ? SEND_MBOX : dodecode
1002 ? SEND_SHOW : doign
1003 ? SEND_TODISP : SEND_TODISP_ALL),
1004 mstats);
1005 srelax();
1006 if (dopipe && boption("page"))
1007 putc('\f', obuf);
1008 if (tstats) {
1009 tstats[0] += mstats[0];
1010 tstats[1] += mstats[1];
1013 srelax_rele();
1014 close_pipe:
1015 if (obuf != stdout) {
1016 /* Ignore SIGPIPE so it can't cause a duplicate close */
1017 safe_signal(SIGPIPE, SIG_IGN);
1018 Pclose(obuf, TRU1);
1019 safe_signal(SIGPIPE, dflpipe);
1021 return 0;
1025 * Pipe the messages requested.
1027 static int
1028 pipe1(char *str, int doign)
1030 char *cmd;
1031 int *msgvec, ret;
1032 off_t stats[2];
1033 bool_t f;
1035 /*LINTED*/
1036 msgvec = (int *)salloc((msgCount + 2) * sizeof *msgvec);
1037 if ((cmd = laststring(str, &f, 1)) == NULL) {
1038 cmd = value("cmd");
1039 if (cmd == NULL || *cmd == '\0') {
1040 fputs(tr(16, "variable cmd not set\n"), stderr);
1041 return 1;
1044 if (!f) {
1045 *msgvec = first(0, MMNORM);
1046 if (*msgvec == 0) {
1047 if (inhook)
1048 return 0;
1049 puts(tr(18, "No messages to pipe."));
1050 return 1;
1052 msgvec[1] = 0;
1053 } else if (getmsglist(str, msgvec, 0) < 0)
1054 return 1;
1055 if (*msgvec == 0) {
1056 if (inhook)
1057 return 0;
1058 printf("No applicable messages.\n");
1059 return 1;
1061 printf(tr(268, "Pipe to: \"%s\"\n"), cmd);
1062 stats[0] = stats[1] = 0;
1063 if ((ret = _type1(msgvec, doign, FAL0, TRU1, FAL0, cmd, stats)) == 0) {
1064 printf("\"%s\" ", cmd);
1065 if (stats[0] >= 0)
1066 printf("%lu", (long)stats[0]);
1067 else
1068 printf(tr(27, "binary"));
1069 printf("/%lu\n", (long)stats[1]);
1071 return ret;
1075 * Paginate messages, honor ignored fields.
1077 FL int
1078 more(void *v)
1080 int *msgvec = v;
1082 return _type1(msgvec, TRU1, TRU1, FAL0, FAL0, NULL, NULL);
1086 * Paginate messages, even printing ignored fields.
1088 FL int
1089 More(void *v)
1091 int *msgvec = v;
1093 return _type1(msgvec, FAL0, TRU1, FAL0, FAL0, NULL, NULL);
1097 * Type out messages, honor ignored fields.
1099 FL int
1100 type(void *v)
1102 int *msgvec = v;
1104 return _type1(msgvec, TRU1, FAL0, FAL0, FAL0, NULL, NULL);
1108 * Type out messages, even printing ignored fields.
1110 FL int
1111 Type(void *v)
1113 int *msgvec = v;
1115 return _type1(msgvec, FAL0, FAL0, FAL0, FAL0, NULL, NULL);
1119 * Show MIME-encoded message text, including all fields.
1121 FL int
1122 show(void *v)
1124 int *msgvec = v;
1126 return _type1(msgvec, FAL0, FAL0, FAL0, TRU1, NULL, NULL);
1130 * Pipe messages, honor ignored fields.
1132 FL int
1133 pipecmd(void *v)
1135 char *str = v;
1136 return(pipe1(str, 1));
1139 * Pipe messages, not respecting ignored fields.
1141 FL int
1142 Pipecmd(void *v)
1144 char *str = v;
1145 return(pipe1(str, 0));
1149 * Print the top so many lines of each desired message.
1150 * The number of lines is taken from the variable "toplines"
1151 * and defaults to 5.
1153 FL int
1154 top(void *v)
1156 int *msgvec = v, *ip, c, topl, lines, empty_last;
1157 struct message *mp;
1158 char *cp, *linebuf = NULL;
1159 size_t linesize = 0;
1160 FILE *ibuf;
1162 topl = 5;
1163 cp = value("toplines");
1164 if (cp != NULL) {
1165 topl = atoi(cp);
1166 if (topl < 0 || topl > 10000)
1167 topl = 5;
1169 empty_last = 1;
1170 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
1171 mp = &message[*ip - 1];
1172 touch(mp);
1173 setdot(mp);
1174 did_print_dot = TRU1;
1175 if (! empty_last)
1176 printf("\n");
1177 _show_msg_overview(mp, *ip, stdout);
1178 if (mp->m_flag & MNOFROM)
1179 printf("From %s %s\n", fakefrom(mp),
1180 fakedate(mp->m_time));
1181 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL) { /* XXX could use TOP */
1182 v = NULL;
1183 break;
1185 c = mp->m_lines;
1186 for (lines = 0; lines < c && UICMP(32, lines, <=, topl);
1187 ++lines) {
1188 if (readline_restart(ibuf, &linebuf, &linesize, 0) < 0)
1189 break;
1190 puts(linebuf);
1192 for (cp = linebuf; *cp && blankchar(*cp); ++cp)
1194 empty_last = (*cp == '\0');
1198 if (linebuf != NULL)
1199 free(linebuf);
1200 return (v != NULL);
1204 * Touch all the given messages so that they will
1205 * get mboxed.
1207 FL int
1208 stouch(void *v)
1210 int *msgvec = v;
1211 int *ip;
1213 for (ip = msgvec; *ip != 0; ip++) {
1214 setdot(&message[*ip-1]);
1215 dot->m_flag |= MTOUCH;
1216 dot->m_flag &= ~MPRESERVE;
1218 * POSIX interpretation necessary.
1220 did_print_dot = TRU1;
1222 return(0);
1226 * Make sure all passed messages get mboxed.
1228 FL int
1229 mboxit(void *v)
1231 int *msgvec = v;
1232 int *ip;
1234 for (ip = msgvec; *ip != 0; ip++) {
1235 setdot(&message[*ip-1]);
1236 dot->m_flag |= MTOUCH|MBOX;
1237 dot->m_flag &= ~MPRESERVE;
1239 * POSIX interpretation necessary.
1241 did_print_dot = TRU1;
1243 return(0);
1247 * List the folders the user currently has.
1249 FL int
1250 folders(void *v)
1252 char dirname[MAXPATHLEN], *name, **argv = v;
1253 char const *cmd;
1255 if (*argv) {
1256 name = expand(*argv);
1257 if (name == NULL)
1258 return 1;
1259 } else if (! getfold(dirname, sizeof dirname)) {
1260 fprintf(stderr, tr(20, "No value set for \"folder\"\n"));
1261 return 1;
1262 } else
1263 name = dirname;
1265 if (which_protocol(name) == PROTO_IMAP) {
1266 #ifdef HAVE_IMAP
1267 imap_folders(name, *argv == NULL);
1268 #else
1269 return ccmdnotsupp(NULL);
1270 #endif
1271 } else {
1272 if ((cmd = value("LISTER")) == NULL)
1273 cmd = LISTER;
1274 run_command(cmd, 0, -1, -1, name, NULL, NULL);
1276 return 0;