main(): fix argument parsing (Gaetan Bisson)..
[s-mailx.git] / cmd1.c
blob9c2713c4e3efa8994700db173d47bd8f7200b5d7
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]) /* TODO line pool */
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 enum {attrlen = 13};
137 char attrlist[attrlen +1], *cp;
138 char const *fmt;
139 NYD_ENTER;
141 if ((cp = ok_vlook(attrlist)) != NULL) {
142 if (strlen(cp) == attrlen) {
143 memcpy(attrlist, cp, attrlen +1);
144 goto jattrok;
146 fprintf(stderr, tr(570,
147 "The value of *attrlist* is not of the correct length\n"));
149 if (ok_blook(bsdcompat) || ok_blook(bsdflags) ||
150 getenv("SYSV3") != NULL) {
151 char const bsdattr[attrlen +1] = "NU *HMFAT+-$";
152 memcpy(attrlist, bsdattr, sizeof bsdattr);
153 } else {
154 char const pattr[attrlen +1] = "NUROSPMFAT+-$";
155 memcpy(attrlist, pattr, sizeof pattr);
157 jattrok:
158 if ((fmt = ok_vlook(headline)) == NULL) {
159 fmt = ((ok_blook(bsdcompat) || ok_blook(bsdheadline))
160 ? "%>%a%m %-20f %16d %3l/%-5o %i%-S"
161 : "%>%a%m %-18f %16d %4l/%-5o %i%-s");
164 __hprf(yetprinted, fmt, msgno, f, threaded, attrlist);
165 NYD_LEAVE;
168 static void
169 __hprf(size_t yetprinted, char const *fmt, size_t msgno, FILE *f,
170 bool_t threaded, char const *attrlist)
172 char datebuf[FROM_DATEBUF], *cp, *subjline;
173 char const *datefmt, *date, *name, *fp;
174 int B, c, i, n, s, wleft, subjlen, isto = 0, isaddr = 0;
175 struct message *mp;
176 time_t datet;
177 NYD_ENTER;
179 mp = message + msgno - 1;
180 datet = mp->m_time;
181 date = NULL;
183 datefmt = ok_vlook(datefield);
184 jredo:
185 if (datefmt != NULL) {
186 fp = hfield1("date", mp);/* TODO use m_date field! */
187 if (fp == NULL) {
188 datefmt = NULL;
189 goto jredo;
191 datet = rfctime(fp);
192 date = fakedate(datet);
193 fp = ok_vlook(datefield_markout_older);
194 i = (*datefmt != '\0');
195 if (fp != NULL)
196 i |= (*fp != '\0') ? 2 | 4 : 2; /* XXX no magics */
198 /* May we strftime(3)? */
199 if (i & (1 | 4))
200 memcpy(&time_current.tc_local, localtime(&datet),
201 sizeof time_current.tc_local);
203 if ((i & 2) && (datet > time_current.tc_time + DATE_SECSDAY ||
204 #define _6M ((DATE_DAYSYEAR / 2) * DATE_SECSDAY)
205 (datet + _6M < time_current.tc_time))) {
206 #undef _6M
207 if ((datefmt = (i & 4) ? fp : NULL) == NULL) {
208 memset(datebuf, ' ', FROM_DATEBUF); /* xxx ur */
209 memcpy(datebuf + 4, date + 4, 7);
210 datebuf[4 + 7] = ' ';
211 memcpy(datebuf + 4 + 7 + 1, date + 20, 4);
212 datebuf[4 + 7 + 1 + 4] = '\0';
213 date = datebuf;
215 } else if ((i & 1) == 0)
216 datefmt = NULL;
217 } else if (datet == (time_t)0 && !(mp->m_flag & MNOFROM)) {
218 /* TODO eliminate this path, query the FROM_ date in setptr(),
219 * TODO all other codepaths do so by themselves ALREADY ?????
220 * TODO assert(mp->m_time != 0);, then
221 * TODO ALSO changes behaviour of datefield_markout_older */
222 _parse_from_(mp, datebuf);
223 date = datebuf;
224 } else
225 date = fakedate(datet);
227 isaddr = 1;
228 name = name1(mp, 0);
229 if (name != NULL && ok_blook(showto) && is_myname(skin(name))) {
230 if ((cp = hfield1("to", mp)) != NULL) {
231 name = cp;
232 isto = 1;
235 if (name == NULL) {
236 name = "";
237 isaddr = 0;
239 if (isaddr)
240 name = ok_blook(showname) ? realname(name) : prstr(skin(name));
242 subjline = NULL;
244 /* Detect the width of the non-format characters in *headline*;
245 * like that we can simply use putc() in the next loop, since we have
246 * already calculated their column widths (TODO it's sick) */
247 wleft = subjlen = scrnwidth;
249 for (fp = fmt; *fp != '\0'; ++fp) {
250 if (*fp == '%') {
251 if (*++fp == '-')
252 ++fp;
253 else if (*fp == '+')
254 ++fp;
255 if (digitchar(*fp)) {
256 n = 0;
258 n = 10*n + *fp - '0';
259 while (++fp, digitchar(*fp));
260 subjlen -= n;
263 if (*fp == '\0')
264 break;
265 } else {
266 #ifdef HAVE_WCWIDTH
267 if (mb_cur_max > 1) {
268 wchar_t wc;
269 if ((s = mbtowc(&wc, fp, mb_cur_max)) == -1)
270 n = s = 1;
271 else if ((n = wcwidth(wc)) == -1)
272 n = 1;
273 } else
274 #endif
275 n = s = 1;
276 subjlen -= n;
277 wleft -= n;
278 while (--s > 0)
279 ++fp;
283 /* Walk *headline*, producing output TODO not (really) MB safe */
284 for (fp = fmt; *fp != '\0'; ++fp) {
285 if ((c = *fp & 0xFF) != '%')
286 putc(c, f);
287 else {
288 B = 0;
289 n = 0;
290 s = 1;
291 if (*++fp == '-') {
292 s = -1;
293 ++fp;
294 } else if (*fp == '+')
295 ++fp;
296 if (digitchar(*fp)) {
298 n = 10*n + *fp - '0';
299 while (++fp, digitchar(*fp));
301 if (*fp == '\0')
302 break;
304 n *= s;
305 switch ((c = *fp & 0xFF)) {
306 case '%':
307 goto jputc;
308 case '>':
309 case '<':
310 if (dot != mp)
311 c = ' ';
312 goto jputc;
313 case '$':
314 #ifdef HAVE_SPAM
315 if (n == 0)
316 n = 4;
317 if (UICMP(32, ABS(n), >, wleft))
318 n = (n < 0) ? -wleft : wleft;
319 { char buf[16];
320 snprintf(buf, sizeof buf, "%u.%u",
321 (mp->m_spamscore >> 8), (mp->m_spamscore & 0xFF));
322 n = fprintf(f, "%*s", n, buf);
323 wleft = (n >= 0) ? wleft - n : 0;
325 #else
326 c = '?';
327 goto jputc;
328 #endif
329 case 'a':
330 c = _dispc(mp, attrlist);
331 jputc:
332 if (UICMP(32, ABS(n), >, wleft))
333 n = (n < 0) ? -wleft : wleft;
334 n = fprintf(f, "%*c", n, c);
335 wleft = (n >= 0) ? wleft - n : 0;
336 break;
337 case 'd':
338 if (datefmt != NULL) {
339 i = strftime(datebuf, sizeof datebuf, datefmt,
340 &time_current.tc_local);
341 if (i != 0)
342 date = datebuf;
343 else
344 fprintf(stderr, tr(174,
345 "Ignored date format, it excesses the target buffer "
346 "(%lu bytes)\n"), (ul_it)sizeof datebuf);
347 datefmt = NULL;
349 if (n == 0)
350 n = 16;
351 if (UICMP(32, ABS(n), >, wleft))
352 n = (n < 0) ? -wleft : wleft;
353 n = fprintf(f, "%*.*s", n, n, date);
354 wleft = (n >= 0) ? wleft - n : 0;
355 break;
356 case 'e':
357 if (n == 0)
358 n = 2;
359 if (UICMP(32, ABS(n), >, wleft))
360 n = (n < 0) ? -wleft : wleft;
361 n = fprintf(f, "%*u", n, (threaded == 1 ? mp->m_level : 0));
362 wleft = (n >= 0) ? wleft - n : 0;
363 break;
364 case 'f':
365 if (n == 0) {
366 n = 18;
367 if (s < 0)
368 n = -n;
370 i = ABS(n);
371 if (i > wleft) {
372 i = wleft;
373 n = (n < 0) ? -wleft : wleft;
375 if (isto) /* XXX tr()! */
376 i -= 3;
377 n = fprintf(f, "%s%s", (isto ? "To " : ""),
378 colalign(name, i, n, &wleft));
379 if (n < 0)
380 wleft = 0;
381 else if (isto)
382 wleft -= 3;
383 break;
384 case 'i':
385 if (threaded) {
386 n = __putindent(f, mp, MIN(wleft, scrnwidth - 60));
387 wleft = (n >= 0) ? wleft - n : 0;
389 break;
390 case 'l':
391 if (n == 0)
392 n = 4;
393 if (UICMP(32, ABS(n), >, wleft))
394 n = (n < 0) ? -wleft : wleft;
395 if (mp->m_xlines) {
396 n = fprintf(f, "%*ld", n, mp->m_xlines);
397 wleft = (n >= 0) ? wleft - n : 0;
398 } else {
399 n = ABS(n);
400 wleft -= n;
401 while (n-- != 0)
402 putc(' ', f);
404 break;
405 case 'm':
406 if (n == 0) {
407 n = 3;
408 if (threaded)
409 for (i = msgCount; i > 999; i /= 10)
410 ++n;
412 if (UICMP(32, ABS(n), >, wleft))
413 n = (n < 0) ? -wleft : wleft;
414 n = fprintf(f, "%*lu", n, (ul_it)msgno);
415 wleft = (n >= 0) ? wleft - n : 0;
416 break;
417 case 'o':
418 if (n == 0)
419 n = -5;
420 if (UICMP(32, ABS(n), >, wleft))
421 n = (n < 0) ? -wleft : wleft;
422 n = fprintf(f, "%*lu", n, (long)mp->m_xsize);
423 wleft = (n >= 0) ? wleft - n : 0;
424 break;
425 case 'S':
426 B = 1;
427 /*FALLTHRU*/
428 case 's':
429 if (n == 0)
430 n = subjlen - 2;
431 if (n > 0 && s < 0)
432 n = -n;
433 if (subjlen > wleft)
434 subjlen = wleft;
435 if (UICMP(32, ABS(n), >, subjlen))
436 n = (n < 0) ? -subjlen : subjlen;
437 if (B)
438 n -= (n < 0) ? -2 : 2;
439 if (n == 0)
440 break;
441 if (subjline == NULL)
442 subjline = __subject(mp, threaded, yetprinted);
443 if (subjline == (char*)-1) {
444 n = fprintf(f, "%*s", n, "");
445 wleft = (n >= 0) ? wleft - n : 0;
446 } else {
447 n = fprintf(f, (B ? "\"%s\"" : "%s"),
448 colalign(subjline, ABS(n), n, &wleft));
449 if (n < 0)
450 wleft = 0;
452 break;
453 case 't':
454 if (n == 0) {
455 n = 3;
456 if (threaded)
457 for (i = msgCount; i > 999; i /= 10)
458 ++n;
460 if (UICMP(32, ABS(n), >, wleft))
461 n = (n < 0) ? -wleft : wleft;
462 n = fprintf(f, "%*lu", n,
463 (threaded ? (ul_it)mp->m_threadpos : (ul_it)msgno));
464 wleft = (n >= 0) ? wleft - n : 0;
465 break;
466 case 'U':
467 #ifdef HAVE_IMAP
468 if (n == 0)
469 n = 9;
470 if (UICMP(32, ABS(n), >, wleft))
471 n = (n < 0) ? -wleft : wleft;
472 n = fprintf(f, "%*lu", n, mp->m_uid);
473 wleft = (n >= 0) ? wleft - n : 0;
474 break;
475 #else
476 c = '?';
477 goto jputc;
478 #endif
481 if (wleft <= 0)
482 break;
485 putc('\n', f);
487 if (subjline != NULL && subjline != (char*)-1)
488 free(subjline);
489 NYD_LEAVE;
492 static char *
493 __subject_trim(char *s)
495 struct {
496 ui8_t len;
497 char dat[7];
498 } const *pp, ignored[] = { /* TODO make ignore list configurable */
499 { 3, "re:" }, { 4, "fwd:" },
500 { 3, "aw:" }, { 5, "antw:" },
501 { 0, "" }
503 NYD_ENTER;
505 jouter:
506 while (*s != '\0') {
507 while (spacechar(*s))
508 ++s;
509 /* TODO While it is maybe ok not to MIME decode these (for purpose), we
510 * TODO should skip =?..?= at the beginning? */
511 for (pp = ignored; pp->len > 0; ++pp)
512 if (is_asccaseprefix(pp->dat, s)) {
513 s += pp->len;
514 goto jouter;
516 break;
518 NYD_LEAVE;
519 return s;
522 static char *
523 __subject(struct message *mp, bool_t threaded, size_t yetprinted)
525 /* XXX NOTE: because of efficiency reasons we simply ignore any encoded
526 * XXX parts and use ASCII case-insensitive comparison */
527 struct str in, out;
528 struct message *xmp;
529 char *rv = (char*)-1, *ms, *mso, *os;
530 NYD_ENTER;
532 if ((ms = hfield1("subject", mp)) == NULL)
533 goto jleave;
535 if (!threaded || mp->m_level == 0)
536 goto jconv;
538 /* In a display thread - check wether this message uses the same
539 * Subject: as it's parent or elder neighbour, suppress printing it if
540 * this is the case. To extend this a bit, ignore any leading Re: or
541 * Fwd: plus follow-up WS. Ignore invisible messages along the way */
542 mso = __subject_trim(ms);
543 for (xmp = mp; (xmp = prev_in_thread(xmp)) != NULL && yetprinted-- > 0;)
544 if (visible(xmp) && (os = hfield1("subject", xmp)) != NULL &&
545 !asccasecmp(mso, __subject_trim(os)))
546 goto jleave;
547 jconv:
548 in.s = ms;
549 in.l = strlen(ms);
550 mime_fromhdr(&in, &out, TD_ICONV | TD_ISPR);
551 rv = out.s;
552 jleave:
553 NYD_LEAVE;
554 return rv;
557 static int
558 __putindent(FILE *fp, struct message *mp, int maxwidth)/* XXX no magic consts */
560 struct message *mq;
561 int *us, indlvl, indw, i, important = MNEW | MFLAGGED;
562 char *cs;
563 NYD_ENTER;
565 if (mp->m_level == 0 || maxwidth == 0) {
566 indw = 0;
567 goto jleave;
570 cs = ac_alloc(mp->m_level);
571 us = ac_alloc(mp->m_level * sizeof *us);
573 i = mp->m_level - 1;
574 if (mp->m_younger && UICMP(32, i + 1, ==, mp->m_younger->m_level)) {
575 if (mp->m_parent && mp->m_parent->m_flag & important)
576 us[i] = mp->m_flag & important ? 0x2523 : 0x2520;
577 else
578 us[i] = mp->m_flag & important ? 0x251D : 0x251C;
579 cs[i] = '+';
580 } else {
581 if (mp->m_parent && mp->m_parent->m_flag & important)
582 us[i] = mp->m_flag & important ? 0x2517 : 0x2516;
583 else
584 us[i] = mp->m_flag & important ? 0x2515 : 0x2514;
585 cs[i] = '\\';
588 mq = mp->m_parent;
589 for (i = mp->m_level - 2; i >= 0; --i) {
590 if (mq) {
591 if (UICMP(32, i, >, mq->m_level - 1)) {
592 us[i] = cs[i] = ' ';
593 continue;
595 if (mq->m_younger) {
596 if (mq->m_parent && (mq->m_parent->m_flag & important))
597 us[i] = 0x2503;
598 else
599 us[i] = 0x2502;
600 cs[i] = '|';
601 } else
602 us[i] = cs[i] = ' ';
603 mq = mq->m_parent;
604 } else
605 us[i] = cs[i] = ' ';
608 --maxwidth;
609 for (indlvl = indw = 0; (ui8_t)indlvl < mp->m_level && indw < maxwidth;
610 ++indlvl) {
611 if (indw < maxwidth - 1)
612 indw += (int)putuc(us[indlvl], cs[indlvl] & 0xFF, fp);
613 else
614 indw += (int)putuc(0x21B8, '^', fp);
616 indw += (/*putuc(0x261E, fp)*/putc('>', fp) != EOF);
618 ac_free(us);
619 ac_free(cs);
620 jleave:
621 NYD_LEAVE;
622 return indw;
625 static int
626 _dispc(struct message *mp, char const *a)
628 int i = ' ';
629 NYD_ENTER;
631 if ((mp->m_flag & (MREAD | MNEW)) == MREAD)
632 i = a[3];
633 if ((mp->m_flag & (MREAD | MNEW)) == (MREAD | MNEW))
634 i = a[2];
635 if (mp->m_flag & MANSWERED)
636 i = a[8];
637 if (mp->m_flag & MDRAFTED)
638 i = a[9];
639 if ((mp->m_flag & (MREAD | MNEW)) == MNEW)
640 i = a[0];
641 if (!(mp->m_flag & (MREAD | MNEW)))
642 i = a[1];
643 if (mp->m_flag & MSPAM)
644 i = a[12];
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 && mp->m_collapsed > 0)
654 i = a[11];
655 if (mb.mb_threaded == 1 && mp->m_collapsed < 0)
656 i = a[10];
657 NYD_LEAVE;
658 return i;
661 static int
662 _scroll1(char *arg, int onlynew)
664 int cur[1], size;
665 NYD_ENTER;
667 cur[0] = onlynew ? -1 : 0;
668 size = screensize();
669 switch (*arg) {
670 case '1': case '2': case '3': case '4': case '5':
671 case '6': case '7': case '8': case '9': case '0':
672 _screen = atoi(arg);
673 goto jscroll_forward;
674 case '\0':
675 ++_screen;
676 goto jscroll_forward;
677 case '$':
678 _screen = msgCount / size;
679 goto jscroll_forward;
680 case '+':
681 if (arg[1] == '\0')
682 ++_screen;
683 else
684 _screen += atoi(arg + 1);
685 jscroll_forward:
686 if (_screen * size > msgCount) {
687 _screen = msgCount / size;
688 printf(tr(7, "On last screenful of messages\n"));
690 break;
692 case '-':
693 if (arg[1] == '\0')
694 --_screen;
695 else
696 _screen -= atoi(arg + 1);
697 if (_screen < 0) {
698 _screen = 0;
699 printf(tr(8, "On first screenful of messages\n"));
701 if (cur[0] == -1)
702 cur[0] = -2;
703 break;
705 default:
706 fprintf(stderr, tr(9, "Unrecognized scrolling command \"%s\"\n"), arg);
707 size = 1;
708 goto jleave;
710 size = c_headers(cur);
711 jleave:
712 NYD_LEAVE;
713 return size;
716 static int
717 _type1(int *msgvec, bool_t doign, bool_t dopage, bool_t dopipe,
718 bool_t dodecode, char *cmd, off_t *tstats)
720 off_t mstats[2];
721 int rv, *ip;
722 struct message *mp;
723 char const *cp;
724 FILE * volatile obuf;
725 NYD_ENTER;
727 enum sendaction const action = ((dopipe && ok_blook(piperaw))
728 ? SEND_MBOX : dodecode
729 ? SEND_SHOW : doign
730 ? SEND_TODISP : SEND_TODISP_ALL);
731 bool_t const volatile formfeed = (dopipe && ok_blook(page));
732 obuf = stdout;
734 if (sigsetjmp(_cmd1_pipestop, 1))
735 goto jclose_pipe;
736 if (dopipe) {
737 if ((cp = ok_vlook(SHELL)) == NULL)
738 cp = XSHELL;
739 if ((obuf = Popen(cmd, "w", cp, 1)) == NULL) {
740 perror(cmd);
741 obuf = stdout;
742 } else
743 safe_signal(SIGPIPE, &_cmd1_brokpipe);
744 } else if ((options & OPT_TTYOUT) &&
745 (dopage || (cp = ok_vlook(crt)) != NULL)) {
746 char const *pager = NULL;
747 size_t nlines = 0;
749 if (!dopage) {
750 for (ip = msgvec; *ip && PTRCMP(ip - msgvec, <, msgCount); ++ip) {
751 mp = message + *ip - 1;
752 if (!(mp->m_have & HAVE_BODY))
753 if (get_body(mp) != OKAY) {
754 rv = 1;
755 goto jleave;
757 nlines += mp->m_lines;
761 if (dopage || UICMP(z, nlines, >,
762 (*cp != '\0' ? atoi(cp) : realscreenheight))) {
763 pager = get_pager();
764 #ifdef HAVE_SETENV
765 if ((cp = getenv("LESS")) == NULL) /* XXX not here! */
766 setenv("LESS", "FRXi", 0); /* XXX add env. */
767 #endif
768 obuf = Popen(pager, "w", NULL, 1);
769 #ifdef HAVE_SETENV
770 if (cp == NULL)
771 unsetenv("LESS"); /* XXX to Popen() etc.?!! */
772 #endif
773 if (obuf == NULL) {
774 perror(pager);
775 obuf = stdout;
776 pager = NULL;
777 } else
778 safe_signal(SIGPIPE, &_cmd1_brokpipe);
780 #ifdef HAVE_COLOUR
781 if (action != SEND_MBOX)
782 colour_table_create(pager); /* (salloc()s!) */
783 #endif
785 #ifdef HAVE_COLOUR
786 else if ((options & OPT_TTYOUT) && action != SEND_MBOX)
787 colour_table_create(NULL); /* (salloc()s!) */
788 #endif
790 /* This may jump, in which case srelax_rele() wouldn't be called, but
791 * it shouldn't matter, because we -- then -- directly reenter the
792 * lex.c:commands() loop, which sreset()s */
793 srelax_hold();
794 for (ip = msgvec; *ip && PTRCMP(ip - msgvec, <, msgCount); ++ip) {
795 mp = message + *ip - 1;
796 touch(mp);
797 setdot(mp);
798 uncollapse1(mp, 1);
799 if (!dopipe) {
800 if (ip != msgvec)
801 fprintf(obuf, "\n");
802 if (action != SEND_MBOX)
803 _show_msg_overview(obuf, mp, *ip);
805 sendmp(mp, obuf, (doign ? ignore : NULL), NULL, action, mstats);
806 srelax();
807 if (formfeed) /* TODO a nicer way to separate piped messages! */
808 putc('\f', obuf);
809 if (tstats) {
810 tstats[0] += mstats[0];
811 tstats[1] += mstats[1];
814 srelax_rele();
816 jclose_pipe:
817 if (obuf != stdout) {
818 /* Ignore SIGPIPE so it can't cause a duplicate close */
819 safe_signal(SIGPIPE, SIG_IGN);
820 colour_reset(obuf); /* XXX hacky; only here because we still jump */
821 Pclose(obuf, TRU1);
822 safe_signal(SIGPIPE, dflpipe);
824 rv = 0;
826 jleave:
827 NYD_LEAVE;
828 return rv;
831 static int
832 _pipe1(char *str, int doign)
834 off_t stats[2];
835 char *cmd;
836 int *msgvec, rv = 1;
837 bool_t needs_list;
838 NYD_ENTER;
840 if ((cmd = laststring(str, &needs_list, 1)) == NULL) {
841 cmd = ok_vlook(cmd);
842 if (cmd == NULL || *cmd == '\0') {
843 fputs(tr(16, "variable cmd not set\n"), stderr);
844 goto jleave;
848 msgvec = salloc((msgCount + 2) * sizeof *msgvec);
850 if (!needs_list) {
851 *msgvec = first(0, MMNORM);
852 if (*msgvec == 0) {
853 if (inhook) {
854 rv = 0;
855 goto jleave;
857 puts(tr(18, "No messages to pipe."));
858 goto jleave;
860 msgvec[1] = 0;
861 } else if (getmsglist(str, msgvec, 0) < 0)
862 goto jleave;
863 if (*msgvec == 0) {
864 if (inhook) {
865 rv = 0;
866 goto jleave;
868 printf("No applicable messages.\n");
869 goto jleave;
872 printf(tr(268, "Pipe to: \"%s\"\n"), cmd);
873 stats[0] = stats[1] = 0;
874 if ((rv = _type1(msgvec, doign, FAL0, TRU1, FAL0, cmd, stats)) == 0) {
875 printf("\"%s\" ", cmd);
876 if (stats[0] >= 0)
877 printf("%lu", (long)stats[0]);
878 else
879 printf(tr(27, "binary"));
880 printf("/%lu\n", (long)stats[1]);
882 jleave:
883 NYD_LEAVE;
884 return rv;
887 FL int
888 c_cmdnotsupp(void *v) /* TODO -> lex.c */
890 NYD_ENTER;
891 UNUSED(v);
892 fprintf(stderr, tr(10, "The requested feature is not compiled in\n"));
893 NYD_LEAVE;
894 return 1;
897 FL int
898 c_headers(void *v)
900 ui32_t flag;
901 int *msgvec = v, g, k, n, mesg, size, lastg = 1;
902 struct message *mp, *mq, *lastmq = NULL;
903 enum mflag fl = MNEW | MFLAGGED;
904 NYD_ENTER;
906 time_current_update(&time_current, FAL0);
908 flag = 0;
909 size = screensize();
910 n = msgvec[0]; /* n == {-2, -1, 0}: called from scroll() */
911 if (_screen < 0)
912 _screen = 0;
913 k = _screen * size;
914 if (k >= msgCount)
915 k = msgCount - size;
916 if (k < 0)
917 k = 0;
919 if (mb.mb_threaded == 0) {
920 g = 0;
921 mq = message;
922 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
923 if (visible(mp)) {
924 if (g % size == 0)
925 mq = mp;
926 if (mp->m_flag & fl) {
927 lastg = g;
928 lastmq = mq;
930 if ((n > 0 && PTRCMP(mp, ==, message + n - 1)) ||
931 (n == 0 && g == k) || (n == -2 && g == k + size && lastmq) ||
932 (n < 0 && g >= k && (mp->m_flag & fl) != 0))
933 break;
934 g++;
936 if (lastmq && (n == -2 ||
937 (n == -1 && PTRCMP(mp, ==, message + msgCount)))) {
938 g = lastg;
939 mq = lastmq;
941 _screen = g / size;
942 mp = mq;
943 mesg = (int)PTR2SIZE(mp - message);
944 if (PTRCMP(dot, !=, message + n - 1)) {
945 for (mq = mp; PTRCMP(mq, <, message + msgCount); ++mq)
946 if (visible(mq)) {
947 setdot(mq);
948 break;
951 #ifdef HAVE_IMAP
952 if (mb.mb_type == MB_IMAP)
953 imap_getheaders(mesg + 1, mesg + size);
954 #endif
955 srelax_hold();
956 for (; PTRCMP(mp, <, message + msgCount); ++mp) {
957 ++mesg;
958 if (!visible(mp))
959 continue;
960 if (UICMP(32, flag++, >=, size))
961 break;
962 _print_head(0, mesg, stdout, 0);
963 srelax();
965 srelax_rele();
966 } else { /* threaded */
967 g = 0;
968 mq = threadroot;
969 for (mp = threadroot; mp; mp = next_in_thread(mp))
970 if (visible(mp) &&
971 (mp->m_collapsed <= 0 || PTRCMP(mp, ==, message + n - 1))) {
972 if (g % size == 0)
973 mq = mp;
974 if (mp->m_flag & fl) {
975 lastg = g;
976 lastmq = mq;
978 if ((n > 0 && PTRCMP(mp, ==, message + n - 1)) ||
979 (n == 0 && g == k) || (n == -2 && g == k + size && lastmq) ||
980 (n < 0 && g >= k && (mp->m_flag & fl) != 0))
981 break;
982 g++;
984 if (lastmq && (n == -2 ||
985 (n == -1 && PTRCMP(mp, ==, message + msgCount)))) {
986 g = lastg;
987 mq = lastmq;
989 _screen = g / size;
990 mp = mq;
991 if (PTRCMP(dot, !=, message + n - 1)) {
992 for (mq = mp; mq; mq = next_in_thread(mq))
993 if (visible(mq) && mq->m_collapsed <= 0) {
994 setdot(mq);
995 break;
998 srelax_hold();
999 while (mp) {
1000 if (visible(mp) &&
1001 (mp->m_collapsed <= 0 || PTRCMP(mp, ==, message + n - 1))) {
1002 if (UICMP(32, flag++, >=, size))
1003 break;
1004 _print_head(flag - 1, PTR2SIZE(mp - message + 1), stdout,
1005 mb.mb_threaded);
1006 srelax();
1008 mp = next_in_thread(mp);
1010 srelax_rele();
1013 if (!flag)
1014 printf(tr(6, "No more mail.\n"));
1015 NYD_LEAVE;
1016 return !flag;
1019 FL int
1020 c_scroll(void *v)
1022 int rv;
1023 NYD_ENTER;
1025 rv = _scroll1(v, 0);
1026 NYD_LEAVE;
1027 return rv;
1030 FL int
1031 c_Scroll(void *v)
1033 int rv;
1034 NYD_ENTER;
1036 rv = _scroll1(v, 1);
1037 NYD_LEAVE;
1038 return rv;
1041 FL int
1042 c_from(void *v)
1044 int *msgvec = v, *ip, n;
1045 char *cp;
1046 FILE * volatile obuf;
1047 NYD_ENTER;
1049 time_current_update(&time_current, FAL0);
1050 obuf = stdout;
1052 /* TODO unfixable memory leaks still */
1053 if (IS_TTY_SESSION() && (cp = ok_vlook(crt)) != NULL) {
1054 for (n = 0, ip = msgvec; *ip != 0; ++ip)
1055 n++;
1056 if (n > (*cp == '\0' ? screensize() : atoi(cp)) + 3) {
1057 char const *p;
1058 if (sigsetjmp(_cmd1_pipejmp, 1))
1059 goto jendpipe;
1060 p = get_pager();
1061 if ((obuf = Popen(p, "w", NULL, 1)) == NULL) {
1062 perror(p);
1063 obuf = stdout;
1064 cp=NULL;
1065 } else
1066 safe_signal(SIGPIPE, &_cmd1_onpipe);
1070 for (n = 0, ip = msgvec; *ip != 0; ++ip) /* TODO join into _print_head() */
1071 _print_head((size_t)n++, (size_t)*ip, obuf, mb.mb_threaded);
1072 if (--ip >= msgvec)
1073 setdot(message + *ip - 1);
1075 jendpipe:
1076 if (obuf != stdout) {
1077 safe_signal(SIGPIPE, SIG_IGN);
1078 Pclose(obuf, TRU1);
1079 safe_signal(SIGPIPE, dflpipe);
1081 NYD_LEAVE;
1082 return 0;
1085 FL void
1086 print_headers(size_t bottom, size_t topx, bool_t only_marked)
1088 size_t printed;
1089 NYD_ENTER;
1091 #ifdef HAVE_IMAP
1092 if (mb.mb_type == MB_IMAP)
1093 imap_getheaders(bottom, topx);
1094 #endif
1095 time_current_update(&time_current, FAL0);
1097 for (printed = 0; bottom <= topx; ++bottom) {
1098 struct message *mp = message + bottom - 1;
1099 if (only_marked) {
1100 if (!(mp->m_flag & MMARK))
1101 continue;
1102 } else if (!visible(mp))
1103 continue;
1104 _print_head(printed++, bottom, stdout, FAL0);
1106 NYD_LEAVE;
1109 FL int
1110 c_pdot(void *v)
1112 NYD_ENTER;
1113 UNUSED(v);
1114 printf("%d\n", (int)PTR2SIZE(dot - message + 1));
1115 NYD_LEAVE;
1116 return 0;
1119 FL int
1120 c_more(void *v)
1122 int *msgvec = v, rv;
1123 NYD_ENTER;
1125 rv = _type1(msgvec, TRU1, TRU1, FAL0, FAL0, NULL, NULL);
1126 NYD_LEAVE;
1127 return rv;
1130 FL int
1131 c_More(void *v)
1133 int *msgvec = v, rv;
1134 NYD_ENTER;
1136 rv = _type1(msgvec, FAL0, TRU1, FAL0, FAL0, NULL, NULL);
1137 NYD_LEAVE;
1138 return rv;
1141 FL int
1142 c_type(void *v)
1144 int *msgvec = v, rv;
1145 NYD_ENTER;
1147 rv = _type1(msgvec, TRU1, FAL0, FAL0, FAL0, NULL, NULL);
1148 NYD_LEAVE;
1149 return rv;
1152 FL int
1153 c_Type(void *v)
1155 int *msgvec = v, rv;
1156 NYD_ENTER;
1158 rv = _type1(msgvec, FAL0, FAL0, FAL0, FAL0, NULL, NULL);
1159 NYD_LEAVE;
1160 return rv;
1163 FL int
1164 c_show(void *v)
1166 int *msgvec = v, rv;
1167 NYD_ENTER;
1169 rv = _type1(msgvec, FAL0, FAL0, FAL0, TRU1, NULL, NULL);
1170 NYD_LEAVE;
1171 return rv;
1174 FL int
1175 c_pipe(void *v)
1177 char *str = v;
1178 int rv;
1179 NYD_ENTER;
1181 rv = _pipe1(str, 1);
1182 NYD_LEAVE;
1183 return rv;
1186 FL int
1187 c_Pipe(void *v)
1189 char *str = v;
1190 int rv;
1191 NYD_ENTER;
1193 rv = _pipe1(str, 0);
1194 NYD_LEAVE;
1195 return rv;
1198 FL int
1199 c_top(void *v)
1201 int *msgvec = v, *ip, c, topl, lines, empty_last;
1202 struct message *mp;
1203 char *cp, *linebuf = NULL;
1204 size_t linesize = 0;
1205 FILE *ibuf;
1206 NYD_ENTER;
1208 topl = 5;
1209 cp = ok_vlook(toplines);
1210 if (cp != NULL) {
1211 topl = atoi(cp);
1212 if (topl < 0 || topl > 10000)
1213 topl = 5;
1216 #ifdef HAVE_COLOUR
1217 if (options & OPT_TTYOUT)
1218 colour_table_create(NULL); /* (salloc()s) */
1219 #endif
1220 empty_last = 1;
1221 for (ip = msgvec; *ip != 0 && UICMP(z, PTR2SIZE(ip - msgvec), <, msgCount);
1222 ++ip) {
1223 mp = message + *ip - 1;
1224 touch(mp);
1225 setdot(mp);
1226 did_print_dot = TRU1;
1227 if (!empty_last)
1228 printf("\n");
1229 _show_msg_overview(stdout, mp, *ip);
1230 if (mp->m_flag & MNOFROM)
1231 /* XXX c_top(): coloured output? */
1232 printf("From %s %s\n", fakefrom(mp), fakedate(mp->m_time));
1233 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL) { /* XXX could use TOP */
1234 v = NULL;
1235 break;
1237 c = mp->m_lines;
1238 for (lines = 0; lines < c && UICMP(32, lines, <=, topl); ++lines) {
1239 if (readline_restart(ibuf, &linebuf, &linesize, 0) < 0)
1240 break;
1241 puts(linebuf);
1243 for (cp = linebuf; *cp != '\0' && blankchar(*cp); ++cp)
1245 empty_last = (*cp == '\0');
1249 if (linebuf != NULL)
1250 free(linebuf);
1251 NYD_LEAVE;
1252 return (v != NULL);
1255 FL int
1256 c_stouch(void *v)
1258 int *msgvec = v, *ip;
1259 NYD_ENTER;
1261 for (ip = msgvec; *ip != 0; ++ip) {
1262 setdot(message + *ip - 1);
1263 dot->m_flag |= MTOUCH;
1264 dot->m_flag &= ~MPRESERVE;
1265 did_print_dot = TRU1;
1267 NYD_LEAVE;
1268 return 0;
1271 FL int
1272 c_mboxit(void *v)
1274 int *msgvec = v, *ip;
1275 NYD_ENTER;
1277 for (ip = msgvec; *ip != 0; ++ip) {
1278 setdot(message + *ip - 1);
1279 dot->m_flag |= MTOUCH | MBOX;
1280 dot->m_flag &= ~MPRESERVE;
1281 did_print_dot = TRU1;
1283 NYD_LEAVE;
1284 return 0;
1287 FL int
1288 c_folders(void *v)
1290 char dirname[PATH_MAX], *name, **argv = v;
1291 char const *cmd;
1292 int rv = 1;
1293 NYD_ENTER;
1295 if (*argv) {
1296 name = expand(*argv);
1297 if (name == NULL)
1298 goto jleave;
1299 } else if (!getfold(dirname, sizeof dirname)) {
1300 fprintf(stderr, tr(20, "No value set for \"folder\"\n"));
1301 goto jleave;
1302 } else
1303 name = dirname;
1305 if (which_protocol(name) == PROTO_IMAP) {
1306 #ifdef HAVE_IMAP
1307 imap_folders(name, *argv == NULL);
1308 #else
1309 rv = c_cmdnotsupp(NULL);
1310 #endif
1311 } else {
1312 if ((cmd = ok_vlook(LISTER)) == NULL)
1313 cmd = XLISTER;
1314 run_command(cmd, 0, -1, -1, name, NULL, NULL);
1316 jleave:
1317 NYD_LEAVE;
1318 return rv;
1321 /* vim:set fenc=utf-8:s-it-mode */