1 /*
2  * Heirloom mailx - a mail user agent derived from Berkeley Mail.
3  *
4  * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5  */
6 /*-
7  * Copyright (c) 1980, 1993
8  *	The Regents of the University of California.  All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  */
38 
39 #ifndef lint
40 #ifdef	DOSCCS
41 static char sccsid[] = "@(#)cmd1.c	2.97 (gritter) 6/16/07";
42 #endif
43 #endif /* not lint */
44 
45 #include "rcv.h"
46 #include "extern.h"
47 #ifdef	HAVE_WCWIDTH
48 #include <wchar.h>
49 #endif
50 
51 /*
52  * Mail -- a mail program
53  *
54  * User commands.
55  */
56 
57 /*
58  * Print the current active headings.
59  * Don't change dot if invoker didn't give an argument.
60  */
61 
62 static int screen;
63 static void onpipe(int signo);
64 static int dispc(struct message *mp, const char *a);
65 static int scroll1(char *arg, int onlynew);
66 static void hprf(const char *fmt, int mesg, FILE *f, int threaded,
67 		const char *attrlist);
68 static int putindent(FILE *fp, struct message *mp, int maxwidth);
69 static int type1(int *msgvec, int doign, int page, int pipe, int decode,
70 		char *cmd, off_t *tstats);
71 static int pipe1(char *str, int doign);
72 void brokpipe(int signo);
73 
74 char *
get_pager(void)75 get_pager(void)
76 {
77 	char *cp;
78 
79 	cp = value("PAGER");
80 	if (cp == NULL || *cp == '\0')
81 		cp = value("bsdcompat") ? "more" : "pg";
82 	return cp;
83 }
84 
85 int
headers(void * v)86 headers(void *v)
87 {
88 	int *msgvec = v;
89 	int g, k, n, mesg, flag = 0, lastg = 1;
90 	struct message *mp, *mq, *lastmq = NULL;
91 	int size;
92 	enum mflag	fl = MNEW|MFLAGGED;
93 
94 	size = screensize();
95 	n = msgvec[0];	/* n == {-2, -1, 0}: called from scroll() */
96 	if (screen < 0)
97 		screen = 0;
98 	k = screen * size;
99 	if (k >= msgCount)
100 		k = msgCount - size;
101 	if (k < 0)
102 		k = 0;
103 	if (mb.mb_threaded == 0) {
104 		g = 0;
105 		mq = &message[0];
106 		for (mp = &message[0]; mp < &message[msgCount]; mp++)
107 			if (visible(mp)) {
108 				if (g % size == 0)
109 					mq = mp;
110 				if (mp->m_flag&fl) {
111 					lastg = g;
112 					lastmq = mq;
113 				}
114 				if (n>0 && mp==&message[n-1] ||
115 						n==0 && g==k ||
116 						n==-2 && g==k+size && lastmq ||
117 						n<0 && g>=k && mp->m_flag&fl)
118 					break;
119 				g++;
120 			}
121 		if (lastmq && (n==-2 || n==-1 && mp==&message[msgCount])) {
122 			g = lastg;
123 			mq = lastmq;
124 		}
125 		screen = g / size;
126 		mp = mq;
127 		mesg = mp - &message[0];
128 		if (dot != &message[n-1]) {
129 			for (mq = mp; mq < &message[msgCount]; mq++)
130 				if (visible(mq)) {
131 					setdot(mq);
132 					break;
133 				}
134 		}
135 		if (mb.mb_type == MB_IMAP)
136 			imap_getheaders(mesg+1, mesg + size);
137 		for (; mp < &message[msgCount]; mp++) {
138 			mesg++;
139 			if (!visible(mp))
140 				continue;
141 			if (flag++ >= size)
142 				break;
143 			printhead(mesg, stdout, 0);
144 		}
145 	} else {	/* threaded */
146 		g = 0;
147 		mq = threadroot;
148 		for (mp = threadroot; mp; mp = next_in_thread(mp))
149 			if (visible(mp) && (mp->m_collapsed <= 0 ||
150 					 mp == &message[n-1])) {
151 				if (g % size == 0)
152 					mq = mp;
153 				if (mp->m_flag&fl) {
154 					lastg = g;
155 					lastmq = mq;
156 				}
157 				if (n>0 && mp==&message[n-1] ||
158 						n==0 && g==k ||
159 						n==-2 && g==k+size && lastmq ||
160 						n<0 && g>=k && mp->m_flag&fl)
161 					break;
162 				g++;
163 			}
164 		if (lastmq && (n==-2 || n==-1 && mp==&message[msgCount])) {
165 			g = lastg;
166 			mq = lastmq;
167 		}
168 		screen = g / size;
169 		mp = mq;
170 		if (dot != &message[n-1]) {
171 			for (mq = mp; mq; mq = next_in_thread(mq))
172 				if (visible(mq) && mq->m_collapsed <= 0) {
173 					setdot(mq);
174 					break;
175 				}
176 		}
177 		while (mp) {
178 			if (visible(mp) && (mp->m_collapsed <= 0 ||
179 					 mp == &message[n-1])) {
180 				if (flag++ >= size)
181 					break;
182 				printhead(mp - &message[0] + 1, stdout,
183 						mb.mb_threaded);
184 			}
185 			mp = next_in_thread(mp);
186 		}
187 	}
188 	if (flag == 0) {
189 		printf(catgets(catd, CATSET, 6, "No more mail.\n"));
190 		return(1);
191 	}
192 	return(0);
193 }
194 
195 /*
196  * Scroll to the next/previous screen
197  */
198 int
scroll(void * v)199 scroll(void *v)
200 {
201 	return scroll1(v, 0);
202 }
203 
204 int
Scroll(void * v)205 Scroll(void *v)
206 {
207 	return scroll1(v, 1);
208 }
209 
210 static int
scroll1(char * arg,int onlynew)211 scroll1(char *arg, int onlynew)
212 {
213 	int size;
214 	int cur[1];
215 
216 	cur[0] = onlynew ? -1 : 0;
217 	size = screensize();
218 	switch (*arg) {
219 	case '1': case '2': case '3': case '4': case '5':
220 	case '6': case '7': case '8': case '9': case '0':
221 		screen = atoi(arg);
222 		goto scroll_forward;
223 	case '\0':
224 		screen++;
225 		goto scroll_forward;
226 	case '$':
227 		screen = msgCount / size;
228 		goto scroll_forward;
229 	case '+':
230 		if (arg[1] == '\0')
231 			screen++;
232 		else
233 			screen += atoi(arg + 1);
234 scroll_forward:
235 		if (screen * size > msgCount) {
236 			screen = msgCount / size;
237 			printf(catgets(catd, CATSET, 7,
238 					"On last screenful of messages\n"));
239 		}
240 		break;
241 
242 	case '-':
243 		if (arg[1] == '\0')
244 			screen--;
245 		else
246 			screen -= atoi(arg + 1);
247 		if (screen < 0) {
248 			screen = 0;
249 			printf(catgets(catd, CATSET, 8,
250 					"On first screenful of messages\n"));
251 		}
252 		if (cur[0] == -1)
253 			cur[0] = -2;
254 		break;
255 
256 	default:
257 		printf(catgets(catd, CATSET, 9,
258 			"Unrecognized scrolling command \"%s\"\n"), arg);
259 		return(1);
260 	}
261 	return(headers(cur));
262 }
263 
264 /*
265  * Compute screen size.
266  */
267 int
screensize(void)268 screensize(void)
269 {
270 	int s;
271 	char *cp;
272 
273 	if ((cp = value("screen")) != NULL && (s = atoi(cp)) > 0)
274 		return s;
275 	return scrnheight - 4;
276 }
277 
278 static sigjmp_buf	pipejmp;
279 
280 /*ARGSUSED*/
281 static void
onpipe(int signo)282 onpipe(int signo)
283 {
284 	siglongjmp(pipejmp, 1);
285 }
286 
287 /*
288  * Print out the headlines for each message
289  * in the passed message list.
290  */
291 int
from(void * v)292 from(void *v)
293 {
294 	int *msgvec = v;
295 	int *ip, n;
296 	FILE *obuf = stdout;
297 	char *cp;
298 
299 	(void)&obuf;
300 	(void)&cp;
301 	if (is_a_tty[0] && is_a_tty[1] && (cp = value("crt")) != NULL) {
302 		for (n = 0, ip = msgvec; *ip; ip++)
303 			n++;
304 		if (n > (*cp == '\0' ? screensize() : atoi(cp)) + 3) {
305 			cp = get_pager();
306 			if (sigsetjmp(pipejmp, 1))
307 				goto endpipe;
308 			if ((obuf = Popen(cp, "w", NULL, 1)) == NULL) {
309 				perror(cp);
310 				obuf = stdout;
311 			} else
312 				safe_signal(SIGPIPE, onpipe);
313 		}
314 	}
315 	for (ip = msgvec; *ip != 0; ip++)
316 		printhead(*ip, obuf, mb.mb_threaded);
317 	if (--ip >= msgvec)
318 		setdot(&message[*ip - 1]);
319 endpipe:
320 	if (obuf != stdout) {
321 		safe_signal(SIGPIPE, SIG_IGN);
322 		Pclose(obuf);
323 		safe_signal(SIGPIPE, dflpipe);
324 	}
325 	return(0);
326 }
327 
328 static int
dispc(struct message * mp,const char * a)329 dispc(struct message *mp, const char *a)
330 {
331 	int	dispc = ' ';
332 
333 	/*
334 	 * Bletch!
335 	 */
336 	if ((mp->m_flag & (MREAD|MNEW)) == MREAD)
337 		dispc = a[3];
338 	if ((mp->m_flag & (MREAD|MNEW)) == (MREAD|MNEW))
339 		dispc = a[2];
340 	if (mp->m_flag & MANSWERED)
341 		dispc = a[8];
342 	if (mp->m_flag & MDRAFTED)
343 		dispc = a[9];
344 	if ((mp->m_flag & (MREAD|MNEW)) == MNEW)
345 		dispc = a[0];
346 	if ((mp->m_flag & (MREAD|MNEW)) == 0)
347 		dispc = a[1];
348 	if (mp->m_flag & MJUNK)
349 		dispc = a[13];
350 	if (mp->m_flag & MSAVED)
351 		dispc = a[4];
352 	if (mp->m_flag & MPRESERVE)
353 		dispc = a[5];
354 	if (mp->m_flag & (MBOX|MBOXED))
355 		dispc = a[6];
356 	if (mp->m_flag & MFLAGGED)
357 		dispc = a[7];
358 	if (mp->m_flag & MKILL)
359 		dispc = a[10];
360 	if (mb.mb_threaded == 1 && mp->m_collapsed > 0)
361 		dispc = a[12];
362 	if (mb.mb_threaded == 1 && mp->m_collapsed < 0)
363 		dispc = a[11];
364 	return dispc;
365 }
366 
367 static void
hprf(const char * fmt,int mesg,FILE * f,int threaded,const char * attrlist)368 hprf(const char *fmt, int mesg, FILE *f, int threaded, const char *attrlist)
369 {
370 	struct message	*mp = &message[mesg-1];
371 	char	*headline = NULL, *subjline, *name, *cp, *pbuf = NULL;
372 	struct headline	hl;
373 	size_t	headsize = 0;
374 	const char	*fp;
375 	int	B, c, i, n, s;
376 	int	headlen = 0;
377 	struct str	in, out;
378 	int	subjlen = scrnwidth, fromlen, isto = 0, isaddr = 0;
379 	FILE	*ibuf;
380 
381 	if ((mp->m_flag & MNOFROM) == 0) {
382 		if ((ibuf = setinput(&mb, mp, NEED_HEADER)) == NULL)
383 			return;
384 		if ((headlen = readline(ibuf, &headline, &headsize)) < 0)
385 			return;
386 	}
387 	if ((subjline = hfield("subject", mp)) == NULL)
388 		subjline = hfield("subj", mp);
389 	if (subjline == NULL) {
390 		out.s = NULL;
391 		out.l = 0;
392 	} else {
393 		in.s = subjline;
394 		in.l = strlen(subjline);
395 		mime_fromhdr(&in, &out, TD_ICONV | TD_ISPR);
396 		subjline = out.s;
397 	}
398 	if ((mp->m_flag & MNOFROM) == 0) {
399 		pbuf = ac_alloc(headlen + 1);
400 		parse(headline, headlen, &hl, pbuf);
401 	} else {
402 		hl.l_from = /*fakefrom(mp);*/NULL;
403 		hl.l_tty = NULL;
404 		hl.l_date = fakedate(mp->m_time);
405 	}
406 	if (value("datefield") && (cp = hfield("date", mp)) != NULL)
407 		hl.l_date = fakedate(rfctime(cp));
408 	if (Iflag) {
409 		if ((name = hfield("newsgroups", mp)) == NULL)
410 			if ((name = hfield("article-id", mp)) == NULL)
411 				name = "<>";
412 		name = prstr(name);
413 	} else if (value("show-rcpt") == NULL) {
414 		name = name1(mp, 0);
415 		isaddr = 1;
416 		if (value("showto") && name && is_myname(skin(name))) {
417 			if ((cp = hfield("to", mp)) != NULL) {
418 				name = cp;
419 				isto = 1;
420 			}
421 		}
422 	} else {
423 		isaddr = 1;
424 		if ((name = hfield("to", mp)) != NULL)
425 			isto = 1;
426 	}
427 	if (name == NULL) {
428 		name = "";
429 		isaddr = 0;
430 	}
431 	if (isaddr) {
432 		if (value("showname"))
433 			name = realname(name);
434 		else {
435 			name = prstr(skin(name));
436 		}
437 	}
438 	for (fp = fmt; *fp; fp++) {
439 		if (*fp == '%') {
440 			if (*++fp == '-') {
441 				fp++;
442 			} else if (*fp == '+')
443 				fp++;
444 			while (digitchar(*fp&0377))
445 				fp++;
446 			if (*fp == '\0')
447 				break;
448 		} else {
449 #if defined (HAVE_MBTOWC) && defined (HAVE_WCWIDTH)
450 			if (mb_cur_max > 1) {
451 				wchar_t	wc;
452 				if ((s = mbtowc(&wc, fp, mb_cur_max)) < 0)
453 					n = s = 1;
454 				else {
455 					if ((n = wcwidth(wc)) < 0)
456 						n = 1;
457 				}
458 			} else
459 #endif  /* HAVE_MBTOWC && HAVE_WCWIDTH */
460 			{
461 				n = s = 1;
462 			}
463 			subjlen -= n;
464 			while (--s > 0)
465 				fp++;
466 		}
467 	}
468 	for (fp = fmt; *fp; fp++) {
469 		if (*fp == '%') {
470 			B = 0;
471 			n = 0;
472 			s = 1;
473 			if (*++fp == '-') {
474 				s = -1;
475 				fp++;
476 			} else if (*fp == '+')
477 				fp++;
478 			if (digitchar(*fp&0377)) {
479 				do
480 					n = 10*n + *fp - '0';
481 				while (fp++, digitchar(*fp&0377));
482 			}
483 			if (*fp == '\0')
484 				break;
485 			n *= s;
486 			switch (*fp) {
487 			case '%':
488 				putc('%', f);
489 				subjlen--;
490 				break;
491 			case '>':
492 			case '<':
493 				c = dot == mp ? *fp&0377 : ' ';
494 				putc(c, f);
495 				subjlen--;
496 				break;
497 			case 'a':
498 				c = dispc(mp, attrlist);
499 				putc(c, f);
500 				subjlen--;
501 				break;
502 			case 'm':
503 				if (n == 0) {
504 					n = 3;
505 					if (threaded)
506 						for (i=msgCount; i>999; i/=10)
507 							n++;
508 				}
509 				subjlen -= fprintf(f, "%*d", n, mesg);
510 				break;
511 			case 'f':
512 				if (n <= 0)
513 					n = 18;
514 				fromlen = n;
515 				if (isto)
516 					fromlen -= 3;
517 				fprintf(f, "%s%s", isto ? "To " : "",
518 						colalign(name, fromlen, 1));
519 				subjlen -= n;
520 				break;
521 			case 'd':
522 				if (n <= 0)
523 					n = 16;
524 				subjlen -= fprintf(f, "%*.*s", n, n, hl.l_date);
525 				break;
526 			case 'l':
527 				if (n == 0)
528 					n = 4;
529 				if (mp->m_xlines)
530 					subjlen -= fprintf(f, "%*ld", n,
531 							mp->m_xlines);
532 				else {
533 					subjlen -= n;
534 					while (n--)
535 						putc(' ', f);
536 				}
537 				break;
538 			case 'o':
539 				if (n == 0)
540 					n = -5;
541 				subjlen -= fprintf(f, "%*lu", n,
542 						(long)mp->m_xsize);
543 				break;
544 			case 'i':
545 				if (threaded)
546 					subjlen -= putindent(f, mp,
547 							scrnwidth - 60);
548 				break;
549 			case 'S':
550 				B = 1;
551 				/*FALLTHRU*/
552 			case 's':
553 				n = n>0 ? n : subjlen - 2;
554 				if (B)
555 					n -= 2;
556 				if (subjline != NULL && n >= 0) {
557 					/* pretty pathetic */
558 					fprintf(f, B ? "\"%s\"" : "%s",
559 						colalign(subjline, n, 0));
560 				}
561 				break;
562 			case 'U':
563 				if (n == 0)
564 					n = 9;
565 				subjlen -= fprintf(f, "%*lu", n, mp->m_uid);
566 				break;
567 			case 'e':
568 				if (n == 0)
569 					n = 2;
570 				subjlen -= fprintf(f, "%*u", n, threaded == 1 ?
571 						mp->m_level : 0);
572 				break;
573 			case 't':
574 				if (n == 0) {
575 					n = 3;
576 					if (threaded)
577 						for (i=msgCount; i>999; i/=10)
578 							n++;
579 				}
580 				fprintf(f, "%*ld", n, threaded ?
581 						mp->m_threadpos : mesg);
582 				subjlen -= n;
583 				break;
584 			case 'c':
585 				if (n == 0)
586 					n = 6;
587 				subjlen -= fprintf(f, "%*g", n, mp->m_score);
588 				break;
589 			}
590 		} else
591 			putc(*fp&0377, f);
592 	}
593 	putc('\n', f);
594 	if (out.s)
595 		free(out.s);
596 	if (headline)
597 		free(headline);
598 	if (pbuf)
599 		ac_free(pbuf);
600 }
601 
602 /*
603  * Print out the indenting in threaded display.
604  */
605 static int
putindent(FILE * fp,struct message * mp,int maxwidth)606 putindent(FILE *fp, struct message *mp, int maxwidth)
607 {
608 	struct message	*mq;
609 	int	indent, i;
610 	int	*us;
611 	char	*cs;
612 	int	important = MNEW|MFLAGGED;
613 
614 	if (mp->m_level == 0)
615 		return 0;
616 	cs = ac_alloc(mp->m_level);
617 	us = ac_alloc(mp->m_level * sizeof *us);
618 	i = mp->m_level - 1;
619 	if (mp->m_younger && mp->m_younger->m_level == i + 1) {
620 		if (mp->m_parent && mp->m_parent->m_flag & important)
621 			us[i] = mp->m_flag & important ? 0x2523 : 0x2520;
622 		else
623 			us[i] = mp->m_flag & important ? 0x251D : 0x251C;
624 		cs[i] = '+';
625 	} else {
626 		if (mp->m_parent && mp->m_parent->m_flag & important)
627 			us[i] = mp->m_flag & important ? 0x2517 : 0x2516;
628 		else
629 			us[i] = mp->m_flag & important ? 0x2515 : 0x2514;
630 		cs[i] = '\\';
631 	}
632 	mq = mp->m_parent;
633 	for (i = mp->m_level - 2; i >= 0; i--) {
634 		if (mq) {
635 			if (i > mq->m_level - 1) {
636 				us[i] = cs[i] = ' ';
637 				continue;
638 			}
639 			if (mq->m_younger) {
640 				if (mq->m_parent &&
641 						mq->m_parent->m_flag&important)
642 					us[i] = 0x2503;
643 				else
644 					us[i] = 0x2502;
645 				cs[i] = '|';
646 			} else
647 				us[i] = cs[i] = ' ';
648 			mq = mq->m_parent;
649 		} else
650 			us[i] = cs[i] = ' ';
651 	}
652 	for (indent = 0; indent < mp->m_level && indent < maxwidth; indent++) {
653 		if (indent < maxwidth - 1)
654 			putuc(us[indent], cs[indent] & 0377, fp);
655 		else
656 			putuc(0x21B8, '^', fp);
657 	}
658 	ac_free(us);
659 	ac_free(cs);
660 	return indent;
661 }
662 
663 /*
664  * Print out the header of a specific message.
665  * This is a slight improvement to the standard one.
666  */
667 void
printhead(int mesg,FILE * f,int threaded)668 printhead(int mesg, FILE *f, int threaded)
669 {
670 	int bsdflags, bsdheadline, sz;
671 	char	*fmt, attrlist[30], *cp;
672 
673 	bsdflags = value("bsdcompat") != NULL || value("bsdflags") != NULL ||
674 		getenv("SYSV3") != NULL;
675 	strcpy(attrlist, bsdflags ? "NU  *HMFATK+-J" : "NUROSPMFATK+-J");
676 	if ((cp = value("attrlist")) != NULL) {
677 		sz = strlen(cp);
678 		if (sz > sizeof attrlist - 1)
679 			sz = sizeof attrlist - 1;
680 		memcpy(attrlist, cp, sz);
681 	}
682 	bsdheadline = value("bsdcompat") != NULL ||
683 		value("bsdheadline") != NULL;
684 	if ((fmt = value("headline")) == NULL)
685 		fmt = bsdheadline ?
686 			"%>%a%m %20f  %16d %3l/%-5o %i%S" :
687 			"%>%a%m %18f %16d %4l/%-5o %i%s";
688 	hprf(fmt, mesg, f, threaded, attrlist);
689 }
690 
691 /*
692  * Print out the value of dot.
693  */
694 /*ARGSUSED*/
695 int
pdot(void * v)696 pdot(void *v)
697 {
698 	printf(catgets(catd, CATSET, 13, "%d\n"),
699 			(int)(dot - &message[0] + 1));
700 	return(0);
701 }
702 
703 /*
704  * Print out all the possible commands.
705  */
706 /*ARGSUSED*/
707 int
pcmdlist(void * v)708 pcmdlist(void *v)
709 {
710 	extern const struct cmd cmdtab[];
711 	const struct cmd *cp;
712 	int cc;
713 
714 	printf(catgets(catd, CATSET, 14, "Commands are:\n"));
715 	for (cc = 0, cp = cmdtab; cp->c_name != NULL; cp++) {
716 		cc += strlen(cp->c_name) + 2;
717 		if (cc > 72) {
718 			printf("\n");
719 			cc = strlen(cp->c_name) + 2;
720 		}
721 		if ((cp+1)->c_name != NULL)
722 			printf(catgets(catd, CATSET, 15, "%s, "), cp->c_name);
723 		else
724 			printf("%s\n", cp->c_name);
725 	}
726 	return(0);
727 }
728 
729 /*
730  * Type out the messages requested.
731  */
732 static sigjmp_buf	pipestop;
733 
734 static int
type1(int * msgvec,int doign,int page,int pipe,int decode,char * cmd,off_t * tstats)735 type1(int *msgvec, int doign, int page, int pipe, int decode,
736 		char *cmd, off_t *tstats)
737 {
738 	int *ip;
739 	struct message *mp;
740 	char *cp;
741 	int nlines;
742 	off_t mstats[2];
743 	/*
744 	 * Must be static to become excluded from sigsetjmp().
745 	 */
746 	static FILE *obuf;
747 #ifdef __GNUC__
748 	/* Avoid longjmp clobbering */
749 	(void) &cp;
750 	(void) &cmd;
751 	(void) &obuf;
752 #endif
753 
754 	obuf = stdout;
755 	if (sigsetjmp(pipestop, 1))
756 		goto close_pipe;
757 	if (pipe) {
758 		cp = value("SHELL");
759 		if (cp == NULL)
760 			cp = SHELL;
761 		obuf = Popen(cmd, "w", cp, 1);
762 		if (obuf == NULL) {
763 			perror(cmd);
764 			obuf = stdout;
765 		} else {
766 			safe_signal(SIGPIPE, brokpipe);
767 		}
768 	} else if (value("interactive") != NULL &&
769 	    (page || (cp = value("crt")) != NULL)) {
770 		nlines = 0;
771 		if (!page) {
772 			for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
773 				if ((message[*ip-1].m_have & HAVE_BODY) == 0) {
774 					if ((get_body(&message[*ip - 1])) !=
775 							OKAY)
776 						return 1;
777 				}
778 				nlines += message[*ip - 1].m_lines;
779 			}
780 		}
781 		if (page || nlines > (*cp ? atoi(cp) : realscreenheight)) {
782 			cp = get_pager();
783 			obuf = Popen(cp, "w", NULL, 1);
784 			if (obuf == NULL) {
785 				perror(cp);
786 				obuf = stdout;
787 			} else
788 				safe_signal(SIGPIPE, brokpipe);
789 		}
790 	}
791 	for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) {
792 		mp = &message[*ip - 1];
793 		touch(mp);
794 		setdot(mp);
795 		uncollapse1(mp, 1);
796 		if (value("quiet") == NULL)
797 			fprintf(obuf, catgets(catd, CATSET, 17,
798 				"Message %2d:\n"), *ip);
799 		send(mp, obuf, doign ? ignore : 0, NULL,
800 			pipe && value("piperaw") ? SEND_MBOX :
801 				decode ? SEND_SHOW :
802 				doign ? SEND_TODISP : SEND_TODISP_ALL,
803 			mstats);
804 		if (pipe && value("page")) {
805 			putc('\f', obuf);
806 		}
807 		if (tstats) {
808 			tstats[0] += mstats[0];
809 			tstats[1] += mstats[1];
810 		}
811 	}
812 close_pipe:
813 	if (obuf != stdout) {
814 		/*
815 		 * Ignore SIGPIPE so it can't cause a duplicate close.
816 		 */
817 		safe_signal(SIGPIPE, SIG_IGN);
818 		Pclose(obuf);
819 		safe_signal(SIGPIPE, dflpipe);
820 	}
821 	return(0);
822 }
823 
824 /*
825  * Get the last, possibly quoted part of linebuf.
826  */
827 char *
laststring(char * linebuf,int * flag,int strip)828 laststring(char *linebuf, int *flag, int strip)
829 {
830 	char *cp, *p;
831 	char quoted;
832 
833 	*flag = 1;
834 	cp = strlen(linebuf) + linebuf - 1;
835 
836 	/*
837 	 * Strip away trailing blanks.
838 	 */
839 	while (cp > linebuf && whitechar(*cp & 0377))
840 		cp--;
841 	*++cp = 0;
842 	if (cp == linebuf) {
843 		*flag = 0;
844 		return NULL;
845 	}
846 
847 	/*
848 	 * Now search for the beginning of the command name.
849 	 */
850 	quoted = *(cp - 1);
851 	if (quoted == '\'' || quoted == '\"') {
852 		cp--;
853 		if (strip)
854 			*cp = '\0';
855 		cp--;
856 		while (cp > linebuf) {
857 			if (*cp != quoted) {
858 				cp--;
859 			} else if (*(cp - 1) != '\\') {
860 				break;
861 			} else {
862 				p = --cp;
863 				do {
864 					*p = *(p + 1);
865 				} while (*p++);
866 				cp--;
867 			}
868 		}
869 		if (cp == linebuf)
870 			*flag = 0;
871 		if (*cp == quoted) {
872 			if (strip)
873 				*cp++ = 0;
874 		} else
875 			*flag = 0;
876 	} else {
877 		while (cp > linebuf && !whitechar(*cp & 0377))
878 			cp--;
879 		if (whitechar(*cp & 0377))
880 			*cp++ = 0;
881 		else
882 			*flag = 0;
883 	}
884 	if (*cp == '\0') {
885 		return(NULL);
886 	}
887 	return(cp);
888 }
889 
890 /*
891  * Pipe the messages requested.
892  */
893 static int
pipe1(char * str,int doign)894 pipe1(char *str, int doign)
895 {
896 	char *cmd;
897 	int f, *msgvec, ret;
898 	off_t stats[2];
899 
900 	/*LINTED*/
901 	msgvec = (int *)salloc((msgCount + 2) * sizeof *msgvec);
902 	if ((cmd = laststring(str, &f, 1)) == NULL) {
903 		cmd = value("cmd");
904 		if (cmd == NULL || *cmd == '\0') {
905 			fputs(catgets(catd, CATSET, 16,
906 				"variable cmd not set\n"), stderr);
907 			return 1;
908 		}
909 	}
910 	if (!f) {
911 		*msgvec = first(0, MMNORM);
912 		if (*msgvec == 0) {
913 			if (inhook)
914 				return 0;
915 			puts(catgets(catd, CATSET, 18, "No messages to pipe."));
916 			return 1;
917 		}
918 		msgvec[1] = 0;
919 	} else if (getmsglist(str, msgvec, 0) < 0)
920 		return 1;
921 	if (*msgvec == 0) {
922 		if (inhook)
923 			return 0;
924 		printf("No applicable messages.\n");
925 		return 1;
926 	}
927 	printf(catgets(catd, CATSET, 268, "Pipe to: \"%s\"\n"), cmd);
928 	stats[0] = stats[1] = 0;
929 	if ((ret = type1(msgvec, doign, 0, 1, 0, cmd, stats)) == 0) {
930 		printf("\"%s\" ", cmd);
931 		if (stats[0] >= 0)
932 			printf("%lu", (long)stats[0]);
933 		else
934 			printf(catgets(catd, CATSET, 27, "binary"));
935 		printf("/%lu\n", (long)stats[1]);
936 	}
937 	return ret;
938 }
939 
940 /*
941  * Paginate messages, honor ignored fields.
942  */
943 int
more(void * v)944 more(void *v)
945 {
946 	int *msgvec = v;
947 	return (type1(msgvec, 1, 1, 0, 0, NULL, NULL));
948 }
949 
950 /*
951  * Paginate messages, even printing ignored fields.
952  */
953 int
More(void * v)954 More(void *v)
955 {
956 	int *msgvec = v;
957 
958 	return (type1(msgvec, 0, 1, 0, 0, NULL, NULL));
959 }
960 
961 /*
962  * Type out messages, honor ignored fields.
963  */
964 int
type(void * v)965 type(void *v)
966 {
967 	int *msgvec = v;
968 
969 	return(type1(msgvec, 1, 0, 0, 0, NULL, NULL));
970 }
971 
972 /*
973  * Type out messages, even printing ignored fields.
974  */
975 int
Type(void * v)976 Type(void *v)
977 {
978 	int *msgvec = v;
979 
980 	return(type1(msgvec, 0, 0, 0, 0, NULL, NULL));
981 }
982 
983 /*
984  * Show MIME-encoded message text, including all fields.
985  */
986 int
show(void * v)987 show(void *v)
988 {
989 	int *msgvec = v;
990 
991 	return(type1(msgvec, 0, 0, 0, 1, NULL, NULL));
992 }
993 
994 /*
995  * Pipe messages, honor ignored fields.
996  */
997 int
pipecmd(void * v)998 pipecmd(void *v)
999 {
1000 	char *str = v;
1001 	return(pipe1(str, 1));
1002 }
1003 /*
1004  * Pipe messages, not respecting ignored fields.
1005  */
1006 int
Pipecmd(void * v)1007 Pipecmd(void *v)
1008 {
1009 	char *str = v;
1010 	return(pipe1(str, 0));
1011 }
1012 
1013 /*
1014  * Respond to a broken pipe signal --
1015  * probably caused by quitting more.
1016  */
1017 /*ARGSUSED*/
1018 void
brokpipe(int signo)1019 brokpipe(int signo)
1020 {
1021 	siglongjmp(pipestop, 1);
1022 }
1023 
1024 /*
1025  * Print the top so many lines of each desired message.
1026  * The number of lines is taken from the variable "toplines"
1027  * and defaults to 5.
1028  */
1029 int
top(void * v)1030 top(void *v)
1031 {
1032 	int *msgvec = v;
1033 	int *ip;
1034 	struct message *mp;
1035 	int c, topl, lines, lineb;
1036 	char *valtop, *linebuf = NULL;
1037 	size_t linesize;
1038 	FILE *ibuf;
1039 
1040 	topl = 5;
1041 	valtop = value("toplines");
1042 	if (valtop != NULL) {
1043 		topl = atoi(valtop);
1044 		if (topl < 0 || topl > 10000)
1045 			topl = 5;
1046 	}
1047 	lineb = 1;
1048 	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
1049 		mp = &message[*ip - 1];
1050 		touch(mp);
1051 		setdot(mp);
1052 		did_print_dot = 1;
1053 		if (value("quiet") == NULL)
1054 			printf(catgets(catd, CATSET, 19,
1055 					"Message %2d:\n"), *ip);
1056 		if (mp->m_flag & MNOFROM)
1057 			printf("From %s %s\n", fakefrom(mp),
1058 					fakedate(mp->m_time));
1059 		if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL)	/* XXX could use TOP */
1060 			return 1;
1061 		c = mp->m_lines;
1062 		if (!lineb)
1063 			printf("\n");
1064 		for (lines = 0; lines < c && lines <= topl; lines++) {
1065 			if (readline(ibuf, &linebuf, &linesize) < 0)
1066 				break;
1067 			puts(linebuf);
1068 			lineb = blankline(linebuf);
1069 		}
1070 	}
1071 	if (linebuf)
1072 		free(linebuf);
1073 	return(0);
1074 }
1075 
1076 /*
1077  * Touch all the given messages so that they will
1078  * get mboxed.
1079  */
1080 int
stouch(void * v)1081 stouch(void *v)
1082 {
1083 	int *msgvec = v;
1084 	int *ip;
1085 
1086 	for (ip = msgvec; *ip != 0; ip++) {
1087 		setdot(&message[*ip-1]);
1088 		dot->m_flag |= MTOUCH;
1089 		dot->m_flag &= ~MPRESERVE;
1090 		/*
1091 		 * POSIX interpretation necessary.
1092 		 */
1093 		did_print_dot = 1;
1094 	}
1095 	return(0);
1096 }
1097 
1098 /*
1099  * Make sure all passed messages get mboxed.
1100  */
1101 int
mboxit(void * v)1102 mboxit(void *v)
1103 {
1104 	int *msgvec = v;
1105 	int *ip;
1106 
1107 	for (ip = msgvec; *ip != 0; ip++) {
1108 		setdot(&message[*ip-1]);
1109 		dot->m_flag |= MTOUCH|MBOX;
1110 		dot->m_flag &= ~MPRESERVE;
1111 		/*
1112 		 * POSIX interpretation necessary.
1113 		 */
1114 		did_print_dot = 1;
1115 	}
1116 	return(0);
1117 }
1118 
1119 /*
1120  * List the folders the user currently has.
1121  */
1122 int
folders(void * v)1123 folders(void *v)
1124 {
1125 	char	**argv = v;
1126 	char dirname[PATHSIZE];
1127 	char *cmd, *name;
1128 
1129 	if (*argv)
1130 		name = expand(*argv);
1131 	else if (getfold(dirname, sizeof dirname) < 0) {
1132 		printf(catgets(catd, CATSET, 20,
1133 				"No value set for \"folder\"\n"));
1134 		return 1;
1135 	} else
1136 		name = dirname;
1137 	if (which_protocol(name) == PROTO_IMAP)
1138 		imap_folders(name, *argv == NULL);
1139 	else {
1140 		if ((cmd = value("LISTER")) == NULL)
1141 			cmd = "ls";
1142 		run_command(cmd, 0, -1, -1, name, NULL, NULL);
1143 	}
1144 	return 0;
1145 }
1146