xref: /openbsd-src/usr.bin/mail/collect.c (revision 085f113bcbd47388d2d63acf6b4339266fb91ab0)
1*085f113bSokan /*	$OpenBSD: collect.c,v 1.34 2014/01/17 18:42:30 okan Exp $	*/
2db59c1a6Smillert /*	$NetBSD: collect.c,v 1.9 1997/07/09 05:25:45 mikel Exp $	*/
37eb34045Sderaadt 
4df930be7Sderaadt /*
5df930be7Sderaadt  * Copyright (c) 1980, 1993
6df930be7Sderaadt  *	The Regents of the University of California.  All rights reserved.
7df930be7Sderaadt  *
8df930be7Sderaadt  * Redistribution and use in source and binary forms, with or without
9df930be7Sderaadt  * modification, are permitted provided that the following conditions
10df930be7Sderaadt  * are met:
11df930be7Sderaadt  * 1. Redistributions of source code must retain the above copyright
12df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer.
13df930be7Sderaadt  * 2. Redistributions in binary form must reproduce the above copyright
14df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer in the
15df930be7Sderaadt  *    documentation and/or other materials provided with the distribution.
16f75387cbSmillert  * 3. Neither the name of the University nor the names of its contributors
17df930be7Sderaadt  *    may be used to endorse or promote products derived from this software
18df930be7Sderaadt  *    without specific prior written permission.
19df930be7Sderaadt  *
20df930be7Sderaadt  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21df930be7Sderaadt  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22df930be7Sderaadt  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23df930be7Sderaadt  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24df930be7Sderaadt  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25df930be7Sderaadt  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26df930be7Sderaadt  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27df930be7Sderaadt  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28df930be7Sderaadt  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29df930be7Sderaadt  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30df930be7Sderaadt  * SUCH DAMAGE.
31df930be7Sderaadt  */
32df930be7Sderaadt 
33df930be7Sderaadt /*
34df930be7Sderaadt  * Mail -- a mail program
35df930be7Sderaadt  *
36df930be7Sderaadt  * Collect input from standard input, handling
37df930be7Sderaadt  * ~ escapes.
38df930be7Sderaadt  */
39df930be7Sderaadt 
40df930be7Sderaadt #include "rcv.h"
41df930be7Sderaadt #include "extern.h"
42df930be7Sderaadt 
43df930be7Sderaadt /*
44df930be7Sderaadt  * Read a message from standard output and return a read file to it
45df930be7Sderaadt  * or NULL on error.
46df930be7Sderaadt  */
47df930be7Sderaadt 
48df930be7Sderaadt /*
49df930be7Sderaadt  * The following hokiness with global variables is so that on
50df930be7Sderaadt  * receipt of an interrupt signal, the partial message can be salted
51df930be7Sderaadt  * away on dead.letter.
52df930be7Sderaadt  */
53df930be7Sderaadt static	FILE	*collf;			/* File for saving away */
54df930be7Sderaadt static	int	hadintr;		/* Have seen one SIGINT so far */
55df930be7Sderaadt 
56df930be7Sderaadt FILE *
collect(struct header * hp,int printheaders)574a9caef2Smillert collect(struct header *hp, int printheaders)
58df930be7Sderaadt {
59df930be7Sderaadt 	FILE *fbuf;
607f56ba93Smillert 	int lc, cc, fd, c, t, lastlong, rc, sig;
617f56ba93Smillert 	int escape, eofcount, longline;
627f56ba93Smillert 	char getsub;
63e6aad408Smillert 	char linebuf[LINESIZE], tempname[PATHSIZE], *cp;
64df930be7Sderaadt 
65df930be7Sderaadt 	collf = NULL;
667f56ba93Smillert 	eofcount = 0;
677f56ba93Smillert 	hadintr = 0;
687f56ba93Smillert 	lastlong = 0;
697f56ba93Smillert 	longline = 0;
707f56ba93Smillert 	if ((cp = value("escape")) != NULL)
717f56ba93Smillert 		escape = *cp;
727f56ba93Smillert 	else
737f56ba93Smillert 		escape = ESCAPE;
74df930be7Sderaadt 	noreset++;
757f56ba93Smillert 
760ecc72fcSmillert 	(void)snprintf(tempname, sizeof(tempname),
770ecc72fcSmillert 	    "%s/mail.RsXXXXXXXXXX", tmpdir);
780ecc72fcSmillert 	if ((fd = mkstemp(tempname)) == -1 ||
790ecc72fcSmillert 	    (collf = Fdopen(fd, "w+")) == NULL) {
80b638aa94Smillert 		warn("%s", tempname);
81df930be7Sderaadt 		goto err;
82df930be7Sderaadt 	}
830ecc72fcSmillert 	(void)rm(tempname);
84df930be7Sderaadt 
85df930be7Sderaadt 	/*
86df930be7Sderaadt 	 * If we are going to prompt for a subject,
87df930be7Sderaadt 	 * refrain from printing a newline after
88df930be7Sderaadt 	 * the headers (since some people mind).
89df930be7Sderaadt 	 */
90df930be7Sderaadt 	t = GTO|GSUBJECT|GCC|GNL;
91df930be7Sderaadt 	getsub = 0;
92c318c72bSmillert 	if (hp->h_subject == NULL && value("interactive") != NULL &&
93c318c72bSmillert 	    (value("ask") != NULL || value("asksub") != NULL))
94df930be7Sderaadt 		t &= ~GNL, getsub++;
95df930be7Sderaadt 	if (printheaders) {
96df930be7Sderaadt 		puthead(hp, stdout, t);
97df930be7Sderaadt 		fflush(stdout);
98df930be7Sderaadt 	}
997f56ba93Smillert 	if (getsub && gethfromtty(hp, GSUBJECT) == -1)
1007f56ba93Smillert 		goto err;
101df930be7Sderaadt 
1027f56ba93Smillert 	if (0) {
103df930be7Sderaadt cont:
1047f56ba93Smillert 		/* Come here for printing the after-suspend message. */
1051c39f73cSderaadt 		if (isatty(0)) {
106db59c1a6Smillert 			puts("(continue)");
107df930be7Sderaadt 			fflush(stdout);
108df930be7Sderaadt 		}
109df930be7Sderaadt 	}
110df930be7Sderaadt 	for (;;) {
1117f56ba93Smillert 		c = readline(stdin, linebuf, LINESIZE, &sig);
1127f56ba93Smillert 
1137f56ba93Smillert 		/* Act on any signal caught during readline() ignoring 'c' */
1147f56ba93Smillert 		switch (sig) {
1157f56ba93Smillert 		case 0:
1167f56ba93Smillert 			break;
1177f56ba93Smillert 		case SIGINT:
1187f56ba93Smillert 			if (collabort())
1197f56ba93Smillert 				goto err;
1207f56ba93Smillert 			continue;
1217f56ba93Smillert 		case SIGHUP:
1227f56ba93Smillert 			rewind(collf);
1237f56ba93Smillert 			savedeadletter(collf);
1247f56ba93Smillert 			/*
1257f56ba93Smillert 			 * Let's pretend nobody else wants to clean up,
1267f56ba93Smillert 			 * a true statement at this time.
1277f56ba93Smillert 			 */
1287f56ba93Smillert 			exit(1);
1297f56ba93Smillert 		default:
1307f56ba93Smillert 			/* Stopped due to job control */
1317f56ba93Smillert 			(void)kill(0, sig);
1327f56ba93Smillert 			goto cont;
1337f56ba93Smillert 		}
1347f56ba93Smillert 
1357f56ba93Smillert 		/* No signal, check for error */
136df930be7Sderaadt 		if (c < 0) {
137c318c72bSmillert 			if (value("interactive") != NULL &&
138c318c72bSmillert 			    value("ignoreeof") != NULL && ++eofcount < 25) {
139db59c1a6Smillert 				puts("Use \".\" to terminate letter");
140df930be7Sderaadt 				continue;
141df930be7Sderaadt 			}
142df930be7Sderaadt 			break;
143df930be7Sderaadt 		}
144db59c1a6Smillert 		lastlong = longline;
145db59c1a6Smillert 		longline = (c == LINESIZE - 1);
146df930be7Sderaadt 		eofcount = 0;
147df930be7Sderaadt 		hadintr = 0;
148df930be7Sderaadt 		if (linebuf[0] == '.' && linebuf[1] == '\0' &&
149c318c72bSmillert 		    value("interactive") != NULL && !lastlong &&
150c318c72bSmillert 		    (value("dot") != NULL || value("ignoreeof") != NULL))
151df930be7Sderaadt 			break;
15237fc7e6fSmillert 		if (linebuf[0] != escape || value("interactive") == NULL ||
15337fc7e6fSmillert 		    lastlong) {
154db59c1a6Smillert 			if (putline(collf, linebuf, !longline) < 0)
155df930be7Sderaadt 				goto err;
156df930be7Sderaadt 			continue;
157df930be7Sderaadt 		}
158*085f113bSokan 		c = (unsigned char)linebuf[1];
159df930be7Sderaadt 		switch (c) {
160df930be7Sderaadt 		default:
161df930be7Sderaadt 			/*
162df930be7Sderaadt 			 * On double escape, just send the single one.
163df930be7Sderaadt 			 * Otherwise, it's an error.
164df930be7Sderaadt 			 */
165df930be7Sderaadt 			if (c == escape) {
166db59c1a6Smillert 				if (putline(collf, &linebuf[1], !longline) < 0)
167df930be7Sderaadt 					goto err;
168df930be7Sderaadt 				else
169df930be7Sderaadt 					break;
170df930be7Sderaadt 			}
171db59c1a6Smillert 			puts("Unknown tilde escape.");
172df930be7Sderaadt 			break;
173df930be7Sderaadt 		case '!':
174df930be7Sderaadt 			/*
175df930be7Sderaadt 			 * Shell escape, send the balance of the
176df930be7Sderaadt 			 * line to sh -c.
177df930be7Sderaadt 			 */
178df930be7Sderaadt 			shell(&linebuf[2]);
179df930be7Sderaadt 			break;
180df930be7Sderaadt 		case ':':
181df930be7Sderaadt 		case '_':
182df930be7Sderaadt 			/*
183df930be7Sderaadt 			 * Escape to command mode, but be nice!
184df930be7Sderaadt 			 */
185df930be7Sderaadt 			execute(&linebuf[2], 1);
186df930be7Sderaadt 			goto cont;
187df930be7Sderaadt 		case '.':
188df930be7Sderaadt 			/*
189df930be7Sderaadt 			 * Simulate end of file on input.
190df930be7Sderaadt 			 */
191df930be7Sderaadt 			goto out;
192df930be7Sderaadt 		case 'q':
193df930be7Sderaadt 			/*
194df930be7Sderaadt 			 * Force a quit of sending mail.
195df930be7Sderaadt 			 * Act like an interrupt happened.
196df930be7Sderaadt 			 */
197df930be7Sderaadt 			hadintr++;
1987f56ba93Smillert 			collabort();
1997f56ba93Smillert 			fputs("Interrupt\n", stderr);
2007f56ba93Smillert 			goto err;
201c09de76fSmartynas 		case 'x':
202c09de76fSmartynas 			/*
203c09de76fSmartynas 			 * Force a quit of sending mail.
204c09de76fSmartynas 			 * Do not save the message.
205c09de76fSmartynas 			 */
206c09de76fSmartynas 			goto err;
207df930be7Sderaadt 		case 'h':
208df930be7Sderaadt 			/*
209df930be7Sderaadt 			 * Grab a bunch of headers.
210df930be7Sderaadt 			 */
211df930be7Sderaadt 			grabh(hp, GTO|GSUBJECT|GCC|GBCC);
212df930be7Sderaadt 			goto cont;
213df930be7Sderaadt 		case 't':
214df930be7Sderaadt 			/*
215df930be7Sderaadt 			 * Add to the To list.
216df930be7Sderaadt 			 */
217df930be7Sderaadt 			hp->h_to = cat(hp->h_to, extract(&linebuf[2], GTO));
218df930be7Sderaadt 			break;
219df930be7Sderaadt 		case 's':
220df930be7Sderaadt 			/*
221df930be7Sderaadt 			 * Set the Subject list.
222df930be7Sderaadt 			 */
223df930be7Sderaadt 			cp = &linebuf[2];
224*085f113bSokan 			while (isspace((unsigned char)*cp))
225df930be7Sderaadt 				cp++;
226df930be7Sderaadt 			hp->h_subject = savestr(cp);
227df930be7Sderaadt 			break;
228df930be7Sderaadt 		case 'c':
229df930be7Sderaadt 			/*
230df930be7Sderaadt 			 * Add to the CC list.
231df930be7Sderaadt 			 */
232df930be7Sderaadt 			hp->h_cc = cat(hp->h_cc, extract(&linebuf[2], GCC));
233df930be7Sderaadt 			break;
234df930be7Sderaadt 		case 'b':
235df930be7Sderaadt 			/*
236df930be7Sderaadt 			 * Add stuff to blind carbon copies list.
237df930be7Sderaadt 			 */
238df930be7Sderaadt 			hp->h_bcc = cat(hp->h_bcc, extract(&linebuf[2], GBCC));
239df930be7Sderaadt 			break;
240df930be7Sderaadt 		case 'd':
2414a9caef2Smillert 			linebuf[2] = '\0';
2424a9caef2Smillert 			strlcat(linebuf, getdeadletter(), sizeof(linebuf));
243df930be7Sderaadt 			/* fall into . . . */
244df930be7Sderaadt 		case 'r':
245df930be7Sderaadt 		case '<':
246df930be7Sderaadt 			/*
247df930be7Sderaadt 			 * Invoke a file:
248df930be7Sderaadt 			 * Search for the file name,
249df930be7Sderaadt 			 * then open it and copy the contents to collf.
250df930be7Sderaadt 			 */
251df930be7Sderaadt 			cp = &linebuf[2];
252*085f113bSokan 			while (isspace((unsigned char)*cp))
253df930be7Sderaadt 				cp++;
254df930be7Sderaadt 			if (*cp == '\0') {
255db59c1a6Smillert 				puts("Interpolate what file?");
256df930be7Sderaadt 				break;
257df930be7Sderaadt 			}
258df930be7Sderaadt 			cp = expand(cp);
259c318c72bSmillert 			if (cp == NULL)
260df930be7Sderaadt 				break;
261df930be7Sderaadt 			if (isdir(cp)) {
262df930be7Sderaadt 				printf("%s: Directory\n", cp);
263df930be7Sderaadt 				break;
264df930be7Sderaadt 			}
265df930be7Sderaadt 			if ((fbuf = Fopen(cp, "r")) == NULL) {
266b638aa94Smillert 				warn("%s", cp);
267df930be7Sderaadt 				break;
268df930be7Sderaadt 			}
269df930be7Sderaadt 			printf("\"%s\" ", cp);
270df930be7Sderaadt 			fflush(stdout);
271df930be7Sderaadt 			lc = 0;
272df930be7Sderaadt 			cc = 0;
2737f56ba93Smillert 			while ((rc = readline(fbuf, linebuf, LINESIZE, NULL)) >= 0) {
274db59c1a6Smillert 				if (rc != LINESIZE - 1)
275df930be7Sderaadt 					lc++;
276db59c1a6Smillert 				if ((t = putline(collf, linebuf,
277db59c1a6Smillert 						 rc != LINESIZE-1)) < 0) {
278db59c1a6Smillert 					(void)Fclose(fbuf);
279df930be7Sderaadt 					goto err;
280df930be7Sderaadt 				}
281df930be7Sderaadt 				cc += t;
282df930be7Sderaadt 			}
283db59c1a6Smillert 			(void)Fclose(fbuf);
284df930be7Sderaadt 			printf("%d/%d\n", lc, cc);
285df930be7Sderaadt 			break;
286df930be7Sderaadt 		case 'w':
287df930be7Sderaadt 			/*
288df930be7Sderaadt 			 * Write the message on a file.
289df930be7Sderaadt 			 */
290df930be7Sderaadt 			cp = &linebuf[2];
291df930be7Sderaadt 			while (*cp == ' ' || *cp == '\t')
292df930be7Sderaadt 				cp++;
293df930be7Sderaadt 			if (*cp == '\0') {
294db59c1a6Smillert 				fputs("Write what file!?\n", stderr);
295df930be7Sderaadt 				break;
296df930be7Sderaadt 			}
297c318c72bSmillert 			if ((cp = expand(cp)) == NULL)
298df930be7Sderaadt 				break;
299df930be7Sderaadt 			rewind(collf);
300df930be7Sderaadt 			exwrite(cp, collf, 1);
301df930be7Sderaadt 			break;
302df930be7Sderaadt 		case 'm':
303df930be7Sderaadt 		case 'M':
304df930be7Sderaadt 		case 'f':
305df930be7Sderaadt 		case 'F':
306df930be7Sderaadt 			/*
307df930be7Sderaadt 			 * Interpolate the named messages, if we
308df930be7Sderaadt 			 * are in receiving mail mode.  Does the
309df930be7Sderaadt 			 * standard list processing garbage.
310df930be7Sderaadt 			 * If ~f is given, we don't shift over.
311df930be7Sderaadt 			 */
3120ecc72fcSmillert 			if (forward(linebuf + 2, collf, tempname, c) < 0)
313df930be7Sderaadt 				goto err;
314df930be7Sderaadt 			goto cont;
315df930be7Sderaadt 		case '?':
316df930be7Sderaadt 			if ((fbuf = Fopen(_PATH_TILDE, "r")) == NULL) {
317db59c1a6Smillert 				warn(_PATH_TILDE);
318df930be7Sderaadt 				break;
319df930be7Sderaadt 			}
320df930be7Sderaadt 			while ((t = getc(fbuf)) != EOF)
321df930be7Sderaadt 				(void)putchar(t);
322db59c1a6Smillert 			(void)Fclose(fbuf);
323df930be7Sderaadt 			break;
324df930be7Sderaadt 		case 'p':
325df930be7Sderaadt 			/*
326df930be7Sderaadt 			 * Print out the current state of the
327df930be7Sderaadt 			 * message without altering anything.
328df930be7Sderaadt 			 */
329df930be7Sderaadt 			rewind(collf);
330db59c1a6Smillert 			puts("-------\nMessage contains:");
331df930be7Sderaadt 			puthead(hp, stdout, GTO|GSUBJECT|GCC|GBCC|GNL);
332df930be7Sderaadt 			while ((t = getc(collf)) != EOF)
333df930be7Sderaadt 				(void)putchar(t);
334df930be7Sderaadt 			goto cont;
335df930be7Sderaadt 		case '|':
336df930be7Sderaadt 			/*
337df930be7Sderaadt 			 * Pipe message through command.
338df930be7Sderaadt 			 * Collect output as new message.
339df930be7Sderaadt 			 */
340df930be7Sderaadt 			rewind(collf);
341df930be7Sderaadt 			mespipe(collf, &linebuf[2]);
342df930be7Sderaadt 			goto cont;
343df930be7Sderaadt 		case 'v':
344df930be7Sderaadt 		case 'e':
345df930be7Sderaadt 			/*
346df930be7Sderaadt 			 * Edit the current message.
347df930be7Sderaadt 			 * 'e' means to use EDITOR
348df930be7Sderaadt 			 * 'v' means to use VISUAL
349df930be7Sderaadt 			 */
350df930be7Sderaadt 			rewind(collf);
351df930be7Sderaadt 			mesedit(collf, c);
352df930be7Sderaadt 			goto cont;
353df930be7Sderaadt 		}
354df930be7Sderaadt 	}
355db7aedf8Smillert 
356db7aedf8Smillert 	if (value("interactive") != NULL) {
357db7aedf8Smillert 		if (value("askcc") != NULL || value("askbcc") != NULL) {
3587f56ba93Smillert 			if (value("askcc") != NULL) {
3597f56ba93Smillert 				if (gethfromtty(hp, GCC) == -1)
3607f56ba93Smillert 					goto err;
3617f56ba93Smillert 			}
3627f56ba93Smillert 			if (value("askbcc") != NULL) {
3637f56ba93Smillert 				if (gethfromtty(hp, GBCC) == -1)
3647f56ba93Smillert 					goto err;
3657f56ba93Smillert 			}
366db7aedf8Smillert 		} else {
367db7aedf8Smillert 			puts("EOT");
368db7aedf8Smillert 			(void)fflush(stdout);
369db7aedf8Smillert 		}
370db7aedf8Smillert 	}
371df930be7Sderaadt 	goto out;
372df930be7Sderaadt err:
373df930be7Sderaadt 	if (collf != NULL) {
374db59c1a6Smillert 		(void)Fclose(collf);
375df930be7Sderaadt 		collf = NULL;
376df930be7Sderaadt 	}
377df930be7Sderaadt out:
378df930be7Sderaadt 	if (collf != NULL)
379df930be7Sderaadt 		rewind(collf);
380df930be7Sderaadt 	noreset--;
381db59c1a6Smillert 	return(collf);
382df930be7Sderaadt }
383df930be7Sderaadt 
384df930be7Sderaadt /*
385df930be7Sderaadt  * Write a file, ex-like if f set.
386df930be7Sderaadt  */
387df930be7Sderaadt int
exwrite(char * name,FILE * fp,int f)3884a9caef2Smillert exwrite(char *name, FILE *fp, int f)
389df930be7Sderaadt {
39036999bedSmillert 	FILE *of;
39136999bedSmillert 	int c;
392db7aedf8Smillert 	ssize_t cc, lc;
393df930be7Sderaadt 	struct stat junk;
394df930be7Sderaadt 
395df930be7Sderaadt 	if (f) {
396df930be7Sderaadt 		printf("\"%s\" ", name);
397df930be7Sderaadt 		fflush(stdout);
398df930be7Sderaadt 	}
39936999bedSmillert 	if (stat(name, &junk) >= 0 && S_ISREG(junk.st_mode)) {
400df930be7Sderaadt 		if (!f)
401df930be7Sderaadt 			fprintf(stderr, "%s: ", name);
402db59c1a6Smillert 		fputs("File exists\n", stderr);
403df930be7Sderaadt 		return(-1);
404df930be7Sderaadt 	}
405df930be7Sderaadt 	if ((of = Fopen(name, "w")) == NULL) {
406c318c72bSmillert 		warn(NULL);
407df930be7Sderaadt 		return(-1);
408df930be7Sderaadt 	}
409df930be7Sderaadt 	lc = 0;
410df930be7Sderaadt 	cc = 0;
411df930be7Sderaadt 	while ((c = getc(fp)) != EOF) {
412df930be7Sderaadt 		cc++;
413df930be7Sderaadt 		if (c == '\n')
414df930be7Sderaadt 			lc++;
415df930be7Sderaadt 		(void)putc(c, of);
416df930be7Sderaadt 		if (ferror(of)) {
417b638aa94Smillert 			warn("%s", name);
418db59c1a6Smillert 			(void)Fclose(of);
419df930be7Sderaadt 			return(-1);
420df930be7Sderaadt 		}
421df930be7Sderaadt 	}
422db59c1a6Smillert 	(void)Fclose(of);
4232eba0a68Smillert 	printf("%lld/%lld\n", (long long)lc, (long long)cc);
424df930be7Sderaadt 	fflush(stdout);
425df930be7Sderaadt 	return(0);
426df930be7Sderaadt }
427df930be7Sderaadt 
428df930be7Sderaadt /*
429df930be7Sderaadt  * Edit the message being collected on fp.
430df930be7Sderaadt  * On return, make the edit file the new temp file.
431df930be7Sderaadt  */
432df930be7Sderaadt void
mesedit(FILE * fp,int c)4334a9caef2Smillert mesedit(FILE *fp, int c)
434df930be7Sderaadt {
4357f56ba93Smillert 	FILE *nf;
4367f56ba93Smillert 	struct sigaction oact;
4377f56ba93Smillert 	sigset_t oset;
438df930be7Sderaadt 
4397f56ba93Smillert 	(void)ignoresig(SIGINT, &oact, &oset);
4407f56ba93Smillert 	nf = run_editor(fp, (off_t)-1, c, 0);
441df930be7Sderaadt 	if (nf != NULL) {
442a52c79cdStobias 		fseek(nf, 0L, SEEK_END);
443df930be7Sderaadt 		collf = nf;
444db59c1a6Smillert 		(void)Fclose(fp);
445df930be7Sderaadt 	}
4467f56ba93Smillert 	(void)sigprocmask(SIG_SETMASK, &oset, NULL);
4477f56ba93Smillert 	(void)sigaction(SIGINT, &oact, NULL);
448df930be7Sderaadt }
449df930be7Sderaadt 
450df930be7Sderaadt /*
451df930be7Sderaadt  * Pipe the message through the command.
452df930be7Sderaadt  * Old message is on stdin of command;
453df930be7Sderaadt  * New message collected from stdout.
454df930be7Sderaadt  * Sh -c must return 0 to accept the new message.
455df930be7Sderaadt  */
456df930be7Sderaadt void
mespipe(FILE * fp,char * cmd)4574a9caef2Smillert mespipe(FILE *fp, char *cmd)
458df930be7Sderaadt {
459df930be7Sderaadt 	FILE *nf;
4600ecc72fcSmillert 	int fd;
4610ecc72fcSmillert 	char *shell, tempname[PATHSIZE];
4627f56ba93Smillert 	struct sigaction oact;
4637f56ba93Smillert 	sigset_t oset;
464df930be7Sderaadt 
4657f56ba93Smillert 	(void)ignoresig(SIGINT, &oact, &oset);
4660ecc72fcSmillert 	(void)snprintf(tempname, sizeof(tempname),
4670ecc72fcSmillert 	    "%s/mail.ReXXXXXXXXXX", tmpdir);
4680ecc72fcSmillert 	if ((fd = mkstemp(tempname)) == -1 ||
4690ecc72fcSmillert 	    (nf = Fdopen(fd, "w+")) == NULL) {
470b638aa94Smillert 		warn("%s", tempname);
471df930be7Sderaadt 		goto out;
472df930be7Sderaadt 	}
4730ecc72fcSmillert 	(void)rm(tempname);
474df930be7Sderaadt 	/*
475df930be7Sderaadt 	 * stdin = current message.
476df930be7Sderaadt 	 * stdout = new message.
477df930be7Sderaadt 	 */
478ca8b07b0Smillert 	shell = value("SHELL");
479df930be7Sderaadt 	if (run_command(shell,
480c318c72bSmillert 	    0, fileno(fp), fileno(nf), "-c", cmd, NULL) < 0) {
481df930be7Sderaadt 		(void)Fclose(nf);
482df930be7Sderaadt 		goto out;
483df930be7Sderaadt 	}
484df930be7Sderaadt 	if (fsize(nf) == 0) {
485df930be7Sderaadt 		fprintf(stderr, "No bytes from \"%s\" !?\n", cmd);
486df930be7Sderaadt 		(void)Fclose(nf);
487df930be7Sderaadt 		goto out;
488df930be7Sderaadt 	}
489df930be7Sderaadt 	/*
490df930be7Sderaadt 	 * Take new files.
491df930be7Sderaadt 	 */
492a52c79cdStobias 	(void)fseek(nf, 0L, SEEK_END);
493df930be7Sderaadt 	collf = nf;
494df930be7Sderaadt 	(void)Fclose(fp);
495df930be7Sderaadt out:
4967f56ba93Smillert 	(void)sigprocmask(SIG_SETMASK, &oset, NULL);
4977f56ba93Smillert 	(void)sigaction(SIGINT, &oact, NULL);
498df930be7Sderaadt }
499df930be7Sderaadt 
500df930be7Sderaadt /*
501df930be7Sderaadt  * Interpolate the named messages into the current
502df930be7Sderaadt  * message, preceding each line with a tab.
503df930be7Sderaadt  * Return a count of the number of characters now in
504df930be7Sderaadt  * the message, or -1 if an error is encountered writing
505df930be7Sderaadt  * the message temporary.  The flag argument is 'm' if we
506df930be7Sderaadt  * should shift over and 'f' if not.
507df930be7Sderaadt  */
508df930be7Sderaadt int
forward(char * ms,FILE * fp,char * fn,int f)5094a9caef2Smillert forward(char *ms, FILE *fp, char *fn, int f)
510df930be7Sderaadt {
51136999bedSmillert 	int *msgvec;
512df930be7Sderaadt 	struct ignoretab *ig;
513df930be7Sderaadt 	char *tabst;
514df930be7Sderaadt 
515db59c1a6Smillert 	msgvec = (int *)salloc((msgCount+1) * sizeof(*msgvec));
516c318c72bSmillert 	if (msgvec == NULL)
517df930be7Sderaadt 		return(0);
518df930be7Sderaadt 	if (getmsglist(ms, msgvec, 0) < 0)
519df930be7Sderaadt 		return(0);
520df930be7Sderaadt 	if (*msgvec == 0) {
521df930be7Sderaadt 		*msgvec = first(0, MMNORM);
522ca9f5f35Savsm 		if (*msgvec == 0) {
523db59c1a6Smillert 			puts("No appropriate messages");
524df930be7Sderaadt 			return(0);
525df930be7Sderaadt 		}
526691235adSmiod 		msgvec[1] = 0;
527df930be7Sderaadt 	}
5280ab8ef84Smartynas 	if (tolower(f) == 'f')
529c318c72bSmillert 		tabst = NULL;
530c318c72bSmillert 	else if ((tabst = value("indentprefix")) == NULL)
531df930be7Sderaadt 		tabst = "\t";
532df930be7Sderaadt 	ig = isupper(f) ? NULL : ignore;
533db59c1a6Smillert 	fputs("Interpolating:", stdout);
534df930be7Sderaadt 	for (; *msgvec != 0; msgvec++) {
535df930be7Sderaadt 		struct message *mp = message + *msgvec - 1;
536df930be7Sderaadt 
537df930be7Sderaadt 		touch(mp);
538df930be7Sderaadt 		printf(" %d", *msgvec);
53980d25fdaSmillert 		if (sendmessage(mp, fp, ig, tabst) < 0) {
540b638aa94Smillert 			warn("%s", fn);
541df930be7Sderaadt 			return(-1);
542df930be7Sderaadt 		}
543df930be7Sderaadt 	}
544db59c1a6Smillert 	putchar('\n');
545df930be7Sderaadt 	return(0);
546df930be7Sderaadt }
547df930be7Sderaadt 
548df930be7Sderaadt /*
5497f56ba93Smillert  * User aborted during message composition.
5507f56ba93Smillert  * Save the partial message in ~/dead.letter.
551df930be7Sderaadt  */
5527f56ba93Smillert int
collabort(void)5534a9caef2Smillert collabort(void)
554df930be7Sderaadt {
555df930be7Sderaadt 	/*
556df930be7Sderaadt 	 * the control flow is subtle, because we can be called from ~q.
557df930be7Sderaadt 	 */
5583d45000cSderaadt 	if (hadintr == 0 && isatty(0)) {
559c318c72bSmillert 		if (value("ignore") != NULL) {
560df930be7Sderaadt 			puts("@");
561df930be7Sderaadt 			fflush(stdout);
562df930be7Sderaadt 			clearerr(stdin);
5637f56ba93Smillert 		} else {
5647f56ba93Smillert 			fflush(stdout);
5657f56ba93Smillert 			fputs("\n(Interrupt -- one more to kill letter)\n",
5667f56ba93Smillert 			    stderr);
5677f56ba93Smillert 			hadintr++;
568df930be7Sderaadt 		}
5697f56ba93Smillert 		return(0);
570df930be7Sderaadt 	}
5717f56ba93Smillert 	fflush(stdout);
572df930be7Sderaadt 	rewind(collf);
573c318c72bSmillert 	if (value("nosave") == NULL)
574df930be7Sderaadt 		savedeadletter(collf);
5757f56ba93Smillert 	return(1);
576df930be7Sderaadt }
577df930be7Sderaadt 
578df930be7Sderaadt void
savedeadletter(FILE * fp)5794a9caef2Smillert savedeadletter(FILE *fp)
580df930be7Sderaadt {
58136999bedSmillert 	FILE *dbuf;
58236999bedSmillert 	int c;
583df930be7Sderaadt 	char *cp;
584df930be7Sderaadt 
585df930be7Sderaadt 	if (fsize(fp) == 0)
586df930be7Sderaadt 		return;
587df930be7Sderaadt 	cp = getdeadletter();
588df930be7Sderaadt 	c = umask(077);
589df930be7Sderaadt 	dbuf = Fopen(cp, "a");
590df930be7Sderaadt 	(void)umask(c);
591df930be7Sderaadt 	if (dbuf == NULL)
592df930be7Sderaadt 		return;
593df930be7Sderaadt 	while ((c = getc(fp)) != EOF)
594df930be7Sderaadt 		(void)putc(c, dbuf);
595db59c1a6Smillert 	(void)Fclose(dbuf);
596df930be7Sderaadt 	rewind(fp);
597df930be7Sderaadt }
598db7aedf8Smillert 
5997f56ba93Smillert int
gethfromtty(struct header * hp,int gflags)6004a9caef2Smillert gethfromtty(struct header *hp, int gflags)
601db7aedf8Smillert {
6027f56ba93Smillert 
6037f56ba93Smillert 	hadintr = 0;
6047f56ba93Smillert 	while (grabh(hp, gflags) != 0) {
6057f56ba93Smillert 		if (collabort())
6067f56ba93Smillert 			return(-1);
607db7aedf8Smillert 	}
6087f56ba93Smillert 	return(0);
609db7aedf8Smillert }
610