xref: /netbsd-src/usr.bin/mail/collect.c (revision 404fbe5fb94ca1e054339640cabb2801ce52dd30)
1 /*	$NetBSD: collect.c,v 1.42 2007/10/29 23:20:38 christos Exp $	*/
2 
3 /*
4  * Copyright (c) 1980, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)collect.c	8.2 (Berkeley) 4/19/94";
36 #else
37 __RCSID("$NetBSD: collect.c,v 1.42 2007/10/29 23:20:38 christos Exp $");
38 #endif
39 #endif /* not lint */
40 
41 /*
42  * Mail -- a mail program
43  *
44  * Collect input from standard input, handling
45  * ~ escapes.
46  */
47 
48 #include <assert.h>
49 #include <util.h>
50 
51 #include "rcv.h"
52 #include "extern.h"
53 #include "format.h"
54 #ifdef MIME_SUPPORT
55 #include "mime.h"
56 #endif
57 #include "thread.h"
58 
59 
60 /*
61  * Read a message from standard input and return a read file to it
62  * or NULL on error.
63  */
64 
65 /*
66  * The following hokiness with global variables is so that on
67  * receipt of an interrupt signal, the partial message can be salted
68  * away on dead.letter.
69  */
70 
71 static	sig_t	saveint;		/* Previous SIGINT value */
72 static	sig_t	savehup;		/* Previous SIGHUP value */
73 static	sig_t	savetstp;		/* Previous SIGTSTP value */
74 static	sig_t	savettou;		/* Previous SIGTTOU value */
75 static	sig_t	savettin;		/* Previous SIGTTIN value */
76 static	FILE	*collf;			/* File for saving away */
77 static	int	hadintr;		/* Have seen one SIGINT so far */
78 
79 static	jmp_buf	colljmp;		/* To get back to work */
80 static	int	colljmp_p;		/* whether to long jump */
81 static	jmp_buf	collabort;		/* To end collection with error */
82 
83 
84 /*
85  * Write a file, ex-like if f set.
86  */
87 static int
88 exwrite(const char name[], FILE *fp, int f)
89 {
90 	FILE *of;
91 	int c;
92 	long cc;
93 	int lc;
94 	struct stat junk;
95 
96 	if (f) {
97 		(void)printf("\"%s\" ", name);
98 		(void)fflush(stdout);
99 	}
100 	if (stat(name, &junk) >= 0 && S_ISREG(junk.st_mode)) {
101 		if (!f)
102 			(void)fprintf(stderr, "%s: ", name);
103 		(void)fprintf(stderr, "File exists\n");
104 		return -1;
105 	}
106 	if ((of = Fopen(name, "w")) == NULL) {
107 		warn("%s", name);
108 		return -1;
109 	}
110 	lc = 0;
111 	cc = 0;
112 	while ((c = getc(fp)) != EOF) {
113 		cc++;
114 		if (c == '\n')
115 			lc++;
116 		(void)putc(c, of);
117 		if (ferror(of)) {
118 			warn("%s", name);
119 			(void)Fclose(of);
120 			return -1;
121 		}
122 	}
123 	(void)Fclose(of);
124 	(void)printf("%d/%ld\n", lc, cc);
125 	(void)fflush(stdout);
126 	return 0;
127 }
128 
129 /*
130  * Edit the message being collected on fp.
131  * On return, make the edit file the new temp file.
132  */
133 static void
134 mesedit(FILE *fp, int c)
135 {
136 	sig_t sigint = signal(SIGINT, SIG_IGN);
137 	FILE *nf = run_editor(fp, (off_t)-1, c, 0);
138 
139 	if (nf != NULL) {
140 		(void)fseek(nf, 0L, 2);
141 		collf = nf;
142 		(void)Fclose(fp);
143 	}
144 	(void)signal(SIGINT, sigint);
145 }
146 
147 /*
148  * Pipe the message through the command.
149  * Old message is on stdin of command;
150  * New message collected from stdout.
151  * Sh -c must return 0 to accept the new message.
152  */
153 static void
154 mespipe(FILE *fp, char cmd[])
155 {
156 	FILE *nf;
157 	sig_t sigint = signal(SIGINT, SIG_IGN);
158 	const char *shellcmd;
159 	int fd;
160 	char tempname[PATHSIZE];
161 
162 	(void)snprintf(tempname, sizeof(tempname),
163 	    "%s/mail.ReXXXXXXXXXX", tmpdir);
164 	if ((fd = mkstemp(tempname)) == -1 ||
165 	    (nf = Fdopen(fd, "w+")) == NULL) {
166 		if (fd != -1)
167 			(void)close(fd);
168 		warn("%s", tempname);
169 		goto out;
170 	}
171 	(void)unlink(tempname);
172 	/*
173 	 * stdin = current message.
174 	 * stdout = new message.
175 	 */
176 	if ((shellcmd = value(ENAME_SHELL)) == NULL)
177 		shellcmd = _PATH_CSHELL;
178 	if (run_command(shellcmd,
179 	    0, fileno(fp), fileno(nf), "-c", cmd, NULL) < 0) {
180 		(void)Fclose(nf);
181 		goto out;
182 	}
183 	if (fsize(nf) == 0) {
184 		(void)fprintf(stderr, "No bytes from \"%s\" !?\n", cmd);
185 		(void)Fclose(nf);
186 		goto out;
187 	}
188 	/*
189 	 * Take new files.
190 	 */
191 	(void)fseek(nf, 0L, 2);
192 	collf = nf;
193 	(void)Fclose(fp);
194 out:
195 	(void)signal(SIGINT, sigint);
196 }
197 
198 /*
199  * Interpolate the named messages into the current
200  * message, preceding each line with a tab.
201  * Return a count of the number of characters now in
202  * the message, or -1 if an error is encountered writing
203  * the message temporary.  The flag argument is 'm' if we
204  * should shift over and 'f' if not.
205  */
206 static int
207 interpolate(char ms[], FILE *fp, char *fn, int f)
208 {
209 	int *msgvec;
210 	struct ignoretab *ig;
211 	const char *tabst;
212 #ifdef MIME_SUPPORT
213 	struct mime_info *mip;
214 	int retval;
215 #endif
216 	msgvec = salloc((get_msgCount() + 1) * sizeof(*msgvec));
217 	if (msgvec == NULL)
218 		return 0;
219 	if (getmsglist(ms, msgvec, 0) < 0)
220 		return 0;
221 	if (*msgvec == 0) {
222 		*msgvec = first(0, MMNORM);
223 		if (*msgvec == 0) {
224 			(void)printf("No appropriate messages\n");
225 			return 0;
226 		}
227 		msgvec[1] = 0;
228 	}
229 	if (f == 'f' || f == 'F')
230 		tabst = NULL;
231 	else if ((tabst = value(ENAME_INDENTPREFIX)) == NULL)
232 		tabst = "\t";
233 	ig = isupper(f) ? NULL : ignore;
234 	(void)printf("Interpolating:");
235 	for (/*EMPTY*/; *msgvec != 0; msgvec++) {
236 		struct message *mp;
237 		char *fmtstr;
238 
239 		mp = get_message(*msgvec);
240 		touch(mp);
241 		(void)printf(" %d", *msgvec);
242 		(void)fflush(stdout);	/* flush stdout and the above */
243 
244 		if (tabst && (fmtstr = value(ENAME_INDENT_PREAMBLE)) != NULL)
245 			fmsgprintf(collf, fmtstr, mp);
246 #ifdef MIME_SUPPORT
247 		mip = NULL;
248 		if (value(ENAME_MIME_DECODE_MSG)) {
249 			if ((tabst == NULL && value(ENAME_MIME_DECODE_INSERT)) ||
250 			    (tabst != NULL && value(ENAME_MIME_DECODE_QUOTE)))
251 				mip = mime_decode_open(mp);
252 		}
253 		retval = mime_sendmessage(mp, fp, ig, tabst, mip);
254 		mime_decode_close(mip);
255 		if (retval < 0)
256 #else
257 		if (sendmessage(mp, fp, ig, tabst, NULL) < 0)
258 #endif
259 		{
260 			warn("%s", fn);
261 			return -1;
262 		}
263 		if (tabst && (fmtstr = value(ENAME_INDENT_POSTSCRIPT)) != NULL)
264 			fmsgprintf(collf, fmtstr, mp);
265 	}
266 	(void)printf("\n");
267 	return 0;
268 }
269 
270 /*
271  * Print (continue) when continued after ^Z.
272  */
273 /*ARGSUSED*/
274 static void
275 collstop(int s)
276 {
277 	sig_t old_action = signal(s, SIG_DFL);
278 	sigset_t nset;
279 
280 	(void)sigemptyset(&nset);
281 	(void)sigaddset(&nset, s);
282 	(void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
283 	(void)kill(0, s);
284 	(void)sigprocmask(SIG_BLOCK, &nset, NULL);
285 	(void)signal(s, old_action);
286 	if (colljmp_p) {
287 		colljmp_p = 0;
288 		hadintr = 0;
289 		longjmp(colljmp, 1);
290 	}
291 }
292 
293 /*
294  * Append the contents of the file to the end of the deadletter file.
295  */
296 PUBLIC void
297 savedeadletter(FILE *fp)
298 {
299 	FILE *dbuf;
300 	mode_t m;
301 	int c;
302 	const char *cp;
303 
304 	if (fsize(fp) == 0)
305 		return;
306 	cp = getdeadletter();
307 	m = umask(077);
308 	dbuf = Fopen(cp, "a");
309 	(void)umask(m);
310 	if (dbuf == NULL)
311 		return;
312 	while ((c = getc(fp)) != EOF)
313 		(void)putc(c, dbuf);
314 	(void)Fclose(dbuf);
315 	rewind(fp);
316 }
317 
318 /*
319  * On interrupt, come here to save the partial message in ~/dead.letter.
320  * Then jump out of the collection loop.
321  */
322 /*ARGSUSED*/
323 static void
324 collint(int s __unused)
325 {
326 	/*
327 	 * the control flow is subtle, because we can be called from ~q.
328 	 */
329 	if (!hadintr) {
330 		if (value(ENAME_IGNORE) != NULL) {
331 			(void)puts("@");
332 			(void)fflush(stdout);
333 			clearerr(stdin);
334 			return;
335 		}
336 		hadintr = 1;
337 		longjmp(colljmp, 1);
338 	}
339 	rewind(collf);
340 	if (value(ENAME_NOSAVE) == NULL)
341 		savedeadletter(collf);
342 	longjmp(collabort, 1);
343 }
344 
345 /*ARGSUSED*/
346 static void
347 collhup(int s __unused)
348 {
349 	rewind(collf);
350 	savedeadletter(collf);
351 	/*
352 	 * Let's pretend nobody else wants to clean up,
353 	 * a true statement at this time.
354 	 */
355 	exit(1);
356 }
357 
358 PUBLIC FILE *
359 collect(struct header *hp, int printheaders)
360 {
361 	FILE *fbuf;
362 	int lc, cc;
363 	int c, fd, t;
364 	char linebuf[LINESIZE];
365 	const char *cp;
366 	char tempname[PATHSIZE];
367 	char mailtempname[PATHSIZE];
368 	int lastlong, rc;	/* So we don't make 2 or more lines
369 					   out of a long input line. */
370 	int eofcount;
371 	int longline;
372 	sigset_t  nset;
373 
374 	/* The following are declared volatile to avoid longjmp clobbering. */
375 	char volatile getsub;
376 	int volatile escape;
377 
378 	(void)memset(mailtempname, 0, sizeof(mailtempname));
379 	collf = NULL;
380 	/*
381 	 * Start catching signals from here, but we're still die on interrupts
382 	 * until we're in the main loop.
383 	 */
384 	(void)sigemptyset(&nset);
385 	(void)sigaddset(&nset, SIGINT);
386 	(void)sigaddset(&nset, SIGHUP);
387 	(void)sigprocmask(SIG_BLOCK, &nset, NULL);
388 	if ((saveint = signal(SIGINT, SIG_IGN)) != SIG_IGN)
389 		(void)signal(SIGINT, collint);
390 	if ((savehup = signal(SIGHUP, SIG_IGN)) != SIG_IGN)
391 		(void)signal(SIGHUP, collhup);
392 	savetstp = signal(SIGTSTP, collstop);
393 	savettou = signal(SIGTTOU, collstop);
394 	savettin = signal(SIGTTIN, collstop);
395 	if (setjmp(collabort) || setjmp(colljmp)) {
396 		(void)rm(mailtempname);
397 		goto err;
398 	}
399 	(void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
400 
401 	noreset++;
402 	(void)snprintf(mailtempname, sizeof(mailtempname),
403 	    "%s/mail.RsXXXXXXXXXX", tmpdir);
404 	if ((fd = mkstemp(mailtempname)) == -1 ||
405 	    (collf = Fdopen(fd, "w+")) == NULL) {
406 		if (fd != -1)
407 			(void)close(fd);
408 		warn("%s", mailtempname);
409 		goto err;
410 	}
411 	(void)rm(mailtempname);
412 
413 	/*
414 	 * If we are going to prompt for a subject,
415 	 * refrain from printing a newline after
416 	 * the headers (since some people mind).
417 	 */
418 	t = GTO|GSUBJECT|GCC|GNL|GSMOPTS;
419 	getsub = 0;
420 	if (hp->h_subject == NULL && value(ENAME_INTERACTIVE) != NULL &&
421 	    (value(ENAME_ASK) != NULL || value(ENAME_ASKSUB) != NULL))
422 		t &= ~GNL, getsub++;
423 	if (printheaders) {
424 		(void)puthead(hp, stdout, t);
425 		(void)fflush(stdout);
426 	}
427 	if ((cp = value(ENAME_ESCAPE)) != NULL)
428 		escape = *cp;
429 	else
430 		escape = ESCAPE;
431 	hadintr = 0;	/* static - no longjmp problem */
432 	if (!setjmp(colljmp)) {
433 		if (getsub)
434 			(void)grabh(hp, GSUBJECT);
435 	} else {
436 		/*
437 		 * Come here for printing the after-signal message.
438 		 * Duplicate messages won't be printed because
439 		 * the write is aborted if we get a SIGTTOU.
440 		 */
441 cont:
442 		if (hadintr) {
443 			(void)fflush(stdout);
444 			(void)fprintf(stderr,
445 			"\n(Interrupt -- one more to kill letter)\n");
446 		} else {
447 			(void)printf("(continue)\n");
448 			(void)fflush(stdout);
449 		}
450 	}
451 	eofcount = 0;	/* reset after possible longjmp */
452 	longline = 0;	/* reset after possible longjmp */
453 	for (;;) {
454 		colljmp_p = 1;
455 		c = mail_readline(stdin, linebuf, LINESIZE);
456 		colljmp_p = 0;
457 #ifdef USE_EDITLINE
458 		if (c < 0) {
459 			char *p;
460 			if (value(ENAME_INTERACTIVE) != NULL &&
461 			    (p = value(ENAME_IGNOREEOF)) != NULL &&
462 			    ++eofcount < (*p == 0 ? 25 : atoi(p))) {
463 				(void)printf("Use \".\" to terminate letter\n");
464 				continue;
465 			}
466 			break;
467 		}
468 #else
469 		if (c < 0) {
470 			if (value(ENAME_INTERACTIVE) != NULL &&
471 			    value(ENAME_IGNOREEOF) != NULL && ++eofcount < 25) {
472 				(void)printf("Use \".\" to terminate letter\n");
473 				continue;
474 			}
475 			break;
476 		}
477 #endif
478 		lastlong = longline;
479 		longline = c == LINESIZE-1;
480 		eofcount = 0;
481 		hadintr = 0;
482 		if (linebuf[0] == '.' && linebuf[1] == '\0' &&
483 		    value(ENAME_INTERACTIVE) != NULL && !lastlong &&
484 		    (value(ENAME_DOT) != NULL || value(ENAME_IGNOREEOF) != NULL))
485 			break;
486 		if (linebuf[0] != escape || value(ENAME_INTERACTIVE) == NULL ||
487 		    lastlong) {
488 			if (putline(collf, linebuf, !longline) < 0)
489 				goto err;
490 			continue;
491 		}
492 		c = linebuf[1];
493 		switch (c) {
494 		default:
495 			/*
496 			 * On double escape, just send the single one.
497 			 * Otherwise, it's an error.
498 			 */
499 			if (c == escape) {
500 				if (putline(collf, &linebuf[1], !longline) < 0)
501 					goto err;
502 				else
503 					break;
504 			}
505 			(void)printf("Unknown tilde escape.\n");
506 			break;
507 #ifdef MIME_SUPPORT
508 		case '@':
509 			hp->h_attach = mime_attach_files(hp->h_attach, &linebuf[2]);
510 			break;
511 #endif
512 		case 'C':
513 			/*
514 			 * Dump core.
515 			 */
516 			(void)core(NULL);
517 			break;
518 		case '!':
519 			/*
520 			 * Shell escape, send the balance of the
521 			 * line to sh -c.
522 			 */
523 			(void)shell(&linebuf[2]);
524 			break;
525 		case ':':
526 		case '_':
527 			/*
528 			 * Escape to command mode, but be nice!
529 			 */
530 			(void)execute(&linebuf[2], ec_composing);
531 			goto cont;
532 		case '.':
533 			/*
534 			 * Simulate end of file on input.
535 			 */
536 			goto out;
537 		case 'q':
538 			/*
539 			 * Force a quit of sending mail.
540 			 * Act like an interrupt happened.
541 			 */
542 			hadintr++;
543 			collint(SIGINT);
544 			exit(1);
545 			/*NOTREACHED*/
546 
547 		case 'x':	/* exit, do not save in dead.letter */
548 			goto err;
549 
550 		case 'h':
551 			/*
552 			 * Grab a bunch of headers.
553 			 */
554 			(void)grabh(hp, GTO|GSUBJECT|GCC|GBCC|GSMOPTS);
555 			goto cont;
556 		case 't':
557 			/*
558 			 * Add to the To list.
559 			 */
560 			hp->h_to = cat(hp->h_to, extract(&linebuf[2], GTO));
561 			break;
562 		case 's':
563 			/*
564 			 * Set the Subject list.
565 			 */
566 			cp = skip_WSP(&linebuf[2]);
567 			hp->h_subject = savestr(cp);
568 			break;
569 		case 'c':
570 			/*
571 			 * Add to the CC list.
572 			 */
573 			hp->h_cc = cat(hp->h_cc, extract(&linebuf[2], GCC));
574 			break;
575 		case 'b':
576 			/*
577 			 * Add stuff to blind carbon copies list.
578 			 */
579 			hp->h_bcc = cat(hp->h_bcc, extract(&linebuf[2], GBCC));
580 			break;
581 		case 'i':
582 		case 'A':
583 		case 'a':
584 			/*
585 			 * Insert named variable in message
586 			 */
587 
588 			switch(c) {
589 				case 'i':
590 					cp = skip_WSP(&linebuf[2]);
591 					break;
592 				case 'a':
593 					cp = "sign";
594 					break;
595 				case 'A':
596 					cp = "Sign";
597 					break;
598 				default:
599 					goto err;
600 			}
601 
602 			if (*cp && (cp = value(cp)) != NULL) {
603 				(void)printf("%s\n", cp);
604 				if (putline(collf, cp, 1) < 0)
605 					goto err;
606 			}
607 
608 			break;
609 
610 		case 'd':
611 			(void)strcpy(linebuf + 2, getdeadletter());
612 			/* FALLTHROUGH */
613 		case 'r':
614 		case '<':
615 			/*
616 			 * Invoke a file:
617 			 * Search for the file name,
618 			 * then open it and copy the contents to collf.
619 			 */
620 			cp = skip_WSP(&linebuf[2]);
621 			if (*cp == '\0') {
622 				(void)printf("Interpolate what file?\n");
623 				break;
624 			}
625 
626 			cp = expand(cp);
627 			if (cp == NULL)
628 				break;
629 
630 			if (*cp == '!') {	/* insert stdout of command */
631 				const char *shellcmd;
632 				int nullfd;
633 				int rc2;
634 
635 				if ((nullfd = open("/dev/null", O_RDONLY, 0)) == -1) {
636 					warn("/dev/null");
637 					break;
638 				}
639 
640 				(void)snprintf(tempname, sizeof(tempname),
641 				    "%s/mail.ReXXXXXXXXXX", tmpdir);
642 				if ((fd = mkstemp(tempname)) == -1 ||
643 				    (fbuf = Fdopen(fd, "w+")) == NULL) {
644 					if (fd != -1)
645 						(void)close(fd);
646 					warn("%s", tempname);
647 					break;
648 				}
649 				(void)unlink(tempname);
650 
651 				if ((shellcmd = value(ENAME_SHELL)) == NULL)
652 					shellcmd = _PATH_CSHELL;
653 
654 				rc2 = run_command(shellcmd, 0, nullfd, fileno(fbuf), "-c", cp + 1, NULL);
655 
656 				(void)close(nullfd);
657 
658 				if (rc2 < 0) {
659 					(void)Fclose(fbuf);
660 					break;
661 				}
662 
663 				if (fsize(fbuf) == 0) {
664 					(void)fprintf(stderr, "No bytes from command \"%s\"\n", cp + 1);
665 					(void)Fclose(fbuf);
666 					break;
667 				}
668 
669 				rewind(fbuf);
670 			}
671 			else if (isdir(cp)) {
672 				(void)printf("%s: Directory\n", cp);
673 				break;
674 			}
675 			else if ((fbuf = Fopen(cp, "r")) == NULL) {
676 				warn("%s", cp);
677 				break;
678 			}
679 			(void)printf("\"%s\" ", cp);
680 			(void)fflush(stdout);
681 			lc = 0;
682 			cc = 0;
683 			while ((rc = mail_readline(fbuf, linebuf, LINESIZE)) >= 0) {
684 				if (rc != LINESIZE-1) lc++;
685 				if ((t = putline(collf, linebuf,
686 						 rc != LINESIZE-1)) < 0) {
687 					(void)Fclose(fbuf);
688 					goto err;
689 				}
690 				cc += t;
691 			}
692 			(void)Fclose(fbuf);
693 			(void)printf("%d/%d\n", lc, cc);
694 			break;
695 		case 'w':
696 			/*
697 			 * Write the message on a file.
698 			 */
699 			cp = skip_WSP(&linebuf[2]);
700 			if (*cp == '\0') {
701 				(void)fprintf(stderr, "Write what file!?\n");
702 				break;
703 			}
704 			if ((cp = expand(cp)) == NULL)
705 				break;
706 			rewind(collf);
707 			(void)exwrite(cp, collf, 1);
708 			break;
709 		case 'm':
710 		case 'M':
711 		case 'f':
712 		case 'F':
713 			/*
714 			 * Interpolate the named messages, if we
715 			 * are in receiving mail mode.  Does the
716 			 * standard list processing garbage.
717 			 * If ~f is given, we don't shift over.
718 			 */
719 			if (interpolate(linebuf + 2, collf, mailtempname, c) < 0)
720 				goto err;
721 			goto cont;
722 		case '?':
723 			cathelp(_PATH_TILDE);
724 			break;
725 		case 'p':
726 			/*
727 			 * Print out the current state of the
728 			 * message without altering anything.
729 			 */
730 			rewind(collf);
731 			(void)printf("-------\nMessage contains:\n");
732 			(void)puthead(hp, stdout, GTO|GSUBJECT|GCC|GBCC|GNL);
733 			while ((t = getc(collf)) != EOF)
734 				(void)putchar(t);
735 			goto cont;
736 		case '|':
737 			/*
738 			 * Pipe message through command.
739 			 * Collect output as new message.
740 			 */
741 			rewind(collf);
742 			mespipe(collf, &linebuf[2]);
743 			goto cont;
744 		case 'v':
745 		case 'e':
746 			/*
747 			 * Edit the current message.
748 			 * 'e' means to use EDITOR
749 			 * 'v' means to use VISUAL
750 			 */
751 			rewind(collf);
752 			mesedit(collf, c);
753 			goto cont;
754 		}
755 	}
756 	goto out;
757 err:
758 	if (collf != NULL) {
759 		(void)Fclose(collf);
760 		collf = NULL;
761 	}
762 out:
763 	if (collf != NULL)
764 		rewind(collf);
765 	noreset--;
766 	(void)sigprocmask(SIG_BLOCK, &nset, NULL);
767 	(void)signal(SIGINT, saveint);
768 	(void)signal(SIGHUP, savehup);
769 	(void)signal(SIGTSTP, savetstp);
770 	(void)signal(SIGTTOU, savettou);
771 	(void)signal(SIGTTIN, savettin);
772 	(void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
773 	return collf;
774 }
775