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