the old _PATH_MAILDIR path had a trailing slash; fix it
[unix-history] / usr / src / usr.bin / mail / collect.c
CommitLineData
9552e6b8
DF
1/*
2 * Copyright (c) 1980 Regents of the University of California.
0c5f72fb
KB
3 * All rights reserved.
4 *
f15db449 5 * %sccs.include.redist.c%
9552e6b8
DF
6 */
7
acfc7e9b 8#ifndef lint
f15db449 9static char sccsid[] = "@(#)collect.c 5.21 (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
KS
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
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
KS
47 struct header *hp;
48{
d33aa50d 49 FILE *fbuf;
2ee3bce2
EW
50 int lc, cc, escape, eofcount;
51 int collint(), collhup(), collstop();
06300dd9
KS
52 register int c, t;
53 char linebuf[LINESIZE], *cp;
54 extern char tempMail[];
4e161d3b 55 char getsub;
828615a1 56 int omask;
06300dd9 57
828615a1 58 collf = NULL;
828615a1
EW
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)
2ee3bce2 65 signal(SIGINT, collint);
828615a1 66 if ((savehup = signal(SIGHUP, SIG_IGN)) != SIG_IGN)
2ee3bce2
EW
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)) {
828615a1 72 remove(tempMail);
06300dd9
KS
73 goto err;
74 }
828615a1
EW
75 sigsetmask(omask & ~(sigmask(SIGINT) | sigmask(SIGHUP)));
76
2ee3bce2 77 noreset++;
d33aa50d 78 if ((collf = fopen(tempMail, "w+")) == NULL) {
06300dd9 79 perror(tempMail);
06300dd9
KS
80 goto err;
81 }
d33aa50d 82 unlink(tempMail);
06300dd9
KS
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 */
06300dd9 89 t = GTO|GSUBJECT|GCC|GNL;
4e161d3b 90 getsub = 0;
686f6134 91 if (hp->h_subject == NOSTR && value("interactive") != NOSTR &&
638cc92d 92 (value("ask") != NOSTR || value("asksub") != NOSTR))
4e161d3b 93 t &= ~GNL, getsub++;
3d6f01e5 94 if (printheaders) {
06300dd9
KS
95 puthead(hp, stdout, t);
96 fflush(stdout);
97 }
06300dd9
KS
98 if ((cp = value("escape")) != NOSTR)
99 escape = *cp;
686f6134
EW
100 else
101 escape = ESCAPE;
2ee3bce2 102 eofcount = 0;
828615a1
EW
103 hadintr = 0;
104
2ee3bce2 105 if (!setjmp(colljmp)) {
828615a1 106 if (getsub)
4e161d3b 107 grabh(hp, GSUBJECT);
828615a1
EW
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 */
114cont:
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);
4e161d3b 122 }
828615a1
EW
123 }
124 for (;;) {
2ee3bce2
EW
125 colljmp_p = 1;
126 c = readline(stdin, linebuf, LINESIZE);
127 colljmp_p = 0;
128 if (c < 0) {
686f6134 129 if (value("interactive") != NOSTR &&
2ee3bce2 130 value("ignoreeof") != NOSTR && ++eofcount < 25) {
828615a1 131 printf("Use \".\" to terminate letter\n");
0fa68535
KS
132 continue;
133 }
06300dd9 134 break;
0fa68535 135 }
2ee3bce2 136 eofcount = 0;
06300dd9 137 hadintr = 0;
686f6134
EW
138 if (linebuf[0] == '.' && linebuf[1] == '\0' &&
139 value("interactive") != NOSTR &&
b068a67e 140 (value("dot") != NOSTR || value("ignoreeof") != NOSTR))
06300dd9 141 break;
686f6134 142 if (linebuf[0] != escape || value("interactive") == NOSTR) {
d33aa50d 143 if (putline(collf, linebuf) < 0)
06300dd9
KS
144 goto err;
145 continue;
146 }
147 c = linebuf[1];
06300dd9
KS
148 switch (c) {
149 default:
150 /*
151 * On double escape, just send the single one.
152 * Otherwise, it's an error.
153 */
06300dd9 154 if (c == escape) {
d33aa50d 155 if (putline(collf, &linebuf[1]) < 0)
06300dd9
KS
156 goto err;
157 else
158 break;
159 }
160 printf("Unknown tilde escape.\n");
161 break;
06300dd9
KS
162 case 'C':
163 /*
164 * Dump core.
165 */
06300dd9
KS
166 core();
167 break;
06300dd9
KS
168 case '!':
169 /*
170 * Shell escape, send the balance of the
171 * line to sh -c.
172 */
06300dd9
KS
173 shell(&linebuf[2]);
174 break;
06300dd9 175 case ':':
06300dd9
KS
176 /*
177 * Escape to command mode, but be nice!
178 */
343c874e 179 execute(&linebuf[2], 1);
828615a1 180 goto cont;
06300dd9
KS
181 case '.':
182 /*
183 * Simulate end of file on input.
184 */
828615a1 185 goto out;
06300dd9 186 case 'q':
06300dd9
KS
187 /*
188 * Force a quit of sending mail.
189 * Act like an interrupt happened.
190 */
06300dd9 191 hadintr++;
2ee3bce2 192 collint(SIGINT);
06300dd9 193 exit(1);
06300dd9
KS
194 case 'h':
195 /*
196 * Grab a bunch of headers.
197 */
06300dd9 198 grabh(hp, GTO|GSUBJECT|GCC|GBCC);
828615a1 199 goto cont;
06300dd9
KS
200 case 't':
201 /*
202 * Add to the To list.
203 */
3d6f01e5 204 hp->h_to = cat(hp->h_to, extract(&linebuf[2], GTO));
06300dd9 205 break;
06300dd9
KS
206 case 's':
207 /*
208 * Set the Subject list.
209 */
06300dd9 210 cp = &linebuf[2];
828615a1 211 while (isspace(*cp))
06300dd9
KS
212 cp++;
213 hp->h_subject = savestr(cp);
06300dd9 214 break;
06300dd9
KS
215 case 'c':
216 /*
217 * Add to the CC list.
218 */
3d6f01e5 219 hp->h_cc = cat(hp->h_cc, extract(&linebuf[2], GCC));
06300dd9 220 break;
06300dd9
KS
221 case 'b':
222 /*
223 * Add stuff to blind carbon copies list.
224 */
3d6f01e5 225 hp->h_bcc = cat(hp->h_bcc, extract(&linebuf[2], GBCC));
06300dd9 226 break;
06300dd9 227 case 'd':
62e28b79 228 strcpy(linebuf + 2, getdeadletter());
06300dd9 229 /* fall into . . . */
06300dd9
KS
230 case 'r':
231 /*
232 * Invoke a file:
233 * Search for the file name,
d33aa50d 234 * then open it and copy the contents to collf.
06300dd9 235 */
06300dd9 236 cp = &linebuf[2];
828615a1 237 while (isspace(*cp))
06300dd9
KS
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)) {
828615a1 247 printf("%s: Directory\n", cp);
06300dd9
KS
248 break;
249 }
250 if ((fbuf = fopen(cp, "r")) == NULL) {
251 perror(cp);
252 break;
253 }
254 printf("\"%s\" ", cp);
80187484 255 fflush(stdout);
06300dd9
KS
256 lc = 0;
257 cc = 0;
2ee3bce2 258 while (readline(fbuf, linebuf, LINESIZE) >= 0) {
06300dd9 259 lc++;
d33aa50d 260 if ((t = putline(collf, linebuf)) < 0) {
06300dd9
KS
261 fclose(fbuf);
262 goto err;
263 }
264 cc += t;
265 }
266 fclose(fbuf);
267 printf("%d/%d\n", lc, cc);
268 break;
06300dd9
KS
269 case 'w':
270 /*
271 * Write the message on a file.
272 */
06300dd9 273 cp = &linebuf[2];
470c33f3 274 while (*cp == ' ' || *cp == '\t')
06300dd9
KS
275 cp++;
276 if (*cp == '\0') {
277 fprintf(stderr, "Write what file!?\n");
278 break;
279 }
280 if ((cp = expand(cp)) == NOSTR)
281 break;
d33aa50d
EW
282 rewind(collf);
283 exwrite(cp, collf, 1);
06300dd9 284 break;
06300dd9 285 case 'm':
2de8fc95 286 case 'M':
06300dd9 287 case 'f':
2de8fc95 288 case 'F':
06300dd9
KS
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 */
470c33f3 295 if (forward(linebuf + 2, collf, c) < 0)
06300dd9 296 goto err;
828615a1 297 goto cont;
06300dd9 298 case '?':
435e8dff
KB
299 if ((fbuf = fopen(_PATH_TILDE, "r")) == NULL) {
300 perror(_PATH_TILDE);
06300dd9
KS
301 break;
302 }
4ee3e718 303 while ((t = getc(fbuf)) != EOF)
2ee3bce2 304 (void) putchar(t);
06300dd9
KS
305 fclose(fbuf);
306 break;
06300dd9
KS
307 case 'p':
308 /*
309 * Print out the current state of the
310 * message without altering anything.
311 */
d33aa50d 312 rewind(collf);
06300dd9
KS
313 printf("-------\nMessage contains:\n");
314 puthead(hp, stdout, GTO|GSUBJECT|GCC|GBCC|GNL);
d33aa50d 315 while ((t = getc(collf)) != EOF)
2ee3bce2 316 (void) putchar(t);
828615a1 317 goto cont;
06300dd9
KS
318 case '|':
319 /*
320 * Pipe message through command.
321 * Collect output as new message.
322 */
d33aa50d
EW
323 rewind(collf);
324 mespipe(collf, &linebuf[2]);
828615a1 325 goto cont;
06300dd9
KS
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 */
d33aa50d
EW
333 rewind(collf);
334 mesedit(collf, c);
828615a1 335 goto cont;
06300dd9
KS
336 }
337 }
828615a1 338 goto out;
06300dd9 339err:
d33aa50d
EW
340 if (collf != NULL) {
341 fclose(collf);
342 collf = NULL;
828615a1
EW
343 }
344out:
d33aa50d
EW
345 if (collf != NULL)
346 rewind(collf);
2ee3bce2
EW
347 noreset--;
348 sigblock(sigmask(SIGINT) | sigmask(SIGHUP));
828615a1
EW
349 signal(SIGINT, saveint);
350 signal(SIGHUP, savehup);
2ee3bce2
EW
351 signal(SIGTSTP, savetstp);
352 signal(SIGTTOU, savettou);
353 signal(SIGTTIN, savettin);
828615a1 354 sigsetmask(omask);
d33aa50d 355 return collf;
06300dd9
KS
356}
357
358/*
359 * Write a file, ex-like if f set.
360 */
361
828615a1 362exwrite(name, fp, f)
06300dd9 363 char name[];
828615a1 364 FILE *fp;
06300dd9
KS
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 }
6d1cdf8d 376 if (stat(name, &junk) >= 0 && (junk.st_mode & S_IFMT) == S_IFREG) {
06300dd9
KS
377 if (!f)
378 fprintf(stderr, "%s: ", name);
828615a1 379 fprintf(stderr, "File exists\n");
06300dd9
KS
380 return(-1);
381 }
382 if ((of = fopen(name, "w")) == NULL) {
383 perror(NOSTR);
384 return(-1);
385 }
386 lc = 0;
387 cc = 0;
828615a1 388 while ((c = getc(fp)) != EOF) {
06300dd9
KS
389 cc++;
390 if (c == '\n')
391 lc++;
2ee3bce2 392 (void) putc(c, of);
06300dd9
KS
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/*
828615a1 406 * Edit the message being collected on fp.
06300dd9
KS
407 * On return, make the edit file the new temp file.
408 */
828615a1
EW
409mesedit(fp, c)
410 FILE *fp;
06300dd9 411{
392fe950 412 sig_t sigint = signal(SIGINT, SIG_IGN);
d33aa50d 413 FILE *nf = run_editor(fp, (off_t)-1, c, 0);
06300dd9 414
d33aa50d
EW
415 if (nf != NULL) {
416 fseek(nf, (off_t)0, 2);
417 collf = nf;
418 fclose(fp);
06300dd9 419 }
d33aa50d 420 (void) signal(SIGINT, sigint);
06300dd9
KS
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 */
828615a1
EW
429mespipe(fp, cmd)
430 FILE *fp;
06300dd9
KS
431 char cmd[];
432{
d33aa50d 433 FILE *nf;
392fe950 434 sig_t sigint = signal(SIGINT, SIG_IGN);
001d60ad 435 extern char tempEdit[];
06300dd9 436
828615a1 437 if ((nf = fopen(tempEdit, "w+")) == NULL) {
06300dd9 438 perror(tempEdit);
d33aa50d 439 goto out;
06300dd9 440 }
d33aa50d
EW
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;
06300dd9 449 }
828615a1 450 if (fsize(nf) == 0) {
06300dd9 451 fprintf(stderr, "No bytes from \"%s\" !?\n", cmd);
d33aa50d
EW
452 (void) fclose(nf);
453 goto out;
06300dd9 454 }
06300dd9
KS
455 /*
456 * Take new files.
457 */
d33aa50d 458 (void) fseek(nf, 0L, 2);
828615a1 459 collf = nf;
d33aa50d
EW
460 (void) fclose(fp);
461out:
462 (void) signal(SIGINT, sigint);
06300dd9
KS
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 */
828615a1 473forward(ms, fp, f)
06300dd9 474 char ms[];
828615a1 475 FILE *fp;
06300dd9 476{
470c33f3 477 register int *msgvec;
06300dd9 478 extern char tempMail[];
2de8fc95
EW
479 struct ignoretab *ig;
480 char *tabst;
06300dd9
KS
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);
470c33f3 487 if (*msgvec == 0) {
06300dd9
KS
488 *msgvec = first(0, MMNORM);
489 if (*msgvec == NULL) {
490 printf("No appropriate messages\n");
491 return(0);
492 }
493 msgvec[1] = NULL;
494 }
2de8fc95
EW
495 if (f == 'f' || f == 'F')
496 tabst = NOSTR;
0f642f7e 497 else if ((tabst = value("indentprefix")) == NOSTR)
2de8fc95
EW
498 tabst = "\t";
499 ig = isupper(f) ? NULL : ignore;
06300dd9 500 printf("Interpolating:");
470c33f3
EW
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) {
2de8fc95
EW
507 perror(tempMail);
508 return(-1);
509 }
06300dd9
KS
510 }
511 printf("\n");
512 return(0);
513}
514
2d55c063
KS
515/*
516 * Print (continue) when continued after ^Z.
517 */
828615a1 518/*ARGSUSED*/
2ee3bce2 519collstop(s)
2d55c063 520{
392fe950 521 sig_t old_action = signal(s, SIG_DFL);
2d55c063 522
2ee3bce2
EW
523 sigsetmask(sigblock(0) & ~sigmask(s));
524 kill(0, s);
525 sigblock(sigmask(s));
526 signal(s, old_action);
527 if (colljmp_p) {
528 colljmp_p = 0;
529 hadintr = 0;
530 longjmp(colljmp, 1);
531 }
2d55c063
KS
532}
533
06300dd9 534/*
2ee3bce2
EW
535 * On interrupt, come here to save the partial message in ~/dead.letter.
536 * Then jump out of the collection loop.
06300dd9 537 */
2ee3bce2
EW
538/*ARGSUSED*/
539collint(s)
06300dd9 540{
2ee3bce2
EW
541 /*
542 * the control flow is subtle, because we can be called from ~q.
543 */
544 if (!hadintr) {
545 if (value("ignore") != NOSTR) {
546 puts("@");
547 fflush(stdout);
548 clearerr(stdin);
549 return;
550 }
828615a1 551 hadintr = 1;
2ee3bce2 552 longjmp(colljmp, 1);
06300dd9 553 }
828615a1 554 rewind(collf);
2ee3bce2 555 if (value("nosave") == NOSTR)
62e28b79 556 savedeadletter(collf);
2ee3bce2
EW
557 longjmp(collabort, 1);
558}
559
560/*ARGSUSED*/
561collhup(s)
562{
563 rewind(collf);
564 savedeadletter(collf);
565 /*
566 * Let's pretend nobody else wants to clean up,
567 * a true statement at this time.
568 */
569 exit(1);
06300dd9
KS
570}
571
62e28b79
EW
572savedeadletter(fp)
573 register FILE *fp;
574{
575 register FILE *dbuf;
576 register int c;
577 char *cp;
578
579 if (fsize(fp) == 0)
580 return;
581 cp = getdeadletter();
582 c = umask(077);
583 dbuf = fopen(cp, "a");
2ee3bce2 584 (void) umask(c);
62e28b79
EW
585 if (dbuf == NULL)
586 return;
587 while ((c = getc(fp)) != EOF)
2ee3bce2 588 (void) putc(c, dbuf);
62e28b79
EW
589 fclose(dbuf);
590 rewind(fp);
591}