1/*
2 * Copyright (c) 1980, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35#if 0
36static char sccsid[] = "@(#)collect.c	8.2 (Berkeley) 4/19/94";
37#endif
38static const char rcsid[] =
39  "$FreeBSD: src/usr.bin/mail/collect.c,v 1.12 2002/06/30 05:25:06 obrien Exp $";
40#endif /* not lint */
41
42#include <sys/cdefs.h>
43
44/*
45 * Mail -- a mail program
46 *
47 * Collect input from standard input, handling
48 * ~ escapes.
49 */
50
51#include "rcv.h"
52#include <fcntl.h>
53#include "extern.h"
54
55/*
56 * Read a message from standard output and return a read file to it
57 * or NULL on error.
58 */
59
60/*
61 * The following hokiness with global variables is so that on
62 * receipt of an interrupt signal, the partial message can be salted
63 * away on dead.letter.
64 */
65
66static	sig_t	saveint;		/* Previous SIGINT value */
67static	sig_t	savehup;		/* Previous SIGHUP value */
68static	sig_t	savetstp;		/* Previous SIGTSTP value */
69static	sig_t	savettou;		/* Previous SIGTTOU value */
70static	sig_t	savettin;		/* Previous SIGTTIN value */
71static	FILE	*collf;			/* File for saving away */
72static	int	hadintr;		/* Have seen one SIGINT so far */
73
74static	jmp_buf	colljmp;		/* To get back to work */
75static	int	colljmp_p;		/* whether to long jump */
76static	jmp_buf	collabort;		/* To end collection with error */
77
78static	jmp_buf	pipejmp;		/* To catch the loss of pipe connection */
79
80void
81brokthepipe(signo)
82        int signo;
83{
84        longjmp(pipejmp, 1);
85}
86
87FILE *
88collect(hp, printheaders)
89	struct header *hp;
90	int printheaders;
91{
92	FILE *fbuf;
93	int lc, cc, escape, eofcount, fd, c, t;
94	char linebuf[LINESIZE], tempname[PATHSIZE], *cp, getsub;
95	sigset_t nset;
96	int longline, lastlong, rc;	/* So we don't make 2 or more lines
97					   out of a long input line. */
98	int nlines, usepager;
99	char *envptr;
100
101	collf = NULL;
102	/*
103	 * Start catching signals from here, but we're still die on interrupts
104	 * until we're in the main loop.
105	 */
106	(void)sigemptyset(&nset);
107	(void)sigaddset(&nset, SIGINT);
108	(void)sigaddset(&nset, SIGHUP);
109	(void)sigprocmask(SIG_BLOCK, &nset, NULL);
110	if ((saveint = signal(SIGINT, SIG_IGN)) != SIG_IGN)
111		(void)signal(SIGINT, collint);
112	if ((savehup = signal(SIGHUP, SIG_IGN)) != SIG_IGN)
113		(void)signal(SIGHUP, collhup);
114	savetstp = signal(SIGTSTP, collstop);
115	savettou = signal(SIGTTOU, collstop);
116	savettin = signal(SIGTTIN, collstop);
117	if (setjmp(collabort) || setjmp(colljmp)) {
118		(void)rm(tempname);
119		goto err;
120	}
121	(void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
122
123	noreset++;
124	(void)snprintf(tempname, sizeof(tempname),
125	    "%s/mail.RsXXXXXXXXXX", tmpdir);
126	if ((fd = mkstemp(tempname)) == -1 ||
127	    (collf = Fdopen(fd, "w+")) == NULL) {
128		warn("%s", tempname);
129		goto err;
130	}
131	(void)rm(tempname);
132
133	/*
134	 * If we are going to prompt for a subject,
135	 * refrain from printing a newline after
136	 * the headers (since some people mind).
137	 */
138	t = GTO|GSUBJECT|GCC|GNL;
139	getsub = 0;
140	if (hp->h_subject == NULL && value("interactive") != NULL &&
141	    (value("ask") != NULL || value("asksub") != NULL))
142		t &= ~GNL, getsub++;
143	if (printheaders) {
144		puthead(hp, stdout, t);
145		(void)fflush(stdout);
146	}
147	if ((cp = value("escape")) != NULL)
148		escape = *cp;
149	else
150		escape = ESCAPE;
151	eofcount = 0;
152	hadintr = 0;
153	lastlong = 0;
154	longline = 0;
155
156	if (!setjmp(colljmp)) {
157		if (getsub) {
158			if (grabh(hp, GSUBJECT)) {
159				/* grabh was interrupted: must count as first one		*/
160				/* makes Unix 2003 conformance tests mailx_01.ex{49,57} pass	*/
161				/* printf("Interrupt from Subject:\n"); */
162				hadintr++;
163				goto cont;
164			}
165		}
166	} else {
167		/*
168		 * Come here for printing the after-signal message.
169		 * Duplicate messages won't be printed because
170		 * the write is aborted if we get a SIGTTOU.
171		 */
172cont:
173		if (hadintr) {
174			(void)fflush(stdout);
175			fprintf(stderr,
176			"\n(Interrupt -- one more to kill letter)\n");
177		} else {
178			printf("(continue)\n");
179			(void)fflush(stdout);
180		}
181	}
182	for (;;) {
183		colljmp_p = 1;
184		c = readline(stdin, linebuf, LINESIZE);
185		colljmp_p = 0;
186		if (c < 0) {
187			if (value("interactive") != NULL &&
188			    value("ignoreeof") != NULL && ++eofcount < 25) {
189				printf("Use \".\" to terminate letter\n");
190				continue;
191			}
192			break;
193		}
194		lastlong = longline;
195		longline = c == LINESIZE - 1;
196		eofcount = 0;
197		hadintr = 0;
198		if (linebuf[0] == '.' && linebuf[1] == '\0' &&
199		    value("interactive") != NULL && !lastlong &&
200		    (value("dot") != NULL || value("ignoreeof") != NULL))
201			break;
202		if (linebuf[0] != escape || value("interactive") == NULL ||
203		    lastlong) {
204			if (putline(collf, linebuf, !longline) < 0)
205				goto err;
206			continue;
207		}
208		c = linebuf[1];
209		switch (c) {
210		default:
211			/*
212			 * On double escape, just send the single one.
213			 * Otherwise, it's an error.
214			 */
215			if (c == escape) {
216				if (putline(collf, &linebuf[1], !longline) < 0)
217					goto err;
218				else
219					break;
220			}
221			printf("Unknown tilde escape.\n");
222			break;
223		case 'C':
224			/*
225			 * Dump core.
226			 */
227			core();
228			break;
229		case '!':
230			/*
231			 * Shell escape, send the balance of the
232			 * line to sh -c.
233			 */
234			shell(&linebuf[2]);
235			break;
236		case ':':
237		case '_':
238			/*
239			 * Escape to command mode, but be nice!
240			 */
241			execute(&linebuf[2], 1);
242			goto cont;
243		case '.':
244			/*
245			 * Simulate end of file on input.
246			 */
247			goto out;
248		case 'q':
249			/*
250			 * Force a quit of sending mail.
251			 * Act like an interrupt happened.
252			 */
253			hadintr++;
254			collint(SIGINT);
255			exit(1);
256		case 'x':
257			/*
258			 * Exit, do not save in dead.letter.
259			 */
260			goto err;
261		case 'h':
262			/*
263			 * Grab a bunch of headers.
264			 */
265			grabh(hp, GTO|GSUBJECT|GCC|GBCC);
266			goto cont;
267		case 't':
268			/*
269			 * Add to the To list.
270			 */
271			hp->h_to = cat(hp->h_to, extract(&linebuf[2], GTO));
272			break;
273		case 's':
274			/*
275			 * Set the Subject line.
276			 */
277			cp = &linebuf[2];
278			while (isspace((unsigned char)*cp))
279				cp++;
280			hp->h_subject = savestr(cp);
281			break;
282		case 'R':
283			/*
284			 * Set the Reply-To line.
285			 */
286			cp = &linebuf[2];
287			while (isspace((unsigned char)*cp))
288				cp++;
289			hp->h_replyto = savestr(cp);
290			break;
291		case 'c':
292			/*
293			 * Add to the CC list.
294			 */
295			hp->h_cc = cat(hp->h_cc, extract(&linebuf[2], GCC));
296			break;
297		case 'b':
298			/*
299			 * Add to the BCC list.
300			 */
301			hp->h_bcc = cat(hp->h_bcc, extract(&linebuf[2], GBCC));
302			break;
303		case 'i':
304		case 'A':
305		case 'a':
306			/*
307			 * Insert named variable in message.
308			 */
309			switch(c) {
310				case 'i':
311					cp = &linebuf[2];
312					while(isspace((unsigned char)*cp))
313						cp++;
314					break;
315				case 'a':
316					cp = "sign";
317					break;
318				case 'A':
319					cp = "Sign";
320					break;
321				default:
322					goto err;
323			}
324
325			if(*cp != '\0' && (cp = value(cp)) != NULL) {
326				if (*cp != '\0') {
327					printf("%s\n", cp);
328					if(putline(collf, cp, 1) < 0)
329						goto err;
330				}
331			}
332
333			break;
334		case 'd':
335			/*
336			 * Read in the dead letter file.
337			 */
338			if (strlcpy(linebuf + 2, getdeadletter(),
339				sizeof(linebuf) - 2)
340			    >= sizeof(linebuf) - 2) {
341				printf("Line buffer overflow\n");
342				break;
343			}
344			/* FALLTHROUGH */
345		case 'r':
346		case '<':
347			/*
348			 * Invoke a file:
349			 * Search for the file name,
350			 * then open it and copy the contents to collf.
351			 */
352			cp = &linebuf[2];
353			while (isspace((unsigned char)*cp))
354				cp++;
355			if (*cp == '\0') {
356				printf("Interpolate what file?\n");
357				break;
358			}
359			cp = expand(cp);
360			if (cp == NULL)
361				break;
362			if (*cp == '!') {
363				/*
364				 * Insert stdout of command.
365				 */
366				char *sh;
367				int nullfd, tempfd, rc;
368				char tempname2[PATHSIZE];
369
370				if ((nullfd = open("/dev/null", O_RDONLY, 0))
371				    == -1) {
372					warn("/dev/null");
373					break;
374				}
375
376				(void)snprintf(tempname2, sizeof(tempname2),
377				    "%s/mail.ReXXXXXXXXXX", tmpdir);
378				if ((tempfd = mkstemp(tempname2)) == -1 ||
379				    (fbuf = Fdopen(tempfd, "w+")) == NULL) {
380					warn("%s", tempname2);
381					break;
382				}
383				(void)unlink(tempname2);
384
385				if ((sh = value("SHELL")) == NULL)
386					sh = _PATH_BSHELL;
387
388				rc = run_command(sh, 0, nullfd, fileno(fbuf),
389				    "-c", cp+1, NULL);
390
391				close(nullfd);
392
393				if (rc < 0) {
394					(void)Fclose(fbuf);
395					break;
396				}
397
398				if (fsize(fbuf) == 0) {
399					fprintf(stderr,
400					    "No bytes from command \"%s\"\n",
401					    cp+1);
402					(void)Fclose(fbuf);
403					break;
404				}
405
406				rewind(fbuf);
407			} else if (isdir(cp)) {
408				printf("%s: Directory\n", cp);
409				break;
410			} else if ((fbuf = Fopen(cp, "r")) == NULL) {
411				warn("%s", cp);
412				break;
413			}
414			printf("\"%s\" ", cp);
415			(void)fflush(stdout);
416			lc = 0;
417			cc = 0;
418			while ((rc = readline(fbuf, linebuf, LINESIZE)) >= 0) {
419				if (rc != LINESIZE - 1)
420					lc++;
421				if ((t = putline(collf, linebuf,
422					 rc != LINESIZE - 1)) < 0) {
423					(void)Fclose(fbuf);
424					goto err;
425				}
426				cc += t;
427			}
428			(void)Fclose(fbuf);
429			printf("%d/%d\n", lc, cc);
430			break;
431		case 'w':
432			/*
433			 * Write the message on a file.
434			 */
435			cp = &linebuf[2];
436			while (*cp == ' ' || *cp == '\t')
437				cp++;
438			if (*cp == '\0') {
439				fprintf(stderr, "Write what file!?\n");
440				break;
441			}
442			if ((cp = expand(cp)) == NULL)
443				break;
444			rewind(collf);
445			exwrite(cp, collf, 1);
446			break;
447		case 'm':
448		case 'M':
449		case 'f':
450		case 'F':
451			/*
452			 * Interpolate the named messages, if we
453			 * are in receiving mail mode.  Does the
454			 * standard list processing garbage.
455			 * If ~f is given, we don't shift over.
456			 */
457			if (forward(linebuf + 2, collf, tempname, c) < 0)
458				goto err;
459			goto cont;
460		case '?':
461			if ((fbuf = Fopen(_PATH_TILDE, "r")) == NULL) {
462				warn("%s", _PATH_TILDE);
463				break;
464			}
465			while ((t = getc(fbuf)) != EOF)
466				(void)putchar(t);
467			(void)Fclose(fbuf);
468			break;
469		case 'p':
470			/*
471			 * Print out the current state of the
472			 * message without altering anything.
473			 */
474			rewind(collf);
475			printf("-------\nMessage contains:\n");
476			puthead(hp, stdout, GTO|GSUBJECT|GCC|GBCC|GNL);
477			if ((envptr = value("crt")) != NULL) {
478				nlines = atoi(envptr);
479			} else {
480				nlines = 0;
481			}
482			fbuf = stdout;
483			usepager = 0;
484			if (nlines>0) {
485				/* See if crt < num lines in file */
486				int countlines = 0;
487				while ((t = getc(collf)) != EOF) {
488					if (t=='\n') {
489						countlines++;
490						if (nlines < countlines) {
491							break;
492						}
493					}
494				}
495				rewind(collf);
496				if (nlines < countlines) {
497					/* Must use a paginator: default is "more" */
498					usepager = 1;
499					envptr = value("PAGER");
500		                        if (envptr == NULL || *envptr == '\0')
501		                                envptr = _PATH_MORE;
502					if (setjmp(pipejmp))
503				                goto close_pipe;
504		                        fbuf = Popen(envptr, "w");
505		                        if (fbuf == NULL) {
506						warnx("%s", envptr);
507		                                fbuf = stdout;
508		                        } else
509						(void)signal(SIGPIPE, brokthepipe);
510				}
511			}
512			while ((t = getc(collf)) != EOF)
513				(void)putchar(t);
514			if (usepager) {
515				close_pipe:
516			        if (fbuf != stdout) {
517			                /*
518			                 * Ignore SIGPIPE so it can't cause a duplicate close.
519					 */
520					(void)signal(SIGPIPE, SIG_IGN);
521					(void)Pclose(fbuf);
522					(void)signal(SIGPIPE, SIG_DFL);
523			        }
524			}
525			goto cont;
526		case '|':
527			/*
528			 * Pipe message through command.
529			 * Collect output as new message.
530			 */
531			rewind(collf);
532			mespipe(collf, &linebuf[2]);
533			goto cont;
534		case 'v':
535		case 'e':
536			/*
537			 * Edit the current message.
538			 * 'e' means to use EDITOR
539			 * 'v' means to use VISUAL
540			 */
541			rewind(collf);
542			mesedit(collf, c);
543			goto cont;
544		}
545	}
546	goto out;
547err:
548	senderr++;	/* set return code */
549	if (collf != NULL) {
550		(void)Fclose(collf);
551		collf = NULL;
552	}
553out:
554	if (collf != NULL)
555		rewind(collf);
556	noreset--;
557	(void)sigprocmask(SIG_BLOCK, &nset, NULL);
558	(void)signal(SIGINT, saveint);
559	(void)signal(SIGHUP, savehup);
560	(void)signal(SIGTSTP, savetstp);
561	(void)signal(SIGTTOU, savettou);
562	(void)signal(SIGTTIN, savettin);
563	(void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
564	return (collf);
565}
566
567/*
568 * Write a file, ex-like if f set.
569 */
570int
571exwrite(name, fp, f)
572	char name[];
573	FILE *fp;
574	int f;
575{
576	FILE *of;
577	int c, lc;
578	long cc;
579#if 0
580	struct stat junk;
581#endif
582
583	if (f) {
584		printf("\"%s\" ", name);
585		(void)fflush(stdout);
586	}
587#if 0
588	if (stat(name, &junk) >= 0 && S_ISREG(junk.st_mode)) {
589		if (!f)
590			fprintf(stderr, "%s: ", name);
591		fprintf(stderr, "File exists\n");
592		return (-1);
593	}
594#endif
595	if ((of = Fopen(name, "a")) == NULL) {
596		warn((char *)NULL);
597		return (-1);
598	}
599	lc = 0;
600	cc = 0;
601	while ((c = getc(fp)) != EOF) {
602		cc++;
603		if (c == '\n')
604			lc++;
605		(void)putc(c, of);
606		if (ferror(of)) {
607			warnx("%s", name);
608			(void)Fclose(of);
609			return (-1);
610		}
611	}
612	(void)Fclose(of);
613	printf("%d/%ld\n", lc, cc);
614	(void)fflush(stdout);
615	return (0);
616}
617
618/*
619 * Edit the message being collected on fp.
620 * On return, make the edit file the new temp file.
621 */
622void
623mesedit(fp, c)
624	FILE *fp;
625	int c;
626{
627	sig_t sigint = signal(SIGINT, SIG_IGN);
628	FILE *nf = run_editor(fp, (off_t)-1, c, 0);
629
630	if (nf != NULL) {
631		(void)fseeko(nf, (off_t)0, SEEK_END);
632		collf = nf;
633		(void)Fclose(fp);
634	}
635	(void)signal(SIGINT, sigint);
636}
637
638static char *
639parse_pipe_args(str, msglist)
640	char str[];
641	char **msglist;
642{
643        char *cp;
644	char quoted;
645
646	*msglist = NULL;
647	if (str==NULL) return NULL;
648	if (*str=='\0') return NULL;
649
650        cp = strlen(str) + str - 1;
651
652        /*
653         * Strip away trailing blanks.
654         */
655
656        while (cp > str && isspace((unsigned char)*cp))
657                cp--;
658        *++cp = '\0';
659
660        /*
661         * Now search for the beginning of the command.
662         */
663	quoted = 0;
664	if (cp > str) { /* check for quotes */
665		cp--;
666		if (*cp=='"' || *cp=='\'' ) {
667			quoted=*cp;
668			cp--;
669		}
670	}
671
672/*
673printf("before loop: str=%s,cp=%s\n", str,cp);
674*/
675
676        while (cp > str  && (!isspace((unsigned char)*cp) || quoted)) {
677		if (quoted) {
678			if (*cp==quoted) {
679				cp--;
680				if (cp>str) {
681					if (!(*cp=='\\' || *cp==quoted)) {
682						quoted=0;
683						continue;
684					}
685				} else /* done */
686					break;
687			}
688		}
689                cp--;
690	}
691        if (cp == str) {
692                return (cp); /* no msglist */
693        }
694
695	*msglist = str;
696        if (isspace((unsigned char)*cp))
697                *cp++ = '\0';
698        else {
699		printf("malformed arguments:%s\n",str);
700        }
701        return (cp);
702}
703
704int
705mailpipe(str)
706	char str[];
707{
708	struct message *mp;
709	int *msgvec, *ip;
710	char *msglist = NULL;
711	char * cmd;
712	char * sh;
713	char cmdline[4096];
714	int do_pagefeed;
715	FILE *fbuf;
716
717	fbuf = stdout;
718
719	/* parse arguments: [[msglist] command]	*/
720	cmd = parse_pipe_args(str, &msglist);
721/*
722	printf (" pipe args: msglist=%s, cmd=%s\n", msglist, cmd);
723*/
724	/* get message list  for reading	*/
725        msgvec = (int *)salloc((msgCount + 2) * sizeof(*msgvec));
726	if (msglist==NULL) {
727                *msgvec = first(0, MMNORM);
728                if (*msgvec == 0) {
729                        printf("No messages to %s.\n", cmd);
730                        return (1);
731                }
732                msgvec[1] = 0;
733	} else {
734	        if (getmsglist(msglist, msgvec, 0) < 0)
735	                return (1);
736	}
737	/* if cmd empty get from cmd= variable	*/
738	if (cmd==NULL) {
739		cmd = value("cmd");
740		if (cmd==NULL || *cmd == '\0') {
741			printf("No command to pipe.\n");
742			return(1);
743		}
744	}
745
746	if ((sh = value("SHELL")) == NULL)
747		sh = _PATH_BSHELL;
748
749	/* complete cmd, open a pipe to shell 	*/
750	cmdline[0] = '\0';
751	strlcpy(cmdline, sh, sizeof(cmdline));
752	strlcat(cmdline, " -c ", sizeof(cmdline));
753	if (*cmd!='"'  && *cmd!='\'') {
754		/* I know this doesn't handle all the cases, but
755		   it is enough to make the conformance tests pass */
756		strlcat(cmdline, "\"", sizeof(cmdline));
757		strlcat(cmdline, cmd, sizeof(cmdline));
758		strlcat(cmdline, "\"", sizeof(cmdline));
759	} else {
760		strlcat(cmdline, cmd, sizeof(cmdline));
761	}
762
763/*
764	printf(" popen cmdline:%s\n", cmdline);
765*/
766	if (setjmp(pipejmp))
767                goto close_pipe;
768
769	fbuf = Popen(cmdline, "w");
770	if (fbuf == NULL) {
771		warnx("%s", cmdline);
772		return(1);
773	} else
774		(void)signal(SIGPIPE, brokthepipe);
775
776	/* paginate if page= set		*/
777	if (value("page") == NULL) {
778		do_pagefeed = 0;
779	} else {
780		do_pagefeed = 1;
781	}
782
783	/* write all messages to the pipe  */
784	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
785                mp = &message[*ip - 1];
786                touch(mp);
787		dot = mp;
788/* printf (" sending message %d\n", ip-msgvec); */
789                if (sendmessage(mp, fbuf, 0, NULL) < 0) {
790                        warnx("%s", cmdline);
791                        (void)Fclose(fbuf);
792                        return (1);
793                }
794		if (do_pagefeed)
795			fprintf(fbuf,"\f"); /* form feed */
796        }
797	(void)fflush(fbuf);
798close_pipe:
799        if (fbuf != stdout) {
800                /*
801                 * Ignore SIGPIPE so it can't cause a duplicate close.
802                 */
803                (void)signal(SIGPIPE, SIG_IGN);
804                (void)Pclose(fbuf);
805                (void)signal(SIGPIPE, SIG_DFL);
806        }
807	return (0);
808}
809
810/*
811 * Pipe the message through the command.
812 * Old message is on stdin of command;
813 * New message collected from stdout.
814 * Sh -c must return 0 to accept the new message.
815 */
816void
817mespipe(fp, cmd)
818	FILE *fp;
819	char cmd[];
820{
821	FILE *nf;
822	int fd;
823	sig_t sigint = signal(SIGINT, SIG_IGN);
824	char *sh, tempname[PATHSIZE];
825
826	(void)snprintf(tempname, sizeof(tempname),
827	    "%s/mail.ReXXXXXXXXXX", tmpdir);
828	if ((fd = mkstemp(tempname)) == -1 ||
829	    (nf = Fdopen(fd, "w+")) == NULL) {
830		warn("%s", tempname);
831		goto out;
832	}
833	(void)rm(tempname);
834	/*
835	 * stdin = current message.
836	 * stdout = new message.
837	 */
838	if ((sh = value("SHELL")) == NULL)
839		sh = _PATH_BSHELL;
840	if (run_command(sh,
841	    0, fileno(fp), fileno(nf), "-c", cmd, NULL) < 0) {
842		(void)Fclose(nf);
843		goto out;
844	}
845	if (fsize(nf) == 0) {
846		fprintf(stderr, "No bytes from \"%s\" !?\n", cmd);
847		(void)Fclose(nf);
848		goto out;
849	}
850	/*
851	 * Take new files.
852	 */
853	(void)fseeko(nf, (off_t)0, SEEK_END);
854	collf = nf;
855	(void)Fclose(fp);
856out:
857	(void)signal(SIGINT, sigint);
858}
859
860/*
861 * Interpolate the named messages into the current
862 * message, preceding each line with a tab.
863 * Return a count of the number of characters now in
864 * the message, or -1 if an error is encountered writing
865 * the message temporary.  The flag argument is 'm' if we
866 * should shift over and 'f' if not.
867 */
868int
869forward(ms, fp, fn, f)
870	char ms[];
871	FILE *fp;
872	char *fn;
873	int f;
874{
875	int *msgvec;
876	struct ignoretab *ig;
877	char *tabst;
878
879	msgvec = (int *)salloc((msgCount+1) * sizeof(*msgvec));
880	if (msgvec == NULL)
881		return (0);
882	if (getmsglist(ms, msgvec, 0) < 0)
883		return (0);
884	if (*msgvec == 0) {
885		*msgvec = first(0, MMNORM);
886		if (*msgvec == 0) {
887			printf("No appropriate messages\n");
888			return (0);
889		}
890		msgvec[1] = 0;
891	}
892	if (f == 'f' || f == 'F')
893		tabst = NULL;
894	else if ((tabst = value("indentprefix")) == NULL)
895		tabst = "\t";
896	ig = isupper((unsigned char)f) ? NULL : ignore;
897	printf("Interpolating:");
898	for (; *msgvec != 0; msgvec++) {
899		struct message *mp = message + *msgvec - 1;
900
901		touch(mp);
902		printf(" %d", *msgvec);
903		if (sendmessage(mp, fp, ig, tabst) < 0) {
904			warnx("%s", fn);
905			return (-1);
906		}
907	}
908	printf("\n");
909	return (0);
910}
911
912/*
913 * Print (continue) when continued after ^Z.
914 */
915/*ARGSUSED*/
916void
917collstop(s)
918	int s;
919{
920	sig_t old_action = signal(s, SIG_DFL);
921	sigset_t nset;
922
923	(void)sigemptyset(&nset);
924	(void)sigaddset(&nset, s);
925	(void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
926	(void)kill(0, s);
927	(void)sigprocmask(SIG_BLOCK, &nset, NULL);
928	(void)signal(s, old_action);
929	if (colljmp_p) {
930		colljmp_p = 0;
931		hadintr = 0;
932		longjmp(colljmp, 1);
933	}
934}
935
936/*
937 * On interrupt, come here to save the partial message in ~/dead.letter.
938 * Then jump out of the collection loop.
939 */
940/*ARGSUSED*/
941void
942collint(s)
943	int s;
944{
945	/*
946	 * the control flow is subtle, because we can be called from ~q.
947	 */
948	if (!hadintr) {
949		if (value("ignore") != NULL) {
950			printf("@");
951			(void)fflush(stdout);
952			clearerr(stdin);
953			return;
954		}
955		hadintr = 1;
956		longjmp(colljmp, 1);
957	}
958	rewind(collf);
959	if (value("save") != NULL)
960		savedeadletter(collf);
961	longjmp(collabort, 1);
962}
963
964/*ARGSUSED*/
965void
966collhup(s)
967	int s;
968{
969	rewind(collf);
970	savedeadletter(collf);
971	/*
972	 * Let's pretend nobody else wants to clean up,
973	 * a true statement at this time.
974	 */
975	exit(1);
976}
977
978void
979savedeadletter(fp)
980	FILE *fp;
981{
982	FILE *dbuf;
983	int c;
984	char *cp;
985
986	if (fsize(fp) == 0)
987		return;
988	cp = getdeadletter();
989	c = umask(077);
990	dbuf = Fopen(cp, "w");
991	(void)umask(c);
992	if (dbuf == NULL)
993		return;
994	while ((c = getc(fp)) != EOF)
995		(void)putc(c, dbuf);
996	(void)Fclose(dbuf);
997	rewind(fp);
998}
999