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