|
|
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 provided
6: * that: (1) source distributions retain this entire copyright notice and
7: * comment, and (2) distributions including binaries display the following
8: * acknowledgement: ``This product includes software developed by the
9: * University of California, Berkeley and its contributors'' in the
10: * documentation or other materials provided with the distribution and in
11: * all advertising materials mentioning features or use of this software.
12: * Neither the name of the University nor the names of its contributors may
13: * be used to endorse or promote products derived from this software without
14: * specific prior written permission.
15: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
16: * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
17: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
18: */
19:
20: #ifndef lint
21: static char sccsid[] = "@(#)collect.c 5.22 (Berkeley) 6/25/90";
22: #endif /* not lint */
23:
24: /*
25: * Mail -- a mail program
26: *
27: * Collect input from standard input, handling
28: * ~ escapes.
29: */
30:
31: #include "rcv.h"
32: #include <sys/stat.h>
33:
34: /*
35: * Read a message from standard output and return a read file to it
36: * or NULL on error.
37: */
38:
39: /*
40: * The following hokiness with global variables is so that on
41: * receipt of an interrupt signal, the partial message can be salted
42: * away on dead.letter.
43: */
44:
45: static sig_t saveint; /* Previous SIGINT value */
46: static sig_t savehup; /* Previous SIGHUP value */
47: static sig_t savetstp; /* Previous SIGTSTP value */
48: static sig_t savettou; /* Previous SIGTTOU value */
49: static sig_t savettin; /* Previous SIGTTIN value */
50: static FILE *collf; /* File for saving away */
51: static int hadintr; /* Have seen one SIGINT so far */
52:
53: static jmp_buf colljmp; /* To get back to work */
54: static int colljmp_p; /* whether to long jump */
55: static jmp_buf collabort; /* To end collection with error */
56:
57: FILE *
58: collect(hp, printheaders)
59: struct header *hp;
60: {
61: FILE *fbuf;
62: int lc, cc, escape, eofcount;
63: int collint(), collhup(), collstop();
64: register int c, t;
65: char linebuf[LINESIZE], *cp;
66: extern char tempMail[];
67: char getsub;
68: int omask;
69:
70: collf = NULL;
71: /*
72: * Start catching signals from here, but we're still die on interrupts
73: * until we're in the main loop.
74: */
75: omask = sigblock(sigmask(SIGINT) | sigmask(SIGHUP));
76: if ((saveint = signal(SIGINT, SIG_IGN)) != SIG_IGN)
77: signal(SIGINT, collint);
78: if ((savehup = signal(SIGHUP, SIG_IGN)) != SIG_IGN)
79: signal(SIGHUP, collhup);
80: savetstp = signal(SIGTSTP, collstop);
81: savettou = signal(SIGTTOU, collstop);
82: savettin = signal(SIGTTIN, collstop);
83: if (setjmp(collabort) || setjmp(colljmp)) {
84: remove(tempMail);
85: goto err;
86: }
87: sigsetmask(omask & ~(sigmask(SIGINT) | sigmask(SIGHUP)));
88:
89: noreset++;
90: if ((collf = Fopen(tempMail, "w+")) == NULL) {
91: perror(tempMail);
92: goto err;
93: }
94: unlink(tempMail);
95:
96: /*
97: * If we are going to prompt for a subject,
98: * refrain from printing a newline after
99: * the headers (since some people mind).
100: */
101: t = GTO|GSUBJECT|GCC|GNL;
102: getsub = 0;
103: if (hp->h_subject == NOSTR && value("interactive") != NOSTR &&
104: (value("ask") != NOSTR || value("asksub") != NOSTR))
105: t &= ~GNL, getsub++;
106: if (printheaders) {
107: puthead(hp, stdout, t);
108: fflush(stdout);
109: }
110: if ((cp = value("escape")) != NOSTR)
111: escape = *cp;
112: else
113: escape = ESCAPE;
114: eofcount = 0;
115: hadintr = 0;
116:
117: if (!setjmp(colljmp)) {
118: if (getsub)
119: grabh(hp, GSUBJECT);
120: } else {
121: /*
122: * Come here for printing the after-signal message.
123: * Duplicate messages won't be printed because
124: * the write is aborted if we get a SIGTTOU.
125: */
126: cont:
127: if (hadintr) {
128: fflush(stdout);
129: fprintf(stderr,
130: "\n(Interrupt -- one more to kill letter)\n");
131: } else {
132: printf("(continue)\n");
133: fflush(stdout);
134: }
135: }
136: for (;;) {
137: colljmp_p = 1;
138: c = readline(stdin, linebuf, LINESIZE);
139: colljmp_p = 0;
140: if (c < 0) {
141: if (value("interactive") != NOSTR &&
142: value("ignoreeof") != NOSTR && ++eofcount < 25) {
143: printf("Use \".\" to terminate letter\n");
144: continue;
145: }
146: break;
147: }
148: eofcount = 0;
149: hadintr = 0;
150: if (linebuf[0] == '.' && linebuf[1] == '\0' &&
151: value("interactive") != NOSTR &&
152: (value("dot") != NOSTR || value("ignoreeof") != NOSTR))
153: break;
154: if (linebuf[0] != escape || value("interactive") == NOSTR) {
155: if (putline(collf, linebuf) < 0)
156: goto err;
157: continue;
158: }
159: c = linebuf[1];
160: switch (c) {
161: default:
162: /*
163: * On double escape, just send the single one.
164: * Otherwise, it's an error.
165: */
166: if (c == escape) {
167: if (putline(collf, &linebuf[1]) < 0)
168: goto err;
169: else
170: break;
171: }
172: printf("Unknown tilde escape.\n");
173: break;
174: case 'C':
175: /*
176: * Dump core.
177: */
178: core();
179: break;
180: case '!':
181: /*
182: * Shell escape, send the balance of the
183: * line to sh -c.
184: */
185: shell(&linebuf[2]);
186: break;
187: case ':':
188: /*
189: * Escape to command mode, but be nice!
190: */
191: execute(&linebuf[2], 1);
192: goto cont;
193: case '.':
194: /*
195: * Simulate end of file on input.
196: */
197: goto out;
198: case 'q':
199: /*
200: * Force a quit of sending mail.
201: * Act like an interrupt happened.
202: */
203: hadintr++;
204: collint(SIGINT);
205: exit(1);
206: case 'h':
207: /*
208: * Grab a bunch of headers.
209: */
210: grabh(hp, GTO|GSUBJECT|GCC|GBCC);
211: goto cont;
212: case 't':
213: /*
214: * Add to the To list.
215: */
216: hp->h_to = cat(hp->h_to, extract(&linebuf[2], GTO));
217: break;
218: case 's':
219: /*
220: * Set the Subject list.
221: */
222: cp = &linebuf[2];
223: while (isspace(*cp))
224: cp++;
225: hp->h_subject = savestr(cp);
226: break;
227: case 'c':
228: /*
229: * Add to the CC list.
230: */
231: hp->h_cc = cat(hp->h_cc, extract(&linebuf[2], GCC));
232: break;
233: case 'b':
234: /*
235: * Add stuff to blind carbon copies list.
236: */
237: hp->h_bcc = cat(hp->h_bcc, extract(&linebuf[2], GBCC));
238: break;
239: case 'd':
240: strcpy(linebuf + 2, getdeadletter());
241: /* fall into . . . */
242: case 'r':
243: /*
244: * Invoke a file:
245: * Search for the file name,
246: * then open it and copy the contents to collf.
247: */
248: cp = &linebuf[2];
249: while (isspace(*cp))
250: cp++;
251: if (*cp == '\0') {
252: printf("Interpolate what file?\n");
253: break;
254: }
255: cp = expand(cp);
256: if (cp == NOSTR)
257: break;
258: if (isdir(cp)) {
259: printf("%s: Directory\n", cp);
260: break;
261: }
262: if ((fbuf = Fopen(cp, "r")) == NULL) {
263: perror(cp);
264: break;
265: }
266: printf("\"%s\" ", cp);
267: fflush(stdout);
268: lc = 0;
269: cc = 0;
270: while (readline(fbuf, linebuf, LINESIZE) >= 0) {
271: lc++;
272: if ((t = putline(collf, linebuf)) < 0) {
273: Fclose(fbuf);
274: goto err;
275: }
276: cc += t;
277: }
278: Fclose(fbuf);
279: printf("%d/%d\n", lc, cc);
280: break;
281: case 'w':
282: /*
283: * Write the message on a file.
284: */
285: cp = &linebuf[2];
286: while (*cp == ' ' || *cp == '\t')
287: cp++;
288: if (*cp == '\0') {
289: fprintf(stderr, "Write what file!?\n");
290: break;
291: }
292: if ((cp = expand(cp)) == NOSTR)
293: break;
294: rewind(collf);
295: exwrite(cp, collf, 1);
296: break;
297: case 'm':
298: case 'M':
299: case 'f':
300: case 'F':
301: /*
302: * Interpolate the named messages, if we
303: * are in receiving mail mode. Does the
304: * standard list processing garbage.
305: * If ~f is given, we don't shift over.
306: */
307: if (forward(linebuf + 2, collf, c) < 0)
308: goto err;
309: goto cont;
310: case '?':
311: if ((fbuf = Fopen(_PATH_TILDE, "r")) == NULL) {
312: perror(_PATH_TILDE);
313: break;
314: }
315: while ((t = getc(fbuf)) != EOF)
316: (void) putchar(t);
317: Fclose(fbuf);
318: break;
319: case 'p':
320: /*
321: * Print out the current state of the
322: * message without altering anything.
323: */
324: rewind(collf);
325: printf("-------\nMessage contains:\n");
326: puthead(hp, stdout, GTO|GSUBJECT|GCC|GBCC|GNL);
327: while ((t = getc(collf)) != EOF)
328: (void) putchar(t);
329: goto cont;
330: case '|':
331: /*
332: * Pipe message through command.
333: * Collect output as new message.
334: */
335: rewind(collf);
336: mespipe(collf, &linebuf[2]);
337: goto cont;
338: case 'v':
339: case 'e':
340: /*
341: * Edit the current message.
342: * 'e' means to use EDITOR
343: * 'v' means to use VISUAL
344: */
345: rewind(collf);
346: mesedit(collf, c);
347: goto cont;
348: }
349: }
350: goto out;
351: err:
352: if (collf != NULL) {
353: Fclose(collf);
354: collf = NULL;
355: }
356: out:
357: if (collf != NULL)
358: rewind(collf);
359: noreset--;
360: sigblock(sigmask(SIGINT) | sigmask(SIGHUP));
361: signal(SIGINT, saveint);
362: signal(SIGHUP, savehup);
363: signal(SIGTSTP, savetstp);
364: signal(SIGTTOU, savettou);
365: signal(SIGTTIN, savettin);
366: sigsetmask(omask);
367: return collf;
368: }
369:
370: /*
371: * Write a file, ex-like if f set.
372: */
373:
374: exwrite(name, fp, f)
375: char name[];
376: FILE *fp;
377: {
378: register FILE *of;
379: register int c;
380: long cc;
381: int lc;
382: struct stat junk;
383:
384: if (f) {
385: printf("\"%s\" ", name);
386: fflush(stdout);
387: }
388: if (stat(name, &junk) >= 0 && (junk.st_mode & S_IFMT) == S_IFREG) {
389: if (!f)
390: fprintf(stderr, "%s: ", name);
391: fprintf(stderr, "File exists\n");
392: return(-1);
393: }
394: if ((of = Fopen(name, "w")) == NULL) {
395: perror(NOSTR);
396: return(-1);
397: }
398: lc = 0;
399: cc = 0;
400: while ((c = getc(fp)) != EOF) {
401: cc++;
402: if (c == '\n')
403: lc++;
404: (void) putc(c, of);
405: if (ferror(of)) {
406: perror(name);
407: Fclose(of);
408: return(-1);
409: }
410: }
411: Fclose(of);
412: printf("%d/%ld\n", lc, cc);
413: fflush(stdout);
414: return(0);
415: }
416:
417: /*
418: * Edit the message being collected on fp.
419: * On return, make the edit file the new temp file.
420: */
421: mesedit(fp, c)
422: FILE *fp;
423: {
424: sig_t sigint = signal(SIGINT, SIG_IGN);
425: FILE *nf = run_editor(fp, (off_t)-1, c, 0);
426:
427: if (nf != NULL) {
428: fseek(nf, (off_t)0, 2);
429: collf = nf;
430: Fclose(fp);
431: }
432: (void) signal(SIGINT, sigint);
433: }
434:
435: /*
436: * Pipe the message through the command.
437: * Old message is on stdin of command;
438: * New message collected from stdout.
439: * Sh -c must return 0 to accept the new message.
440: */
441: mespipe(fp, cmd)
442: FILE *fp;
443: char cmd[];
444: {
445: FILE *nf;
446: sig_t sigint = signal(SIGINT, SIG_IGN);
447: extern char tempEdit[];
448:
449: if ((nf = Fopen(tempEdit, "w+")) == NULL) {
450: perror(tempEdit);
451: goto out;
452: }
453: (void) unlink(tempEdit);
454: /*
455: * stdin = current message.
456: * stdout = new message.
457: */
458: if (run_command(cmd, 0, fileno(fp), fileno(nf), NOSTR) < 0) {
459: (void) Fclose(nf);
460: goto out;
461: }
462: if (fsize(nf) == 0) {
463: fprintf(stderr, "No bytes from \"%s\" !?\n", cmd);
464: (void) Fclose(nf);
465: goto out;
466: }
467: /*
468: * Take new files.
469: */
470: (void) fseek(nf, 0L, 2);
471: collf = nf;
472: (void) Fclose(fp);
473: out:
474: (void) signal(SIGINT, sigint);
475: }
476:
477: /*
478: * Interpolate the named messages into the current
479: * message, preceding each line with a tab.
480: * Return a count of the number of characters now in
481: * the message, or -1 if an error is encountered writing
482: * the message temporary. The flag argument is 'm' if we
483: * should shift over and 'f' if not.
484: */
485: forward(ms, fp, f)
486: char ms[];
487: FILE *fp;
488: {
489: register int *msgvec;
490: extern char tempMail[];
491: struct ignoretab *ig;
492: char *tabst;
493:
494: msgvec = (int *) salloc((msgCount+1) * sizeof *msgvec);
495: if (msgvec == (int *) NOSTR)
496: return(0);
497: if (getmsglist(ms, msgvec, 0) < 0)
498: return(0);
499: if (*msgvec == 0) {
500: *msgvec = first(0, MMNORM);
501: if (*msgvec == NULL) {
502: printf("No appropriate messages\n");
503: return(0);
504: }
505: msgvec[1] = NULL;
506: }
507: if (f == 'f' || f == 'F')
508: tabst = NOSTR;
509: else if ((tabst = value("indentprefix")) == NOSTR)
510: tabst = "\t";
511: ig = isupper(f) ? NULL : ignore;
512: printf("Interpolating:");
513: for (; *msgvec != 0; msgvec++) {
514: struct message *mp = message + *msgvec - 1;
515:
516: touch(mp);
517: printf(" %d", *msgvec);
518: if (send(mp, fp, ig, tabst) < 0) {
519: perror(tempMail);
520: return(-1);
521: }
522: }
523: printf("\n");
524: return(0);
525: }
526:
527: /*
528: * Print (continue) when continued after ^Z.
529: */
530: /*ARGSUSED*/
531: collstop(s)
532: {
533: sig_t old_action = signal(s, SIG_DFL);
534:
535: sigsetmask(sigblock(0) & ~sigmask(s));
536: kill(0, s);
537: sigblock(sigmask(s));
538: signal(s, old_action);
539: if (colljmp_p) {
540: colljmp_p = 0;
541: hadintr = 0;
542: longjmp(colljmp, 1);
543: }
544: }
545:
546: /*
547: * On interrupt, come here to save the partial message in ~/dead.letter.
548: * Then jump out of the collection loop.
549: */
550: /*ARGSUSED*/
551: collint(s)
552: {
553: /*
554: * the control flow is subtle, because we can be called from ~q.
555: */
556: if (!hadintr) {
557: if (value("ignore") != NOSTR) {
558: puts("@");
559: fflush(stdout);
560: clearerr(stdin);
561: return;
562: }
563: hadintr = 1;
564: longjmp(colljmp, 1);
565: }
566: rewind(collf);
567: if (value("nosave") == NOSTR)
568: savedeadletter(collf);
569: longjmp(collabort, 1);
570: }
571:
572: /*ARGSUSED*/
573: collhup(s)
574: {
575: rewind(collf);
576: savedeadletter(collf);
577: /*
578: * Let's pretend nobody else wants to clean up,
579: * a true statement at this time.
580: */
581: exit(1);
582: }
583:
584: savedeadletter(fp)
585: register FILE *fp;
586: {
587: register FILE *dbuf;
588: register int c;
589: char *cp;
590:
591: if (fsize(fp) == 0)
592: return;
593: cp = getdeadletter();
594: c = umask(077);
595: dbuf = Fopen(cp, "a");
596: (void) umask(c);
597: if (dbuf == NULL)
598: return;
599: while ((c = getc(fp)) != EOF)
600: (void) putc(c, dbuf);
601: Fclose(dbuf);
602: rewind(fp);
603: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.