/* slocal.c - MH style mailer to write to a local user's mailbox */

/* This program implements mail delivery in the MH/MMDF style.

   Under SendMail, users should add the line

	"| /usr/local/lib/mh/slocal"

   to their $HOME/.forward file.

   Under MMDF-I, users should (symbolically) link /usr/local/lib/mh/slocal
   to $HOME/bin/rcvmail.

   Under stand-alone MH, post will automatically run this during local
   delivery.

   This program should be used ONLY if you have "mts sendmail" or "mts mh"
   or "mts mmdf1" set in your MH configuration.
 */

/*  */

#include "../h/mh.h"
#include "../h/dropsbr.h"
#include "../h/rcvmail.h"
#include "../zotnet/tws.h"
#include "../zotnet/mts.h"
#include <pwd.h>
#include <signal.h>
#ifndef	V7
#include <sys/ioctl.h>
#endif	not V7
#include <sys/stat.h>
#include <utmp.h>


#define	NVEC	100

/*  */

static struct swit switches[] = {
#define	ADDRSW	0
    "addr address", 0,
#define	USERSW	1
    "user name", 0,
#define	FILESW	2
    "file file", 0,
#define	SENDSW	3
    "sender address", 0,
#define	MBOXSW	4
    "mailbox file", 0,
#define	HOMESW	5
    "home directory", 0,

#define	MAILSW	6
    "maildelivery file", 0,

#define	VERBSW	7
    "verbose", 0,
#define	NVERBSW	8
    "noverbose", 0,

#define	DEBUGSW	9
    "debug", 0,

#define	HELPSW	10
    "help", 4,

    NULL, NULL
};

/*  */

static int  debug = 0;
static int  globbed = 0;
static int  parsed = 0;
static int  utmped = 0;
static int  verbose = 0;

static char *addr = NULLCP;
static char *user = NULLCP;
static char *info = NULLCP;
static char *file = NULLCP;
static char *sender = NULLCP;
static char *mbox = NULLCP;
static char *home = NULLCP;


static struct passwd *pw;


static char ddate[BUFSIZ];

struct tws *now;


static jmp_buf myctx;

/*  */

static struct pair {
    char   *p_name;
    char   *p_value;
    char    p_flags;
#define	P_NIL	0x00
#define	P_ADR	0x01
#define	P_HID	0x02
#define	P_CHK	0x04
} hdrs[NVEC + 1] = {
    "source", NULL, P_HID,
    "addr", NULL, P_HID,

    "Return-Path", NULL, P_ADR,
    "Reply-To", NULL, P_ADR,
    "From", NULL, P_ADR,
    "Sender", NULL, P_ADR,
    "To", NULL, P_ADR,
    "cc", NULL, P_ADR,
    "Resent-Reply-To", NULL, P_ADR,
    "Resent-From", NULL, P_ADR,
    "Resent-Sender", NULL, P_ADR,
    "Resent-To", NULL, P_ADR,
    "Resent-cc", NULL, P_ADR,

    NULL
};


static struct pair  vars[] = {
    "sender", NULL, P_NIL,
    "address", NULL, P_NIL,
    "size", NULL, P_NIL,
    "reply-to", NULL, P_CHK,
    "info", NULL, P_NIL,

    NULL
};

/*  */

extern char **environ;


long    lseek ();
#ifdef	SYS5
struct passwd *getpwnam ();
#endif	SYS5

static int localmail(), usr_delivery(), split(), parse(), logged_in(),
    timely(), usr_file(), usr_hook(), usr_pipe(), copyfile();
static void expand(), glob(), copyinfo(), adorn();
static struct pair *lookup();
static SIGDECL alrmser();

/*  */

/* ARGSUSED */

