/* @(#)mail.c 	(c) copyright 1986 (Dan Heller) */

#include "mush.h"

/*
 * mail.c --
 *    do_mail() 	external interface to these functions; parses options.
 *    mail_someone()    called from do_mail() to begin composing the message.
 *    start_file()      creates the editing file and reset signal catching.
 *    add_to_letter()	adds the next line to letter --determine ~ escapes.
 *    finish_up_letter()  prompts for Cc:, verifies send, adds signatures.
 *    send_it() 	expands aliases, invokes mailer, sends to record file.
 *    add_headers()	adds all headers to a FILE *, reads draft files
 *    rm_edfile()	signals are directed here. remove letter, longjmp
 *    dead_letter()     make a dead.letter if mail failed.
 */

static char Subject[BUFSIZ],To[HDRSIZ],Cc[HDRSIZ],Bcc[HDRSIZ],in_reply_to[256];
static int killme;
static u_long flags;
static SIGRET (*oldterm)(), (*oldint)(), (*oldquit)();
static int finish_up_letter(), send_it(), start_file();
static int mail_someone();
static long add_headers();
static jmp_buf cntrl_c_buf;
static char *Hfile = NULL, *edfile = NULL;
FILE *ed_fp = NULL_FILE;
char *hfile = NULL, *mktemp();

/* argc and argv could be null if coming from tool mode compose */
do_mail(n, argv, list)
register int n;   /* no need for "argc", so use the space for a variable */
register char **argv, *list;
{
    char firstchar = (argv)? **argv: 'm';
    char *to = NULL, *cc = NULL, *addcc = NULL, *bcc = NULL, *subj = NULL;
    char *route = NULL;
    char buf[HDRSIZ];
#ifndef MSGS_DYNAMIC
    char inc_list[MAXMSGS_BITS];
#else
    char *inc_list = (char *) alloca(MSGS_BITS(msg_cnt));
#endif
    u_long flgs = 0;

    if (ison(glob_flags, IS_GETTING)) {
	wprint("You must finish the letter you are editing first.\n");
	return -1;
    }
    if (ison(glob_flags, DO_PIPE)) {
	wprint("You can't pipe out of the %s command.\n", argv? *argv : "mail");
	return -1;
    }
    clear_msg_list(inc_list);
    hfile = Hfile = NULL;

    /* If piped to mail, include the messages piped */
    if (ison(glob_flags, IS_PIPE) ||
	(lower(firstchar) == 'r' && do_set(set_options, "autoinclude"))) {
	turnon(flgs, INCLUDE);
	bitput(list, inc_list, msg_cnt, =);
    }
    /* Set SIGN and DO_FORTUNE now so we can turn them off later */
    if (do_set(set_options, "autosign"))
	turnon(flgs, SIGN);
    if (do_set(set_options, "fortune"))
	turnon(flgs, DO_FORTUNE);
    while (argv && *argv && *++argv && **argv == '-') {
	n = 1;
	while (n && argv[0][n])
	    switch (argv[0][n]) {
#ifdef VERBOSE_ARG
		case 'v': turnon(flgs, VERBOSE); n++; break;
#endif /* VERBOSE_ARG */
		case 'H':
		    if (argv[1]) {
			n = 0;
			Hfile = *++argv;
		    } else {
			wprint("Must specify a file\n");
			return -1;
		    }
		when 'h':
		    if (argv[1]) {
			n = -1; /* it gets incremented below */
			hfile = savestr(*++argv);
			if (ison(glob_flags, REDIRECT)) {
			    turnoff(glob_flags, REDIRECT);
			    turnon(flgs, SEND_NOW);
			}
		    } else {
			wprint("Must specify a file containing headers\n");
			return -1;
		    }
		    /* Fall through */
		case 'E': turnon(flgs, EDIT_HDRS); n++;
		when 'e': turnon(flgs, EDIT); n++;
		when 'F': turnon(flgs, DO_FORTUNE); n++;
		when 'b':
		    if (argv[1]) {
			n = 0, bcc = *++argv;
			fix_up_addr(bcc);
		    } else {
			wprint("Must specify blind-carbon list\n");
			return -1;
		    }
		when 'c':
		    if (argv[1]) {
			n = 0, addcc = *++argv;
			fix_up_addr(addcc);
		    } else {
			wprint("Must specify carbon-copy list\n");
			return -1;
		    }
		when 's':
		    if (argv[1])
			n = 0, subj = *++argv;
		    else
			n++, turnon(flgs, NEW_SUBJECT);
		when 'i': case 'I': case 'f': {
		    int m;
		    if (!msg_cnt) {
			wprint("No message to include!\n");
			return -1;
		    }
		    if (argv[0][n] == 'i') {
			turnon(flgs, INCLUDE);
			turnoff(flgs, INCLUDE_H);
			turnoff(flgs, FORWARD);
		    } else if (argv[0][n] == 'I') {
			turnon(flgs, INCLUDE_H);
			turnoff(flgs, INCLUDE);
			turnoff(flgs, FORWARD);
		    } else if (argv[0][n] == 'f') {
			turnon(flgs, FORWARD);
			turnon(flgs, SEND_NOW);
			turnoff(flgs, INCLUDE_H);
			turnoff(flgs, INCLUDE);
		    }
		    /* "-i 3-5" or "-i3-5"  Consider the latter case first */
		    if (!argv[0][++n])
			argv++, n = 0;
		    (*argv) += n;
		    m = get_msg_list(argv, inc_list);
		    (*argv) -= n;
		    if (m == -1)
			return -1;
		    /* if there were args, then go back to the first char
		     * in the next argv
		     */
		    if (m)
			n = 0;
		    if (!n) /* n may be 0 from above! */
			argv += (m-1);
		}
		when 'U':
		    turnon(flgs, SEND_NOW);
		    n++;
		when 'u':
		    turnoff(flgs, SIGN);
		    turnoff(flgs, DO_FORTUNE);
		    n++;
		when 'r':
		    if (lower(firstchar) == 'r') {
			route = *++argv;
			n = 0;
			break;
		    }
		    /* fall thru */
		default:
		    if (argv[0][n] != '?') {
			wprint("%c: unknown option\n\n", argv[0][n]);
			return -1;
		    } else
			return help(0, "mail", cmd_help);
	    }
    }
    if (isoff(flgs, FORWARD)) {
	if (ison(flgs, SEND_NOW)) {
	    if (!hfile && !Hfile) {
		wprint("Can't send immediately without draft file.\n");
		return -1;
	    }
	    turnoff(flgs, EDIT); /* -U overrides -e */
	} else if (do_set(set_options, "autoedit"))
	    turnon(flgs, EDIT);
    } else if (ison(flgs, EDIT)) /* -e modifies -f */
	turnoff(flgs, SEND_NOW);
#ifdef VERBOSE_ARG
    if (do_set(set_options, "verbose"))
	turnon(flgs, VERBOSE);
#endif /* VERBOSE_ARG */
    *in_reply_to = *To = *Subject = *Cc = *Bcc = 0;
    if (lower(firstchar) == 'r') {
	char *in_reply_fmt, *pcc = NULL;
	to = To, cc = Cc;
	/*
	 * Generate a reply to all the messages passed to respond().  This
	 * list is different than the include-msg list above.  Get info about
	 * whom the messages were sent to for reply-all.
	 * BUG: currently, redundant addresses aren't pruned from Bcc list!
	 */
	for (n = 0; n < msg_cnt; n++)
	    if (msg_bit(list, n)) {
		if (to != To)
		    *to++ = ',', *to++ = ' ';
		(void) reply_to(n, (firstchar == 'R'), buf);
		if (strlen(buf) + (to - To) > sizeof(To) - 1) {
		    wprint("# recipients exceeded at msg %d\n", n);
		    break;
		}
		to += Strcpy(to, buf);
		if (firstchar == 'R') {
		    if (pcc = cc_to(n, buf)) {
			/* if there was a previous cc, append ", " */
			if (cc != Cc)
			    *cc++ = ',', *cc++ = ' ';
			if (strlen(pcc) + (cc - Cc) > sizeof(Cc) - 1)
			    wprint("# Cc's exceeded at msg %d\n", n);
			else
			    cc += Strcpy(cc, pcc);
		    }
		}
		/* remove redundant addresses now, or headers could get too
		 * long before the list runs out (it still might)
		 */
		rm_redundant_addrs(To, Cc);
		to = To + strlen(To);
		cc = Cc + strlen(Cc);
	    }
	/* clean up end of Cc line for replyall's */
	while (*cc == ' ' || *cc == ',')
	    *cc-- = '\0';
	if (firstchar == 'R' && !do_set(set_options, "metoo")) {
	    /* Each reply_to() call above will leave at least
	     * one person in To.  If that one person was us,
	     * we need to get removed from the complete list.
	     */
	    (void) take_me_off(to);
	}
	to = To, cc = Cc;
	if (route || (route = do_set(set_options, "auto_route")))
	    /* careful! This routine could add lots-o-bytes and lose addresses
	     * to avoid writing out of segment.
	     */
	    route_addresses(To, Cc, route);
	if (in_reply_fmt = do_set(set_options, "in_reply_to"))
	    /* "9" here is a magic # --see compose_hdr() */
	    (void) strcpy(in_reply_to,
			    format_hdr(current_msg, in_reply_fmt, FALSE)+9);
    }
    if (ison(flgs, FORWARD) && ison(flgs, EDIT) ||
	    lower(firstchar) == 'r' && isoff(flgs, NEW_SUBJECT)) {
	turnoff(flgs, NEW_SUBJECT);
	if (subj && *subj && (isoff(flgs, FORWARD) || ison(flgs, EDIT)))
	    subj = strcpy(Subject, subj);
	else if (subj = subject_to(current_msg, buf))
	    subj = strcpy(Subject, buf + 4*(lower(firstchar) != 'r'));
    } else if (isoff(flgs, NEW_SUBJECT) && isoff(flgs, FORWARD) &&
	(do_set(set_options, "ask") || do_set(set_options, "asksub")))
	turnon(flgs, NEW_SUBJECT);
    if (argv && *argv) {
	char buf[HDRSIZ];
	(void) argv_to_string(buf, argv);
	fix_up_addr(buf);
	to = &To[strlen(To)];
	if (*To)
	    *to++ = ',', *to++ = ' ';
	(void) strcpy(to, buf);
	to = To;
    }
    if (addcc && *addcc) {
	cc = &Cc[strlen(Cc)];
	if (*Cc)
	    *cc++ = ',', *cc++ = ' ';
	(void) strcpy(cc, addcc); /* addcc has already been fixed up */
	cc = Cc;
    }
    /* remove any redundant addresses that just got added */
    rm_redundant_addrs(To, Cc);
    if (bcc && *bcc)
	(void) strncpy(Bcc, bcc, sizeof(Bcc)); /* bcc already fixed up */
    bcc = Bcc;

    return mail_someone(to, subj, cc, bcc, flgs, inc_list);
}

