/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 1985, 2010, Oracle and/or its affiliates. All rights reserved. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ /* * mailx -- a modified version of a University of California at Berkeley * mail program * * Collect input from standard input, handling * ~ escapes. */ #include "rcv.h" #include #ifdef SIGCONT static void collcont(int); #endif static void collrub(int s); static void cpout(char *str, FILE *ofd); static int exwrite(char name[], FILE *ibuf); static int forward(char ms[], FILE *obuf, int f); static void intack(int); static int forward(char ms[], FILE *obuf, int f); static FILE *mesedit(FILE *ibuf, FILE *obuf, int c, struct header *hp); static FILE *mespipe(FILE *ibuf, FILE *obuf, char cmd[]); static void resetsigs(int resethup); static int stripnulls(char *linebuf, int nread); static void xhalt(void); static char **Xaddone(char **hf, char news[]); static int tabputs(const char *line, FILE *obuf); /* * 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 * mailx if stdio allowed simultaneous read/write access. */ static void (*savesig)(int); /* Previous SIGINT value */ static void (*savehup)(int); /* Previous SIGHUP value */ #ifdef SIGCONT static void (*savecont)(int); /* Previous SIGCONT value */ #endif static FILE *newi; /* File for saving away */ static FILE *newo; /* Output side of same */ static int ignintr; /* Ignore interrups */ static int hadintr; /* Have seen one SIGINT so far */ static struct header *savehp; static jmp_buf coljmp; /* To get back to work */ FILE * collect(struct header *hp) { FILE *ibuf, *fbuf, *obuf; int escape, eof; long lc, cc; int c, t; int hdrs; char linebuf[LINESIZE+1], *cp; char *iprompt; int inhead; void (*sigpipe)(int), (*sigint)(int); int fd = -1; noreset++; ibuf = obuf = NULL; newi = newo = NULL; if ((fd = open(tempMail, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0 || (obuf = fdopen(fd, "w")) == NULL) { perror(tempMail); goto err; } newo = obuf; if ((ibuf = fopen(tempMail, "r")) == NULL) { perror(tempMail); newo = NULL; fclose(obuf); goto err; } newi = ibuf; removefile(tempMail); ignintr = (int)value("ignore"); hadintr = 1; inhead = 1; savehp = hp; # ifdef VMUNIX if ((savesig = sigset(SIGINT, SIG_IGN)) != SIG_IGN) sigset(SIGINT, ignintr ? intack : collrub), sigblock(sigmask(SIGINT)); if ((savehup = sigset(SIGHUP, SIG_IGN)) != SIG_IGN) sigset(SIGHUP, collrub), sigblock(sigmask(SIGHUP)); # else /* VMUNIX */ # ifdef OLD_BSD_SIGS if ((savesig = sigset(SIGINT, SIG_IGN)) != SIG_IGN) sigset(SIGINT, ignintr ? intack : collrub); if ((savehup = sigset(SIGHUP, SIG_IGN)) != SIG_IGN) sigset(SIGHUP, collrub); # else if ((savesig = sigset(SIGINT, SIG_IGN)) != SIG_IGN) { sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGINT); sigset(SIGINT, ignintr ? intack : collrub); sigprocmask(SIG_BLOCK, &mask, NULL); } if ((savehup = sigset(SIGHUP, SIG_IGN)) != SIG_IGN) { sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGHUP); sigset(SIGHUP, collrub); sigprocmask(SIG_BLOCK, &mask, NULL); } # endif # endif /* VMUNIX */ #ifdef SIGCONT savecont = sigset(SIGCONT, collcont); #endif /* * If we are going to prompt for subject/cc/bcc, * refrain from printing a newline after * the headers (since some people mind). */ if (hp->h_subject == NOSTR) { hp->h_subject = sflag; sflag = NOSTR; } if (hp->h_cc == NOSTR) { hp->h_cc = cflag; cflag = NOSTR; } if (hp->h_bcc == NOSTR) { hp->h_bcc = bflag; bflag = NOSTR; } t = GMASK; hdrs = 0; if (intty && !tflag) { if (hp->h_to == NOSTR) hdrs |= GTO; if (hp->h_subject == NOSTR && value("asksub")) hdrs |= GSUBJECT; if (hp->h_cc == NOSTR && value("askcc")) hdrs |= GCC; if (hp->h_bcc == NOSTR && value("askbcc")) hdrs |= GBCC; if (hdrs) t &= ~GNL; } if (hp->h_seq != 0) { puthead(hp, stdout, t, 0); fflush(stdout); } if (setjmp(coljmp)) goto err; escape = SENDESC; if ((cp = value("escape")) != NOSTR) escape = *cp; eof = 0; if ((cp = value("MAILX_HEAD")) != NOSTR) { cpout( cp, obuf); if (isatty(fileno(stdin))) cpout( cp, stdout); } iprompt = value("iprompt"); fflush(obuf); hadintr = 0; for (;;) { int nread, hasnulls; # ifdef VMUNIX int omask = sigblock(0) &~ (sigmask(SIGINT)|sigmask(SIGHUP)); # else # ifndef OLD_BSD_SIGS sigset_t omask; sigprocmask(0, NULL, &omask); sigdelset(&omask, SIGINT); sigdelset(&omask, SIGHUP); # endif # endif setjmp(coljmp); # ifdef VMUNIX sigsetmask(omask); # else /* VMUNIX */ # ifdef OLD_BSD_SIGS sigrelse(SIGINT); sigrelse(SIGHUP); # else sigprocmask(SIG_SETMASK, &omask, NULL); # endif # endif /* VMUNIX */ if (intty && !tflag && outtty && iprompt) fputs(iprompt, stdout); flush(); if (hdrs) { grabh(hp, hdrs, 1); hdrs = 0; continue; } if ((nread = getaline(linebuf,LINESIZE,stdin,&hasnulls)) == 0) { if (intty && value("ignoreeof") != NOSTR) { if (++eof > 35) break; printf(gettext( "Use \".\" to terminate letter\n")); continue; } break; } eof = 0; hadintr = 0; if (intty && equal(".\n", linebuf) && (value("dot") != NOSTR || value("ignoreeof") != NOSTR)) break; /* * If -t, scan text for headers. */ if (tflag) { char *cp2; if (!inhead) { writeit: if (write(fileno(obuf),linebuf,nread) != nread) goto werr; continue; } if (linebuf[0] == '\n') { /* got blank line after header, ignore it */ inhead = 0; continue; } if (!headerp(linebuf)) { /* got non-header line, save it */ inhead = 0; goto writeit; } if (hasnulls) nread = stripnulls(linebuf, nread); for (;;) { char line2[LINESIZE]; c = getc(stdin); ungetc(c, stdin); if (!isspace(c) || c == '\n') break; if (readline(stdin, line2) < 0) break; for (cp2 = line2; *cp2 != 0 && isspace(*cp2); cp2++) ; if (strlen(linebuf) + strlen(cp2) >= (unsigned)LINESIZE-2) break; cp = &linebuf[strlen(linebuf)]; while (cp > linebuf && (isspace(cp[-1]) || cp[-1] == '\\')) cp--; *cp++ = ' '; strcpy(cp, cp2); } if ((c = strlen(linebuf)) > 0) { cp = &linebuf[c-1]; while (cp > linebuf && isspace(*cp)) cp--; *++cp = 0; } if (ishfield(linebuf, "to")) hp->h_to = addto(hp->h_to, hcontents(linebuf)); else if (ishfield(linebuf, "subject")) hp->h_subject = addone(hp->h_subject, hcontents(linebuf)); else if (ishfield(linebuf, "cc")) hp->h_cc = addto(hp->h_cc, hcontents(linebuf)); else if (ishfield(linebuf, "bcc")) hp->h_bcc = addto(hp->h_bcc, hcontents(linebuf)); else if (ishfield(linebuf, "default-options")) hp->h_defopt = addone(hp->h_defopt, hcontents(linebuf)); else hp->h_others = Xaddone(hp->h_others, linebuf); hp->h_seq++; continue; } if ((linebuf[0] != escape) || (rflag != NOSTR) || (!intty && !(int)value("escapeok"))) { if (write(fileno(obuf),linebuf,nread) != nread) goto werr; continue; } /* * On double escape, just send the single one. */ if ((nread > 1) && (linebuf[1] == escape)) { if (write(fileno(obuf),linebuf+1,nread-1) != (nread-1)) goto werr; continue; } if (hasnulls) nread = stripnulls(linebuf, nread); c = linebuf[1]; linebuf[nread - 1] = '\0'; switch (c) { default: /* * Otherwise, it's an error. */ printf(gettext("Unknown tilde escape.\n")); break; case 'a': case 'A': /* * autograph; sign the letter. */ if (cp = value(c=='a' ? "sign":"Sign")) { if (*cp) cpout( cp, obuf); if (isatty(fileno(stdin))) { if (*cp) cpout( cp, stdout); } } break; case 'i': /* * insert string */ for (cp = &linebuf[2]; any(*cp, " \t"); cp++) ; if (*cp) cp = value(cp); if (cp != NOSTR) { if (*cp) cpout(cp, obuf); if (isatty(fileno(stdout))) { if (*cp) cpout(cp, stdout); } } 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); iprompt = value("iprompt"); if (cp = value("escape")) escape = *cp; printf(gettext("(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); /* NOTREACHED */ case 'x': xhalt(); break; /* not reached */ case 'h': /* * Grab a bunch of headers. */ if (!intty || !outtty) { printf(gettext("~h: no can do!?\n")); break; } grabh(hp, GMASK, (int)value("bsdcompat")); printf(gettext("(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 'R': hp->h_defopt = addone(hp->h_defopt, myname); hp->h_seq++; fprintf(stderr, gettext("Return receipt marked.\n")); receipt_flg = 1; break; case 'd': copy(Getf("DEAD"), &linebuf[2]); /* FALLTHROUGH */ case '<': case 'r': { int ispip; /* * Invoke a file: * Search for the file name, * then open it and copy the contents to obuf. * * if name begins with '!', read from a command */ cp = &linebuf[2]; while (any(*cp, " \t")) cp++; if (*cp == '\0') { printf(gettext("Interpolate what file?\n")); break; } if (*cp=='!') { /* take input from a command */ ispip = 1; if ((fbuf = npopen(++cp, "r"))==NULL) { perror(""); break; } sigint = sigset(SIGINT, SIG_IGN); } else { ispip = 0; cp = expand(cp); if (cp == NOSTR) break; if (isdir(cp)) { printf(gettext("%s: directory\n"), cp); break; } if ((fbuf = fopen(cp, "r")) == NULL) { perror(cp); break; } } printf("\"%s\" ", cp); flush(); lc = cc = 0; while ((t = getc(fbuf)) != EOF) { if (t == '\n') lc++; if (putc(t, obuf) == EOF) { if (ispip) { npclose(fbuf); sigset(SIGINT, sigint); } else fclose(fbuf); goto werr; } cc++; } if (ispip) { npclose(fbuf); sigset(SIGINT, sigint); } else fclose(fbuf); printf("%ld/%ld\n", lc, cc); fflush(obuf); break; } case 'w': /* * Write the message on a file. */ cp = &linebuf[2]; while (any(*cp, " \t")) cp++; if (*cp == '\0') { fprintf(stderr, gettext("Write what file!?\n")); break; } if ((cp = expand(cp)) == NOSTR) break; fflush(obuf); rewind(ibuf); exwrite(cp, ibuf); break; case 'm': case 'M': case 'f': case 'F': /* * Interpolate the named messages, if we * are in receiving mail mode. Does the * standard list processing garbage. * If ~f or ~F is given, we don't shift over. */ if (!rcvmode) { printf(gettext( "No messages to send from!?!\n")); break; } cp = &linebuf[2]; while (any(*cp, " \t")) cp++; if (forward(cp, obuf, c) < 0) goto werr; fflush(obuf); printf(gettext("(continue)\n")); break; case '?': if ((fbuf = fopen(THELPFILE, "r")) == NULL) { printf(gettext("No help just now.\n")); 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. */ int nlines; extern jmp_buf pipestop; extern void brokpipe(int); fflush(obuf); rewind(ibuf); fbuf = stdout; if (setjmp(pipestop)) goto ret0; if (intty && outtty && (cp = value("crt")) != NOSTR) { nlines = (*cp == '\0' ? screensize() : atoi(cp)) - 7; /* 7 for hdr lines */ while ((t = getc(ibuf)) != EOF) { if (t == '\n') if (--nlines <= 0) break; } rewind(ibuf); if (nlines <= 0) { fbuf = npopen(MORE, "w"); if (fbuf == NULL) { perror(MORE); fbuf = stdout; } else { sigint = sigset(SIGINT, SIG_IGN); sigpipe = sigset(SIGPIPE, brokpipe); } } } fprintf(fbuf, gettext("-------\nMessage contains:\n")); puthead(hp, fbuf, GMASK, 0); while ((t = getc(ibuf))!=EOF) putc(t, fbuf); ret0: if (fbuf != stdout) { npclose(fbuf); sigset(SIGPIPE, sigpipe); sigset(SIGINT, sigint); } printf(gettext("(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(gettext("(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, hp)) == NULL) goto err; newo = obuf; ibuf = newi; printf(gettext("(continue)\n")); break; } fflush(obuf); } eofl: fflush(obuf); if ((cp = value("MAILX_TAIL")) != NOSTR) { cpout( cp, obuf); if (isatty(fileno(stdin))) cpout( cp, stdout); } fclose(obuf); rewind(ibuf); resetsigs(0); noreset = 0; return(ibuf); werr: /* * Write error occurred on tmp file, save partial * message in dead.letter. */ perror(tempMail); fflush(obuf); rewind(ibuf); if (fsize(ibuf) > 0) { char *deadletter; deadletter = Getf("DEAD"); fprintf(stderr, gettext("Saving partial message in %s\n"), deadletter); if ((fbuf = fopen(deadletter, value("appenddeadletter") == NOSTR ? "w" : "a")) != NULL) { chmod(deadletter, DEADPERM); puthead(hp, fbuf, GMASK|GCLEN, fsize(ibuf)); lcwrite(deadletter, ibuf, fbuf, value("appenddeadletter") != NOSTR); fclose(fbuf); } else perror(deadletter); } err: if (ibuf != NULL) fclose(ibuf); if (obuf != NULL) fclose(obuf); resetsigs(0); noreset = 0; return(NULL); } static void resetsigs(int resethup) { (void) sigset(SIGINT, savesig); if (resethup) (void) sigset(SIGHUP, savehup); #ifdef SIGCONT # ifdef preSVr4 (void) sigset(SIGCONT, savecont); # else { struct sigaction nsig; nsig.sa_handler = (void (*)())savecont; sigemptyset(&nsig.sa_mask); nsig.sa_flags = SA_RESTART; (void) sigaction(SIGCONT, &nsig, (struct sigaction*)0); } # endif #endif } /* * Write a file ex-like. */ static int exwrite(char name[], FILE *ibuf) { FILE *of; struct stat junk; void (*sigint)(int), (*sigpipe)(int); int pi = (*name == '!'); if ((of = pi ? npopen(++name, "w") : fopen(name, "a")) == NULL) { perror(name); return(-1); } if (pi) { sigint = sigset(SIGINT, SIG_IGN); sigpipe = sigset(SIGPIPE, SIG_IGN); } lcwrite(name, ibuf, of, 0); pi ? npclose(of) : fclose(of); if (pi) { sigset(SIGPIPE, sigpipe); sigset(SIGINT, sigint); } return(0); } void lcwrite(char *fn, FILE *fi, FILE *fo, int addnl) { int c; long lc, cc; printf("\"%s\" ", fn); fflush(stdout); lc = cc = 0; while ((c = getc(fi)) != EOF) { cc++; if (putc(c, fo) == '\n') lc++; if (ferror(fo)) { perror(""); return; } } if (addnl) { putc('\n', fo); lc++; cc++; } fflush(fo); if (fferror(fo)) { perror(""); return; } printf("%ld/%ld\n", lc, cc); fflush(stdout); } /* * 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. */ static FILE * mesedit(FILE *ibuf, FILE *obuf, int c, struct header *hp) { pid_t pid; FILE *fbuf; int t; void (*sigint)(int); #ifdef SIGCONT void (*sigcont)(int); #endif struct stat sbuf; char *edit; char hdr[LINESIZE]; char *oto, *osubject, *occ, *obcc, **oothers; int fd = -1; if (stat(tempEdit, &sbuf) >= 0) { printf(gettext("%s: file exists\n"), tempEdit); goto out; } if ((fd = open(tempEdit, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0 || (fbuf = fdopen(fd, "w")) == NULL) { perror(tempEdit); goto out; } fflush(obuf); rewind(ibuf); puthead(hp, fbuf, GMASK, 0); while ((t = getc(ibuf)) != EOF) putc(t, fbuf); fflush(fbuf); if (fferror(fbuf)) { perror(tempEdit); removefile(tempEdit); goto out; } fclose(fbuf); if ((edit = value(c == 'e' ? "EDITOR" : "VISUAL")) == NOSTR || *edit == '\0') edit = c == 'e' ? EDITOR : VISUAL; edit = safeexpand(edit); /* * Fork/execlp the editor on the edit file */ pid = vfork(); if (pid == (pid_t)-1) { perror("fork"); removefile(tempEdit); goto out; } if (pid == 0) { char ecmd[BUFSIZ]; char *Shell; sigchild(); execlp(edit, edit, tempEdit, (char *)0); /* * If execlp fails, "edit" might really be a complete * shell command, not a simple pathname. Try using * the shell to run it. */ snprintf(ecmd, sizeof (ecmd), "exec %s %s", edit, tempEdit); if ((Shell = value("SHELL")) == NULL || *Shell=='\0') Shell = SHELL; execlp(Shell, Shell, "-c", ecmd, NULL); perror(edit); _exit(1); } sigint = sigset(SIGINT, SIG_IGN); #ifdef SIGCONT sigcont = sigset(SIGCONT, SIG_DFL); #endif while (wait((int *)0) != pid) ; sigset(SIGINT, sigint); #ifdef SIGCONT sigset(SIGCONT, sigcont); #endif /* * Now switch to new file. */ if ((fbuf = fopen(tempEdit, "r")) == NULL) { perror(tempEdit); removefile(tempEdit); goto out; } removefile(tempEdit); /* save the old headers, in case they are accidentally deleted */ osubject = hp->h_subject; oto = hp->h_to; occ = hp->h_cc; obcc = hp->h_bcc; oothers = hp->h_others; hp->h_to = hp->h_subject = hp->h_cc = hp->h_bcc = hp->h_defopt = NOSTR; hp->h_others = NOSTRPTR; hp->h_seq = 0; while (gethfield(fbuf, hdr, 9999L) > 0) { if (ishfield(hdr, "to")) hp->h_to = addto(hp->h_to, hcontents(hdr)); else if (ishfield(hdr, "subject")) hp->h_subject = addone(hp->h_subject, hcontents(hdr)); else if (ishfield(hdr, "cc")) hp->h_cc = addto(hp->h_cc, hcontents(hdr)); else if (ishfield(hdr, "bcc")) hp->h_bcc = addto(hp->h_bcc, hcontents(hdr)); else if (ishfield(hdr, "default-options")) hp->h_defopt = addone(hp->h_defopt, hcontents(hdr)); else hp->h_others = Xaddone(hp->h_others, hdr); hp->h_seq++; } if (hp->h_seq == 0) { /* if we didn't see any headers, restore the original headers */ hp->h_subject = osubject; hp->h_to = oto; hp->h_cc = occ; hp->h_bcc = obcc; hp->h_others = oothers; printf(gettext( "(Deleted headers restored to original values)\n")); } if ((fd = open(tempMail, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0 || (obuf = fdopen(fd, "w")) == NULL) { perror(tempMail); fclose(fbuf); goto out; } if ((ibuf = fopen(tempMail, "r")) == NULL) { perror(tempMail); removefile(tempMail); fclose(fbuf); fclose(obuf); goto out; } removefile(tempMail); if (strlen(hdr) != 0) { fputs(hdr, obuf); putc('\n', obuf); } while ((t = getc(fbuf)) != EOF) putc(t, obuf); fclose(fbuf); fclose(newo); fclose(newi); newo = obuf; newi = ibuf; out: return(newo); } /* * 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. */ static FILE * mespipe(FILE *ibuf, FILE *obuf, char cmd[]) { FILE *ni, *no; pid_t pid; int s; void (*sigint)(int); char *Shell; int fd = -1; newi = ibuf; if ((fd = open(tempEdit, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0 || (no = fdopen(fd, "w")) == NULL) { perror(tempEdit); return(obuf); } if ((ni = fopen(tempEdit, "r")) == NULL) { perror(tempEdit); fclose(no); removefile(tempEdit); return(obuf); } removefile(tempEdit); fflush(obuf); rewind(ibuf); if ((Shell = value("SHELL")) == NULL || *Shell=='\0') Shell = SHELL; if ((pid = vfork()) == (pid_t)-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); execlp(Shell, Shell, "-c", cmd, (char *)0); perror(Shell); _exit(1); } sigint = sigset(SIGINT, SIG_IGN); while (wait(&s) != pid) ; sigset(SIGINT, sigint); if (s != 0 || pid == (pid_t)-1) { fprintf(stderr, gettext("\"%s\" failed!?\n"), cmd); goto err; } if (fsize(ni) == 0) { fprintf(stderr, gettext("No bytes from \"%s\" !?\n"), cmd); goto err; } /* * Take new files. */ newi = ni; fclose(ibuf); fclose(obuf); return(no); err: fclose(no); fclose(ni); return(obuf); } static char *indentprefix; /* used instead of tab by tabputs */ /* * 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. */ static int forward(char ms[], FILE *obuf, int f) { int *msgvec, *ip; msgvec = (int *) salloc((msgCount+1) * sizeof *msgvec); if (msgvec == NOINTPTR) return(0); if (getmsglist(ms, msgvec, 0) < 0) return(0); if (*msgvec == 0) { *msgvec = first(0, MMNORM); if (*msgvec == 0) { printf(gettext("No appropriate messages\n")); return(0); } msgvec[1] = 0; } if (tolower(f) == 'm') indentprefix = value("indentprefix"); printf(gettext("Interpolating:")); for (ip = msgvec; *ip != 0; ip++) { touch(*ip); printf(" %d", *ip); if (msend(&message[*ip-1], obuf, islower(f) ? M_IGNORE : 0, tolower(f) == 'm' ? tabputs : fputs) < 0) { perror(tempMail); return(-1); } } fflush(obuf); if (fferror(obuf)) { perror(tempMail); return(-1); } printf("\n"); return(0); } static int tabputs(const char *line, FILE *obuf) { if (indentprefix) fputs(indentprefix, obuf); /* Don't create lines with only a tab on them */ else if (line[0] != '\n') fputc('\t', obuf); return (fputs(line, obuf)); } /* * Print (continue) when continued after ^Z. */ #ifdef SIGCONT static void #ifdef __cplusplus collcont(int) #else /* ARGSUSED */ collcont(int s) #endif { printf(gettext("(continue)\n")); fflush(stdout); } #endif /* SIGCONT */ /* * 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. */ static void collrub(int s) { FILE *dbuf; char *deadletter; # ifdef OLD_BSD_SIGS if (s == SIGHUP) sigignore(SIGHUP); # endif if (s == SIGINT && hadintr == 0) { hadintr++; fflush(stdout); fprintf(stderr, gettext("\n(Interrupt -- one more to kill letter)\n")); # ifdef OLD_BSD_SIGS sigrelse(s); # endif longjmp(coljmp, 1); } fclose(newo); rewind(newi); if (s == SIGINT && value("save")==NOSTR || fsize(newi) == 0) goto done; deadletter = Getf("DEAD"); if ((dbuf = fopen(deadletter, (value("appenddeadletter") == NOSTR ? "w" : "a"))) == NULL) { perror(deadletter); goto done; } chmod(deadletter, DEADPERM); puthead(savehp, dbuf, GMASK|GCLEN, fsize(newi)); lcwrite(deadletter, newi, dbuf, value("appenddeadletter") != NOSTR); fclose(dbuf); done: fclose(newi); resetsigs(1); if (rcvmode) { if (s == SIGHUP) hangup(s); else stop(s); } else exit(1); } /* * Acknowledge an interrupt signal from the tty by typing an @ */ static void #ifdef __cplusplus intack(int) #else /* ARGSUSED */ intack(int s) #endif { puts("@"); fflush(stdout); clearerr(stdin); longjmp(coljmp,1); } /* Read line from stdin, noting any NULL characters. Return the number of characters read. Note that the buffer passed must be 1 larger than "size" for the trailing NUL byte. */ int getaline(char *line, int size, FILE *f, int *hasnulls) { int i, ch; for (i = 0; (i < size) && ((ch=getc(f)) != EOF); ) { if ( ch == '\0' ) *hasnulls = 1; if ((line[i++] = (char)ch) == '\n') break; } line[i] = '\0'; return(i); } void #ifdef __cplusplus savedead(int) #else /* ARGSUSED */ savedead(int s) #endif { collrub(SIGINT); exit(1); /* NOTREACHED */ } /* * Add a list of addresses to the end of a header entry field. */ char * addto(char hf[], char news[]) { char name[LINESIZE]; int comma = docomma(news); while (news = yankword(news, name, sizeof (name), comma)) { nstrcat(name, sizeof (name), ", "); hf = addone(hf, name); } return hf; } /* * Add a string to the end of a header entry field. */ char * addone(char hf[], char news[]) { char *cp, *cp2, *linebuf; if (hf == NOSTR) hf = savestr(""); if (*news == '\0') return(hf); linebuf = (char *)srealloc(hf, (unsigned)(strlen(hf) + strlen(news) + 2)); cp2 = strchr(linebuf, '\0'); if (cp2 > linebuf && cp2[-1] != ' ') *cp2++ = ' '; for (cp = news; any(*cp, " \t"); cp++) ; while (*cp != '\0') *cp2++ = *cp++; *cp2 = '\0'; return(linebuf); } static int nptrs(char **hf) { int i; if (!hf) return(0); for (i = 0; *hf; hf++) i++; return(i); } /* * Add a non-standard header to the end of the non-standard headers. */ static char ** Xaddone(char **hf, char news[]) { char *linebuf; char **ohf = hf; int nhf = nptrs(hf); if (hf == NOSTRPTR) hf = (char**)salloc(sizeof(char*) * 2); else hf = (char**)srealloc(hf, sizeof(char*) * (nhf + 2)); if (hf == NOSTRPTR) { fprintf(stderr, gettext("No room, header lost: %s\n"), news); return(ohf); } linebuf = (char *)salloc((unsigned)(strlen(news) + 1)); strcpy(linebuf, news); hf[nhf++] = linebuf; hf[nhf] = NOSTR; return(hf); } static void cpout(char *str, FILE *ofd) { char *cp = str; while (*cp) { if (*cp == '\\') { switch (*(cp+1)) { case 'n': putc('\n', ofd); cp++; break; case 't': putc('\t', ofd); cp++; break; default: putc('\\', ofd); } } else { putc(*cp, ofd); } cp++; } putc('\n', ofd); fflush(ofd); } static void xhalt(void) { fclose(newo); fclose(newi); sigset(SIGINT, savesig); sigset(SIGHUP, savehup); if (rcvmode) stop(0); exit(1); /* NOTREACHED */ } /* * Strip the nulls from a buffer of length n */ static int stripnulls(char *linebuf, int nread) { int i, j; for (i = 0; i < nread; i++) if (linebuf[i] == '\0') break; for (j = i; j < nread; j++) if (linebuf[j] != '\0') linebuf[i++] = linebuf[j]; linebuf[i] = '\0'; return(i); }