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