static int
mail_someone(to, subject, cc, bcc, flgs, list)
register char *to, *subject, *cc, *bcc, *list;
u_long flgs;
{
    register char *p;

    flags = flgs;
    if (to && *to) {
	if (!*To)
	    (void) strncpy(To, to, sizeof(To));
    } else
	to = NO_STRING;
    if (subject && *subject) {
	if (!*Subject)
	    (void) strncpy(Subject, subject, sizeof(Subject));
    } else
	subject = NO_STRING;
    if (cc && *cc) {
	if (!*Cc)
	    (void) strncpy(Cc, cc, sizeof(Cc));
    } else
	Cc[0] = '\0';
    if (bcc && *bcc) {
	if (!*Bcc)
	    (void) strncpy(Bcc, bcc, sizeof(Bcc));
    } else
	Bcc[0] = '\0';

    if (ison(glob_flags, REDIRECT)) {
	/*
	 * NOTE: Could change this to finish_up_letter() to allow
	 * signatures to be appended.  The -U! option to mush would
	 * be extended to suppress signing when redirection is on.
	 */
	int sent = send_it();
	if (sent == -1) {
	    wprint("Message not sent!\n");
	    rm_edfile(-1);
	}
	return sent;
    }
    /* if (!*to) then prompting will be done */
    if (!istool && !hfile) {
	if (p = set_header("To: ", to, !*to)) {
	    if (!*to) /* if user typed To-line here, fix up the addresses */
		fix_up_addr(p);
	    (void) strcpy(To, p);
	}
	if (!*To && ison(flags, FORWARD) && ison(flags, SEND_NOW)) {
	    turnoff(flags, SEND_NOW); /* user must edit To: line or do again */
	    print("(You must add a To: address.)\n");
	}
	/* don't prompt for subject if forwarding mail */
	if (isoff(flags, FORWARD) && (*subject || ison(flags, NEW_SUBJECT)) &&
		(p = set_header("Subject: ", subject,
			!*subject && ison(flags, NEW_SUBJECT))))
	    (void) strcpy(Subject, p);
	if (*Cc || ison(flags, EDIT_HDRS) && do_set(set_options, "askcc")) {
	    if ((p = set_header("Cc: ", cc, !*Cc)) && *p) {
		fix_up_addr(p);
		(void) strcpy(Cc, p);
	    }
	}
	if (*Bcc)
	    print("Bcc: %s\n", Bcc);
	putchar('\n');
    }

    /* If forwarding w/o editing, start a new file for each. */
    if (ison(flags, FORWARD) && ison(flags, SEND_NOW)) {
#ifndef MSGS_DYNAMIC
	char fwd[MAXMSGS_BITS];
#else
	char *fwd = (char *) alloca(MSGS_BITS(msg_cnt));
#endif
	register int i;
	clear_msg_list(fwd);
	for (i = 0; i < msg_cnt; i++)
	    if (msg_bit(list, i)) {
		set_msg_bit(fwd, i);
		if (start_file(fwd) < 0)
		    return -1;
		turnon(msg[i].m_flags, FORWARD);
		if (isoff(glob_flags, READ_ONLY))
		    turnon(glob_flags, DO_UPDATE);
		clear_msg_list(fwd);
	    }
    } else
	return start_file(list);
    return 0;
}

static
start_file(list)
char *list;
{
    register char  *dir;
    register int   i;
    char  	   line[MAXPATHLEN];
    int		   had_hfile = FALSE;

    /* getdir() uses the home directory if no tmpdir */
    if (!(dir = getdir(do_set(set_options, "tmpdir"))))
alted:
	dir = ALTERNATE_HOME;
    (void) mktemp(sprintf(line, "%s/%s", dir, EDFILE));
    strdup(edfile, line);
    if (!(ed_fp = mask_fopen(edfile, "w+"))) {
	if (strcmp(dir, ALTERNATE_HOME))
	    goto alted;
	error("can't create %s", edfile);
	return -1;
    }
    if (!istool) {
	oldint = signal(SIGINT, rm_edfile);
	oldquit = signal(SIGQUIT, rm_edfile);
	oldterm = signal(SIGTERM, rm_edfile);
    }

    if (istool && isoff(flags, SEND_NOW) ||
	    (isoff(flags, FORWARD) || isoff(flags, SEND_NOW)) &&
	    (ison(flags, EDIT_HDRS) || do_set(set_options, "edit_hdrs"))) {
	turnon(flags, EDIT_HDRS);
	if (hfile)
	    had_hfile = TRUE;
	if (add_headers(NULL_FILE, &ed_fp, 1, flags) == (long) -1)
	    return -1;
    }
    if (Hfile) {
	(void) file_to_fp(Hfile, ed_fp, "r");
	Hfile = NULL;
	had_hfile = TRUE;
    }
    if (istool && isoff(flags, SEND_NOW))
	strdup(hfile, edfile);

    /* if flags call for it, include current message (with header?) */
    if (ison(flags, INCLUDE|FORWARD|INCLUDE_H)) {
	long copy_flgs = 0, is_forw = ison(flags, FORWARD);
	char buf[sizeof(To)];
	if (!is_forw) {
	    turnon(copy_flgs, INDENT);
	    if (ison(flags, INCLUDE_H) &&
		    !chk_option("alwaysignore", "include"))
		turnon(copy_flgs, NO_IGNORE);
	    else if (ison(flags, INCLUDE))
		turnon(copy_flgs, NO_HEADER);
	} else if (ison(flags, SEND_NOW) ||
		!chk_option("alwaysignore", "forward"))
	    turnon(copy_flgs, FORWARD);	/* FORWARD implies NO_IGNORE */
#ifdef MSG_SEPARATOR
	turnon(copy_flgs, NO_SEPARATOR);
#endif /* MSG_SEPARATOR */
	for (i = 0; i < msg_cnt; i++)
	    if (msg_bit(list, i)) {
		if (is_forw && isoff(flags, SEND_NOW)) {
		    (void) reply_to(i, FALSE, buf);
		    (void) fprintf(ed_fp,"--- Forwarded mail from %s\n\n",buf);
		}
		wprint("%sing message %d ...",
		    is_forw? "forward" : "includ", i+1);
		wprint("(%d lines)\n",
		    copy_msg(i, ed_fp, (u_long) copy_flgs, NULL));
		set_isread(i); /* if we included it, we read it, right? */
		if (is_forw && isoff(flags, SEND_NOW))
		    (void) fprintf(ed_fp,
			"\n--- End of forwarded message from %s\n", buf);
		if (!is_forw || isoff(flags, SEND_NOW))
		    (void) fputc('\n', ed_fp);
	    }
	(void) fflush(ed_fp);
    }
    if (!istool && ison(glob_flags, WARNING)) {
	if (escape && strncmp(escape, DEF_ESCAPE, 1))
	    print("(escape character is set to `%c')\n", *escape);
	if (wrapcolumn && wrapcolumn < 20)
	    print("(warning: wrapping only %d columns from the left!)\n",
		    wrapcolumn);
    }

    /* do an "if" again in case editor not found and EDIT turned off */
    if (!istool && ison(flags, EDIT)) {
	char **argv, *edit;
	int argc;
	if ((!(edit = do_set(set_options, "visual")) || !*edit) &&
		(!(edit = do_set(set_options, "editor")) || !*edit))
	    edit = DEF_EDITOR;
	(void) sprintf(line, "%s %s", edit, edfile);
	if ((argv = mk_argv(line, &argc, FALSE)) && argc > 0) {
	    print("Starting \"%s\"...\n", argv[0]);
	    (void) fclose(ed_fp);
	    ed_fp = NULL_FILE;
	    execute(argv);
	    free_vec(argv);
	    turnoff(flags, EDIT);
	    turnoff(flags, FORWARD); /* forwarded messages must be unedited */
	    /* upon exit of editor, user must now type eofc or "." to send */
	    if (!(ed_fp = fopen(edfile, "r+"))) {
		error("can't reopen %s", edfile);
		return -1;
	    }
	    (void) fseek(ed_fp, 0L, 2);
	} else
	    print("Unable to start \"%s\"\n", edit);
	wprint("(continue editing letter or ^%c to send)\n", eofc + '@');
    } else if (ison(flags, SEND_NOW)) {
	/* if finish_up_letter() was successful, file was successfully sent. */
	if (!setjmp(cntrl_c_buf) && finish_up_letter() == 0) {
	    rm_edfile(0);
	    return 0;
	}
    } else if (had_hfile) {
	/* it's not obvious what's going on -- enlighten user */
	wprint("(continue editing or ^%c to send)\n", eofc + '@');
    }

#ifdef SUNTOOL
    if (istool) {
	/* toolmode doesn't care if SEND_NOW -- user continues to edit file.
	 * if SEND_NOW is not set, then the editor file has just been started,
	 * so again, just return so user can edit file.
	 */
	if (ed_fp)
	    fclose(ed_fp), ed_fp = NULL_FILE;
	turnon(glob_flags, IS_GETTING);
	return 0;
    }
#endif /* SUNTOOL */
    if (ison(flags, SEND_NOW)) {
	/* editing couldn't have been on -- finish_up_letter() failed */
	rm_edfile(0 - ison(flags, FORWARD));
	return -1;
    }

    i = 0;
    turnon(glob_flags, IS_GETTING);
    do  {
	/* If the user hits ^C in cbreak mode, mush will return to
	 * Getstr and not clear the buffer. whatever is typed next will
	 * be appended to the line.  jumping here will force the line to
	 * be cleared cuz it's a new call.
	 */
	(void) setjmp(cntrl_c_buf);
	while (Getstr(line, sizeof(line), 0) > -1) {
	    if (!istool) /* toolmode checks on a timer -- don't do it here */
		(void) check_new_mail(); /* if new mail comes in, get it */
	    if ((i = add_to_letter(line)) <= 0)
		break;
	}
    } while (i >= 0 && finish_up_letter() == -1);
    turnoff(glob_flags, IS_GETTING);
    return i; /* return -1 if ~x or ~q to terminate letter */
}

