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