main(argc, argv)
	int argc;
	char **argv;
{
    int     fd;
    FILE   *fp    = stdin;
    char   *cp,
	   *mdlvr = NULL,
            buf[100],
            from[BUFSIZ],
            mailbox[BUFSIZ],
            tmpfil[BUFSIZ],
          **argp = argv + 1;

    invo_name = r1bindex (*argv, '/');
    m_foil (NULLCP);
    mts_init (invo_name);

/*  */

    while (cp = *argp++) {
	if (*cp == '-')
	    switch (smatch (++cp, switches)) {
		case AMBIGSW:
		    ambigsw (cp, switches);
		    done (1);
		case UNKWNSW:
		    adios (NULLCP, "-%s unknown", cp);
		case HELPSW:
		    (void) sprintf (buf, "%s [switches] [address info sender]",
			    invo_name);
		    help (buf, switches);
		    done (1);

		case ADDRSW:
		    if (!(addr = *argp++))/* allow -xyz arguments */
			adios (NULLCP, "missing argument to %s", argp[-2]);
		    continue;
		case USERSW:
		    if (!(user = *argp++))/* allow -xyz arguments */
			adios (NULLCP, "missing argument to %s", argp[-2]);
		    continue;
		case FILESW:
		    if (!(file = *argp++) || *file == '-')
			adios (NULLCP, "missing argument to %s", argp[-2]);
		    continue;
		case SENDSW:
		    if (!(sender = *argp++))/* allow -xyz arguments */
			adios (NULLCP, "missing argument to %s", argp[-2]);
		    continue;
		case MBOXSW:
		    if (!(mbox = *argp++) || *mbox == '-')
			adios (NULLCP, "missing argument to %s", argp[-2]);
		    continue;
		case HOMESW:
		    if (!(home = *argp++) || *home == '-')
			adios (NULLCP, "missing argument to %s", argp[-2]);
		    continue;

		case MAILSW:
		    if (!(cp = *argp++) || *cp == '-')
			adios (NULLCP, "missing argument to %s", argp[-2]);
		    if (mdlvr)
			adios (NULLCP, "only one maildelivery file at a time!");
		    mdlvr = cp;
		    continue;

		case VERBSW:
		    verbose++;
		    continue;
		case NVERBSW:
		    verbose = 0;
		    continue;

		case DEBUGSW:
		    debug++;
		    continue;
	    }

	switch (argp - (argv + 1)) {
	    case 1:
		addr = cp;
		break;

	    case 2:
		info = cp;
		break;

	    case 3:
		sender = cp;
		break;
	}
    }

/*  */

    if (addr == NULL)
	addr = getusr ();
    if (user == NULL)
	user = (cp = index (addr, '.')) ? ++cp : addr;
    if ((pw = getpwnam (user)) == NULL)
	adios (NULLCP, "no such local user as %s", user);

    if (chdir (pw -> pw_dir) == NOTOK)
	(void) chdir ("/");
    (void) umask (0077);

    if (geteuid () == 0) {
#ifdef	BSD41A
	(void) inigrp (pw -> pw_name, pw -> pw_gid);
#endif	BSD41A
	(void) setgid (pw -> pw_gid);
#ifdef	BSD42
	(void) initgroups (pw -> pw_name, pw -> pw_gid);
#endif	BSD42
	(void) setuid (pw -> pw_uid);
    }

    if (info == NULL)
	info = "";

    setbuf (stdin, NULLCP);

    if (file == NULL) {
	if ((fd = copyfile (fileno (stdin), file = tmpfil, 1)) == NOTOK)
	    adios (NULLCP, "unable to create temporary file");
	if (debug)
	    fprintf (stderr, "temporary file \"%s\" selected\n", tmpfil);
	else
	    (void) unlink (tmpfil);
   	if ((fp = fdopen (fd, "r+")) == NULL)
	    adios (NULLCP, "unable to access temporary file");
    }
    else
	fd = fileno (stdin);

    from[0] = NULL;
    if (sender == NULL)
	copyinfo (fp, from);


    if (mbox == NULL) {
	(void) sprintf (mailbox, "%s/%s",
		mmdfldir[0] ? mmdfldir : pw -> pw_dir,
		mmdflfil[0] ? mmdflfil : pw -> pw_name);
	mbox = mailbox;
    }
    if (home == NULL)
	home = pw -> pw_dir;

    if ((now = dtwstime ()) == NULL)
	adios (NULLCP, "unable to ascertain local time");
    (void) sprintf (ddate, "Delivery-Date: %s\n", dtimenow ());

    if (debug) {
	fprintf (stderr, "addr=\"%s\" user=\"%s\" info=\"%s\" file=\"%s\"\n",
		addr, user, info, file);
	fprintf (stderr, "sender=\"%s\" mbox=\"%s\" home=\"%s\" from=\"%s\"\n",
		sender, mbox, home, from);
	fprintf (stderr, "ddate=\"%s\" now=%02d:%02d\n",
		ddate, now -> tw_hour, now -> tw_min);
    }

    done (localmail (fd, from, mdlvr) != NOTOK ? RCV_MOK : RCV_MBX);
}