char *tilde_commands[] = {
    "commands: [OPTIONAL argument]",
    "t [list]\tChange list of recipients",
    "s [subject]\tModify [set] subject header",
    "c [cc list]\tModify [set] carbon copy recipients",
    "b [bcc list]\tModify [set] blind carbon recipients",
    "h\t\tModify all message headers",
    "e [editor]\tEnter editor. Editor used: \"set editor\", env EDITOR, vi",
    "v [editor]\tEnter visual editor. \"set visual\", env VISUAL, vi",
    "u\t\tEdit previous (last) line in file.",
    "p [pager]\tPage message; pager used: \"set pager\", env. PAGER, more",
    "i [msg#'s]\tInclude current msg body [msg#'s] indented by \"indent_str\"",
    "I [msg#'s]\tSame, but include the message headers from included messages",
    "f [msg#'s]\tForward mail. Not indented, but marked as \"forwarded mail\"",
    "S[!]\t\tInclude Signature file [suppress file]",
    "F[!]\t\tAdd a fortune at end of letter [don't add]",
    "w file\t\tWrite msg buffer to file name",
    "a file\t\tAppend msg buffer to file name",
    "r file\t\tRead filename into message buffer",
    "q \t\tQuit message; save in dead.letter (unless \"nosave\" is set).",
    "x \t\tQuit message; don't save in dead.letter.",
    "$variable\tInsert the string value for \"variable\" into message.",
    ":cmd\t\tRun the mail command \"cmd\".",
    "|cmd\t\tPipe the message through the unix command \"cmd\".",
    "E[!]\t\tClear contents of letter after saving to dead.letter [unless !].",
    0
};

/*
 * TC_EDIT(tc) returns TRUE if tilde_command[tc] involves message
 * editing operations.  Used when EDIT_HDRS is active.
 */
#define TC_EDIT(tc) ((tc) && ((tc) < 6 || !tilde_commands[(tc)+1]))

/*
 * Add the line (char *) parameter to the letter.  Determine tilde
 * escapes and determine what to do.  This function returns 0 to
 * indicate user wants to end the letter, -1 if the letter cannot
 * be sent (~q, ~x no buffer after editor, etc...) or 1 to indicate
 * successful addition of the line to the letter.
 * This function may be called by toolmode just to change certain mush
 * internal variables via tilde escapes.  Thus, ed_fp might be null.
 */
