1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 1998 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 31 /* 32 * University Copyright- Copyright (c) 1982, 1986, 1988 33 * The Regents of the University of California 34 * All Rights Reserved 35 * 36 * University Acknowledgment- Portions of this document are derived from 37 * software developed by the University of California, Berkeley, and its 38 * contributors. 39 */ 40 41 #pragma ident "%Z%%M% %I% %E% SMI" 42 43 /* 44 * mailx -- a modified version of a University of California at Berkeley 45 * mail program 46 * 47 * Collect input from standard input, handling 48 * ~ escapes. 49 */ 50 51 #include "rcv.h" 52 #include <locale.h> 53 54 #ifdef SIGCONT 55 static void collcont(int); 56 #endif 57 static void collrub(int s); 58 static void cpout(char *str, FILE *ofd); 59 static int exwrite(char name[], FILE *ibuf); 60 static int forward(char ms[], FILE *obuf, int f); 61 static void intack(int); 62 static int forward(char ms[], FILE *obuf, int f); 63 static FILE *mesedit(FILE *ibuf, FILE *obuf, int c, struct header *hp); 64 static FILE *mespipe(FILE *ibuf, FILE *obuf, char cmd[]); 65 static void resetsigs(int resethup); 66 static int stripnulls(register char *linebuf, register int nread); 67 static void xhalt(void); 68 static char **Xaddone(char **hf, char news[]); 69 static int tabputs(const char *line, FILE *obuf); 70 71 /* 72 * Read a message from standard output and return a read file to it 73 * or NULL on error. 74 */ 75 76 /* 77 * The following hokiness with global variables is so that on 78 * receipt of an interrupt signal, the partial message can be salted 79 * away on dead.letter. The output file must be available to flush, 80 * and the input to read. Several open files could be saved all through 81 * mailx if stdio allowed simultaneous read/write access. 82 */ 83 84 static void (*savesig)(int); /* Previous SIGINT value */ 85 static void (*savehup)(int); /* Previous SIGHUP value */ 86 #ifdef SIGCONT 87 static void (*savecont)(int); /* Previous SIGCONT value */ 88 #endif 89 static FILE *newi; /* File for saving away */ 90 static FILE *newo; /* Output side of same */ 91 static int ignintr; /* Ignore interrups */ 92 static int hadintr; /* Have seen one SIGINT so far */ 93 static struct header *savehp; 94 static jmp_buf coljmp; /* To get back to work */ 95 96 FILE * 97 collect(struct header *hp) 98 { 99 FILE *ibuf, *fbuf, *obuf; 100 int escape, eof; 101 long lc, cc; 102 register int c, t; 103 int hdrs; 104 char linebuf[LINESIZE+1], *cp; 105 char *iprompt; 106 int inhead; 107 void (*sigpipe)(int), (*sigint)(int); 108 int fd = -1; 109 110 noreset++; 111 ibuf = obuf = NULL; 112 newi = newo = NULL; 113 if ((fd = open(tempMail, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0 || 114 (obuf = fdopen(fd, "w")) == NULL) { 115 perror(tempMail); 116 goto err; 117 } 118 newo = obuf; 119 if ((ibuf = fopen(tempMail, "r")) == NULL) { 120 perror(tempMail); 121 newo = NULL; 122 fclose(obuf); 123 goto err; 124 } 125 newi = ibuf; 126 removefile(tempMail); 127 128 ignintr = (int)value("ignore"); 129 hadintr = 1; 130 inhead = 1; 131 savehp = hp; 132 # ifdef VMUNIX 133 if ((savesig = sigset(SIGINT, SIG_IGN)) != SIG_IGN) 134 sigset(SIGINT, ignintr ? intack : collrub), sigblock(sigmask(SIGINT)); 135 if ((savehup = sigset(SIGHUP, SIG_IGN)) != SIG_IGN) 136 sigset(SIGHUP, collrub), sigblock(sigmask(SIGHUP)); 137 # else /* VMUNIX */ 138 # ifdef OLD_BSD_SIGS 139 if ((savesig = sigset(SIGINT, SIG_IGN)) != SIG_IGN) 140 sigset(SIGINT, ignintr ? intack : collrub); 141 if ((savehup = sigset(SIGHUP, SIG_IGN)) != SIG_IGN) 142 sigset(SIGHUP, collrub); 143 # else 144 if ((savesig = sigset(SIGINT, SIG_IGN)) != SIG_IGN) { 145 sigset_t mask; 146 147 sigemptyset(&mask); 148 sigaddset(&mask, SIGINT); 149 sigset(SIGINT, ignintr ? intack : collrub); 150 sigprocmask(SIG_BLOCK, &mask, NULL); 151 } 152 if ((savehup = sigset(SIGHUP, SIG_IGN)) != SIG_IGN) { 153 sigset_t mask; 154 155 sigemptyset(&mask); 156 sigaddset(&mask, SIGHUP); 157 sigset(SIGHUP, collrub); 158 sigprocmask(SIG_BLOCK, &mask, NULL); 159 } 160 # endif 161 # endif /* VMUNIX */ 162 #ifdef SIGCONT 163 savecont = sigset(SIGCONT, collcont); 164 #endif 165 /* 166 * If we are going to prompt for subject/cc/bcc, 167 * refrain from printing a newline after 168 * the headers (since some people mind). 169 */ 170 171 if (hp->h_subject == NOSTR) { 172 hp->h_subject = sflag; 173 sflag = NOSTR; 174 } 175 if (hp->h_cc == NOSTR) { 176 hp->h_cc = cflag; 177 cflag = NOSTR; 178 } 179 if (hp->h_bcc == NOSTR) { 180 hp->h_bcc = bflag; 181 bflag = NOSTR; 182 } 183 t = GMASK; 184 hdrs = 0; 185 if (intty && !tflag) { 186 if (hp->h_to == NOSTR) 187 hdrs |= GTO; 188 if (hp->h_subject == NOSTR && value("asksub")) 189 hdrs |= GSUBJECT; 190 if (hp->h_cc == NOSTR && value("askcc")) 191 hdrs |= GCC; 192 if (hp->h_bcc == NOSTR && value("askbcc")) 193 hdrs |= GBCC; 194 if (hdrs) 195 t &= ~GNL; 196 } 197 if (hp->h_seq != 0) { 198 puthead(hp, stdout, t, 0); 199 fflush(stdout); 200 } 201 if (setjmp(coljmp)) 202 goto err; 203 escape = SENDESC; 204 if ((cp = value("escape")) != NOSTR) 205 escape = *cp; 206 eof = 0; 207 if ((cp = value("MAILX_HEAD")) != NOSTR) { 208 cpout( cp, obuf); 209 if (isatty(fileno(stdin))) 210 cpout( cp, stdout); 211 } 212 iprompt = value("iprompt"); 213 fflush(obuf); 214 hadintr = 0; 215 for (;;) { 216 int nread, hasnulls; 217 # ifdef VMUNIX 218 int omask = sigblock(0) &~ (sigmask(SIGINT)|sigmask(SIGHUP)); 219 # else 220 # ifndef OLD_BSD_SIGS 221 sigset_t omask; 222 sigprocmask(0, NULL, &omask); 223 sigdelset(&omask, SIGINT); 224 sigdelset(&omask, SIGHUP); 225 # endif 226 # endif 227 228 setjmp(coljmp); 229 # ifdef VMUNIX 230 sigsetmask(omask); 231 # else /* VMUNIX */ 232 # ifdef OLD_BSD_SIGS 233 sigrelse(SIGINT); 234 sigrelse(SIGHUP); 235 # else 236 sigprocmask(SIG_SETMASK, &omask, NULL); 237 # endif 238 # endif /* VMUNIX */ 239 if (intty && !tflag && outtty && iprompt) 240 fputs(iprompt, stdout); 241 flush(); 242 if (hdrs) { 243 grabh(hp, hdrs, 1); 244 hdrs = 0; 245 continue; 246 } 247 if ((nread = getline(linebuf,LINESIZE,stdin,&hasnulls)) == NULL) { 248 if (intty && value("ignoreeof") != NOSTR) { 249 if (++eof > 35) 250 break; 251 printf(gettext( 252 "Use \".\" to terminate letter\n")); 253 continue; 254 } 255 break; 256 } 257 eof = 0; 258 hadintr = 0; 259 if (intty && equal(".\n", linebuf) && 260 (value("dot") != NOSTR || value("ignoreeof") != NOSTR)) 261 break; 262 /* 263 * If -t, scan text for headers. 264 */ 265 if (tflag) { 266 char *cp2; 267 268 if (!inhead) { 269 writeit: 270 if (write(fileno(obuf),linebuf,nread) != nread) 271 goto werr; 272 continue; 273 } 274 if (linebuf[0] == '\n') { 275 /* got blank line after header, ignore it */ 276 inhead = 0; 277 continue; 278 } 279 if (!headerp(linebuf)) { 280 /* got non-header line, save it */ 281 inhead = 0; 282 goto writeit; 283 } 284 if (hasnulls) 285 nread = stripnulls(linebuf, nread); 286 for (;;) { 287 char line2[LINESIZE]; 288 289 c = getc(stdin); 290 ungetc(c, stdin); 291 if (!isspace(c) || c == '\n') 292 break; 293 if (readline(stdin, line2) < 0) 294 break; 295 for (cp2 = line2; *cp2 != 0 && isspace(*cp2); 296 cp2++) 297 ; 298 if (strlen(linebuf) + strlen(cp2) >= 299 (unsigned)LINESIZE-2) 300 break; 301 cp = &linebuf[strlen(linebuf)]; 302 while (cp > linebuf && 303 (isspace(cp[-1]) || cp[-1] == '\\')) 304 cp--; 305 *cp++ = ' '; 306 strcpy(cp, cp2); 307 } 308 if ((c = strlen(linebuf)) > 0) { 309 cp = &linebuf[c-1]; 310 while (cp > linebuf && isspace(*cp)) 311 cp--; 312 *++cp = 0; 313 } 314 if (ishfield(linebuf, "to")) 315 hp->h_to = addto(hp->h_to, hcontents(linebuf)); 316 else if (ishfield(linebuf, "subject")) 317 hp->h_subject = 318 addone(hp->h_subject, hcontents(linebuf)); 319 else if (ishfield(linebuf, "cc")) 320 hp->h_cc = addto(hp->h_cc, hcontents(linebuf)); 321 else if (ishfield(linebuf, "bcc")) 322 hp->h_bcc = 323 addto(hp->h_bcc, hcontents(linebuf)); 324 else if (ishfield(linebuf, "default-options")) 325 hp->h_defopt = 326 addone(hp->h_defopt, hcontents(linebuf)); 327 else 328 hp->h_others = Xaddone(hp->h_others, linebuf); 329 hp->h_seq++; 330 continue; 331 } 332 if ((linebuf[0] != escape) || (rflag != NOSTR) || 333 (!intty && !(int)value("escapeok"))) { 334 if (write(fileno(obuf),linebuf,nread) != nread) 335 goto werr; 336 continue; 337 } 338 /* 339 * On double escape, just send the single one. 340 */ 341 if ((nread > 1) && (linebuf[1] == escape)) { 342 if (write(fileno(obuf),linebuf+1,nread-1) != (nread-1)) 343 goto werr; 344 continue; 345 } 346 if (hasnulls) 347 nread = stripnulls(linebuf, nread); 348 c = linebuf[1]; 349 linebuf[nread - 1] = '\0'; 350 switch (c) { 351 default: 352 /* 353 * Otherwise, it's an error. 354 */ 355 printf(gettext("Unknown tilde escape.\n")); 356 break; 357 358 case 'a': 359 case 'A': 360 /* 361 * autograph; sign the letter. 362 */ 363 364 if (cp = value(c=='a' ? "sign":"Sign")) { 365 if (*cp) 366 cpout( cp, obuf); 367 if (isatty(fileno(stdin))) { 368 if (*cp) 369 cpout( cp, stdout); 370 } 371 } 372 373 break; 374 375 case 'i': 376 /* 377 * insert string 378 */ 379 for (cp = &linebuf[2]; any(*cp, " \t"); cp++) 380 ; 381 if (*cp) 382 cp = value(cp); 383 if (cp != NOSTR) { 384 if (*cp) 385 cpout(cp, obuf); 386 if (isatty(fileno(stdout))) { 387 if (*cp) 388 cpout(cp, stdout); 389 } 390 } 391 break; 392 393 case '!': 394 /* 395 * Shell escape, send the balance of the 396 * line to sh -c. 397 */ 398 399 shell(&linebuf[2]); 400 break; 401 402 case ':': 403 case '_': 404 /* 405 * Escape to command mode, but be nice! 406 */ 407 408 execute(&linebuf[2], 1); 409 iprompt = value("iprompt"); 410 if (cp = value("escape")) 411 escape = *cp; 412 printf(gettext("(continue)\n")); 413 break; 414 415 case '.': 416 /* 417 * Simulate end of file on input. 418 */ 419 goto eofl; 420 421 case 'q': 422 case 'Q': 423 /* 424 * Force a quit of sending mail. 425 * Act like an interrupt happened. 426 */ 427 428 hadintr++; 429 collrub(SIGINT); 430 exit(1); 431 /* NOTREACHED */ 432 433 case 'x': 434 xhalt(); 435 break; /* not reached */ 436 437 case 'h': 438 /* 439 * Grab a bunch of headers. 440 */ 441 if (!intty || !outtty) { 442 printf(gettext("~h: no can do!?\n")); 443 break; 444 } 445 grabh(hp, GMASK, (int)value("bsdcompat")); 446 printf(gettext("(continue)\n")); 447 break; 448 449 case 't': 450 /* 451 * Add to the To list. 452 */ 453 454 hp->h_to = addto(hp->h_to, &linebuf[2]); 455 hp->h_seq++; 456 break; 457 458 case 's': 459 /* 460 * Set the Subject list. 461 */ 462 463 cp = &linebuf[2]; 464 while (any(*cp, " \t")) 465 cp++; 466 hp->h_subject = savestr(cp); 467 hp->h_seq++; 468 break; 469 470 case 'c': 471 /* 472 * Add to the CC list. 473 */ 474 475 hp->h_cc = addto(hp->h_cc, &linebuf[2]); 476 hp->h_seq++; 477 break; 478 479 case 'b': 480 /* 481 * Add stuff to blind carbon copies list. 482 */ 483 hp->h_bcc = addto(hp->h_bcc, &linebuf[2]); 484 hp->h_seq++; 485 break; 486 487 case 'R': 488 hp->h_defopt = addone(hp->h_defopt, myname); 489 hp->h_seq++; 490 fprintf(stderr, gettext("Return receipt marked.\n")); 491 receipt_flg = 1; 492 break; 493 494 case 'd': 495 copy(Getf("DEAD"), &linebuf[2]); 496 /* FALLTHROUGH */ 497 498 case '<': 499 case 'r': { 500 int ispip; 501 /* 502 * Invoke a file: 503 * Search for the file name, 504 * then open it and copy the contents to obuf. 505 * 506 * if name begins with '!', read from a command 507 */ 508 509 cp = &linebuf[2]; 510 while (any(*cp, " \t")) 511 cp++; 512 if (*cp == '\0') { 513 printf(gettext("Interpolate what file?\n")); 514 break; 515 } 516 if (*cp=='!') { 517 /* take input from a command */ 518 ispip = 1; 519 if ((fbuf = npopen(++cp, "r"))==NULL) { 520 perror(""); 521 break; 522 } 523 sigint = sigset(SIGINT, SIG_IGN); 524 } else { 525 ispip = 0; 526 cp = expand(cp); 527 if (cp == NOSTR) 528 break; 529 if (isdir(cp)) { 530 printf(gettext("%s: directory\n"), cp); 531 break; 532 } 533 if ((fbuf = fopen(cp, "r")) == NULL) { 534 perror(cp); 535 break; 536 } 537 } 538 printf("\"%s\" ", cp); 539 flush(); 540 lc = cc = 0; 541 while ((t = getc(fbuf)) != EOF) { 542 if (t == '\n') 543 lc++; 544 if (putc(t, obuf) == EOF) { 545 if (ispip) { 546 npclose(fbuf); 547 sigset(SIGINT, sigint); 548 } else 549 fclose(fbuf); 550 goto werr; 551 } 552 cc++; 553 } 554 if (ispip) { 555 npclose(fbuf); 556 sigset(SIGINT, sigint); 557 } else 558 fclose(fbuf); 559 printf("%ld/%ld\n", lc, cc); 560 fflush(obuf); 561 break; 562 } 563 564 case 'w': 565 /* 566 * Write the message on a file. 567 */ 568 569 cp = &linebuf[2]; 570 while (any(*cp, " \t")) 571 cp++; 572 if (*cp == '\0') { 573 fprintf(stderr, gettext("Write what file!?\n")); 574 break; 575 } 576 if ((cp = expand(cp)) == NOSTR) 577 break; 578 fflush(obuf); 579 rewind(ibuf); 580 exwrite(cp, ibuf); 581 break; 582 583 case 'm': 584 case 'M': 585 case 'f': 586 case 'F': 587 /* 588 * Interpolate the named messages, if we 589 * are in receiving mail mode. Does the 590 * standard list processing garbage. 591 * If ~f or ~F is given, we don't shift over. 592 */ 593 594 if (!rcvmode) { 595 printf(gettext( 596 "No messages to send from!?!\n")); 597 break; 598 } 599 cp = &linebuf[2]; 600 while (any(*cp, " \t")) 601 cp++; 602 if (forward(cp, obuf, c) < 0) 603 goto werr; 604 fflush(obuf); 605 printf(gettext("(continue)\n")); 606 break; 607 608 case '?': 609 if ((fbuf = fopen(THELPFILE, "r")) == NULL) { 610 printf(gettext("No help just now.\n")); 611 break; 612 } 613 t = getc(fbuf); 614 while (t != -1) { 615 putchar(t); 616 t = getc(fbuf); 617 } 618 fclose(fbuf); 619 break; 620 621 case 'p': { 622 /* 623 * Print out the current state of the 624 * message without altering anything. 625 */ 626 int nlines; 627 extern jmp_buf pipestop; 628 extern void brokpipe(int); 629 630 fflush(obuf); 631 rewind(ibuf); 632 fbuf = stdout; 633 if (setjmp(pipestop)) 634 goto ret0; 635 if (intty && outtty && (cp = value("crt")) != NOSTR) { 636 nlines = 637 (*cp == '\0' ? screensize() : atoi(cp)) - 7; 638 /* 7 for hdr lines */ 639 while ((t = getc(ibuf)) != EOF) { 640 if (t == '\n') 641 if (--nlines <= 0) 642 break; 643 } 644 rewind(ibuf); 645 if (nlines <= 0) { 646 fbuf = npopen(MORE, "w"); 647 if (fbuf == NULL) { 648 perror(MORE); 649 fbuf = stdout; 650 } else { 651 sigint = sigset(SIGINT, SIG_IGN); 652 sigpipe = sigset(SIGPIPE, brokpipe); 653 } 654 } 655 } 656 fprintf(fbuf, gettext("-------\nMessage contains:\n")); 657 puthead(hp, fbuf, GMASK, 0); 658 while ((t = getc(ibuf))!=EOF) 659 putc(t, fbuf); 660 ret0: 661 if (fbuf != stdout) { 662 npclose(fbuf); 663 sigset(SIGPIPE, sigpipe); 664 sigset(SIGINT, sigint); 665 } 666 printf(gettext("(continue)\n")); 667 break; 668 } 669 670 case '^': 671 case '|': 672 /* 673 * Pipe message through command. 674 * Collect output as new message. 675 */ 676 677 obuf = mespipe(ibuf, obuf, &linebuf[2]); 678 newo = obuf; 679 ibuf = newi; 680 newi = ibuf; 681 printf(gettext("(continue)\n")); 682 break; 683 684 case 'v': 685 case 'e': 686 /* 687 * Edit the current message. 688 * 'e' means to use EDITOR 689 * 'v' means to use VISUAL 690 */ 691 692 if ((obuf = mesedit(ibuf, obuf, c, hp)) == NULL) 693 goto err; 694 newo = obuf; 695 ibuf = newi; 696 printf(gettext("(continue)\n")); 697 break; 698 } 699 fflush(obuf); 700 } 701 eofl: 702 fflush(obuf); 703 if ((cp = value("MAILX_TAIL")) != NOSTR) { 704 cpout( cp, obuf); 705 if (isatty(fileno(stdin))) 706 cpout( cp, stdout); 707 } 708 fclose(obuf); 709 rewind(ibuf); 710 resetsigs(0); 711 noreset = 0; 712 return(ibuf); 713 714 werr: 715 /* 716 * Write error occurred on tmp file, save partial 717 * message in dead.letter. 718 */ 719 perror(tempMail); 720 fflush(obuf); 721 rewind(ibuf); 722 if (fsize(ibuf) > 0) { 723 char *deadletter; 724 725 deadletter = Getf("DEAD"); 726 fprintf(stderr, gettext("Saving partial message in %s\n"), 727 deadletter); 728 if ((fbuf = fopen(deadletter, 729 value("appenddeadletter") == NOSTR ? "w" : "a")) != NULL) { 730 chmod(deadletter, DEADPERM); 731 puthead(hp, fbuf, GMASK|GCLEN, fsize(ibuf)); 732 lcwrite(deadletter, ibuf, fbuf, value("appenddeadletter") != NOSTR); 733 fclose(fbuf); 734 } else 735 perror(deadletter); 736 } 737 738 err: 739 if (ibuf != NULL) 740 fclose(ibuf); 741 if (obuf != NULL) 742 fclose(obuf); 743 resetsigs(0); 744 noreset = 0; 745 return(NULL); 746 } 747 748 static void 749 resetsigs(int resethup) 750 { 751 (void) sigset(SIGINT, savesig); 752 if (resethup) 753 (void) sigset(SIGHUP, savehup); 754 #ifdef SIGCONT 755 # ifdef preSVr4 756 (void) sigset(SIGCONT, savecont); 757 # else 758 { 759 struct sigaction nsig; 760 nsig.sa_handler = (void (*)())savecont; 761 sigemptyset(&nsig.sa_mask); 762 nsig.sa_flags = SA_RESTART; 763 (void) sigaction(SIGCONT, &nsig, (struct sigaction*)0); 764 } 765 # endif 766 #endif 767 } 768 769 /* 770 * Write a file ex-like. 771 */ 772 773 static int 774 exwrite(char name[], FILE *ibuf) 775 { 776 register FILE *of; 777 struct stat junk; 778 void (*sigint)(int), (*sigpipe)(int); 779 int pi = (*name == '!'); 780 781 if ((of = pi ? npopen(++name, "w") : fopen(name, "a")) == NULL) { 782 perror(name); 783 return(-1); 784 } 785 if (pi) { 786 sigint = sigset(SIGINT, SIG_IGN); 787 sigpipe = sigset(SIGPIPE, SIG_IGN); 788 } 789 lcwrite(name, ibuf, of, 0); 790 pi ? npclose(of) : fclose(of); 791 if (pi) { 792 sigset(SIGPIPE, sigpipe); 793 sigset(SIGINT, sigint); 794 } 795 return(0); 796 } 797 798 void 799 lcwrite(char *fn, FILE *fi, FILE *fo, int addnl) 800 { 801 register int c; 802 long lc, cc; 803 804 printf("\"%s\" ", fn); 805 fflush(stdout); 806 lc = cc = 0; 807 while ((c = getc(fi)) != EOF) { 808 cc++; 809 if (putc(c, fo) == '\n') 810 lc++; 811 if (ferror(fo)) { 812 perror(""); 813 return; 814 } 815 } 816 if (addnl) { 817 putc('\n', fo); 818 lc++; 819 cc++; 820 } 821 fflush(fo); 822 if (fferror(fo)) { 823 perror(""); 824 return; 825 } 826 printf("%ld/%ld\n", lc, cc); 827 fflush(stdout); 828 } 829 830 /* 831 * Edit the message being collected on ibuf and obuf. 832 * Write the message out onto some poorly-named temp file 833 * and point an editor at it. 834 * 835 * On return, make the edit file the new temp file. 836 */ 837 838 static FILE * 839 mesedit(FILE *ibuf, FILE *obuf, int c, struct header *hp) 840 { 841 pid_t pid; 842 FILE *fbuf; 843 register int t; 844 void (*sigint)(int); 845 #ifdef SIGCONT 846 void (*sigcont)(int); 847 #endif 848 struct stat sbuf; 849 register char *edit; 850 char hdr[LINESIZE]; 851 char *oto, *osubject, *occ, *obcc, **oothers; 852 int fd = -1; 853 854 if (stat(tempEdit, &sbuf) >= 0) { 855 printf(gettext("%s: file exists\n"), tempEdit); 856 goto out; 857 } 858 if ((fd = open(tempEdit, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0 || 859 (fbuf = fdopen(fd, "w")) == NULL) { 860 perror(tempEdit); 861 goto out; 862 } 863 fflush(obuf); 864 rewind(ibuf); 865 puthead(hp, fbuf, GMASK, 0); 866 while ((t = getc(ibuf)) != EOF) 867 putc(t, fbuf); 868 fflush(fbuf); 869 if (fferror(fbuf)) { 870 perror(tempEdit); 871 removefile(tempEdit); 872 goto out; 873 } 874 fclose(fbuf); 875 if ((edit = value(c == 'e' ? "EDITOR" : "VISUAL")) == NOSTR || 876 *edit == '\0') 877 edit = c == 'e' ? EDITOR : VISUAL; 878 edit = safeexpand(edit); 879 880 /* 881 * Fork/execlp the editor on the edit file 882 */ 883 884 pid = vfork(); 885 if (pid == (pid_t)-1) { 886 perror("fork"); 887 removefile(tempEdit); 888 goto out; 889 } 890 if (pid == 0) { 891 char ecmd[BUFSIZ]; 892 char *Shell; 893 894 sigchild(); 895 execlp(edit, edit, tempEdit, (char *)0); 896 /* 897 * If execlp fails, "edit" might really be a complete 898 * shell command, not a simple pathname. Try using 899 * the shell to run it. 900 */ 901 snprintf(ecmd, sizeof (ecmd), "exec %s %s", edit, tempEdit); 902 if ((Shell = value("SHELL")) == NULL || *Shell=='\0') 903 Shell = SHELL; 904 execlp(Shell, Shell, "-c", ecmd, NULL); 905 perror(edit); 906 _exit(1); 907 } 908 sigint = sigset(SIGINT, SIG_IGN); 909 #ifdef SIGCONT 910 sigcont = sigset(SIGCONT, SIG_DFL); 911 #endif 912 while (wait((int *)0) != pid) 913 ; 914 sigset(SIGINT, sigint); 915 #ifdef SIGCONT 916 sigset(SIGCONT, sigcont); 917 #endif 918 /* 919 * Now switch to new file. 920 */ 921 922 if ((fbuf = fopen(tempEdit, "r")) == NULL) { 923 perror(tempEdit); 924 removefile(tempEdit); 925 goto out; 926 } 927 removefile(tempEdit); 928 929 /* save the old headers, in case they are accidentally deleted */ 930 osubject = hp->h_subject; 931 oto = hp->h_to; 932 occ = hp->h_cc; 933 obcc = hp->h_bcc; 934 oothers = hp->h_others; 935 hp->h_to = hp->h_subject = hp->h_cc = hp->h_bcc = hp->h_defopt = NOSTR; 936 hp->h_others = NOSTRPTR; 937 hp->h_seq = 0; 938 while (gethfield(fbuf, hdr, 9999L) > 0) { 939 if (ishfield(hdr, "to")) 940 hp->h_to = addto(hp->h_to, hcontents(hdr)); 941 else if (ishfield(hdr, "subject")) 942 hp->h_subject = addone(hp->h_subject, hcontents(hdr)); 943 else if (ishfield(hdr, "cc")) 944 hp->h_cc = addto(hp->h_cc, hcontents(hdr)); 945 else if (ishfield(hdr, "bcc")) 946 hp->h_bcc = addto(hp->h_bcc, hcontents(hdr)); 947 else if (ishfield(hdr, "default-options")) 948 hp->h_defopt = addone(hp->h_defopt, hcontents(hdr)); 949 else 950 hp->h_others = Xaddone(hp->h_others, hdr); 951 hp->h_seq++; 952 } 953 if (hp->h_seq == 0) { 954 /* if we didn't see any headers, restore the original headers */ 955 hp->h_subject = osubject; 956 hp->h_to = oto; 957 hp->h_cc = occ; 958 hp->h_bcc = obcc; 959 hp->h_others = oothers; 960 printf(gettext( 961 "(Deleted headers restored to original values)\n")); 962 } 963 if ((fd = open(tempMail, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0 || 964 (obuf = fdopen(fd, "w")) == NULL) { 965 perror(tempMail); 966 fclose(fbuf); 967 goto out; 968 } 969 if ((ibuf = fopen(tempMail, "r")) == NULL) { 970 perror(tempMail); 971 removefile(tempMail); 972 fclose(fbuf); 973 fclose(obuf); 974 goto out; 975 } 976 removefile(tempMail); 977 if (strlen(hdr) != 0) { 978 fputs(hdr, obuf); 979 putc('\n', obuf); 980 } 981 while ((t = getc(fbuf)) != EOF) 982 putc(t, obuf); 983 fclose(fbuf); 984 fclose(newo); 985 fclose(newi); 986 newo = obuf; 987 newi = ibuf; 988 out: 989 return(newo); 990 } 991 992 /* 993 * Pipe the message through the command. 994 * Old message is on stdin of command; 995 * New message collected from stdout. 996 * Sh -c must return 0 to accept the new message. 997 */ 998 999 static FILE * 1000 mespipe(FILE *ibuf, FILE *obuf, char cmd[]) 1001 { 1002 register FILE *ni, *no; 1003 pid_t pid; 1004 int s; 1005 void (*sigint)(int); 1006 char *Shell; 1007 int fd = -1; 1008 1009 newi = ibuf; 1010 if ((fd = open(tempEdit, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0 || 1011 (no = fdopen(fd, "w")) == NULL) { 1012 perror(tempEdit); 1013 return(obuf); 1014 } 1015 if ((ni = fopen(tempEdit, "r")) == NULL) { 1016 perror(tempEdit); 1017 fclose(no); 1018 removefile(tempEdit); 1019 return(obuf); 1020 } 1021 removefile(tempEdit); 1022 fflush(obuf); 1023 rewind(ibuf); 1024 if ((Shell = value("SHELL")) == NULL || *Shell=='\0') 1025 Shell = SHELL; 1026 if ((pid = vfork()) == (pid_t)-1) { 1027 perror("fork"); 1028 goto err; 1029 } 1030 if (pid == 0) { 1031 /* 1032 * stdin = current message. 1033 * stdout = new message. 1034 */ 1035 1036 sigchild(); 1037 close(0); 1038 dup(fileno(ibuf)); 1039 close(1); 1040 dup(fileno(no)); 1041 for (s = 4; s < 15; s++) 1042 close(s); 1043 execlp(Shell, Shell, "-c", cmd, (char *)0); 1044 perror(Shell); 1045 _exit(1); 1046 } 1047 sigint = sigset(SIGINT, SIG_IGN); 1048 while (wait(&s) != pid) 1049 ; 1050 sigset(SIGINT, sigint); 1051 if (s != 0 || pid == (pid_t)-1) { 1052 fprintf(stderr, gettext("\"%s\" failed!?\n"), cmd); 1053 goto err; 1054 } 1055 if (fsize(ni) == 0) { 1056 fprintf(stderr, gettext("No bytes from \"%s\" !?\n"), cmd); 1057 goto err; 1058 } 1059 1060 /* 1061 * Take new files. 1062 */ 1063 1064 newi = ni; 1065 fclose(ibuf); 1066 fclose(obuf); 1067 return(no); 1068 1069 err: 1070 fclose(no); 1071 fclose(ni); 1072 return(obuf); 1073 } 1074 1075 static char *indentprefix; /* used instead of tab by tabputs */ 1076 1077 /* 1078 * Interpolate the named messages into the current 1079 * message, preceding each line with a tab. 1080 * Return a count of the number of characters now in 1081 * the message, or -1 if an error is encountered writing 1082 * the message temporary. The flag argument is 'm' if we 1083 * should shift over and 'f' if not. 1084 */ 1085 static int 1086 forward(char ms[], FILE *obuf, int f) 1087 { 1088 register int *msgvec, *ip; 1089 1090 msgvec = (int *) salloc((msgCount+1) * sizeof *msgvec); 1091 if (msgvec == NOINTPTR) 1092 return(0); 1093 if (getmsglist(ms, msgvec, 0) < 0) 1094 return(0); 1095 if (*msgvec == NULL) { 1096 *msgvec = first(0, MMNORM); 1097 if (*msgvec == NULL) { 1098 printf(gettext("No appropriate messages\n")); 1099 return(0); 1100 } 1101 msgvec[1] = NULL; 1102 } 1103 if (tolower(f) == 'm') 1104 indentprefix = value("indentprefix"); 1105 printf(gettext("Interpolating:")); 1106 for (ip = msgvec; *ip != NULL; ip++) { 1107 touch(*ip); 1108 printf(" %d", *ip); 1109 if (msend(&message[*ip-1], obuf, islower(f) ? M_IGNORE : 0, 1110 tolower(f) == 'm' ? tabputs : fputs) < 0) { 1111 perror(tempMail); 1112 return(-1); 1113 } 1114 } 1115 fflush(obuf); 1116 if (fferror(obuf)) { 1117 perror(tempMail); 1118 return(-1); 1119 } 1120 printf("\n"); 1121 return(0); 1122 } 1123 1124 static int 1125 tabputs(const char *line, FILE *obuf) 1126 { 1127 1128 if (indentprefix) 1129 fputs(indentprefix, obuf); 1130 /* Don't create lines with only a tab on them */ 1131 else if (line[0] != '\n') 1132 fputc('\t', obuf); 1133 return (fputs(line, obuf)); 1134 } 1135 1136 /* 1137 * Print (continue) when continued after ^Z. 1138 */ 1139 #ifdef SIGCONT 1140 static void 1141 #ifdef __cplusplus 1142 collcont(int) 1143 #else 1144 /* ARGSUSED */ 1145 collcont(int s) 1146 #endif 1147 { 1148 printf(gettext("(continue)\n")); 1149 fflush(stdout); 1150 } 1151 #endif /* SIGCONT */ 1152 1153 /* 1154 * On interrupt, go here to save the partial 1155 * message on ~/dead.letter. 1156 * Then restore signals and execute the normal 1157 * signal routine. We only come here if signals 1158 * were previously set anyway. 1159 */ 1160 static void 1161 collrub(int s) 1162 { 1163 register FILE *dbuf; 1164 register char *deadletter; 1165 1166 # ifdef OLD_BSD_SIGS 1167 if (s == SIGHUP) 1168 sigignore(SIGHUP); 1169 # endif 1170 if (s == SIGINT && hadintr == 0) { 1171 hadintr++; 1172 fflush(stdout); 1173 fprintf(stderr, 1174 gettext("\n(Interrupt -- one more to kill letter)\n")); 1175 # ifdef OLD_BSD_SIGS 1176 sigrelse(s); 1177 # endif 1178 longjmp(coljmp, 1); 1179 } 1180 fclose(newo); 1181 rewind(newi); 1182 if (s == SIGINT && value("save")==NOSTR || fsize(newi) == 0) 1183 goto done; 1184 deadletter = Getf("DEAD"); 1185 if ((dbuf = fopen(deadletter, 1186 (value("appenddeadletter") == NOSTR ? "w" : "a"))) == NULL) { 1187 perror(deadletter); 1188 goto done; 1189 } 1190 chmod(deadletter, DEADPERM); 1191 puthead(savehp, dbuf, GMASK|GCLEN, fsize(newi)); 1192 lcwrite(deadletter, newi, dbuf, value("appenddeadletter") != NOSTR); 1193 fclose(dbuf); 1194 done: 1195 fclose(newi); 1196 resetsigs(1); 1197 if (rcvmode) { 1198 if (s == SIGHUP) 1199 hangup(s); 1200 else 1201 stop(s); 1202 } 1203 else 1204 exit(1); 1205 } 1206 1207 /* 1208 * Acknowledge an interrupt signal from the tty by typing an @ 1209 */ 1210 static void 1211 #ifdef __cplusplus 1212 intack(int) 1213 #else 1214 /* ARGSUSED */ 1215 intack(int s) 1216 #endif 1217 { 1218 1219 puts("@"); 1220 fflush(stdout); 1221 clearerr(stdin); 1222 longjmp(coljmp,1); 1223 } 1224 1225 /* Read line from stdin, noting any NULL characters. 1226 Return the number of characters read. Note that the buffer 1227 passed must be 1 larger than "size" for the trailing NUL byte. 1228 */ 1229 int 1230 getline(char *line, int size, FILE *f, int *hasnulls) 1231 { 1232 register int i, ch; 1233 for (i = 0; (i < size) && ((ch=getc(f)) != EOF); ) { 1234 if ( ch == '\0' ) 1235 *hasnulls = 1; 1236 if ((line[i++] = (char)ch) == '\n') break; 1237 } 1238 line[i] = '\0'; 1239 return(i); 1240 } 1241 1242 void 1243 #ifdef __cplusplus 1244 savedead(int) 1245 #else 1246 /* ARGSUSED */ 1247 savedead(int s) 1248 #endif 1249 { 1250 collrub(SIGINT); 1251 exit(1); 1252 /* NOTREACHED */ 1253 } 1254 1255 /* 1256 * Add a list of addresses to the end of a header entry field. 1257 */ 1258 char * 1259 addto(char hf[], char news[]) 1260 { 1261 char name[LINESIZE]; 1262 int comma = docomma(news); 1263 1264 while (news = yankword(news, name, sizeof (name), comma)) { 1265 nstrcat(name, sizeof (name), ", "); 1266 hf = addone(hf, name); 1267 } 1268 return hf; 1269 } 1270 1271 /* 1272 * Add a string to the end of a header entry field. 1273 */ 1274 char * 1275 addone(char hf[], char news[]) 1276 { 1277 register char *cp, *cp2, *linebuf; 1278 1279 if (hf == NOSTR) 1280 hf = savestr(""); 1281 if (*news == '\0') 1282 return(hf); 1283 linebuf = (char *)srealloc(hf, (unsigned)(strlen(hf) + strlen(news) + 2)); 1284 cp2 = strchr(linebuf, '\0'); 1285 if (cp2 > linebuf && cp2[-1] != ' ') 1286 *cp2++ = ' '; 1287 for (cp = news; any(*cp, " \t"); cp++) 1288 ; 1289 while (*cp != '\0') 1290 *cp2++ = *cp++; 1291 *cp2 = '\0'; 1292 return(linebuf); 1293 } 1294 1295 static int 1296 nptrs(char **hf) 1297 { 1298 register int i; 1299 1300 if (!hf) 1301 return(0); 1302 for (i = 0; *hf; hf++) 1303 i++; 1304 return(i); 1305 } 1306 1307 /* 1308 * Add a non-standard header to the end of the non-standard headers. 1309 */ 1310 static char ** 1311 Xaddone(char **hf, char news[]) 1312 { 1313 register char *linebuf; 1314 char **ohf = hf; 1315 int nhf = nptrs(hf); 1316 1317 if (hf == NOSTRPTR) 1318 hf = (char**)salloc(sizeof(char*) * 2); 1319 else 1320 hf = (char**)srealloc(hf, sizeof(char*) * (nhf + 2)); 1321 if (hf == NOSTRPTR) { 1322 fprintf(stderr, gettext("No room, header lost: %s\n"), news); 1323 return(ohf); 1324 } 1325 linebuf = (char *)salloc((unsigned)(strlen(news) + 1)); 1326 strcpy(linebuf, news); 1327 hf[nhf++] = linebuf; 1328 hf[nhf] = NOSTR; 1329 return(hf); 1330 } 1331 1332 static void 1333 cpout(char *str, FILE *ofd) 1334 { 1335 register char *cp = str; 1336 1337 while (*cp) { 1338 if (*cp == '\\') { 1339 switch (*(cp+1)) { 1340 case 'n': 1341 putc('\n', ofd); 1342 cp++; 1343 break; 1344 case 't': 1345 putc('\t', ofd); 1346 cp++; 1347 break; 1348 default: 1349 putc('\\', ofd); 1350 } 1351 } else { 1352 putc(*cp, ofd); 1353 } 1354 cp++; 1355 } 1356 putc('\n', ofd); 1357 fflush(ofd); 1358 } 1359 1360 static void 1361 xhalt(void) 1362 { 1363 fclose(newo); 1364 fclose(newi); 1365 sigset(SIGINT, savesig); 1366 sigset(SIGHUP, savehup); 1367 if (rcvmode) 1368 stop(0); 1369 exit(1); 1370 /* NOTREACHED */ 1371 } 1372 1373 /* 1374 * Strip the nulls from a buffer of length n 1375 */ 1376 static int 1377 stripnulls(register char *linebuf, register int nread) 1378 { 1379 register int i, j; 1380 1381 for (i = 0; i < nread; i++) 1382 if (linebuf[i] == '\0') 1383 break; 1384 for (j = i; j < nread; j++) 1385 if (linebuf[j] != '\0') 1386 linebuf[i++] = linebuf[j]; 1387 linebuf[i] = '\0'; 1388 return(i); 1389 } 1390