/*  */

static int
localmail(fd, from, mdlvr)
	int fd;
	char *from, *mdlvr;
{
    if (usr_delivery (fd, mdlvr ? mdlvr : ".maildelivery", 0) != NOTOK)
	return OK;

    if (usr_delivery (fd, maildelivery, 1) != NOTOK)
	return OK;

#ifdef	notdef
    if (verbose)
	printf ("(invoking hook)\n");
    if (usr_hook (fd, mbox) != NOTOK)
	return OK;
#endif	notdef

    if (verbose)
	printf ("(trying normal delivery)\n");
    return usr_file (fd, mbox, from);
}

/*  */

#define	matches(a,b)	(stringdex (b, a) >= 0)

static int
usr_delivery(fd, delivery, su)
	int fd, su;
	char *delivery;
{
    int     i,
	    accept,
            status,
            won,
	    vecp;
    register char  *cp,
                   *action,
                   *field,
                   *pattern,
		   *string;
    char    buffer[BUFSIZ],
	    tmpbuf[BUFSIZ],
           *vec[NVEC];
    struct stat st;
    register struct pair   *p;
    register FILE  *fp;

    if ((fp = fopen (delivery, "r")) == NULL)
	return NOTOK;
    if (fstat (fileno (fp), &st) == NOTOK
	    || (st.st_uid != 0 && (su || st.st_uid != pw -> pw_uid))
	    || st.st_mode & 0022) {
	if (verbose) {
	    printf ("%s: ownership/modes bad (%d, %d,%d,0%o)\n",
		    delivery, su, pw -> pw_uid, st.st_uid, st.st_mode);
	    (void) fflush (stdout);
	}
	return NOTOK;
    }

    won = 0;
    while (fgets (buffer, sizeof buffer, fp) != NULL) {
	if (*buffer == '#')
	    continue;
	if (cp = index (buffer, '\n'))
	    *cp = NULL;
	if ((vecp = split (buffer, vec)) < 5)
	    continue;
	if (debug)
	    for (i = 0; vec[i]; i++)
		fprintf (stderr, "vec[%d]: \"%s\"\n", i, vec[i]);

	field = vec[0];
	pattern = vec[1];
	action = vec[2];

	switch (vec[3][0]) {
	    case '?':
		if (won)
		    continue;	/* else fall */
	    case 'A':
	    case 'a':
		accept = 1;
		break;

	    case 'R':
	    case 'r':
	    default:
		accept = 0;
		break;
	}

	string = vec[4];

	if (vecp > 5) {
	    if (uleq (vec[5], "select")) {
		if (logged_in () != NOTOK)
		    continue;
		if (vecp > 7 && timely (vec[6], vec[7]) == NOTOK)
		    continue;
	    }
	}

	switch (*field) {
	    case '*':
		break;

	    case 'd':
		if (uleq (field, "default")) {
		    if (won)
			continue;
		    break;
		}		/* else fall */

	    default:
		if (!parsed && parse (fd) == NOTOK) {
		    (void) fclose (fp);
		    return NOTOK;
		}
		if ((p = lookup (hdrs, field)) == NULL
			|| !matches (p -> p_value, pattern))
		    continue;
		break;
	}

	switch (*action) {
	    case 'q':
		if (!uleq (action, "qpipe"))
		    continue;	/* else fall */
	    case '^':
		expand (tmpbuf, string, fd);
		if (split (tmpbuf, vec) < 1)
		    continue;
		status = usr_pipe (fd, tmpbuf, vec[0], vec);
		break;

	    case 'p':
		if (!uleq (action, "pipe"))
		    continue;	/* else fall */
	    case '|':
		vec[2] = "sh";
		vec[3] = "-c";
		expand (tmpbuf, string, fd);
		vec[4] = tmpbuf;
		vec[5] = NULL;
		status = usr_pipe (fd, tmpbuf, "/bin/sh", vec + 2);
		break;

	    case 'f':
		if (!uleq (action, "file"))
		    continue;	/* else fall */
	    case '>':
		status = usr_file (fd, string, NULLCP);
		break;

	    case 'd':
		if (!uleq (action, "destroy"))
		    continue;
		status = OK;
		break;
	}

	if (accept) {
	    if (status == NOTOK) {
		won = 0;
		break;
	    }
	    won++;
	}
    }

    (void) fclose (fp);
    return (won ? OK : NOTOK);
}