add_to_letter(line)
char line[];
{
    register char *p;
    char buf[HDRSIZ > MAXPATHLEN ? HDRSIZ : MAXPATHLEN];

    killme = 0;
    if (ed_fp) /* may be null if istool */
	(void) fseek(ed_fp, 0L, 2);

    if (!strcmp(line, ".") && do_set(set_options, "dot"))
	return 0;
    if (line[0] != *escape || ison(glob_flags, QUOTE_MACRO)) {
	(void) fputs(line, ed_fp);
	(void) fputc('\n', ed_fp);
	(void) fflush(ed_fp);
	return 1;
    }
    /* all commands are "~c" (where 'c' is the command). set p = first
     * character after 'c' and skip whitespace
     */
    p = &line[2];
    skipspaces(0);
    switch (line[1]) {
	case 'v' : case 'p': case 'e' : case '|' : {
	    if (!*p || *p == 'i')
		switch (line[1]) {
		    case 'p' :
			if (!*p && !(p = do_set(set_options, "pager")))
			    p = DEF_PAGER;
			if (!*p || !strcmp(p, "internal"))
			    p = NULL;
		    when 'v' :
			if (*p && p[1] || (p = do_set(set_options, "visual")))
			    break;
			/* else fall through */
		    default :
			if (!(p = do_set(set_options, "editor")) || !*p)
			    p = DEF_EDITOR;
		    when '|' :
			print("No command for pipe\n");
			return 1;
		}
	    if (line[1] == 'p' || line[1] == '|')
		rewind(ed_fp);
	    if (line[1] == 'p') {
		(void) do_pager(p, TRUE); /* start the pager "p" */
		if (isoff(flags, EDIT_HDRS)) {
		    (void) do_pager(sprintf(buf, "To: %s\n", To), FALSE);
		    if (Subject[0])
			(void) do_pager(sprintf(buf, "Subject: %s\n", Subject),
					FALSE);
		    if (Cc[0])
			(void) do_pager(sprintf(buf, "Cc: %s\n", Cc), FALSE);
		    if (Bcc[0])
			(void) do_pager(sprintf(buf, "Bcc: %s\n", Bcc), FALSE);
		    (void) do_pager(strcpy(buf,
					    "--------\nMessage contains:\n"),
			FALSE);
		}
		while (fgets(buf, sizeof(buf), ed_fp))
		    if (do_pager(buf, FALSE) == EOF)
			break;
		(void) do_pager(NULL, FALSE); /* end pager */
	    } else if (line[1] == '|') {
		FILE *pipe_fp;
		(void) sprintf(buf, "( %s ) > %s", p, edfile);
		if (unlink(edfile) < 0) {
		    error("Can't unlink %s:", edfile);
		    break; /* Drop out of switch */
		}
		if ((pipe_fp = popen(buf, "w")) == NULL_FILE) {
		    error("Can't run \"%s\":", p);
		    (void) file_to_fp(edfile, ed_fp, "w");
		} else {
		    while (fgets(buf, sizeof(buf), ed_fp))
			if (fputs(buf, pipe_fp) == EOF) {
			    print("Broken pipe\n");
			    break;
			}
		    (void) pclose(pipe_fp);
		}
		pipe_fp = ed_fp; /* save ed_fp until we can reopen it */
		if (!(ed_fp = fopen(edfile, "r+"))) {
		    error("can't reopen %s", edfile);
		    (void) rewind(pipe_fp);
		    if (file_to_fp(edfile, pipe_fp, "w") < 0 ||
			    !(ed_fp = fopen(edfile, "r+"))) {
			error("can't restore old contents of %s", edfile);
			ed_fp = pipe_fp;
			dead_letter(0);
			return -1;
		    }
		}
		(void) fclose(pipe_fp);
	    } else {
		char **argv;
		int argc;
		(void) sprintf(buf, "%s %s", p, edfile);
		if ((argv = mk_argv(buf, &argc, FALSE)) && argc > 0) {
		    (void) fclose(ed_fp);
		    ed_fp = NULL_FILE;
		    execute(argv);
		    free_vec(argv);
		    /* tool will return even tho editor isn't done */
		    if (!(ed_fp = fopen(edfile, "r+"))) {
			error("can't reopen %s", edfile);
			return -1;
		    }
		} else
		    print("Unable to start \"%s\"\n", p);
	    }
	}
	when '$': {
	    register char *p2;
	    if (!(p2 = do_set(set_options, p)))
		print("(%s isn't set)\n", p);
	    else
		putstring(p2, ed_fp);
	}
	when ':': {
#ifndef MSGS_DYNAMIC
	    char new[MAXMSGS_BITS];
#else
	    char *new = (char *) alloca(MSGS_BITS(msg_cnt));
#endif
	    u_long save_flags = glob_flags;

	    turnon(glob_flags, IGN_SIGS);
	    turnoff(glob_flags, DO_PIPE);
	    turnoff(glob_flags, IS_PIPE);
	    (void) cmd_line(p, new);
	    glob_flags = save_flags;
	}
	when 'i': case 'f': case 'I': case 'm': {
	    int  n;
	    u_long copy_flgs = 0;
#ifndef MSGS_DYNAMIC
	    char list[MAXMSGS_BITS];
#else
	    char *list = (char *) alloca(MSGS_BITS(msg_cnt));
#endif

	    if (!msg_cnt) {
		wprint("No messages.\n");
		break;
	    }
	    clear_msg_list(list);
	    if (line[1] != 'f') {
		turnon(copy_flgs, INDENT);
		if (line[1] == 'i')
		    turnon(copy_flgs, NO_HEADER);
		else if (!chk_option("alwaysignore", "include"))
		    turnon(copy_flgs, NO_IGNORE);
	     } else if (!chk_option("alwaysignore", "forward"))
		turnon(copy_flgs, NO_IGNORE);
#ifdef MSG_SEPARATOR
	    turnon(copy_flgs, NO_SEPARATOR);
#endif /* MSG_SEPARATOR */
	    if (!*p)
		set_msg_bit(list, current_msg);
	    else if (!do_range(p, list))
		return 1;
	    for (n = 0; n < msg_cnt; n++)
		if (msg_bit(list, n)) {
		    if (line[1] == 'f') {
			(void) reply_to(n, FALSE, buf);
			(void) fprintf(ed_fp,
				    "--- Forwarded mail from %s\n\n", buf);
		    }
		    wprint("Including message %d ... ", n+1);
		    wprint("(%d lines)\n", copy_msg(n, ed_fp, copy_flgs, NULL));
		    set_isread(n);
		    if (line[1] == 'f')
			(void) fprintf(ed_fp,
			    "\n--- End of forwarded message from %s\n\n", buf);
		    else
			(void) fputc('\n', ed_fp);
		}
	}
	/* To: Cc: and Bcc: headers */
	when 'b':
	case 't':
	case 'c': {
	    char *h = (line[1] == 't')? To : (line[1] == 'c')? Cc : Bcc;
	    char *Prompt = line[1] == 't'? "To: " :
			   line[1] == 'c'? "Cc: " : "Bcc: ";
	    if (ison(flags, EDIT_HDRS)) {
		print("You must use an editor to change your headers.\n");
		break;
	    }

	    if (*p) {
		fix_up_addr(p);
		if (*h)
		    (void) sprintf(h+strlen(h), ", %s", p);
		else
		    (void) strcpy(h, p);
	    } else if (!(p = set_header(Prompt, h, TRUE)) || !*p)
		*h = 0;
	    else {
		fix_up_addr(p);
		(void) strcpy(h, p);
	    }
	}
	when 's':
	    if (ison(flags, EDIT_HDRS)) {
		print("You must use an editor to change your headers.\n");
		break;
	    }
	    if (*p || (p = set_header("Subject: ", Subject, 1)))
		if (!*p)
		    Subject[0] = 0;
		else
		    (void) strcpy(Subject, p);
	when 'h':
	    if (ison(flags, EDIT_HDRS)) {
		print("You must use an editor to change your headers.\n");
		break;
	    }
	    while ((p = set_header("To: ", To, 1)) && !*p)
		print("(There must be a recipient.)\n");
	    (void) strcpy(To, p);
	    if (p = set_header("Subject: ", Subject, 1))
		if (!*p)
		    Subject[0] = 0;
		else
		    (void) strcpy(Subject, p);
	    if (p = set_header("Cc: ", Cc, 1))
		if (!*p)
		    Cc[0] = 0;
		else {
		    fix_up_addr(p);
		    (void) strcpy(Cc, p);
		}
	    if (p = set_header("Bcc: ", Bcc, 1))
		if (!*p)
		    Bcc[0] = 0;
		else {
		    fix_up_addr(p);
		    (void) strcpy(Bcc, p);
		}
	when 'F': case 'S' : {
	    if (*p == '!')
		turnoff(flags, line[1] == 'F'? DO_FORTUNE : SIGN);
	    else
		turnon(flags, line[1] == 'F'? DO_FORTUNE : SIGN);
	    wprint("%sadding %s at end of message.\n", *p == '!'? "not " : "",
		line[1] == 'F'? "fortune" : "signature");
	}
	when 'w': case 'a': case 'r':
	    if (!*p) {
		print("(you must specify a filename)\n");
		return 1;
	    }
	    (void) fseek(ed_fp, 0L, 2); /* append */
	    (void) file_to_fp(p, ed_fp, (line[1] == 'r')? "r":
			      (line[1] == 'w')? "w": "a");
	/* go up one line in the message file and allow the user to edit it */
	when 'u': {
	    long newpos, pos = ftell(ed_fp);
	    char oldline[256];
	    if (pos <= 0L) { /* pos could be -1 if ftell() failed */
		print("(No previous line in file.)\n");
		return 1;
	    }
	    /* get the last 256 bytes written and read backwards from the
	     * current place until '\n' is found. Start by moving past the
	     * first \n which is at the end of the line we want to edit
	     */
	    newpos = max(0, pos - 256L);
	    (void) fseek(ed_fp, newpos, L_SET);
	    /* don't fgets -- it'll stop at a \n */
	    (void) fread(line, sizeof(char), (int)(pos-newpos), ed_fp);
	    pos--;
	    /* the last char in line should be a \n cuz it was last input */
	    if (line[(int)(pos-newpos)] != '\n')
		print("I don't know how, but your last line ended with %c.\n",
		    line[(int)(pos-newpos)]);
	    else
		line[(int)(pos-newpos)] = 0; /* null terminate \n for ^H-ing */
	    for (pos--; pos > newpos && line[(int)(pos-newpos)] != '\n'; pos--)
		;
	    /* we've gone back to the end of the second previous line. Check
	     * to see if the char we're pointing to is a \n.  It should be, but
	     * if it's not, we moved back to the first line of the file.
	     */
	    if (line[(int)(pos-newpos)] == '\n')
		++pos;
	    /* save the old line that's there in case the user boo-boos */
	    (void) strcpy(oldline, &line[(int)(pos-newpos)]);
	    /* let set header print out the line and get the input */
	    if (!(p = set_header("", &line[(int)(pos-newpos)], TRUE))) {
		print("Something bad happened and I don't know what it is.\n");
		p = oldline;
	    } else if (*p == *escape)
		print("(Warning: %c escapes ignored on %cu lines.)\n",
				*escape, *escape);
	    /* seek to to the position where the new line will go */
	    (void) fseek(ed_fp, pos, L_SET);
	    /* put the newly typed line */
	    (void) fputs(p, ed_fp); /* don't add \n. padding may be necessary */
	    /* if the new line is less than the old line, we're going to do
	     * one of two things.  The best thing to do is to truncate the
	     * file to the end of the new line.  Sys-v can't do that, so we
	     * pad the line with blanks.  May be messy in some cases, but...
	     */
	    if ((pos = strlen(p) - strlen(oldline)) < 0) {
#ifndef SYSV
		/* add the \n, flush the file, truncate to the current pos */
		(void) fputc('\n', ed_fp);
		(void) fflush(ed_fp);
		(void) ftruncate(fileno(ed_fp), (off_t) ftell(ed_fp));
#else /* SYSV */
		/* pad with blanks to the length of the old line. add \n */
		while (pos++ < 0)
		    (void) fputc(' ', ed_fp);
		(void) fputc('\n', ed_fp), (void) fflush(ed_fp);
#endif /* SYSV */
	    } else {
		/* the new line is >= the old line, add \n -- no trunc req. */
	        (void) fputc('\n', ed_fp);
		(void) fflush(ed_fp);
	    }
	    return 1;
	 }
	/* break;  not here cuz of "return" (lint). */
	case 'E':
	    if (ison(flags, EDIT_HDRS)) {
		print("You must use an editor to empty the message buffer.\n");
		break;
	    }
	    if (*p != '!' && !do_set(set_options, "nosave"))
		dead_letter(0);
	    if (emptyfile(&ed_fp, edfile) == -1) {
		error(edfile);
		return -1;
	    } else
		print("Message buffer empty\n");
	when 'q':
	    /* save in dead.letter if nosave not set -- rm_edfile(-2). */
	    rm_edfile(-2); /* doesn't return out of tool mode */
	    return -1;
	    /* break; not stated cuz of "return" (lint) */
	case 'x':
	    /* don't save dead.letter -- simulate normal rm_edfile() call */
	    rm_edfile(0);
	    return -1;
	    /* break; (not specified for lint) */
	default:
	    if (line[1] == *escape) {
		(void) fputs(&line[1], ed_fp);
		(void) fputc('\n', ed_fp);
		(void) fflush(ed_fp);
		return 1;
	    } else if (line[1] == '?') {
		register int x;
		(void) do_pager(NULL, TRUE); /* start pager */
		for (x = 0; tilde_commands[x]; x++) {
		    if (ison(flags, EDIT_HDRS) && TC_EDIT(x))
			continue;
		    (void) sprintf(buf, "%s%s\n", escape, tilde_commands[x]);
		    if (do_pager(buf, FALSE) == EOF)
			break;
		}
		if (tilde_commands[x] == NULL) {
		    (void) sprintf(buf,
			"%s%s\t\tBegin a line with a single %s\n",
			escape, escape, escape);
		    (void) do_pager(buf, FALSE);
		}
		(void) do_pager(NULL, FALSE); /* end pager */
	    } else
		print("`%c': unknown %c escape. Use %c? for help.\n",
		    line[1], *escape, *escape);
	    return 1;
    }
    if (ed_fp)
	(void) fseek(ed_fp, 0L, 2);
    if (!istool)
	wprint("(continue editing letter)\n");
    return 1;
}

