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