/*  */

#define	QUOTE	'\\'

static int
split(cp, vec)
	char *cp, **vec;
{
    register int    i;
    register char  *s;

    for (i = 0, s = cp; i <= NVEC;) {
	vec[i] = NULL;
	while (isspace (*s) || *s == ',')
	    *s++ = NULL;
	if (*s == NULL)
	    break;

	if (*s == '"') {
	    for (vec[i++] = ++s; *s != NULL && *s != '"'; s++)
		if (*s == QUOTE) {
		    if (*++s == '"')
			(void) strcpy (s - 1, s);
		    s--;
		}
	    if (*s == '"')
		*s++ = NULL;
	    continue;
	}
	if (*s == QUOTE && *++s != '"')
	    s--;
	vec[i++] = s++;

	while (*s != NULL && !isspace (*s) && *s != ',')
	    s++;
    }
    vec[i] = NULL;

    return i;
}

/*  */

static int
parse(fd)
	register int fd;
{
    register int    i,
                    state;
    int     fd1;
    register char  *cp,
                   *dp,
                   *lp;
    char    name[NAMESZ],
            field[BUFSIZ];
    register struct pair   *p,
			   *q;
    register FILE  *in;

    if (parsed++)
	return OK;

    if ((fd1 = dup (fd)) == NOTOK)
	return NOTOK;
    if ((in = fdopen (fd1, "r")) == NULL) {
	(void) close (fd1);
	return NOTOK;
    }
    rewind (in);

    if (p = lookup (hdrs, "source"))
	p -> p_value = getcpy (sender);
    if (p = lookup (hdrs, "addr"))
	p -> p_value = getcpy (addr);

    for (i = 0, state = FLD;;) {
	switch (state = m_getfld (state, name, field, sizeof field, in)) {
	    case FLD:
	    case FLDEOF:
	    case FLDPLUS:
		lp = add (field, NULLCP);
		while (state == FLDPLUS) {
		    state = m_getfld (state, name, field, sizeof field, in);
		    lp = add (field, lp);
		}
		for (p = hdrs; p -> p_name; p++)
		    if (uleq (p -> p_name, name)) {
			if (!(p -> p_flags & P_HID)) {
			    if (cp = p -> p_value)
				if (p -> p_flags & P_ADR) {
				    dp = cp + strlen (cp) - 1;
				    if (*dp == '\n')
					*dp = NULL;
				    cp = add (",\n\t", cp);
				}
				else
				    cp = add ("\t", cp);
			    p -> p_value = add (lp, cp);
			}
			free (lp);
			break;
		    }
		if (p -> p_name == NULL && i < NVEC) {
		    p -> p_name = getcpy (name);
		    p -> p_value = lp;
		    p -> p_flags = P_NIL;
		    p++, i++;
		    p -> p_name = NULL;
		}
		if (state != FLDEOF)
		    continue;
		break;

	    case BODY:
	    case BODYEOF:
	    case FILEEOF:
		break;

	    case LENERR:
	    case FMTERR:
		advise (NULLCP, "format error in message");
		break;

	    default:
		advise (NULLCP, "internal error");
		(void) fclose (in);
		return NOTOK;
	}
	break;
    }
    (void) fclose (in);

    if (p = lookup (vars, "reply-to")) {
	if ((q = lookup (hdrs, "reply-to")) == NULL || q -> p_value == NULL)
	    q = lookup (hdrs, "from");
	p -> p_value = getcpy (q ? q -> p_value : "");
	p -> p_flags &= ~P_CHK;
	if (debug)
	    fprintf (stderr, "vars[%d]: name=\"%s\" value=\"%s\"\n",
		    p - vars, p -> p_name, p -> p_value);
    }
    if (debug)
	for (p = hdrs; p -> p_name; p++)
	    fprintf (stderr, "hdrs[%d]: name=\"%s\" value=\"%s\"\n",
		p - hdrs, p -> p_name, p -> p_value);

    return OK;
}

/*  */

#define	LPAREN	'('
#define	RPAREN	')'