/*
 * finish up the letter. ask for the cc line, if verify is set, ask to
 * verify sending, continue editing, or to dump the whole idea.
 * Then check for signature and fortune.  Finally, pass it to send_it()
 * to actually send it off.
 * Return 0 on success, -1 on failure.
 */
static int
finish_up_letter()
{
    register char *p;
    int c;
    char buf[MAXPATHLEN];

    /* forwarded mail has no additional personalized text */
    if (ison(flags, FORWARD)) {
	if (send_it() == -1) {
	    wprint("Message not sent!\n");
	    return -1;
	}
	turnoff(glob_flags, IS_GETTING);
	return 0;
    }

    /* REDIRECT should never be on here, but just in case */
    if (isoff(glob_flags, REDIRECT)) {
	if (!istool) {
	    if (isoff(flags, EDIT_HDRS) && do_set(set_options, "askcc")) {
		if (p = set_header("Cc: ", Cc, 1))
		    (void) strcpy(Cc, p);
	    }
	}
	/* ~v on the Cc line asks for verification, first initialize p! */
	p = NULL;
	if (!strncmp(Cc, "~v", 2) ||
		/* Flashy test for $verify either empty or set to "mail" */
		glob(p = do_set(set_options, "verify"),
					    "{,{,*[ \\,]}mail{,[ \\,]*}}")) {
	    if (!p) /* so we don't Cc to ~v! */
		*Cc = 0;
	    for (;;) {
#ifdef SUNTOOL
		if (istool)
		    c = (ask("Send Message?") == TRUE)? 's' : 'c';
		else
#endif /* SUNTOOL */
		{
		    print("send, continue editing, discard [s,c,d]? ");
		    c = Getstr(buf, sizeof(buf), 0);
		    if (c < 0)
			putchar('\n');
		    else if (!istool)
			c = lower(*buf);
		}
		if (c == 'd') {
		    rm_edfile(-2);
		    return 0;
		} else if (c == 'c') {
		    wprint("(continue editing letter)\n");
		    return -1;
		} else if (c == 's')
		    break;
	    }
	}
    }

    if (send_it() == -1) {
	if (isoff(flags, SEND_NOW))
	    wprint("(continue)\n");
	return -1;
    }
    return 0;
}

/*
 * actually send the letter.
 * 1. reset all the signals because of fork.
 * 2. determine recipients (users, address, files, programs)
 * 3. determine mailer, fork and return (if not verbose).
 * 4. popen mailer, $record, and other files specified in step 1.
 * 5. make the headers; this includes To: line, and user set headers, etc...
 * 6. copy the letter right into the array of file pointers (step 1).
 * 7. close the mailer and other files (step 1) and remove the edit-file.
 * return -1 if mail wasn't sent.  could be user error, could be the system.
 * allow user to try again or to save message to file and abort message.
 * return 0 if successful.
 */
