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