xref: /netbsd-src/usr.bin/mail/collect.c (revision 4fe1ef32f37334167b71a62286b21da7d9542909)
1*4fe1ef32Schristos /*	$NetBSD: collect.c,v 1.49 2017/11/09 20:27:50 christos Exp $	*/
288b833a7Schristos 
361f28255Scgd /*
42cb5542fSderaadt  * Copyright (c) 1980, 1993
52cb5542fSderaadt  *	The Regents of the University of California.  All rights reserved.
661f28255Scgd  *
761f28255Scgd  * Redistribution and use in source and binary forms, with or without
861f28255Scgd  * modification, are permitted provided that the following conditions
961f28255Scgd  * are met:
1061f28255Scgd  * 1. Redistributions of source code must retain the above copyright
1161f28255Scgd  *    notice, this list of conditions and the following disclaimer.
1261f28255Scgd  * 2. Redistributions in binary form must reproduce the above copyright
1361f28255Scgd  *    notice, this list of conditions and the following disclaimer in the
1461f28255Scgd  *    documentation and/or other materials provided with the distribution.
1589aaa1bbSagc  * 3. Neither the name of the University nor the names of its contributors
1661f28255Scgd  *    may be used to endorse or promote products derived from this software
1761f28255Scgd  *    without specific prior written permission.
1861f28255Scgd  *
1961f28255Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2061f28255Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2161f28255Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2261f28255Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2361f28255Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2461f28255Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2561f28255Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2661f28255Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2761f28255Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2861f28255Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2961f28255Scgd  * SUCH DAMAGE.
3061f28255Scgd  */
3161f28255Scgd 
327c81c8f3Slukem #include <sys/cdefs.h>
3361f28255Scgd #ifndef lint
3488b833a7Schristos #if 0
3588b833a7Schristos static char sccsid[] = "@(#)collect.c	8.2 (Berkeley) 4/19/94";
3688b833a7Schristos #else
37*4fe1ef32Schristos __RCSID("$NetBSD: collect.c,v 1.49 2017/11/09 20:27:50 christos Exp $");
3888b833a7Schristos #endif
3961f28255Scgd #endif /* not lint */
4061f28255Scgd 
4161f28255Scgd /*
4261f28255Scgd  * Mail -- a mail program
4361f28255Scgd  *
4461f28255Scgd  * Collect input from standard input, handling
4561f28255Scgd  * ~ escapes.
4661f28255Scgd  */
4761f28255Scgd 
48798fbc60Schristos #include <assert.h>
49798fbc60Schristos #include <util.h>
50798fbc60Schristos 
5161f28255Scgd #include "rcv.h"
522cb5542fSderaadt #include "extern.h"
53798fbc60Schristos #include "format.h"
548207b28aSchristos #ifdef MIME_SUPPORT
558207b28aSchristos #include "mime.h"
568207b28aSchristos #endif
57ca13337dSchristos #include "sig.h"
58f3098750Schristos #include "thread.h"
5961f28255Scgd 
6085ef0603Schristos 
6161f28255Scgd /*
626e6e3265Swiz  * Read a message from standard input and return a read file to it
6361f28255Scgd  * or NULL on error.
6461f28255Scgd  */
6561f28255Scgd 
6661f28255Scgd /*
6761f28255Scgd  * The following hokiness with global variables is so that on
6861f28255Scgd  * receipt of an interrupt signal, the partial message can be salted
6961f28255Scgd  * away on dead.letter.
7061f28255Scgd  */
7161f28255Scgd static	FILE	*collf;			/* File for saving away */
7261f28255Scgd static	int	hadintr;		/* Have seen one SIGINT so far */
7361f28255Scgd 
74ca13337dSchristos static 	jmp_buf	abort_jmpbuf;		/* To end collection with error */
75ca13337dSchristos static 	jmp_buf	reset_jmpbuf;		/* To get back to work */
76ca13337dSchristos static	int	reset_on_stop;		/* To do job control longjmp. */
7785c81c58Schristos 
78f3098750Schristos /*
79f3098750Schristos  * Write a file, ex-like if f set.
80f3098750Schristos  */
81f3098750Schristos static int
exwrite(const char name[],FILE * fp,int f)82f3098750Schristos exwrite(const char name[], FILE *fp, int f)
83f3098750Schristos {
84f3098750Schristos 	FILE *of;
85f3098750Schristos 	int c;
86f3098750Schristos 	long cc;
87f3098750Schristos 	int lc;
88f3098750Schristos 	struct stat junk;
89f3098750Schristos 
90f3098750Schristos 	if (f) {
91f3098750Schristos 		(void)printf("\"%s\" ", name);
92f3098750Schristos 		(void)fflush(stdout);
93f3098750Schristos 	}
94f3098750Schristos 	if (stat(name, &junk) >= 0 && S_ISREG(junk.st_mode)) {
95f3098750Schristos 		if (!f)
96f3098750Schristos 			(void)fprintf(stderr, "%s: ", name);
97f3098750Schristos 		(void)fprintf(stderr, "File exists\n");
98f3098750Schristos 		return -1;
99f3098750Schristos 	}
1005942983dSchristos 	if ((of = Fopen(name, "we")) == NULL) {
101f3098750Schristos 		warn("%s", name);
102f3098750Schristos 		return -1;
103f3098750Schristos 	}
104f3098750Schristos 	lc = 0;
105f3098750Schristos 	cc = 0;
106f3098750Schristos 	while ((c = getc(fp)) != EOF) {
107f3098750Schristos 		cc++;
108f3098750Schristos 		if (c == '\n')
109f3098750Schristos 			lc++;
110f3098750Schristos 		(void)putc(c, of);
111f3098750Schristos 		if (ferror(of)) {
112f3098750Schristos 			warn("%s", name);
113f3098750Schristos 			(void)Fclose(of);
114f3098750Schristos 			return -1;
115f3098750Schristos 		}
116f3098750Schristos 	}
117f3098750Schristos 	(void)Fclose(of);
118f3098750Schristos 	(void)printf("%d/%ld\n", lc, cc);
119f3098750Schristos 	(void)fflush(stdout);
120f3098750Schristos 	return 0;
121f3098750Schristos }
122f3098750Schristos 
123f3098750Schristos /*
124f3098750Schristos  * Edit the message being collected on fp.
125f3098750Schristos  * On return, make the edit file the new temp file.
126f3098750Schristos  */
127f3098750Schristos static void
mesedit(FILE * fp,int c)128f3098750Schristos mesedit(FILE *fp, int c)
129f3098750Schristos {
130ca13337dSchristos 	struct sigaction osa;
131ca13337dSchristos 	sigset_t oset;
132ca13337dSchristos 	FILE *nf;
133f3098750Schristos 
134ca13337dSchristos 	sig_check();
135ca13337dSchristos 	(void)sig_ignore(SIGINT, &osa, &oset);
136ca13337dSchristos 	nf = run_editor(fp, (off_t)-1, c, 0);
137f3098750Schristos 	if (nf != NULL) {
138f3098750Schristos 		(void)fseek(nf, 0L, 2);
139f3098750Schristos 		collf = nf;
140f3098750Schristos 		(void)Fclose(fp);
141f3098750Schristos 	}
142ca13337dSchristos 	(void)sig_restore(SIGINT, &osa, &oset);
143ca13337dSchristos 	sig_check();
144f3098750Schristos }
145f3098750Schristos 
146f3098750Schristos /*
147f3098750Schristos  * Pipe the message through the command.
148f3098750Schristos  * Old message is on stdin of command;
149f3098750Schristos  * New message collected from stdout.
150f3098750Schristos  * Sh -c must return 0 to accept the new message.
151f3098750Schristos  */
152f3098750Schristos static void
mespipe(FILE * fp,char cmd[])153f3098750Schristos mespipe(FILE *fp, char cmd[])
154f3098750Schristos {
155f3098750Schristos 	FILE *nf;
156ca13337dSchristos 	struct sigaction osa;
157ca13337dSchristos 	sigset_t oset;
158f3098750Schristos 	const char *shellcmd;
159f3098750Schristos 	int fd;
160f3098750Schristos 	char tempname[PATHSIZE];
161f3098750Schristos 
162ca13337dSchristos 	sig_check();
163ca13337dSchristos 	(void)sig_ignore(SIGINT, &osa, &oset);
164ca13337dSchristos 
165f3098750Schristos 	(void)snprintf(tempname, sizeof(tempname),
166f3098750Schristos 	    "%s/mail.ReXXXXXXXXXX", tmpdir);
167f3098750Schristos 	if ((fd = mkstemp(tempname)) == -1 ||
168*4fe1ef32Schristos 	    (nf = Fdopen(fd, "wef+")) == NULL) {
169f3098750Schristos 		if (fd != -1)
170f3098750Schristos 			(void)close(fd);
171f3098750Schristos 		warn("%s", tempname);
172f3098750Schristos 		goto out;
173f3098750Schristos 	}
174f3098750Schristos 	(void)unlink(tempname);
175f3098750Schristos 	/*
176f3098750Schristos 	 * stdin = current message.
177f3098750Schristos 	 * stdout = new message.
178f3098750Schristos 	 */
179f3098750Schristos 	if ((shellcmd = value(ENAME_SHELL)) == NULL)
180f3098750Schristos 		shellcmd = _PATH_CSHELL;
181f3098750Schristos 	if (run_command(shellcmd,
182ca13337dSchristos 	    NULL, fileno(fp), fileno(nf), "-c", cmd, NULL) < 0) {
183f3098750Schristos 		(void)Fclose(nf);
184f3098750Schristos 		goto out;
185f3098750Schristos 	}
186f3098750Schristos 	if (fsize(nf) == 0) {
187f3098750Schristos 		(void)fprintf(stderr, "No bytes from \"%s\" !?\n", cmd);
188f3098750Schristos 		(void)Fclose(nf);
189f3098750Schristos 		goto out;
190f3098750Schristos 	}
191f3098750Schristos 	/*
192f3098750Schristos 	 * Take new files.
193f3098750Schristos 	 */
194f3098750Schristos 	(void)fseek(nf, 0L, 2);
195f3098750Schristos 	collf = nf;
196f3098750Schristos 	(void)Fclose(fp);
197f3098750Schristos  out:
198ca13337dSchristos 	(void)sig_restore(SIGINT, &osa, &oset);
199ca13337dSchristos 	sig_check();
200f3098750Schristos }
201f3098750Schristos 
202f3098750Schristos /*
203f3098750Schristos  * Interpolate the named messages into the current
204f3098750Schristos  * message, preceding each line with a tab.
205f3098750Schristos  * Return a count of the number of characters now in
206f3098750Schristos  * the message, or -1 if an error is encountered writing
207f3098750Schristos  * the message temporary.  The flag argument is 'm' if we
208f3098750Schristos  * should shift over and 'f' if not.
209f3098750Schristos  */
210f3098750Schristos static int
interpolate(char ms[],FILE * fp,char * fn,int f)2114556f89aSchristos interpolate(char ms[], FILE *fp, char *fn, int f)
212f3098750Schristos {
213f3098750Schristos 	int *msgvec;
214f3098750Schristos 	struct ignoretab *ig;
215f3098750Schristos 	const char *tabst;
216f3098750Schristos #ifdef MIME_SUPPORT
217f3098750Schristos 	struct mime_info *mip;
218f3098750Schristos 	int retval;
219f3098750Schristos #endif
2202283d346Schristos 	msgvec = salloc((get_msgCount() + 1) * sizeof(*msgvec));
221f3098750Schristos 	if (msgvec == NULL)
222f3098750Schristos 		return 0;
223f3098750Schristos 	if (getmsglist(ms, msgvec, 0) < 0)
224f3098750Schristos 		return 0;
225f3098750Schristos 	if (*msgvec == 0) {
226f3098750Schristos 		*msgvec = first(0, MMNORM);
227f3098750Schristos 		if (*msgvec == 0) {
228f3098750Schristos 			(void)printf("No appropriate messages\n");
229f3098750Schristos 			return 0;
230f3098750Schristos 		}
231f3098750Schristos 		msgvec[1] = 0;
232f3098750Schristos 	}
233f3098750Schristos 	if (f == 'f' || f == 'F')
234f3098750Schristos 		tabst = NULL;
235f3098750Schristos 	else if ((tabst = value(ENAME_INDENTPREFIX)) == NULL)
236f3098750Schristos 		tabst = "\t";
237f3098750Schristos 	ig = isupper(f) ? NULL : ignore;
238f3098750Schristos 	(void)printf("Interpolating:");
239d727506fSchristos 	for (/*EMPTY*/; *msgvec != 0; msgvec++) {
240f3098750Schristos 		struct message *mp;
241f3098750Schristos 		char *fmtstr;
242f3098750Schristos 
243f3098750Schristos 		mp = get_message(*msgvec);
244f3098750Schristos 		touch(mp);
245f3098750Schristos 		(void)printf(" %d", *msgvec);
246f3098750Schristos 		(void)fflush(stdout);	/* flush stdout and the above */
247f3098750Schristos 
248f3098750Schristos 		if (tabst && (fmtstr = value(ENAME_INDENT_PREAMBLE)) != NULL)
249f3098750Schristos 			fmsgprintf(collf, fmtstr, mp);
250f3098750Schristos #ifdef MIME_SUPPORT
251f3098750Schristos 		mip = NULL;
252f3098750Schristos 		if (value(ENAME_MIME_DECODE_MSG)) {
253f3098750Schristos 			if ((tabst == NULL && value(ENAME_MIME_DECODE_INSERT)) ||
254f3098750Schristos 			    (tabst != NULL && value(ENAME_MIME_DECODE_QUOTE)))
255f3098750Schristos 				mip = mime_decode_open(mp);
256f3098750Schristos 		}
257f3098750Schristos 		retval = mime_sendmessage(mp, fp, ig, tabst, mip);
258f3098750Schristos 		mime_decode_close(mip);
259f3098750Schristos 		if (retval < 0)
260f3098750Schristos #else
261f3098750Schristos 		if (sendmessage(mp, fp, ig, tabst, NULL) < 0)
262f3098750Schristos #endif
263f3098750Schristos 		{
264f3098750Schristos 			warn("%s", fn);
265f3098750Schristos 			return -1;
266f3098750Schristos 		}
267f3098750Schristos 		if (tabst && (fmtstr = value(ENAME_INDENT_POSTSCRIPT)) != NULL)
268f3098750Schristos 			fmsgprintf(collf, fmtstr, mp);
269f3098750Schristos 	}
270f3098750Schristos 	(void)printf("\n");
271f3098750Schristos 	return 0;
272f3098750Schristos }
273f3098750Schristos 
274f3098750Schristos /*
275f3098750Schristos  * Append the contents of the file to the end of the deadletter file.
276f3098750Schristos  */
277f3098750Schristos PUBLIC void
savedeadletter(FILE * fp)278f3098750Schristos savedeadletter(FILE *fp)
279f3098750Schristos {
280f3098750Schristos 	FILE *dbuf;
281f3098750Schristos 	mode_t m;
282f3098750Schristos 	int c;
283f3098750Schristos 	const char *cp;
284f3098750Schristos 
285f3098750Schristos 	if (fsize(fp) == 0)
286f3098750Schristos 		return;
287f3098750Schristos 	cp = getdeadletter();
288f3098750Schristos 	m = umask(077);
2895942983dSchristos 	dbuf = Fopen(cp, "ae");
290f3098750Schristos 	(void)umask(m);
291f3098750Schristos 	if (dbuf == NULL)
292f3098750Schristos 		return;
293ca13337dSchristos 	(void)printf("Saving message body to `%s'.\n", cp);
294f3098750Schristos 	while ((c = getc(fp)) != EOF)
295f3098750Schristos 		(void)putc(c, dbuf);
296f3098750Schristos 	(void)Fclose(dbuf);
297f3098750Schristos 	rewind(fp);
298f3098750Schristos }
299f3098750Schristos 
300f3098750Schristos /*
301f3098750Schristos  * On interrupt, come here to save the partial message in ~/dead.letter.
302f3098750Schristos  * Then jump out of the collection loop.
303f3098750Schristos  */
304f3098750Schristos static void
coll_int(int signo)305ca13337dSchristos coll_int(int signo)
306f3098750Schristos {
3078e33b891Schristos 	sig_t o = signal(SIGINT, SIG_IGN);
308ca13337dSchristos 
309f3098750Schristos 	/*
310f3098750Schristos 	 * the control flow is subtle, because we can be called from ~q.
311f3098750Schristos 	 */
312f3098750Schristos 	if (!hadintr) {
313f3098750Schristos 		if (value(ENAME_IGNORE) != NULL) {
314f3098750Schristos 			(void)puts("@");
315f3098750Schristos 			(void)fflush(stdout);
316f3098750Schristos 			clearerr(stdin);
3173cc72f88Schristos 			signal(SIGINT, o);
318f3098750Schristos 			return;
319f3098750Schristos 		}
320f3098750Schristos 		hadintr = 1;
3213cc72f88Schristos 		signal(SIGINT, o);
322ca13337dSchristos 		longjmp(reset_jmpbuf, signo);
323f3098750Schristos 	}
3248e33b891Schristos 	if (collf) {
325f3098750Schristos 		rewind(collf);
326f3098750Schristos 		if (value(ENAME_NOSAVE) == NULL)
327f3098750Schristos 			savedeadletter(collf);
3288e33b891Schristos 	}
3298e33b891Schristos 	signal(SIGINT, o);
330ca13337dSchristos 	longjmp(abort_jmpbuf, signo);
331f3098750Schristos }
332f3098750Schristos 
333f3098750Schristos /*ARGSUSED*/
3346818646aSjoerg __dead static void
coll_hup(int signo __unused)335ca13337dSchristos coll_hup(int signo __unused)
336f3098750Schristos {
337ca13337dSchristos 
338f3098750Schristos 	rewind(collf);
339f3098750Schristos 	savedeadletter(collf);
340f3098750Schristos 	/*
341f3098750Schristos 	 * Let's pretend nobody else wants to clean up,
342f3098750Schristos 	 * a true statement at this time.
343f3098750Schristos 	 */
344ca13337dSchristos 	exit(EXIT_FAILURE);
345ca13337dSchristos }
346ca13337dSchristos 
347ca13337dSchristos /*
348ca13337dSchristos  * Print (continue) when continued after ^Z.
349ca13337dSchristos  */
350ca13337dSchristos static void
coll_stop(int signo)351ca13337dSchristos coll_stop(int signo)
352ca13337dSchristos {
353ca13337dSchristos 
354ca13337dSchristos 	if (reset_on_stop) {
355ca13337dSchristos 		reset_on_stop = 0;
356ca13337dSchristos 		hadintr = 0;
357ca13337dSchristos 		longjmp(reset_jmpbuf, signo);
358ca13337dSchristos 	}
359f3098750Schristos }
360f3098750Schristos 
361f3098750Schristos PUBLIC FILE *
collect(struct header * hp,int printheaders)362b127ccccSwiz collect(struct header *hp, int printheaders)
36361f28255Scgd {
364a7879b44Schristos 	sig_t volatile old_sigint = sig_current(SIGINT);
365a7879b44Schristos 	sig_t volatile old_sighup = sig_current(SIGHUP);
366a7879b44Schristos 	sig_t volatile old_sigtstp = sig_current(SIGTSTP);
367a7879b44Schristos 	sig_t volatile old_sigttin = sig_current(SIGTTIN);
368a7879b44Schristos 	sig_t volatile old_sigttou = sig_current(SIGTTOU);
36961f28255Scgd 	FILE *fbuf;
370f0f6b1cdSchristos 	int lc, cc;
371443084c8Swiz 	int c, fd, t;
372ece0fd5cSchristos 	char linebuf[LINESIZE];
373ece0fd5cSchristos 	const char *cp;
374443084c8Swiz 	char tempname[PATHSIZE];
375240d8221Swiz 	char mailtempname[PATHSIZE];
3768207b28aSchristos 	int eofcount;
3778207b28aSchristos 	int longline;
378ca13337dSchristos 	int lastlong, rc;	/* So we don't make 2 or more lines
379ca13337dSchristos 				   out of a long input line. */
380f0f6b1cdSchristos 
381f0f6b1cdSchristos 	/* The following are declared volatile to avoid longjmp clobbering. */
3828207b28aSchristos 	char volatile getsub;
3838207b28aSchristos 	int volatile escape;
38461f28255Scgd 
385ca286310Schristos 	(void)memset(mailtempname, 0, sizeof(mailtempname));
38661f28255Scgd 	collf = NULL;
387ca13337dSchristos 
388ca13337dSchristos 	if (setjmp(abort_jmpbuf) || setjmp(reset_jmpbuf)) {
389240d8221Swiz 		(void)rm(mailtempname);
39061f28255Scgd 		goto err;
39161f28255Scgd 	}
392ca13337dSchristos 	sig_check();
393ca13337dSchristos 
394ca13337dSchristos 	sig_hold();
395ca13337dSchristos 	old_sigint  = sig_signal(SIGINT,  coll_int);
396ca13337dSchristos 	old_sighup  = sig_signal(SIGHUP,  coll_hup);
397ca13337dSchristos 	old_sigtstp = sig_signal(SIGTSTP, coll_stop);
398ca13337dSchristos 	old_sigttin = sig_signal(SIGTTIN, coll_stop);
399ca13337dSchristos 	old_sigttou = sig_signal(SIGTTOU, coll_stop);
400ca13337dSchristos 	sig_release();
40161f28255Scgd 
40261f28255Scgd 	noreset++;
403240d8221Swiz 	(void)snprintf(mailtempname, sizeof(mailtempname),
404240d8221Swiz 	    "%s/mail.RsXXXXXXXXXX", tmpdir);
405240d8221Swiz 	if ((fd = mkstemp(mailtempname)) == -1 ||
406*4fe1ef32Schristos 	    (collf = Fdopen(fd, "wef+")) == NULL) {
407240d8221Swiz 		if (fd != -1)
408ca286310Schristos 			(void)close(fd);
409240d8221Swiz 		warn("%s", mailtempname);
41061f28255Scgd 		goto err;
41161f28255Scgd 	}
412240d8221Swiz 	(void)rm(mailtempname);
41361f28255Scgd 
41461f28255Scgd 	/*
41561f28255Scgd 	 * If we are going to prompt for a subject,
41661f28255Scgd 	 * refrain from printing a newline after
41761f28255Scgd 	 * the headers (since some people mind).
41861f28255Scgd 	 */
41985c81c58Schristos 	t = GTO | GSUBJECT | GCC | GNL | GSMOPTS;
42061f28255Scgd 	getsub = 0;
421f3098750Schristos 	if (hp->h_subject == NULL && value(ENAME_INTERACTIVE) != NULL &&
422ca13337dSchristos 	    (value(ENAME_ASK) != NULL || value(ENAME_ASKSUB) != NULL)) {
423ca13337dSchristos 		t &= ~GNL;
424ca13337dSchristos 		getsub++;
425ca13337dSchristos 	}
42661f28255Scgd 	if (printheaders) {
427ca286310Schristos 		(void)puthead(hp, stdout, t);
428ca286310Schristos 		(void)fflush(stdout);
42961f28255Scgd 	}
430f3098750Schristos 	if ((cp = value(ENAME_ESCAPE)) != NULL)
43161f28255Scgd 		escape = *cp;
43261f28255Scgd 	else
43361f28255Scgd 		escape = ESCAPE;
434ca13337dSchristos 	hadintr = 0;
435ca13337dSchristos 	if (setjmp(reset_jmpbuf) == 0) {
43661f28255Scgd 		if (getsub)
437ca286310Schristos 			(void)grabh(hp, GSUBJECT);
43861f28255Scgd 	} else {
43961f28255Scgd 		/*
44061f28255Scgd 		 * Come here for printing the after-signal message.
44161f28255Scgd 		 * Duplicate messages won't be printed because
44261f28255Scgd 		 * the write is aborted if we get a SIGTTOU.
44361f28255Scgd 		 */
44461f28255Scgd  cont:
44561f28255Scgd 		if (hadintr) {
446ca286310Schristos 			(void)fflush(stdout);
447ca286310Schristos 			(void)fprintf(stderr,
44861f28255Scgd 			"\n(Interrupt -- one more to kill letter)\n");
44961f28255Scgd 		} else {
450ca286310Schristos 			(void)printf("(continue)\n");
451ca286310Schristos 			(void)fflush(stdout);
45261f28255Scgd 		}
45361f28255Scgd 	}
4548207b28aSchristos 	eofcount = 0;	/* reset after possible longjmp */
4558207b28aSchristos 	longline = 0;	/* reset after possible longjmp */
45661f28255Scgd 	for (;;) {
457ca13337dSchristos 		reset_on_stop = 1;
458ca13337dSchristos 		c = readline(stdin, linebuf, LINESIZE, reset_on_stop);
459ca13337dSchristos 		reset_on_stop = 0;
460ca13337dSchristos 
46185c81c58Schristos 		if (c < 0) {
46285c81c58Schristos 			char *p;
463ca13337dSchristos 
464f3098750Schristos 			if (value(ENAME_INTERACTIVE) != NULL &&
465f3098750Schristos 			    (p = value(ENAME_IGNOREEOF)) != NULL &&
46685c81c58Schristos 			    ++eofcount < (*p == 0 ? 25 : atoi(p))) {
46785c81c58Schristos 				(void)printf("Use \".\" to terminate letter\n");
46885c81c58Schristos 				continue;
46985c81c58Schristos 			}
47085c81c58Schristos 			break;
47185c81c58Schristos 		}
472a8316bbcSphil 		lastlong = longline;
473a8316bbcSphil 		longline = c == LINESIZE - 1;
47461f28255Scgd 		eofcount = 0;
47561f28255Scgd 		hadintr = 0;
47661f28255Scgd 		if (linebuf[0] == '.' && linebuf[1] == '\0' &&
477f3098750Schristos 		    value(ENAME_INTERACTIVE) != NULL && !lastlong &&
478f3098750Schristos 		    (value(ENAME_DOT) != NULL || value(ENAME_IGNOREEOF) != NULL))
47961f28255Scgd 			break;
480f3098750Schristos 		if (linebuf[0] != escape || value(ENAME_INTERACTIVE) == NULL ||
481a8316bbcSphil 		    lastlong) {
482a8316bbcSphil 			if (putline(collf, linebuf, !longline) < 0)
48361f28255Scgd 				goto err;
48461f28255Scgd 			continue;
48561f28255Scgd 		}
48661f28255Scgd 		c = linebuf[1];
48761f28255Scgd 		switch (c) {
48861f28255Scgd 		default:
48961f28255Scgd 			/*
49061f28255Scgd 			 * On double escape, just send the single one.
49161f28255Scgd 			 * Otherwise, it's an error.
49261f28255Scgd 			 */
49361f28255Scgd 			if (c == escape) {
494a8316bbcSphil 				if (putline(collf, &linebuf[1], !longline) < 0)
49561f28255Scgd 					goto err;
49661f28255Scgd 				else
49761f28255Scgd 					break;
49861f28255Scgd 			}
499ca286310Schristos 			(void)printf("Unknown tilde escape.\n");
50061f28255Scgd 			break;
5018207b28aSchristos #ifdef MIME_SUPPORT
5028207b28aSchristos 		case '@':
503798fbc60Schristos 			hp->h_attach = mime_attach_files(hp->h_attach, &linebuf[2]);
5048207b28aSchristos 			break;
5058207b28aSchristos #endif
50661f28255Scgd 		case 'C':
50761f28255Scgd 			/*
50861f28255Scgd 			 * Dump core.
50961f28255Scgd 			 */
510ca286310Schristos 			(void)core(NULL);
51161f28255Scgd 			break;
51261f28255Scgd 		case '!':
51361f28255Scgd 			/*
51461f28255Scgd 			 * Shell escape, send the balance of the
51561f28255Scgd 			 * line to sh -c.
51661f28255Scgd 			 */
517ca286310Schristos 			(void)shell(&linebuf[2]);
51861f28255Scgd 			break;
51961f28255Scgd 		case ':':
520a067af72Sjtc 		case '_':
52161f28255Scgd 			/*
52261f28255Scgd 			 * Escape to command mode, but be nice!
52361f28255Scgd 			 */
5248d6767f0Schristos 			(void)execute(&linebuf[2], ec_composing);
52561f28255Scgd 			goto cont;
52661f28255Scgd 		case '.':
52761f28255Scgd 			/*
52861f28255Scgd 			 * Simulate end of file on input.
52961f28255Scgd 			 */
53061f28255Scgd 			goto out;
53161f28255Scgd 		case 'q':
53261f28255Scgd 			/*
53361f28255Scgd 			 * Force a quit of sending mail.
53461f28255Scgd 			 * Act like an interrupt happened.
53561f28255Scgd 			 */
53661f28255Scgd 			hadintr++;
537ca13337dSchristos 			coll_int(SIGINT);
53861f28255Scgd 			exit(1);
539ca286310Schristos 			/*NOTREACHED*/
540fb60b363Smjl 
541fb60b363Smjl 		case 'x':	/* exit, do not save in dead.letter */
542fb60b363Smjl 			goto err;
543fb60b363Smjl 
54461f28255Scgd 		case 'h':
54561f28255Scgd 			/*
54661f28255Scgd 			 * Grab a bunch of headers.
54761f28255Scgd 			 */
54885c81c58Schristos 			(void)grabh(hp, GTO | GSUBJECT | GCC | GBCC | GSMOPTS);
54961f28255Scgd 			goto cont;
55061f28255Scgd 		case 't':
55161f28255Scgd 			/*
55261f28255Scgd 			 * Add to the To list.
55361f28255Scgd 			 */
55461f28255Scgd 			hp->h_to = cat(hp->h_to, extract(&linebuf[2], GTO));
55561f28255Scgd 			break;
55661f28255Scgd 		case 's':
55761f28255Scgd 			/*
55861f28255Scgd 			 * Set the Subject list.
55961f28255Scgd 			 */
560d727506fSchristos 			cp = skip_WSP(&linebuf[2]);
56161f28255Scgd 			hp->h_subject = savestr(cp);
56261f28255Scgd 			break;
56361f28255Scgd 		case 'c':
56461f28255Scgd 			/*
56561f28255Scgd 			 * Add to the CC list.
56661f28255Scgd 			 */
56761f28255Scgd 			hp->h_cc = cat(hp->h_cc, extract(&linebuf[2], GCC));
56861f28255Scgd 			break;
56961f28255Scgd 		case 'b':
57061f28255Scgd 			/*
57161f28255Scgd 			 * Add stuff to blind carbon copies list.
57261f28255Scgd 			 */
57361f28255Scgd 			hp->h_bcc = cat(hp->h_bcc, extract(&linebuf[2], GBCC));
57461f28255Scgd 			break;
575fb60b363Smjl 		case 'i':
576fb60b363Smjl 		case 'A':
577fb60b363Smjl 		case 'a':
578fb60b363Smjl 			/*
579fb60b363Smjl 			 * Insert named variable in message
580fb60b363Smjl 			 */
581fb60b363Smjl 
582fb60b363Smjl 			switch(c) {
583fb60b363Smjl 				case 'i':
584d727506fSchristos 					cp = skip_WSP(&linebuf[2]);
585fb60b363Smjl 					break;
586fb60b363Smjl 				case 'a':
587fb60b363Smjl 					cp = "sign";
588fb60b363Smjl 					break;
589fb60b363Smjl 				case 'A':
590fb60b363Smjl 					cp = "Sign";
591fb60b363Smjl 					break;
592fb60b363Smjl 				default:
593fb60b363Smjl 					goto err;
594fb60b363Smjl 			}
595fb60b363Smjl 
596ab850155Swiz 			if (*cp && (cp = value(cp)) != NULL) {
597ca286310Schristos 				(void)printf("%s\n", cp);
598fb60b363Smjl 				if (putline(collf, cp, 1) < 0)
599fb60b363Smjl 					goto err;
600fb60b363Smjl 			}
601fb60b363Smjl 
602fb60b363Smjl 			break;
603fb60b363Smjl 
60461f28255Scgd 		case 'd':
605ca286310Schristos 			(void)strcpy(linebuf + 2, getdeadletter());
606ca286310Schristos 			/* FALLTHROUGH */
60761f28255Scgd 		case 'r':
608a067af72Sjtc 		case '<':
60961f28255Scgd 			/*
61061f28255Scgd 			 * Invoke a file:
61161f28255Scgd 			 * Search for the file name,
61261f28255Scgd 			 * then open it and copy the contents to collf.
61361f28255Scgd 			 */
614d727506fSchristos 			cp = skip_WSP(&linebuf[2]);
61561f28255Scgd 			if (*cp == '\0') {
616ca286310Schristos 				(void)printf("Interpolate what file?\n");
61761f28255Scgd 				break;
61861f28255Scgd 			}
619fb60b363Smjl 
62061f28255Scgd 			cp = expand(cp);
621ab850155Swiz 			if (cp == NULL)
62261f28255Scgd 				break;
623fb60b363Smjl 
624fb60b363Smjl 			if (*cp == '!') {	/* insert stdout of command */
625ece0fd5cSchristos 				const char *shellcmd;
626fb60b363Smjl 				int nullfd;
6274e972651Swiz 				int rc2;
628fb60b363Smjl 
629fb60b363Smjl 				if ((nullfd = open("/dev/null", O_RDONLY, 0)) == -1) {
630ae38aa87Swiz 					warn("/dev/null");
631fb60b363Smjl 					break;
632fb60b363Smjl 				}
633fb60b363Smjl 
634443084c8Swiz 				(void)snprintf(tempname, sizeof(tempname),
635240d8221Swiz 				    "%s/mail.ReXXXXXXXXXX", tmpdir);
636443084c8Swiz 				if ((fd = mkstemp(tempname)) == -1 ||
637*4fe1ef32Schristos 				    (fbuf = Fdopen(fd, "wef+")) == NULL) {
638443084c8Swiz 					if (fd != -1)
639ca286310Schristos 						(void)close(fd);
640443084c8Swiz 					warn("%s", tempname);
641fb60b363Smjl 					break;
642fb60b363Smjl 				}
643443084c8Swiz 				(void)unlink(tempname);
644fb60b363Smjl 
645f3098750Schristos 				if ((shellcmd = value(ENAME_SHELL)) == NULL)
6464e972651Swiz 					shellcmd = _PATH_CSHELL;
647fb60b363Smjl 
648ca13337dSchristos 				rc2 = run_command(shellcmd, NULL, nullfd, fileno(fbuf), "-c", cp + 1, NULL);
649fb60b363Smjl 
650ca286310Schristos 				(void)close(nullfd);
651fb60b363Smjl 
6524e972651Swiz 				if (rc2 < 0) {
653fb60b363Smjl 					(void)Fclose(fbuf);
654fb60b363Smjl 					break;
655fb60b363Smjl 				}
656fb60b363Smjl 
657fb60b363Smjl 				if (fsize(fbuf) == 0) {
658ca286310Schristos 					(void)fprintf(stderr, "No bytes from command \"%s\"\n", cp + 1);
659fb60b363Smjl 					(void)Fclose(fbuf);
660fb60b363Smjl 					break;
661fb60b363Smjl 				}
662fb60b363Smjl 
663fb60b363Smjl 				rewind(fbuf);
664fb60b363Smjl 			}
665fb60b363Smjl 			else if (isdir(cp)) {
666ca286310Schristos 				(void)printf("%s: Directory\n", cp);
66761f28255Scgd 				break;
66861f28255Scgd 			}
6695942983dSchristos 			else if ((fbuf = Fopen(cp, "re")) == NULL) {
670ae38aa87Swiz 				warn("%s", cp);
67161f28255Scgd 				break;
67261f28255Scgd 			}
673ca286310Schristos 			(void)printf("\"%s\" ", cp);
674ca286310Schristos 			(void)fflush(stdout);
67561f28255Scgd 			lc = 0;
67661f28255Scgd 			cc = 0;
677ca13337dSchristos 			while ((rc = readline(fbuf, linebuf, LINESIZE, 0)) >= 0) {
678a8316bbcSphil 				if (rc != LINESIZE-1) lc++;
679a8316bbcSphil 				if ((t = putline(collf, linebuf,
680a8316bbcSphil 						 rc != LINESIZE-1)) < 0) {
681ca286310Schristos 					(void)Fclose(fbuf);
68261f28255Scgd 					goto err;
68361f28255Scgd 				}
68461f28255Scgd 				cc += t;
68561f28255Scgd 			}
686ca286310Schristos 			(void)Fclose(fbuf);
687ca286310Schristos 			(void)printf("%d/%d\n", lc, cc);
68861f28255Scgd 			break;
68961f28255Scgd 		case 'w':
69061f28255Scgd 			/*
69161f28255Scgd 			 * Write the message on a file.
69261f28255Scgd 			 */
693d727506fSchristos 			cp = skip_WSP(&linebuf[2]);
69461f28255Scgd 			if (*cp == '\0') {
695ca286310Schristos 				(void)fprintf(stderr, "Write what file!?\n");
69661f28255Scgd 				break;
69761f28255Scgd 			}
698ab850155Swiz 			if ((cp = expand(cp)) == NULL)
69961f28255Scgd 				break;
70061f28255Scgd 			rewind(collf);
701ca286310Schristos 			(void)exwrite(cp, collf, 1);
70261f28255Scgd 			break;
70361f28255Scgd 		case 'm':
70461f28255Scgd 		case 'M':
70561f28255Scgd 		case 'f':
70661f28255Scgd 		case 'F':
70761f28255Scgd 			/*
70861f28255Scgd 			 * Interpolate the named messages, if we
70961f28255Scgd 			 * are in receiving mail mode.  Does the
71061f28255Scgd 			 * standard list processing garbage.
71161f28255Scgd 			 * If ~f is given, we don't shift over.
71261f28255Scgd 			 */
7134556f89aSchristos 			if (interpolate(linebuf + 2, collf, mailtempname, c) < 0)
71461f28255Scgd 				goto err;
71561f28255Scgd 			goto cont;
71661f28255Scgd 		case '?':
71741101b58Schristos 			cathelp(_PATH_TILDE);
71861f28255Scgd 			break;
71961f28255Scgd 		case 'p':
72061f28255Scgd 			/*
72161f28255Scgd 			 * Print out the current state of the
72261f28255Scgd 			 * message without altering anything.
72361f28255Scgd 			 */
72461f28255Scgd 			rewind(collf);
725ca286310Schristos 			(void)printf("-------\nMessage contains:\n");
726ca13337dSchristos 			(void)puthead(hp, stdout,
727ca13337dSchristos 			    GTO | GSUBJECT | GCC | GBCC | GSMOPTS | GNL);
72861f28255Scgd 			while ((t = getc(collf)) != EOF)
72961f28255Scgd 				(void)putchar(t);
73061f28255Scgd 			goto cont;
73161f28255Scgd 		case '|':
73261f28255Scgd 			/*
73361f28255Scgd 			 * Pipe message through command.
73461f28255Scgd 			 * Collect output as new message.
73561f28255Scgd 			 */
73661f28255Scgd 			rewind(collf);
73761f28255Scgd 			mespipe(collf, &linebuf[2]);
73861f28255Scgd 			goto cont;
73961f28255Scgd 		case 'v':
74061f28255Scgd 		case 'e':
74161f28255Scgd 			/*
74261f28255Scgd 			 * Edit the current message.
74361f28255Scgd 			 * 'e' means to use EDITOR
74461f28255Scgd 			 * 'v' means to use VISUAL
74561f28255Scgd 			 */
74661f28255Scgd 			rewind(collf);
74761f28255Scgd 			mesedit(collf, c);
74861f28255Scgd 			goto cont;
74961f28255Scgd 		}
75061f28255Scgd 	}
75161f28255Scgd 	goto out;
75261f28255Scgd  err:
75361f28255Scgd 	if (collf != NULL) {
754ca286310Schristos 		(void)Fclose(collf);
75561f28255Scgd 		collf = NULL;
75661f28255Scgd 	}
75761f28255Scgd  out:
75861f28255Scgd 	if (collf != NULL)
75961f28255Scgd 		rewind(collf);
76061f28255Scgd 	noreset--;
761ca13337dSchristos 
762ca13337dSchristos 	sig_hold();
763ca13337dSchristos 	(void)sig_signal(SIGINT,  old_sigint);
764ca13337dSchristos 	(void)sig_signal(SIGHUP,  old_sighup);
765ca13337dSchristos 	(void)sig_signal(SIGTSTP, old_sigtstp);
766ca13337dSchristos 	(void)sig_signal(SIGTTIN, old_sigttin);
767ca13337dSchristos 	(void)sig_signal(SIGTTOU, old_sigttou);
768ca13337dSchristos 	sig_release();
769ca13337dSchristos 
770ca13337dSchristos 	sig_check();
77161f28255Scgd 	return collf;
77261f28255Scgd }
773