static int
send_it()
{
    register char *p, *b, *addr_list;
#ifdef MAXFILES
    register int size = MAXFILES - 1;
    FILE *files[MAXFILES];
    char *names[MAXFILES];
#else
    register int size = getdtablesize() - 1;
    FILE *files[30];  /* 30 should be sufficiently large enough */
    char *names[30];
#endif /* MAXFILES */
#if defined(VERBOSE_ARG)
    SIGRET (*oldchld)();
#endif /* VERBOSE_ARG */
    int next_file = 1; /* reserve files[0] for the mail delivery program */
    int log_file = -1; /* the index into the files array for mail logging */
    char buf[3*HDRSIZ];
    char *orig_to, *orig_cc, *orig_bcc; /* save originals to restore on error */
    char expand = !do_set(set_options, "no_expand");
    int fork_pid;

    names[0] = names[1] = NULL; /* for free_vec() */
    /* If edit_hdrs, make sure the correct headers exist and are intact
     * before bothering to continue.
     */
    if (ison(flags, EDIT_HDRS)) {
	/* fool header_field into thinking that the file is the folder */
	FILE *save_tmpf = tmpf;
	long old_offset = msg[msg_cnt].m_offset;

	if (!ed_fp) {
	    wprint("No file for headers!\n");
	    return -1;
	}
	Debug("Getting headers from file ... ");

	tmpf = ed_fp;
	msg[msg_cnt].m_offset = 0L;
	if (p = header_field(msg_cnt, "to")) {
	    (void) strcpy(To, p);
	    Cc[0] = Bcc[0] = 0;
	    if (p = header_field(msg_cnt, "cc"))
		(void) strcpy(Cc, p);
	    if (p = header_field(msg_cnt, "bcc"))
		(void) strcpy(Bcc, p);
	    if (p = header_field(msg_cnt, "fcc"))
		next_file += find_files(p, names+next_file, size-next_file, 1);
	} else
	    *To = 0; /* Error caught below */
	msg[msg_cnt].m_offset = old_offset;
	tmpf = save_tmpf;
	Debug("\n");
    }
    if (!*To) {
	wprint("You must have a To: recipient to send mail.\n");
	if (!istool) {
	    (void) signal(SIGINT, oldint);
	    (void) signal(SIGQUIT, oldquit);
	    (void) signal(SIGTERM, oldterm);
	}
	free_vec(&names[1]);
	return -1;
    }

    if (!(p = do_set(set_options, "sendmail")))
	p = MAIL_DELIVERY;

#ifdef VERBOSE_ARG
    /* Tool mode can't do verbosity -- no window for the MTA output */
    if (!istool && (ison(flags, VERBOSE) || do_set(set_options, "verbose"))) {
	turnon(flags, VERBOSE); /* prevent fork when "verbose" has changed */
	oldchld = signal(SIGCHLD, SIG_DFL); /* let pclose() do the wait() */
#if defined(MMDF) && !defined(M_EXECMAIL)
	b = &buf[strlen(sprintf(buf, "%s%s", p, VERBOSE_ARG))];
#else /* MMDF */
	b = &buf[strlen(sprintf(buf, "%s %s", p, VERBOSE_ARG))];
#endif /* MMDF && !M_EXECMAIL */
    } else
#endif /* VERBOSE_ARG */
	b = buf + Strcpy(buf, p);
#ifdef METOO_ARG
    if (!strcmp(p, MAIL_DELIVERY) && do_set(set_options, "metoo"))
	b += strlen(sprintf(b, " %s", METOO_ARG));
#endif /* METOO_ARG */
    *b++ = ' ', *b = 0; /* strcat(b, " "); */
    addr_list = b; /* save this position to check for addresses later */

    /* save original list.  If alias expansion fails, replace address lists
     * with what was originally typed so user can fix it.  This isn't necessary
     * if the lists are already in the file the user is editing (edit_hdrs).
     */
    if (isoff(flags, EDIT_HDRS))
	orig_to = savestr(To);
    /*
     * Build the address lines to give to the mail transfer system.  This
     * address line cannot contain comment fields!  First, expand aliases
     * since they may contain comment fields within addresses. Copy this
     * result back into the Buffer since this will go into the header ...
     * Next, remove all comments so the buffer contains ONLY valid addresses.
     * Next, strip off any filenames/programs which might occur in the list.
     * Finally, add this information to the command line buffer (buf).
     * Remove commas if necessary (see ifdefs).  In the event of errors,
     * force a dead letter by rm_edfile(-1).
     */
    if (!(p = alias_to_address(To))) {
	print("address expansion failed for To: list.\n");
	free_vec(&names[1]);
	if (isoff(flags, EDIT_HDRS))
	    strcpy(To, orig_to), xfree(orig_to);
	return -1;
    } else {
	next_file += find_files(p, names+next_file, size-next_file, 0);
	if (expand)
	    (void) strcpy(To, p);
	rm_cmts_in_addr(p);
	skipspaces(0);
	b += Strcpy(b, p);
    }
    if (isoff(flags, EDIT_HDRS))
	orig_cc = savestr(Cc);
    if (*Cc) {
	if (!(p = alias_to_address(Cc))) {
	    wprint("address expansion failed for Cc: list.\n");
	    free_vec(&names[1]);
	    if (isoff(flags, EDIT_HDRS)) {
		strcpy(To, orig_to), xfree(orig_to);
		strcpy(Cc, orig_cc), xfree(orig_cc);
	    }
	    return -1;
	} else {
	    next_file += find_files(p, names+next_file, size-next_file, 0);
	    if (expand)
		(void) strcpy(Cc, p);
	    rm_cmts_in_addr(p);
	    skipspaces(0);
	    if (*p) {
		*b++ = ',', *b++ = ' ';
		b += Strcpy(b, p);
	    }
	}
    }

    /* expand Bcc addrs, but don't add to list yet.  sign letter first */
    if (isoff(flags, EDIT_HDRS))
	orig_bcc = savestr(Bcc);
    if (*Bcc) {
	if (p = alias_to_address(Bcc))
	    p = strcpy(Bcc, p);
	else {
	    wprint("address expansion failed for Bcc: list.\n");
	    free_vec(&names[1]);
	    /* rm_edfile(-1); */
	    if (isoff(flags, EDIT_HDRS)) {
		strcpy(To, orig_to), xfree(orig_to);
		strcpy(Cc, orig_cc), xfree(orig_cc);
		strcpy(Bcc, orig_bcc), xfree(orig_bcc);
	    }
	    return -1;
	}
    } else
	p = NULL;

    /* Sign the letter before adding the Bcc list since they aren't
     * considered when adding a signature.
     */
    if (*addr_list && ison(flags, SIGN|DO_FORTUNE) &&
	    isoff(glob_flags, REDIRECT) && isoff(flags, FORWARD))
	sign_letter(addr_list, flags, ed_fp);

    if (p) { /* p still points to expanded Bcc list */
	next_file += find_files(p, names+next_file, size-next_file, 0);
	rm_cmts_in_addr(p);
	skipspaces(0);
	if (*p) {
	    *b++ = ',', *b++ = ' ';
	    b += Strcpy(b, p);
	}
    }
    if (!*addr_list && next_file == 1) {
	wprint("There must be at least 1 legal recipient.\n");
	if (isoff(flags, EDIT_HDRS)) {
	    strcpy(To, orig_to), xfree(orig_to);
	    strcpy(Cc, orig_cc), xfree(orig_cc);
	    strcpy(Bcc, orig_bcc), xfree(orig_bcc);
	}
	return -1;
    }

#ifdef NO_COMMAS
    for (p = buf; p = index(p, ','); p++)
	*p = ' ';
#endif /* NO_COMMAS */

    Debug("mail command: %s\n", buf);

    if (isoff(flags, VERBOSE) && debug < 3)
	switch (fork_pid = fork()) {
	    case  0:  /* the child will send the letter. ignore signals */
#if defined(SYSV) && !defined(AUX) && !defined(IRIX4)
		if (setpgrp() == -1)
#else /* !SYSV || AUX || IRIX4 */
		if (setpgrp(0, getpid()) == -1)
#endif /* SYSV && !AUX || IRIX4 */
		    error("setpgrp");
		/* NOTE: No special case needed for tool here because
		 * this is the sending child -- it's going to pclose()
		 * and then exit(), so who cares about the notifier?
		 */
		(void) signal(SIGCHLD, SIG_DFL);
		(void) signal(SIGTERM, SIG_IGN);
		(void) signal(SIGINT, SIG_IGN);
		(void) signal(SIGHUP, SIG_IGN);
		(void) signal(SIGQUIT, SIG_IGN);
#ifdef SIGTTIN
		(void) signal(SIGTTOU, SIG_IGN);
		(void) signal(SIGTTIN, SIG_IGN);
#endif /* SIGTTIN */
#ifdef SIGCONT
		(void) signal(SIGCONT, SIG_IGN);
		(void) signal(SIGTSTP, SIG_IGN);
#endif /* SIGCONT */
		turnon(glob_flags, IGN_SIGS);
	    when -1:
		error("fork failed trying to send mail");
		if (isoff(flags, EDIT_HDRS)) {
		    strcpy(To, orig_to);
		    strcpy(Cc, orig_cc);
		    strcpy(Bcc, orig_bcc);
		}
		/* fall thru */
	    default:
		if (fork_pid > 0) {
#ifdef SUNTOOL
		    /* If we're a tool, we have to register a handler
		     * for the fork_pid.  Otherwise, it's used only as
		     * an error indicator, so reset it to zero.
		     */
		    if (istool)
			notify_set_wait3_func(mfprint_sw, my_wait3, fork_pid);
#endif /* SUNTOOL */
		    fork_pid = 0;
		}
		/* istool doesn't need ed_fp, so don't keep it around */
		if (istool || !fork_pid && isoff(glob_flags, REDIRECT))
		    (void) fclose(ed_fp), ed_fp = NULL_FILE;
		free_vec(&names[1]);
		if (isoff(flags, EDIT_HDRS))
		    xfree(orig_to), xfree(orig_cc), xfree(orig_bcc);
		if (!istool) {
		    (void) signal(SIGINT, oldint);
		    (void) signal(SIGQUIT, oldquit);
		    (void) signal(SIGTERM, oldterm);
		}
		return fork_pid;
	}

#if defined(MMDF) && !defined(M_EXECMAIL)
    *(addr_list-1) = '\0';
#endif /* MMDF && !M_EXECMAIL */
    if (debug > 2) {
	files[0] = stdout;
	if (!*addr_list)
	    addr_list = "[no recipients]";
    } else if (*addr_list) {
	if (!(files[0] = open_file(buf, TRUE, FALSE))) {
	    rm_edfile(-1); /* force saving of undeliverable mail */
	    if (isoff(flags, VERBOSE) && debug < 3)
		exit(-1);
	    else
		return 0;
	}
    } else
	files[0] = NULL_FILE;

    if (ison(flags, VERBOSE))
	wprint("Sending letter ... "), (void) fflush(stdout);
#if defined(MMDF) && !defined(M_EXECMAIL)
    /* give address list to submit */
    for (p = addr_list; *p && (p = any(p, ",<")); p++)
	if (*p == ',')
	    *p = '\n';
	else
	    p = index(p, '>');
    if (*addr_list)
	(void) fprintf(files[0], "%s\n\n", addr_list);
#endif /* MMDF && !M_EXECMAIL */

    /* see if log is set.  This is just to add message headers. No msg body. */
    if (p = do_set(set_options, "logfile")) {
	if (!*p)
	    p = "~/mail.log";
	if (!index("~|/+", *p))
	    (void) sprintf(buf, "%s/%s", do_set(set_options, "cwd"), p);
	else
	    (void) strcpy(buf, p);
	log_file = next_file;
	next_file += find_files(buf, names+next_file, size-next_file, 0);
	if (log_file == next_file)
	    log_file = -1;
    }

    /* see if record is set.  If so, open that file for appending and add
     * the letter in a format such that mail can be read from it
     */
    if (p = do_set(set_options, "record")) {
	if (!*p)
	    p = "~/record";
	if (!index("~|/+", *p))
	    (void) sprintf(buf, "%s/%s", do_set(set_options, "cwd"), p);
	else
	    (void) strcpy(buf, p);
	next_file += find_files(buf, names+next_file, size-next_file, 0);
    }

    /* Don't need to open names[0] as files[0], so skip those */
    next_file = 1 + open_list(names + 1, files + 1, next_file - 1);

    /* First, put the message separator in... */
    for (size = 1; size < next_file; size++)
#ifndef MSG_SEPARATOR
	{
	    time_t t;
	    (void) time(&t);
	    (void) fprintf(files[size], "From %s %s", login, ctime(&t));
	}
#else /* MSG_SEPARATOR */
#ifdef MMDF
	(void) fputs(MSG_SEPARATOR, files[size]);
#else /* MMDF */
	(void) fprintf(files[size], "%s\n", MSG_SEPARATOR);
#endif /* MMDF */
#endif /* MSG_SEPARATOR */

    /* if redirection, ed_fp = stdin, else rewind the file just made */
    if (isoff(glob_flags, REDIRECT))
	rewind(ed_fp);
    else
	ed_fp = stdin;

#ifndef MSG_SEPARATOR
    /* If forwarding or reading a draft, skip the leading From_ line.
     * This is done for drafts so that messages saved by dead_letter()
     * can be read back in as a draft; in other cases, this isn't done
     * for edit_hdrs because FORWARD wouldn't be set.
     */
    if (ison(flags, FORWARD|SEND_NOW) && fgets(buf, sizeof buf, ed_fp) &&
	strncmp(buf, "From ", 5) != 0)
	rewind(ed_fp); /* No From_ line (should never happen) */
#endif /* MSG_SEPARATOR */
    {
	long offset = add_headers(ed_fp, files, next_file, flags);
	if (offset == -1)
	    offset = 0L;
	(void) fseek(ed_fp, offset, L_SET);
    }

    /* Read from stdin or the edfile till EOF and send it all to the mailer
     * and other open files/folders/programs. Check for "From " at the
     * beginnings of these lines to prevent creating new messages in folders.
     */
    while (fgets(buf, sizeof buf, ed_fp))
	for (size = 0; size < next_file; size++) {
	    if (!files[size]) /* files[0] will be NULL if not calling MTA */
		continue;
	    if (size == log_file)
		continue;
#ifndef MSG_SEPARATOR
	    if (!strncmp(buf, "From ", 5))
		(void) fputc('>', files[size]);
#endif /* MSG_SEPARATOR */
	    if (fputs(buf, files[size]) == EOF) {
		if (size == 0) {
		    error("Lost connection to MTA");
		    dead_letter(-1);
		    break;
		} else {
		    /* Drop this file, but continue writing others */
		    if (names[size]) {
			error("Write failed: %s", names[size]);
			(void) close_lock(names[size], files[size]);
			xfree(names[size]);
		    } else
			error("Write failed");
		    if (size < --next_file) {
			names[size] = names[next_file];
			files[size--] = files[next_file];
		    }
		    files[next_file] = NULL_FILE;
		    names[next_file] = NULL;
		}
	    }
	}

    /* loop thru the open files (except for the first: the mail delivery agent)
     * and append a blank line so that ucb-mail can read these folders.
     * Then close the files.
     */
    for (size = 1; size < next_file; size++) {
#ifdef END_MSG_SEP
	(void) fputs(END_MSG_SEP, files[size]);
#endif /* END_MSG_SEP */
	if (names[size]) {
#ifndef END_MSG_SEP
	    (void) fputc('\n', files[size]);
#endif /* !END_MSG_SEP */
	    if (close_lock(names[size], files[size]) == EOF) {
		error("Warning: Close failed: %s", names[size]);
	    }
	    xfree(names[size]);
	} else {
	    if (debug < 3)
		(void) fclose(files[size]); /* Don't mess with pclose() */
	    else
		(void) pclose(files[size]); /* unless we never forked */
	}
    }

    if (debug < 3) {
	int reply_code = files[0]? pclose(files[0]) : (MTA_EXIT << 8);
	Debug("pclose reply_code = %d\n", reply_code);
	rm_edfile((reply_code == (MTA_EXIT << 8))? 0 : -1);
    } else
	rm_edfile(0);

#ifdef VERBOSE_ARG
    if (!istool && ison(flags, VERBOSE))
	(void) signal(SIGCHLD, oldchld);
#endif /* VERBOSE_ARG */

    if (ison(flags, VERBOSE) || debug > 2) {
	if (isoff(glob_flags, REDIRECT))
	    wprint("sent.\n");
	if (!istool) {
	    (void) signal(SIGINT, oldint);
	    (void) signal(SIGQUIT, oldquit);
	    (void) signal(SIGTERM, oldterm);
	}
    } else
	exit(0); /* not a user exit -- a child exit */
    return 0;
}