static void
expand(s1, s2, fd)
	register char *s1, *s2;
	int fd;
{
    register char   c,
                   *cp;
    register struct pair   *p;

    if (!globbed)
	glob (fd);

    while (c = *s2++)
	if (c != '$' || *s2 != LPAREN)
	    *s1++ = c;
	else {
	    for (cp = ++s2; *s2 && *s2 != RPAREN; s2++)
		continue;
	    if (*s2 != RPAREN) {
		s2 = --cp;
		continue;
	    }
	    *s2++ = NULL;
	    if (p = lookup (vars, cp)) {
		if (!parsed && (p -> p_flags & P_CHK))
		    (void) parse (fd);

		(void) strcpy (s1, p -> p_value);
		s1 += strlen (s1);
	    }
	}
    *s1 = NULL;
}

/*  */

static void
glob(fd)
	register int  fd;
{
    char buffer[BUFSIZ];
    struct stat st;
    register struct pair   *p;

    if (globbed++)
	return;

    if (p = lookup (vars, "sender"))
	p -> p_value = getcpy (sender);
    if (p = lookup (vars, "address"))
	p -> p_value = getcpy (addr);
    if (p = lookup (vars, "size")) {
	(void) sprintf (buffer, "%d",
		fstat (fd, &st) != NOTOK ? (int) st.st_size : 0);
	p -> p_value = getcpy (buffer);
    }
    if (p = lookup (vars, "info"))
	p -> p_value = getcpy (info);

    if (debug)
	for (p = vars; p -> p_name; p++)
	    fprintf (stderr, "vars[%d]: name=\"%s\" value=\"%s\"\n",
		    p - vars, p -> p_name, p -> p_value);
}

/*  */

static struct pair *
lookup(pairs, key)
	register struct pair *pairs;
	register char *key;
{
    register char  *cp;

    for (; cp = pairs -> p_name; pairs++)
	if (uleq (cp, key))
	    return pairs;

    return NULL;
}

/*  */

static int
logged_in()
{
    struct utmp ut;
    register FILE  *uf;

    if (utmped)
	return utmped;

    if ((uf = fopen ("/etc/utmp", "r")) == NULL)
	return NOTOK;

    while (fread ((char *) &ut, sizeof ut, 1, uf) == 1)
	if (ut.ut_name[0] != NULL
		&& strncmp (user, ut.ut_name, sizeof ut.ut_name) == 0) {
	    if (debug)
		continue;
	    (void) fclose (uf);
	    return (utmped = DONE);
	}

    (void) fclose (uf);
    return (utmped = NOTOK);
}


static int
timely(t1, t2)
	char *t1, *t2;
{
#define	check(t,a,b)		if (t < a || t > b) return NOTOK
#define	cmpar(h1,m1,h2,m2)	if (h1 < h2 || (h1 == h2 && m1 < m2)) return OK

    int     t1hours,
            t1mins,
            t2hours,
            t2mins;

    if (sscanf (t1, "%d:%d", &t1hours, &t1mins) != 2)
	return NOTOK;
    check (t1hours, 0, 23);
    check (t1mins, 0, 59);

    if (sscanf (t2, "%d:%d", &t2hours, &t2mins) != 2)
	return NOTOK;
    check (t2hours, 0, 23);
    check (t2mins, 0, 59);

    cmpar (now -> tw_hour, now -> tw_min, t1hours, t1mins);
    cmpar (t2hours, t2mins, now -> tw_hour, now -> tw_min);

    return NOTOK;
}

/*  */

static int
usr_file(fd, mailbox, from)
	int fd;
	char *mailbox, *from;
{
    int	    md,
	    mapping;
    register char  *bp;
    char    buffer[BUFSIZ];

    if (verbose)
	printf ("\tdelivering to file \"%s\"", mailbox);
    if (from && *from) {
	(void) mbx_uucp ();
	if (verbose)
	    printf (" (uucp style)");
	(void) sprintf (buffer, "%s%s", from, ddate);
	bp = buffer;
	mapping = 0;
    }
    else {
	bp = ddate;
	mapping = 1;
    }
    if (verbose)
	(void) fflush (stdout);

    if ((md = mbx_open (mailbox, pw -> pw_uid, pw -> pw_gid, m_gmprot ()))
	    == NOTOK) {
	adorn ("", "unable to open:");
	return NOTOK;
    }

    (void) lseek (fd, 0L, 0);
    if (mbx_copy (mailbox, md, fd, mapping, bp, verbose) == NOTOK) {
	adorn ("", "error writing to:");
	return NOTOK;
    }

    (void) mbx_close (mailbox, md);
    if (verbose) {
	printf (", done.\n");
	(void) fflush (stdout);
    }
    return OK;
}

