19b50d902SRodney W. Grimes /* 29b50d902SRodney W. Grimes * Copyright (c) 1980, 1993 39b50d902SRodney W. Grimes * The Regents of the University of California. All rights reserved. 49b50d902SRodney W. Grimes * 59b50d902SRodney W. Grimes * Redistribution and use in source and binary forms, with or without 69b50d902SRodney W. Grimes * modification, are permitted provided that the following conditions 79b50d902SRodney W. Grimes * are met: 89b50d902SRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 99b50d902SRodney W. Grimes * notice, this list of conditions and the following disclaimer. 109b50d902SRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 119b50d902SRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 129b50d902SRodney W. Grimes * documentation and/or other materials provided with the distribution. 139b50d902SRodney W. Grimes * 4. Neither the name of the University nor the names of its contributors 149b50d902SRodney W. Grimes * may be used to endorse or promote products derived from this software 159b50d902SRodney W. Grimes * without specific prior written permission. 169b50d902SRodney W. Grimes * 179b50d902SRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 189b50d902SRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 199b50d902SRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 209b50d902SRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 219b50d902SRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 229b50d902SRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 239b50d902SRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 249b50d902SRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 259b50d902SRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 269b50d902SRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 279b50d902SRodney W. Grimes * SUCH DAMAGE. 289b50d902SRodney W. Grimes */ 299b50d902SRodney W. Grimes 309b50d902SRodney W. Grimes #ifndef lint 310c3a8314SMike Heffner #if 0 329b50d902SRodney W. Grimes static char sccsid[] = "@(#)collect.c 8.2 (Berkeley) 4/19/94"; 330c3a8314SMike Heffner #endif 349b50d902SRodney W. Grimes #endif /* not lint */ 35e026a48cSDavid E. O'Brien #include <sys/cdefs.h> 36e026a48cSDavid E. O'Brien __FBSDID("$FreeBSD$"); 379b50d902SRodney W. Grimes 389b50d902SRodney W. Grimes /* 399b50d902SRodney W. Grimes * Mail -- a mail program 409b50d902SRodney W. Grimes * 419b50d902SRodney W. Grimes * Collect input from standard input, handling 429b50d902SRodney W. Grimes * ~ escapes. 439b50d902SRodney W. Grimes */ 449b50d902SRodney W. Grimes 459b50d902SRodney W. Grimes #include "rcv.h" 4669131e40SMike Heffner #include <fcntl.h> 479b50d902SRodney W. Grimes #include "extern.h" 489b50d902SRodney W. Grimes 499b50d902SRodney W. Grimes /* 50*24b797ccSPedro F. Giffuni * Read a message from standard input and return a read file to it 519b50d902SRodney W. Grimes * or NULL on error. 529b50d902SRodney W. Grimes */ 539b50d902SRodney W. Grimes 549b50d902SRodney W. Grimes /* 559b50d902SRodney W. Grimes * The following hokiness with global variables is so that on 569b50d902SRodney W. Grimes * receipt of an interrupt signal, the partial message can be salted 579b50d902SRodney W. Grimes * away on dead.letter. 589b50d902SRodney W. Grimes */ 599b50d902SRodney W. Grimes 609b50d902SRodney W. Grimes static sig_t saveint; /* Previous SIGINT value */ 619b50d902SRodney W. Grimes static sig_t savehup; /* Previous SIGHUP value */ 629b50d902SRodney W. Grimes static sig_t savetstp; /* Previous SIGTSTP value */ 639b50d902SRodney W. Grimes static sig_t savettou; /* Previous SIGTTOU value */ 649b50d902SRodney W. Grimes static sig_t savettin; /* Previous SIGTTIN value */ 659b50d902SRodney W. Grimes static FILE *collf; /* File for saving away */ 669b50d902SRodney W. Grimes static int hadintr; /* Have seen one SIGINT so far */ 679b50d902SRodney W. Grimes 689b50d902SRodney W. Grimes static jmp_buf colljmp; /* To get back to work */ 699b50d902SRodney W. Grimes static int colljmp_p; /* whether to long jump */ 709b50d902SRodney W. Grimes static jmp_buf collabort; /* To end collection with error */ 719b50d902SRodney W. Grimes 729b50d902SRodney W. Grimes FILE * 736d8484b0SPhilippe Charnier collect(struct header *hp, int printheaders) 749b50d902SRodney W. Grimes { 759b50d902SRodney W. Grimes FILE *fbuf; 769ce73e90SMike Heffner int lc, cc, escape, eofcount, fd, c, t; 779ce73e90SMike Heffner char linebuf[LINESIZE], tempname[PATHSIZE], *cp, getsub; 78856f23edSMike Heffner sigset_t nset; 79856f23edSMike Heffner int longline, lastlong, rc; /* So we don't make 2 or more lines 80856f23edSMike Heffner out of a long input line. */ 819b50d902SRodney W. Grimes 829b50d902SRodney W. Grimes collf = NULL; 839b50d902SRodney W. Grimes /* 849b50d902SRodney W. Grimes * Start catching signals from here, but we're still die on interrupts 859b50d902SRodney W. Grimes * until we're in the main loop. 869b50d902SRodney W. Grimes */ 87856f23edSMike Heffner (void)sigemptyset(&nset); 88856f23edSMike Heffner (void)sigaddset(&nset, SIGINT); 89856f23edSMike Heffner (void)sigaddset(&nset, SIGHUP); 90856f23edSMike Heffner (void)sigprocmask(SIG_BLOCK, &nset, NULL); 919b50d902SRodney W. Grimes if ((saveint = signal(SIGINT, SIG_IGN)) != SIG_IGN) 929ce73e90SMike Heffner (void)signal(SIGINT, collint); 939b50d902SRodney W. Grimes if ((savehup = signal(SIGHUP, SIG_IGN)) != SIG_IGN) 949ce73e90SMike Heffner (void)signal(SIGHUP, collhup); 959b50d902SRodney W. Grimes savetstp = signal(SIGTSTP, collstop); 969b50d902SRodney W. Grimes savettou = signal(SIGTTOU, collstop); 979b50d902SRodney W. Grimes savettin = signal(SIGTTIN, collstop); 989b50d902SRodney W. Grimes if (setjmp(collabort) || setjmp(colljmp)) { 999ce73e90SMike Heffner (void)rm(tempname); 1009b50d902SRodney W. Grimes goto err; 1019b50d902SRodney W. Grimes } 102856f23edSMike Heffner (void)sigprocmask(SIG_UNBLOCK, &nset, NULL); 1039b50d902SRodney W. Grimes 1049b50d902SRodney W. Grimes noreset++; 1059ce73e90SMike Heffner (void)snprintf(tempname, sizeof(tempname), 1069ce73e90SMike Heffner "%s/mail.RsXXXXXXXXXX", tmpdir); 1070c3a8314SMike Heffner if ((fd = mkstemp(tempname)) == -1 || 1080c3a8314SMike Heffner (collf = Fdopen(fd, "w+")) == NULL) { 1090c3a8314SMike Heffner warn("%s", tempname); 1109b50d902SRodney W. Grimes goto err; 1119b50d902SRodney W. Grimes } 1129ce73e90SMike Heffner (void)rm(tempname); 1139b50d902SRodney W. Grimes 1149b50d902SRodney W. Grimes /* 1159b50d902SRodney W. Grimes * If we are going to prompt for a subject, 1169b50d902SRodney W. Grimes * refrain from printing a newline after 1179b50d902SRodney W. Grimes * the headers (since some people mind). 1189b50d902SRodney W. Grimes */ 1199b50d902SRodney W. Grimes t = GTO|GSUBJECT|GCC|GNL; 1209b50d902SRodney W. Grimes getsub = 0; 1219ce73e90SMike Heffner if (hp->h_subject == NULL && value("interactive") != NULL && 1229ce73e90SMike Heffner (value("ask") != NULL || value("asksub") != NULL)) 1239b50d902SRodney W. Grimes t &= ~GNL, getsub++; 1249b50d902SRodney W. Grimes if (printheaders) { 1259b50d902SRodney W. Grimes puthead(hp, stdout, t); 1269ce73e90SMike Heffner (void)fflush(stdout); 1279b50d902SRodney W. Grimes } 1289ce73e90SMike Heffner if ((cp = value("escape")) != NULL) 1299b50d902SRodney W. Grimes escape = *cp; 1309b50d902SRodney W. Grimes else 1319b50d902SRodney W. Grimes escape = ESCAPE; 1329b50d902SRodney W. Grimes eofcount = 0; 1339b50d902SRodney W. Grimes hadintr = 0; 134856f23edSMike Heffner lastlong = 0; 135856f23edSMike Heffner longline = 0; 1369b50d902SRodney W. Grimes 1379b50d902SRodney W. Grimes if (!setjmp(colljmp)) { 1389b50d902SRodney W. Grimes if (getsub) 1399b50d902SRodney W. Grimes grabh(hp, GSUBJECT); 1409b50d902SRodney W. Grimes } else { 1419b50d902SRodney W. Grimes /* 1429b50d902SRodney W. Grimes * Come here for printing the after-signal message. 1439b50d902SRodney W. Grimes * Duplicate messages won't be printed because 1449b50d902SRodney W. Grimes * the write is aborted if we get a SIGTTOU. 1459b50d902SRodney W. Grimes */ 1469b50d902SRodney W. Grimes cont: 1479b50d902SRodney W. Grimes if (hadintr) { 1489ce73e90SMike Heffner (void)fflush(stdout); 1499b50d902SRodney W. Grimes fprintf(stderr, 1509b50d902SRodney W. Grimes "\n(Interrupt -- one more to kill letter)\n"); 1519b50d902SRodney W. Grimes } else { 1529b50d902SRodney W. Grimes printf("(continue)\n"); 1539ce73e90SMike Heffner (void)fflush(stdout); 1549b50d902SRodney W. Grimes } 1559b50d902SRodney W. Grimes } 1569b50d902SRodney W. Grimes for (;;) { 1579b50d902SRodney W. Grimes colljmp_p = 1; 1589b50d902SRodney W. Grimes c = readline(stdin, linebuf, LINESIZE); 1599b50d902SRodney W. Grimes colljmp_p = 0; 1609b50d902SRodney W. Grimes if (c < 0) { 1619ce73e90SMike Heffner if (value("interactive") != NULL && 1629ce73e90SMike Heffner value("ignoreeof") != NULL && ++eofcount < 25) { 1639b50d902SRodney W. Grimes printf("Use \".\" to terminate letter\n"); 1649b50d902SRodney W. Grimes continue; 1659b50d902SRodney W. Grimes } 1669b50d902SRodney W. Grimes break; 1679b50d902SRodney W. Grimes } 168856f23edSMike Heffner lastlong = longline; 169856f23edSMike Heffner longline = c == LINESIZE - 1; 1709b50d902SRodney W. Grimes eofcount = 0; 1719b50d902SRodney W. Grimes hadintr = 0; 1729b50d902SRodney W. Grimes if (linebuf[0] == '.' && linebuf[1] == '\0' && 173856f23edSMike Heffner value("interactive") != NULL && !lastlong && 1749ce73e90SMike Heffner (value("dot") != NULL || value("ignoreeof") != NULL)) 1759b50d902SRodney W. Grimes break; 176856f23edSMike Heffner if (linebuf[0] != escape || value("interactive") == NULL || 177856f23edSMike Heffner lastlong) { 178856f23edSMike Heffner if (putline(collf, linebuf, !longline) < 0) 1799b50d902SRodney W. Grimes goto err; 1809b50d902SRodney W. Grimes continue; 1819b50d902SRodney W. Grimes } 1829b50d902SRodney W. Grimes c = linebuf[1]; 1839b50d902SRodney W. Grimes switch (c) { 1849b50d902SRodney W. Grimes default: 1859b50d902SRodney W. Grimes /* 1869b50d902SRodney W. Grimes * On double escape, just send the single one. 1879b50d902SRodney W. Grimes * Otherwise, it's an error. 1889b50d902SRodney W. Grimes */ 1899b50d902SRodney W. Grimes if (c == escape) { 190856f23edSMike Heffner if (putline(collf, &linebuf[1], !longline) < 0) 1919b50d902SRodney W. Grimes goto err; 1929b50d902SRodney W. Grimes else 1939b50d902SRodney W. Grimes break; 1949b50d902SRodney W. Grimes } 1959b50d902SRodney W. Grimes printf("Unknown tilde escape.\n"); 1969b50d902SRodney W. Grimes break; 1979b50d902SRodney W. Grimes case 'C': 1989b50d902SRodney W. Grimes /* 1999b50d902SRodney W. Grimes * Dump core. 2009b50d902SRodney W. Grimes */ 2019b50d902SRodney W. Grimes core(); 2029b50d902SRodney W. Grimes break; 2039b50d902SRodney W. Grimes case '!': 2049b50d902SRodney W. Grimes /* 2059b50d902SRodney W. Grimes * Shell escape, send the balance of the 2069b50d902SRodney W. Grimes * line to sh -c. 2079b50d902SRodney W. Grimes */ 2089b50d902SRodney W. Grimes shell(&linebuf[2]); 2099b50d902SRodney W. Grimes break; 2109b50d902SRodney W. Grimes case ':': 21169131e40SMike Heffner case '_': 2129b50d902SRodney W. Grimes /* 2139b50d902SRodney W. Grimes * Escape to command mode, but be nice! 2149b50d902SRodney W. Grimes */ 2159b50d902SRodney W. Grimes execute(&linebuf[2], 1); 2169b50d902SRodney W. Grimes goto cont; 2179b50d902SRodney W. Grimes case '.': 2189b50d902SRodney W. Grimes /* 2199b50d902SRodney W. Grimes * Simulate end of file on input. 2209b50d902SRodney W. Grimes */ 2219b50d902SRodney W. Grimes goto out; 2229b50d902SRodney W. Grimes case 'q': 2239b50d902SRodney W. Grimes /* 2249b50d902SRodney W. Grimes * Force a quit of sending mail. 2259b50d902SRodney W. Grimes * Act like an interrupt happened. 2269b50d902SRodney W. Grimes */ 2279b50d902SRodney W. Grimes hadintr++; 2289b50d902SRodney W. Grimes collint(SIGINT); 2299b50d902SRodney W. Grimes exit(1); 23069131e40SMike Heffner case 'x': 23169131e40SMike Heffner /* 23269131e40SMike Heffner * Exit, do not save in dead.letter. 23369131e40SMike Heffner */ 23469131e40SMike Heffner goto err; 2359b50d902SRodney W. Grimes case 'h': 2369b50d902SRodney W. Grimes /* 2379b50d902SRodney W. Grimes * Grab a bunch of headers. 2389b50d902SRodney W. Grimes */ 2399b50d902SRodney W. Grimes grabh(hp, GTO|GSUBJECT|GCC|GBCC); 2409b50d902SRodney W. Grimes goto cont; 2419b50d902SRodney W. Grimes case 't': 2429b50d902SRodney W. Grimes /* 2439b50d902SRodney W. Grimes * Add to the To list. 2449b50d902SRodney W. Grimes */ 2459b50d902SRodney W. Grimes hp->h_to = cat(hp->h_to, extract(&linebuf[2], GTO)); 2469b50d902SRodney W. Grimes break; 2479b50d902SRodney W. Grimes case 's': 2489b50d902SRodney W. Grimes /* 24999bd6601SJoerg Wunsch * Set the Subject line. 2509b50d902SRodney W. Grimes */ 2519b50d902SRodney W. Grimes cp = &linebuf[2]; 2526d48fa43SAndrey A. Chernov while (isspace((unsigned char)*cp)) 2539b50d902SRodney W. Grimes cp++; 2549b50d902SRodney W. Grimes hp->h_subject = savestr(cp); 2559b50d902SRodney W. Grimes break; 25699bd6601SJoerg Wunsch case 'R': 25799bd6601SJoerg Wunsch /* 25899bd6601SJoerg Wunsch * Set the Reply-To line. 25999bd6601SJoerg Wunsch */ 26099bd6601SJoerg Wunsch cp = &linebuf[2]; 2616d48fa43SAndrey A. Chernov while (isspace((unsigned char)*cp)) 26299bd6601SJoerg Wunsch cp++; 26399bd6601SJoerg Wunsch hp->h_replyto = savestr(cp); 26499bd6601SJoerg Wunsch break; 2659b50d902SRodney W. Grimes case 'c': 2669b50d902SRodney W. Grimes /* 2679b50d902SRodney W. Grimes * Add to the CC list. 2689b50d902SRodney W. Grimes */ 2699b50d902SRodney W. Grimes hp->h_cc = cat(hp->h_cc, extract(&linebuf[2], GCC)); 2709b50d902SRodney W. Grimes break; 2719b50d902SRodney W. Grimes case 'b': 2729b50d902SRodney W. Grimes /* 27369131e40SMike Heffner * Add to the BCC list. 2749b50d902SRodney W. Grimes */ 2759b50d902SRodney W. Grimes hp->h_bcc = cat(hp->h_bcc, extract(&linebuf[2], GBCC)); 2769b50d902SRodney W. Grimes break; 27769131e40SMike Heffner case 'i': 27869131e40SMike Heffner case 'A': 27969131e40SMike Heffner case 'a': 28069131e40SMike Heffner /* 2819b12c3f3SMike Heffner * Insert named variable in message. 28269131e40SMike Heffner */ 28369131e40SMike Heffner switch(c) { 28469131e40SMike Heffner case 'i': 28569131e40SMike Heffner cp = &linebuf[2]; 28669131e40SMike Heffner while(isspace((unsigned char)*cp)) 28769131e40SMike Heffner cp++; 28869131e40SMike Heffner break; 28969131e40SMike Heffner case 'a': 29069131e40SMike Heffner cp = "sign"; 29169131e40SMike Heffner break; 29269131e40SMike Heffner case 'A': 29369131e40SMike Heffner cp = "Sign"; 29469131e40SMike Heffner break; 29569131e40SMike Heffner default: 29669131e40SMike Heffner goto err; 29769131e40SMike Heffner } 29869131e40SMike Heffner 29969131e40SMike Heffner if(*cp != '\0' && (cp = value(cp)) != NULL) { 30069131e40SMike Heffner printf("%s\n", cp); 30169131e40SMike Heffner if(putline(collf, cp, 1) < 0) 30269131e40SMike Heffner goto err; 30369131e40SMike Heffner } 30469131e40SMike Heffner 30569131e40SMike Heffner break; 3069b50d902SRodney W. Grimes case 'd': 30769131e40SMike Heffner /* 30869131e40SMike Heffner * Read in the dead letter file. 30969131e40SMike Heffner */ 31069131e40SMike Heffner if (strlcpy(linebuf + 2, getdeadletter(), 31169131e40SMike Heffner sizeof(linebuf) - 2) 3120c3a8314SMike Heffner >= sizeof(linebuf) - 2) { 3130c3a8314SMike Heffner printf("Line buffer overflow\n"); 3140c3a8314SMike Heffner break; 3150c3a8314SMike Heffner } 31669131e40SMike Heffner /* FALLTHROUGH */ 3179b50d902SRodney W. Grimes case 'r': 31869131e40SMike Heffner case '<': 3199b50d902SRodney W. Grimes /* 3209b50d902SRodney W. Grimes * Invoke a file: 3219b50d902SRodney W. Grimes * Search for the file name, 3229b50d902SRodney W. Grimes * then open it and copy the contents to collf. 3239b50d902SRodney W. Grimes */ 3249b50d902SRodney W. Grimes cp = &linebuf[2]; 3256d48fa43SAndrey A. Chernov while (isspace((unsigned char)*cp)) 3269b50d902SRodney W. Grimes cp++; 3279b50d902SRodney W. Grimes if (*cp == '\0') { 3289b50d902SRodney W. Grimes printf("Interpolate what file?\n"); 3299b50d902SRodney W. Grimes break; 3309b50d902SRodney W. Grimes } 3319b50d902SRodney W. Grimes cp = expand(cp); 3329ce73e90SMike Heffner if (cp == NULL) 3339b50d902SRodney W. Grimes break; 33469131e40SMike Heffner if (*cp == '!') { 33569131e40SMike Heffner /* 33669131e40SMike Heffner * Insert stdout of command. 33769131e40SMike Heffner */ 33869131e40SMike Heffner char *sh; 33969131e40SMike Heffner int nullfd, tempfd, rc; 34069131e40SMike Heffner char tempname2[PATHSIZE]; 34169131e40SMike Heffner 34269131e40SMike Heffner if ((nullfd = open("/dev/null", O_RDONLY, 0)) 34369131e40SMike Heffner == -1) { 34469131e40SMike Heffner warn("/dev/null"); 3459b50d902SRodney W. Grimes break; 3469b50d902SRodney W. Grimes } 34769131e40SMike Heffner 34869131e40SMike Heffner (void)snprintf(tempname2, sizeof(tempname2), 34969131e40SMike Heffner "%s/mail.ReXXXXXXXXXX", tmpdir); 35069131e40SMike Heffner if ((tempfd = mkstemp(tempname2)) == -1 || 35169131e40SMike Heffner (fbuf = Fdopen(tempfd, "w+")) == NULL) { 35269131e40SMike Heffner warn("%s", tempname2); 35369131e40SMike Heffner break; 35469131e40SMike Heffner } 35569131e40SMike Heffner (void)unlink(tempname2); 35669131e40SMike Heffner 35769131e40SMike Heffner if ((sh = value("SHELL")) == NULL) 35869131e40SMike Heffner sh = _PATH_CSHELL; 35969131e40SMike Heffner 36069131e40SMike Heffner rc = run_command(sh, 0, nullfd, fileno(fbuf), 36169131e40SMike Heffner "-c", cp+1, NULL); 36269131e40SMike Heffner 36369131e40SMike Heffner close(nullfd); 36469131e40SMike Heffner 36569131e40SMike Heffner if (rc < 0) { 36669131e40SMike Heffner (void)Fclose(fbuf); 36769131e40SMike Heffner break; 36869131e40SMike Heffner } 36969131e40SMike Heffner 37069131e40SMike Heffner if (fsize(fbuf) == 0) { 37169131e40SMike Heffner fprintf(stderr, 37269131e40SMike Heffner "No bytes from command \"%s\"\n", 37369131e40SMike Heffner cp+1); 37469131e40SMike Heffner (void)Fclose(fbuf); 37569131e40SMike Heffner break; 37669131e40SMike Heffner } 37769131e40SMike Heffner 37869131e40SMike Heffner rewind(fbuf); 37969131e40SMike Heffner } else if (isdir(cp)) { 38069131e40SMike Heffner printf("%s: Directory\n", cp); 38169131e40SMike Heffner break; 38269131e40SMike Heffner } else if ((fbuf = Fopen(cp, "r")) == NULL) { 3830c3a8314SMike Heffner warn("%s", cp); 3849b50d902SRodney W. Grimes break; 3859b50d902SRodney W. Grimes } 3869b50d902SRodney W. Grimes printf("\"%s\" ", cp); 3879ce73e90SMike Heffner (void)fflush(stdout); 3889b50d902SRodney W. Grimes lc = 0; 3899b50d902SRodney W. Grimes cc = 0; 390856f23edSMike Heffner while ((rc = readline(fbuf, linebuf, LINESIZE)) >= 0) { 391856f23edSMike Heffner if (rc != LINESIZE - 1) 3929b50d902SRodney W. Grimes lc++; 393856f23edSMike Heffner if ((t = putline(collf, linebuf, 394856f23edSMike Heffner rc != LINESIZE - 1)) < 0) { 3959ce73e90SMike Heffner (void)Fclose(fbuf); 3969b50d902SRodney W. Grimes goto err; 3979b50d902SRodney W. Grimes } 3989b50d902SRodney W. Grimes cc += t; 3999b50d902SRodney W. Grimes } 4009ce73e90SMike Heffner (void)Fclose(fbuf); 4019b50d902SRodney W. Grimes printf("%d/%d\n", lc, cc); 4029b50d902SRodney W. Grimes break; 4039b50d902SRodney W. Grimes case 'w': 4049b50d902SRodney W. Grimes /* 4059b50d902SRodney W. Grimes * Write the message on a file. 4069b50d902SRodney W. Grimes */ 4079b50d902SRodney W. Grimes cp = &linebuf[2]; 4089b50d902SRodney W. Grimes while (*cp == ' ' || *cp == '\t') 4099b50d902SRodney W. Grimes cp++; 4109b50d902SRodney W. Grimes if (*cp == '\0') { 4119b50d902SRodney W. Grimes fprintf(stderr, "Write what file!?\n"); 4129b50d902SRodney W. Grimes break; 4139b50d902SRodney W. Grimes } 4149ce73e90SMike Heffner if ((cp = expand(cp)) == NULL) 4159b50d902SRodney W. Grimes break; 4169b50d902SRodney W. Grimes rewind(collf); 4179b50d902SRodney W. Grimes exwrite(cp, collf, 1); 4189b50d902SRodney W. Grimes break; 4199b50d902SRodney W. Grimes case 'm': 4209b50d902SRodney W. Grimes case 'M': 4219b50d902SRodney W. Grimes case 'f': 4229b50d902SRodney W. Grimes case 'F': 4239b50d902SRodney W. Grimes /* 4249b50d902SRodney W. Grimes * Interpolate the named messages, if we 4259b50d902SRodney W. Grimes * are in receiving mail mode. Does the 4269b50d902SRodney W. Grimes * standard list processing garbage. 4279b50d902SRodney W. Grimes * If ~f is given, we don't shift over. 4289b50d902SRodney W. Grimes */ 4290c3a8314SMike Heffner if (forward(linebuf + 2, collf, tempname, c) < 0) 4309b50d902SRodney W. Grimes goto err; 4319b50d902SRodney W. Grimes goto cont; 4329b50d902SRodney W. Grimes case '?': 4339b50d902SRodney W. Grimes if ((fbuf = Fopen(_PATH_TILDE, "r")) == NULL) { 4340c3a8314SMike Heffner warn("%s", _PATH_TILDE); 4359b50d902SRodney W. Grimes break; 4369b50d902SRodney W. Grimes } 4379b50d902SRodney W. Grimes while ((t = getc(fbuf)) != EOF) 4389b50d902SRodney W. Grimes (void)putchar(t); 4399ce73e90SMike Heffner (void)Fclose(fbuf); 4409b50d902SRodney W. Grimes break; 4419b50d902SRodney W. Grimes case 'p': 4429b50d902SRodney W. Grimes /* 4439b50d902SRodney W. Grimes * Print out the current state of the 4449b50d902SRodney W. Grimes * message without altering anything. 4459b50d902SRodney W. Grimes */ 4469b50d902SRodney W. Grimes rewind(collf); 4479b50d902SRodney W. Grimes printf("-------\nMessage contains:\n"); 4489b50d902SRodney W. Grimes puthead(hp, stdout, GTO|GSUBJECT|GCC|GBCC|GNL); 4499b50d902SRodney W. Grimes while ((t = getc(collf)) != EOF) 4509b50d902SRodney W. Grimes (void)putchar(t); 4519b50d902SRodney W. Grimes goto cont; 4529b50d902SRodney W. Grimes case '|': 4539b50d902SRodney W. Grimes /* 4549b50d902SRodney W. Grimes * Pipe message through command. 4559b50d902SRodney W. Grimes * Collect output as new message. 4569b50d902SRodney W. Grimes */ 4579b50d902SRodney W. Grimes rewind(collf); 4589b50d902SRodney W. Grimes mespipe(collf, &linebuf[2]); 4599b50d902SRodney W. Grimes goto cont; 4609b50d902SRodney W. Grimes case 'v': 4619b50d902SRodney W. Grimes case 'e': 4629b50d902SRodney W. Grimes /* 4639b50d902SRodney W. Grimes * Edit the current message. 4649b50d902SRodney W. Grimes * 'e' means to use EDITOR 4659b50d902SRodney W. Grimes * 'v' means to use VISUAL 4669b50d902SRodney W. Grimes */ 4679b50d902SRodney W. Grimes rewind(collf); 4689b50d902SRodney W. Grimes mesedit(collf, c); 4699b50d902SRodney W. Grimes goto cont; 4709b50d902SRodney W. Grimes } 4719b50d902SRodney W. Grimes } 4729b50d902SRodney W. Grimes goto out; 4739b50d902SRodney W. Grimes err: 4749b50d902SRodney W. Grimes if (collf != NULL) { 4759ce73e90SMike Heffner (void)Fclose(collf); 4769b50d902SRodney W. Grimes collf = NULL; 4779b50d902SRodney W. Grimes } 4789b50d902SRodney W. Grimes out: 4799b50d902SRodney W. Grimes if (collf != NULL) 4809b50d902SRodney W. Grimes rewind(collf); 4819b50d902SRodney W. Grimes noreset--; 482856f23edSMike Heffner (void)sigprocmask(SIG_BLOCK, &nset, NULL); 4839ce73e90SMike Heffner (void)signal(SIGINT, saveint); 4849ce73e90SMike Heffner (void)signal(SIGHUP, savehup); 4859ce73e90SMike Heffner (void)signal(SIGTSTP, savetstp); 4869ce73e90SMike Heffner (void)signal(SIGTTOU, savettou); 4879ce73e90SMike Heffner (void)signal(SIGTTIN, savettin); 488856f23edSMike Heffner (void)sigprocmask(SIG_UNBLOCK, &nset, NULL); 4899ce73e90SMike Heffner return (collf); 4909b50d902SRodney W. Grimes } 4919b50d902SRodney W. Grimes 4929b50d902SRodney W. Grimes /* 4939b50d902SRodney W. Grimes * Write a file, ex-like if f set. 4949b50d902SRodney W. Grimes */ 4959b50d902SRodney W. Grimes int 4966d8484b0SPhilippe Charnier exwrite(char name[], FILE *fp, int f) 4979b50d902SRodney W. Grimes { 4989ce73e90SMike Heffner FILE *of; 4999ce73e90SMike Heffner int c, lc; 5009b50d902SRodney W. Grimes long cc; 5019b50d902SRodney W. Grimes struct stat junk; 5029b50d902SRodney W. Grimes 5039b50d902SRodney W. Grimes if (f) { 5049b50d902SRodney W. Grimes printf("\"%s\" ", name); 5059ce73e90SMike Heffner (void)fflush(stdout); 5069b50d902SRodney W. Grimes } 5070c3a8314SMike Heffner if (stat(name, &junk) >= 0 && S_ISREG(junk.st_mode)) { 5089b50d902SRodney W. Grimes if (!f) 5099b50d902SRodney W. Grimes fprintf(stderr, "%s: ", name); 5109b50d902SRodney W. Grimes fprintf(stderr, "File exists\n"); 5119b50d902SRodney W. Grimes return (-1); 5129b50d902SRodney W. Grimes } 5139b50d902SRodney W. Grimes if ((of = Fopen(name, "w")) == NULL) { 5149ce73e90SMike Heffner warn((char *)NULL); 5159b50d902SRodney W. Grimes return (-1); 5169b50d902SRodney W. Grimes } 5179b50d902SRodney W. Grimes lc = 0; 5189b50d902SRodney W. Grimes cc = 0; 5199b50d902SRodney W. Grimes while ((c = getc(fp)) != EOF) { 5209b50d902SRodney W. Grimes cc++; 5219b50d902SRodney W. Grimes if (c == '\n') 5229b50d902SRodney W. Grimes lc++; 5239b50d902SRodney W. Grimes (void)putc(c, of); 5249b50d902SRodney W. Grimes if (ferror(of)) { 5250c3a8314SMike Heffner warnx("%s", name); 5269ce73e90SMike Heffner (void)Fclose(of); 5279b50d902SRodney W. Grimes return (-1); 5289b50d902SRodney W. Grimes } 5299b50d902SRodney W. Grimes } 5309ce73e90SMike Heffner (void)Fclose(of); 5319b50d902SRodney W. Grimes printf("%d/%ld\n", lc, cc); 5329ce73e90SMike Heffner (void)fflush(stdout); 5339b50d902SRodney W. Grimes return (0); 5349b50d902SRodney W. Grimes } 5359b50d902SRodney W. Grimes 5369b50d902SRodney W. Grimes /* 5379b50d902SRodney W. Grimes * Edit the message being collected on fp. 5389b50d902SRodney W. Grimes * On return, make the edit file the new temp file. 5399b50d902SRodney W. Grimes */ 5409b50d902SRodney W. Grimes void 5416d8484b0SPhilippe Charnier mesedit(FILE *fp, int c) 5429b50d902SRodney W. Grimes { 5439b50d902SRodney W. Grimes sig_t sigint = signal(SIGINT, SIG_IGN); 5449b50d902SRodney W. Grimes FILE *nf = run_editor(fp, (off_t)-1, c, 0); 5459b50d902SRodney W. Grimes 5469b50d902SRodney W. Grimes if (nf != NULL) { 547af8c3262SAndrey A. Chernov (void)fseeko(nf, (off_t)0, SEEK_END); 5489b50d902SRodney W. Grimes collf = nf; 5499ce73e90SMike Heffner (void)Fclose(fp); 5509b50d902SRodney W. Grimes } 5519b50d902SRodney W. Grimes (void)signal(SIGINT, sigint); 5529b50d902SRodney W. Grimes } 5539b50d902SRodney W. Grimes 5549b50d902SRodney W. Grimes /* 5559b50d902SRodney W. Grimes * Pipe the message through the command. 5569b50d902SRodney W. Grimes * Old message is on stdin of command; 5579b50d902SRodney W. Grimes * New message collected from stdout. 5589b50d902SRodney W. Grimes * Sh -c must return 0 to accept the new message. 5599b50d902SRodney W. Grimes */ 5609b50d902SRodney W. Grimes void 5616d8484b0SPhilippe Charnier mespipe(FILE *fp, char cmd[]) 5629b50d902SRodney W. Grimes { 5639b50d902SRodney W. Grimes FILE *nf; 5640c3a8314SMike Heffner int fd; 5659b50d902SRodney W. Grimes sig_t sigint = signal(SIGINT, SIG_IGN); 5669ce73e90SMike Heffner char *sh, tempname[PATHSIZE]; 5679b50d902SRodney W. Grimes 5689ce73e90SMike Heffner (void)snprintf(tempname, sizeof(tempname), 5699ce73e90SMike Heffner "%s/mail.ReXXXXXXXXXX", tmpdir); 5700c3a8314SMike Heffner if ((fd = mkstemp(tempname)) == -1 || 5710c3a8314SMike Heffner (nf = Fdopen(fd, "w+")) == NULL) { 5720c3a8314SMike Heffner warn("%s", tempname); 5739b50d902SRodney W. Grimes goto out; 5749b50d902SRodney W. Grimes } 5750c3a8314SMike Heffner (void)rm(tempname); 5769b50d902SRodney W. Grimes /* 5779b50d902SRodney W. Grimes * stdin = current message. 5789b50d902SRodney W. Grimes * stdout = new message. 5799b50d902SRodney W. Grimes */ 5809ce73e90SMike Heffner if ((sh = value("SHELL")) == NULL) 5819ce73e90SMike Heffner sh = _PATH_CSHELL; 5829ce73e90SMike Heffner if (run_command(sh, 5839ce73e90SMike Heffner 0, fileno(fp), fileno(nf), "-c", cmd, NULL) < 0) { 5849b50d902SRodney W. Grimes (void)Fclose(nf); 5859b50d902SRodney W. Grimes goto out; 5869b50d902SRodney W. Grimes } 5879b50d902SRodney W. Grimes if (fsize(nf) == 0) { 5889b50d902SRodney W. Grimes fprintf(stderr, "No bytes from \"%s\" !?\n", cmd); 5899b50d902SRodney W. Grimes (void)Fclose(nf); 5909b50d902SRodney W. Grimes goto out; 5919b50d902SRodney W. Grimes } 5929b50d902SRodney W. Grimes /* 5939b50d902SRodney W. Grimes * Take new files. 5949b50d902SRodney W. Grimes */ 595af8c3262SAndrey A. Chernov (void)fseeko(nf, (off_t)0, SEEK_END); 5969b50d902SRodney W. Grimes collf = nf; 5979b50d902SRodney W. Grimes (void)Fclose(fp); 5989b50d902SRodney W. Grimes out: 5999b50d902SRodney W. Grimes (void)signal(SIGINT, sigint); 6009b50d902SRodney W. Grimes } 6019b50d902SRodney W. Grimes 6029b50d902SRodney W. Grimes /* 6039b50d902SRodney W. Grimes * Interpolate the named messages into the current 6049b50d902SRodney W. Grimes * message, preceding each line with a tab. 6059b50d902SRodney W. Grimes * Return a count of the number of characters now in 6069b50d902SRodney W. Grimes * the message, or -1 if an error is encountered writing 6079b50d902SRodney W. Grimes * the message temporary. The flag argument is 'm' if we 6089b50d902SRodney W. Grimes * should shift over and 'f' if not. 6099b50d902SRodney W. Grimes */ 6109b50d902SRodney W. Grimes int 6116d8484b0SPhilippe Charnier forward(char ms[], FILE *fp, char *fn, int f) 6129b50d902SRodney W. Grimes { 6139ce73e90SMike Heffner int *msgvec; 6149b50d902SRodney W. Grimes struct ignoretab *ig; 6159b50d902SRodney W. Grimes char *tabst; 6169b50d902SRodney W. Grimes 6179ce73e90SMike Heffner msgvec = (int *)salloc((msgCount+1) * sizeof(*msgvec)); 6189ce73e90SMike Heffner if (msgvec == NULL) 6199b50d902SRodney W. Grimes return (0); 6209b50d902SRodney W. Grimes if (getmsglist(ms, msgvec, 0) < 0) 6219b50d902SRodney W. Grimes return (0); 6229b50d902SRodney W. Grimes if (*msgvec == 0) { 6239b50d902SRodney W. Grimes *msgvec = first(0, MMNORM); 624d030d2d2SPoul-Henning Kamp if (*msgvec == 0) { 6259b50d902SRodney W. Grimes printf("No appropriate messages\n"); 6269b50d902SRodney W. Grimes return (0); 6279b50d902SRodney W. Grimes } 628d030d2d2SPoul-Henning Kamp msgvec[1] = 0; 6299b50d902SRodney W. Grimes } 6309b50d902SRodney W. Grimes if (f == 'f' || f == 'F') 6319ce73e90SMike Heffner tabst = NULL; 6329ce73e90SMike Heffner else if ((tabst = value("indentprefix")) == NULL) 6339b50d902SRodney W. Grimes tabst = "\t"; 6346d48fa43SAndrey A. Chernov ig = isupper((unsigned char)f) ? NULL : ignore; 6359b50d902SRodney W. Grimes printf("Interpolating:"); 6369b50d902SRodney W. Grimes for (; *msgvec != 0; msgvec++) { 6379b50d902SRodney W. Grimes struct message *mp = message + *msgvec - 1; 6389b50d902SRodney W. Grimes 6399b50d902SRodney W. Grimes touch(mp); 6409b50d902SRodney W. Grimes printf(" %d", *msgvec); 6410c3a8314SMike Heffner if (sendmessage(mp, fp, ig, tabst) < 0) { 6420c3a8314SMike Heffner warnx("%s", fn); 6439b50d902SRodney W. Grimes return (-1); 6449b50d902SRodney W. Grimes } 6459b50d902SRodney W. Grimes } 6469b50d902SRodney W. Grimes printf("\n"); 6479b50d902SRodney W. Grimes return (0); 6489b50d902SRodney W. Grimes } 6499b50d902SRodney W. Grimes 6509b50d902SRodney W. Grimes /* 6519b50d902SRodney W. Grimes * Print (continue) when continued after ^Z. 6529b50d902SRodney W. Grimes */ 6539b50d902SRodney W. Grimes /*ARGSUSED*/ 6549b50d902SRodney W. Grimes void 6556d8484b0SPhilippe Charnier collstop(int s) 6569b50d902SRodney W. Grimes { 6579b50d902SRodney W. Grimes sig_t old_action = signal(s, SIG_DFL); 658856f23edSMike Heffner sigset_t nset; 6599b50d902SRodney W. Grimes 660856f23edSMike Heffner (void)sigemptyset(&nset); 661856f23edSMike Heffner (void)sigaddset(&nset, s); 662856f23edSMike Heffner (void)sigprocmask(SIG_UNBLOCK, &nset, NULL); 6639ce73e90SMike Heffner (void)kill(0, s); 664856f23edSMike Heffner (void)sigprocmask(SIG_BLOCK, &nset, NULL); 6659ce73e90SMike Heffner (void)signal(s, old_action); 6669b50d902SRodney W. Grimes if (colljmp_p) { 6679b50d902SRodney W. Grimes colljmp_p = 0; 6689b50d902SRodney W. Grimes hadintr = 0; 6699b50d902SRodney W. Grimes longjmp(colljmp, 1); 6709b50d902SRodney W. Grimes } 6719b50d902SRodney W. Grimes } 6729b50d902SRodney W. Grimes 6739b50d902SRodney W. Grimes /* 6749b50d902SRodney W. Grimes * On interrupt, come here to save the partial message in ~/dead.letter. 6759b50d902SRodney W. Grimes * Then jump out of the collection loop. 6769b50d902SRodney W. Grimes */ 6779b50d902SRodney W. Grimes /*ARGSUSED*/ 6789b50d902SRodney W. Grimes void 6796d8484b0SPhilippe Charnier collint(int s __unused) 6809b50d902SRodney W. Grimes { 6819b50d902SRodney W. Grimes /* 6829b50d902SRodney W. Grimes * the control flow is subtle, because we can be called from ~q. 6839b50d902SRodney W. Grimes */ 6849b50d902SRodney W. Grimes if (!hadintr) { 6859ce73e90SMike Heffner if (value("ignore") != NULL) { 6869ce73e90SMike Heffner printf("@"); 6879ce73e90SMike Heffner (void)fflush(stdout); 6889b50d902SRodney W. Grimes clearerr(stdin); 6899b50d902SRodney W. Grimes return; 6909b50d902SRodney W. Grimes } 6919b50d902SRodney W. Grimes hadintr = 1; 6929b50d902SRodney W. Grimes longjmp(colljmp, 1); 6939b50d902SRodney W. Grimes } 6949b50d902SRodney W. Grimes rewind(collf); 6959ce73e90SMike Heffner if (value("nosave") == NULL) 6969b50d902SRodney W. Grimes savedeadletter(collf); 6979b50d902SRodney W. Grimes longjmp(collabort, 1); 6989b50d902SRodney W. Grimes } 6999b50d902SRodney W. Grimes 7009b50d902SRodney W. Grimes /*ARGSUSED*/ 7019b50d902SRodney W. Grimes void 7026d8484b0SPhilippe Charnier collhup(int s __unused) 7039b50d902SRodney W. Grimes { 7049b50d902SRodney W. Grimes rewind(collf); 7059b50d902SRodney W. Grimes savedeadletter(collf); 7069b50d902SRodney W. Grimes /* 7079b50d902SRodney W. Grimes * Let's pretend nobody else wants to clean up, 7089b50d902SRodney W. Grimes * a true statement at this time. 7099b50d902SRodney W. Grimes */ 7109b50d902SRodney W. Grimes exit(1); 7119b50d902SRodney W. Grimes } 7129b50d902SRodney W. Grimes 7139b50d902SRodney W. Grimes void 7146d8484b0SPhilippe Charnier savedeadletter(FILE *fp) 7159b50d902SRodney W. Grimes { 7169ce73e90SMike Heffner FILE *dbuf; 7179ce73e90SMike Heffner int c; 7189b50d902SRodney W. Grimes char *cp; 7199b50d902SRodney W. Grimes 7209b50d902SRodney W. Grimes if (fsize(fp) == 0) 7219b50d902SRodney W. Grimes return; 7229b50d902SRodney W. Grimes cp = getdeadletter(); 7239b50d902SRodney W. Grimes c = umask(077); 7249b50d902SRodney W. Grimes dbuf = Fopen(cp, "a"); 7259b50d902SRodney W. Grimes (void)umask(c); 7269b50d902SRodney W. Grimes if (dbuf == NULL) 7279b50d902SRodney W. Grimes return; 7289b50d902SRodney W. Grimes while ((c = getc(fp)) != EOF) 7299b50d902SRodney W. Grimes (void)putc(c, dbuf); 7309ce73e90SMike Heffner (void)Fclose(dbuf); 7319b50d902SRodney W. Grimes rewind(fp); 7329b50d902SRodney W. Grimes } 733