/*
 * Add the necessary headers to make a file a legitimate mail message.
 * This could be for a file which the user will edit (via edit_hdrs) or
 * for delivery to an MTA.
 * Make folders conform to RFC-822 by adding From: and Date: headers.
 * Prefix certain header with the "Resent-" prefix when forwarding.
 * Return offset of fp if we're parsing it for headers (for delivery to MTA).
 */
static long
add_headers(fp, files, size, flags)
FILE *fp, *files[];
int size;
u_long flags;
{
    char buf[BUFSIZ], From_buf[256], *pF = From_buf, date_str[64];
    char *host = NULL, *p, *subj = NULL, *own_from = NULL; /* See WARNING */
    int i, for_editor = (fp == NULL_FILE);
    int got_date = for_editor, got_from = for_editor;
    struct options *opts;
    extern char *facebuf;

    if (for_editor && hfile) {
	i = file_to_fp(hfile, files[0], "r");
	xfree(hfile), hfile = NULL;
	return (i < 0 ? -1 : TRUE);
    }

    buf[0] = 0;
    if (ourname)
	host = ourname[0];
    if (for_editor)
	turnoff(flags,FORWARD); /* forwarded messages must not be edited */

    /* [Re]create a From: header -- check first to see if the user has
     * created a From: header with the my_hdr command (the own_hdrs list).
     * If his is not legitimate, warn user and use the other header.
     */
    if ((for_editor || isoff(flags, EDIT_HDRS)) &&
	    own_hdrs && !do_set(set_options, "no_hdrs")) {
	for (opts = own_hdrs; opts; opts = opts->next)
	    if (!strcmp(opts->option, "From:")) {
		p = opts->value;
		skipspaces(0);
		sprintf(buf, "%sFrom: %s\n",
				ison(flags, FORWARD)? "Resent-" : "", p);
		own_from = buf;
		/* WARNING: the above depends on the following facts:
		 * 1. If for_editor, own_from will be output immediately,
		 *    so buf will not be overwritten;
		 * 2. If !for_editor but EDIT_HDRS, the "real" from line
		 *    will be read from the file so own_from isn't needed;
		 * 3. If neither, From: is the first line output, so
		 *    buf will not be overwritten.
		 * Any change in the above means a new buffer for own_from
		 * may be needed.  Check carefully.
		 */
	    }
	}
    if (ison(flags, FORWARD))
	pF += Strcpy(From_buf, "Resent-");
    pF += Strcpy(pF, "From: ");
#ifdef UUCP
    if (host && *host)
	pF += strlen(sprintf(pF, "%s!", host));
#endif /* UUCP */
    pF += Strcpy(pF, login);
#ifndef UUCP
    if (host && *host)
	pF += strlen(sprintf(pF, "@%s", host));
#endif /* UUCP */
    if ((p = do_set(set_options, "realname")) ||
	(p = do_set(set_options, "name")))
	pF += strlen(sprintf(pF, " (%s)", p));
    *pF++ = '\n', *pF++ = 0;

    /* First print From, Date, In-Reply-To */
    for (i = 0; i < size; i++) {
	if (!files[i])
	    continue;
	if (for_editor)
	    if (own_from)
		(void) fputs(own_from, files[i]);
	    else
		(void) fputs(From_buf, files[i]);
	else if (isoff(flags, EDIT_HDRS)) {
#ifdef PICKY_MAILER
	    if (i > 0)
#endif /* PICKY_MAILER */
	    if (own_from)
		(void) fputs(own_from, files[i]);
	    else
		(void) fputs(From_buf, files[i]);
	    got_from = TRUE;
	}
	if (for_editor || isoff(flags, EDIT_HDRS)) {
#ifdef PICKY_MAILER
	    if (i > 0 && !for_editor)
#endif /* PICKY_MAILER */
	    (void) fprintf(files[i], "%sDate: %s\n",
		ison(flags, FORWARD) ? "Resent-" : "", rfc_date(date_str));
	    got_date = TRUE;
	    if (*in_reply_to)
		fprintf(files[i], "In-Reply-To: %s\n", in_reply_to);
	}
    }
    if (own_from)
	*own_from = 0; /* buf[0] must be 0 below */
    /* next print user's own message headers */
    if (for_editor || isoff(flags, EDIT_HDRS))
	if (own_hdrs && !do_set(set_options, "no_hdrs")) {
	    for (opts = own_hdrs; opts; opts = opts->next) {
		if (!strcmp(opts->option, "From:"))
		    continue;
		for (i = 0; i < size; i++) {
		    if (!files[i])
			continue;
		    p = opts->value;
		    skipspaces(0);
		    fprintf(files[i], "%s %s\n", opts->option, p);
		}
	    }
	}

    /*
     * Now either prepare to put the rest of the headers into the file
     * or (when sending edited headers) copy them back out of the file
     */
    if (for_editor) {
	char *orig = NULL;
	/* for edit_hdrs, print the headers followed by a blank line */
	if (To[0]) {
	    orig = savestr(To);
	    if (!(p = alias_to_address(To))) {
		wprint("To: list unmodified.\n");
		p = orig;
	    }
	    (void) strcpy(To, p);
	}
	if (Cc[0]) {
	    strdup(orig, Cc);
	    if (!(p = alias_to_address(Cc))) {
		wprint("Cc: list unmodified.\n");
		p = orig;
	    }
	    (void) strcpy(Cc, p);
	}
	if (Bcc[0]) {
	    strdup(orig, Bcc);
	    if (!(p = alias_to_address(Bcc))) {
		wprint("Bcc: list unmodified.\n");
		p = orig;
	    }
	    (void) strcpy(Bcc, p);
	}
	xfree(orig);
    } else if (ison(flags, EDIT_HDRS)) {
	/* copy the headers of the message removing special headers */
	int print_hdr = FALSE;
	if (isoff(flags, SEND_NOW))
	    rewind(fp); /* Drafts may have had fp positioned */
	while (fgets(buf, sizeof(buf), fp)) {
	    (void) no_newln(buf);
	    if (!buf[0])
		break;
	    /* if the first char is NOT a space, it MUST be a new header.
	     * Otherwise, it is considered part of the message body.
	     */
	    if (!isspace(buf[0])) {
		print_hdr = TRUE;
		if (!(p = any(buf, " \t:")) || isspace(*p))
		    break; /* this is not a legitimate header */
		skipspaces(1);
		if (!*p)
		    print_hdr = FALSE; /* blank headers are not allowed */
		p = buf;
		if (!lcase_strncmp(buf, "resent-", 7)) {
		    if (ison(flags, EDIT_HDRS))
	    wprint("You can't use \"Resent-\" headers in edited messages.\n");
		    p += 7;
		}
		if (!lcase_strncmp(p, "to:", 3) ||
		    !lcase_strncmp(p, "cc:", 3) ||
		    !lcase_strncmp(p, "bcc:", 4) ||
		    !lcase_strncmp(p, "fcc:", 4) ||
		    !lcase_strncmp(p, "x-mailer:", 9) ||
		    !lcase_strncmp(p, "x-face:", 7) ||
		    !lcase_strncmp(p, "status:", 7))
		    print_hdr = FALSE;
		else if (!lcase_strncmp(p, "date:", 5))
		    if (got_date)
			wprint("You can't change or add date headers.\n");
		    else {
			got_date = TRUE;
			(void) sprintf(buf, "Date: %s", rfc_date(date_str));
			p = buf;
		    }
		else if (!lcase_strncmp(p, "subject:", 8))
		    (print_hdr = FALSE), strdup(subj, p);
		else if (!lcase_strncmp(p, "from:", 5)) {
		    char not_me[BUFSIZ];
		    (void) strcpy(not_me, buf + 5);
		    take_me_off(not_me);
		    if (*not_me) {
			/* Ignore bogus From: if we have a good one */
			if (got_from)
			    print_hdr = FALSE;
			/* otherwise, output a good one */
			else {
			    (void) strcpy(buf, From_buf);
			    (void) no_newln(buf);
			}
		    }
		    got_from = TRUE;
#ifdef PICKY_MAILER
		    /* don't send From: to mta -- fool "for loop" below
		     * by initializing the loop at files[1], not files[0]
		     */
		    if (!for_editor)
			print_hdr = 2;
#endif /* PICKY_MAILER */
		}
	    }
	    if (print_hdr)
		/* print_hdr may be 2 for From: header */
		for (i = print_hdr-1; i < size; i++)
		    if (files[i]) {
			(void) fputs(buf, files[i]);
			(void) fputc('\n', files[i]);
		    }
	}
    }
    /* Finally, do the required (or changed) headers (Date, To, Cc) */
    (void) wrap_addrs(To, 80);
    (void) wrap_addrs(Cc, 80);
    (void) wrap_addrs(Bcc, 80);
    for (i = 0; i < size; i++) {
	if (!files[i])
	    continue;
#ifdef PICKY_MAILER
	if (i > 0) {
#endif /* PICKY_MAILER */
	if (!got_from)
	    (void) fputs(From_buf, files[i]);
	if (!got_date)
	    (void) fprintf(files[i], "%sDate: %s\n",
		ison(flags, FORWARD) ? "Resent-" : "", rfc_date(date_str));
#ifdef PICKY_MAILER
	}
#endif /* PICKY_MAILER */
	if (facebuf != NULL)
		(void) fprintf(files[i], "X-Face: %s", facebuf);
	(void) fprintf(files[i], "X-Mailer: %s\n", check_internal("version"));
	(void) fprintf(files[i], "%sTo: %s\n",
	    ison(flags, FORWARD) ? "Resent-" : "", To);
	if (for_editor || isoff(flags, EDIT_HDRS)) {
	    if (isoff(flags, FORWARD) &&
		    (*Subject || for_editor && (do_set(set_options, "ask") ||
					    do_set(set_options, "asksub"))))
		(void) fprintf(files[i], "Subject: %s\n", Subject);
	} else if (subj && *subj && strlen(subj) > 9)
	    (void) (fputs(subj, files[i]), fputc('\n', files[i]));
	if (*Cc || for_editor && do_set(set_options, "askcc"))
	    (void) fprintf(files[i], "%sCc: %s\n",
		ison(flags, FORWARD) ? "Resent-" : "", Cc);
	if (i > 0 || for_editor)
	    /* Do not send these to mail transfer agent */
	    if (*Bcc)
		(void) fprintf(files[i], "%sBcc: %s\n",
		    ison(flags, FORWARD) ? "Resent-" : "", Bcc);
	if (i > 0)
	    (void) fprintf(files[i], "Status: OR\n");
    }
    for (i = 0; i < size; i++)
	if (files[i])
	    (void) fflush(files[i]);
    if (buf[0]) /* last attempted header read was a line of msg text */
	for (i = 0; i < size; i++) {
	    if (files[i]) {
		(void) fputs(buf, files[i]);
		(void) fputc('\n', files[i]);
		(void) fflush(files[i]);
	    }
	}
    else
	if (isoff(flags, FORWARD))
	    for (i = 0; i < size; i++)
		if (files[i]) {
		    (void) fputc('\n', files[i]);
		    (void) fflush(files[i]);
		}
    return fp? ftell(fp) : (long)TRUE;
}