/*  */

#ifdef	notdef
static int
usr_hook(fd, mailbox)
	int fd;
	char *mailbox;
{
    int     i,
            vecp;
    char    receive[BUFSIZ],
            tmpfil[BUFSIZ],
           *vec[NVEC];

    if ((fd = copyfile (fd, tmpfil, 0)) == NOTOK) {
	if (verbose)
	    adorn ("unable to copy message; skipping hook\n");
	return NOTOK;
    }
    (void) chown (tmpfil, pw -> pw_uid, pw -> pw_gid);

    vecp = 1;
    (void) sprintf (receive, "%s/.mh_receive", pw -> pw_dir);
    switch (access (receive, 01)) {
	case NOTOK:
	    (void) sprintf (receive, "%s/bin/rcvmail", pw -> pw_dir);
	    if (access (receive, 01) == NOTOK) {
		(void) unlink (tmpfil);
		if (verbose) {
		    printf ("\tnot present\n");
		    (void) fflush (stdout);
		}
		return NOTOK;
	    }
	    vec[vecp++] = addr;
	    vec[vecp++] = tmpfil;
	    vec[vecp++] = sender;
	    break;

	default:
	    vec[vecp++] = tmpfil;
	    vec[vecp++] = mailbox;
	    vec[vecp++] = home;
	    vec[vecp++] = addr;
	    vec[vecp++] = sender;
	    break;
    }
    vec[0] = r1bindex (receive, '/');
    vec[vecp] = NULL;

    i = usr_pipe (fd, "rcvmail", receive, vec);
    (void) unlink (tmpfil);

    return i;
}
#endif	notdef

/*  */

static int
usr_pipe(fd, cmd, pgm, vec)
	int fd;
	char *cmd, *pgm, **vec;
{
    int     bytes,
	    i,
            child_id,
            status;
    struct stat st;

    if (verbose) {
	printf ("\tdelivering to pipe \"%s\"", cmd);
	(void) fflush (stdout);
    }
    (void) lseek (fd, 0L, 0);

    for (i = 0; (child_id = fork ()) == NOTOK && i < 5; i++)
	sleep (5);
    switch (child_id) {
	case NOTOK:
	    adorn ("fork", "unable to");
	    return NOTOK;

	case OK:
	    if (fd != 0)
		(void) dup2 (fd, 0);
	    (void) freopen ("/dev/null", "w", stdout);
	    (void) freopen ("/dev/null", "w", stderr);
	    if (fd != 3)
		(void) dup2 (fd, 3);
	    closefds (4);
#ifdef	TIOCNOTTY
	    if ((fd = open ("/dev/tty", 2)) != NOTOK) {
		(void) ioctl (fd, TIOCNOTTY, NULLCP);
		(void) close (fd);
	    }
#endif	TIOCNOTTY
#ifdef	BSD42
	    (void) setpgrp (0, getpid ());
#endif	BSD42

	    *environ = NULL;
	    (void) putenv ("USER", pw -> pw_name);
	    (void) putenv ("HOME", pw -> pw_dir);
	    (void) putenv ("SHELL", pw -> pw_shell);

	    execvp (pgm, vec);
	    _exit (-1);

	default:
	    switch (setjmp (myctx)) {
		case OK:
		    (void) signal (SIGALRM, alrmser);
		    bytes = fstat (fd, &st) != NOTOK ? (int) st.st_size : 100;
		    if (bytes <= 0)
			bytes = 100;
		    (void) alarm ((unsigned) (bytes * 60 + 300));

		    status = pidwait (child_id, OK);

		    (void) alarm (0);
#ifdef	MMDFI
		    if (status == RP_MOK || status == RP_OK)
			status = 0;
#endif	MMDFI
		    if (verbose) {
			if (status == 0)
			    printf (", wins.\n");
			else
			    if ((status & 0xff00) == 0xff00)
				printf (", system error\n");
			    else
				(void) pidstatus (status, stdout, ", loses");
			(void) fflush (stdout);
		    }
		    return (status == 0 ? OK : NOTOK);

		default:
#ifndef	BSD42
		    (void) kill (child_id, SIGKILL);
#else	BSD42
		    (void) killpg (child_id, SIGKILL);
#endif	BSD42
		    if (verbose) {
			printf (", timed-out; terminated\n");
			(void) fflush (stdout);
		    }
		    return NOTOK;
	    }
    }
}

