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