collect.c revision 82793
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
3874769Smikehstatic const char rcsid[] =
3974769Smikeh  "$FreeBSD: head/usr.bin/mail/collect.c 82793 2001-09-02 14:40:51Z ache $";
401590Srgrimes#endif /* not lint */
411590Srgrimes
421590Srgrimes/*
431590Srgrimes * Mail -- a mail program
441590Srgrimes *
451590Srgrimes * Collect input from standard input, handling
461590Srgrimes * ~ escapes.
471590Srgrimes */
481590Srgrimes
491590Srgrimes#include "rcv.h"
501590Srgrimes#include "extern.h"
511590Srgrimes
521590Srgrimes/*
531590Srgrimes * Read a message from standard output and return a read file to it
541590Srgrimes * or NULL on error.
551590Srgrimes */
561590Srgrimes
571590Srgrimes/*
581590Srgrimes * The following hokiness with global variables is so that on
591590Srgrimes * receipt of an interrupt signal, the partial message can be salted
601590Srgrimes * away on dead.letter.
611590Srgrimes */
621590Srgrimes
631590Srgrimesstatic	sig_t	saveint;		/* Previous SIGINT value */
641590Srgrimesstatic	sig_t	savehup;		/* Previous SIGHUP value */
651590Srgrimesstatic	sig_t	savetstp;		/* Previous SIGTSTP value */
661590Srgrimesstatic	sig_t	savettou;		/* Previous SIGTTOU value */
671590Srgrimesstatic	sig_t	savettin;		/* Previous SIGTTIN value */
681590Srgrimesstatic	FILE	*collf;			/* File for saving away */
691590Srgrimesstatic	int	hadintr;		/* Have seen one SIGINT so far */
701590Srgrimes
711590Srgrimesstatic	jmp_buf	colljmp;		/* To get back to work */
721590Srgrimesstatic	int	colljmp_p;		/* whether to long jump */
731590Srgrimesstatic	jmp_buf	collabort;		/* To end collection with error */
741590Srgrimes
751590SrgrimesFILE *
761590Srgrimescollect(hp, printheaders)
771590Srgrimes	struct header *hp;
781590Srgrimes	int printheaders;
791590Srgrimes{
801590Srgrimes	FILE *fbuf;
8177274Smikeh	int lc, cc, escape, eofcount, fd, c, t;
8277274Smikeh	char linebuf[LINESIZE], tempname[PATHSIZE], *cp, getsub;
831590Srgrimes	int omask;
841590Srgrimes
851590Srgrimes	collf = NULL;
861590Srgrimes	/*
871590Srgrimes	 * Start catching signals from here, but we're still die on interrupts
881590Srgrimes	 * until we're in the main loop.
891590Srgrimes	 */
901590Srgrimes	omask = sigblock(sigmask(SIGINT) | sigmask(SIGHUP));
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	}
1021590Srgrimes	sigsetmask(omask & ~(sigmask(SIGINT) | sigmask(SIGHUP)));
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;
1341590Srgrimes
1351590Srgrimes	if (!setjmp(colljmp)) {
1361590Srgrimes		if (getsub)
1371590Srgrimes			grabh(hp, GSUBJECT);
1381590Srgrimes	} else {
1391590Srgrimes		/*
1401590Srgrimes		 * Come here for printing the after-signal message.
1411590Srgrimes		 * Duplicate messages won't be printed because
1421590Srgrimes		 * the write is aborted if we get a SIGTTOU.
1431590Srgrimes		 */
1441590Srgrimescont:
1451590Srgrimes		if (hadintr) {
14677274Smikeh			(void)fflush(stdout);
1471590Srgrimes			fprintf(stderr,
1481590Srgrimes			"\n(Interrupt -- one more to kill letter)\n");
1491590Srgrimes		} else {
1501590Srgrimes			printf("(continue)\n");
15177274Smikeh			(void)fflush(stdout);
1521590Srgrimes		}
1531590Srgrimes	}
1541590Srgrimes	for (;;) {
1551590Srgrimes		colljmp_p = 1;
1561590Srgrimes		c = readline(stdin, linebuf, LINESIZE);
1571590Srgrimes		colljmp_p = 0;
1581590Srgrimes		if (c < 0) {
15977274Smikeh			if (value("interactive") != NULL &&
16077274Smikeh			    value("ignoreeof") != NULL && ++eofcount < 25) {
1611590Srgrimes				printf("Use \".\" to terminate letter\n");
1621590Srgrimes				continue;
1631590Srgrimes			}
1641590Srgrimes			break;
1651590Srgrimes		}
1661590Srgrimes		eofcount = 0;
1671590Srgrimes		hadintr = 0;
1681590Srgrimes		if (linebuf[0] == '.' && linebuf[1] == '\0' &&
16977274Smikeh		    value("interactive") != NULL &&
17077274Smikeh		    (value("dot") != NULL || value("ignoreeof") != NULL))
1711590Srgrimes			break;
17277274Smikeh		if (linebuf[0] != escape || value("interactive") == NULL) {
1731590Srgrimes			if (putline(collf, linebuf) < 0)
1741590Srgrimes				goto err;
1751590Srgrimes			continue;
1761590Srgrimes		}
1771590Srgrimes		c = linebuf[1];
1781590Srgrimes		switch (c) {
1791590Srgrimes		default:
1801590Srgrimes			/*
1811590Srgrimes			 * On double escape, just send the single one.
1821590Srgrimes			 * Otherwise, it's an error.
1831590Srgrimes			 */
1841590Srgrimes			if (c == escape) {
1851590Srgrimes				if (putline(collf, &linebuf[1]) < 0)
1861590Srgrimes					goto err;
1871590Srgrimes				else
1881590Srgrimes					break;
1891590Srgrimes			}
1901590Srgrimes			printf("Unknown tilde escape.\n");
1911590Srgrimes			break;
1921590Srgrimes		case 'C':
1931590Srgrimes			/*
1941590Srgrimes			 * Dump core.
1951590Srgrimes			 */
1961590Srgrimes			core();
1971590Srgrimes			break;
1981590Srgrimes		case '!':
1991590Srgrimes			/*
2001590Srgrimes			 * Shell escape, send the balance of the
2011590Srgrimes			 * line to sh -c.
2021590Srgrimes			 */
2031590Srgrimes			shell(&linebuf[2]);
2041590Srgrimes			break;
2051590Srgrimes		case ':':
2061590Srgrimes			/*
2071590Srgrimes			 * Escape to command mode, but be nice!
2081590Srgrimes			 */
2091590Srgrimes			execute(&linebuf[2], 1);
2101590Srgrimes			goto cont;
2111590Srgrimes		case '.':
2121590Srgrimes			/*
2131590Srgrimes			 * Simulate end of file on input.
2141590Srgrimes			 */
2151590Srgrimes			goto out;
2161590Srgrimes		case 'q':
2171590Srgrimes			/*
2181590Srgrimes			 * Force a quit of sending mail.
2191590Srgrimes			 * Act like an interrupt happened.
2201590Srgrimes			 */
2211590Srgrimes			hadintr++;
2221590Srgrimes			collint(SIGINT);
2231590Srgrimes			exit(1);
2241590Srgrimes		case 'h':
2251590Srgrimes			/*
2261590Srgrimes			 * Grab a bunch of headers.
2271590Srgrimes			 */
2281590Srgrimes			grabh(hp, GTO|GSUBJECT|GCC|GBCC);
2291590Srgrimes			goto cont;
2301590Srgrimes		case 't':
2311590Srgrimes			/*
2321590Srgrimes			 * Add to the To list.
2331590Srgrimes			 */
2341590Srgrimes			hp->h_to = cat(hp->h_to, extract(&linebuf[2], GTO));
2351590Srgrimes			break;
2361590Srgrimes		case 's':
2371590Srgrimes			/*
23832189Sjoerg			 * Set the Subject line.
2391590Srgrimes			 */
2401590Srgrimes			cp = &linebuf[2];
2411590Srgrimes			while (isspace(*cp))
2421590Srgrimes				cp++;
2431590Srgrimes			hp->h_subject = savestr(cp);
2441590Srgrimes			break;
24532189Sjoerg		case 'R':
24632189Sjoerg			/*
24732189Sjoerg			 * Set the Reply-To line.
24832189Sjoerg			 */
24932189Sjoerg			cp = &linebuf[2];
25032189Sjoerg			while (isspace(*cp))
25132189Sjoerg				cp++;
25232189Sjoerg			hp->h_replyto = savestr(cp);
25332189Sjoerg			break;
2541590Srgrimes		case 'c':
2551590Srgrimes			/*
2561590Srgrimes			 * Add to the CC list.
2571590Srgrimes			 */
2581590Srgrimes			hp->h_cc = cat(hp->h_cc, extract(&linebuf[2], GCC));
2591590Srgrimes			break;
2601590Srgrimes		case 'b':
2611590Srgrimes			/*
2621590Srgrimes			 * Add stuff to blind carbon copies list.
2631590Srgrimes			 */
2641590Srgrimes			hp->h_bcc = cat(hp->h_bcc, extract(&linebuf[2], GBCC));
2651590Srgrimes			break;
2661590Srgrimes		case 'd':
26774769Smikeh			if (strlcpy(linebuf + 2, getdeadletter(), sizeof(linebuf) - 2)
26874769Smikeh			    >= sizeof(linebuf) - 2) {
26974769Smikeh				printf("Line buffer overflow\n");
27074769Smikeh				break;
27174769Smikeh			}
2721590Srgrimes			/* fall into . . . */
2731590Srgrimes		case 'r':
2741590Srgrimes			/*
2751590Srgrimes			 * Invoke a file:
2761590Srgrimes			 * Search for the file name,
2771590Srgrimes			 * then open it and copy the contents to collf.
2781590Srgrimes			 */
2791590Srgrimes			cp = &linebuf[2];
2801590Srgrimes			while (isspace(*cp))
2811590Srgrimes				cp++;
2821590Srgrimes			if (*cp == '\0') {
2831590Srgrimes				printf("Interpolate what file?\n");
2841590Srgrimes				break;
2851590Srgrimes			}
2861590Srgrimes			cp = expand(cp);
28777274Smikeh			if (cp == NULL)
2881590Srgrimes				break;
2891590Srgrimes			if (isdir(cp)) {
2901590Srgrimes				printf("%s: Directory\n", cp);
2911590Srgrimes				break;
2921590Srgrimes			}
2931590Srgrimes			if ((fbuf = Fopen(cp, "r")) == NULL) {
29474769Smikeh				warn("%s", cp);
2951590Srgrimes				break;
2961590Srgrimes			}
2971590Srgrimes			printf("\"%s\" ", cp);
29877274Smikeh			(void)fflush(stdout);
2991590Srgrimes			lc = 0;
3001590Srgrimes			cc = 0;
3011590Srgrimes			while (readline(fbuf, linebuf, LINESIZE) >= 0) {
3021590Srgrimes				lc++;
3031590Srgrimes				if ((t = putline(collf, linebuf)) < 0) {
30477274Smikeh					(void)Fclose(fbuf);
3051590Srgrimes					goto err;
3061590Srgrimes				}
3071590Srgrimes				cc += t;
3081590Srgrimes			}
30977274Smikeh			(void)Fclose(fbuf);
3101590Srgrimes			printf("%d/%d\n", lc, cc);
3111590Srgrimes			break;
3121590Srgrimes		case 'w':
3131590Srgrimes			/*
3141590Srgrimes			 * Write the message on a file.
3151590Srgrimes			 */
3161590Srgrimes			cp = &linebuf[2];
3171590Srgrimes			while (*cp == ' ' || *cp == '\t')
3181590Srgrimes				cp++;
3191590Srgrimes			if (*cp == '\0') {
3201590Srgrimes				fprintf(stderr, "Write what file!?\n");
3211590Srgrimes				break;
3221590Srgrimes			}
32377274Smikeh			if ((cp = expand(cp)) == NULL)
3241590Srgrimes				break;
3251590Srgrimes			rewind(collf);
3261590Srgrimes			exwrite(cp, collf, 1);
3271590Srgrimes			break;
3281590Srgrimes		case 'm':
3291590Srgrimes		case 'M':
3301590Srgrimes		case 'f':
3311590Srgrimes		case 'F':
3321590Srgrimes			/*
3331590Srgrimes			 * Interpolate the named messages, if we
3341590Srgrimes			 * are in receiving mail mode.  Does the
3351590Srgrimes			 * standard list processing garbage.
3361590Srgrimes			 * If ~f is given, we don't shift over.
3371590Srgrimes			 */
33874769Smikeh			if (forward(linebuf + 2, collf, tempname, c) < 0)
3391590Srgrimes				goto err;
3401590Srgrimes			goto cont;
3411590Srgrimes		case '?':
3421590Srgrimes			if ((fbuf = Fopen(_PATH_TILDE, "r")) == NULL) {
34374769Smikeh				warn("%s", _PATH_TILDE);
3441590Srgrimes				break;
3451590Srgrimes			}
3461590Srgrimes			while ((t = getc(fbuf)) != EOF)
34777274Smikeh				(void)putchar(t);
34877274Smikeh			(void)Fclose(fbuf);
3491590Srgrimes			break;
3501590Srgrimes		case 'p':
3511590Srgrimes			/*
3521590Srgrimes			 * Print out the current state of the
3531590Srgrimes			 * message without altering anything.
3541590Srgrimes			 */
3551590Srgrimes			rewind(collf);
3561590Srgrimes			printf("-------\nMessage contains:\n");
3571590Srgrimes			puthead(hp, stdout, GTO|GSUBJECT|GCC|GBCC|GNL);
3581590Srgrimes			while ((t = getc(collf)) != EOF)
35977274Smikeh				(void)putchar(t);
3601590Srgrimes			goto cont;
3611590Srgrimes		case '|':
3621590Srgrimes			/*
3631590Srgrimes			 * Pipe message through command.
3641590Srgrimes			 * Collect output as new message.
3651590Srgrimes			 */
3661590Srgrimes			rewind(collf);
3671590Srgrimes			mespipe(collf, &linebuf[2]);
3681590Srgrimes			goto cont;
3691590Srgrimes		case 'v':
3701590Srgrimes		case 'e':
3711590Srgrimes			/*
3721590Srgrimes			 * Edit the current message.
3731590Srgrimes			 * 'e' means to use EDITOR
3741590Srgrimes			 * 'v' means to use VISUAL
3751590Srgrimes			 */
3761590Srgrimes			rewind(collf);
3771590Srgrimes			mesedit(collf, c);
3781590Srgrimes			goto cont;
3791590Srgrimes		}
3801590Srgrimes	}
3811590Srgrimes	goto out;
3821590Srgrimeserr:
3831590Srgrimes	if (collf != NULL) {
38477274Smikeh		(void)Fclose(collf);
3851590Srgrimes		collf = NULL;
3861590Srgrimes	}
3871590Srgrimesout:
3881590Srgrimes	if (collf != NULL)
3891590Srgrimes		rewind(collf);
3901590Srgrimes	noreset--;
39177274Smikeh	(void)sigblock(sigmask(SIGINT) | sigmask(SIGHUP));
39277274Smikeh	(void)signal(SIGINT, saveint);
39377274Smikeh	(void)signal(SIGHUP, savehup);
39477274Smikeh	(void)signal(SIGTSTP, savetstp);
39577274Smikeh	(void)signal(SIGTTOU, savettou);
39677274Smikeh	(void)signal(SIGTTIN, savettin);
39777274Smikeh	(void)sigsetmask(omask);
39877274Smikeh	return (collf);
3991590Srgrimes}
4001590Srgrimes
4011590Srgrimes/*
4021590Srgrimes * Write a file, ex-like if f set.
4031590Srgrimes */
4041590Srgrimesint
4051590Srgrimesexwrite(name, fp, f)
4061590Srgrimes	char name[];
4071590Srgrimes	FILE *fp;
4081590Srgrimes	int f;
4091590Srgrimes{
41077274Smikeh	FILE *of;
41177274Smikeh	int c, lc;
4121590Srgrimes	long cc;
4131590Srgrimes	struct stat junk;
4141590Srgrimes
4151590Srgrimes	if (f) {
4161590Srgrimes		printf("\"%s\" ", name);
41777274Smikeh		(void)fflush(stdout);
4181590Srgrimes	}
41974769Smikeh	if (stat(name, &junk) >= 0 && S_ISREG(junk.st_mode)) {
4201590Srgrimes		if (!f)
4211590Srgrimes			fprintf(stderr, "%s: ", name);
4221590Srgrimes		fprintf(stderr, "File exists\n");
42377274Smikeh		return (-1);
4241590Srgrimes	}
4251590Srgrimes	if ((of = Fopen(name, "w")) == NULL) {
42677274Smikeh		warn((char *)NULL);
42777274Smikeh		return (-1);
4281590Srgrimes	}
4291590Srgrimes	lc = 0;
4301590Srgrimes	cc = 0;
4311590Srgrimes	while ((c = getc(fp)) != EOF) {
4321590Srgrimes		cc++;
4331590Srgrimes		if (c == '\n')
4341590Srgrimes			lc++;
43577274Smikeh		(void)putc(c, of);
4361590Srgrimes		if (ferror(of)) {
43774769Smikeh			warnx("%s", name);
43877274Smikeh			(void)Fclose(of);
43977274Smikeh			return (-1);
4401590Srgrimes		}
4411590Srgrimes	}
44277274Smikeh	(void)Fclose(of);
4431590Srgrimes	printf("%d/%ld\n", lc, cc);
44477274Smikeh	(void)fflush(stdout);
44577274Smikeh	return (0);
4461590Srgrimes}
4471590Srgrimes
4481590Srgrimes/*
4491590Srgrimes * Edit the message being collected on fp.
4501590Srgrimes * On return, make the edit file the new temp file.
4511590Srgrimes */
4521590Srgrimesvoid
4531590Srgrimesmesedit(fp, c)
4541590Srgrimes	FILE *fp;
4551590Srgrimes	int c;
4561590Srgrimes{
4571590Srgrimes	sig_t sigint = signal(SIGINT, SIG_IGN);
4581590Srgrimes	FILE *nf = run_editor(fp, (off_t)-1, c, 0);
4591590Srgrimes
4601590Srgrimes	if (nf != NULL) {
46182793Sache		(void)fseeko(nf, (off_t)0, SEEK_END);
4621590Srgrimes		collf = nf;
46377274Smikeh		(void)Fclose(fp);
4641590Srgrimes	}
46577274Smikeh	(void)signal(SIGINT, sigint);
4661590Srgrimes}
4671590Srgrimes
4681590Srgrimes/*
4691590Srgrimes * Pipe the message through the command.
4701590Srgrimes * Old message is on stdin of command;
4711590Srgrimes * New message collected from stdout.
4721590Srgrimes * Sh -c must return 0 to accept the new message.
4731590Srgrimes */
4741590Srgrimesvoid
4751590Srgrimesmespipe(fp, cmd)
4761590Srgrimes	FILE *fp;
4771590Srgrimes	char cmd[];
4781590Srgrimes{
4791590Srgrimes	FILE *nf;
48074769Smikeh	int fd;
4811590Srgrimes	sig_t sigint = signal(SIGINT, SIG_IGN);
48277274Smikeh	char *sh, tempname[PATHSIZE];
4831590Srgrimes
48477274Smikeh	(void)snprintf(tempname, sizeof(tempname),
48577274Smikeh	    "%s/mail.ReXXXXXXXXXX", tmpdir);
48674769Smikeh	if ((fd = mkstemp(tempname)) == -1 ||
48774769Smikeh	    (nf = Fdopen(fd, "w+")) == NULL) {
48874769Smikeh		warn("%s", tempname);
4891590Srgrimes		goto out;
4901590Srgrimes	}
49177274Smikeh	(void)rm(tempname);
4921590Srgrimes	/*
4931590Srgrimes	 * stdin = current message.
4941590Srgrimes	 * stdout = new message.
4951590Srgrimes	 */
49677274Smikeh	if ((sh = value("SHELL")) == NULL)
49777274Smikeh		sh = _PATH_CSHELL;
49877274Smikeh	if (run_command(sh,
49977274Smikeh	    0, fileno(fp), fileno(nf), "-c", cmd, NULL) < 0) {
50077274Smikeh		(void)Fclose(nf);
5011590Srgrimes		goto out;
5021590Srgrimes	}
5031590Srgrimes	if (fsize(nf) == 0) {
5041590Srgrimes		fprintf(stderr, "No bytes from \"%s\" !?\n", cmd);
50577274Smikeh		(void)Fclose(nf);
5061590Srgrimes		goto out;
5071590Srgrimes	}
5081590Srgrimes	/*
5091590Srgrimes	 * Take new files.
5101590Srgrimes	 */
51182793Sache	(void)fseeko(nf, (off_t)0, SEEK_END);
5121590Srgrimes	collf = nf;
51377274Smikeh	(void)Fclose(fp);
5141590Srgrimesout:
51577274Smikeh	(void)signal(SIGINT, sigint);
5161590Srgrimes}
5171590Srgrimes
5181590Srgrimes/*
5191590Srgrimes * Interpolate the named messages into the current
5201590Srgrimes * message, preceding each line with a tab.
5211590Srgrimes * Return a count of the number of characters now in
5221590Srgrimes * the message, or -1 if an error is encountered writing
5231590Srgrimes * the message temporary.  The flag argument is 'm' if we
5241590Srgrimes * should shift over and 'f' if not.
5251590Srgrimes */
5261590Srgrimesint
52774769Smikehforward(ms, fp, fn, f)
5281590Srgrimes	char ms[];
5291590Srgrimes	FILE *fp;
53074769Smikeh	char *fn;
5311590Srgrimes	int f;
5321590Srgrimes{
53377274Smikeh	int *msgvec;
5341590Srgrimes	struct ignoretab *ig;
5351590Srgrimes	char *tabst;
5361590Srgrimes
53777274Smikeh	msgvec = (int *)salloc((msgCount+1) * sizeof(*msgvec));
53877274Smikeh	if (msgvec == NULL)
53977274Smikeh		return (0);
5401590Srgrimes	if (getmsglist(ms, msgvec, 0) < 0)
54177274Smikeh		return (0);
5421590Srgrimes	if (*msgvec == 0) {
5431590Srgrimes		*msgvec = first(0, MMNORM);
54429574Sphk		if (*msgvec == 0) {
5451590Srgrimes			printf("No appropriate messages\n");
54677274Smikeh			return (0);
5471590Srgrimes		}
54829574Sphk		msgvec[1] = 0;
5491590Srgrimes	}
5501590Srgrimes	if (f == 'f' || f == 'F')
55177274Smikeh		tabst = NULL;
55277274Smikeh	else if ((tabst = value("indentprefix")) == NULL)
5531590Srgrimes		tabst = "\t";
5541590Srgrimes	ig = isupper(f) ? NULL : ignore;
5551590Srgrimes	printf("Interpolating:");
5561590Srgrimes	for (; *msgvec != 0; msgvec++) {
5571590Srgrimes		struct message *mp = message + *msgvec - 1;
5581590Srgrimes
5591590Srgrimes		touch(mp);
5601590Srgrimes		printf(" %d", *msgvec);
56174769Smikeh		if (sendmessage(mp, fp, ig, tabst) < 0) {
56274769Smikeh			warnx("%s", fn);
56377274Smikeh			return (-1);
5641590Srgrimes		}
5651590Srgrimes	}
5661590Srgrimes	printf("\n");
56777274Smikeh	return (0);
5681590Srgrimes}
5691590Srgrimes
5701590Srgrimes/*
5711590Srgrimes * Print (continue) when continued after ^Z.
5721590Srgrimes */
5731590Srgrimes/*ARGSUSED*/
5741590Srgrimesvoid
5751590Srgrimescollstop(s)
5761590Srgrimes	int s;
5771590Srgrimes{
5781590Srgrimes	sig_t old_action = signal(s, SIG_DFL);
5791590Srgrimes
58077274Smikeh	(void)sigsetmask(sigblock(0) & ~sigmask(s));
58177274Smikeh	(void)kill(0, s);
58277274Smikeh	(void)sigblock(sigmask(s));
58377274Smikeh	(void)signal(s, old_action);
5841590Srgrimes	if (colljmp_p) {
5851590Srgrimes		colljmp_p = 0;
5861590Srgrimes		hadintr = 0;
5871590Srgrimes		longjmp(colljmp, 1);
5881590Srgrimes	}
5891590Srgrimes}
5901590Srgrimes
5911590Srgrimes/*
5921590Srgrimes * On interrupt, come here to save the partial message in ~/dead.letter.
5931590Srgrimes * Then jump out of the collection loop.
5941590Srgrimes */
5951590Srgrimes/*ARGSUSED*/
5961590Srgrimesvoid
5971590Srgrimescollint(s)
5981590Srgrimes	int s;
5991590Srgrimes{
6001590Srgrimes	/*
6011590Srgrimes	 * the control flow is subtle, because we can be called from ~q.
6021590Srgrimes	 */
6031590Srgrimes	if (!hadintr) {
60477274Smikeh		if (value("ignore") != NULL) {
60577274Smikeh			printf("@");
60677274Smikeh			(void)fflush(stdout);
6071590Srgrimes			clearerr(stdin);
6081590Srgrimes			return;
6091590Srgrimes		}
6101590Srgrimes		hadintr = 1;
6111590Srgrimes		longjmp(colljmp, 1);
6121590Srgrimes	}
6131590Srgrimes	rewind(collf);
61477274Smikeh	if (value("nosave") == NULL)
6151590Srgrimes		savedeadletter(collf);
6161590Srgrimes	longjmp(collabort, 1);
6171590Srgrimes}
6181590Srgrimes
6191590Srgrimes/*ARGSUSED*/
6201590Srgrimesvoid
6211590Srgrimescollhup(s)
6221590Srgrimes	int s;
6231590Srgrimes{
6241590Srgrimes	rewind(collf);
6251590Srgrimes	savedeadletter(collf);
6261590Srgrimes	/*
6271590Srgrimes	 * Let's pretend nobody else wants to clean up,
6281590Srgrimes	 * a true statement at this time.
6291590Srgrimes	 */
6301590Srgrimes	exit(1);
6311590Srgrimes}
6321590Srgrimes
6331590Srgrimesvoid
6341590Srgrimessavedeadletter(fp)
63577274Smikeh	FILE *fp;
6361590Srgrimes{
63777274Smikeh	FILE *dbuf;
63877274Smikeh	int c;
6391590Srgrimes	char *cp;
6401590Srgrimes
6411590Srgrimes	if (fsize(fp) == 0)
6421590Srgrimes		return;
6431590Srgrimes	cp = getdeadletter();
6441590Srgrimes	c = umask(077);
6451590Srgrimes	dbuf = Fopen(cp, "a");
64677274Smikeh	(void)umask(c);
6471590Srgrimes	if (dbuf == NULL)
6481590Srgrimes		return;
6491590Srgrimes	while ((c = getc(fp)) != EOF)
65077274Smikeh		(void)putc(c, dbuf);
65177274Smikeh	(void)Fclose(dbuf);
6521590Srgrimes	rewind(fp);
6531590Srgrimes}
654