/*  */

/* ARGSUSED */

static SIGDECL
alrmser(i)
	int i;
{
    longjmp (myctx, DONE);
}

/*  */

static void
copyinfo(fp, from)
	register FILE *fp;
	char *from;
{
    int     i;
    register char  *cp;
    static char buffer[BUFSIZ];

    if (fgets (from, BUFSIZ, fp) == NULL)
	adios (NULLCP, "no message");

    if (strncmp (from, "From ", i = strlen ("From "))) {
	rewind (fp);
	*from = NULL;
	return;
    }

    (void) strcpy (buffer, from + i);
    if (cp = index (buffer, '\n')) {
	*cp = NULL;
	cp -= 24;
	if (cp < buffer)
	    cp = buffer;
    }
    else
	cp = buffer;
    *cp = NULL;

    for (cp = buffer + strlen (buffer) - 1; cp >= buffer; cp--)
	if (isspace (*cp))
	    *cp = NULL;
	else
	    break;
    sender = buffer;
    rewind (fp);
}

/*  */

static int
copyfile(qd, tmpfil, fold)
	int qd, fold;
	register char *tmpfil;
{
    register int    i,
                    fd1,
                    fd2;
    char    buffer[BUFSIZ];
    register FILE  *qfp,
		   *ffp;

    (void) strcpy (tmpfil, m_tmpfil (invo_name));
    if ((fd1 = creat (tmpfil, 0600)) == NOTOK)
	return NOTOK;
    (void) close (fd1);
    if ((fd1 = open (tmpfil, 2)) == NOTOK)
	return NOTOK;

    if (!fold) {
	while ((i = read (qd, buffer, sizeof buffer)) > 0)
	    if (write (fd1, buffer, i) != i) {
you_lose: ;
		(void) close (fd1);
		(void) unlink (tmpfil);
		return NOTOK;
	    }
	if (i == NOTOK)
	    goto you_lose;
	(void) lseek (fd1, 0L, 0);
	return fd1;
    }

    if ((fd2 = dup (qd)) == NOTOK) {
	(void) close (fd1);
	return NOTOK;
    }
    if ((qfp = fdopen (fd2, "r")) == NULL) {
	(void) close (fd1);
	(void) close (fd2);
	return NOTOK;
    }

    if ((fd2 = dup (fd1)) == NOTOK) {
	(void) close (fd1);
	(void) fclose (qfp);
	return NOTOK;
    }
    if ((ffp = fdopen (fd2, "r+")) == NULL) {
	(void) close (fd1);
	(void) close (fd2);
	(void) fclose (qfp);
	return NOTOK;
    }

    i = strlen ("From ");
    while (fgets (buffer, sizeof buffer, qfp)) {
	if (!strncmp (buffer, "From ", i))
	    putc ('>', ffp);
	fputs (buffer, ffp);
	if (ferror (ffp)) {
	    (void) close (fd1);
	    (void) fclose (ffp);
	    (void) fclose (qfp);
	    return NOTOK;
	}
    }

    (void) fclose (ffp);
    if (ferror (qfp)) {
	(void) close (fd1);
	(void) fclose (qfp);
	return NOTOK;
    }
    (void) fclose (qfp);

    (void) lseek (fd1, 0L, 0);

    return fd1;
}

/*  */

/* VARARGS2 */

static void
adorn(what, fmt, a, b, c, d, e, f)
	char   *what, *fmt, *a, *b, *c, *d, *e, *f;
{
    char   *cp = invo_name;

    if (!verbose)
	return;
    printf (", ");

    invo_name = NULL;
    advise (what, fmt, a, b, c, d, e, f);
    invo_name = cp;
}

/*
 * XXX We need to force config.o to be linked in to get around
 * library order problems
 */
/* XXX don't bother if linking with the shared library */
#ifndef SHARED
static void
kludge()
{
	(void)libpath((char *)0);
}
#endif
