11590Srgrimes/*
21590Srgrimes * Copyright (c) 1980, 1993
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * Redistribution and use in source and binary forms, with or without
61590Srgrimes * modification, are permitted provided that the following conditions
71590Srgrimes * are met:
81590Srgrimes * 1. Redistributions of source code must retain the above copyright
91590Srgrimes *    notice, this list of conditions and the following disclaimer.
101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111590Srgrimes *    notice, this list of conditions and the following disclaimer in the
121590Srgrimes *    documentation and/or other materials provided with the distribution.
131590Srgrimes * 4. Neither the name of the University nor the names of its contributors
141590Srgrimes *    may be used to endorse or promote products derived from this software
151590Srgrimes *    without specific prior written permission.
161590Srgrimes *
171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271590Srgrimes * SUCH DAMAGE.
281590Srgrimes */
291590Srgrimes
301590Srgrimes#ifndef lint
3174769Smikeh#if 0
321590Srgrimesstatic char sccsid[] = "@(#)collect.c	8.2 (Berkeley) 4/19/94";
3374769Smikeh#endif
341590Srgrimes#endif /* not lint */
3599112Sobrien#include <sys/cdefs.h>
3699112Sobrien__FBSDID("$FreeBSD$");
371590Srgrimes
381590Srgrimes/*
391590Srgrimes * Mail -- a mail program
401590Srgrimes *
411590Srgrimes * Collect input from standard input, handling
421590Srgrimes * ~ escapes.
431590Srgrimes */
441590Srgrimes
451590Srgrimes#include "rcv.h"
4688428Smikeh#include <fcntl.h>
471590Srgrimes#include "extern.h"
481590Srgrimes
491590Srgrimes/*
501590Srgrimes * Read a message from standard output and return a read file to it
511590Srgrimes * or NULL on error.
521590Srgrimes */
531590Srgrimes
541590Srgrimes/*
551590Srgrimes * The following hokiness with global variables is so that on
561590Srgrimes * receipt of an interrupt signal, the partial message can be salted
571590Srgrimes * away on dead.letter.
581590Srgrimes */
591590Srgrimes
601590Srgrimesstatic	sig_t	saveint;		/* Previous SIGINT value */
611590Srgrimesstatic	sig_t	savehup;		/* Previous SIGHUP value */
621590Srgrimesstatic	sig_t	savetstp;		/* Previous SIGTSTP value */
631590Srgrimesstatic	sig_t	savettou;		/* Previous SIGTTOU value */
641590Srgrimesstatic	sig_t	savettin;		/* Previous SIGTTIN value */
651590Srgrimesstatic	FILE	*collf;			/* File for saving away */
661590Srgrimesstatic	int	hadintr;		/* Have seen one SIGINT so far */
671590Srgrimes
681590Srgrimesstatic	jmp_buf	colljmp;		/* To get back to work */
691590Srgrimesstatic	int	colljmp_p;		/* whether to long jump */
701590Srgrimesstatic	jmp_buf	collabort;		/* To end collection with error */
711590Srgrimes
721590SrgrimesFILE *
73216564Scharniercollect(struct header *hp, int printheaders)
741590Srgrimes{
751590Srgrimes	FILE *fbuf;
7677274Smikeh	int lc, cc, escape, eofcount, fd, c, t;
7777274Smikeh	char linebuf[LINESIZE], tempname[PATHSIZE], *cp, getsub;
7888150Smikeh	sigset_t nset;
7988150Smikeh	int longline, lastlong, rc;	/* So we don't make 2 or more lines
8088150Smikeh					   out of a long input line. */
811590Srgrimes
821590Srgrimes	collf = NULL;
831590Srgrimes	/*
841590Srgrimes	 * Start catching signals from here, but we're still die on interrupts
851590Srgrimes	 * until we're in the main loop.
861590Srgrimes	 */
8788150Smikeh	(void)sigemptyset(&nset);
8888150Smikeh	(void)sigaddset(&nset, SIGINT);
8988150Smikeh	(void)sigaddset(&nset, SIGHUP);
9088150Smikeh	(void)sigprocmask(SIG_BLOCK, &nset, NULL);
911590Srgrimes	if ((saveint = signal(SIGINT, SIG_IGN)) != SIG_IGN)
9277274Smikeh		(void)signal(SIGINT, collint);
931590Srgrimes	if ((savehup = signal(SIGHUP, SIG_IGN)) != SIG_IGN)
9477274Smikeh		(void)signal(SIGHUP, collhup);
951590Srgrimes	savetstp = signal(SIGTSTP, collstop);
961590Srgrimes	savettou = signal(SIGTTOU, collstop);
971590Srgrimes	savettin = signal(SIGTTIN, collstop);
981590Srgrimes	if (setjmp(collabort) || setjmp(colljmp)) {
9977274Smikeh		(void)rm(tempname);
1001590Srgrimes		goto err;
1011590Srgrimes	}
10288150Smikeh	(void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
1031590Srgrimes
1041590Srgrimes	noreset++;
10577274Smikeh	(void)snprintf(tempname, sizeof(tempname),
10677274Smikeh	    "%s/mail.RsXXXXXXXXXX", tmpdir);
10774769Smikeh	if ((fd = mkstemp(tempname)) == -1 ||
10874769Smikeh	    (collf = Fdopen(fd, "w+")) == NULL) {
10974769Smikeh		warn("%s", tempname);
1101590Srgrimes		goto err;
1111590Srgrimes	}
11277274Smikeh	(void)rm(tempname);
1131590Srgrimes
1141590Srgrimes	/*
1151590Srgrimes	 * If we are going to prompt for a subject,
1161590Srgrimes	 * refrain from printing a newline after
1171590Srgrimes	 * the headers (since some people mind).
1181590Srgrimes	 */
1191590Srgrimes	t = GTO|GSUBJECT|GCC|GNL;
1201590Srgrimes	getsub = 0;
12177274Smikeh	if (hp->h_subject == NULL && value("interactive") != NULL &&
12277274Smikeh	    (value("ask") != NULL || value("asksub") != NULL))
1231590Srgrimes		t &= ~GNL, getsub++;
1241590Srgrimes	if (printheaders) {
1251590Srgrimes		puthead(hp, stdout, t);
12677274Smikeh		(void)fflush(stdout);
1271590Srgrimes	}
12877274Smikeh	if ((cp = value("escape")) != NULL)
1291590Srgrimes		escape = *cp;
1301590Srgrimes	else
1311590Srgrimes		escape = ESCAPE;
1321590Srgrimes	eofcount = 0;
1331590Srgrimes	hadintr = 0;
13488150Smikeh	lastlong = 0;
13588150Smikeh	longline = 0;
1361590Srgrimes
1371590Srgrimes	if (!setjmp(colljmp)) {
1381590Srgrimes		if (getsub)
1391590Srgrimes			grabh(hp, GSUBJECT);
1401590Srgrimes	} else {
1411590Srgrimes		/*
1421590Srgrimes		 * Come here for printing the after-signal message.
1431590Srgrimes		 * Duplicate messages won't be printed because
1441590Srgrimes		 * the write is aborted if we get a SIGTTOU.
1451590Srgrimes		 */
1461590Srgrimescont:
1471590Srgrimes		if (hadintr) {
14877274Smikeh			(void)fflush(stdout);
1491590Srgrimes			fprintf(stderr,
1501590Srgrimes			"\n(Interrupt -- one more to kill letter)\n");
1511590Srgrimes		} else {
1521590Srgrimes			printf("(continue)\n");
15377274Smikeh			(void)fflush(stdout);
1541590Srgrimes		}
1551590Srgrimes	}
1561590Srgrimes	for (;;) {
1571590Srgrimes		colljmp_p = 1;
1581590Srgrimes		c = readline(stdin, linebuf, LINESIZE);
1591590Srgrimes		colljmp_p = 0;
1601590Srgrimes		if (c < 0) {
16177274Smikeh			if (value("interactive") != NULL &&
16277274Smikeh			    value("ignoreeof") != NULL && ++eofcount < 25) {
1631590Srgrimes				printf("Use \".\" to terminate letter\n");
1641590Srgrimes				continue;
1651590Srgrimes			}
1661590Srgrimes			break;
1671590Srgrimes		}
16888150Smikeh		lastlong = longline;
16988150Smikeh		longline = c == LINESIZE - 1;
1701590Srgrimes		eofcount = 0;
1711590Srgrimes		hadintr = 0;
1721590Srgrimes		if (linebuf[0] == '.' && linebuf[1] == '\0' &&
17388150Smikeh		    value("interactive") != NULL && !lastlong &&
17477274Smikeh		    (value("dot") != NULL || value("ignoreeof") != NULL))
1751590Srgrimes			break;
17688150Smikeh		if (linebuf[0] != escape || value("interactive") == NULL ||
17788150Smikeh		    lastlong) {
17888150Smikeh			if (putline(collf, linebuf, !longline) < 0)
1791590Srgrimes				goto err;
1801590Srgrimes			continue;
1811590Srgrimes		}
1821590Srgrimes		c = linebuf[1];
1831590Srgrimes		switch (c) {
1841590Srgrimes		default:
1851590Srgrimes			/*
1861590Srgrimes			 * On double escape, just send the single one.
1871590Srgrimes			 * Otherwise, it's an error.
1881590Srgrimes			 */
1891590Srgrimes			if (c == escape) {
19088150Smikeh				if (putline(collf, &linebuf[1], !longline) < 0)
1911590Srgrimes					goto err;
1921590Srgrimes				else
1931590Srgrimes					break;
1941590Srgrimes			}
1951590Srgrimes			printf("Unknown tilde escape.\n");
1961590Srgrimes			break;
1971590Srgrimes		case 'C':
1981590Srgrimes			/*
1991590Srgrimes			 * Dump core.
2001590Srgrimes			 */
2011590Srgrimes			core();
2021590Srgrimes			break;
2031590Srgrimes		case '!':
2041590Srgrimes			/*
2051590Srgrimes			 * Shell escape, send the balance of the
2061590Srgrimes			 * line to sh -c.
2071590Srgrimes			 */
2081590Srgrimes			shell(&linebuf[2]);
2091590Srgrimes			break;
2101590Srgrimes		case ':':
21188428Smikeh		case '_':
2121590Srgrimes			/*
2131590Srgrimes			 * Escape to command mode, but be nice!
2141590Srgrimes			 */
2151590Srgrimes			execute(&linebuf[2], 1);
2161590Srgrimes			goto cont;
2171590Srgrimes		case '.':
2181590Srgrimes			/*
2191590Srgrimes			 * Simulate end of file on input.
2201590Srgrimes			 */
2211590Srgrimes			goto out;
2221590Srgrimes		case 'q':
2231590Srgrimes			/*
2241590Srgrimes			 * Force a quit of sending mail.
2251590Srgrimes			 * Act like an interrupt happened.
2261590Srgrimes			 */
2271590Srgrimes			hadintr++;
2281590Srgrimes			collint(SIGINT);
2291590Srgrimes			exit(1);
23088428Smikeh		case 'x':
23188428Smikeh			/*
23288428Smikeh			 * Exit, do not save in dead.letter.
23388428Smikeh			 */
23488428Smikeh			goto err;
2351590Srgrimes		case 'h':
2361590Srgrimes			/*
2371590Srgrimes			 * Grab a bunch of headers.
2381590Srgrimes			 */
2391590Srgrimes			grabh(hp, GTO|GSUBJECT|GCC|GBCC);
2401590Srgrimes			goto cont;
2411590Srgrimes		case 't':
2421590Srgrimes			/*
2431590Srgrimes			 * Add to the To list.
2441590Srgrimes			 */
2451590Srgrimes			hp->h_to = cat(hp->h_to, extract(&linebuf[2], GTO));
2461590Srgrimes			break;
2471590Srgrimes		case 's':
2481590Srgrimes			/*
24932189Sjoerg			 * Set the Subject line.
2501590Srgrimes			 */
2511590Srgrimes			cp = &linebuf[2];
25288227Sache			while (isspace((unsigned char)*cp))
2531590Srgrimes				cp++;
2541590Srgrimes			hp->h_subject = savestr(cp);
2551590Srgrimes			break;
25632189Sjoerg		case 'R':
25732189Sjoerg			/*
25832189Sjoerg			 * Set the Reply-To line.
25932189Sjoerg			 */
26032189Sjoerg			cp = &linebuf[2];
26188227Sache			while (isspace((unsigned char)*cp))
26232189Sjoerg				cp++;
26332189Sjoerg			hp->h_replyto = savestr(cp);
26432189Sjoerg			break;
2651590Srgrimes		case 'c':
2661590Srgrimes			/*
2671590Srgrimes			 * Add to the CC list.
2681590Srgrimes			 */
2691590Srgrimes			hp->h_cc = cat(hp->h_cc, extract(&linebuf[2], GCC));
2701590Srgrimes			break;
2711590Srgrimes		case 'b':
2721590Srgrimes			/*
27388428Smikeh			 * Add to the BCC list.
2741590Srgrimes			 */
2751590Srgrimes			hp->h_bcc = cat(hp->h_bcc, extract(&linebuf[2], GBCC));
2761590Srgrimes			break;
27788428Smikeh		case 'i':
27888428Smikeh		case 'A':
27988428Smikeh		case 'a':
28088428Smikeh			/*
28188434Smikeh			 * Insert named variable in message.
28288428Smikeh			 */
28388428Smikeh			switch(c) {
28488428Smikeh				case 'i':
28588428Smikeh					cp = &linebuf[2];
28688428Smikeh					while(isspace((unsigned char)*cp))
28788428Smikeh						cp++;
28888428Smikeh					break;
28988428Smikeh				case 'a':
29088428Smikeh					cp = "sign";
29188428Smikeh					break;
29288428Smikeh				case 'A':
29388428Smikeh					cp = "Sign";
29488428Smikeh					break;
29588428Smikeh				default:
29688428Smikeh					goto err;
29788428Smikeh			}
29888428Smikeh
29988428Smikeh			if(*cp != '\0' && (cp = value(cp)) != NULL) {
30088428Smikeh				printf("%s\n", cp);
30188428Smikeh				if(putline(collf, cp, 1) < 0)
30288428Smikeh					goto err;
30388428Smikeh			}
30488428Smikeh
30588428Smikeh			break;
3061590Srgrimes		case 'd':
30788428Smikeh			/*
30888428Smikeh			 * Read in the dead letter file.
30988428Smikeh			 */
31088428Smikeh			if (strlcpy(linebuf + 2, getdeadletter(),
31188428Smikeh				sizeof(linebuf) - 2)
31274769Smikeh			    >= sizeof(linebuf) - 2) {
31374769Smikeh				printf("Line buffer overflow\n");
31474769Smikeh				break;
31574769Smikeh			}
31688428Smikeh			/* FALLTHROUGH */
3171590Srgrimes		case 'r':
31888428Smikeh		case '<':
3191590Srgrimes			/*
3201590Srgrimes			 * Invoke a file:
3211590Srgrimes			 * Search for the file name,
3221590Srgrimes			 * then open it and copy the contents to collf.
3231590Srgrimes			 */
3241590Srgrimes			cp = &linebuf[2];
32588227Sache			while (isspace((unsigned char)*cp))
3261590Srgrimes				cp++;
3271590Srgrimes			if (*cp == '\0') {
3281590Srgrimes				printf("Interpolate what file?\n");
3291590Srgrimes				break;
3301590Srgrimes			}
3311590Srgrimes			cp = expand(cp);
33277274Smikeh			if (cp == NULL)
3331590Srgrimes				break;
33488428Smikeh			if (*cp == '!') {
33588428Smikeh				/*
33688428Smikeh				 * Insert stdout of command.
33788428Smikeh				 */
33888428Smikeh				char *sh;
33988428Smikeh				int nullfd, tempfd, rc;
34088428Smikeh				char tempname2[PATHSIZE];
34188428Smikeh
34288428Smikeh				if ((nullfd = open("/dev/null", O_RDONLY, 0))
34388428Smikeh				    == -1) {
34488428Smikeh					warn("/dev/null");
34588428Smikeh					break;
34688428Smikeh				}
34788428Smikeh
34888428Smikeh				(void)snprintf(tempname2, sizeof(tempname2),
34988428Smikeh				    "%s/mail.ReXXXXXXXXXX", tmpdir);
35088428Smikeh				if ((tempfd = mkstemp(tempname2)) == -1 ||
35188428Smikeh				    (fbuf = Fdopen(tempfd, "w+")) == NULL) {
35288428Smikeh					warn("%s", tempname2);
35388428Smikeh					break;
35488428Smikeh				}
35588428Smikeh				(void)unlink(tempname2);
35688428Smikeh
35788428Smikeh				if ((sh = value("SHELL")) == NULL)
35888428Smikeh					sh = _PATH_CSHELL;
35988428Smikeh
36088428Smikeh				rc = run_command(sh, 0, nullfd, fileno(fbuf),
36188428Smikeh				    "-c", cp+1, NULL);
36288428Smikeh
36388428Smikeh				close(nullfd);
36488428Smikeh
36588428Smikeh				if (rc < 0) {
36688428Smikeh					(void)Fclose(fbuf);
36788428Smikeh					break;
36888428Smikeh				}
36988428Smikeh
37088428Smikeh				if (fsize(fbuf) == 0) {
37188428Smikeh					fprintf(stderr,
37288428Smikeh					    "No bytes from command \"%s\"\n",
37388428Smikeh					    cp+1);
37488428Smikeh					(void)Fclose(fbuf);
37588428Smikeh					break;
37688428Smikeh				}
37788428Smikeh
37888428Smikeh				rewind(fbuf);
37988428Smikeh			} else if (isdir(cp)) {
3801590Srgrimes				printf("%s: Directory\n", cp);
3811590Srgrimes				break;
38288428Smikeh			} else if ((fbuf = Fopen(cp, "r")) == NULL) {
38374769Smikeh				warn("%s", cp);
3841590Srgrimes				break;
3851590Srgrimes			}
3861590Srgrimes			printf("\"%s\" ", cp);
38777274Smikeh			(void)fflush(stdout);
3881590Srgrimes			lc = 0;
3891590Srgrimes			cc = 0;
39088150Smikeh			while ((rc = readline(fbuf, linebuf, LINESIZE)) >= 0) {
39188150Smikeh				if (rc != LINESIZE - 1)
39288150Smikeh					lc++;
39388150Smikeh				if ((t = putline(collf, linebuf,
39488150Smikeh					 rc != LINESIZE - 1)) < 0) {
39577274Smikeh					(void)Fclose(fbuf);
3961590Srgrimes					goto err;
3971590Srgrimes				}
3981590Srgrimes				cc += t;
3991590Srgrimes			}
40077274Smikeh			(void)Fclose(fbuf);
4011590Srgrimes			printf("%d/%d\n", lc, cc);
4021590Srgrimes			break;
4031590Srgrimes		case 'w':
4041590Srgrimes			/*
4051590Srgrimes			 * Write the message on a file.
4061590Srgrimes			 */
4071590Srgrimes			cp = &linebuf[2];
4081590Srgrimes			while (*cp == ' ' || *cp == '\t')
4091590Srgrimes				cp++;
4101590Srgrimes			if (*cp == '\0') {
4111590Srgrimes				fprintf(stderr, "Write what file!?\n");
4121590Srgrimes				break;
4131590Srgrimes			}
41477274Smikeh			if ((cp = expand(cp)) == NULL)
4151590Srgrimes				break;
4161590Srgrimes			rewind(collf);
4171590Srgrimes			exwrite(cp, collf, 1);
4181590Srgrimes			break;
4191590Srgrimes		case 'm':
4201590Srgrimes		case 'M':
4211590Srgrimes		case 'f':
4221590Srgrimes		case 'F':
4231590Srgrimes			/*
4241590Srgrimes			 * Interpolate the named messages, if we
4251590Srgrimes			 * are in receiving mail mode.  Does the
4261590Srgrimes			 * standard list processing garbage.
4271590Srgrimes			 * If ~f is given, we don't shift over.
4281590Srgrimes			 */
42974769Smikeh			if (forward(linebuf + 2, collf, tempname, c) < 0)
4301590Srgrimes				goto err;
4311590Srgrimes			goto cont;
4321590Srgrimes		case '?':
4331590Srgrimes			if ((fbuf = Fopen(_PATH_TILDE, "r")) == NULL) {
43474769Smikeh				warn("%s", _PATH_TILDE);
4351590Srgrimes				break;
4361590Srgrimes			}
4371590Srgrimes			while ((t = getc(fbuf)) != EOF)
43877274Smikeh				(void)putchar(t);
43977274Smikeh			(void)Fclose(fbuf);
4401590Srgrimes			break;
4411590Srgrimes		case 'p':
4421590Srgrimes			/*
4431590Srgrimes			 * Print out the current state of the
4441590Srgrimes			 * message without altering anything.
4451590Srgrimes			 */
4461590Srgrimes			rewind(collf);
4471590Srgrimes			printf("-------\nMessage contains:\n");
4481590Srgrimes			puthead(hp, stdout, GTO|GSUBJECT|GCC|GBCC|GNL);
4491590Srgrimes			while ((t = getc(collf)) != EOF)
45077274Smikeh				(void)putchar(t);
4511590Srgrimes			goto cont;
4521590Srgrimes		case '|':
4531590Srgrimes			/*
4541590Srgrimes			 * Pipe message through command.
4551590Srgrimes			 * Collect output as new message.
4561590Srgrimes			 */
4571590Srgrimes			rewind(collf);
4581590Srgrimes			mespipe(collf, &linebuf[2]);
4591590Srgrimes			goto cont;
4601590Srgrimes		case 'v':
4611590Srgrimes		case 'e':
4621590Srgrimes			/*
4631590Srgrimes			 * Edit the current message.
4641590Srgrimes			 * 'e' means to use EDITOR
4651590Srgrimes			 * 'v' means to use VISUAL
4661590Srgrimes			 */
4671590Srgrimes			rewind(collf);
4681590Srgrimes			mesedit(collf, c);
4691590Srgrimes			goto cont;
4701590Srgrimes		}
4711590Srgrimes	}
4721590Srgrimes	goto out;
4731590Srgrimeserr:
4741590Srgrimes	if (collf != NULL) {
47577274Smikeh		(void)Fclose(collf);
4761590Srgrimes		collf = NULL;
4771590Srgrimes	}
4781590Srgrimesout:
4791590Srgrimes	if (collf != NULL)
4801590Srgrimes		rewind(collf);
4811590Srgrimes	noreset--;
48288150Smikeh	(void)sigprocmask(SIG_BLOCK, &nset, NULL);
48377274Smikeh	(void)signal(SIGINT, saveint);
48477274Smikeh	(void)signal(SIGHUP, savehup);
48577274Smikeh	(void)signal(SIGTSTP, savetstp);
48677274Smikeh	(void)signal(SIGTTOU, savettou);
48777274Smikeh	(void)signal(SIGTTIN, savettin);
48888150Smikeh	(void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
48977274Smikeh	return (collf);
4901590Srgrimes}
4911590Srgrimes
4921590Srgrimes/*
4931590Srgrimes * Write a file, ex-like if f set.
4941590Srgrimes */
4951590Srgrimesint
496216564Scharnierexwrite(char name[], FILE *fp, int f)
4971590Srgrimes{
49877274Smikeh	FILE *of;
49977274Smikeh	int c, lc;
5001590Srgrimes	long cc;
5011590Srgrimes	struct stat junk;
5021590Srgrimes
5031590Srgrimes	if (f) {
5041590Srgrimes		printf("\"%s\" ", name);
50577274Smikeh		(void)fflush(stdout);
5061590Srgrimes	}
50774769Smikeh	if (stat(name, &junk) >= 0 && S_ISREG(junk.st_mode)) {
5081590Srgrimes		if (!f)
5091590Srgrimes			fprintf(stderr, "%s: ", name);
5101590Srgrimes		fprintf(stderr, "File exists\n");
51177274Smikeh		return (-1);
5121590Srgrimes	}
5131590Srgrimes	if ((of = Fopen(name, "w")) == NULL) {
51477274Smikeh		warn((char *)NULL);
51577274Smikeh		return (-1);
5161590Srgrimes	}
5171590Srgrimes	lc = 0;
5181590Srgrimes	cc = 0;
5191590Srgrimes	while ((c = getc(fp)) != EOF) {
5201590Srgrimes		cc++;
5211590Srgrimes		if (c == '\n')
5221590Srgrimes			lc++;
52377274Smikeh		(void)putc(c, of);
5241590Srgrimes		if (ferror(of)) {
52574769Smikeh			warnx("%s", name);
52677274Smikeh			(void)Fclose(of);
52777274Smikeh			return (-1);
5281590Srgrimes		}
5291590Srgrimes	}
53077274Smikeh	(void)Fclose(of);
5311590Srgrimes	printf("%d/%ld\n", lc, cc);
53277274Smikeh	(void)fflush(stdout);
53377274Smikeh	return (0);
5341590Srgrimes}
5351590Srgrimes
5361590Srgrimes/*
5371590Srgrimes * Edit the message being collected on fp.
5381590Srgrimes * On return, make the edit file the new temp file.
5391590Srgrimes */
5401590Srgrimesvoid
541216564Scharniermesedit(FILE *fp, int c)
5421590Srgrimes{
5431590Srgrimes	sig_t sigint = signal(SIGINT, SIG_IGN);
5441590Srgrimes	FILE *nf = run_editor(fp, (off_t)-1, c, 0);
5451590Srgrimes
5461590Srgrimes	if (nf != NULL) {
54782793Sache		(void)fseeko(nf, (off_t)0, SEEK_END);
5481590Srgrimes		collf = nf;
54977274Smikeh		(void)Fclose(fp);
5501590Srgrimes	}
55177274Smikeh	(void)signal(SIGINT, sigint);
5521590Srgrimes}
5531590Srgrimes
5541590Srgrimes/*
5551590Srgrimes * Pipe the message through the command.
5561590Srgrimes * Old message is on stdin of command;
5571590Srgrimes * New message collected from stdout.
5581590Srgrimes * Sh -c must return 0 to accept the new message.
5591590Srgrimes */
5601590Srgrimesvoid
561216564Scharniermespipe(FILE *fp, char cmd[])
5621590Srgrimes{
5631590Srgrimes	FILE *nf;
56474769Smikeh	int fd;
5651590Srgrimes	sig_t sigint = signal(SIGINT, SIG_IGN);
56677274Smikeh	char *sh, tempname[PATHSIZE];
5671590Srgrimes
56877274Smikeh	(void)snprintf(tempname, sizeof(tempname),
56977274Smikeh	    "%s/mail.ReXXXXXXXXXX", tmpdir);
57074769Smikeh	if ((fd = mkstemp(tempname)) == -1 ||
57174769Smikeh	    (nf = Fdopen(fd, "w+")) == NULL) {
57274769Smikeh		warn("%s", tempname);
5731590Srgrimes		goto out;
5741590Srgrimes	}
57577274Smikeh	(void)rm(tempname);
5761590Srgrimes	/*
5771590Srgrimes	 * stdin = current message.
5781590Srgrimes	 * stdout = new message.
5791590Srgrimes	 */
58077274Smikeh	if ((sh = value("SHELL")) == NULL)
58177274Smikeh		sh = _PATH_CSHELL;
58277274Smikeh	if (run_command(sh,
58377274Smikeh	    0, fileno(fp), fileno(nf), "-c", cmd, NULL) < 0) {
58477274Smikeh		(void)Fclose(nf);
5851590Srgrimes		goto out;
5861590Srgrimes	}
5871590Srgrimes	if (fsize(nf) == 0) {
5881590Srgrimes		fprintf(stderr, "No bytes from \"%s\" !?\n", cmd);
58977274Smikeh		(void)Fclose(nf);
5901590Srgrimes		goto out;
5911590Srgrimes	}
5921590Srgrimes	/*
5931590Srgrimes	 * Take new files.
5941590Srgrimes	 */
59582793Sache	(void)fseeko(nf, (off_t)0, SEEK_END);
5961590Srgrimes	collf = nf;
59777274Smikeh	(void)Fclose(fp);
5981590Srgrimesout:
59977274Smikeh	(void)signal(SIGINT, sigint);
6001590Srgrimes}
6011590Srgrimes
6021590Srgrimes/*
6031590Srgrimes * Interpolate the named messages into the current
6041590Srgrimes * message, preceding each line with a tab.
6051590Srgrimes * Return a count of the number of characters now in
6061590Srgrimes * the message, or -1 if an error is encountered writing
6071590Srgrimes * the message temporary.  The flag argument is 'm' if we
6081590Srgrimes * should shift over and 'f' if not.
6091590Srgrimes */
6101590Srgrimesint
611216564Scharnierforward(char ms[], FILE *fp, char *fn, int f)
6121590Srgrimes{
61377274Smikeh	int *msgvec;
6141590Srgrimes	struct ignoretab *ig;
6151590Srgrimes	char *tabst;
6161590Srgrimes
61777274Smikeh	msgvec = (int *)salloc((msgCount+1) * sizeof(*msgvec));
61877274Smikeh	if (msgvec == NULL)
61977274Smikeh		return (0);
6201590Srgrimes	if (getmsglist(ms, msgvec, 0) < 0)
62177274Smikeh		return (0);
6221590Srgrimes	if (*msgvec == 0) {
6231590Srgrimes		*msgvec = first(0, MMNORM);
62429574Sphk		if (*msgvec == 0) {
6251590Srgrimes			printf("No appropriate messages\n");
62677274Smikeh			return (0);
6271590Srgrimes		}
62829574Sphk		msgvec[1] = 0;
6291590Srgrimes	}
6301590Srgrimes	if (f == 'f' || f == 'F')
63177274Smikeh		tabst = NULL;
63277274Smikeh	else if ((tabst = value("indentprefix")) == NULL)
6331590Srgrimes		tabst = "\t";
63488227Sache	ig = isupper((unsigned char)f) ? NULL : ignore;
6351590Srgrimes	printf("Interpolating:");
6361590Srgrimes	for (; *msgvec != 0; msgvec++) {
6371590Srgrimes		struct message *mp = message + *msgvec - 1;
6381590Srgrimes
6391590Srgrimes		touch(mp);
6401590Srgrimes		printf(" %d", *msgvec);
64174769Smikeh		if (sendmessage(mp, fp, ig, tabst) < 0) {
64274769Smikeh			warnx("%s", fn);
64377274Smikeh			return (-1);
6441590Srgrimes		}
6451590Srgrimes	}
6461590Srgrimes	printf("\n");
64777274Smikeh	return (0);
6481590Srgrimes}
6491590Srgrimes
6501590Srgrimes/*
6511590Srgrimes * Print (continue) when continued after ^Z.
6521590Srgrimes */
6531590Srgrimes/*ARGSUSED*/
6541590Srgrimesvoid
655216564Scharniercollstop(int s)
6561590Srgrimes{
6571590Srgrimes	sig_t old_action = signal(s, SIG_DFL);
65888150Smikeh	sigset_t nset;
6591590Srgrimes
66088150Smikeh	(void)sigemptyset(&nset);
66188150Smikeh	(void)sigaddset(&nset, s);
66288150Smikeh	(void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
66377274Smikeh	(void)kill(0, s);
66488150Smikeh	(void)sigprocmask(SIG_BLOCK, &nset, NULL);
66577274Smikeh	(void)signal(s, old_action);
6661590Srgrimes	if (colljmp_p) {
6671590Srgrimes		colljmp_p = 0;
6681590Srgrimes		hadintr = 0;
6691590Srgrimes		longjmp(colljmp, 1);
6701590Srgrimes	}
6711590Srgrimes}
6721590Srgrimes
6731590Srgrimes/*
6741590Srgrimes * On interrupt, come here to save the partial message in ~/dead.letter.
6751590Srgrimes * Then jump out of the collection loop.
6761590Srgrimes */
6771590Srgrimes/*ARGSUSED*/
6781590Srgrimesvoid
679216564Scharniercollint(int s __unused)
6801590Srgrimes{
6811590Srgrimes	/*
6821590Srgrimes	 * the control flow is subtle, because we can be called from ~q.
6831590Srgrimes	 */
6841590Srgrimes	if (!hadintr) {
68577274Smikeh		if (value("ignore") != NULL) {
68677274Smikeh			printf("@");
68777274Smikeh			(void)fflush(stdout);
6881590Srgrimes			clearerr(stdin);
6891590Srgrimes			return;
6901590Srgrimes		}
6911590Srgrimes		hadintr = 1;
6921590Srgrimes		longjmp(colljmp, 1);
6931590Srgrimes	}
6941590Srgrimes	rewind(collf);
69577274Smikeh	if (value("nosave") == NULL)
6961590Srgrimes		savedeadletter(collf);
6971590Srgrimes	longjmp(collabort, 1);
6981590Srgrimes}
6991590Srgrimes
7001590Srgrimes/*ARGSUSED*/
7011590Srgrimesvoid
702216564Scharniercollhup(int s __unused)
7031590Srgrimes{
7041590Srgrimes	rewind(collf);
7051590Srgrimes	savedeadletter(collf);
7061590Srgrimes	/*
7071590Srgrimes	 * Let's pretend nobody else wants to clean up,
7081590Srgrimes	 * a true statement at this time.
7091590Srgrimes	 */
7101590Srgrimes	exit(1);
7111590Srgrimes}
7121590Srgrimes
7131590Srgrimesvoid
714216564Scharniersavedeadletter(FILE *fp)
7151590Srgrimes{
71677274Smikeh	FILE *dbuf;
71777274Smikeh	int c;
7181590Srgrimes	char *cp;
7191590Srgrimes
7201590Srgrimes	if (fsize(fp) == 0)
7211590Srgrimes		return;
7221590Srgrimes	cp = getdeadletter();
7231590Srgrimes	c = umask(077);
7241590Srgrimes	dbuf = Fopen(cp, "a");
72577274Smikeh	(void)umask(c);
7261590Srgrimes	if (dbuf == NULL)
7271590Srgrimes		return;
7281590Srgrimes	while ((c = getc(fp)) != EOF)
72977274Smikeh		(void)putc(c, dbuf);
73077274Smikeh	(void)Fclose(dbuf);
7311590Srgrimes	rewind(fp);
7321590Srgrimes}
733