/* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ #ifndef lint static char *sccsid = "@(#)collect.c 5.2 (Berkeley) 6/21/85"; #endif not lint /* * Mail -- a mail program * * Collect input from standard input, handling * ~ escapes. */ #include "rcv.h" #include /* * Read a message from standard output and return a read file to it * or NULL on error. */ /* * The following hokiness with global variables is so that on * receipt of an interrupt signal, the partial message can be salted * away on dead.letter. The output file must be available to flush, * and the input to read. Several open files could be saved all through * Mail if stdio allowed simultaneous read/write access. */ static int (*savesig)(); /* Previous SIGINT value */ static int (*savehup)(); /* Previous SIGHUP value */ # ifdef VMUNIX static int (*savecont)(); /* Previous SIGCONT value */ # endif VMUNIX static FILE *newi; /* File for saving away */ static FILE *newo; /* Output side of same */ static int hf; /* Ignore interrups */ static int hadintr; /* Have seen one SIGINT so far */ static jmp_buf coljmp; /* To get back to work */ FILE * collect(hp) struct header *hp; { FILE *ibuf, *fbuf, *obuf; int lc, cc, escape, collrub(), intack(), collhup, collcont(), eof; register int c, t; char linebuf[LINESIZE], *cp; extern char tempMail[]; int notify(); extern collintsig(), collhupsig(); char getsub; noreset++; ibuf = obuf = NULL; if (value("ignore") != NOSTR) hf = 1; else hf = 0; hadintr = 0; # ifdef VMUNIX if ((savesig = sigset(SIGINT, SIG_IGN)) != SIG_IGN) sigset(SIGINT, hf ? intack : collrub), sigblock(sigmask(SIGINT)); if ((savehup = sigset(SIGHUP, SIG_IGN)) != SIG_IGN) sigset(SIGHUP, collrub), sigblock(sigmask(SIGHUP)); savecont = sigset(SIGCONT, collcont); # else VMUNIX savesig = signal(SIGINT, SIG_IGN); savehup = signal(SIGHUP, SIG_IGN); # endif VMUNIX newi = NULL; newo = NULL; if ((obuf = fopen(tempMail, "w")) == NULL) { perror(tempMail); goto err; } newo = obuf; if ((ibuf = fopen(tempMail, "r")) == NULL) { perror(tempMail); newo = NULL; fclose(obuf); goto err; } newi = ibuf; remove(tempMail); /* * If we are going to prompt for a subject, * refrain from printing a newline after * the headers (since some people mind). */ t = GTO|GSUBJECT|GCC|GNL; getsub = 0; if (intty && sflag == NOSTR && hp->h_subject == NOSTR && value("ask")) t &= ~GNL, getsub++; if (hp->h_seq != 0) { puthead(hp, stdout, t); fflush(stdout); } escape = ESCAPE; if ((cp = value("escape")) != NOSTR) escape = *cp; eof = 0; for (;;) { int omask = sigblock(0) &~ (sigmask(SIGINT)|sigmask(SIGHUP)); setjmp(coljmp); # ifdef VMUNIX sigsetmask(omask); # else VMUNIX if (savesig != SIG_IGN) signal(SIGINT, hf ? intack : collintsig); if (savehup != SIG_IGN) signal(SIGHUP, collhupsig); # endif VMUNIX fflush(stdout); if (getsub) { grabh(hp, GSUBJECT); getsub = 0; continue; } if (readline(stdin, linebuf) <= 0) { if (intty && value("ignoreeof") != NOSTR) { if (++eof > 35) break; printf("Use \".\" to terminate letter\n", escape); continue; } break; } eof = 0; hadintr = 0; if (intty && equal(".", linebuf) && (value("dot") != NOSTR || value("ignoreeof") != NOSTR)) break; if (linebuf[0] != escape || rflag != NOSTR) { if ((t = putline(obuf, linebuf)) < 0) goto err; continue; } c = linebuf[1]; switch (c) { default: /* * On double escape, just send the single one. * Otherwise, it's an error. */ if (c == escape) { if (putline(obuf, &linebuf[1]) < 0) goto err; else break; } printf("Unknown tilde escape.\n"); break; case 'C': /* * Dump core. */ core(); break; case '!': /* * Shell escape, send the balance of the * line to sh -c. */ shell(&linebuf[2]); break; case ':': case '_': /* * Escape to command mode, but be nice! */ execute(&linebuf[2], 1); printf("(continue)\n"); break; case '.': /* * Simulate end of file on input. */ goto eofl; case 'q': case 'Q': /* * Force a quit of sending mail. * Act like an interrupt happened. */ hadintr++; collrub(SIGINT); exit(1); case 'h': /* * Grab a bunch of headers. */ if (!intty || !outtty) { printf("~h: no can do!?\n"); break; } grabh(hp, GTO|GSUBJECT|GCC|GBCC); printf("(continue)\n"); break; case 't': /* * Add to the To list. */ hp->h_to = addto(hp->h_to, &linebuf[2]); hp->h_seq++; break; case 's': /* * Set the Subject list. */ cp = &linebuf[2]; while (any(*cp, " \t")) cp++; hp->h_subject = savestr(cp); hp->h_seq++; break; case 'c': /* * Add to the CC list. */ hp->h_cc = addto(hp->h_cc, &linebuf[2]); hp->h_seq++; break; case 'b': /* * Add stuff to blind carbon copies list. */ hp->h_bcc = addto(hp->h_bcc, &linebuf[2]); hp->h_seq++; break; case 'd': copy(deadletter, &linebuf[2]); /* fall into . . . */ case 'r': /* * Invoke a file: * Search for the file name, * then open it and copy the contents to obuf. */ cp = &linebuf[2]; while (any(*cp, " \t")) cp++; if (*cp == '\0') { printf("Interpolate what file?\n"); break; } cp = expand(cp); if (cp == NOSTR) break; if (isdir(cp)) { printf("%s: directory\n"); break; } if ((fbuf = fopen(cp, "r")) == NULL) { perror(cp); break; } printf("\"%s\" ", cp); fflush(stdout); lc = 0; cc = 0; while (readline(fbuf, linebuf) > 0) { lc++; if ((t = putline(obuf, linebuf)) < 0) { fclose(fbuf); goto err; } cc += t; } fclose(fbuf); printf("%d/%d\n", lc, cc); break; case 'w': /* * Write the message on a file. */ cp = &linebuf[2]; while (any(*cp, " \t")) cp++; if (*cp == '\0') { fprintf(stderr, "Write what file!?\n"); break; } if ((cp = expand(cp)) == NOSTR) break; fflush(obuf); rewind(ibuf); exwrite(cp, ibuf, 1); break; case 'm': case 'f': /* * Interpolate the named messages, if we * are in receiving mail mode. Does the * standard list processing garbage. * If ~f is given, we don't shift over. */ if (!rcvmode) { printf("No messages to send from!?!\n"); break; } cp = &linebuf[2]; while (any(*cp, " \t")) cp++; if (forward(cp, obuf, c) < 0) goto err; printf("(continue)\n"); break; case '?': if ((fbuf = fopen(THELPFILE, "r")) == NULL) { perror(THELPFILE); break; } t = getc(fbuf); while (t != -1) { putchar(t); t = getc(fbuf); } fclose(fbuf); break; case 'p': /* * Print out the current state of the * message without altering anything. */ fflush(obuf); rewind(ibuf); printf("-------\nMessage contains:\n"); puthead(hp, stdout, GTO|GSUBJECT|GCC|GBCC|GNL); t = getc(ibuf); while (t != EOF) { putchar(t); t = getc(ibuf); } printf("(continue)\n"); break; case '^': case '|': /* * Pipe message through command. * Collect output as new message. */ obuf = mespipe(ibuf, obuf, &linebuf[2]); newo = obuf; ibuf = newi; newi = ibuf; printf("(continue)\n"); break; case 'v': case 'e': /* * Edit the current message. * 'e' means to use EDITOR * 'v' means to use VISUAL */ if ((obuf = mesedit(ibuf, obuf, c)) == NULL) goto err; newo = obuf; ibuf = newi; printf("(continue)\n"); break; } } eofl: fclose(obuf); rewind(ibuf); sigset(SIGINT, savesig); sigset(SIGHUP, savehup); # ifdef VMUNIX sigset(SIGCONT, savecont); sigsetmask(0); # endif VMUNIX noreset = 0; return(ibuf); err: if (ibuf != NULL) fclose(ibuf); if (obuf != NULL) fclose(obuf); sigset(SIGINT, savesig); sigset(SIGHUP, savehup); # ifdef VMUNIX sigset(SIGCONT, savecont); sigsetmask(0); # endif VMUNIX noreset = 0; return(NULL); } /* * Non destructively interrogate the value of the given signal. */ psig(n) { register (*wassig)(); wassig = sigset(n, SIG_IGN); sigset(n, wassig); return((int) wassig); } /* * Write a file, ex-like if f set. */ exwrite(name, ibuf, f) char name[]; FILE *ibuf; { register FILE *of; register int c; long cc; int lc; struct stat junk; if (f) { printf("\"%s\" ", name); fflush(stdout); } if (stat(name, &junk) >= 0 && (junk.st_mode & S_IFMT) == S_IFREG) { if (!f) fprintf(stderr, "%s: ", name); fprintf(stderr, "File exists\n", name); return(-1); } if ((of = fopen(name, "w")) == NULL) { perror(NOSTR); return(-1); } lc = 0; cc = 0; while ((c = getc(ibuf)) != EOF) { cc++; if (c == '\n') lc++; putc(c, of); if (ferror(of)) { perror(name); fclose(of); return(-1); } } fclose(of); printf("%d/%ld\n", lc, cc); fflush(stdout); return(0); } /* * Edit the message being collected on ibuf and obuf. * Write the message out onto some poorly-named temp file * and point an editor at it. * * On return, make the edit file the new temp file. */ FILE * mesedit(ibuf, obuf, c) FILE *ibuf, *obuf; { int pid, s; FILE *fbuf; register int t; int (*sig)(), (*scont)(), signull(); struct stat sbuf; extern char tempMail[], tempEdit[]; register char *edit; sig = sigset(SIGINT, SIG_IGN); # ifdef VMUNIX scont = sigset(SIGCONT, signull); # endif VMUNIX if (stat(tempEdit, &sbuf) >= 0) { printf("%s: file exists\n", tempEdit); goto out; } close(creat(tempEdit, 0600)); if ((fbuf = fopen(tempEdit, "w")) == NULL) { perror(tempEdit); goto out; } fflush(obuf); rewind(ibuf); t = getc(ibuf); while (t != EOF) { putc(t, fbuf); t = getc(ibuf); } fflush(fbuf); if (ferror(fbuf)) { perror(tempEdit); remove(tempEdit); goto fix; } fclose(fbuf); if ((edit = value(c == 'e' ? "EDITOR" : "VISUAL")) == NOSTR) edit = c == 'e' ? EDITOR : VISUAL; pid = vfork(); if (pid == 0) { sigchild(); if (sig != SIG_IGN) sigsys(SIGINT, SIG_DFL); execl(edit, edit, tempEdit, 0); perror(edit); _exit(1); } if (pid == -1) { perror("fork"); remove(tempEdit); goto out; } while (wait(&s) != pid) ; if ((s & 0377) != 0) { printf("Fatal error in \"%s\"\n", edit); remove(tempEdit); goto out; } /* * Now switch to new file. */ if ((fbuf = fopen(tempEdit, "a")) == NULL) { perror(tempEdit); remove(tempEdit); goto out; } if ((ibuf = fopen(tempEdit, "r")) == NULL) { perror(tempEdit); fclose(fbuf); remove(tempEdit); goto out; } remove(tempEdit); fclose(obuf); fclose(newi); obuf = fbuf; goto out; fix: perror(tempEdit); out: # ifdef VMUNIX sigset(SIGCONT, scont); # endif VMUNIX sigset(SIGINT, sig); newi = ibuf; return(obuf); } /* * Pipe the message through the command. * Old message is on stdin of command; * New message collected from stdout. * Sh -c must return 0 to accept the new message. */ FILE * mespipe(ibuf, obuf, cmd) FILE *ibuf, *obuf; char cmd[]; { register FILE *ni, *no; int pid, s; int (*savesig)(); char *Shell; newi = ibuf; if ((no = fopen(tempEdit, "w")) == NULL) { perror(tempEdit); return(obuf); } if ((ni = fopen(tempEdit, "r")) == NULL) { perror(tempEdit); fclose(no); remove(tempEdit); return(obuf); } remove(tempEdit); savesig = sigset(SIGINT, SIG_IGN); fflush(obuf); rewind(ibuf); if ((Shell = value("SHELL")) == NULL) Shell = "/bin/sh"; if ((pid = vfork()) == -1) { perror("fork"); goto err; } if (pid == 0) { /* * stdin = current message. * stdout = new message. */ sigchild(); close(0); dup(fileno(ibuf)); close(1); dup(fileno(no)); for (s = 4; s < 15; s++) close(s); execl(Shell, Shell, "-c", cmd, 0); perror(Shell); _exit(1); } while (wait(&s) != pid) ; if (s != 0 || pid == -1) { fprintf(stderr, "\"%s\" failed!?\n", cmd); goto err; } if (fsize(ni) == 0) { fprintf(stderr, "No bytes from \"%s\" !?\n", cmd); goto err; } /* * Take new files. */ newi = ni; fclose(ibuf); fclose(obuf); sigset(SIGINT, savesig); return(no); err: fclose(no); fclose(ni); sigset(SIGINT, savesig); return(obuf); } /* * Interpolate the named messages into the current * message, preceding each line with a tab. * Return a count of the number of characters now in * the message, or -1 if an error is encountered writing * the message temporary. The flag argument is 'm' if we * should shift over and 'f' if not. */ forward(ms, obuf, f) char ms[]; FILE *obuf; { register int *msgvec, *ip; extern char tempMail[]; msgvec = (int *) salloc((msgCount+1) * sizeof *msgvec); if (msgvec == (int *) NOSTR) return(0); if (getmsglist(ms, msgvec, 0) < 0) return(0); if (*msgvec == NULL) { *msgvec = first(0, MMNORM); if (*msgvec == NULL) { printf("No appropriate messages\n"); return(0); } msgvec[1] = NULL; } printf("Interpolating:"); for (ip = msgvec; *ip != NULL; ip++) { touch(*ip); printf(" %d", *ip); if (f == 'm') { if (transmit(&message[*ip-1], obuf) < 0L) { perror(tempMail); return(-1); } } else if (send(&message[*ip-1], obuf, 0) < 0) { perror(tempMail); return(-1); } } printf("\n"); return(0); } /* * Send message described by the passed pointer to the * passed output buffer. Insert a tab in front of each * line. Return a count of the characters sent, or -1 * on error. */ long transmit(mailp, obuf) struct message *mailp; FILE *obuf; { register struct message *mp; register int ch; long c, n; int bol; FILE *ibuf; mp = mailp; ibuf = setinput(mp); c = mp->m_size; n = c; bol = 1; while (c-- > 0L) { if (bol) { bol = 0; putc('\t', obuf); n++; if (ferror(obuf)) { perror("/tmp"); return(-1L); } } ch = getc(ibuf); if (ch == '\n') bol++; putc(ch, obuf); if (ferror(obuf)) { perror("/tmp"); return(-1L); } } return(n); } /* * Print (continue) when continued after ^Z. */ collcont(s) { printf("(continue)\n"); fflush(stdout); } /* * On interrupt, go here to save the partial * message on ~/dead.letter. * Then restore signals and execute the normal * signal routine. We only come here if signals * were previously set anyway. */ # ifndef VMUNIX collintsig() { signal(SIGINT, SIG_IGN); collrub(SIGINT); } collhupsig() { signal(SIGHUP, SIG_IGN); collrub(SIGHUP); } # endif VMUNIX collrub(s) { register FILE *dbuf; register int c; if (s == SIGINT && hadintr == 0) { hadintr++; fflush(stdout); fprintf(stderr, "\n(Interrupt -- one more to kill letter)\n"); longjmp(coljmp, 1); } fclose(newo); rewind(newi); if (s == SIGINT && value("nosave") != NOSTR || fsize(newi) == 0) goto done; if ((dbuf = fopen(deadletter, "w")) == NULL) goto done; chmod(deadletter, 0600); while ((c = getc(newi)) != EOF) putc(c, dbuf); fclose(dbuf); done: fclose(newi); sigset(SIGINT, savesig); sigset(SIGHUP, savehup); # ifdef VMUNIX sigset(SIGCONT, savecont); # endif VMUNIX if (rcvmode) { if (s == SIGHUP) hangup(SIGHUP); else stop(s); } else exit(1); } /* * Acknowledge an interrupt signal from the tty by typing an @ */ intack(s) { puts("@"); fflush(stdout); clearerr(stdin); } /* * Add a string to the end of a header entry field. */ char * addto(hf, news) char hf[], news[]; { register char *cp, *cp2, *linebuf; if (hf == NOSTR) hf = ""; if (*news == '\0') return(hf); linebuf = salloc(strlen(hf) + strlen(news) + 2); for (cp = hf; any(*cp, " \t"); cp++) ; for (cp2 = linebuf; *cp;) *cp2++ = *cp++; *cp2++ = ' '; for (cp = news; any(*cp, " \t"); cp++) ; while (*cp != '\0') *cp2++ = *cp++; *cp2 = '\0'; return(linebuf); }