collect.c revision 99112
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 * 3. All advertising materials mentioning features or use of this software
141590Srgrimes *    must display the following acknowledgement:
151590Srgrimes *	This product includes software developed by the University of
161590Srgrimes *	California, Berkeley and its contributors.
171590Srgrimes * 4. Neither the name of the University nor the names of its contributors
181590Srgrimes *    may be used to endorse or promote products derived from this software
191590Srgrimes *    without specific prior written permission.
201590Srgrimes *
211590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311590Srgrimes * SUCH DAMAGE.
321590Srgrimes */
331590Srgrimes
341590Srgrimes#ifndef lint
3574769Smikeh#if 0
361590Srgrimesstatic char sccsid[] = "@(#)collect.c	8.2 (Berkeley) 4/19/94";
3774769Smikeh#endif
381590Srgrimes#endif /* not lint */
3999112Sobrien#include <sys/cdefs.h>
4099112Sobrien__FBSDID("$FreeBSD: head/usr.bin/mail/collect.c 99112 2002-06-30 05:25:07Z obrien $");
411590Srgrimes
421590Srgrimes/*
431590Srgrimes * Mail -- a mail program
441590Srgrimes *
451590Srgrimes * Collect input from standard input, handling
461590Srgrimes * ~ escapes.
471590Srgrimes */
481590Srgrimes
491590Srgrimes#include "rcv.h"
5088428Smikeh#include <fcntl.h>
511590Srgrimes#include "extern.h"
521590Srgrimes
531590Srgrimes/*
541590Srgrimes * Read a message from standard output and return a read file to it
551590Srgrimes * or NULL on error.
561590Srgrimes */
571590Srgrimes
581590Srgrimes/*
591590Srgrimes * The following hokiness with global variables is so that on
601590Srgrimes * receipt of an interrupt signal, the partial message can be salted
611590Srgrimes * away on dead.letter.
621590Srgrimes */
631590Srgrimes
641590Srgrimesstatic	sig_t	saveint;		/* Previous SIGINT value */
651590Srgrimesstatic	sig_t	savehup;		/* Previous SIGHUP value */
661590Srgrimesstatic	sig_t	savetstp;		/* Previous SIGTSTP value */
671590Srgrimesstatic	sig_t	savettou;		/* Previous SIGTTOU value */
681590Srgrimesstatic	sig_t	savettin;		/* Previous SIGTTIN value */
691590Srgrimesstatic	FILE	*collf;			/* File for saving away */
701590Srgrimesstatic	int	hadintr;		/* Have seen one SIGINT so far */
711590Srgrimes
721590Srgrimesstatic	jmp_buf	colljmp;		/* To get back to work */
731590Srgrimesstatic	int	colljmp_p;		/* whether to long jump */
741590Srgrimesstatic	jmp_buf	collabort;		/* To end collection with error */
751590Srgrimes
761590SrgrimesFILE *
771590Srgrimescollect(hp, printheaders)
781590Srgrimes	struct header *hp;
791590Srgrimes	int printheaders;
801590Srgrimes{
811590Srgrimes	FILE *fbuf;
8277274Smikeh	int lc, cc, escape, eofcount, fd, c, t;
8377274Smikeh	char linebuf[LINESIZE], tempname[PATHSIZE], *cp, getsub;
8488150Smikeh	sigset_t nset;
8588150Smikeh	int longline, lastlong, rc;	/* So we don't make 2 or more lines
8688150Smikeh					   out of a long input line. */
871590Srgrimes
881590Srgrimes	collf = NULL;
891590Srgrimes	/*
901590Srgrimes	 * Start catching signals from here, but we're still die on interrupts
911590Srgrimes	 * until we're in the main loop.
921590Srgrimes	 */
9388150Smikeh	(void)sigemptyset(&nset);
9488150Smikeh	(void)sigaddset(&nset, SIGINT);
9588150Smikeh	(void)sigaddset(&nset, SIGHUP);
9688150Smikeh	(void)sigprocmask(SIG_BLOCK, &nset, NULL);
971590Srgrimes	if ((saveint = signal(SIGINT, SIG_IGN)) != SIG_IGN)
9877274Smikeh		(void)signal(SIGINT, collint);
991590Srgrimes	if ((savehup = signal(SIGHUP, SIG_IGN)) != SIG_IGN)
10077274Smikeh		(void)signal(SIGHUP, collhup);
1011590Srgrimes	savetstp = signal(SIGTSTP, collstop);
1021590Srgrimes	savettou = signal(SIGTTOU, collstop);
1031590Srgrimes	savettin = signal(SIGTTIN, collstop);
1041590Srgrimes	if (setjmp(collabort) || setjmp(colljmp)) {
10577274Smikeh		(void)rm(tempname);
1061590Srgrimes		goto err;
1071590Srgrimes	}
10888150Smikeh	(void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
1091590Srgrimes
1101590Srgrimes	noreset++;
11177274Smikeh	(void)snprintf(tempname, sizeof(tempname),
11277274Smikeh	    "%s/mail.RsXXXXXXXXXX", tmpdir);
11374769Smikeh	if ((fd = mkstemp(tempname)) == -1 ||
11474769Smikeh	    (collf = Fdopen(fd, "w+")) == NULL) {
11574769Smikeh		warn("%s", tempname);
1161590Srgrimes		goto err;
1171590Srgrimes	}
11877274Smikeh	(void)rm(tempname);
1191590Srgrimes
1201590Srgrimes	/*
1211590Srgrimes	 * If we are going to prompt for a subject,
1221590Srgrimes	 * refrain from printing a newline after
1231590Srgrimes	 * the headers (since some people mind).
1241590Srgrimes	 */
1251590Srgrimes	t = GTO|GSUBJECT|GCC|GNL;
1261590Srgrimes	getsub = 0;
12777274Smikeh	if (hp->h_subject == NULL && value("interactive") != NULL &&
12877274Smikeh	    (value("ask") != NULL || value("asksub") != NULL))
1291590Srgrimes		t &= ~GNL, getsub++;
1301590Srgrimes	if (printheaders) {
1311590Srgrimes		puthead(hp, stdout, t);
13277274Smikeh		(void)fflush(stdout);
1331590Srgrimes	}
13477274Smikeh	if ((cp = value("escape")) != NULL)
1351590Srgrimes		escape = *cp;
1361590Srgrimes	else
1371590Srgrimes		escape = ESCAPE;
1381590Srgrimes	eofcount = 0;
1391590Srgrimes	hadintr = 0;
14088150Smikeh	lastlong = 0;
14188150Smikeh	longline = 0;
1421590Srgrimes
1431590Srgrimes	if (!setjmp(colljmp)) {
1441590Srgrimes		if (getsub)
1451590Srgrimes			grabh(hp, GSUBJECT);
1461590Srgrimes	} else {
1471590Srgrimes		/*
1481590Srgrimes		 * Come here for printing the after-signal message.
1491590Srgrimes		 * Duplicate messages won't be printed because
1501590Srgrimes		 * the write is aborted if we get a SIGTTOU.
1511590Srgrimes		 */
1521590Srgrimescont:
1531590Srgrimes		if (hadintr) {
15477274Smikeh			(void)fflush(stdout);
1551590Srgrimes			fprintf(stderr,
1561590Srgrimes			"\n(Interrupt -- one more to kill letter)\n");
1571590Srgrimes		} else {
1581590Srgrimes			printf("(continue)\n");
15977274Smikeh			(void)fflush(stdout);
1601590Srgrimes		}
1611590Srgrimes	}
1621590Srgrimes	for (;;) {
1631590Srgrimes		colljmp_p = 1;
1641590Srgrimes		c = readline(stdin, linebuf, LINESIZE);
1651590Srgrimes		colljmp_p = 0;
1661590Srgrimes		if (c < 0) {
16777274Smikeh			if (value("interactive") != NULL &&
16877274Smikeh			    value("ignoreeof") != NULL && ++eofcount < 25) {
1691590Srgrimes				printf("Use \".\" to terminate letter\n");
1701590Srgrimes				continue;
1711590Srgrimes			}
1721590Srgrimes			break;
1731590Srgrimes		}
17488150Smikeh		lastlong = longline;
17588150Smikeh		longline = c == LINESIZE - 1;
1761590Srgrimes		eofcount = 0;
1771590Srgrimes		hadintr = 0;
1781590Srgrimes		if (linebuf[0] == '.' && linebuf[1] == '\0' &&
17988150Smikeh		    value("interactive") != NULL && !lastlong &&
18077274Smikeh		    (value("dot") != NULL || value("ignoreeof") != NULL))
1811590Srgrimes			break;
18288150Smikeh		if (linebuf[0] != escape || value("interactive") == NULL ||
18388150Smikeh		    lastlong) {
18488150Smikeh			if (putline(collf, linebuf, !longline) < 0)
1851590Srgrimes				goto err;
1861590Srgrimes			continue;
1871590Srgrimes		}
1881590Srgrimes		c = linebuf[1];
1891590Srgrimes		switch (c) {
1901590Srgrimes		default:
1911590Srgrimes			/*
1921590Srgrimes			 * On double escape, just send the single one.
1931590Srgrimes			 * Otherwise, it's an error.
1941590Srgrimes			 */
1951590Srgrimes			if (c == escape) {
19688150Smikeh				if (putline(collf, &linebuf[1], !longline) < 0)
1971590Srgrimes					goto err;
1981590Srgrimes				else
1991590Srgrimes					break;
2001590Srgrimes			}
2011590Srgrimes			printf("Unknown tilde escape.\n");
2021590Srgrimes			break;
2031590Srgrimes		case 'C':
2041590Srgrimes			/*
2051590Srgrimes			 * Dump core.
2061590Srgrimes			 */
2071590Srgrimes			core();
2081590Srgrimes			break;
2091590Srgrimes		case '!':
2101590Srgrimes			/*
2111590Srgrimes			 * Shell escape, send the balance of the
2121590Srgrimes			 * line to sh -c.
2131590Srgrimes			 */
2141590Srgrimes			shell(&linebuf[2]);
2151590Srgrimes			break;
2161590Srgrimes		case ':':
21788428Smikeh		case '_':
2181590Srgrimes			/*
2191590Srgrimes			 * Escape to command mode, but be nice!
2201590Srgrimes			 */
2211590Srgrimes			execute(&linebuf[2], 1);
2221590Srgrimes			goto cont;
2231590Srgrimes		case '.':
2241590Srgrimes			/*
2251590Srgrimes			 * Simulate end of file on input.
2261590Srgrimes			 */
2271590Srgrimes			goto out;
2281590Srgrimes		case 'q':
2291590Srgrimes			/*
2301590Srgrimes			 * Force a quit of sending mail.
2311590Srgrimes			 * Act like an interrupt happened.
2321590Srgrimes			 */
2331590Srgrimes			hadintr++;
2341590Srgrimes			collint(SIGINT);
2351590Srgrimes			exit(1);
23688428Smikeh		case 'x':
23788428Smikeh			/*
23888428Smikeh			 * Exit, do not save in dead.letter.
23988428Smikeh			 */
24088428Smikeh			goto err;
2411590Srgrimes		case 'h':
2421590Srgrimes			/*
2431590Srgrimes			 * Grab a bunch of headers.
2441590Srgrimes			 */
2451590Srgrimes			grabh(hp, GTO|GSUBJECT|GCC|GBCC);
2461590Srgrimes			goto cont;
2471590Srgrimes		case 't':
2481590Srgrimes			/*
2491590Srgrimes			 * Add to the To list.
2501590Srgrimes			 */
2511590Srgrimes			hp->h_to = cat(hp->h_to, extract(&linebuf[2], GTO));
2521590Srgrimes			break;
2531590Srgrimes		case 's':
2541590Srgrimes			/*
25532189Sjoerg			 * Set the Subject line.
2561590Srgrimes			 */
2571590Srgrimes			cp = &linebuf[2];
25888227Sache			while (isspace((unsigned char)*cp))
2591590Srgrimes				cp++;
2601590Srgrimes			hp->h_subject = savestr(cp);
2611590Srgrimes			break;
26232189Sjoerg		case 'R':
26332189Sjoerg			/*
26432189Sjoerg			 * Set the Reply-To line.
26532189Sjoerg			 */
26632189Sjoerg			cp = &linebuf[2];
26788227Sache			while (isspace((unsigned char)*cp))
26832189Sjoerg				cp++;
26932189Sjoerg			hp->h_replyto = savestr(cp);
27032189Sjoerg			break;
2711590Srgrimes		case 'c':
2721590Srgrimes			/*
2731590Srgrimes			 * Add to the CC list.
2741590Srgrimes			 */
2751590Srgrimes			hp->h_cc = cat(hp->h_cc, extract(&linebuf[2], GCC));
2761590Srgrimes			break;
2771590Srgrimes		case 'b':
2781590Srgrimes			/*
27988428Smikeh			 * Add to the BCC list.
2801590Srgrimes			 */
2811590Srgrimes			hp->h_bcc = cat(hp->h_bcc, extract(&linebuf[2], GBCC));
2821590Srgrimes			break;
28388428Smikeh		case 'i':
28488428Smikeh		case 'A':
28588428Smikeh		case 'a':
28688428Smikeh			/*
28788434Smikeh			 * Insert named variable in message.
28888428Smikeh			 */
28988428Smikeh			switch(c) {
29088428Smikeh				case 'i':
29188428Smikeh					cp = &linebuf[2];
29288428Smikeh					while(isspace((unsigned char)*cp))
29388428Smikeh						cp++;
29488428Smikeh					break;
29588428Smikeh				case 'a':
29688428Smikeh					cp = "sign";
29788428Smikeh					break;
29888428Smikeh				case 'A':
29988428Smikeh					cp = "Sign";
30088428Smikeh					break;
30188428Smikeh				default:
30288428Smikeh					goto err;
30388428Smikeh			}
30488428Smikeh
30588428Smikeh			if(*cp != '\0' && (cp = value(cp)) != NULL) {
30688428Smikeh				printf("%s\n", cp);
30788428Smikeh				if(putline(collf, cp, 1) < 0)
30888428Smikeh					goto err;
30988428Smikeh			}
31088428Smikeh
31188428Smikeh			break;
3121590Srgrimes		case 'd':
31388428Smikeh			/*
31488428Smikeh			 * Read in the dead letter file.
31588428Smikeh			 */
31688428Smikeh			if (strlcpy(linebuf + 2, getdeadletter(),
31788428Smikeh				sizeof(linebuf) - 2)
31874769Smikeh			    >= sizeof(linebuf) - 2) {
31974769Smikeh				printf("Line buffer overflow\n");
32074769Smikeh				break;
32174769Smikeh			}
32288428Smikeh			/* FALLTHROUGH */
3231590Srgrimes		case 'r':
32488428Smikeh		case '<':
3251590Srgrimes			/*
3261590Srgrimes			 * Invoke a file:
3271590Srgrimes			 * Search for the file name,
3281590Srgrimes			 * then open it and copy the contents to collf.
3291590Srgrimes			 */
3301590Srgrimes			cp = &linebuf[2];
33188227Sache			while (isspace((unsigned char)*cp))
3321590Srgrimes				cp++;
3331590Srgrimes			if (*cp == '\0') {
3341590Srgrimes				printf("Interpolate what file?\n");
3351590Srgrimes				break;
3361590Srgrimes			}
3371590Srgrimes			cp = expand(cp);
33877274Smikeh			if (cp == NULL)
3391590Srgrimes				break;
34088428Smikeh			if (*cp == '!') {
34188428Smikeh				/*
34288428Smikeh				 * Insert stdout of command.
34388428Smikeh				 */
34488428Smikeh				char *sh;
34588428Smikeh				int nullfd, tempfd, rc;
34688428Smikeh				char tempname2[PATHSIZE];
34788428Smikeh
34888428Smikeh				if ((nullfd = open("/dev/null", O_RDONLY, 0))
34988428Smikeh				    == -1) {
35088428Smikeh					warn("/dev/null");
35188428Smikeh					break;
35288428Smikeh				}
35388428Smikeh
35488428Smikeh				(void)snprintf(tempname2, sizeof(tempname2),
35588428Smikeh				    "%s/mail.ReXXXXXXXXXX", tmpdir);
35688428Smikeh				if ((tempfd = mkstemp(tempname2)) == -1 ||
35788428Smikeh				    (fbuf = Fdopen(tempfd, "w+")) == NULL) {
35888428Smikeh					warn("%s", tempname2);
35988428Smikeh					break;
36088428Smikeh				}
36188428Smikeh				(void)unlink(tempname2);
36288428Smikeh
36388428Smikeh				if ((sh = value("SHELL")) == NULL)
36488428Smikeh					sh = _PATH_CSHELL;
36588428Smikeh
36688428Smikeh				rc = run_command(sh, 0, nullfd, fileno(fbuf),
36788428Smikeh				    "-c", cp+1, NULL);
36888428Smikeh
36988428Smikeh				close(nullfd);
37088428Smikeh
37188428Smikeh				if (rc < 0) {
37288428Smikeh					(void)Fclose(fbuf);
37388428Smikeh					break;
37488428Smikeh				}
37588428Smikeh
37688428Smikeh				if (fsize(fbuf) == 0) {
37788428Smikeh					fprintf(stderr,
37888428Smikeh					    "No bytes from command \"%s\"\n",
37988428Smikeh					    cp+1);
38088428Smikeh					(void)Fclose(fbuf);
38188428Smikeh					break;
38288428Smikeh				}
38388428Smikeh
38488428Smikeh				rewind(fbuf);
38588428Smikeh			} else if (isdir(cp)) {
3861590Srgrimes				printf("%s: Directory\n", cp);
3871590Srgrimes				break;
38888428Smikeh			} else if ((fbuf = Fopen(cp, "r")) == NULL) {
38974769Smikeh				warn("%s", cp);
3901590Srgrimes				break;
3911590Srgrimes			}
3921590Srgrimes			printf("\"%s\" ", cp);
39377274Smikeh			(void)fflush(stdout);
3941590Srgrimes			lc = 0;
3951590Srgrimes			cc = 0;
39688150Smikeh			while ((rc = readline(fbuf, linebuf, LINESIZE)) >= 0) {
39788150Smikeh				if (rc != LINESIZE - 1)
39888150Smikeh					lc++;
39988150Smikeh				if ((t = putline(collf, linebuf,
40088150Smikeh					 rc != LINESIZE - 1)) < 0) {
40177274Smikeh					(void)Fclose(fbuf);
4021590Srgrimes					goto err;
4031590Srgrimes				}
4041590Srgrimes				cc += t;
4051590Srgrimes			}
40677274Smikeh			(void)Fclose(fbuf);
4071590Srgrimes			printf("%d/%d\n", lc, cc);
4081590Srgrimes			break;
4091590Srgrimes		case 'w':
4101590Srgrimes			/*
4111590Srgrimes			 * Write the message on a file.
4121590Srgrimes			 */
4131590Srgrimes			cp = &linebuf[2];
4141590Srgrimes			while (*cp == ' ' || *cp == '\t')
4151590Srgrimes				cp++;
4161590Srgrimes			if (*cp == '\0') {
4171590Srgrimes				fprintf(stderr, "Write what file!?\n");
4181590Srgrimes				break;
4191590Srgrimes			}
42077274Smikeh			if ((cp = expand(cp)) == NULL)
4211590Srgrimes				break;
4221590Srgrimes			rewind(collf);
4231590Srgrimes			exwrite(cp, collf, 1);
4241590Srgrimes			break;
4251590Srgrimes		case 'm':
4261590Srgrimes		case 'M':
4271590Srgrimes		case 'f':
4281590Srgrimes		case 'F':
4291590Srgrimes			/*
4301590Srgrimes			 * Interpolate the named messages, if we
4311590Srgrimes			 * are in receiving mail mode.  Does the
4321590Srgrimes			 * standard list processing garbage.
4331590Srgrimes			 * If ~f is given, we don't shift over.
4341590Srgrimes			 */
43574769Smikeh			if (forward(linebuf + 2, collf, tempname, c) < 0)
4361590Srgrimes				goto err;
4371590Srgrimes			goto cont;
4381590Srgrimes		case '?':
4391590Srgrimes			if ((fbuf = Fopen(_PATH_TILDE, "r")) == NULL) {
44074769Smikeh				warn("%s", _PATH_TILDE);
4411590Srgrimes				break;
4421590Srgrimes			}
4431590Srgrimes			while ((t = getc(fbuf)) != EOF)
44477274Smikeh				(void)putchar(t);
44577274Smikeh			(void)Fclose(fbuf);
4461590Srgrimes			break;
4471590Srgrimes		case 'p':
4481590Srgrimes			/*
4491590Srgrimes			 * Print out the current state of the
4501590Srgrimes			 * message without altering anything.
4511590Srgrimes			 */
4521590Srgrimes			rewind(collf);
4531590Srgrimes			printf("-------\nMessage contains:\n");
4541590Srgrimes			puthead(hp, stdout, GTO|GSUBJECT|GCC|GBCC|GNL);
4551590Srgrimes			while ((t = getc(collf)) != EOF)
45677274Smikeh				(void)putchar(t);
4571590Srgrimes			goto cont;
4581590Srgrimes		case '|':
4591590Srgrimes			/*
4601590Srgrimes			 * Pipe message through command.
4611590Srgrimes			 * Collect output as new message.
4621590Srgrimes			 */
4631590Srgrimes			rewind(collf);
4641590Srgrimes			mespipe(collf, &linebuf[2]);
4651590Srgrimes			goto cont;
4661590Srgrimes		case 'v':
4671590Srgrimes		case 'e':
4681590Srgrimes			/*
4691590Srgrimes			 * Edit the current message.
4701590Srgrimes			 * 'e' means to use EDITOR
4711590Srgrimes			 * 'v' means to use VISUAL
4721590Srgrimes			 */
4731590Srgrimes			rewind(collf);
4741590Srgrimes			mesedit(collf, c);
4751590Srgrimes			goto cont;
4761590Srgrimes		}
4771590Srgrimes	}
4781590Srgrimes	goto out;
4791590Srgrimeserr:
4801590Srgrimes	if (collf != NULL) {
48177274Smikeh		(void)Fclose(collf);
4821590Srgrimes		collf = NULL;
4831590Srgrimes	}
4841590Srgrimesout:
4851590Srgrimes	if (collf != NULL)
4861590Srgrimes		rewind(collf);
4871590Srgrimes	noreset--;
48888150Smikeh	(void)sigprocmask(SIG_BLOCK, &nset, NULL);
48977274Smikeh	(void)signal(SIGINT, saveint);
49077274Smikeh	(void)signal(SIGHUP, savehup);
49177274Smikeh	(void)signal(SIGTSTP, savetstp);
49277274Smikeh	(void)signal(SIGTTOU, savettou);
49377274Smikeh	(void)signal(SIGTTIN, savettin);
49488150Smikeh	(void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
49577274Smikeh	return (collf);
4961590Srgrimes}
4971590Srgrimes
4981590Srgrimes/*
4991590Srgrimes * Write a file, ex-like if f set.
5001590Srgrimes */
5011590Srgrimesint
5021590Srgrimesexwrite(name, fp, f)
5031590Srgrimes	char name[];
5041590Srgrimes	FILE *fp;
5051590Srgrimes	int f;
5061590Srgrimes{
50777274Smikeh	FILE *of;
50877274Smikeh	int c, lc;
5091590Srgrimes	long cc;
5101590Srgrimes	struct stat junk;
5111590Srgrimes
5121590Srgrimes	if (f) {
5131590Srgrimes		printf("\"%s\" ", name);
51477274Smikeh		(void)fflush(stdout);
5151590Srgrimes	}
51674769Smikeh	if (stat(name, &junk) >= 0 && S_ISREG(junk.st_mode)) {
5171590Srgrimes		if (!f)
5181590Srgrimes			fprintf(stderr, "%s: ", name);
5191590Srgrimes		fprintf(stderr, "File exists\n");
52077274Smikeh		return (-1);
5211590Srgrimes	}
5221590Srgrimes	if ((of = Fopen(name, "w")) == NULL) {
52377274Smikeh		warn((char *)NULL);
52477274Smikeh		return (-1);
5251590Srgrimes	}
5261590Srgrimes	lc = 0;
5271590Srgrimes	cc = 0;
5281590Srgrimes	while ((c = getc(fp)) != EOF) {
5291590Srgrimes		cc++;
5301590Srgrimes		if (c == '\n')
5311590Srgrimes			lc++;
53277274Smikeh		(void)putc(c, of);
5331590Srgrimes		if (ferror(of)) {
53474769Smikeh			warnx("%s", name);
53577274Smikeh			(void)Fclose(of);
53677274Smikeh			return (-1);
5371590Srgrimes		}
5381590Srgrimes	}
53977274Smikeh	(void)Fclose(of);
5401590Srgrimes	printf("%d/%ld\n", lc, cc);
54177274Smikeh	(void)fflush(stdout);
54277274Smikeh	return (0);
5431590Srgrimes}
5441590Srgrimes
5451590Srgrimes/*
5461590Srgrimes * Edit the message being collected on fp.
5471590Srgrimes * On return, make the edit file the new temp file.
5481590Srgrimes */
5491590Srgrimesvoid
5501590Srgrimesmesedit(fp, c)
5511590Srgrimes	FILE *fp;
5521590Srgrimes	int c;
5531590Srgrimes{
5541590Srgrimes	sig_t sigint = signal(SIGINT, SIG_IGN);
5551590Srgrimes	FILE *nf = run_editor(fp, (off_t)-1, c, 0);
5561590Srgrimes
5571590Srgrimes	if (nf != NULL) {
55882793Sache		(void)fseeko(nf, (off_t)0, SEEK_END);
5591590Srgrimes		collf = nf;
56077274Smikeh		(void)Fclose(fp);
5611590Srgrimes	}
56277274Smikeh	(void)signal(SIGINT, sigint);
5631590Srgrimes}
5641590Srgrimes
5651590Srgrimes/*
5661590Srgrimes * Pipe the message through the command.
5671590Srgrimes * Old message is on stdin of command;
5681590Srgrimes * New message collected from stdout.
5691590Srgrimes * Sh -c must return 0 to accept the new message.
5701590Srgrimes */
5711590Srgrimesvoid
5721590Srgrimesmespipe(fp, cmd)
5731590Srgrimes	FILE *fp;
5741590Srgrimes	char cmd[];
5751590Srgrimes{
5761590Srgrimes	FILE *nf;
57774769Smikeh	int fd;
5781590Srgrimes	sig_t sigint = signal(SIGINT, SIG_IGN);
57977274Smikeh	char *sh, tempname[PATHSIZE];
5801590Srgrimes
58177274Smikeh	(void)snprintf(tempname, sizeof(tempname),
58277274Smikeh	    "%s/mail.ReXXXXXXXXXX", tmpdir);
58374769Smikeh	if ((fd = mkstemp(tempname)) == -1 ||
58474769Smikeh	    (nf = Fdopen(fd, "w+")) == NULL) {
58574769Smikeh		warn("%s", tempname);
5861590Srgrimes		goto out;
5871590Srgrimes	}
58877274Smikeh	(void)rm(tempname);
5891590Srgrimes	/*
5901590Srgrimes	 * stdin = current message.
5911590Srgrimes	 * stdout = new message.
5921590Srgrimes	 */
59377274Smikeh	if ((sh = value("SHELL")) == NULL)
59477274Smikeh		sh = _PATH_CSHELL;
59577274Smikeh	if (run_command(sh,
59677274Smikeh	    0, fileno(fp), fileno(nf), "-c", cmd, NULL) < 0) {
59777274Smikeh		(void)Fclose(nf);
5981590Srgrimes		goto out;
5991590Srgrimes	}
6001590Srgrimes	if (fsize(nf) == 0) {
6011590Srgrimes		fprintf(stderr, "No bytes from \"%s\" !?\n", cmd);
60277274Smikeh		(void)Fclose(nf);
6031590Srgrimes		goto out;
6041590Srgrimes	}
6051590Srgrimes	/*
6061590Srgrimes	 * Take new files.
6071590Srgrimes	 */
60882793Sache	(void)fseeko(nf, (off_t)0, SEEK_END);
6091590Srgrimes	collf = nf;
61077274Smikeh	(void)Fclose(fp);
6111590Srgrimesout:
61277274Smikeh	(void)signal(SIGINT, sigint);
6131590Srgrimes}
6141590Srgrimes
6151590Srgrimes/*
6161590Srgrimes * Interpolate the named messages into the current
6171590Srgrimes * message, preceding each line with a tab.
6181590Srgrimes * Return a count of the number of characters now in
6191590Srgrimes * the message, or -1 if an error is encountered writing
6201590Srgrimes * the message temporary.  The flag argument is 'm' if we
6211590Srgrimes * should shift over and 'f' if not.
6221590Srgrimes */
6231590Srgrimesint
62474769Smikehforward(ms, fp, fn, f)
6251590Srgrimes	char ms[];
6261590Srgrimes	FILE *fp;
62774769Smikeh	char *fn;
6281590Srgrimes	int f;
6291590Srgrimes{
63077274Smikeh	int *msgvec;
6311590Srgrimes	struct ignoretab *ig;
6321590Srgrimes	char *tabst;
6331590Srgrimes
63477274Smikeh	msgvec = (int *)salloc((msgCount+1) * sizeof(*msgvec));
63577274Smikeh	if (msgvec == NULL)
63677274Smikeh		return (0);
6371590Srgrimes	if (getmsglist(ms, msgvec, 0) < 0)
63877274Smikeh		return (0);
6391590Srgrimes	if (*msgvec == 0) {
6401590Srgrimes		*msgvec = first(0, MMNORM);
64129574Sphk		if (*msgvec == 0) {
6421590Srgrimes			printf("No appropriate messages\n");
64377274Smikeh			return (0);
6441590Srgrimes		}
64529574Sphk		msgvec[1] = 0;
6461590Srgrimes	}
6471590Srgrimes	if (f == 'f' || f == 'F')
64877274Smikeh		tabst = NULL;
64977274Smikeh	else if ((tabst = value("indentprefix")) == NULL)
6501590Srgrimes		tabst = "\t";
65188227Sache	ig = isupper((unsigned char)f) ? NULL : ignore;
6521590Srgrimes	printf("Interpolating:");
6531590Srgrimes	for (; *msgvec != 0; msgvec++) {
6541590Srgrimes		struct message *mp = message + *msgvec - 1;
6551590Srgrimes
6561590Srgrimes		touch(mp);
6571590Srgrimes		printf(" %d", *msgvec);
65874769Smikeh		if (sendmessage(mp, fp, ig, tabst) < 0) {
65974769Smikeh			warnx("%s", fn);
66077274Smikeh			return (-1);
6611590Srgrimes		}
6621590Srgrimes	}
6631590Srgrimes	printf("\n");
66477274Smikeh	return (0);
6651590Srgrimes}
6661590Srgrimes
6671590Srgrimes/*
6681590Srgrimes * Print (continue) when continued after ^Z.
6691590Srgrimes */
6701590Srgrimes/*ARGSUSED*/
6711590Srgrimesvoid
6721590Srgrimescollstop(s)
6731590Srgrimes	int s;
6741590Srgrimes{
6751590Srgrimes	sig_t old_action = signal(s, SIG_DFL);
67688150Smikeh	sigset_t nset;
6771590Srgrimes
67888150Smikeh	(void)sigemptyset(&nset);
67988150Smikeh	(void)sigaddset(&nset, s);
68088150Smikeh	(void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
68177274Smikeh	(void)kill(0, s);
68288150Smikeh	(void)sigprocmask(SIG_BLOCK, &nset, NULL);
68377274Smikeh	(void)signal(s, old_action);
6841590Srgrimes	if (colljmp_p) {
6851590Srgrimes		colljmp_p = 0;
6861590Srgrimes		hadintr = 0;
6871590Srgrimes		longjmp(colljmp, 1);
6881590Srgrimes	}
6891590Srgrimes}
6901590Srgrimes
6911590Srgrimes/*
6921590Srgrimes * On interrupt, come here to save the partial message in ~/dead.letter.
6931590Srgrimes * Then jump out of the collection loop.
6941590Srgrimes */
6951590Srgrimes/*ARGSUSED*/
6961590Srgrimesvoid
6971590Srgrimescollint(s)
6981590Srgrimes	int s;
6991590Srgrimes{
7001590Srgrimes	/*
7011590Srgrimes	 * the control flow is subtle, because we can be called from ~q.
7021590Srgrimes	 */
7031590Srgrimes	if (!hadintr) {
70477274Smikeh		if (value("ignore") != NULL) {
70577274Smikeh			printf("@");
70677274Smikeh			(void)fflush(stdout);
7071590Srgrimes			clearerr(stdin);
7081590Srgrimes			return;
7091590Srgrimes		}
7101590Srgrimes		hadintr = 1;
7111590Srgrimes		longjmp(colljmp, 1);
7121590Srgrimes	}
7131590Srgrimes	rewind(collf);
71477274Smikeh	if (value("nosave") == NULL)
7151590Srgrimes		savedeadletter(collf);
7161590Srgrimes	longjmp(collabort, 1);
7171590Srgrimes}
7181590Srgrimes
7191590Srgrimes/*ARGSUSED*/
7201590Srgrimesvoid
7211590Srgrimescollhup(s)
7221590Srgrimes	int s;
7231590Srgrimes{
7241590Srgrimes	rewind(collf);
7251590Srgrimes	savedeadletter(collf);
7261590Srgrimes	/*
7271590Srgrimes	 * Let's pretend nobody else wants to clean up,
7281590Srgrimes	 * a true statement at this time.
7291590Srgrimes	 */
7301590Srgrimes	exit(1);
7311590Srgrimes}
7321590Srgrimes
7331590Srgrimesvoid
7341590Srgrimessavedeadletter(fp)
73577274Smikeh	FILE *fp;
7361590Srgrimes{
73777274Smikeh	FILE *dbuf;
73877274Smikeh	int c;
7391590Srgrimes	char *cp;
7401590Srgrimes
7411590Srgrimes	if (fsize(fp) == 0)
7421590Srgrimes		return;
7431590Srgrimes	cp = getdeadletter();
7441590Srgrimes	c = umask(077);
7451590Srgrimes	dbuf = Fopen(cp, "a");
74677274Smikeh	(void)umask(c);
7471590Srgrimes	if (dbuf == NULL)
7481590Srgrimes		return;
7491590Srgrimes	while ((c = getc(fp)) != EOF)
75077274Smikeh		(void)putc(c, dbuf);
75177274Smikeh	(void)Fclose(dbuf);
7521590Srgrimes	rewind(fp);
7531590Srgrimes}
754