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