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