/* ARGSUSED */
SIGRET
rm_edfile(sig)
{
    if (sig > 0) {
	char *fix;
	if (ison(glob_flags, IGN_SIGS))
	    return;
	/* wrapcolumn may have been trashed -- restore it */
	if ((fix = do_set(set_options, "wrapcolumn")) && *fix)
	    wrapcolumn = atoi(fix);
	mac_flush(); /* abort pending macros */
    }
    /* now check whether we should abort the letter */
    if (sig > 0 && !killme && ison(glob_flags, IS_GETTING)) {
	if (!istool)
	    (void) signal(sig, rm_edfile);
	killme = 1;
	print("\n** interrupt -- one more to kill letter **\n");
	longjmp(cntrl_c_buf, 1);
    }
    killme = 0;
    /* if sig == -1, force a save into dead.letter.
     * else, check for nosave not being set and save anyway if it's not set
     * sig == 0 indicates normal exit (or ~x), so don't save a dead letter.
     */
    if (sig == -1 || sig != 0 && !do_set(set_options, "nosave"))
	dead_letter(sig);
    if (isoff(glob_flags, REDIRECT) && ed_fp) /* ed_fp may be null in toolmode*/
	(void) fclose(ed_fp), ed_fp = NULL_FILE;
    (void) unlink(edfile);

    turnoff(glob_flags, IS_GETTING);
    if (sig == -1)
	return;

    if (sig == SIGHUP)
	cleanup(0);
    if (!istool) {
	(void) signal(SIGINT, oldint);
	(void) signal(SIGQUIT, oldquit);
	(void) signal(SIGTERM, oldterm);
    }

    if (sig == 0 || sig == -2 || istool) /* make sure sigchld is reset first */
	return;

    if (isoff(glob_flags, DO_SHELL)) {  /* If we're not in a shell, exit */
	puts("exiting");
	echo_on();
	exit(1);
    }
    longjmp(jmpbuf, 1);
}

/* save letter into dead letter */
dead_letter(sig)
int sig;	/* signal passed to rm_edfile() or 0 */
{
    char 	*p, buf[BUFSIZ];
    long 	t;
    FILE 	*dead;

    if (ison(glob_flags, REDIRECT)) {
	print("input redirected -- can't save dead letter.\n");
	return;
    }
    /* If the file doesn't exist, get outta here. File may not exist if
     * user generated a ^C from a promptable header and catch sent us here.
     */
    if (!ed_fp && Access(edfile, R_OK) != 0)
	return;
    /* User may have killed mush via a signal while he was in an editor.
     * ed_fp will be NULL in this case.  Since the file does exist (above),
     * open it so we can copy it to dead letter.
     */
    if (!ed_fp && !(ed_fp = fopen(edfile, "r"))) {
	error("can't save dead letter from %s", edfile);
	return;
    }
    /* don't save a dead letter if there's nothing to save. */
    if (fseek(ed_fp, 0L, 2) || ftell(ed_fp) <= 1L)
	return;
    if (!(p = do_set(set_options, "dead")))
	p = "~/dead.letter";
    if (!(dead = open_file(p + (*p == '|'), (*p == '|'), TRUE)))
	return;
    (void) time (&t);
    (void) fflush(ed_fp);
    rewind(ed_fp);
#ifdef MSG_SEPARATOR
	(void) fputs(MSG_SEPARATOR, dead);
#ifndef MMDF
    (void) fputc('\n', dead);
#endif /* MMDF */
#else /* MSG_SEPARATOR */
    (void) fprintf(dead, "From %s %s", login, ctime(&t));
#endif /* MSG_SEPARATOR */
    (void) fprintf(dead, "From: %s\nTo: %s\nSubject: %s\n", login, To, Subject);
    (void) fprintf(dead, "Date: %s\n", rfc_date(buf));
    if (*Cc)
	(void) fprintf(dead, "Cc: %s\n", Cc);
    if (*Bcc)
	(void) fprintf(dead, "Bcc: %s\n", Bcc);
    (void) fputc('\n', dead);
    while (fgets(buf, sizeof(buf), ed_fp))
	(void) fputs(buf, dead);
    (void) fputc('\n', dead);
#ifdef END_MSG_SEP
    (void) fputs(END_MSG_SEP, dead);
#endif /* END_MSG_SEP */
    if (*p != '|')
	(void) close_lock(p, dead);
    else
	(void) pclose(dead);
    wprint("Saved%s letter in %s.\n", sig > 0? " unfinished" : "", p);
}
