1 #ifndef lint 2 static char sccsid[] = "@(#)collect.c 2.18 (Berkeley) 01/09/85"; 3 #endif 4 5 /* 6 * Mail -- a mail program 7 * 8 * Collect input from standard input, handling 9 * ~ escapes. 10 */ 11 12 #include "rcv.h" 13 #include <sys/stat.h> 14 15 /* 16 * Read a message from standard output and return a read file to it 17 * or NULL on error. 18 */ 19 20 /* 21 * The following hokiness with global variables is so that on 22 * receipt of an interrupt signal, the partial message can be salted 23 * away on dead.letter. The output file must be available to flush, 24 * and the input to read. Several open files could be saved all through 25 * Mail if stdio allowed simultaneous read/write access. 26 */ 27 28 static int (*savesig)(); /* Previous SIGINT value */ 29 static int (*savehup)(); /* Previous SIGHUP value */ 30 # ifdef VMUNIX 31 static int (*savecont)(); /* Previous SIGCONT value */ 32 # endif VMUNIX 33 static FILE *newi; /* File for saving away */ 34 static FILE *newo; /* Output side of same */ 35 static int hf; /* Ignore interrups */ 36 static int hadintr; /* Have seen one SIGINT so far */ 37 38 static jmp_buf coljmp; /* To get back to work */ 39 40 FILE * 41 collect(hp) 42 struct header *hp; 43 { 44 FILE *ibuf, *fbuf, *obuf; 45 int lc, cc, escape, collrub(), intack(), collhup, collcont(), eof; 46 register int c, t; 47 char linebuf[LINESIZE], *cp; 48 extern char tempMail[]; 49 int notify(); 50 extern collintsig(), collhupsig(); 51 char getsub; 52 53 noreset++; 54 ibuf = obuf = NULL; 55 if (value("ignore") != NOSTR) 56 hf = 1; 57 else 58 hf = 0; 59 hadintr = 0; 60 # ifdef VMUNIX 61 if ((savesig = sigset(SIGINT, SIG_IGN)) != SIG_IGN) 62 sigset(SIGINT, hf ? intack : collrub), sigblock(sigmask(SIGINT)); 63 if ((savehup = sigset(SIGHUP, SIG_IGN)) != SIG_IGN) 64 sigset(SIGHUP, collrub), sigblock(sigmask(SIGHUP)); 65 savecont = sigset(SIGCONT, collcont); 66 # else VMUNIX 67 savesig = signal(SIGINT, SIG_IGN); 68 savehup = signal(SIGHUP, SIG_IGN); 69 # endif VMUNIX 70 newi = NULL; 71 newo = NULL; 72 if ((obuf = fopen(tempMail, "w")) == NULL) { 73 perror(tempMail); 74 goto err; 75 } 76 newo = obuf; 77 if ((ibuf = fopen(tempMail, "r")) == NULL) { 78 perror(tempMail); 79 newo = NULL; 80 fclose(obuf); 81 goto err; 82 } 83 newi = ibuf; 84 remove(tempMail); 85 86 /* 87 * If we are going to prompt for a subject, 88 * refrain from printing a newline after 89 * the headers (since some people mind). 90 */ 91 92 t = GTO|GSUBJECT|GCC|GNL; 93 getsub = 0; 94 if (intty && sflag == NOSTR && hp->h_subject == NOSTR && value("ask")) 95 t &= ~GNL, getsub++; 96 if (hp->h_seq != 0) { 97 puthead(hp, stdout, t); 98 fflush(stdout); 99 } 100 escape = ESCAPE; 101 if ((cp = value("escape")) != NOSTR) 102 escape = *cp; 103 eof = 0; 104 for (;;) { 105 int omask = sigblock(0) &~ (sigmask(SIGINT)|sigmask(SIGHUP)); 106 107 setjmp(coljmp); 108 # ifdef VMUNIX 109 sigsetmask(omask); 110 # else VMUNIX 111 if (savesig != SIG_IGN) 112 signal(SIGINT, hf ? intack : collintsig); 113 if (savehup != SIG_IGN) 114 signal(SIGHUP, collhupsig); 115 # endif VMUNIX 116 fflush(stdout); 117 if (getsub) { 118 grabh(hp, GSUBJECT); 119 getsub = 0; 120 continue; 121 } 122 if (readline(stdin, linebuf) <= 0) { 123 if (intty && value("ignoreeof") != NOSTR) { 124 if (++eof > 35) 125 break; 126 printf("Use \".\" to terminate letter\n", 127 escape); 128 continue; 129 } 130 break; 131 } 132 eof = 0; 133 hadintr = 0; 134 if (intty && equal(".", linebuf) && 135 (value("dot") != NOSTR || value("ignoreeof") != NOSTR)) 136 break; 137 if (linebuf[0] != escape || rflag != NOSTR) { 138 if ((t = putline(obuf, linebuf)) < 0) 139 goto err; 140 continue; 141 } 142 c = linebuf[1]; 143 switch (c) { 144 default: 145 /* 146 * On double escape, just send the single one. 147 * Otherwise, it's an error. 148 */ 149 150 if (c == escape) { 151 if (putline(obuf, &linebuf[1]) < 0) 152 goto err; 153 else 154 break; 155 } 156 printf("Unknown tilde escape.\n"); 157 break; 158 159 case 'C': 160 /* 161 * Dump core. 162 */ 163 164 core(); 165 break; 166 167 case '!': 168 /* 169 * Shell escape, send the balance of the 170 * line to sh -c. 171 */ 172 173 shell(&linebuf[2]); 174 break; 175 176 case ':': 177 case '_': 178 /* 179 * Escape to command mode, but be nice! 180 */ 181 182 execute(&linebuf[2], 1); 183 printf("(continue)\n"); 184 break; 185 186 case '.': 187 /* 188 * Simulate end of file on input. 189 */ 190 goto eofl; 191 192 case 'q': 193 case 'Q': 194 /* 195 * Force a quit of sending mail. 196 * Act like an interrupt happened. 197 */ 198 199 hadintr++; 200 collrub(SIGINT); 201 exit(1); 202 203 case 'h': 204 /* 205 * Grab a bunch of headers. 206 */ 207 if (!intty || !outtty) { 208 printf("~h: no can do!?\n"); 209 break; 210 } 211 grabh(hp, GTO|GSUBJECT|GCC|GBCC); 212 printf("(continue)\n"); 213 break; 214 215 case 't': 216 /* 217 * Add to the To list. 218 */ 219 220 hp->h_to = addto(hp->h_to, &linebuf[2]); 221 hp->h_seq++; 222 break; 223 224 case 's': 225 /* 226 * Set the Subject list. 227 */ 228 229 cp = &linebuf[2]; 230 while (any(*cp, " \t")) 231 cp++; 232 hp->h_subject = savestr(cp); 233 hp->h_seq++; 234 break; 235 236 case 'c': 237 /* 238 * Add to the CC list. 239 */ 240 241 hp->h_cc = addto(hp->h_cc, &linebuf[2]); 242 hp->h_seq++; 243 break; 244 245 case 'b': 246 /* 247 * Add stuff to blind carbon copies list. 248 */ 249 hp->h_bcc = addto(hp->h_bcc, &linebuf[2]); 250 hp->h_seq++; 251 break; 252 253 case 'd': 254 copy(deadletter, &linebuf[2]); 255 /* fall into . . . */ 256 257 case 'r': 258 /* 259 * Invoke a file: 260 * Search for the file name, 261 * then open it and copy the contents to obuf. 262 */ 263 264 cp = &linebuf[2]; 265 while (any(*cp, " \t")) 266 cp++; 267 if (*cp == '\0') { 268 printf("Interpolate what file?\n"); 269 break; 270 } 271 cp = expand(cp); 272 if (cp == NOSTR) 273 break; 274 if (isdir(cp)) { 275 printf("%s: directory\n"); 276 break; 277 } 278 if ((fbuf = fopen(cp, "r")) == NULL) { 279 perror(cp); 280 break; 281 } 282 printf("\"%s\" ", cp); 283 fflush(stdout); 284 lc = 0; 285 cc = 0; 286 while (readline(fbuf, linebuf) > 0) { 287 lc++; 288 if ((t = putline(obuf, linebuf)) < 0) { 289 fclose(fbuf); 290 goto err; 291 } 292 cc += t; 293 } 294 fclose(fbuf); 295 printf("%d/%d\n", lc, cc); 296 break; 297 298 case 'w': 299 /* 300 * Write the message on a file. 301 */ 302 303 cp = &linebuf[2]; 304 while (any(*cp, " \t")) 305 cp++; 306 if (*cp == '\0') { 307 fprintf(stderr, "Write what file!?\n"); 308 break; 309 } 310 if ((cp = expand(cp)) == NOSTR) 311 break; 312 fflush(obuf); 313 rewind(ibuf); 314 exwrite(cp, ibuf, 1); 315 break; 316 317 case 'm': 318 case 'f': 319 /* 320 * Interpolate the named messages, if we 321 * are in receiving mail mode. Does the 322 * standard list processing garbage. 323 * If ~f is given, we don't shift over. 324 */ 325 326 if (!rcvmode) { 327 printf("No messages to send from!?!\n"); 328 break; 329 } 330 cp = &linebuf[2]; 331 while (any(*cp, " \t")) 332 cp++; 333 if (forward(cp, obuf, c) < 0) 334 goto err; 335 printf("(continue)\n"); 336 break; 337 338 case '?': 339 if ((fbuf = fopen(THELPFILE, "r")) == NULL) { 340 perror(THELPFILE); 341 break; 342 } 343 t = getc(fbuf); 344 while (t != -1) { 345 putchar(t); 346 t = getc(fbuf); 347 } 348 fclose(fbuf); 349 break; 350 351 case 'p': 352 /* 353 * Print out the current state of the 354 * message without altering anything. 355 */ 356 357 fflush(obuf); 358 rewind(ibuf); 359 printf("-------\nMessage contains:\n"); 360 puthead(hp, stdout, GTO|GSUBJECT|GCC|GBCC|GNL); 361 t = getc(ibuf); 362 while (t != EOF) { 363 putchar(t); 364 t = getc(ibuf); 365 } 366 printf("(continue)\n"); 367 break; 368 369 case '^': 370 case '|': 371 /* 372 * Pipe message through command. 373 * Collect output as new message. 374 */ 375 376 obuf = mespipe(ibuf, obuf, &linebuf[2]); 377 newo = obuf; 378 ibuf = newi; 379 newi = ibuf; 380 printf("(continue)\n"); 381 break; 382 383 case 'v': 384 case 'e': 385 /* 386 * Edit the current message. 387 * 'e' means to use EDITOR 388 * 'v' means to use VISUAL 389 */ 390 391 if ((obuf = mesedit(ibuf, obuf, c)) == NULL) 392 goto err; 393 newo = obuf; 394 ibuf = newi; 395 printf("(continue)\n"); 396 break; 397 } 398 } 399 eofl: 400 fclose(obuf); 401 rewind(ibuf); 402 sigset(SIGINT, savesig); 403 sigset(SIGHUP, savehup); 404 # ifdef VMUNIX 405 sigset(SIGCONT, savecont); 406 sigsetmask(0); 407 # endif VMUNIX 408 noreset = 0; 409 return(ibuf); 410 411 err: 412 if (ibuf != NULL) 413 fclose(ibuf); 414 if (obuf != NULL) 415 fclose(obuf); 416 sigset(SIGINT, savesig); 417 sigset(SIGHUP, savehup); 418 # ifdef VMUNIX 419 sigset(SIGCONT, savecont); 420 sigsetmask(0); 421 # endif VMUNIX 422 noreset = 0; 423 return(NULL); 424 } 425 426 /* 427 * Non destructively interrogate the value of the given signal. 428 */ 429 430 psig(n) 431 { 432 register (*wassig)(); 433 434 wassig = sigset(n, SIG_IGN); 435 sigset(n, wassig); 436 return((int) wassig); 437 } 438 439 /* 440 * Write a file, ex-like if f set. 441 */ 442 443 exwrite(name, ibuf, f) 444 char name[]; 445 FILE *ibuf; 446 { 447 register FILE *of; 448 register int c; 449 long cc; 450 int lc; 451 struct stat junk; 452 453 if (f) { 454 printf("\"%s\" ", name); 455 fflush(stdout); 456 } 457 if (stat(name, &junk) >= 0 && (junk.st_mode & S_IFMT) == S_IFREG) { 458 if (!f) 459 fprintf(stderr, "%s: ", name); 460 fprintf(stderr, "File exists\n", name); 461 return(-1); 462 } 463 if ((of = fopen(name, "w")) == NULL) { 464 perror(NOSTR); 465 return(-1); 466 } 467 lc = 0; 468 cc = 0; 469 while ((c = getc(ibuf)) != EOF) { 470 cc++; 471 if (c == '\n') 472 lc++; 473 putc(c, of); 474 if (ferror(of)) { 475 perror(name); 476 fclose(of); 477 return(-1); 478 } 479 } 480 fclose(of); 481 printf("%d/%ld\n", lc, cc); 482 fflush(stdout); 483 return(0); 484 } 485 486 /* 487 * Edit the message being collected on ibuf and obuf. 488 * Write the message out onto some poorly-named temp file 489 * and point an editor at it. 490 * 491 * On return, make the edit file the new temp file. 492 */ 493 494 FILE * 495 mesedit(ibuf, obuf, c) 496 FILE *ibuf, *obuf; 497 { 498 int pid, s; 499 FILE *fbuf; 500 register int t; 501 int (*sig)(), (*scont)(), signull(); 502 struct stat sbuf; 503 extern char tempMail[], tempEdit[]; 504 register char *edit; 505 506 sig = sigset(SIGINT, SIG_IGN); 507 # ifdef VMUNIX 508 scont = sigset(SIGCONT, signull); 509 # endif VMUNIX 510 if (stat(tempEdit, &sbuf) >= 0) { 511 printf("%s: file exists\n", tempEdit); 512 goto out; 513 } 514 close(creat(tempEdit, 0600)); 515 if ((fbuf = fopen(tempEdit, "w")) == NULL) { 516 perror(tempEdit); 517 goto out; 518 } 519 fflush(obuf); 520 rewind(ibuf); 521 t = getc(ibuf); 522 while (t != EOF) { 523 putc(t, fbuf); 524 t = getc(ibuf); 525 } 526 fflush(fbuf); 527 if (ferror(fbuf)) { 528 perror(tempEdit); 529 remove(tempEdit); 530 goto fix; 531 } 532 fclose(fbuf); 533 if ((edit = value(c == 'e' ? "EDITOR" : "VISUAL")) == NOSTR) 534 edit = c == 'e' ? EDITOR : VISUAL; 535 pid = vfork(); 536 if (pid == 0) { 537 sigchild(); 538 if (sig != SIG_IGN) 539 sigsys(SIGINT, SIG_DFL); 540 execl(edit, edit, tempEdit, 0); 541 perror(edit); 542 _exit(1); 543 } 544 if (pid == -1) { 545 perror("fork"); 546 remove(tempEdit); 547 goto out; 548 } 549 while (wait(&s) != pid) 550 ; 551 if ((s & 0377) != 0) { 552 printf("Fatal error in \"%s\"\n", edit); 553 remove(tempEdit); 554 goto out; 555 } 556 557 /* 558 * Now switch to new file. 559 */ 560 561 if ((fbuf = fopen(tempEdit, "a")) == NULL) { 562 perror(tempEdit); 563 remove(tempEdit); 564 goto out; 565 } 566 if ((ibuf = fopen(tempEdit, "r")) == NULL) { 567 perror(tempEdit); 568 fclose(fbuf); 569 remove(tempEdit); 570 goto out; 571 } 572 remove(tempEdit); 573 fclose(obuf); 574 fclose(newi); 575 obuf = fbuf; 576 goto out; 577 fix: 578 perror(tempEdit); 579 out: 580 # ifdef VMUNIX 581 sigset(SIGCONT, scont); 582 # endif VMUNIX 583 sigset(SIGINT, sig); 584 newi = ibuf; 585 return(obuf); 586 } 587 588 /* 589 * Pipe the message through the command. 590 * Old message is on stdin of command; 591 * New message collected from stdout. 592 * Sh -c must return 0 to accept the new message. 593 */ 594 595 FILE * 596 mespipe(ibuf, obuf, cmd) 597 FILE *ibuf, *obuf; 598 char cmd[]; 599 { 600 register FILE *ni, *no; 601 int pid, s; 602 int (*savesig)(); 603 char *Shell; 604 605 newi = ibuf; 606 if ((no = fopen(tempEdit, "w")) == NULL) { 607 perror(tempEdit); 608 return(obuf); 609 } 610 if ((ni = fopen(tempEdit, "r")) == NULL) { 611 perror(tempEdit); 612 fclose(no); 613 remove(tempEdit); 614 return(obuf); 615 } 616 remove(tempEdit); 617 savesig = sigset(SIGINT, SIG_IGN); 618 fflush(obuf); 619 rewind(ibuf); 620 if ((Shell = value("SHELL")) == NULL) 621 Shell = "/bin/sh"; 622 if ((pid = vfork()) == -1) { 623 perror("fork"); 624 goto err; 625 } 626 if (pid == 0) { 627 /* 628 * stdin = current message. 629 * stdout = new message. 630 */ 631 632 sigchild(); 633 close(0); 634 dup(fileno(ibuf)); 635 close(1); 636 dup(fileno(no)); 637 for (s = 4; s < 15; s++) 638 close(s); 639 execl(Shell, Shell, "-c", cmd, 0); 640 perror(Shell); 641 _exit(1); 642 } 643 while (wait(&s) != pid) 644 ; 645 if (s != 0 || pid == -1) { 646 fprintf(stderr, "\"%s\" failed!?\n", cmd); 647 goto err; 648 } 649 if (fsize(ni) == 0) { 650 fprintf(stderr, "No bytes from \"%s\" !?\n", cmd); 651 goto err; 652 } 653 654 /* 655 * Take new files. 656 */ 657 658 newi = ni; 659 fclose(ibuf); 660 fclose(obuf); 661 sigset(SIGINT, savesig); 662 return(no); 663 664 err: 665 fclose(no); 666 fclose(ni); 667 sigset(SIGINT, savesig); 668 return(obuf); 669 } 670 671 /* 672 * Interpolate the named messages into the current 673 * message, preceding each line with a tab. 674 * Return a count of the number of characters now in 675 * the message, or -1 if an error is encountered writing 676 * the message temporary. The flag argument is 'm' if we 677 * should shift over and 'f' if not. 678 */ 679 forward(ms, obuf, f) 680 char ms[]; 681 FILE *obuf; 682 { 683 register int *msgvec, *ip; 684 extern char tempMail[]; 685 686 msgvec = (int *) salloc((msgCount+1) * sizeof *msgvec); 687 if (msgvec == (int *) NOSTR) 688 return(0); 689 if (getmsglist(ms, msgvec, 0) < 0) 690 return(0); 691 if (*msgvec == NULL) { 692 *msgvec = first(0, MMNORM); 693 if (*msgvec == NULL) { 694 printf("No appropriate messages\n"); 695 return(0); 696 } 697 msgvec[1] = NULL; 698 } 699 printf("Interpolating:"); 700 for (ip = msgvec; *ip != NULL; ip++) { 701 touch(*ip); 702 printf(" %d", *ip); 703 if (f == 'm') { 704 if (transmit(&message[*ip-1], obuf) < 0L) { 705 perror(tempMail); 706 return(-1); 707 } 708 } else 709 if (send(&message[*ip-1], obuf, 0) < 0) { 710 perror(tempMail); 711 return(-1); 712 } 713 } 714 printf("\n"); 715 return(0); 716 } 717 718 /* 719 * Send message described by the passed pointer to the 720 * passed output buffer. Insert a tab in front of each 721 * line. Return a count of the characters sent, or -1 722 * on error. 723 */ 724 725 long 726 transmit(mailp, obuf) 727 struct message *mailp; 728 FILE *obuf; 729 { 730 register struct message *mp; 731 register int ch; 732 long c, n; 733 int bol; 734 FILE *ibuf; 735 736 mp = mailp; 737 ibuf = setinput(mp); 738 c = mp->m_size; 739 n = c; 740 bol = 1; 741 while (c-- > 0L) { 742 if (bol) { 743 bol = 0; 744 putc('\t', obuf); 745 n++; 746 if (ferror(obuf)) { 747 perror("/tmp"); 748 return(-1L); 749 } 750 } 751 ch = getc(ibuf); 752 if (ch == '\n') 753 bol++; 754 putc(ch, obuf); 755 if (ferror(obuf)) { 756 perror("/tmp"); 757 return(-1L); 758 } 759 } 760 return(n); 761 } 762 763 /* 764 * Print (continue) when continued after ^Z. 765 */ 766 collcont(s) 767 { 768 769 printf("(continue)\n"); 770 fflush(stdout); 771 } 772 773 /* 774 * On interrupt, go here to save the partial 775 * message on ~/dead.letter. 776 * Then restore signals and execute the normal 777 * signal routine. We only come here if signals 778 * were previously set anyway. 779 */ 780 781 # ifndef VMUNIX 782 collintsig() 783 { 784 signal(SIGINT, SIG_IGN); 785 collrub(SIGINT); 786 } 787 788 collhupsig() 789 { 790 signal(SIGHUP, SIG_IGN); 791 collrub(SIGHUP); 792 } 793 # endif VMUNIX 794 795 collrub(s) 796 { 797 register FILE *dbuf; 798 register int c; 799 800 if (s == SIGINT && hadintr == 0) { 801 hadintr++; 802 fflush(stdout); 803 fprintf(stderr, "\n(Interrupt -- one more to kill letter)\n"); 804 longjmp(coljmp, 1); 805 } 806 fclose(newo); 807 rewind(newi); 808 if (s == SIGINT && value("nosave") != NOSTR || fsize(newi) == 0) 809 goto done; 810 if ((dbuf = fopen(deadletter, "w")) == NULL) 811 goto done; 812 chmod(deadletter, 0600); 813 while ((c = getc(newi)) != EOF) 814 putc(c, dbuf); 815 fclose(dbuf); 816 817 done: 818 fclose(newi); 819 sigset(SIGINT, savesig); 820 sigset(SIGHUP, savehup); 821 # ifdef VMUNIX 822 sigset(SIGCONT, savecont); 823 # endif VMUNIX 824 if (rcvmode) { 825 if (s == SIGHUP) 826 hangup(SIGHUP); 827 else 828 stop(s); 829 } 830 else 831 exit(1); 832 } 833 834 /* 835 * Acknowledge an interrupt signal from the tty by typing an @ 836 */ 837 838 intack(s) 839 { 840 841 puts("@"); 842 fflush(stdout); 843 clearerr(stdin); 844 } 845 846 /* 847 * Add a string to the end of a header entry field. 848 */ 849 850 char * 851 addto(hf, news) 852 char hf[], news[]; 853 { 854 register char *cp, *cp2, *linebuf; 855 856 if (hf == NOSTR) 857 hf = ""; 858 if (*news == '\0') 859 return(hf); 860 linebuf = salloc(strlen(hf) + strlen(news) + 2); 861 for (cp = hf; any(*cp, " \t"); cp++) 862 ; 863 for (cp2 = linebuf; *cp;) 864 *cp2++ = *cp++; 865 *cp2++ = ' '; 866 for (cp = news; any(*cp, " \t"); cp++) 867 ; 868 while (*cp != '\0') 869 *cp2++ = *cp++; 870 *cp2 = '\0'; 871 return(linebuf); 872 } 873