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