4.3BSD/usr/contrib/mh/uip/slocal.c

Compare this file to the similar file:
Show the results in this format:

/* 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
};

struct pair *lookup ();


static struct pair  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;

void	adorn ();
int	alrmser ();


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

/*  */

/* ARGSUSED */

main (argc, argv, envp)
int     argc;
char  **argv,
      **envp;
{
    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  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	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	int alrmser (i)
int     i;
{
    longjmp (myctx, DONE);
}

/*  */

static	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;
}