collect.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 1998 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* 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.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* mailx -- a modified version of a University of California at Berkeley
* mail program
*
* Collect input from standard input, handling
* ~ escapes.
*/
#include "rcv.h"
#include <locale.h>
#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(register char *linebuf, register 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;
register 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 = getline(linebuf,LINESIZE,stdin,&hasnulls)) == NULL) {
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)
{
register 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)
{
register 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;
register int t;
void (*sigint)(int);
#ifdef SIGCONT
void (*sigcont)(int);
#endif
struct stat sbuf;
register 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[])
{
register 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)
{
register 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 == NULL) {
*msgvec = first(0, MMNORM);
if (*msgvec == NULL) {
printf(gettext("No appropriate messages\n"));
return(0);
}
msgvec[1] = NULL;
}
if (tolower(f) == 'm')
indentprefix = value("indentprefix");
printf(gettext("Interpolating:"));
for (ip = msgvec; *ip != NULL; 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)
{
register FILE *dbuf;
register 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
getline(char *line, int size, FILE *f, int *hasnulls)
{
register 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[])
{
register 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)
{
register 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[])
{
register 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)
{
register 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(register char *linebuf, register int nread)
{
register 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);
}