1160814Ssimon/*
2160814Ssimon * Copyright (c) 1980, 1993
3160814Ssimon *	The Regents of the University of California.  All rights reserved.
4160814Ssimon *
5160814Ssimon * Redistribution and use in source and binary forms, with or without
6160814Ssimon * modification, are permitted provided that the following conditions
7160814Ssimon * are met:
8160814Ssimon * 1. Redistributions of source code must retain the above copyright
9160814Ssimon *    notice, this list of conditions and the following disclaimer.
10160814Ssimon * 2. Redistributions in binary form must reproduce the above copyright
11160814Ssimon *    notice, this list of conditions and the following disclaimer in the
12160814Ssimon *    documentation and/or other materials provided with the distribution.
13160814Ssimon * 4. Neither the name of the University nor the names of its contributors
14160814Ssimon *    may be used to endorse or promote products derived from this software
15160814Ssimon *    without specific prior written permission.
16160814Ssimon *
17160814Ssimon * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18160814Ssimon * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19160814Ssimon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20160814Ssimon * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21160814Ssimon * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22160814Ssimon * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23160814Ssimon * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24160814Ssimon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25160814Ssimon * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26160814Ssimon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27160814Ssimon * SUCH DAMAGE.
28160814Ssimon */
29160814Ssimon
30160814Ssimon#ifndef lint
31160814Ssimon#if 0
32160814Ssimonstatic char sccsid[] = "@(#)collect.c	8.2 (Berkeley) 4/19/94";
33160814Ssimon#endif
34160814Ssimon#endif /* not lint */
35160814Ssimon#include <sys/cdefs.h>
36160814Ssimon__FBSDID("$FreeBSD$");
37160814Ssimon
38160814Ssimon/*
39160814Ssimon * Mail -- a mail program
40160814Ssimon *
41160814Ssimon * Collect input from standard input, handling
42160814Ssimon * ~ escapes.
43160814Ssimon */
44160814Ssimon
45160814Ssimon#include "rcv.h"
46160814Ssimon#include <fcntl.h>
47160814Ssimon#include "extern.h"
48160814Ssimon
49160814Ssimon/*
50160814Ssimon * Read a message from standard output and return a read file to it
51160814Ssimon * or NULL on error.
52160814Ssimon */
53160814Ssimon
54160814Ssimon/*
55160814Ssimon * The following hokiness with global variables is so that on
56160814Ssimon * receipt of an interrupt signal, the partial message can be salted
57160814Ssimon * away on dead.letter.
58160814Ssimon */
59160814Ssimon
60160814Ssimonstatic	sig_t	saveint;		/* Previous SIGINT value */
61160814Ssimonstatic	sig_t	savehup;		/* Previous SIGHUP value */
62160814Ssimonstatic	sig_t	savetstp;		/* Previous SIGTSTP value */
63160814Ssimonstatic	sig_t	savettou;		/* Previous SIGTTOU value */
64160814Ssimonstatic	sig_t	savettin;		/* Previous SIGTTIN value */
65160814Ssimonstatic	FILE	*collf;			/* File for saving away */
66160814Ssimonstatic	int	hadintr;		/* Have seen one SIGINT so far */
67160814Ssimon
68160814Ssimonstatic	jmp_buf	colljmp;		/* To get back to work */
69160814Ssimonstatic	int	colljmp_p;		/* whether to long jump */
70160814Ssimonstatic	jmp_buf	collabort;		/* To end collection with error */
71160814Ssimon
72160814SsimonFILE *
73160814Ssimoncollect(struct header *hp, int printheaders)
74160814Ssimon{
75160814Ssimon	FILE *fbuf;
76160814Ssimon	int lc, cc, escape, eofcount, fd, c, t;
77160814Ssimon	char linebuf[LINESIZE], tempname[PATHSIZE], *cp, getsub;
78160814Ssimon	sigset_t nset;
79160814Ssimon	int longline, lastlong, rc;	/* So we don't make 2 or more lines
80160814Ssimon					   out of a long input line. */
81160814Ssimon
82160814Ssimon	collf = NULL;
83160814Ssimon	/*
84160814Ssimon	 * Start catching signals from here, but we're still die on interrupts
85160814Ssimon	 * until we're in the main loop.
86160814Ssimon	 */
87160814Ssimon	(void)sigemptyset(&nset);
88160814Ssimon	(void)sigaddset(&nset, SIGINT);
89160814Ssimon	(void)sigaddset(&nset, SIGHUP);
90160814Ssimon	(void)sigprocmask(SIG_BLOCK, &nset, NULL);
91160814Ssimon	if ((saveint = signal(SIGINT, SIG_IGN)) != SIG_IGN)
92160814Ssimon		(void)signal(SIGINT, collint);
93160814Ssimon	if ((savehup = signal(SIGHUP, SIG_IGN)) != SIG_IGN)
94160814Ssimon		(void)signal(SIGHUP, collhup);
95160814Ssimon	savetstp = signal(SIGTSTP, collstop);
96160814Ssimon	savettou = signal(SIGTTOU, collstop);
97160814Ssimon	savettin = signal(SIGTTIN, collstop);
98160814Ssimon	if (setjmp(collabort) || setjmp(colljmp)) {
99160814Ssimon		(void)rm(tempname);
100160814Ssimon		goto err;
101160814Ssimon	}
102160814Ssimon	(void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
103160814Ssimon
104160814Ssimon	noreset++;
105160814Ssimon	(void)snprintf(tempname, sizeof(tempname),
106160814Ssimon	    "%s/mail.RsXXXXXXXXXX", tmpdir);
107160814Ssimon	if ((fd = mkstemp(tempname)) == -1 ||
108160814Ssimon	    (collf = Fdopen(fd, "w+")) == NULL) {
109160814Ssimon		warn("%s", tempname);
110160814Ssimon		goto err;
111160814Ssimon	}
112160814Ssimon	(void)rm(tempname);
113160814Ssimon
114160814Ssimon	/*
115160814Ssimon	 * If we are going to prompt for a subject,
116160814Ssimon	 * refrain from printing a newline after
117160814Ssimon	 * the headers (since some people mind).
118160814Ssimon	 */
119160814Ssimon	t = GTO|GSUBJECT|GCC|GNL;
120160814Ssimon	getsub = 0;
121160814Ssimon	if (hp->h_subject == NULL && value("interactive") != NULL &&
122160814Ssimon	    (value("ask") != NULL || value("asksub") != NULL))
123160814Ssimon		t &= ~GNL, getsub++;
124160814Ssimon	if (printheaders) {
125160814Ssimon		puthead(hp, stdout, t);
126215697Ssimon		(void)fflush(stdout);
127160814Ssimon	}
128215697Ssimon	if ((cp = value("escape")) != NULL)
129215697Ssimon		escape = *cp;
130215697Ssimon	else
131215697Ssimon		escape = ESCAPE;
132215697Ssimon	eofcount = 0;
133215697Ssimon	hadintr = 0;
134215697Ssimon	lastlong = 0;
135215697Ssimon	longline = 0;
136215697Ssimon
137215697Ssimon	if (!setjmp(colljmp)) {
138215697Ssimon		if (getsub)
139215697Ssimon			grabh(hp, GSUBJECT);
140215697Ssimon	} else {
141215697Ssimon		/*
142215697Ssimon		 * Come here for printing the after-signal message.
143215697Ssimon		 * Duplicate messages won't be printed because
144215697Ssimon		 * the write is aborted if we get a SIGTTOU.
145215697Ssimon		 */
146215697Ssimoncont:
147215697Ssimon		if (hadintr) {
148215697Ssimon			(void)fflush(stdout);
149215697Ssimon			fprintf(stderr,
150215697Ssimon			"\n(Interrupt -- one more to kill letter)\n");
151215697Ssimon		} else {
152215697Ssimon			printf("(continue)\n");
153215697Ssimon			(void)fflush(stdout);
154215697Ssimon		}
155215697Ssimon	}
156237657Sjkim	for (;;) {
157215697Ssimon		colljmp_p = 1;
158160814Ssimon		c = readline(stdin, linebuf, LINESIZE);
159279264Sdelphij		colljmp_p = 0;
160160814Ssimon		if (c < 0) {
161160814Ssimon			if (value("interactive") != NULL &&
162160814Ssimon			    value("ignoreeof") != NULL && ++eofcount < 25) {
163160814Ssimon				printf("Use \".\" to terminate letter\n");
164160814Ssimon				continue;
165160814Ssimon			}
166160814Ssimon			break;
167160814Ssimon		}
168160814Ssimon		lastlong = longline;
169172767Ssimon		longline = c == LINESIZE - 1;
170160814Ssimon		eofcount = 0;
171160814Ssimon		hadintr = 0;
172215697Ssimon		if (linebuf[0] == '.' && linebuf[1] == '\0' &&
173172767Ssimon		    value("interactive") != NULL && !lastlong &&
174172767Ssimon		    (value("dot") != NULL || value("ignoreeof") != NULL))
175172767Ssimon			break;
176215697Ssimon		if (linebuf[0] != escape || value("interactive") == NULL ||
177160814Ssimon		    lastlong) {
178172767Ssimon			if (putline(collf, linebuf, !longline) < 0)
179172767Ssimon				goto err;
180172767Ssimon			continue;
181160814Ssimon		}
182172767Ssimon		c = linebuf[1];
183172767Ssimon		switch (c) {
184172767Ssimon		default:
185172767Ssimon			/*
186172767Ssimon			 * On double escape, just send the single one.
187172767Ssimon			 * Otherwise, it's an error.
188172767Ssimon			 */
189172767Ssimon			if (c == escape) {
190172767Ssimon				if (putline(collf, &linebuf[1], !longline) < 0)
191160814Ssimon					goto err;
192172767Ssimon				else
193172767Ssimon					break;
194160814Ssimon			}
195215697Ssimon			printf("Unknown tilde escape.\n");
196215697Ssimon			break;
197215697Ssimon		case 'C':
198215697Ssimon			/*
199215697Ssimon			 * Dump core.
200215697Ssimon			 */
201215697Ssimon			core();
202215697Ssimon			break;
203215697Ssimon		case '!':
204215697Ssimon			/*
205215697Ssimon			 * Shell escape, send the balance of the
206215697Ssimon			 * line to sh -c.
207215697Ssimon			 */
208215697Ssimon			shell(&linebuf[2]);
209215697Ssimon			break;
210172767Ssimon		case ':':
211172767Ssimon		case '_':
212172767Ssimon			/*
213279264Sdelphij			 * Escape to command mode, but be nice!
214172767Ssimon			 */
215260405Sdelphij			execute(&linebuf[2], 1);
216260405Sdelphij			goto cont;
217260405Sdelphij		case '.':
218260405Sdelphij			/*
219260405Sdelphij			 * Simulate end of file on input.
220260405Sdelphij			 */
221172767Ssimon			goto out;
222215697Ssimon		case 'q':
223172767Ssimon			/*
224172767Ssimon			 * Force a quit of sending mail.
225160814Ssimon			 * Act like an interrupt happened.
226279264Sdelphij			 */
227279264Sdelphij			hadintr++;
228279264Sdelphij			collint(SIGINT);
229279264Sdelphij			exit(1);
230279264Sdelphij		case 'x':
231279264Sdelphij			/*
232279264Sdelphij			 * Exit, do not save in dead.letter.
233160814Ssimon			 */
234160814Ssimon			goto err;
235279264Sdelphij		case 'h':
236172767Ssimon			/*
237279264Sdelphij			 * Grab a bunch of headers.
238279264Sdelphij			 */
239279264Sdelphij			grabh(hp, GTO|GSUBJECT|GCC|GBCC);
240279264Sdelphij			goto cont;
241160814Ssimon		case 't':
242279264Sdelphij			/*
243279264Sdelphij			 * Add to the To list.
244279264Sdelphij			 */
245279264Sdelphij			hp->h_to = cat(hp->h_to, extract(&linebuf[2], GTO));
246279264Sdelphij			break;
247279264Sdelphij		case 's':
248279264Sdelphij			/*
249279264Sdelphij			 * Set the Subject line.
250279264Sdelphij			 */
251160814Ssimon			cp = &linebuf[2];
252279264Sdelphij			while (isspace((unsigned char)*cp))
253279264Sdelphij				cp++;
254160814Ssimon			hp->h_subject = savestr(cp);
255279264Sdelphij			break;
256279264Sdelphij		case 'R':
257160814Ssimon			/*
258279264Sdelphij			 * Set the Reply-To line.
259279264Sdelphij			 */
260279264Sdelphij			cp = &linebuf[2];
261279264Sdelphij			while (isspace((unsigned char)*cp))
262279264Sdelphij				cp++;
263279264Sdelphij			hp->h_replyto = savestr(cp);
264279264Sdelphij			break;
265160814Ssimon		case 'c':
266279264Sdelphij			/*
267279264Sdelphij			 * Add to the CC list.
268160814Ssimon			 */
269279264Sdelphij			hp->h_cc = cat(hp->h_cc, extract(&linebuf[2], GCC));
270160814Ssimon			break;
271160814Ssimon		case 'b':
272160814Ssimon			/*
273160814Ssimon			 * Add to the BCC list.
274160814Ssimon			 */
275205128Ssimon			hp->h_bcc = cat(hp->h_bcc, extract(&linebuf[2], GBCC));
276238405Sjkim			break;
277205128Ssimon		case 'i':
278205128Ssimon		case 'A':
279205128Ssimon		case 'a':
280205128Ssimon			/*
281205128Ssimon			 * Insert named variable in message.
282205128Ssimon			 */
283205128Ssimon			switch(c) {
284205128Ssimon				case 'i':
285205128Ssimon					cp = &linebuf[2];
286160814Ssimon					while(isspace((unsigned char)*cp))
287279264Sdelphij						cp++;
288279264Sdelphij					break;
289160814Ssimon				case 'a':
290279264Sdelphij					cp = "sign";
291279264Sdelphij					break;
292279264Sdelphij				case 'A':
293279264Sdelphij					cp = "Sign";
294279264Sdelphij					break;
295279264Sdelphij				default:
296160814Ssimon					goto err;
297160814Ssimon			}
298160814Ssimon
299160814Ssimon			if(*cp != '\0' && (cp = value(cp)) != NULL) {
300160814Ssimon				printf("%s\n", cp);
301160814Ssimon				if(putline(collf, cp, 1) < 0)
302160814Ssimon					goto err;
303279264Sdelphij			}
304279264Sdelphij
305279264Sdelphij			break;
306279264Sdelphij		case 'd':
307279264Sdelphij			/*
308279264Sdelphij			 * Read in the dead letter file.
309279264Sdelphij			 */
310279264Sdelphij			if (strlcpy(linebuf + 2, getdeadletter(),
311279264Sdelphij				sizeof(linebuf) - 2)
312279264Sdelphij			    >= sizeof(linebuf) - 2) {
313160814Ssimon				printf("Line buffer overflow\n");
314160814Ssimon				break;
315279264Sdelphij			}
316279264Sdelphij			/* FALLTHROUGH */
317160814Ssimon		case 'r':
318160814Ssimon		case '<':
319160814Ssimon			/*
320160814Ssimon			 * Invoke a file:
321279264Sdelphij			 * Search for the file name,
322279264Sdelphij			 * then open it and copy the contents to collf.
323279264Sdelphij			 */
324160814Ssimon			cp = &linebuf[2];
325160814Ssimon			while (isspace((unsigned char)*cp))
326160814Ssimon				cp++;
327160814Ssimon			if (*cp == '\0') {
328160814Ssimon				printf("Interpolate what file?\n");
329160814Ssimon				break;
330160814Ssimon			}
331160814Ssimon			cp = expand(cp);
332160814Ssimon			if (cp == NULL)
333160814Ssimon				break;
334279264Sdelphij			if (*cp == '!') {
335279264Sdelphij				/*
336279264Sdelphij				 * Insert stdout of command.
337279264Sdelphij				 */
338279264Sdelphij				char *sh;
339160814Ssimon				int nullfd, tempfd, rc;
340172767Ssimon				char tempname2[PATHSIZE];
341279264Sdelphij
342279264Sdelphij				if ((nullfd = open("/dev/null", O_RDONLY, 0))
343279264Sdelphij				    == -1) {
344279264Sdelphij					warn("/dev/null");
345279264Sdelphij					break;
346279264Sdelphij				}
347279264Sdelphij
348279264Sdelphij				(void)snprintf(tempname2, sizeof(tempname2),
349279264Sdelphij				    "%s/mail.ReXXXXXXXXXX", tmpdir);
350279264Sdelphij				if ((tempfd = mkstemp(tempname2)) == -1 ||
351279264Sdelphij				    (fbuf = Fdopen(tempfd, "w+")) == NULL) {
352279264Sdelphij					warn("%s", tempname2);
353160814Ssimon					break;
354160814Ssimon				}
355160814Ssimon				(void)unlink(tempname2);
356160814Ssimon
357160814Ssimon				if ((sh = value("SHELL")) == NULL)
358160814Ssimon					sh = _PATH_CSHELL;
359160814Ssimon
360160814Ssimon				rc = run_command(sh, 0, nullfd, fileno(fbuf),
361160814Ssimon				    "-c", cp+1, NULL);
362160814Ssimon
363160814Ssimon				close(nullfd);
364160814Ssimon
365160814Ssimon				if (rc < 0) {
366160814Ssimon					(void)Fclose(fbuf);
367160814Ssimon					break;
368160814Ssimon				}
369279264Sdelphij
370205128Ssimon				if (fsize(fbuf) == 0) {
371279264Sdelphij					fprintf(stderr,
372279264Sdelphij					    "No bytes from command \"%s\"\n",
373279264Sdelphij					    cp+1);
374279264Sdelphij					(void)Fclose(fbuf);
375279264Sdelphij					break;
376279264Sdelphij				}
377279264Sdelphij
378279264Sdelphij				rewind(fbuf);
379279264Sdelphij			} else if (isdir(cp)) {
380279264Sdelphij				printf("%s: Directory\n", cp);
381279264Sdelphij				break;
382160814Ssimon			} else if ((fbuf = Fopen(cp, "r")) == NULL) {
383279264Sdelphij				warn("%s", cp);
384160814Ssimon				break;
385279264Sdelphij			}
386160814Ssimon			printf("\"%s\" ", cp);
387160814Ssimon			(void)fflush(stdout);
388160814Ssimon			lc = 0;
389172767Ssimon			cc = 0;
390160814Ssimon			while ((rc = readline(fbuf, linebuf, LINESIZE)) >= 0) {
391160814Ssimon				if (rc != LINESIZE - 1)
392172767Ssimon					lc++;
393172767Ssimon				if ((t = putline(collf, linebuf,
394160814Ssimon					 rc != LINESIZE - 1)) < 0) {
395172767Ssimon					(void)Fclose(fbuf);
396160814Ssimon					goto err;
397160814Ssimon				}
398194206Ssimon				cc += t;
399172767Ssimon			}
400194206Ssimon			(void)Fclose(fbuf);
401172767Ssimon			printf("%d/%d\n", lc, cc);
402238405Sjkim			break;
403172767Ssimon		case 'w':
404172767Ssimon			/*
405172767Ssimon			 * Write the message on a file.
406172767Ssimon			 */
407172767Ssimon			cp = &linebuf[2];
408172767Ssimon			while (*cp == ' ' || *cp == '\t')
409172767Ssimon				cp++;
410172767Ssimon			if (*cp == '\0') {
411172767Ssimon				fprintf(stderr, "Write what file!?\n");
412194206Ssimon				break;
413172767Ssimon			}
414172767Ssimon			if ((cp = expand(cp)) == NULL)
415172767Ssimon				break;
416172767Ssimon			rewind(collf);
417194206Ssimon			exwrite(cp, collf, 1);
418172767Ssimon			break;
419172767Ssimon		case 'm':
420194206Ssimon		case 'M':
421172767Ssimon		case 'f':
422172767Ssimon		case 'F':
423160814Ssimon			/*
424160814Ssimon			 * Interpolate the named messages, if we
425160814Ssimon			 * are in receiving mail mode.  Does the
426160814Ssimon			 * standard list processing garbage.
427160814Ssimon			 * If ~f is given, we don't shift over.
428160814Ssimon			 */
429160814Ssimon			if (forward(linebuf + 2, collf, tempname, c) < 0)
430160814Ssimon				goto err;
431160814Ssimon			goto cont;
432172767Ssimon		case '?':
433160814Ssimon			if ((fbuf = Fopen(_PATH_TILDE, "r")) == NULL) {
434160814Ssimon				warn("%s", _PATH_TILDE);
435160814Ssimon				break;
436160814Ssimon			}
437160814Ssimon			while ((t = getc(fbuf)) != EOF)
438160814Ssimon				(void)putchar(t);
439160814Ssimon			(void)Fclose(fbuf);
440160814Ssimon			break;
441160814Ssimon		case 'p':
442160814Ssimon			/*
443160814Ssimon			 * Print out the current state of the
444160814Ssimon			 * message without altering anything.
445160814Ssimon			 */
446160814Ssimon			rewind(collf);
447160814Ssimon			printf("-------\nMessage contains:\n");
448160814Ssimon			puthead(hp, stdout, GTO|GSUBJECT|GCC|GBCC|GNL);
449160814Ssimon			while ((t = getc(collf)) != EOF)
450160814Ssimon				(void)putchar(t);
451160814Ssimon			goto cont;
452172767Ssimon		case '|':
453215697Ssimon			/*
454215697Ssimon			 * Pipe message through command.
455160814Ssimon			 * Collect output as new message.
456160814Ssimon			 */
457160814Ssimon			rewind(collf);
458160814Ssimon			mespipe(collf, &linebuf[2]);
459160814Ssimon			goto cont;
460160814Ssimon		case 'v':
461160814Ssimon		case 'e':
462160814Ssimon			/*
463160814Ssimon			 * Edit the current message.
464160814Ssimon			 * 'e' means to use EDITOR
465160814Ssimon			 * 'v' means to use VISUAL
466160814Ssimon			 */
467160814Ssimon			rewind(collf);
468160814Ssimon			mesedit(collf, c);
469160814Ssimon			goto cont;
470160814Ssimon		}
471160814Ssimon	}
472172767Ssimon	goto out;
473172767Ssimonerr:
474215697Ssimon	if (collf != NULL) {
475160814Ssimon		(void)Fclose(collf);
476215697Ssimon		collf = NULL;
477215697Ssimon	}
478215697Ssimonout:
479215697Ssimon	if (collf != NULL)
480215697Ssimon		rewind(collf);
481215697Ssimon	noreset--;
482215697Ssimon	(void)sigprocmask(SIG_BLOCK, &nset, NULL);
483160814Ssimon	(void)signal(SIGINT, saveint);
484215697Ssimon	(void)signal(SIGHUP, savehup);
485215697Ssimon	(void)signal(SIGTSTP, savetstp);
486160814Ssimon	(void)signal(SIGTTOU, savettou);
487215697Ssimon	(void)signal(SIGTTIN, savettin);
488215697Ssimon	(void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
489215697Ssimon	return (collf);
490215697Ssimon}
491215697Ssimon
492215697Ssimon/*
493215697Ssimon * Write a file, ex-like if f set.
494215697Ssimon */
495215697Ssimonint
496215697Ssimonexwrite(char name[], FILE *fp, int f)
497172767Ssimon{
498215697Ssimon	FILE *of;
499215697Ssimon	int c, lc;
500215697Ssimon	long cc;
501215697Ssimon	struct stat junk;
502215697Ssimon
503172767Ssimon	if (f) {
504215697Ssimon		printf("\"%s\" ", name);
505172767Ssimon		(void)fflush(stdout);
506237657Sjkim	}
507237657Sjkim	if (stat(name, &junk) >= 0 && S_ISREG(junk.st_mode)) {
508237657Sjkim		if (!f)
509160814Ssimon			fprintf(stderr, "%s: ", name);
510215697Ssimon		fprintf(stderr, "File exists\n");
511215697Ssimon		return (-1);
512160814Ssimon	}
513160814Ssimon	if ((of = Fopen(name, "w")) == NULL) {
514160814Ssimon		warn((char *)NULL);
515160814Ssimon		return (-1);
516160814Ssimon	}
517160814Ssimon	lc = 0;
518160814Ssimon	cc = 0;
519160814Ssimon	while ((c = getc(fp)) != EOF) {
520172767Ssimon		cc++;
521172767Ssimon		if (c == '\n')
522172767Ssimon			lc++;
523160814Ssimon		(void)putc(c, of);
524172767Ssimon		if (ferror(of)) {
525172767Ssimon			warnx("%s", name);
526172767Ssimon			(void)Fclose(of);
527160814Ssimon			return (-1);
528172767Ssimon		}
529172767Ssimon	}
530172767Ssimon	(void)Fclose(of);
531172767Ssimon	printf("%d/%ld\n", lc, cc);
532172767Ssimon	(void)fflush(stdout);
533172767Ssimon	return (0);
534160814Ssimon}
535172767Ssimon
536172767Ssimon/*
537172767Ssimon * Edit the message being collected on fp.
538172767Ssimon * On return, make the edit file the new temp file.
539172767Ssimon */
540160814Ssimonvoid
541172767Ssimonmesedit(FILE *fp, int c)
542172767Ssimon{
543172767Ssimon	sig_t sigint = signal(SIGINT, SIG_IGN);
544172767Ssimon	FILE *nf = run_editor(fp, (off_t)-1, c, 0);
545238405Sjkim
546172767Ssimon	if (nf != NULL) {
547172767Ssimon		(void)fseeko(nf, (off_t)0, SEEK_END);
548172767Ssimon		collf = nf;
549172767Ssimon		(void)Fclose(fp);
550160814Ssimon	}
551172767Ssimon	(void)signal(SIGINT, sigint);
552172767Ssimon}
553172767Ssimon
554172767Ssimon/*
555172767Ssimon * Pipe the message through the command.
556172767Ssimon * Old message is on stdin of command;
557172767Ssimon * New message collected from stdout.
558172767Ssimon * Sh -c must return 0 to accept the new message.
559172767Ssimon */
560172767Ssimonvoid
561172767Ssimonmespipe(FILE *fp, char cmd[])
562172767Ssimon{
563172767Ssimon	FILE *nf;
564160814Ssimon	int fd;
565172767Ssimon	sig_t sigint = signal(SIGINT, SIG_IGN);
566172767Ssimon	char *sh, tempname[PATHSIZE];
567160814Ssimon
568172767Ssimon	(void)snprintf(tempname, sizeof(tempname),
569160814Ssimon	    "%s/mail.ReXXXXXXXXXX", tmpdir);
570172767Ssimon	if ((fd = mkstemp(tempname)) == -1 ||
571172767Ssimon	    (nf = Fdopen(fd, "w+")) == NULL) {
572172767Ssimon		warn("%s", tempname);
573172767Ssimon		goto out;
574172767Ssimon	}
575172767Ssimon	(void)rm(tempname);
576172767Ssimon	/*
577172767Ssimon	 * stdin = current message.
578172767Ssimon	 * stdout = new message.
579172767Ssimon	 */
580160814Ssimon	if ((sh = value("SHELL")) == NULL)
581172767Ssimon		sh = _PATH_CSHELL;
582172767Ssimon	if (run_command(sh,
583172767Ssimon	    0, fileno(fp), fileno(nf), "-c", cmd, NULL) < 0) {
584172767Ssimon		(void)Fclose(nf);
585160814Ssimon		goto out;
586172767Ssimon	}
587215697Ssimon	if (fsize(nf) == 0) {
588215697Ssimon		fprintf(stderr, "No bytes from \"%s\" !?\n", cmd);
589215697Ssimon		(void)Fclose(nf);
590215697Ssimon		goto out;
591160814Ssimon	}
592172767Ssimon	/*
593172767Ssimon	 * Take new files.
594196474Ssimon	 */
595172767Ssimon	(void)fseeko(nf, (off_t)0, SEEK_END);
596160814Ssimon	collf = nf;
597172767Ssimon	(void)Fclose(fp);
598160814Ssimonout:
599172767Ssimon	(void)signal(SIGINT, sigint);
600172767Ssimon}
601194206Ssimon
602172767Ssimon/*
603172767Ssimon * Interpolate the named messages into the current
604172767Ssimon * message, preceding each line with a tab.
605160814Ssimon * Return a count of the number of characters now in
606172767Ssimon * the message, or -1 if an error is encountered writing
607172767Ssimon * the message temporary.  The flag argument is 'm' if we
608160814Ssimon * should shift over and 'f' if not.
609172767Ssimon */
610172767Ssimonint
611172767Ssimonforward(char ms[], FILE *fp, char *fn, int f)
612196474Ssimon{
613172767Ssimon	int *msgvec;
614160814Ssimon	struct ignoretab *ig;
615172767Ssimon	char *tabst;
616172767Ssimon
617172767Ssimon	msgvec = (int *)salloc((msgCount+1) * sizeof(*msgvec));
618172767Ssimon	if (msgvec == NULL)
619172767Ssimon		return (0);
620172767Ssimon	if (getmsglist(ms, msgvec, 0) < 0)
621172767Ssimon		return (0);
622172767Ssimon	if (*msgvec == 0) {
623160814Ssimon		*msgvec = first(0, MMNORM);
624271304Sdelphij		if (*msgvec == 0) {
625271304Sdelphij			printf("No appropriate messages\n");
626271304Sdelphij			return (0);
627271304Sdelphij		}
628271304Sdelphij		msgvec[1] = 0;
629271304Sdelphij	}
630271304Sdelphij	if (f == 'f' || f == 'F')
631271304Sdelphij		tabst = NULL;
632271304Sdelphij	else if ((tabst = value("indentprefix")) == NULL)
633271304Sdelphij		tabst = "\t";
634160814Ssimon	ig = isupper((unsigned char)f) ? NULL : ignore;
635172767Ssimon	printf("Interpolating:");
636271304Sdelphij	for (; *msgvec != 0; msgvec++) {
637215697Ssimon		struct message *mp = message + *msgvec - 1;
638215697Ssimon
639215697Ssimon		touch(mp);
640215697Ssimon		printf(" %d", *msgvec);
641238405Sjkim		if (sendmessage(mp, fp, ig, tabst) < 0) {
642271304Sdelphij			warnx("%s", fn);
643215697Ssimon			return (-1);
644271304Sdelphij		}
645271304Sdelphij	}
646215697Ssimon	printf("\n");
647215697Ssimon	return (0);
648271304Sdelphij}
649271304Sdelphij
650215697Ssimon/*
651215697Ssimon * Print (continue) when continued after ^Z.
652238405Sjkim */
653238405Sjkim/*ARGSUSED*/
654238405Sjkimvoid
655238405Sjkimcollstop(int s)
656215697Ssimon{
657215697Ssimon	sig_t old_action = signal(s, SIG_DFL);
658215697Ssimon	sigset_t nset;
659215697Ssimon
660215697Ssimon	(void)sigemptyset(&nset);
661215697Ssimon	(void)sigaddset(&nset, s);
662215697Ssimon	(void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
663215697Ssimon	(void)kill(0, s);
664215697Ssimon	(void)sigprocmask(SIG_BLOCK, &nset, NULL);
665215697Ssimon	(void)signal(s, old_action);
666215697Ssimon	if (colljmp_p) {
667267104Sdelphij		colljmp_p = 0;
668215697Ssimon		hadintr = 0;
669267104Sdelphij		longjmp(colljmp, 1);
670267104Sdelphij	}
671267104Sdelphij}
672267104Sdelphij
673267104Sdelphij/*
674267104Sdelphij * On interrupt, come here to save the partial message in ~/dead.letter.
675267104Sdelphij * Then jump out of the collection loop.
676215697Ssimon */
677267104Sdelphij/*ARGSUSED*/
678215697Ssimonvoid
679271304Sdelphijcollint(int s __unused)
680271304Sdelphij{
681215697Ssimon	/*
682215697Ssimon	 * the control flow is subtle, because we can be called from ~q.
683215697Ssimon	 */
684215697Ssimon	if (!hadintr) {
685215697Ssimon		if (value("ignore") != NULL) {
686215697Ssimon			printf("@");
687215697Ssimon			(void)fflush(stdout);
688215697Ssimon			clearerr(stdin);
689215697Ssimon			return;
690215697Ssimon		}
691215697Ssimon		hadintr = 1;
692215697Ssimon		longjmp(colljmp, 1);
693215697Ssimon	}
694215697Ssimon	rewind(collf);
695215697Ssimon	if (value("nosave") == NULL)
696215697Ssimon		savedeadletter(collf);
697215697Ssimon	longjmp(collabort, 1);
698215697Ssimon}
699215697Ssimon
700271304Sdelphij/*ARGSUSED*/
701271304Sdelphijvoid
702271304Sdelphijcollhup(int s __unused)
703215697Ssimon{
704215697Ssimon	rewind(collf);
705215697Ssimon	savedeadletter(collf);
706215697Ssimon	/*
707215697Ssimon	 * Let's pretend nobody else wants to clean up,
708215697Ssimon	 * a true statement at this time.
709215697Ssimon	 */
710215697Ssimon	exit(1);
711215697Ssimon}
712215697Ssimon
713215697Ssimonvoid
714215697Ssimonsavedeadletter(FILE *fp)
715215697Ssimon{
716215697Ssimon	FILE *dbuf;
717215697Ssimon	int c;
718215697Ssimon	char *cp;
719238405Sjkim
720215697Ssimon	if (fsize(fp) == 0)
721215697Ssimon		return;
722279264Sdelphij	cp = getdeadletter();
723215697Ssimon	c = umask(077);
724215697Ssimon	dbuf = Fopen(cp, "a");
725215697Ssimon	(void)umask(c);
726271304Sdelphij	if (dbuf == NULL)
727271304Sdelphij		return;
728271304Sdelphij	while ((c = getc(fp)) != EOF)
729271304Sdelphij		(void)putc(c, dbuf);
730271304Sdelphij	(void)Fclose(dbuf);
731271304Sdelphij	rewind(fp);
732215697Ssimon}
733215697Ssimon