1160814Ssimon/* 2160814Ssimon * Copyright (c) 1980, 1993 3160814Ssimon * The Regents of the University of California. All rights reserved. 4160814Ssimon * 5160814Ssimon * Redistribution and use in source and binary forms, with or without 6160814Ssimon * modification, are permitted provided that the following conditions 7160814Ssimon * are met: 8160814Ssimon * 1. Redistributions of source code must retain the above copyright 9160814Ssimon * notice, this list of conditions and the following disclaimer. 10160814Ssimon * 2. Redistributions in binary form must reproduce the above copyright 11160814Ssimon * notice, this list of conditions and the following disclaimer in the 12160814Ssimon * documentation and/or other materials provided with the distribution. 13160814Ssimon * 4. Neither the name of the University nor the names of its contributors 14160814Ssimon * may be used to endorse or promote products derived from this software 15160814Ssimon * without specific prior written permission. 16160814Ssimon * 17160814Ssimon * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18160814Ssimon * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19160814Ssimon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20160814Ssimon * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21160814Ssimon * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22160814Ssimon * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23160814Ssimon * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24160814Ssimon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25160814Ssimon * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26160814Ssimon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27160814Ssimon * SUCH DAMAGE. 28160814Ssimon */ 29160814Ssimon 30160814Ssimon#ifndef lint 31160814Ssimon#if 0 32160814Ssimonstatic char sccsid[] = "@(#)collect.c 8.2 (Berkeley) 4/19/94"; 33160814Ssimon#endif 34160814Ssimon#endif /* not lint */ 35160814Ssimon#include <sys/cdefs.h> 36160814Ssimon__FBSDID("$FreeBSD$"); 37160814Ssimon 38160814Ssimon/* 39160814Ssimon * Mail -- a mail program 40160814Ssimon * 41160814Ssimon * Collect input from standard input, handling 42160814Ssimon * ~ escapes. 43160814Ssimon */ 44160814Ssimon 45160814Ssimon#include "rcv.h" 46160814Ssimon#include <fcntl.h> 47160814Ssimon#include "extern.h" 48160814Ssimon 49160814Ssimon/* 50160814Ssimon * Read a message from standard output and return a read file to it 51160814Ssimon * or NULL on error. 52160814Ssimon */ 53160814Ssimon 54160814Ssimon/* 55160814Ssimon * The following hokiness with global variables is so that on 56160814Ssimon * receipt of an interrupt signal, the partial message can be salted 57160814Ssimon * away on dead.letter. 58160814Ssimon */ 59160814Ssimon 60160814Ssimonstatic sig_t saveint; /* Previous SIGINT value */ 61160814Ssimonstatic sig_t savehup; /* Previous SIGHUP value */ 62160814Ssimonstatic sig_t savetstp; /* Previous SIGTSTP value */ 63160814Ssimonstatic sig_t savettou; /* Previous SIGTTOU value */ 64160814Ssimonstatic sig_t savettin; /* Previous SIGTTIN value */ 65160814Ssimonstatic FILE *collf; /* File for saving away */ 66160814Ssimonstatic int hadintr; /* Have seen one SIGINT so far */ 67160814Ssimon 68160814Ssimonstatic jmp_buf colljmp; /* To get back to work */ 69160814Ssimonstatic int colljmp_p; /* whether to long jump */ 70160814Ssimonstatic jmp_buf collabort; /* To end collection with error */ 71160814Ssimon 72160814SsimonFILE * 73160814Ssimoncollect(struct header *hp, int printheaders) 74160814Ssimon{ 75160814Ssimon FILE *fbuf; 76160814Ssimon int lc, cc, escape, eofcount, fd, c, t; 77160814Ssimon char linebuf[LINESIZE], tempname[PATHSIZE], *cp, getsub; 78160814Ssimon sigset_t nset; 79160814Ssimon int longline, lastlong, rc; /* So we don't make 2 or more lines 80160814Ssimon out of a long input line. */ 81160814Ssimon 82160814Ssimon collf = NULL; 83160814Ssimon /* 84160814Ssimon * Start catching signals from here, but we're still die on interrupts 85160814Ssimon * until we're in the main loop. 86160814Ssimon */ 87160814Ssimon (void)sigemptyset(&nset); 88160814Ssimon (void)sigaddset(&nset, SIGINT); 89160814Ssimon (void)sigaddset(&nset, SIGHUP); 90160814Ssimon (void)sigprocmask(SIG_BLOCK, &nset, NULL); 91160814Ssimon if ((saveint = signal(SIGINT, SIG_IGN)) != SIG_IGN) 92160814Ssimon (void)signal(SIGINT, collint); 93160814Ssimon if ((savehup = signal(SIGHUP, SIG_IGN)) != SIG_IGN) 94160814Ssimon (void)signal(SIGHUP, collhup); 95160814Ssimon savetstp = signal(SIGTSTP, collstop); 96160814Ssimon savettou = signal(SIGTTOU, collstop); 97160814Ssimon savettin = signal(SIGTTIN, collstop); 98160814Ssimon if (setjmp(collabort) || setjmp(colljmp)) { 99160814Ssimon (void)rm(tempname); 100160814Ssimon goto err; 101160814Ssimon } 102160814Ssimon (void)sigprocmask(SIG_UNBLOCK, &nset, NULL); 103160814Ssimon 104160814Ssimon noreset++; 105160814Ssimon (void)snprintf(tempname, sizeof(tempname), 106160814Ssimon "%s/mail.RsXXXXXXXXXX", tmpdir); 107160814Ssimon if ((fd = mkstemp(tempname)) == -1 || 108160814Ssimon (collf = Fdopen(fd, "w+")) == NULL) { 109160814Ssimon warn("%s", tempname); 110160814Ssimon goto err; 111160814Ssimon } 112160814Ssimon (void)rm(tempname); 113160814Ssimon 114160814Ssimon /* 115160814Ssimon * If we are going to prompt for a subject, 116160814Ssimon * refrain from printing a newline after 117160814Ssimon * the headers (since some people mind). 118160814Ssimon */ 119160814Ssimon t = GTO|GSUBJECT|GCC|GNL; 120160814Ssimon getsub = 0; 121160814Ssimon if (hp->h_subject == NULL && value("interactive") != NULL && 122160814Ssimon (value("ask") != NULL || value("asksub") != NULL)) 123160814Ssimon t &= ~GNL, getsub++; 124160814Ssimon if (printheaders) { 125160814Ssimon puthead(hp, stdout, t); 126215697Ssimon (void)fflush(stdout); 127160814Ssimon } 128215697Ssimon if ((cp = value("escape")) != NULL) 129215697Ssimon escape = *cp; 130215697Ssimon else 131215697Ssimon escape = ESCAPE; 132215697Ssimon eofcount = 0; 133215697Ssimon hadintr = 0; 134215697Ssimon lastlong = 0; 135215697Ssimon longline = 0; 136215697Ssimon 137215697Ssimon if (!setjmp(colljmp)) { 138215697Ssimon if (getsub) 139215697Ssimon grabh(hp, GSUBJECT); 140215697Ssimon } else { 141215697Ssimon /* 142215697Ssimon * Come here for printing the after-signal message. 143215697Ssimon * Duplicate messages won't be printed because 144215697Ssimon * the write is aborted if we get a SIGTTOU. 145215697Ssimon */ 146215697Ssimoncont: 147215697Ssimon if (hadintr) { 148215697Ssimon (void)fflush(stdout); 149215697Ssimon fprintf(stderr, 150215697Ssimon "\n(Interrupt -- one more to kill letter)\n"); 151215697Ssimon } else { 152215697Ssimon printf("(continue)\n"); 153215697Ssimon (void)fflush(stdout); 154215697Ssimon } 155215697Ssimon } 156237657Sjkim for (;;) { 157215697Ssimon colljmp_p = 1; 158160814Ssimon c = readline(stdin, linebuf, LINESIZE); 159279264Sdelphij colljmp_p = 0; 160160814Ssimon if (c < 0) { 161160814Ssimon if (value("interactive") != NULL && 162160814Ssimon value("ignoreeof") != NULL && ++eofcount < 25) { 163160814Ssimon printf("Use \".\" to terminate letter\n"); 164160814Ssimon continue; 165160814Ssimon } 166160814Ssimon break; 167160814Ssimon } 168160814Ssimon lastlong = longline; 169172767Ssimon longline = c == LINESIZE - 1; 170160814Ssimon eofcount = 0; 171160814Ssimon hadintr = 0; 172215697Ssimon if (linebuf[0] == '.' && linebuf[1] == '\0' && 173172767Ssimon value("interactive") != NULL && !lastlong && 174172767Ssimon (value("dot") != NULL || value("ignoreeof") != NULL)) 175172767Ssimon break; 176215697Ssimon if (linebuf[0] != escape || value("interactive") == NULL || 177160814Ssimon lastlong) { 178172767Ssimon if (putline(collf, linebuf, !longline) < 0) 179172767Ssimon goto err; 180172767Ssimon continue; 181160814Ssimon } 182172767Ssimon c = linebuf[1]; 183172767Ssimon switch (c) { 184172767Ssimon default: 185172767Ssimon /* 186172767Ssimon * On double escape, just send the single one. 187172767Ssimon * Otherwise, it's an error. 188172767Ssimon */ 189172767Ssimon if (c == escape) { 190172767Ssimon if (putline(collf, &linebuf[1], !longline) < 0) 191160814Ssimon goto err; 192172767Ssimon else 193172767Ssimon break; 194160814Ssimon } 195215697Ssimon printf("Unknown tilde escape.\n"); 196215697Ssimon break; 197215697Ssimon case 'C': 198215697Ssimon /* 199215697Ssimon * Dump core. 200215697Ssimon */ 201215697Ssimon core(); 202215697Ssimon break; 203215697Ssimon case '!': 204215697Ssimon /* 205215697Ssimon * Shell escape, send the balance of the 206215697Ssimon * line to sh -c. 207215697Ssimon */ 208215697Ssimon shell(&linebuf[2]); 209215697Ssimon break; 210172767Ssimon case ':': 211172767Ssimon case '_': 212172767Ssimon /* 213279264Sdelphij * Escape to command mode, but be nice! 214172767Ssimon */ 215260405Sdelphij execute(&linebuf[2], 1); 216260405Sdelphij goto cont; 217260405Sdelphij case '.': 218260405Sdelphij /* 219260405Sdelphij * Simulate end of file on input. 220260405Sdelphij */ 221172767Ssimon goto out; 222215697Ssimon case 'q': 223172767Ssimon /* 224172767Ssimon * Force a quit of sending mail. 225160814Ssimon * Act like an interrupt happened. 226279264Sdelphij */ 227279264Sdelphij hadintr++; 228279264Sdelphij collint(SIGINT); 229279264Sdelphij exit(1); 230279264Sdelphij case 'x': 231279264Sdelphij /* 232279264Sdelphij * Exit, do not save in dead.letter. 233160814Ssimon */ 234160814Ssimon goto err; 235279264Sdelphij case 'h': 236172767Ssimon /* 237279264Sdelphij * Grab a bunch of headers. 238279264Sdelphij */ 239279264Sdelphij grabh(hp, GTO|GSUBJECT|GCC|GBCC); 240279264Sdelphij goto cont; 241160814Ssimon case 't': 242279264Sdelphij /* 243279264Sdelphij * Add to the To list. 244279264Sdelphij */ 245279264Sdelphij hp->h_to = cat(hp->h_to, extract(&linebuf[2], GTO)); 246279264Sdelphij break; 247279264Sdelphij case 's': 248279264Sdelphij /* 249279264Sdelphij * Set the Subject line. 250279264Sdelphij */ 251160814Ssimon cp = &linebuf[2]; 252279264Sdelphij while (isspace((unsigned char)*cp)) 253279264Sdelphij cp++; 254160814Ssimon hp->h_subject = savestr(cp); 255279264Sdelphij break; 256279264Sdelphij case 'R': 257160814Ssimon /* 258279264Sdelphij * Set the Reply-To line. 259279264Sdelphij */ 260279264Sdelphij cp = &linebuf[2]; 261279264Sdelphij while (isspace((unsigned char)*cp)) 262279264Sdelphij cp++; 263279264Sdelphij hp->h_replyto = savestr(cp); 264279264Sdelphij break; 265160814Ssimon case 'c': 266279264Sdelphij /* 267279264Sdelphij * Add to the CC list. 268160814Ssimon */ 269279264Sdelphij hp->h_cc = cat(hp->h_cc, extract(&linebuf[2], GCC)); 270160814Ssimon break; 271160814Ssimon case 'b': 272160814Ssimon /* 273160814Ssimon * Add to the BCC list. 274160814Ssimon */ 275205128Ssimon hp->h_bcc = cat(hp->h_bcc, extract(&linebuf[2], GBCC)); 276238405Sjkim break; 277205128Ssimon case 'i': 278205128Ssimon case 'A': 279205128Ssimon case 'a': 280205128Ssimon /* 281205128Ssimon * Insert named variable in message. 282205128Ssimon */ 283205128Ssimon switch(c) { 284205128Ssimon case 'i': 285205128Ssimon cp = &linebuf[2]; 286160814Ssimon while(isspace((unsigned char)*cp)) 287279264Sdelphij cp++; 288279264Sdelphij break; 289160814Ssimon case 'a': 290279264Sdelphij cp = "sign"; 291279264Sdelphij break; 292279264Sdelphij case 'A': 293279264Sdelphij cp = "Sign"; 294279264Sdelphij break; 295279264Sdelphij default: 296160814Ssimon goto err; 297160814Ssimon } 298160814Ssimon 299160814Ssimon if(*cp != '\0' && (cp = value(cp)) != NULL) { 300160814Ssimon printf("%s\n", cp); 301160814Ssimon if(putline(collf, cp, 1) < 0) 302160814Ssimon goto err; 303279264Sdelphij } 304279264Sdelphij 305279264Sdelphij break; 306279264Sdelphij case 'd': 307279264Sdelphij /* 308279264Sdelphij * Read in the dead letter file. 309279264Sdelphij */ 310279264Sdelphij if (strlcpy(linebuf + 2, getdeadletter(), 311279264Sdelphij sizeof(linebuf) - 2) 312279264Sdelphij >= sizeof(linebuf) - 2) { 313160814Ssimon printf("Line buffer overflow\n"); 314160814Ssimon break; 315279264Sdelphij } 316279264Sdelphij /* FALLTHROUGH */ 317160814Ssimon case 'r': 318160814Ssimon case '<': 319160814Ssimon /* 320160814Ssimon * Invoke a file: 321279264Sdelphij * Search for the file name, 322279264Sdelphij * then open it and copy the contents to collf. 323279264Sdelphij */ 324160814Ssimon cp = &linebuf[2]; 325160814Ssimon while (isspace((unsigned char)*cp)) 326160814Ssimon cp++; 327160814Ssimon if (*cp == '\0') { 328160814Ssimon printf("Interpolate what file?\n"); 329160814Ssimon break; 330160814Ssimon } 331160814Ssimon cp = expand(cp); 332160814Ssimon if (cp == NULL) 333160814Ssimon break; 334279264Sdelphij if (*cp == '!') { 335279264Sdelphij /* 336279264Sdelphij * Insert stdout of command. 337279264Sdelphij */ 338279264Sdelphij char *sh; 339160814Ssimon int nullfd, tempfd, rc; 340172767Ssimon char tempname2[PATHSIZE]; 341279264Sdelphij 342279264Sdelphij if ((nullfd = open("/dev/null", O_RDONLY, 0)) 343279264Sdelphij == -1) { 344279264Sdelphij warn("/dev/null"); 345279264Sdelphij break; 346279264Sdelphij } 347279264Sdelphij 348279264Sdelphij (void)snprintf(tempname2, sizeof(tempname2), 349279264Sdelphij "%s/mail.ReXXXXXXXXXX", tmpdir); 350279264Sdelphij if ((tempfd = mkstemp(tempname2)) == -1 || 351279264Sdelphij (fbuf = Fdopen(tempfd, "w+")) == NULL) { 352279264Sdelphij warn("%s", tempname2); 353160814Ssimon break; 354160814Ssimon } 355160814Ssimon (void)unlink(tempname2); 356160814Ssimon 357160814Ssimon if ((sh = value("SHELL")) == NULL) 358160814Ssimon sh = _PATH_CSHELL; 359160814Ssimon 360160814Ssimon rc = run_command(sh, 0, nullfd, fileno(fbuf), 361160814Ssimon "-c", cp+1, NULL); 362160814Ssimon 363160814Ssimon close(nullfd); 364160814Ssimon 365160814Ssimon if (rc < 0) { 366160814Ssimon (void)Fclose(fbuf); 367160814Ssimon break; 368160814Ssimon } 369279264Sdelphij 370205128Ssimon if (fsize(fbuf) == 0) { 371279264Sdelphij fprintf(stderr, 372279264Sdelphij "No bytes from command \"%s\"\n", 373279264Sdelphij cp+1); 374279264Sdelphij (void)Fclose(fbuf); 375279264Sdelphij break; 376279264Sdelphij } 377279264Sdelphij 378279264Sdelphij rewind(fbuf); 379279264Sdelphij } else if (isdir(cp)) { 380279264Sdelphij printf("%s: Directory\n", cp); 381279264Sdelphij break; 382160814Ssimon } else if ((fbuf = Fopen(cp, "r")) == NULL) { 383279264Sdelphij warn("%s", cp); 384160814Ssimon break; 385279264Sdelphij } 386160814Ssimon printf("\"%s\" ", cp); 387160814Ssimon (void)fflush(stdout); 388160814Ssimon lc = 0; 389172767Ssimon cc = 0; 390160814Ssimon while ((rc = readline(fbuf, linebuf, LINESIZE)) >= 0) { 391160814Ssimon if (rc != LINESIZE - 1) 392172767Ssimon lc++; 393172767Ssimon if ((t = putline(collf, linebuf, 394160814Ssimon rc != LINESIZE - 1)) < 0) { 395172767Ssimon (void)Fclose(fbuf); 396160814Ssimon goto err; 397160814Ssimon } 398194206Ssimon cc += t; 399172767Ssimon } 400194206Ssimon (void)Fclose(fbuf); 401172767Ssimon printf("%d/%d\n", lc, cc); 402238405Sjkim break; 403172767Ssimon case 'w': 404172767Ssimon /* 405172767Ssimon * Write the message on a file. 406172767Ssimon */ 407172767Ssimon cp = &linebuf[2]; 408172767Ssimon while (*cp == ' ' || *cp == '\t') 409172767Ssimon cp++; 410172767Ssimon if (*cp == '\0') { 411172767Ssimon fprintf(stderr, "Write what file!?\n"); 412194206Ssimon break; 413172767Ssimon } 414172767Ssimon if ((cp = expand(cp)) == NULL) 415172767Ssimon break; 416172767Ssimon rewind(collf); 417194206Ssimon exwrite(cp, collf, 1); 418172767Ssimon break; 419172767Ssimon case 'm': 420194206Ssimon case 'M': 421172767Ssimon case 'f': 422172767Ssimon case 'F': 423160814Ssimon /* 424160814Ssimon * Interpolate the named messages, if we 425160814Ssimon * are in receiving mail mode. Does the 426160814Ssimon * standard list processing garbage. 427160814Ssimon * If ~f is given, we don't shift over. 428160814Ssimon */ 429160814Ssimon if (forward(linebuf + 2, collf, tempname, c) < 0) 430160814Ssimon goto err; 431160814Ssimon goto cont; 432172767Ssimon case '?': 433160814Ssimon if ((fbuf = Fopen(_PATH_TILDE, "r")) == NULL) { 434160814Ssimon warn("%s", _PATH_TILDE); 435160814Ssimon break; 436160814Ssimon } 437160814Ssimon while ((t = getc(fbuf)) != EOF) 438160814Ssimon (void)putchar(t); 439160814Ssimon (void)Fclose(fbuf); 440160814Ssimon break; 441160814Ssimon case 'p': 442160814Ssimon /* 443160814Ssimon * Print out the current state of the 444160814Ssimon * message without altering anything. 445160814Ssimon */ 446160814Ssimon rewind(collf); 447160814Ssimon printf("-------\nMessage contains:\n"); 448160814Ssimon puthead(hp, stdout, GTO|GSUBJECT|GCC|GBCC|GNL); 449160814Ssimon while ((t = getc(collf)) != EOF) 450160814Ssimon (void)putchar(t); 451160814Ssimon goto cont; 452172767Ssimon case '|': 453215697Ssimon /* 454215697Ssimon * Pipe message through command. 455160814Ssimon * Collect output as new message. 456160814Ssimon */ 457160814Ssimon rewind(collf); 458160814Ssimon mespipe(collf, &linebuf[2]); 459160814Ssimon goto cont; 460160814Ssimon case 'v': 461160814Ssimon case 'e': 462160814Ssimon /* 463160814Ssimon * Edit the current message. 464160814Ssimon * 'e' means to use EDITOR 465160814Ssimon * 'v' means to use VISUAL 466160814Ssimon */ 467160814Ssimon rewind(collf); 468160814Ssimon mesedit(collf, c); 469160814Ssimon goto cont; 470160814Ssimon } 471160814Ssimon } 472172767Ssimon goto out; 473172767Ssimonerr: 474215697Ssimon if (collf != NULL) { 475160814Ssimon (void)Fclose(collf); 476215697Ssimon collf = NULL; 477215697Ssimon } 478215697Ssimonout: 479215697Ssimon if (collf != NULL) 480215697Ssimon rewind(collf); 481215697Ssimon noreset--; 482215697Ssimon (void)sigprocmask(SIG_BLOCK, &nset, NULL); 483160814Ssimon (void)signal(SIGINT, saveint); 484215697Ssimon (void)signal(SIGHUP, savehup); 485215697Ssimon (void)signal(SIGTSTP, savetstp); 486160814Ssimon (void)signal(SIGTTOU, savettou); 487215697Ssimon (void)signal(SIGTTIN, savettin); 488215697Ssimon (void)sigprocmask(SIG_UNBLOCK, &nset, NULL); 489215697Ssimon return (collf); 490215697Ssimon} 491215697Ssimon 492215697Ssimon/* 493215697Ssimon * Write a file, ex-like if f set. 494215697Ssimon */ 495215697Ssimonint 496215697Ssimonexwrite(char name[], FILE *fp, int f) 497172767Ssimon{ 498215697Ssimon FILE *of; 499215697Ssimon int c, lc; 500215697Ssimon long cc; 501215697Ssimon struct stat junk; 502215697Ssimon 503172767Ssimon if (f) { 504215697Ssimon printf("\"%s\" ", name); 505172767Ssimon (void)fflush(stdout); 506237657Sjkim } 507237657Sjkim if (stat(name, &junk) >= 0 && S_ISREG(junk.st_mode)) { 508237657Sjkim if (!f) 509160814Ssimon fprintf(stderr, "%s: ", name); 510215697Ssimon fprintf(stderr, "File exists\n"); 511215697Ssimon return (-1); 512160814Ssimon } 513160814Ssimon if ((of = Fopen(name, "w")) == NULL) { 514160814Ssimon warn((char *)NULL); 515160814Ssimon return (-1); 516160814Ssimon } 517160814Ssimon lc = 0; 518160814Ssimon cc = 0; 519160814Ssimon while ((c = getc(fp)) != EOF) { 520172767Ssimon cc++; 521172767Ssimon if (c == '\n') 522172767Ssimon lc++; 523160814Ssimon (void)putc(c, of); 524172767Ssimon if (ferror(of)) { 525172767Ssimon warnx("%s", name); 526172767Ssimon (void)Fclose(of); 527160814Ssimon return (-1); 528172767Ssimon } 529172767Ssimon } 530172767Ssimon (void)Fclose(of); 531172767Ssimon printf("%d/%ld\n", lc, cc); 532172767Ssimon (void)fflush(stdout); 533172767Ssimon return (0); 534160814Ssimon} 535172767Ssimon 536172767Ssimon/* 537172767Ssimon * Edit the message being collected on fp. 538172767Ssimon * On return, make the edit file the new temp file. 539172767Ssimon */ 540160814Ssimonvoid 541172767Ssimonmesedit(FILE *fp, int c) 542172767Ssimon{ 543172767Ssimon sig_t sigint = signal(SIGINT, SIG_IGN); 544172767Ssimon FILE *nf = run_editor(fp, (off_t)-1, c, 0); 545238405Sjkim 546172767Ssimon if (nf != NULL) { 547172767Ssimon (void)fseeko(nf, (off_t)0, SEEK_END); 548172767Ssimon collf = nf; 549172767Ssimon (void)Fclose(fp); 550160814Ssimon } 551172767Ssimon (void)signal(SIGINT, sigint); 552172767Ssimon} 553172767Ssimon 554172767Ssimon/* 555172767Ssimon * Pipe the message through the command. 556172767Ssimon * Old message is on stdin of command; 557172767Ssimon * New message collected from stdout. 558172767Ssimon * Sh -c must return 0 to accept the new message. 559172767Ssimon */ 560172767Ssimonvoid 561172767Ssimonmespipe(FILE *fp, char cmd[]) 562172767Ssimon{ 563172767Ssimon FILE *nf; 564160814Ssimon int fd; 565172767Ssimon sig_t sigint = signal(SIGINT, SIG_IGN); 566172767Ssimon char *sh, tempname[PATHSIZE]; 567160814Ssimon 568172767Ssimon (void)snprintf(tempname, sizeof(tempname), 569160814Ssimon "%s/mail.ReXXXXXXXXXX", tmpdir); 570172767Ssimon if ((fd = mkstemp(tempname)) == -1 || 571172767Ssimon (nf = Fdopen(fd, "w+")) == NULL) { 572172767Ssimon warn("%s", tempname); 573172767Ssimon goto out; 574172767Ssimon } 575172767Ssimon (void)rm(tempname); 576172767Ssimon /* 577172767Ssimon * stdin = current message. 578172767Ssimon * stdout = new message. 579172767Ssimon */ 580160814Ssimon if ((sh = value("SHELL")) == NULL) 581172767Ssimon sh = _PATH_CSHELL; 582172767Ssimon if (run_command(sh, 583172767Ssimon 0, fileno(fp), fileno(nf), "-c", cmd, NULL) < 0) { 584172767Ssimon (void)Fclose(nf); 585160814Ssimon goto out; 586172767Ssimon } 587215697Ssimon if (fsize(nf) == 0) { 588215697Ssimon fprintf(stderr, "No bytes from \"%s\" !?\n", cmd); 589215697Ssimon (void)Fclose(nf); 590215697Ssimon goto out; 591160814Ssimon } 592172767Ssimon /* 593172767Ssimon * Take new files. 594196474Ssimon */ 595172767Ssimon (void)fseeko(nf, (off_t)0, SEEK_END); 596160814Ssimon collf = nf; 597172767Ssimon (void)Fclose(fp); 598160814Ssimonout: 599172767Ssimon (void)signal(SIGINT, sigint); 600172767Ssimon} 601194206Ssimon 602172767Ssimon/* 603172767Ssimon * Interpolate the named messages into the current 604172767Ssimon * message, preceding each line with a tab. 605160814Ssimon * Return a count of the number of characters now in 606172767Ssimon * the message, or -1 if an error is encountered writing 607172767Ssimon * the message temporary. The flag argument is 'm' if we 608160814Ssimon * should shift over and 'f' if not. 609172767Ssimon */ 610172767Ssimonint 611172767Ssimonforward(char ms[], FILE *fp, char *fn, int f) 612196474Ssimon{ 613172767Ssimon int *msgvec; 614160814Ssimon struct ignoretab *ig; 615172767Ssimon char *tabst; 616172767Ssimon 617172767Ssimon msgvec = (int *)salloc((msgCount+1) * sizeof(*msgvec)); 618172767Ssimon if (msgvec == NULL) 619172767Ssimon return (0); 620172767Ssimon if (getmsglist(ms, msgvec, 0) < 0) 621172767Ssimon return (0); 622172767Ssimon if (*msgvec == 0) { 623160814Ssimon *msgvec = first(0, MMNORM); 624271304Sdelphij if (*msgvec == 0) { 625271304Sdelphij printf("No appropriate messages\n"); 626271304Sdelphij return (0); 627271304Sdelphij } 628271304Sdelphij msgvec[1] = 0; 629271304Sdelphij } 630271304Sdelphij if (f == 'f' || f == 'F') 631271304Sdelphij tabst = NULL; 632271304Sdelphij else if ((tabst = value("indentprefix")) == NULL) 633271304Sdelphij tabst = "\t"; 634160814Ssimon ig = isupper((unsigned char)f) ? NULL : ignore; 635172767Ssimon printf("Interpolating:"); 636271304Sdelphij for (; *msgvec != 0; msgvec++) { 637215697Ssimon struct message *mp = message + *msgvec - 1; 638215697Ssimon 639215697Ssimon touch(mp); 640215697Ssimon printf(" %d", *msgvec); 641238405Sjkim if (sendmessage(mp, fp, ig, tabst) < 0) { 642271304Sdelphij warnx("%s", fn); 643215697Ssimon return (-1); 644271304Sdelphij } 645271304Sdelphij } 646215697Ssimon printf("\n"); 647215697Ssimon return (0); 648271304Sdelphij} 649271304Sdelphij 650215697Ssimon/* 651215697Ssimon * Print (continue) when continued after ^Z. 652238405Sjkim */ 653238405Sjkim/*ARGSUSED*/ 654238405Sjkimvoid 655238405Sjkimcollstop(int s) 656215697Ssimon{ 657215697Ssimon sig_t old_action = signal(s, SIG_DFL); 658215697Ssimon sigset_t nset; 659215697Ssimon 660215697Ssimon (void)sigemptyset(&nset); 661215697Ssimon (void)sigaddset(&nset, s); 662215697Ssimon (void)sigprocmask(SIG_UNBLOCK, &nset, NULL); 663215697Ssimon (void)kill(0, s); 664215697Ssimon (void)sigprocmask(SIG_BLOCK, &nset, NULL); 665215697Ssimon (void)signal(s, old_action); 666215697Ssimon if (colljmp_p) { 667267104Sdelphij colljmp_p = 0; 668215697Ssimon hadintr = 0; 669267104Sdelphij longjmp(colljmp, 1); 670267104Sdelphij } 671267104Sdelphij} 672267104Sdelphij 673267104Sdelphij/* 674267104Sdelphij * On interrupt, come here to save the partial message in ~/dead.letter. 675267104Sdelphij * Then jump out of the collection loop. 676215697Ssimon */ 677267104Sdelphij/*ARGSUSED*/ 678215697Ssimonvoid 679271304Sdelphijcollint(int s __unused) 680271304Sdelphij{ 681215697Ssimon /* 682215697Ssimon * the control flow is subtle, because we can be called from ~q. 683215697Ssimon */ 684215697Ssimon if (!hadintr) { 685215697Ssimon if (value("ignore") != NULL) { 686215697Ssimon printf("@"); 687215697Ssimon (void)fflush(stdout); 688215697Ssimon clearerr(stdin); 689215697Ssimon return; 690215697Ssimon } 691215697Ssimon hadintr = 1; 692215697Ssimon longjmp(colljmp, 1); 693215697Ssimon } 694215697Ssimon rewind(collf); 695215697Ssimon if (value("nosave") == NULL) 696215697Ssimon savedeadletter(collf); 697215697Ssimon longjmp(collabort, 1); 698215697Ssimon} 699215697Ssimon 700271304Sdelphij/*ARGSUSED*/ 701271304Sdelphijvoid 702271304Sdelphijcollhup(int s __unused) 703215697Ssimon{ 704215697Ssimon rewind(collf); 705215697Ssimon savedeadletter(collf); 706215697Ssimon /* 707215697Ssimon * Let's pretend nobody else wants to clean up, 708215697Ssimon * a true statement at this time. 709215697Ssimon */ 710215697Ssimon exit(1); 711215697Ssimon} 712215697Ssimon 713215697Ssimonvoid 714215697Ssimonsavedeadletter(FILE *fp) 715215697Ssimon{ 716215697Ssimon FILE *dbuf; 717215697Ssimon int c; 718215697Ssimon char *cp; 719238405Sjkim 720215697Ssimon if (fsize(fp) == 0) 721215697Ssimon return; 722279264Sdelphij cp = getdeadletter(); 723215697Ssimon c = umask(077); 724215697Ssimon dbuf = Fopen(cp, "a"); 725215697Ssimon (void)umask(c); 726271304Sdelphij if (dbuf == NULL) 727271304Sdelphij return; 728271304Sdelphij while ((c = getc(fp)) != EOF) 729271304Sdelphij (void)putc(c, dbuf); 730271304Sdelphij (void)Fclose(dbuf); 731271304Sdelphij rewind(fp); 732215697Ssimon} 733215697Ssimon