1/* 2 * Copyright (c) 1980, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34#ifndef lint 35#if 0 36static char sccsid[] = "@(#)collect.c 8.2 (Berkeley) 4/19/94"; 37#endif 38static const char rcsid[] = 39 "$FreeBSD: src/usr.bin/mail/collect.c,v 1.12 2002/06/30 05:25:06 obrien Exp $"; 40#endif /* not lint */ 41 42#include <sys/cdefs.h> 43 44/* 45 * Mail -- a mail program 46 * 47 * Collect input from standard input, handling 48 * ~ escapes. 49 */ 50 51#include "rcv.h" 52#include <fcntl.h> 53#include "extern.h" 54 55/* 56 * Read a message from standard output and return a read file to it 57 * or NULL on error. 58 */ 59 60/* 61 * The following hokiness with global variables is so that on 62 * receipt of an interrupt signal, the partial message can be salted 63 * away on dead.letter. 64 */ 65 66static sig_t saveint; /* Previous SIGINT value */ 67static sig_t savehup; /* Previous SIGHUP value */ 68static sig_t savetstp; /* Previous SIGTSTP value */ 69static sig_t savettou; /* Previous SIGTTOU value */ 70static sig_t savettin; /* Previous SIGTTIN value */ 71static FILE *collf; /* File for saving away */ 72static int hadintr; /* Have seen one SIGINT so far */ 73 74static jmp_buf colljmp; /* To get back to work */ 75static int colljmp_p; /* whether to long jump */ 76static jmp_buf collabort; /* To end collection with error */ 77 78static jmp_buf pipejmp; /* To catch the loss of pipe connection */ 79 80void 81brokthepipe(signo) 82 int signo; 83{ 84 longjmp(pipejmp, 1); 85} 86 87FILE * 88collect(hp, printheaders) 89 struct header *hp; 90 int printheaders; 91{ 92 FILE *fbuf; 93 int lc, cc, escape, eofcount, fd, c, t; 94 char linebuf[LINESIZE], tempname[PATHSIZE], *cp, getsub; 95 sigset_t nset; 96 int longline, lastlong, rc; /* So we don't make 2 or more lines 97 out of a long input line. */ 98 int nlines, usepager; 99 char *envptr; 100 101 collf = NULL; 102 /* 103 * Start catching signals from here, but we're still die on interrupts 104 * until we're in the main loop. 105 */ 106 (void)sigemptyset(&nset); 107 (void)sigaddset(&nset, SIGINT); 108 (void)sigaddset(&nset, SIGHUP); 109 (void)sigprocmask(SIG_BLOCK, &nset, NULL); 110 if ((saveint = signal(SIGINT, SIG_IGN)) != SIG_IGN) 111 (void)signal(SIGINT, collint); 112 if ((savehup = signal(SIGHUP, SIG_IGN)) != SIG_IGN) 113 (void)signal(SIGHUP, collhup); 114 savetstp = signal(SIGTSTP, collstop); 115 savettou = signal(SIGTTOU, collstop); 116 savettin = signal(SIGTTIN, collstop); 117 if (setjmp(collabort) || setjmp(colljmp)) { 118 (void)rm(tempname); 119 goto err; 120 } 121 (void)sigprocmask(SIG_UNBLOCK, &nset, NULL); 122 123 noreset++; 124 (void)snprintf(tempname, sizeof(tempname), 125 "%s/mail.RsXXXXXXXXXX", tmpdir); 126 if ((fd = mkstemp(tempname)) == -1 || 127 (collf = Fdopen(fd, "w+")) == NULL) { 128 warn("%s", tempname); 129 goto err; 130 } 131 (void)rm(tempname); 132 133 /* 134 * If we are going to prompt for a subject, 135 * refrain from printing a newline after 136 * the headers (since some people mind). 137 */ 138 t = GTO|GSUBJECT|GCC|GNL; 139 getsub = 0; 140 if (hp->h_subject == NULL && value("interactive") != NULL && 141 (value("ask") != NULL || value("asksub") != NULL)) 142 t &= ~GNL, getsub++; 143 if (printheaders) { 144 puthead(hp, stdout, t); 145 (void)fflush(stdout); 146 } 147 if ((cp = value("escape")) != NULL) 148 escape = *cp; 149 else 150 escape = ESCAPE; 151 eofcount = 0; 152 hadintr = 0; 153 lastlong = 0; 154 longline = 0; 155 156 if (!setjmp(colljmp)) { 157 if (getsub) { 158 if (grabh(hp, GSUBJECT)) { 159 /* grabh was interrupted: must count as first one */ 160 /* makes Unix 2003 conformance tests mailx_01.ex{49,57} pass */ 161 /* printf("Interrupt from Subject:\n"); */ 162 hadintr++; 163 goto cont; 164 } 165 } 166 } else { 167 /* 168 * Come here for printing the after-signal message. 169 * Duplicate messages won't be printed because 170 * the write is aborted if we get a SIGTTOU. 171 */ 172cont: 173 if (hadintr) { 174 (void)fflush(stdout); 175 fprintf(stderr, 176 "\n(Interrupt -- one more to kill letter)\n"); 177 } else { 178 printf("(continue)\n"); 179 (void)fflush(stdout); 180 } 181 } 182 for (;;) { 183 colljmp_p = 1; 184 c = readline(stdin, linebuf, LINESIZE); 185 colljmp_p = 0; 186 if (c < 0) { 187 if (value("interactive") != NULL && 188 value("ignoreeof") != NULL && ++eofcount < 25) { 189 printf("Use \".\" to terminate letter\n"); 190 continue; 191 } 192 break; 193 } 194 lastlong = longline; 195 longline = c == LINESIZE - 1; 196 eofcount = 0; 197 hadintr = 0; 198 if (linebuf[0] == '.' && linebuf[1] == '\0' && 199 value("interactive") != NULL && !lastlong && 200 (value("dot") != NULL || value("ignoreeof") != NULL)) 201 break; 202 if (linebuf[0] != escape || value("interactive") == NULL || 203 lastlong) { 204 if (putline(collf, linebuf, !longline) < 0) 205 goto err; 206 continue; 207 } 208 c = linebuf[1]; 209 switch (c) { 210 default: 211 /* 212 * On double escape, just send the single one. 213 * Otherwise, it's an error. 214 */ 215 if (c == escape) { 216 if (putline(collf, &linebuf[1], !longline) < 0) 217 goto err; 218 else 219 break; 220 } 221 printf("Unknown tilde escape.\n"); 222 break; 223 case 'C': 224 /* 225 * Dump core. 226 */ 227 core(); 228 break; 229 case '!': 230 /* 231 * Shell escape, send the balance of the 232 * line to sh -c. 233 */ 234 shell(&linebuf[2]); 235 break; 236 case ':': 237 case '_': 238 /* 239 * Escape to command mode, but be nice! 240 */ 241 execute(&linebuf[2], 1); 242 goto cont; 243 case '.': 244 /* 245 * Simulate end of file on input. 246 */ 247 goto out; 248 case 'q': 249 /* 250 * Force a quit of sending mail. 251 * Act like an interrupt happened. 252 */ 253 hadintr++; 254 collint(SIGINT); 255 exit(1); 256 case 'x': 257 /* 258 * Exit, do not save in dead.letter. 259 */ 260 goto err; 261 case 'h': 262 /* 263 * Grab a bunch of headers. 264 */ 265 grabh(hp, GTO|GSUBJECT|GCC|GBCC); 266 goto cont; 267 case 't': 268 /* 269 * Add to the To list. 270 */ 271 hp->h_to = cat(hp->h_to, extract(&linebuf[2], GTO)); 272 break; 273 case 's': 274 /* 275 * Set the Subject line. 276 */ 277 cp = &linebuf[2]; 278 while (isspace((unsigned char)*cp)) 279 cp++; 280 hp->h_subject = savestr(cp); 281 break; 282 case 'R': 283 /* 284 * Set the Reply-To line. 285 */ 286 cp = &linebuf[2]; 287 while (isspace((unsigned char)*cp)) 288 cp++; 289 hp->h_replyto = savestr(cp); 290 break; 291 case 'c': 292 /* 293 * Add to the CC list. 294 */ 295 hp->h_cc = cat(hp->h_cc, extract(&linebuf[2], GCC)); 296 break; 297 case 'b': 298 /* 299 * Add to the BCC list. 300 */ 301 hp->h_bcc = cat(hp->h_bcc, extract(&linebuf[2], GBCC)); 302 break; 303 case 'i': 304 case 'A': 305 case 'a': 306 /* 307 * Insert named variable in message. 308 */ 309 switch(c) { 310 case 'i': 311 cp = &linebuf[2]; 312 while(isspace((unsigned char)*cp)) 313 cp++; 314 break; 315 case 'a': 316 cp = "sign"; 317 break; 318 case 'A': 319 cp = "Sign"; 320 break; 321 default: 322 goto err; 323 } 324 325 if(*cp != '\0' && (cp = value(cp)) != NULL) { 326 if (*cp != '\0') { 327 printf("%s\n", cp); 328 if(putline(collf, cp, 1) < 0) 329 goto err; 330 } 331 } 332 333 break; 334 case 'd': 335 /* 336 * Read in the dead letter file. 337 */ 338 if (strlcpy(linebuf + 2, getdeadletter(), 339 sizeof(linebuf) - 2) 340 >= sizeof(linebuf) - 2) { 341 printf("Line buffer overflow\n"); 342 break; 343 } 344 /* FALLTHROUGH */ 345 case 'r': 346 case '<': 347 /* 348 * Invoke a file: 349 * Search for the file name, 350 * then open it and copy the contents to collf. 351 */ 352 cp = &linebuf[2]; 353 while (isspace((unsigned char)*cp)) 354 cp++; 355 if (*cp == '\0') { 356 printf("Interpolate what file?\n"); 357 break; 358 } 359 cp = expand(cp); 360 if (cp == NULL) 361 break; 362 if (*cp == '!') { 363 /* 364 * Insert stdout of command. 365 */ 366 char *sh; 367 int nullfd, tempfd, rc; 368 char tempname2[PATHSIZE]; 369 370 if ((nullfd = open("/dev/null", O_RDONLY, 0)) 371 == -1) { 372 warn("/dev/null"); 373 break; 374 } 375 376 (void)snprintf(tempname2, sizeof(tempname2), 377 "%s/mail.ReXXXXXXXXXX", tmpdir); 378 if ((tempfd = mkstemp(tempname2)) == -1 || 379 (fbuf = Fdopen(tempfd, "w+")) == NULL) { 380 warn("%s", tempname2); 381 break; 382 } 383 (void)unlink(tempname2); 384 385 if ((sh = value("SHELL")) == NULL) 386 sh = _PATH_BSHELL; 387 388 rc = run_command(sh, 0, nullfd, fileno(fbuf), 389 "-c", cp+1, NULL); 390 391 close(nullfd); 392 393 if (rc < 0) { 394 (void)Fclose(fbuf); 395 break; 396 } 397 398 if (fsize(fbuf) == 0) { 399 fprintf(stderr, 400 "No bytes from command \"%s\"\n", 401 cp+1); 402 (void)Fclose(fbuf); 403 break; 404 } 405 406 rewind(fbuf); 407 } else if (isdir(cp)) { 408 printf("%s: Directory\n", cp); 409 break; 410 } else if ((fbuf = Fopen(cp, "r")) == NULL) { 411 warn("%s", cp); 412 break; 413 } 414 printf("\"%s\" ", cp); 415 (void)fflush(stdout); 416 lc = 0; 417 cc = 0; 418 while ((rc = readline(fbuf, linebuf, LINESIZE)) >= 0) { 419 if (rc != LINESIZE - 1) 420 lc++; 421 if ((t = putline(collf, linebuf, 422 rc != LINESIZE - 1)) < 0) { 423 (void)Fclose(fbuf); 424 goto err; 425 } 426 cc += t; 427 } 428 (void)Fclose(fbuf); 429 printf("%d/%d\n", lc, cc); 430 break; 431 case 'w': 432 /* 433 * Write the message on a file. 434 */ 435 cp = &linebuf[2]; 436 while (*cp == ' ' || *cp == '\t') 437 cp++; 438 if (*cp == '\0') { 439 fprintf(stderr, "Write what file!?\n"); 440 break; 441 } 442 if ((cp = expand(cp)) == NULL) 443 break; 444 rewind(collf); 445 exwrite(cp, collf, 1); 446 break; 447 case 'm': 448 case 'M': 449 case 'f': 450 case 'F': 451 /* 452 * Interpolate the named messages, if we 453 * are in receiving mail mode. Does the 454 * standard list processing garbage. 455 * If ~f is given, we don't shift over. 456 */ 457 if (forward(linebuf + 2, collf, tempname, c) < 0) 458 goto err; 459 goto cont; 460 case '?': 461 if ((fbuf = Fopen(_PATH_TILDE, "r")) == NULL) { 462 warn("%s", _PATH_TILDE); 463 break; 464 } 465 while ((t = getc(fbuf)) != EOF) 466 (void)putchar(t); 467 (void)Fclose(fbuf); 468 break; 469 case 'p': 470 /* 471 * Print out the current state of the 472 * message without altering anything. 473 */ 474 rewind(collf); 475 printf("-------\nMessage contains:\n"); 476 puthead(hp, stdout, GTO|GSUBJECT|GCC|GBCC|GNL); 477 if ((envptr = value("crt")) != NULL) { 478 nlines = atoi(envptr); 479 } else { 480 nlines = 0; 481 } 482 fbuf = stdout; 483 usepager = 0; 484 if (nlines>0) { 485 /* See if crt < num lines in file */ 486 int countlines = 0; 487 while ((t = getc(collf)) != EOF) { 488 if (t=='\n') { 489 countlines++; 490 if (nlines < countlines) { 491 break; 492 } 493 } 494 } 495 rewind(collf); 496 if (nlines < countlines) { 497 /* Must use a paginator: default is "more" */ 498 usepager = 1; 499 envptr = value("PAGER"); 500 if (envptr == NULL || *envptr == '\0') 501 envptr = _PATH_MORE; 502 if (setjmp(pipejmp)) 503 goto close_pipe; 504 fbuf = Popen(envptr, "w"); 505 if (fbuf == NULL) { 506 warnx("%s", envptr); 507 fbuf = stdout; 508 } else 509 (void)signal(SIGPIPE, brokthepipe); 510 } 511 } 512 while ((t = getc(collf)) != EOF) 513 (void)putchar(t); 514 if (usepager) { 515 close_pipe: 516 if (fbuf != stdout) { 517 /* 518 * Ignore SIGPIPE so it can't cause a duplicate close. 519 */ 520 (void)signal(SIGPIPE, SIG_IGN); 521 (void)Pclose(fbuf); 522 (void)signal(SIGPIPE, SIG_DFL); 523 } 524 } 525 goto cont; 526 case '|': 527 /* 528 * Pipe message through command. 529 * Collect output as new message. 530 */ 531 rewind(collf); 532 mespipe(collf, &linebuf[2]); 533 goto cont; 534 case 'v': 535 case 'e': 536 /* 537 * Edit the current message. 538 * 'e' means to use EDITOR 539 * 'v' means to use VISUAL 540 */ 541 rewind(collf); 542 mesedit(collf, c); 543 goto cont; 544 } 545 } 546 goto out; 547err: 548 senderr++; /* set return code */ 549 if (collf != NULL) { 550 (void)Fclose(collf); 551 collf = NULL; 552 } 553out: 554 if (collf != NULL) 555 rewind(collf); 556 noreset--; 557 (void)sigprocmask(SIG_BLOCK, &nset, NULL); 558 (void)signal(SIGINT, saveint); 559 (void)signal(SIGHUP, savehup); 560 (void)signal(SIGTSTP, savetstp); 561 (void)signal(SIGTTOU, savettou); 562 (void)signal(SIGTTIN, savettin); 563 (void)sigprocmask(SIG_UNBLOCK, &nset, NULL); 564 return (collf); 565} 566 567/* 568 * Write a file, ex-like if f set. 569 */ 570int 571exwrite(name, fp, f) 572 char name[]; 573 FILE *fp; 574 int f; 575{ 576 FILE *of; 577 int c, lc; 578 long cc; 579#if 0 580 struct stat junk; 581#endif 582 583 if (f) { 584 printf("\"%s\" ", name); 585 (void)fflush(stdout); 586 } 587#if 0 588 if (stat(name, &junk) >= 0 && S_ISREG(junk.st_mode)) { 589 if (!f) 590 fprintf(stderr, "%s: ", name); 591 fprintf(stderr, "File exists\n"); 592 return (-1); 593 } 594#endif 595 if ((of = Fopen(name, "a")) == NULL) { 596 warn((char *)NULL); 597 return (-1); 598 } 599 lc = 0; 600 cc = 0; 601 while ((c = getc(fp)) != EOF) { 602 cc++; 603 if (c == '\n') 604 lc++; 605 (void)putc(c, of); 606 if (ferror(of)) { 607 warnx("%s", name); 608 (void)Fclose(of); 609 return (-1); 610 } 611 } 612 (void)Fclose(of); 613 printf("%d/%ld\n", lc, cc); 614 (void)fflush(stdout); 615 return (0); 616} 617 618/* 619 * Edit the message being collected on fp. 620 * On return, make the edit file the new temp file. 621 */ 622void 623mesedit(fp, c) 624 FILE *fp; 625 int c; 626{ 627 sig_t sigint = signal(SIGINT, SIG_IGN); 628 FILE *nf = run_editor(fp, (off_t)-1, c, 0); 629 630 if (nf != NULL) { 631 (void)fseeko(nf, (off_t)0, SEEK_END); 632 collf = nf; 633 (void)Fclose(fp); 634 } 635 (void)signal(SIGINT, sigint); 636} 637 638static char * 639parse_pipe_args(str, msglist) 640 char str[]; 641 char **msglist; 642{ 643 char *cp; 644 char quoted; 645 646 *msglist = NULL; 647 if (str==NULL) return NULL; 648 if (*str=='\0') return NULL; 649 650 cp = strlen(str) + str - 1; 651 652 /* 653 * Strip away trailing blanks. 654 */ 655 656 while (cp > str && isspace((unsigned char)*cp)) 657 cp--; 658 *++cp = '\0'; 659 660 /* 661 * Now search for the beginning of the command. 662 */ 663 quoted = 0; 664 if (cp > str) { /* check for quotes */ 665 cp--; 666 if (*cp=='"' || *cp=='\'' ) { 667 quoted=*cp; 668 cp--; 669 } 670 } 671 672/* 673printf("before loop: str=%s,cp=%s\n", str,cp); 674*/ 675 676 while (cp > str && (!isspace((unsigned char)*cp) || quoted)) { 677 if (quoted) { 678 if (*cp==quoted) { 679 cp--; 680 if (cp>str) { 681 if (!(*cp=='\\' || *cp==quoted)) { 682 quoted=0; 683 continue; 684 } 685 } else /* done */ 686 break; 687 } 688 } 689 cp--; 690 } 691 if (cp == str) { 692 return (cp); /* no msglist */ 693 } 694 695 *msglist = str; 696 if (isspace((unsigned char)*cp)) 697 *cp++ = '\0'; 698 else { 699 printf("malformed arguments:%s\n",str); 700 } 701 return (cp); 702} 703 704int 705mailpipe(str) 706 char str[]; 707{ 708 struct message *mp; 709 int *msgvec, *ip; 710 char *msglist = NULL; 711 char * cmd; 712 char * sh; 713 char cmdline[4096]; 714 int do_pagefeed; 715 FILE *fbuf; 716 717 fbuf = stdout; 718 719 /* parse arguments: [[msglist] command] */ 720 cmd = parse_pipe_args(str, &msglist); 721/* 722 printf (" pipe args: msglist=%s, cmd=%s\n", msglist, cmd); 723*/ 724 /* get message list for reading */ 725 msgvec = (int *)salloc((msgCount + 2) * sizeof(*msgvec)); 726 if (msglist==NULL) { 727 *msgvec = first(0, MMNORM); 728 if (*msgvec == 0) { 729 printf("No messages to %s.\n", cmd); 730 return (1); 731 } 732 msgvec[1] = 0; 733 } else { 734 if (getmsglist(msglist, msgvec, 0) < 0) 735 return (1); 736 } 737 /* if cmd empty get from cmd= variable */ 738 if (cmd==NULL) { 739 cmd = value("cmd"); 740 if (cmd==NULL || *cmd == '\0') { 741 printf("No command to pipe.\n"); 742 return(1); 743 } 744 } 745 746 if ((sh = value("SHELL")) == NULL) 747 sh = _PATH_BSHELL; 748 749 /* complete cmd, open a pipe to shell */ 750 cmdline[0] = '\0'; 751 strlcpy(cmdline, sh, sizeof(cmdline)); 752 strlcat(cmdline, " -c ", sizeof(cmdline)); 753 if (*cmd!='"' && *cmd!='\'') { 754 /* I know this doesn't handle all the cases, but 755 it is enough to make the conformance tests pass */ 756 strlcat(cmdline, "\"", sizeof(cmdline)); 757 strlcat(cmdline, cmd, sizeof(cmdline)); 758 strlcat(cmdline, "\"", sizeof(cmdline)); 759 } else { 760 strlcat(cmdline, cmd, sizeof(cmdline)); 761 } 762 763/* 764 printf(" popen cmdline:%s\n", cmdline); 765*/ 766 if (setjmp(pipejmp)) 767 goto close_pipe; 768 769 fbuf = Popen(cmdline, "w"); 770 if (fbuf == NULL) { 771 warnx("%s", cmdline); 772 return(1); 773 } else 774 (void)signal(SIGPIPE, brokthepipe); 775 776 /* paginate if page= set */ 777 if (value("page") == NULL) { 778 do_pagefeed = 0; 779 } else { 780 do_pagefeed = 1; 781 } 782 783 /* write all messages to the pipe */ 784 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { 785 mp = &message[*ip - 1]; 786 touch(mp); 787 dot = mp; 788/* printf (" sending message %d\n", ip-msgvec); */ 789 if (sendmessage(mp, fbuf, 0, NULL) < 0) { 790 warnx("%s", cmdline); 791 (void)Fclose(fbuf); 792 return (1); 793 } 794 if (do_pagefeed) 795 fprintf(fbuf,"\f"); /* form feed */ 796 } 797 (void)fflush(fbuf); 798close_pipe: 799 if (fbuf != stdout) { 800 /* 801 * Ignore SIGPIPE so it can't cause a duplicate close. 802 */ 803 (void)signal(SIGPIPE, SIG_IGN); 804 (void)Pclose(fbuf); 805 (void)signal(SIGPIPE, SIG_DFL); 806 } 807 return (0); 808} 809 810/* 811 * Pipe the message through the command. 812 * Old message is on stdin of command; 813 * New message collected from stdout. 814 * Sh -c must return 0 to accept the new message. 815 */ 816void 817mespipe(fp, cmd) 818 FILE *fp; 819 char cmd[]; 820{ 821 FILE *nf; 822 int fd; 823 sig_t sigint = signal(SIGINT, SIG_IGN); 824 char *sh, tempname[PATHSIZE]; 825 826 (void)snprintf(tempname, sizeof(tempname), 827 "%s/mail.ReXXXXXXXXXX", tmpdir); 828 if ((fd = mkstemp(tempname)) == -1 || 829 (nf = Fdopen(fd, "w+")) == NULL) { 830 warn("%s", tempname); 831 goto out; 832 } 833 (void)rm(tempname); 834 /* 835 * stdin = current message. 836 * stdout = new message. 837 */ 838 if ((sh = value("SHELL")) == NULL) 839 sh = _PATH_BSHELL; 840 if (run_command(sh, 841 0, fileno(fp), fileno(nf), "-c", cmd, NULL) < 0) { 842 (void)Fclose(nf); 843 goto out; 844 } 845 if (fsize(nf) == 0) { 846 fprintf(stderr, "No bytes from \"%s\" !?\n", cmd); 847 (void)Fclose(nf); 848 goto out; 849 } 850 /* 851 * Take new files. 852 */ 853 (void)fseeko(nf, (off_t)0, SEEK_END); 854 collf = nf; 855 (void)Fclose(fp); 856out: 857 (void)signal(SIGINT, sigint); 858} 859 860/* 861 * Interpolate the named messages into the current 862 * message, preceding each line with a tab. 863 * Return a count of the number of characters now in 864 * the message, or -1 if an error is encountered writing 865 * the message temporary. The flag argument is 'm' if we 866 * should shift over and 'f' if not. 867 */ 868int 869forward(ms, fp, fn, f) 870 char ms[]; 871 FILE *fp; 872 char *fn; 873 int f; 874{ 875 int *msgvec; 876 struct ignoretab *ig; 877 char *tabst; 878 879 msgvec = (int *)salloc((msgCount+1) * sizeof(*msgvec)); 880 if (msgvec == NULL) 881 return (0); 882 if (getmsglist(ms, msgvec, 0) < 0) 883 return (0); 884 if (*msgvec == 0) { 885 *msgvec = first(0, MMNORM); 886 if (*msgvec == 0) { 887 printf("No appropriate messages\n"); 888 return (0); 889 } 890 msgvec[1] = 0; 891 } 892 if (f == 'f' || f == 'F') 893 tabst = NULL; 894 else if ((tabst = value("indentprefix")) == NULL) 895 tabst = "\t"; 896 ig = isupper((unsigned char)f) ? NULL : ignore; 897 printf("Interpolating:"); 898 for (; *msgvec != 0; msgvec++) { 899 struct message *mp = message + *msgvec - 1; 900 901 touch(mp); 902 printf(" %d", *msgvec); 903 if (sendmessage(mp, fp, ig, tabst) < 0) { 904 warnx("%s", fn); 905 return (-1); 906 } 907 } 908 printf("\n"); 909 return (0); 910} 911 912/* 913 * Print (continue) when continued after ^Z. 914 */ 915/*ARGSUSED*/ 916void 917collstop(s) 918 int s; 919{ 920 sig_t old_action = signal(s, SIG_DFL); 921 sigset_t nset; 922 923 (void)sigemptyset(&nset); 924 (void)sigaddset(&nset, s); 925 (void)sigprocmask(SIG_UNBLOCK, &nset, NULL); 926 (void)kill(0, s); 927 (void)sigprocmask(SIG_BLOCK, &nset, NULL); 928 (void)signal(s, old_action); 929 if (colljmp_p) { 930 colljmp_p = 0; 931 hadintr = 0; 932 longjmp(colljmp, 1); 933 } 934} 935 936/* 937 * On interrupt, come here to save the partial message in ~/dead.letter. 938 * Then jump out of the collection loop. 939 */ 940/*ARGSUSED*/ 941void 942collint(s) 943 int s; 944{ 945 /* 946 * the control flow is subtle, because we can be called from ~q. 947 */ 948 if (!hadintr) { 949 if (value("ignore") != NULL) { 950 printf("@"); 951 (void)fflush(stdout); 952 clearerr(stdin); 953 return; 954 } 955 hadintr = 1; 956 longjmp(colljmp, 1); 957 } 958 rewind(collf); 959 if (value("save") != NULL) 960 savedeadletter(collf); 961 longjmp(collabort, 1); 962} 963 964/*ARGSUSED*/ 965void 966collhup(s) 967 int s; 968{ 969 rewind(collf); 970 savedeadletter(collf); 971 /* 972 * Let's pretend nobody else wants to clean up, 973 * a true statement at this time. 974 */ 975 exit(1); 976} 977 978void 979savedeadletter(fp) 980 FILE *fp; 981{ 982 FILE *dbuf; 983 int c; 984 char *cp; 985 986 if (fsize(fp) == 0) 987 return; 988 cp = getdeadletter(); 989 c = umask(077); 990 dbuf = Fopen(cp, "w"); 991 (void)umask(c); 992 if (dbuf == NULL) 993 return; 994 while ((c = getc(fp)) != EOF) 995 (void)putc(c, dbuf); 996 (void)Fclose(dbuf); 997 rewind(fp); 998} 999