1 /* 2 * Copyright (c) 1980 Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char sccsid[] = "@(#)collect.c 5.24 (Berkeley) 04/01/91"; 10 #endif /* not lint */ 11 12 /* 13 * Mail -- a mail program 14 * 15 * Collect input from standard input, handling 16 * ~ escapes. 17 */ 18 19 #include "rcv.h" 20 #include <sys/stat.h> 21 22 /* 23 * Read a message from standard output and return a read file to it 24 * or NULL on error. 25 */ 26 27 /* 28 * The following hokiness with global variables is so that on 29 * receipt of an interrupt signal, the partial message can be salted 30 * away on dead.letter. 31 */ 32 33 static sig_t saveint; /* Previous SIGINT value */ 34 static sig_t savehup; /* Previous SIGHUP value */ 35 static sig_t savetstp; /* Previous SIGTSTP value */ 36 static sig_t savettou; /* Previous SIGTTOU value */ 37 static sig_t savettin; /* Previous SIGTTIN value */ 38 static FILE *collf; /* File for saving away */ 39 static int hadintr; /* Have seen one SIGINT so far */ 40 41 static jmp_buf colljmp; /* To get back to work */ 42 static int colljmp_p; /* whether to long jump */ 43 static jmp_buf collabort; /* To end collection with error */ 44 45 FILE * 46 collect(hp, printheaders) 47 struct header *hp; 48 { 49 FILE *fbuf; 50 int lc, cc, escape, eofcount; 51 register int c, t; 52 char linebuf[LINESIZE], *cp; 53 extern char tempMail[]; 54 char getsub; 55 int omask; 56 void collint(), collhup(), collstop(); 57 58 collf = NULL; 59 /* 60 * Start catching signals from here, but we're still die on interrupts 61 * until we're in the main loop. 62 */ 63 omask = sigblock(sigmask(SIGINT) | sigmask(SIGHUP)); 64 if ((saveint = signal(SIGINT, SIG_IGN)) != SIG_IGN) 65 signal(SIGINT, collint); 66 if ((savehup = signal(SIGHUP, SIG_IGN)) != SIG_IGN) 67 signal(SIGHUP, collhup); 68 savetstp = signal(SIGTSTP, collstop); 69 savettou = signal(SIGTTOU, collstop); 70 savettin = signal(SIGTTIN, collstop); 71 if (setjmp(collabort) || setjmp(colljmp)) { 72 rm(tempMail); 73 goto err; 74 } 75 sigsetmask(omask & ~(sigmask(SIGINT) | sigmask(SIGHUP))); 76 77 noreset++; 78 if ((collf = Fopen(tempMail, "w+")) == NULL) { 79 perror(tempMail); 80 goto err; 81 } 82 unlink(tempMail); 83 84 /* 85 * If we are going to prompt for a subject, 86 * refrain from printing a newline after 87 * the headers (since some people mind). 88 */ 89 t = GTO|GSUBJECT|GCC|GNL; 90 getsub = 0; 91 if (hp->h_subject == NOSTR && value("interactive") != NOSTR && 92 (value("ask") != NOSTR || value("asksub") != NOSTR)) 93 t &= ~GNL, getsub++; 94 if (printheaders) { 95 puthead(hp, stdout, t); 96 fflush(stdout); 97 } 98 if ((cp = value("escape")) != NOSTR) 99 escape = *cp; 100 else 101 escape = ESCAPE; 102 eofcount = 0; 103 hadintr = 0; 104 105 if (!setjmp(colljmp)) { 106 if (getsub) 107 grabh(hp, GSUBJECT); 108 } else { 109 /* 110 * Come here for printing the after-signal message. 111 * Duplicate messages won't be printed because 112 * the write is aborted if we get a SIGTTOU. 113 */ 114 cont: 115 if (hadintr) { 116 fflush(stdout); 117 fprintf(stderr, 118 "\n(Interrupt -- one more to kill letter)\n"); 119 } else { 120 printf("(continue)\n"); 121 fflush(stdout); 122 } 123 } 124 for (;;) { 125 colljmp_p = 1; 126 c = readline(stdin, linebuf, LINESIZE); 127 colljmp_p = 0; 128 if (c < 0) { 129 if (value("interactive") != NOSTR && 130 value("ignoreeof") != NOSTR && ++eofcount < 25) { 131 printf("Use \".\" to terminate letter\n"); 132 continue; 133 } 134 break; 135 } 136 eofcount = 0; 137 hadintr = 0; 138 if (linebuf[0] == '.' && linebuf[1] == '\0' && 139 value("interactive") != NOSTR && 140 (value("dot") != NOSTR || value("ignoreeof") != NOSTR)) 141 break; 142 if (linebuf[0] != escape || value("interactive") == NOSTR) { 143 if (putline(collf, linebuf) < 0) 144 goto err; 145 continue; 146 } 147 c = linebuf[1]; 148 switch (c) { 149 default: 150 /* 151 * On double escape, just send the single one. 152 * Otherwise, it's an error. 153 */ 154 if (c == escape) { 155 if (putline(collf, &linebuf[1]) < 0) 156 goto err; 157 else 158 break; 159 } 160 printf("Unknown tilde escape.\n"); 161 break; 162 case 'C': 163 /* 164 * Dump core. 165 */ 166 core(); 167 break; 168 case '!': 169 /* 170 * Shell escape, send the balance of the 171 * line to sh -c. 172 */ 173 shell(&linebuf[2]); 174 break; 175 case ':': 176 /* 177 * Escape to command mode, but be nice! 178 */ 179 execute(&linebuf[2], 1); 180 goto cont; 181 case '.': 182 /* 183 * Simulate end of file on input. 184 */ 185 goto out; 186 case 'q': 187 /* 188 * Force a quit of sending mail. 189 * Act like an interrupt happened. 190 */ 191 hadintr++; 192 collint(SIGINT); 193 exit(1); 194 case 'h': 195 /* 196 * Grab a bunch of headers. 197 */ 198 grabh(hp, GTO|GSUBJECT|GCC|GBCC); 199 goto cont; 200 case 't': 201 /* 202 * Add to the To list. 203 */ 204 hp->h_to = cat(hp->h_to, extract(&linebuf[2], GTO)); 205 break; 206 case 's': 207 /* 208 * Set the Subject list. 209 */ 210 cp = &linebuf[2]; 211 while (isspace(*cp)) 212 cp++; 213 hp->h_subject = savestr(cp); 214 break; 215 case 'c': 216 /* 217 * Add to the CC list. 218 */ 219 hp->h_cc = cat(hp->h_cc, extract(&linebuf[2], GCC)); 220 break; 221 case 'b': 222 /* 223 * Add stuff to blind carbon copies list. 224 */ 225 hp->h_bcc = cat(hp->h_bcc, extract(&linebuf[2], GBCC)); 226 break; 227 case 'd': 228 strcpy(linebuf + 2, getdeadletter()); 229 /* fall into . . . */ 230 case 'r': 231 /* 232 * Invoke a file: 233 * Search for the file name, 234 * then open it and copy the contents to collf. 235 */ 236 cp = &linebuf[2]; 237 while (isspace(*cp)) 238 cp++; 239 if (*cp == '\0') { 240 printf("Interpolate what file?\n"); 241 break; 242 } 243 cp = expand(cp); 244 if (cp == NOSTR) 245 break; 246 if (isdir(cp)) { 247 printf("%s: Directory\n", cp); 248 break; 249 } 250 if ((fbuf = Fopen(cp, "r")) == NULL) { 251 perror(cp); 252 break; 253 } 254 printf("\"%s\" ", cp); 255 fflush(stdout); 256 lc = 0; 257 cc = 0; 258 while (readline(fbuf, linebuf, LINESIZE) >= 0) { 259 lc++; 260 if ((t = putline(collf, linebuf)) < 0) { 261 Fclose(fbuf); 262 goto err; 263 } 264 cc += t; 265 } 266 Fclose(fbuf); 267 printf("%d/%d\n", lc, cc); 268 break; 269 case 'w': 270 /* 271 * Write the message on a file. 272 */ 273 cp = &linebuf[2]; 274 while (*cp == ' ' || *cp == '\t') 275 cp++; 276 if (*cp == '\0') { 277 fprintf(stderr, "Write what file!?\n"); 278 break; 279 } 280 if ((cp = expand(cp)) == NOSTR) 281 break; 282 rewind(collf); 283 exwrite(cp, collf, 1); 284 break; 285 case 'm': 286 case 'M': 287 case 'f': 288 case 'F': 289 /* 290 * Interpolate the named messages, if we 291 * are in receiving mail mode. Does the 292 * standard list processing garbage. 293 * If ~f is given, we don't shift over. 294 */ 295 if (forward(linebuf + 2, collf, c) < 0) 296 goto err; 297 goto cont; 298 case '?': 299 if ((fbuf = Fopen(_PATH_TILDE, "r")) == NULL) { 300 perror(_PATH_TILDE); 301 break; 302 } 303 while ((t = getc(fbuf)) != EOF) 304 (void) putchar(t); 305 Fclose(fbuf); 306 break; 307 case 'p': 308 /* 309 * Print out the current state of the 310 * message without altering anything. 311 */ 312 rewind(collf); 313 printf("-------\nMessage contains:\n"); 314 puthead(hp, stdout, GTO|GSUBJECT|GCC|GBCC|GNL); 315 while ((t = getc(collf)) != EOF) 316 (void) putchar(t); 317 goto cont; 318 case '|': 319 /* 320 * Pipe message through command. 321 * Collect output as new message. 322 */ 323 rewind(collf); 324 mespipe(collf, &linebuf[2]); 325 goto cont; 326 case 'v': 327 case 'e': 328 /* 329 * Edit the current message. 330 * 'e' means to use EDITOR 331 * 'v' means to use VISUAL 332 */ 333 rewind(collf); 334 mesedit(collf, c); 335 goto cont; 336 } 337 } 338 goto out; 339 err: 340 if (collf != NULL) { 341 Fclose(collf); 342 collf = NULL; 343 } 344 out: 345 if (collf != NULL) 346 rewind(collf); 347 noreset--; 348 sigblock(sigmask(SIGINT) | sigmask(SIGHUP)); 349 signal(SIGINT, saveint); 350 signal(SIGHUP, savehup); 351 signal(SIGTSTP, savetstp); 352 signal(SIGTTOU, savettou); 353 signal(SIGTTIN, savettin); 354 sigsetmask(omask); 355 return collf; 356 } 357 358 /* 359 * Write a file, ex-like if f set. 360 */ 361 362 exwrite(name, fp, f) 363 char name[]; 364 FILE *fp; 365 { 366 register FILE *of; 367 register int c; 368 long cc; 369 int lc; 370 struct stat junk; 371 372 if (f) { 373 printf("\"%s\" ", name); 374 fflush(stdout); 375 } 376 if (stat(name, &junk) >= 0 && (junk.st_mode & S_IFMT) == S_IFREG) { 377 if (!f) 378 fprintf(stderr, "%s: ", name); 379 fprintf(stderr, "File exists\n"); 380 return(-1); 381 } 382 if ((of = Fopen(name, "w")) == NULL) { 383 perror(NOSTR); 384 return(-1); 385 } 386 lc = 0; 387 cc = 0; 388 while ((c = getc(fp)) != EOF) { 389 cc++; 390 if (c == '\n') 391 lc++; 392 (void) putc(c, of); 393 if (ferror(of)) { 394 perror(name); 395 Fclose(of); 396 return(-1); 397 } 398 } 399 Fclose(of); 400 printf("%d/%ld\n", lc, cc); 401 fflush(stdout); 402 return(0); 403 } 404 405 /* 406 * Edit the message being collected on fp. 407 * On return, make the edit file the new temp file. 408 */ 409 mesedit(fp, c) 410 FILE *fp; 411 { 412 sig_t sigint = signal(SIGINT, SIG_IGN); 413 FILE *nf = run_editor(fp, (off_t)-1, c, 0); 414 415 if (nf != NULL) { 416 fseek(nf, (off_t)0, 2); 417 collf = nf; 418 Fclose(fp); 419 } 420 (void) signal(SIGINT, sigint); 421 } 422 423 /* 424 * Pipe the message through the command. 425 * Old message is on stdin of command; 426 * New message collected from stdout. 427 * Sh -c must return 0 to accept the new message. 428 */ 429 mespipe(fp, cmd) 430 FILE *fp; 431 char cmd[]; 432 { 433 FILE *nf; 434 sig_t sigint = signal(SIGINT, SIG_IGN); 435 extern char tempEdit[]; 436 437 if ((nf = Fopen(tempEdit, "w+")) == NULL) { 438 perror(tempEdit); 439 goto out; 440 } 441 (void) unlink(tempEdit); 442 /* 443 * stdin = current message. 444 * stdout = new message. 445 */ 446 if (run_command(cmd, 0, fileno(fp), fileno(nf), NOSTR) < 0) { 447 (void) Fclose(nf); 448 goto out; 449 } 450 if (fsize(nf) == 0) { 451 fprintf(stderr, "No bytes from \"%s\" !?\n", cmd); 452 (void) Fclose(nf); 453 goto out; 454 } 455 /* 456 * Take new files. 457 */ 458 (void) fseek(nf, 0L, 2); 459 collf = nf; 460 (void) Fclose(fp); 461 out: 462 (void) signal(SIGINT, sigint); 463 } 464 465 /* 466 * Interpolate the named messages into the current 467 * message, preceding each line with a tab. 468 * Return a count of the number of characters now in 469 * the message, or -1 if an error is encountered writing 470 * the message temporary. The flag argument is 'm' if we 471 * should shift over and 'f' if not. 472 */ 473 forward(ms, fp, f) 474 char ms[]; 475 FILE *fp; 476 { 477 register int *msgvec; 478 extern char tempMail[]; 479 struct ignoretab *ig; 480 char *tabst; 481 482 msgvec = (int *) salloc((msgCount+1) * sizeof *msgvec); 483 if (msgvec == (int *) NOSTR) 484 return(0); 485 if (getmsglist(ms, msgvec, 0) < 0) 486 return(0); 487 if (*msgvec == 0) { 488 *msgvec = first(0, MMNORM); 489 if (*msgvec == NULL) { 490 printf("No appropriate messages\n"); 491 return(0); 492 } 493 msgvec[1] = NULL; 494 } 495 if (f == 'f' || f == 'F') 496 tabst = NOSTR; 497 else if ((tabst = value("indentprefix")) == NOSTR) 498 tabst = "\t"; 499 ig = isupper(f) ? NULL : ignore; 500 printf("Interpolating:"); 501 for (; *msgvec != 0; msgvec++) { 502 struct message *mp = message + *msgvec - 1; 503 504 touch(mp); 505 printf(" %d", *msgvec); 506 if (send(mp, fp, ig, tabst) < 0) { 507 perror(tempMail); 508 return(-1); 509 } 510 } 511 printf("\n"); 512 return(0); 513 } 514 515 /* 516 * Print (continue) when continued after ^Z. 517 */ 518 /*ARGSUSED*/ 519 void 520 collstop(s) 521 { 522 sig_t old_action = signal(s, SIG_DFL); 523 524 sigsetmask(sigblock(0) & ~sigmask(s)); 525 kill(0, s); 526 sigblock(sigmask(s)); 527 signal(s, old_action); 528 if (colljmp_p) { 529 colljmp_p = 0; 530 hadintr = 0; 531 longjmp(colljmp, 1); 532 } 533 } 534 535 /* 536 * On interrupt, come here to save the partial message in ~/dead.letter. 537 * Then jump out of the collection loop. 538 */ 539 /*ARGSUSED*/ 540 void 541 collint(s) 542 { 543 /* 544 * the control flow is subtle, because we can be called from ~q. 545 */ 546 if (!hadintr) { 547 if (value("ignore") != NOSTR) { 548 puts("@"); 549 fflush(stdout); 550 clearerr(stdin); 551 return; 552 } 553 hadintr = 1; 554 longjmp(colljmp, 1); 555 } 556 rewind(collf); 557 if (value("nosave") == NOSTR) 558 savedeadletter(collf); 559 longjmp(collabort, 1); 560 } 561 562 /*ARGSUSED*/ 563 void 564 collhup(s) 565 { 566 rewind(collf); 567 savedeadletter(collf); 568 /* 569 * Let's pretend nobody else wants to clean up, 570 * a true statement at this time. 571 */ 572 exit(1); 573 } 574 575 savedeadletter(fp) 576 register FILE *fp; 577 { 578 register FILE *dbuf; 579 register int c; 580 char *cp; 581 582 if (fsize(fp) == 0) 583 return; 584 cp = getdeadletter(); 585 c = umask(077); 586 dbuf = Fopen(cp, "a"); 587 (void) umask(c); 588 if (dbuf == NULL) 589 return; 590 while ((c = getc(fp)) != EOF) 591 (void) putc(c, dbuf); 592 Fclose(dbuf); 593 rewind(fp); 594 } 595