view uip/msh.c @ 16:07f8972434be

fix for Yosemita Clang
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Wed, 12 Nov 2014 02:46:27 +0900
parents 441a2190cfae
children
line wrap: on
line source

/* msh.c - The MH shell (sigh) */
#ifndef	lint
static char ident[] = "@(#)$Id: msh.c,v 1.1.1.1 2005/04/18 14:46:07 kono Exp $";
#endif	/* lint */

/* TODO:
	Keep more status information in maildrop map
 */

#include "../h/mh.h"
#include "../h/dropsbr.h"
#include "../h/formatsbr.h"
#include "../h/scansbr.h"
#include "../zotnet/tws.h"
#include <stdio.h>
#include <unistd.h>
#include "../zotnet/mts.h"
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifndef	SYS5
#include <sgtty.h>
#else	/* SYS5 */
#include <termio.h>
#ifndef	NOIOCTLH
#include <sys/ioctl.h>
#endif	/* NOIOCTLH */
#endif	/* SYS5 */
#include <pwd.h>
#include <setjmp.h>
#include <signal.h>
#include "../h/mshsbr.h"
#include "../h/vmhsbr.h"
#ifdef LOCALE
#include	<locale.h>
#endif
#ifdef READLINE
#include <readline/readline.h>
#endif 
#ifndef	MIME
#define	MIMEminc(a)	(a)
#else
#define	MIMEminc(a)	0
#endif

#define	QUOTE	'\\'		/* sigh */


/*  */

static struct swit switches[] = {
#define	IDSW	0
    "idstart number", -7,	/* interface from bbc */
#define	FDSW	1
    "idstop number", -6,	/*  .. */
#define	QDSW	2
    "idquit number", -6,	/*  .. */
#define	NMSW	3
    "idname BBoard", -6,	/*  .. */

#define	PRMPTSW	4
    "prompt string", 0,

#define	SCANSW	5
    "scan", 0,
#define	NSCANSW	6
    "noscan", 0,

#define	READSW	7
    "vmhread fd", -7,
#define	WRITESW	8
    "vmhwrite fd", -8,	

#define	PREADSW	9
    "popread fd", -7,
#define	PWRITSW	10
    "popwrite fd", -8,

#define	TCURSW	11
    "topcur", 0,
#define	NTCURSW	12
    "notopcur", 0,

#define	HELPSW	13
    "help", 4,

    NULL, 0
};

/*  */
				/* FOLDER */
char  *fmsh = NULL;		/* folder instead of file */
int    modified;		/* command modified folder */
struct msgs *mp;		/* used a lot */
static int   nMsgs = 0;
struct Msg  *Msgs = NULL;	/* Msgs[0] not used */
static FILE *fp;		/* input file */
static FILE *yp = NULL;		/* temporary file */
static int  mode;		/* mode of file */
static int  numfds = 0;		/* number of files cached */
static int  maxfds = 0;		/* number of files cached to be cached */
static time_t mtime = (time_t) 0;/* mtime of file */


				/* VMH */
#define	ALARM	((unsigned int) 10)
#define	ttyN(c)	ttyNaux ((c), NULLCP)

static int  vmh = 0;

static int  vmhpid = OK;
static int  vmhfd0;
static int  vmhfd1;
static int  vmhfd2;

static int  vmhtty = NOTOK;

#define	SCAN	1
#define	STATUS	2
#define	DISPLAY	3
#define	NWIN	DISPLAY

static int  topcur = 0;

static int  numwins = 0;
static int  windows[NWIN + 1];

static jmp_buf peerenv;

void	padios (), padvise ();
static TYPESIG	alrmser ();


#if defined(BPOP) || defined(NNTP)
				/* POP */

int	pmsh = 0;		/* BPOP enabled */

extern char response[];
#endif	/* BPOP || NNTP */


				/* PARENT */
static int  pfd = NOTOK;	/* fd parent is reading from */
static int  ppid = 0;		/* pid of parent */


				/* COMMAND */
int     interactive;		/* running from a /dev/tty */
int     redirected;		/* re-directing output */
FILE  *sp = NULL;		/* original stdout */

char   *cmd_name;		/* command being run */

char    myfilter[BUFSIZ];	/* path to mhl.forward */

static char *myprompt = "(%s) ";/* prompting string */


				/* BBOARDS */
static int    gap;		/* gap in BBoard-ID:s */

static char *myname = NULL;	/* BBoard name */

char   *BBoard_ID = "BBoard-ID";/* BBoard-ID constant */

				/* SIGNALS */
TYPESIG (*istat) ();		/* original SIGINT */
static TYPESIG  (*pstat) ();	/* current SIGPIPE */
TYPESIG (*qstat) ();		/* original SIGQUIT */
#ifdef	SIGTSTP
static TYPESIG  (*tstat) ();	/* original SIGTSTP */
#endif	/* SIGTSTP */
int     interrupted;		/* SIGINT detected */
int     broken_pipe;		/* SIGPIPE detected */
int     told_to_quit;		/* SIGQUIT detected */

#if defined(BSD42) || defined(SVR4)
int     should_intr;		/* signal handler should interrupt call */
jmp_buf sigenv;			/* the environment pointer */
#endif	/* BSD42 || SVR4 */

static TYPESIG	intrser (), pipeser (), quitser ();


#ifndef	__STDC__
#ifdef	SYS5
struct passwd  *getpwnam ();
#endif	/* SYS5 */
#endif

static int	read_map(), read_file(), check_folder(), getargs(), parse();
static int	getcmds(), init_io(), peerwait();
static void finaux_io(); 
static int     initaux_io();
static int	pINI(), pQRY(), pQRY1(), pQRY2(), pCMD(), pFIN();
static int	ttyR(), ttyNaux(), winN(), winR(), winX();
static		void msh(), m_gMsgs(), scanrange(), scanstring(), quit();
static		void fin_io(), m_init();
#if defined(BPOP) || defined(NNTP)
static int	read_pop();
#endif

void readids (int id);
void display_info (int scansw);
/*  */

/* ARGSUSED */

main (argc, argv)
int     argc;
char  **argv;
{
    int	    id = 0,
            scansw = 0,
    	    vmh1 = 0,
            vmh2 = 0;
#if defined(BPOP) || defined(NNTP)
    int	    pmsh1 = 0,
	    pmsh2 = 0;
#endif	/* BPOP || NNTP */
    char   *cp,
           *file = NULL,
           *folder = NULL,
          **ap,
          **argp,
            buf[80],
           *arguments[MAXARGS];

#ifdef LOCALE
	setlocale(LC_ALL, "");
#endif
#ifdef JAPAN
	ml_init();
#endif /* JAPAN */
    invo_name = r1bindex (argv[0], '/');
    mts_init (invo_name);
    if ((cp = m_find (invo_name)) != NULL) {
	ap = brkstring (cp = getcpy (cp), " ", "\n");
	ap = copyip (ap, arguments);
    }
    else
	ap = arguments;
    (void) copyip (argv + 1, ap);
    argp = arguments;

/*  */

    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] file", invo_name);
		    help (buf, switches);
		    done (1);

		case IDSW: 
		    if (!(cp = *argp++) || *cp == '-')
			adios (NULLCP, "missing argument to %s", argp[-2]);
		    if ((id = atoi (cp)) < 1)
			adios (NULLCP, "bad argument %s %s", argp[-2], cp);
		    continue;
		case FDSW: 
		    if (!(cp = *argp++) || *cp == '-')
			adios (NULLCP, "missing argument to %s", argp[-2]);
		    if ((pfd = atoi (cp)) <= 1)
			adios (NULLCP, "bad argument %s %s", argp[-2], cp);
		    continue;
		case QDSW: 
		    if (!(cp = *argp++) || *cp == '-')
			adios (NULLCP, "missing argument to %s", argp[-2]);
		    if ((ppid = atoi (cp)) <= 1)
			adios (NULLCP, "bad argument %s %s", argp[-2], cp);
		    continue;
		case NMSW:
		    if (!(myname = *argp++) || *myname == '-')
			adios (NULLCP, "missing argument to %s", argp[-2]);
		    continue;

		case SCANSW: 
		    scansw++;
		    continue;
		case NSCANSW: 
		    scansw = 0;
		    continue;

		case PRMPTSW:
		    if (!(myprompt = *argp++) || *myprompt == '-')
			adios (NULLCP, "missing argument to %s", argp[-2]);
		    continue;

		case READSW: 
		    if (!(cp = *argp++) || *cp == '-')
			adios (NULLCP, "missing argument to %s", argp[-2]);
		    if ((vmh1 = atoi (cp)) < 1)
			adios (NULLCP, "bad argument %s %s", argp[-2], cp);
		    continue;
		case WRITESW: 
		    if (!(cp = *argp++) || *cp == '-')
			adios (NULLCP, "missing argument to %s", argp[-2]);
		    if ((vmh2 = atoi (cp)) < 1)
			adios (NULLCP, "bad argument %s %s", argp[-2], cp);
		    continue;

		case PREADSW: 
		    if (!(cp = *argp++) || *cp == '-')
			adios (NULLCP, "missing argument to %s", argp[-2]);
#if defined(BPOP) || defined(NNTP)
		    if ((pmsh1 = atoi (cp)) < 1)
			adios (NULLCP, "bad argument %s %s", argp[-2], cp);
#endif	/* BPOP || NNTP */
		    continue;
		case PWRITSW: 
		    if (!(cp = *argp++) || *cp == '-')
			adios (NULLCP, "missing argument to %s", argp[-2]);
#if defined(BPOP) || defined(NNTP)
		    if ((pmsh2 = atoi (cp)) < 1)
			adios (NULLCP, "bad argument %s %s", argp[-2], cp);
#endif	/* BPOP || NNTP */
		    continue;

		case TCURSW:
		    topcur++;
		    continue;
		case NTCURSW:
		    topcur = 0;
		    continue;
	    }
	if (*cp == '+' || *cp == '@') {
	    if (folder)
		adios (NULLCP, "only one folder at a time!");
	    else
		folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
	}
	else
	    if (file)
		adios (NULLCP, "only one file at a time!");
	    else
		file = cp;
    }

/*  */

    if (!file && !folder)
	file = "./msgbox";
    if (file && folder)
	adios (NULLCP, "use a file or a folder, not both");
    (void) strcpy (myfilter, libpath (mhlforward));
#ifdef	FIOCLEX
    if (pfd > 1)
	(void) ioctl (pfd, FIOCLEX, NULLCP);
#endif	/* FIOCLEX */

#if defined(BSD42) || defined(SVR4)
    should_intr = 0;
#endif	/* BSD42 || SVR4 */
    setsigx (istat, SIGINT, intrser);
    setsigx (qstat, SIGQUIT, quitser);

    (void) sc_width ();		/* MAGIC... */

    if (vmh = vmh1 && vmh2) {
	(void) rcinit (vmh1, vmh2);
	(void) pINI ();
	(void) signal (SIGINT, SIG_IGN);
	(void) signal (SIGQUIT, SIG_IGN);
#ifdef	SIGTSTP
	tstat = signal (SIGTSTP, SIG_IGN);
#endif	/* SIGTSTP */
    }

#if defined(BPOP) || defined(NNTP)
    if (pmsh = pmsh1 && pmsh2) {
	cp = getenv ("MHPOPDEBUG");
#ifdef	NNTP
	if (pop_set (pmsh1, pmsh2, cp && *cp, myname) == NOTOK)
#else	/* NNTP */
	if (pop_set (pmsh1, pmsh2, cp && *cp) == NOTOK)
#endif	/* NNTP */
	    padios (NULLCP, "%s", response);
	if (folder)
	    file = folder, folder = NULL;
    }
#endif	/* BPOP || NNTP */

    if (folder)
	fsetup (folder);
    else
	setup (file);
    readids (id);
    display_info (id > 0 ? scansw : 0);

    msh (id > 0 ? scansw : 0);

    m_reset ();
    
    done (0);
}

/*  */

static struct swit mshcmds[] = {
#define	ADVCMD	0
    "advance", -7,
#define	ALICMD	1
    "ali", 0,
#define	EXPLCMD	2
    "burst", 0,
#define	COMPCMD	3
    "comp", 0,
#define	DISTCMD	4
    "dist", 0,
#define	EXITCMD	5
    "exit", 0,
#define	FOLDCMD	6
    "folder", 0,
#define	FORWCMD	7
    "forw", 0,
#define	HELPCMD	8
    "help", 0,
#define	INCMD	9
    "inc", 0,
#define	MARKCMD	10
    "mark", 0,
#define	MAILCMD	11
    "mhmail", 0,
#define	MHNCMD	12
    "mhn", MIMEminc(-3),
#define	MSGKCMD	13
    "msgchk", 0,
#define	NEXTCMD	14
    "next", 0,
#define	PACKCMD	15
    "packf", 0,
#define	PICKCMD	16
    "pick", 0,
#define	PREVCMD	17
    "prev", 0,
#define	QUITCMD	18
    "quit", 0,
#define	FILECMD	19
    "refile", 0,
#define	REPLCMD	20
    "repl", 0,
#define	RMMCMD	21
    "rmm", 0,
#define	SCANCMD	22
    "scan", 0,
#define	SENDCMD	23
    "send", 0,
#define	SHOWCMD	24
    "show", 0,
#define	SORTCMD	25
    "sortm", 0,
#define	WHATCMD	26
    "whatnow", 0,
#define	WHOMCMD	27
    "whom", 0,

    NULL, 0
};

/*  */

static void msh (scansw)
int     scansw;
{
    int     i;
    register char  *cp,
                  **ap;
    char    prompt[BUFSIZ],
           *vec[MAXARGS];
    struct Cmd  typein;
    register struct Cmd *cmdp;
    static int once_only = ADVCMD;

    (void) sprintf (prompt, myprompt, invo_name);
    cmdp = &typein;

#ifdef READLINE
    (void) initialize_readline();
#endif
    for (;;) {
	if (yp) {
	    (void) fclose (yp);
	    yp = NULL;
	}
	if (vmh) {
	    if ((i = getcmds (mshcmds, cmdp, scansw)) == EOF) {
		(void) rcdone ();
		return;
	    }
	}
	else {
	    (void) check_folder (scansw);
	    if ((i = getargs (prompt, mshcmds, cmdp)) == EOF) {
		(void) putchar ('\n');
		return;
	    }
	}
	cmd_name = mshcmds[i].sw;

	switch (i) {
	    case QUITCMD: 
		quit ();
		return;

	    case ADVCMD:
		if (once_only == ADVCMD)
		    once_only = i = SHOWCMD;
		else
		    i = mp -> curmsg != mp -> hghmsg ? NEXTCMD : EXITCMD;
		cmd_name = mshcmds[i].sw;
		/* and fall... */

	    case EXITCMD:
	    case EXPLCMD: 
	    case FOLDCMD: 
	    case FORWCMD: 	/* sigh */
	    case MARKCMD: 
	    case NEXTCMD: 
	    case PACKCMD: 
	    case PICKCMD: 
	    case PREVCMD: 
	    case RMMCMD: 
	    case SHOWCMD: 
	    case SCANCMD: 
	    case SORTCMD: 
		if ((cp = m_find (cmd_name)) != NULL) {
		    ap = brkstring (cp = getcpy (cp), " ", "\n");
		    ap = copyip (ap, vec);
		}
		else
		    ap = vec;
		break;

	    default: 
		cp = NULL;
		ap = vec;
		break;
	}
	(void) copyip (cmdp -> args + 1, ap);

	m_init ();

	if (!vmh && init_io (cmdp, vmh) == NOTOK) {
	    if (cp != NULL)
		free (cp);
	    continue;
	}
	modified = 0;
	redirected = vmh || cmdp -> direction != STDIO;

	switch (i) {
	    case ALICMD: 
	    case COMPCMD: 
	    case INCMD: 
	    case MAILCMD: 
	    case MSGKCMD: 
	    case SENDCMD: 
	    case WHATCMD: 
	    case WHOMCMD: 
		if (!vmh || ttyN (cmdp) != NOTOK)
		    forkcmd (vec, cmd_name);
		break;

	    case DISTCMD: 
		if (!vmh || ttyN (cmdp) != NOTOK)
		    distcmd (vec);
		break;

	    case EXPLCMD: 
		if (!vmh || winN (cmdp, DISPLAY, 1) != NOTOK)
		    explcmd (vec);
		break;

	    case FILECMD: 
		if (!vmh 
			|| (filehak (vec) == OK ? ttyN (cmdp)
					: winN (cmdp, DISPLAY, 1)) != NOTOK)
		    filecmd (vec);
		break;

	    case FOLDCMD: 
		if (!vmh || winN (cmdp, DISPLAY, 1) != NOTOK)
		    foldcmd (vec);
		break;

	    case FORWCMD: 
		if (!vmh || ttyN (cmdp) != NOTOK)
		    forwcmd (vec);
		break;

	    case HELPCMD: 
		if (!vmh || winN (cmdp, DISPLAY, 1) != NOTOK)
		    helpcmd (vec);
		break;

	    case EXITCMD:
	    case MARKCMD: 
		if (!vmh || winN (cmdp, DISPLAY, 1) != NOTOK)
		    markcmd (vec);
		break;

	    case MHNCMD:
#ifdef	MIME
		if (!vmh || ttyN (cmdp) != NOTOK)
		    mhncmd (vec);
#endif
		break;

	    case NEXTCMD: 
	    case PREVCMD: 
	    case SHOWCMD: 
		if (!vmh || winN (cmdp, DISPLAY, 1) != NOTOK)
		    showcmd (vec);
		break;

	    case PACKCMD: 
		if (!vmh 
			|| (packhak (vec) == OK ? ttyN (cmdp)
					: winN (cmdp, DISPLAY, 1)) != NOTOK)
		    packcmd (vec);
		break;

	    case PICKCMD: 
		if (!vmh || winN (cmdp, DISPLAY, 1) != NOTOK)
		    pickcmd (vec);
		break;

	    case REPLCMD: 
		if (!vmh || ttyN (cmdp) != NOTOK)
		    replcmd (vec);
		break;

	    case RMMCMD: 
		if (!vmh || winN (cmdp, DISPLAY, 1) != NOTOK)
		    rmmcmd (vec);
		break;

	    case SCANCMD: 
		if (!vmh || winN (cmdp, DISPLAY, 1) != NOTOK)
		    scancmd (vec);
		break;

	    case SORTCMD: 
		if (!vmh || winN (cmdp, DISPLAY, 1) != NOTOK)
		    sortcmd (vec);
		break;

	    default: 
		padios (NULLCP, "no dispatch for %s", cmd_name);
	}

	if (vmh) {
	    if (vmhtty != NOTOK)
		(void) ttyR (cmdp);
	    if (vmhpid > OK)
		(void) winR (cmdp);
	}
	else
	    fin_io (cmdp, vmh);
	if (cp != NULL)
	    free (cp);
	if (i == EXITCMD) {
	    quit ();
	    return;
	}
    }
}

/*  */

fsetup (folder)
char   *folder;
{
    register int msgnum;
    char   *maildir;
    struct stat st;

    maildir = m_maildir (folder);
    if (chdir (maildir) == NOTOK)
	padios (maildir, "unable to change directory to");
    if (!(mp = m_gmsg (folder)))
	padios (NULLCP, "unable to read folder %s", folder);
    if (mp -> hghmsg == 0)
	padios (NULLCP, "no messages in %s", folder);

    mode = m_gmprot ();
    mtime = stat (mp -> foldpath, &st) != NOTOK ? st.st_mtime : 0;

    m_gMsgs (mp -> hghmsg);

    for (msgnum = mp -> lowmsg; msgnum <= mp -> hghmsg; msgnum++) {
	Msgs[msgnum].m_bboard_id = 0;
	Msgs[msgnum].m_top = NOTOK;
	Msgs[msgnum].m_start = Msgs[msgnum].m_stop = 0L;
	Msgs[msgnum].m_scanl = NULL;
    }

    m_init ();

    fmsh = getcpy (folder);

#if !defined(BSD42) && !defined(linux) && defined(_NFILE)
    maxfds = _NFILE / 2;
#else	/* BSD42 || linux */
    maxfds = getdtablesize () / 2;
#endif	/* BSD42 || linux */
    if ((maxfds -= 2) < 1)
	maxfds = 1;
}

/*  */

setup (file)
char   *file;
{
    int     i,
            msgp;
#if defined(BPOP) || defined(NNTP)
    char    tmpfil[BUFSIZ];
#endif	/* BPOP || NNTP */
    struct stat st;

#if defined(BPOP) || defined(NNTP)
    if (pmsh) {
	(void) strcpy (tmpfil, m_tmpfil (invo_name));
	if ((fp = fopen (tmpfil, "w+")) == NULL)
	    padios (tmpfil, "unable to create");
	(void) unlink (tmpfil);
    }
    else
#endif	/* BPOP || NNTP */
    if ((fp = fopen (file, "r")) == NULL)
	padios (file, "unable to read");
#ifdef	FIOCLEX
    (void) ioctl (fileno (fp), FIOCLEX, NULLCP);
#endif	/* FIOCLEX */
    if (fstat (fileno (fp), &st) != NOTOK) {
	mode = (int) (st.st_mode & 0777), mtime = st.st_mtime;
	msgp = read_map (file, (long) st.st_size);
    }
    else {
	mode = m_gmprot (), mtime = 0;
	msgp = 0;
    }

    if ((msgp = read_file (msgp ? Msgs[msgp].m_stop : 0L, msgp + 1)) < 1)
	padios (NULLCP, "no messages in %s", myname ? myname : file);

    mp = (struct msgs  *) calloc ((unsigned) 1, MHSIZE (mp, 1, msgp + 1));
    if (mp == NULL)
	padios (NULLCP, "unable to allocate folder storage");

    mp -> hghmsg = msgp;
    mp -> nummsg = msgp;
    mp -> lowmsg = 1;
    mp -> curmsg = 0;

    mp -> foldpath = getcpy (myname ? myname : file);
    mp -> msgflags = 0;
#if defined(BPOP) || defined(NNTP)
    if (pmsh)
	mp -> msgflags |= READONLY;
    else {
#endif	/* BPOP || NNTP */
	(void) stat (file, &st);
	if (st.st_uid != getuid () || access (file, 02) == NOTOK)
	    mp -> msgflags |= READONLY;
#if defined(BPOP) || defined(NNTP)
    }
#endif	/* BPOP || NNTP */
    mp -> lowoff = 1;
    mp -> hghoff = mp -> hghmsg + 1;

#ifdef	MTR
    mp -> msgstats = (short *)
		calloc ((unsigned) 1, MHSIZEX (mp, mp -> lowmsg, mp -> hghmsg));
    if (mp -> msgstats == NULL)
	padios (NULLCP, "unable to allocate messages storage");
    mp -> msgstats = (mp -> msgbase = mp -> msgstats) - mp -> lowoff;
    if (mp -> msgstats < (short *)0)
	padios (NULLCP, "setup() botch -- you lose big");
#endif	/* MTR */
#if defined(BPOP) || defined(NNTP)
    if (pmsh) {
#ifndef	NNTP
	for (i = mp -> lowmsg; i <= mp -> hghmsg; i++) {
	    Msgs[i].m_top = i;
	    mp -> msgstats[i] = EXISTS | VIRTUAL;
	}
#else	/* NNTP */
	for (i = mp -> lowmsg; i <= mp -> hghmsg; i++) {
	    if (Msgs[i].m_top)			/* set in read_pop() */
		mp -> msgstats[i] = EXISTS | VIRTUAL;
	}
#endif	/* NNTP */
    }
    else
#endif	/* BPOP || NNTP */
    for (i = mp -> lowmsg; i <= mp -> hghmsg; i++)
	mp -> msgstats[i] = EXISTS;
    m_init ();

    mp -> msgattrs[0] = getcpy ("unseen");
    mp -> msgattrs[1] = NULL;

    m_unknown (fp);		/* the MAGIC invocation */    
    if (fmsh) {
	free (fmsh);
	fmsh = NULL;
    }
}

/*  */

static int  read_map (file, size)
char   *file;
long    size;
{
    register int    i,
                    msgp;
    register struct drop   *dp,
                           *mp;
    struct drop *rp;

#if defined(BPOP) || defined(NNTP)
    if (pmsh)
	return read_pop ();
#endif	/* BPOP || NNTP */

    if ((i = map_read (file, size, &rp, 1)) == 0)
	return 0;

    m_gMsgs (i);

    msgp = 1;
    for (dp = rp + 1; i-- > 0; msgp++, dp++) {
	mp = &Msgs[msgp].m_drop;
	mp -> d_id = dp -> d_id;
	mp -> d_size = dp -> d_size;
	mp -> d_start = dp -> d_start;
	mp -> d_stop = dp -> d_stop;
	Msgs[msgp].m_scanl = NULL;
    }
    free ((char *) rp);

    return (msgp - 1);
}

/*  */

static  int	read_file (pos, msgp)
register long	pos;
register int    msgp;
{
    register int    i;
    register struct drop   *dp,
                           *mp;
    struct drop *rp;

#if defined(BPOP) || defined(NNTP)
    if (pmsh)
	return (msgp - 1);
#endif	/* BPOP || NNTP */

    if ((i = mbx_read (fp, pos, &rp, 1)) <= 0)
	return (msgp - 1);

    m_gMsgs ((msgp - 1) + i);

    for (dp = rp; i-- > 0; msgp++, dp++) {
	mp = &Msgs[msgp].m_drop;
	mp -> d_id = 0;
	mp -> d_size = dp -> d_size;
	mp -> d_start = dp -> d_start;
	mp -> d_stop = dp -> d_stop;
	Msgs[msgp].m_scanl = NULL;
    }
    free ((char *) rp);

    return (msgp - 1);
}

/*  */

#if defined(BPOP) || defined(NNTP)
#ifdef	NNTP
static	int	pop_base = 0;

static	int	pop_statmsg (s)
register char *s;
{
    register int i, n;

    n = (i = atoi (s)) - pop_base;	 /* s="nnn header-line..." */
    Msgs[n].m_top = Msgs[n].m_bboard_id = i;
}

#endif	/* NNTP */
static int  read_pop () {
    int	    nmsgs,
            nbytes;

    if (pop_stat (&nmsgs, &nbytes) == NOTOK)
	padios (NULLCP, "%s", response);

    m_gMsgs (nmsgs);

#ifdef	NNTP	/* this makes read_pop() do some real work... */
    pop_base = nbytes - 1; 	/* nmsgs=last-first+1, nbytes=first */
    pop_exists (pop_statmsg);
#endif	/* NNTP */
    return nmsgs;
}


static int  pop_action (s)
register char  *s;
{
    fprintf (yp, "%s\n", s);
}
#endif	/* BPOP || NNTP */

/*  */

static void m_gMsgs (n)
int	n;
{
    int	    nmsgs;

    if (Msgs == NULL) {
	nMsgs = n + MAXFOLDER / 2;
	Msgs = (struct Msg *) calloc ((unsigned) (nMsgs + 2), sizeof *Msgs);
	if (Msgs == NULL)
	    padios (NULLCP, "unable to allocate Msgs structure");
	return;
    }

    if (nMsgs >= n)
	return;

    nmsgs = nMsgs + n + MAXFOLDER / 2;
    Msgs = (struct Msg *) realloc ((char *) Msgs,
	                    (unsigned) (nmsgs + 2) * sizeof *Msgs);
    if (Msgs == NULL)
	padios (NULLCP, "unable to reallocate Msgs structure");
    bzero ((char *) (Msgs + nMsgs + 2),
	   (unsigned) ((nmsgs - nMsgs) * sizeof *Msgs));

    nMsgs = nmsgs;
}

/*  */

FILE   *msh_ready (msgnum, full)
register int msgnum;
int	full;
{
    register int    msgp;
    int     fd;
    long    pos1,
            pos2;
    char   *cp,
            tmpfil[BUFSIZ];

    if (yp) {
	(void) fclose (yp);
	yp = NULL;
    }

    if (fmsh) {
	if ((fd = Msgs[msgnum].m_top) == NOTOK) {
	    if (numfds >= maxfds)
		for (msgp = mp -> lowmsg; msgp <= mp -> hghmsg; msgp++)
		    if (Msgs[msgp].m_top != NOTOK) {
			(void) close (Msgs[msgp].m_top);
			Msgs[msgp].m_top = NOTOK;
			numfds--;
			break;
		    }

	    if ((fd = open (cp = m_name (msgnum), 0)) == NOTOK)
		padios (cp, "unable to open message");
	    Msgs[msgnum].m_top = fd;
	    numfds++;
	}

	if ((fd = dup (fd)) == NOTOK)
	    padios ("cached message", "unable to dup");
	if ((yp = fdopen (fd, "r")) == NULL)
	    padios (NULLCP, "unable to fdopen cached message");
	(void) fseek (yp, 0L, 0);
	return yp;
    }

#if defined(BPOP) || defined(NNTP)
    if (pmsh && (mp -> msgstats[msgnum] & VIRTUAL)) {
	if (Msgs[msgnum].m_top == 0)
	    padios (NULLCP, "msh_ready (%d, %d) botch", msgnum, full);
	if (!full) {
	    (void) strcpy (tmpfil, m_tmpfil (invo_name));
	    if ((yp = fopen (tmpfil, "w+")) == NULL)
		padios (tmpfil, "unable to create");
	    (void) unlink (tmpfil);

	    if (pop_top (Msgs[msgnum].m_top, 4, pop_action) == NOTOK)
#ifndef NNTP
		padios (NULLCP, "%s", response);
#else /* NNTP */
	    {
		if (strncmp(response, "423 ", 4) == 0)
		    /* 423 no such article number in this group */
		    fprintf (yp, "Expired:\n\n"); /* dummy */
		else
		    padios (NULLCP, "%s", response);
	    }
#endif /* NNTP */

	    m_eomsbr ((int (*)()) 0);	/* XXX */
	    msg_style = MS_DEFAULT;	/*  .. */
	    (void) fseek (yp, 0L, 0);
	    return yp;
	}

	(void) fseek (fp, 0L, 2);
	(void) fwrite (mmdlm1, 1, strlen (mmdlm1), fp);
	if (fflush (fp))
	    padios ("temporary file", "write error on");
	(void) fseek (fp, 0L, 2);
	pos1 = ftell (fp);

	yp = fp;
	if (pop_retr (Msgs[msgnum].m_top, pop_action) == NOTOK)
#ifndef NNTP
	    padios (NULLCP, "%s", response);
#else /* NNTP */
	{
	    if (strncmp(response, "423 ", 4) == 0)
		/* 423 no such article number in this group */
		fprintf (yp, "Expired:\n\n"); /* dummy */
	    else
		padios (NULLCP, "%s", response);
	}
#endif /* NNTP */
	yp = NULL;

	(void) fseek (fp, 0L, 2);
	pos2 = ftell (fp);
	(void) fwrite (mmdlm2, 1, strlen (mmdlm2), fp);
	if (fflush (fp))
	    padios ("temporary file", "write error on");

	Msgs[msgnum].m_start = pos1;
	Msgs[msgnum].m_stop = pos2;

	mp -> msgstats[msgnum] &= ~VIRTUAL;
    }
#endif	/* BPOP || NNTP */

    m_eomsbr ((int (*)()) 0);	/* XXX */
    (void) fseek (fp, Msgs[msgnum].m_start, 0);
    return fp;
}

/*  */

static int  check_folder (scansw)
int     scansw;
{
    int     flags,
            i,
            low,
            hgh,
            msgp;
    struct stat st;

#if defined(BPOP) || defined(NNTP)
    if (pmsh)
	return 0;
#endif	/* BPOP || NNTP */

    if (fmsh) {
	if (stat (mp -> foldpath, &st) == NOTOK)
	    padios (mp -> foldpath, "unable to stat");
	if (mtime == st.st_mtime)
	    return 0;
	mtime = st.st_mtime;

	low = mp -> hghmsg + 1;
	m_fmsg (mp);

	if (!(mp = m_gmsg (fmsh)))
	    padios (NULLCP, "unable to re-read folder %s", fmsh);

	hgh = mp -> hghmsg;

	for (msgp = mp -> lowmsg; msgp <= mp -> hghmsg; msgp++) {
	    if (Msgs[msgp].m_top != NOTOK) {
		(void) close (Msgs[msgp].m_top);
		Msgs[msgp].m_top = NOTOK;
		numfds--;
	    }
	    if (Msgs[msgp].m_scanl) {
		free (Msgs[msgp].m_scanl);
		Msgs[msgp].m_scanl = NULL;
	    }
	}

	m_init ();

	if (modified || low > hgh)
	    return 1;
	goto check_vmh;
    }
    if (fstat (fileno (fp), &st) == NOTOK)
	padios (mp -> foldpath, "unable to fstat");
    if (mtime == st.st_mtime)
	return 0;
    mode = (int) (st.st_mode & 0777);
    mtime = st.st_mtime;

    if ((msgp = read_file (Msgs[mp -> hghmsg].m_stop, mp -> hghmsg + 1)) < 1)
	padios (NULLCP, "no messages in %s", mp -> foldpath);	/* XXX */
    if (msgp <= mp -> hghmsg)
	return 0;		/* XXX */

    if ((mp = m_remsg (mp, 0, msgp)) == NULL)
	padios (NULLCP, "unable to allocate folder storage");

    low = mp -> hghmsg + 1, hgh = msgp;
    flags = scansw ? m_seqflag (mp, "unseen") : 0;
    for (i = mp -> hghmsg + 1; i <= msgp; i++) {
	mp -> msgstats[i] = EXISTS | flags;
	mp -> nummsg++;
    }
    mp -> hghmsg = msgp;
    m_init ();

check_vmh: ;
    if (vmh)
	return 1;

    advise (NULLCP, "new messages have arrived!\007");
    if (scansw)
	scanrange (low, hgh);

    return 1;
}

/*  */

static	void scanrange (low, hgh)
int	low,
	hgh;
{
    char    buffer[BUFSIZ];

    (void) sprintf (buffer, "%d-%d", low, hgh);
    scanstring (buffer);
}


static	void scanstring (arg)
char   *arg;
{
    char   *cp,
          **ap,
           *vec[MAXARGS];

    if ((cp = m_find (cmd_name = "scan")) != NULL) {
	ap = brkstring (cp = getcpy (cp), " ", "\n");
	ap = copyip (ap, vec);
    }
    else
	ap = vec;
    *ap++ = arg;
    *ap = NULL;
    m_init ();
    scancmd (vec);
    if (cp != NULL)
	free (cp);
}

/*  */

void
readids (id)
int     id;
{
    register int    cur,
                    flags,
                    i,
                    msgnum;

    if (mp -> curmsg == 0)
	m_setcur (mp, mp -> lowmsg);
    if (id <= 0 || (flags = m_seqflag (mp, "unseen")) == 0)
	return;

    for (msgnum = mp -> hghmsg; msgnum >= mp -> lowmsg; msgnum--)
	mp -> msgstats[msgnum] |= flags;

    if (id != 1) {
	cur = mp -> curmsg;

	for (msgnum = mp -> hghmsg; msgnum >= mp -> lowmsg; msgnum--)
	  if (mp -> msgstats[msgnum] & EXISTS)		/* FIX */
	    if ((i = readid (msgnum)) > 0 && i < id) {
		cur = msgnum + 1;
		mp -> msgstats[msgnum] &= ~flags;
		break;
	    }
	for (i = mp -> lowmsg; i < msgnum; i++)
	    mp -> msgstats[i] &= ~flags;

	if (cur > mp -> hghmsg)
	    cur = mp -> hghmsg;

	m_setcur (mp, cur);
    }

    if ((gap = 1 < id && id < (i = readid (mp -> lowmsg)) ? id : 0) && !vmh)
	advise (NULLCP, "gap in ID:s, last seen %d, lowest present %d\n",
		id - 1, i);
}

/*  */

int 	readid (msgnum)
int     msgnum;
{
    int     i,
            state;
#if defined(BPOP) || defined(NNTP)
    int	    arg1,
	    arg2,
	    arg3;
#endif	/* BPOP || NNTP */
    char   *bp,
            buf[BUFSIZ],
            name[NAMESZ];
    register FILE *zp;

    if (Msgs[msgnum].m_bboard_id)
	return Msgs[msgnum].m_bboard_id;
#if defined(BPOP) || defined(NNTP)
    if (pmsh) {
	if (Msgs[msgnum].m_top == 0)
	    padios (NULLCP, "readid (%d) botch", msgnum);
	if (pop_list (Msgs[msgnum].m_top, (int *) 0, &arg1, &arg2, &arg3) == OK
		&& arg3 > 0)
	    return (Msgs[msgnum].m_bboard_id = arg3);
    }
#endif	/* BPOP || NNTP */

    zp = msh_ready (msgnum, 0);
    for (state = FLD;;)
	switch (state = m_getfld (state, name, buf, sizeof buf, zp)) {
	    case FLD: 
	    case FLDEOF: 
	    case FLDPLUS: 
		if (uleq (name, BBoard_ID)) {
		    bp = getcpy (buf);
		    while (state == FLDPLUS) {
			state = m_getfld (state, name, buf, sizeof buf, zp);
			bp = add (buf, bp);
		    }
		    i = atoi (bp);
		    free (bp);
		    if (i > 0)
			return (Msgs[msgnum].m_bboard_id = i);
		    else
			continue;
		}
		while (state == FLDPLUS)
		    state = m_getfld (state, name, buf, sizeof buf, zp);
		if (state != FLDEOF)
		    continue;

	    default: 
		return 0;
	}
}

/*  */

void
display_info (scansw)
int     scansw;
{
    int     flags,
            sd;

    interactive = isatty (fileno (stdout));
    if (sp == NULL) {
	if ((sd = dup (fileno (stdout))) == NOTOK)
	    padios ("standard output", "unable to dup");
#ifndef	BSD42			/* XXX */
#ifdef	FIOCLEX
	(void) ioctl (sd, FIOCLEX, NULL);
#endif	/* FIOCLEX */
#endif	/* not BSD42 */
	if ((sp = fdopen (sd, "w")) == NULL)
	    padios ("standard output", "unable to fdopen");
    }

    (void) m_putenv ("mhfolder", mp -> foldpath);
    if (vmh)
	return;

    if (myname) {
	printf ("Reading ");
	if (SOprintf ("%s", myname))
	    printf ("%s", myname);
	printf (", currently at message %d of %d\n",
		mp -> curmsg, mp -> hghmsg);
    }
    else {
	printf ("Reading ");
	if (fmsh)
	    printf ("+%s", fmsh);
	else
	    printf ("%s", mp -> foldpath);
	printf (", currently at message %d of %d\n",
		mp -> curmsg, mp -> hghmsg);
    }

    if ((flags = m_seqflag (mp, "unseen"))
	    && scansw
	    && (mp -> msgstats[mp -> hghmsg] & flags))
	scanstring ("unseen");
}

/*  */

static	void write_ids () {
    int     i = 0,
	    flags,
            msgnum;
    char    buffer[80];

    if (pfd <= 1)
	return;

    if (flags = m_seqflag (mp, "unseen"))
	for (msgnum = mp -> hghmsg; msgnum >= mp -> lowmsg; msgnum--)
	    if (!(mp -> msgstats[msgnum] & flags)) {
		if (Msgs[msgnum].m_bboard_id == 0)
		    (void) readid (msgnum);
		if ((i = Msgs[msgnum].m_bboard_id) > 0)
		    break;
	    }

    (void) sprintf (buffer, "%d %d\n", i, Msgs[mp -> hghmsg].m_bboard_id);
    (void) write (pfd, buffer, sizeof buffer);
    (void) close (pfd);
    pfd = NOTOK;
}

/*  */

static void quit () {
    int     i,
            md,
            msgnum;
    char   *cp,
            tmpfil[BUFSIZ],
            map1[BUFSIZ],
            map2[BUFSIZ];
    struct stat st;
    FILE   *dp;

    if (!(mp -> msgflags & MODIFIED) || mp -> msgflags & READONLY || fmsh) {
	    if (vmh)
		(void) rc2peer (RC_FIN, 0, NULLCP);
	return;
    }

    if (vmh) 
	(void) ttyNaux (NULLCMD, "FAST");
    cp = NULL;
    if ((dp = lkfopen (mp -> foldpath, "r")) == NULL) {
	advise (mp -> foldpath, "unable to lock");
	if (vmh) {
	    (void) ttyR (NULLCMD);
	    (void) pFIN ();
	}	
	return;
    }
    if (fstat (fileno (dp), &st) == NOTOK) {
	advise (mp -> foldpath, "unable to stat");
	goto release;
    }
    if (mtime != st.st_mtime) {
	advise (NULLCP, "new messages have arrived, no update");
	goto release;
    }
    mode = (int) (st.st_mode & 0777);

    if (mp -> nummsg == 0) {
	cp = concat ("Zero file \"", mp -> foldpath, "\"? ", NULLCP);
	if (getanswer (cp)) {
	    if ((i = creat (mp -> foldpath, mode)) != NOTOK)
		(void) close (i);
	    else
		advise (mp -> foldpath, "error zero'ing");
	    (void) unlink (map_name (mp -> foldpath));/* XXX */
	}
	goto release;
    }

    cp = concat ("Update file \"", mp -> foldpath, "\"? ", NULLCP);
    if (!getanswer (cp))
	goto release;
    (void) strcpy (tmpfil, m_backup (mp -> foldpath));
    if ((md = mbx_open (tmpfil, st.st_uid, st.st_gid, mode)) == NOTOK) {
	advise (tmpfil, "unable to open");
	goto release;
    }

    for (msgnum = mp -> lowmsg; msgnum <= mp -> hghmsg; msgnum++)
	if (mp -> msgstats[msgnum] & EXISTS
		&& pack (tmpfil, md, msgnum) == NOTOK) {
	    (void) mbx_close (tmpfil, md);
	    (void) unlink (tmpfil);
	    (void) unlink (map_name (tmpfil));
	    goto release;
	}
    (void) mbx_close (tmpfil, md);

    if (rename (tmpfil, mp -> foldpath) == NOTOK)
	admonish (mp -> foldpath, "unable to rename %s to", tmpfil);
    else {
	(void) strcpy (map1, map_name (tmpfil));
	(void) strcpy (map2, map_name (mp -> foldpath));

	if (rename (map1, map2) == NOTOK) {
	    admonish (map2, "unable to rename %s to", map1);
	    (void) unlink (map1);
	    (void) unlink (map2);
	}
    }

release: ;
    if (cp)
	free (cp);
    (void) lkfclose (dp, mp -> foldpath);
    if (vmh) {
	(void) ttyR (NULLCMD);
	(void) pFIN ();
    }
}

/*  */

#ifdef READLINE
char *
command_comp(text, state)
char *text;
int state;
{
    static int list_index, len;
    char *name;

    if (!state) {
	list_index = 0;
	len = strlen(text);
    }
    while ((name = mshcmds[list_index].sw) != NULLCP) {
	if (mshcmds[list_index++].minchars < 0)
	    continue;
	if (strncmp(name, text, len) == 0)
	    return getcpy(name);
    }
    return NULLCP;
}

#ifdef SYS5DIR
#include <dirent.h>
#else
#include <direct.h>
#endif

char *
folder_comp(text, state)
char *text;
int state;
{
    static DIR *dp;
    static char *dirname = NULLCP;
    static char *filename = NULLCP;
    static char *foldername = NULLCP;
    static char *subdir = NULLCP;
    static int len;
#ifdef SYS5DIR
    struct dirent *entry = (struct dirent *)0;
#else
    struct direct *entry = (struct direct *)0;
#endif
    char *p;

    if (!state) {
	foldername = getcpy(text);
	if ((p = rindex(text, '/')) != NULL) {
	    foldername[p-text+1] = '\0';
	    filename = getcpy(p+1);
	    dirname = m_mailpath(path(foldername+1,
				      *text == '+' ? TFOLDER : TSUBCWF));
	} else {
	    foldername[1] = '\0';
	    filename = getcpy(text+1);
	    dirname = m_mailpath(*text == '+' ? "" : path("./", TSUBCWF));
	}
	dp = opendir(dirname);
	len = strlen(filename);
	subdir = NULLCP;
    }
    if (subdir && *subdir) {
	p = subdir;
	subdir = NULLCP;
	return p;
    }
    while (dp && (entry = readdir(dp))) {
	if ((len == 0
	     && (entry->d_name[0] != '.' ||
		 (entry->d_name[1] &&
		  (entry->d_name[1] != '.' || entry->d_name[2]))))
	    ||
	    (len != 0
	     && strncmp(filename, entry->d_name, len) == 0)) {
	    struct stat st;
	    if (lstat(add(entry->d_name, add("/", add(dirname, NULLCP))), &st)
		!= NOTOK
		&& (st.st_mode & S_IFDIR)) {
		if (st.st_nlink > 2)
		    subdir = add("/", add(entry->d_name,
					  add(foldername, NULLCP)));
		break;
	    }
	}
    }
    if (entry)
	return add(entry->d_name, add(foldername, NULLCP));

    if (dp) {
	closedir(dp);
	dp = (DIR *)0;
    }
    if (dirname) {
	free(dirname);
	dirname = NULLCP;
    }
    if (filename) {
	free(filename);
	filename = NULLCP;
    }
    if (foldername) {
	free(foldername);
	foldername = NULLCP;
    }
    if (subdir) {
	free(subdir);
	subdir = NULLCP;
    }
    return NULLCP;
}

char **
msh_completion(text, start, end)
char *text;
int start, end;
{
    char **matches = NULLVP;

    if (start == 0)
	matches = completion_matches(text, command_comp);
    else if (text[0] == '+' || text[0] == '@')
	matches = completion_matches(text, folder_comp);

    return matches;
}

initialize_readline()
{
    rl_readline_name = invo_name;
    rl_basic_word_break_characters = " \t\n\"\\'`$><=;|&{(";
    rl_attempted_completion_function = (CPPFunction *)msh_completion;
}
#endif /* READLINE */

static int  getargs (prompt, sw, cmdp)
char   *prompt;
struct swit *sw;
struct Cmd *cmdp;
{
    int     i;
    char   *cp;
    static char buffer[BUFSIZ];

    told_to_quit = 0;
    for (;;) {
	interrupted = 0;
#if defined(BSD42) || defined(SVR4)
	switch (setjmp (sigenv)) {
	    case OK:
		should_intr = 1;
		break;

	    default:
		should_intr = 0;
		if (interrupted && !told_to_quit) {
		    (void) putchar ('\n');
		    continue;
		}
		if (ppid > 0)
		    (void) kill (ppid, SIGEMT);
		return EOF;
	}
#endif	/* BSD42 || SVR4 */
#ifdef READLINE
	if (!(cp = readline(interactive ? prompt : ""))) {
#if !defined(BSD42) && !defined(SVR4)
	    if (ppid > 0)
	      (void) kill (ppid, SIGEMT);
	    return EOF;
#else	/* BSD42 || SVR4 */
	    longjmp (sigenv, DONE);
#endif	/* BSD42 || SVR4 */
	}
	if (!*cp)
	    continue;
	add_history(cp);
	strncpy(buffer, cp, sizeof buffer - 2);
	buffer[sizeof buffer - 2] = '\0';
	free(cp);
#else /* READLINE */
	if (interactive) {
	    printf ("%s", prompt);
	    (void) fflush (stdout);
	}
	for (cp = buffer; (i = getchar ()) != '\n';) {
#if !defined(BSD42) && !defined(SVR4)
	    if (interrupted && !told_to_quit) {
		buffer[0] = '\0';
		(void) putchar ('\n');
		break;
	    }
	    if (told_to_quit || i == EOF) {
		if (ppid > 0)
		    (void) kill (ppid, SIGEMT);
		return EOF;
	    }
#else	/* BSD42 || SVR4 */
	    if (i == EOF)
		longjmp (sigenv, DONE);
#endif	/* BSD42 || SVR4 */
	    if (cp < &buffer[sizeof buffer - 2])
		*cp++ = i;
	}
	*cp = 0;

	if (buffer[0] == 0)
	    continue;
#endif /* READLINE */
	if (buffer[0] == '?') {
	    printf ("commands:\n");
	    printsw (ALL, sw, "");
	    printf ("type CTRL-D or use ``quit'' to leave %s\n",
		    invo_name);
	    continue;
	}

	if (parse (buffer, cmdp) == NOTOK)
	    continue;

	switch (i = smatch (cmdp -> args[0], sw)) {
	    case AMBIGSW: 
		ambigsw (cmdp -> args[0], sw);
		continue;
	    case UNKWNSW: 
		printf ("say what: ``%s'' -- type ? (or help) for help\n",
			cmdp -> args[0]);
		continue;
	    default: 
#if defined(BSD42) || defined(SVR4)
		should_intr = 0;
#endif	/* BSD42 || SVR4 */
		return i;
	}
    }
}

/*  */

static int  getcmds (sw, cmdp, scansw)
struct swit *sw;
struct Cmd *cmdp;
int	scansw;
{
    int     i;
    struct record   rcs,
                   *rc = &rcs;

    initrc (rc);

    for (;;)
	switch (peer2rc (rc)) {
	    case RC_QRY: 
		(void) pQRY (rc -> rc_data, scansw);
		break;

	    case RC_CMD: 
		if ((i = pCMD (rc -> rc_data, sw, cmdp)) != NOTOK)
		    return i;
		break;

	    case RC_FIN: 
		if (ppid > 0)
		    (void) kill (ppid, SIGEMT);
		return EOF;

	    case RC_XXX: 
		padios (NULLCP, "%s", rc -> rc_data);

	    default: 
		(void) fmt2peer (RC_ERR, "pLOOP protocol screw-up");
		done (1);
	}
}

/*  */

static int  parse (buffer, cmdp)
char   *buffer;
struct Cmd *cmdp;
{
    int     argp = 0;
    char    c,
           *cp,
           *pp;

    cmdp -> line[0] = 0;
    pp = cmdp -> args[argp++] = cmdp -> line;
    cmdp -> redirect = NULL;
    cmdp -> direction = STDIO;
    cmdp -> stream = NULL;

    for (cp = buffer; c = *cp; cp++)
	if (!isspace ((unsigned char) c))
	    break;
    if (c == 0) {
	if (vmh)
	    (void) fmt2peer (RC_EOF, "null command");
	return NOTOK;
    }

    while (c = *cp++) {
	if (isspace ((unsigned char) c)) {
	    while (isspace ((unsigned char) c))
		c = *cp++;
	    if (c == 0)
		break;
	    *pp++ = 0;
	    cmdp -> args[argp++] = pp;
	    *pp = 0;
	}

	switch (c) {
	    case '"': 
		for (;;) {
		    switch (c = *cp++) {
			case 0: 
			    padvise (NULLCP, "unmatched \"");
			    return NOTOK;
			case '"': 
			    break;
			case QUOTE: 
			    if ((c = *cp++) == 0)
				goto no_quoting;
			default: 
			    *pp++ = c;
			    continue;
		    }
		    break;
		}
		continue;

	    case QUOTE: 
		if ((c = *cp++) == 0) {
	    no_quoting: ;
		    padvise (NULLCP, "the newline character can not be quoted");
		    return NOTOK;
		}

	    default: ;
		*pp++ = c;
		continue;

	    case '>': 
	    case '|': 
		if (pp == cmdp -> line) {
		    padvise (NULLCP, "invalid null command");
		    return NOTOK;
		}
		if (*cmdp -> args[argp - 1] == 0)
		    argp--;
		cmdp -> direction = c == '>' ? CRTIO : PIPIO;
		if (cmdp -> direction == CRTIO && (c = *cp) == '>') {
		    cmdp -> direction = APPIO;
		    cp++;
		}
		cmdp -> redirect = pp + 1;/* sigh */
		for (; c = *cp; cp++)
		    if (!isspace ((unsigned char) c))
			break;
		if (c == 0) {
		    padvise (NULLCP, cmdp -> direction != PIPIO
			    ? "missing name for redirect"
			    : "invalid null command");
		    return NOTOK;
		}
		(void) strcpy (cmdp -> redirect, cp);
		if (cmdp -> direction != PIPIO) {
		    for (; *cp; cp++)
			if (isspace ((unsigned char) *cp)) {
			    padvise (NULLCP, "bad name for redirect");
			    return NOTOK;
			}
		    if (expand (cmdp -> redirect) == NOTOK)
			return NOTOK;
		}
		break;
	}
	break;
    }

    *pp++ = 0;
    cmdp -> args[argp] = NULL;

    return OK;
}

/*  */

int	expand (redirect)
char   *redirect;
{
    char   *cp,
           *pp;
    char    path[BUFSIZ];
    struct passwd  *pw;

    if (*redirect != '~')
	return OK;

    if (cp = index (pp = redirect + 1, '/'))
	*cp++ = 0;
    if (*pp == 0)
	pp = mypath;
    else
	if (pw = getpwnam (pp))
	    pp = pw -> pw_dir;
	else {
	    padvise (NULLCP, "unknown user: %s", pp);
	    return NOTOK;
	}

    (void) sprintf (path, "%s/%s", pp, cp ? cp : "");
    (void) strcpy (redirect, path);
    return OK;
}

/*  */

static int  init_io (cmdp, vio)
register struct Cmd *cmdp;
int	vio;
{
    int     io,
            result;

    io = vmh;

    vmh = vio;
    result = initaux_io (cmdp);
    vmh = io;

    return result;
}


static int  initaux_io (cmdp)
register struct Cmd *cmdp;
{
    char   *mode;

    switch (cmdp -> direction) {
	case STDIO: 
	    return OK;

	case CRTIO: 
	case APPIO: 
	    mode = cmdp -> direction == CRTIO ? "write" : "append";
	    if ((cmdp -> stream = fopen (cmdp -> redirect, mode)) == NULL) {
		padvise (cmdp -> redirect, "unable to %s ", mode);
		cmdp -> direction = STDIO;
		return NOTOK;
	    }
	    break;

	case PIPIO: 
	    if ((cmdp -> stream = popen (cmdp -> redirect, "w")) == NULL) {
		padvise (cmdp -> redirect, "unable to pipe");
		cmdp -> direction = STDIO;
		return NOTOK;
	    }
	    (void) signal (SIGPIPE, pipeser);
	    broken_pipe = 0;
	    break;

	default: 
	    padios (NULLCP, "unknown redirection for command");
    }

    (void) fflush (stdout);
    if (dup2 (fileno (cmdp -> stream), fileno (stdout)) == NOTOK)
	padios ("standard output", "unable to dup2");
    clearerr (stdout);

    return OK;
}

/*  */

static  void fin_io (cmdp, vio)
register struct Cmd *cmdp;
int	vio;
{
    int     io;

    io = vmh;

    vmh = vio;
    finaux_io (cmdp);
    vmh = io;
}


static void  finaux_io (cmdp)
register struct Cmd *cmdp;
{
    switch (cmdp -> direction) {
	case STDIO: 
	    return;

	case CRTIO: 
	case APPIO: 
	    (void) fflush (stdout);
	    (void) close (fileno (stdout));
	    if (ferror (stdout))
		padvise (NULLCP, "problems writing %s", cmdp -> redirect);
	    (void) fclose (cmdp -> stream);
	    break;

	case PIPIO: 
	    (void) fflush (stdout);
	    (void) close (fileno (stdout));
	    (void) pclose (cmdp -> stream);
	    (void) signal (SIGPIPE, SIG_DFL);
	    break;

	default: 
	    padios (NULLCP, "unknown redirection for command");
    }

    if (dup2 (fileno (sp), fileno (stdout)) == NOTOK)
	padios ("standard output", "unable to dup2");
    clearerr (stdout);

    cmdp -> direction = STDIO;
}

/*  */

static  void m_init () {
    int     msgnum;

    for (msgnum = mp -> lowmsg; msgnum <= mp -> hghmsg; msgnum++)
	mp -> msgstats[msgnum] &= ~SELECTED;
    mp -> lowsel = mp -> hghsel = mp -> numsel = 0;
}


m_reset () {
    write_ids ();
    m_fmsg (mp);
    myname = NULL;
#if defined(BPOP) || defined(NNTP)
    if (pmsh) {
	(void) pop_done ();
	pmsh = 0;
    }
#endif	/* BPOP || NNTP */
}

/*  */

void	m_setcur (mp, msgnum)
register struct msgs *mp;
register int msgnum;
{
    if (mp -> curmsg == msgnum)
	return;

    if (mp -> curmsg && Msgs[mp -> curmsg].m_scanl) {
	free (Msgs[mp -> curmsg].m_scanl);
	Msgs[mp -> curmsg].m_scanl = NULL;
    }
    if (Msgs[msgnum].m_scanl) {
	free (Msgs[msgnum].m_scanl);
	Msgs[msgnum].m_scanl = NULL;
    }

    mp -> curmsg = msgnum;
}

/*  */

/* ARGSUSED */

static TYPESIG  intrser (i)
int     i;
{
#ifndef	BSD42
    (void) signal (SIGINT, intrser);
#endif	/* not BSD42 */

    discard (stdout);

    interrupted++;
#if defined(BSD42) || defined(SVR4)
    if (should_intr)
	longjmp (sigenv, NOTOK);
#endif	/* BSD42 || SVR4 */
}


/* ARGSUSED */

static TYPESIG  pipeser (i)
int     i;
{
#ifndef	BSD42
    (void) signal (SIGPIPE, pipeser);
#endif	/* not BSD42 */

    if (broken_pipe++ == 0)
	fprintf (stderr, "broken pipe\n");
    told_to_quit++;
    interrupted++;
#if defined(BSD42) || defined(SVR4)
    if (should_intr)
	longjmp (sigenv, NOTOK);
#endif	/* BSD42 || SVR4 */
}


/* ARGSUSED */

static TYPESIG  quitser (i)
int     i;
{
#ifndef	BSD42
    (void) signal (SIGQUIT, quitser);
#endif	/* BSD42 */

    told_to_quit++;
    interrupted++;
#if defined(BSD42) || defined(SVR4)
    if (should_intr)
	longjmp (sigenv, NOTOK);
#endif	/* BSD42 || SVR4 */
}

/*  */

static int  pINI () {
    int     i,
            vrsn;
    char   *bp;
    struct record   rcs,
                   *rc = &rcs;

    initrc (rc);

    switch (peer2rc (rc)) {
	case RC_INI: 
	    bp = rc -> rc_data;
	    while (isspace (*bp))
		bp++;
	    if (sscanf (bp, "%d", &vrsn) != 1) {
	bad_init: ;
		(void) fmt2peer (RC_ERR, "bad init \"%s\"", rc -> rc_data);
		done (1);
	    }
	    if (vrsn != RC_VRSN) {
		(void) fmt2peer (RC_ERR, "version %d unsupported", vrsn);
		done (1);
	    }

	    while (*bp && !isspace (*bp))
		bp++;
	    while (isspace (*bp))
		bp++;
	    if (sscanf (bp, "%d", &numwins) != 1 || numwins <= 0)
		goto bad_init;
	    if (numwins > NWIN)
		numwins = NWIN;

	    for (i = 1; i <= numwins; i++) {
		while (*bp && !isspace (*bp))
		    bp++;
		while (isspace (*bp))
		    bp++;
		if (sscanf (bp, "%d", &windows[i]) != 1 || windows[i] <= 0)
		    goto bad_init;
	    }
	    (void) rc2peer (RC_ACK, 0, NULLCP);
	    return OK;

	case RC_XXX: 
	    padios (NULLCP, "%s", rc -> rc_data);

	default: 
	    (void) fmt2peer (RC_ERR, "pINI protocol screw-up");
	    done (1);		/* NOTREACHED */
    }
}

/*  */

/* ARGSUSED */

static int  pQRY (str, scansw)
char   *str;
int	scansw;
{
    if (pQRY1 (scansw) == NOTOK || pQRY2 () == NOTOK)
	return NOTOK;

    (void) rc2peer (RC_EOF, 0, NULLCP);
    return OK;
}
	
/*  */

static int  pQRY1 (scansw)
int	scansw;
{
    int     oldhgh;
    static int  lastlow = 0,
                lastcur = 0,
                lasthgh = 0,
		lastnum = 0;

    oldhgh = mp -> hghmsg;
    if (check_folder (scansw) && oldhgh < mp -> hghmsg) {
	switch (winX (STATUS)) {
	    case NOTOK: 
		return NOTOK;

	    case OK: 
		printf ("new messages have arrived!");
		(void) fflush (stdout);
		(void) fflush (stderr);
		_exit (0);	/* NOTREACHED */

	    default: 
		lastlow = lastcur = lasthgh = lastnum = 0;
		break;
	}

	switch (winX (DISPLAY)) {
	    case NOTOK: 
		return NOTOK;

	    case OK: 
		scanrange (oldhgh + 1, mp -> hghmsg);
		(void) fflush (stdout);
		(void) fflush (stderr);
		_exit (0);	/* NOTREACHED */

	    default: 
		break;
	}
	return OK;
    }

    if (gap)
	switch (winX (STATUS)) {
	    case NOTOK:
		return NOTOK;

	    case OK:
		printf ("%s: gap in ID:s, last seen %d, lowest present %d\n",
		    myname ? myname : fmsh ? fmsh : mp -> foldpath, gap - 1,
		    readid (mp -> lowmsg));
		(void) fflush (stdout);
		(void) fflush (stderr);
		_exit (0);	/* NOTREACHED */

	    default:
		gap = 0;
		return OK;
	}

    if (mp -> lowmsg != lastlow
	    || mp -> curmsg != lastcur
	    || mp -> hghmsg != lasthgh
	    || mp -> nummsg != lastnum)
	switch (winX (STATUS)) {
	    case NOTOK: 
		return NOTOK;

	    case OK: 
		foldcmd (NULLVP);
		(void) fflush (stdout);
		(void) fflush (stderr);
		_exit (0);	/* NOTREACHED */

	    default: 
		lastlow = mp -> lowmsg;
		lastcur = mp -> curmsg;
		lasthgh = mp -> hghmsg;
		lastnum = mp -> nummsg;
		return OK;
	}

    return OK;
}

/*  */

static int  pQRY2 () {
    int     i,
            j,
	    k,
            msgnum,
            n;
    static int  cur = 0,
                num = 0,
		lo = 0,
		hi = 0;

    if (mp -> nummsg == 0 && mp -> nummsg != num)
	switch (winX (SCAN)) {
	    case NOTOK: 
		return NOTOK;

	    case OK: 
		printf ("empty!");
		(void) fflush (stdout);
		(void) fflush (stderr);
		_exit (0);	/* NOTREACHED */

	    default: 
		num = mp -> nummsg;
		return OK;
	}
    num = mp -> nummsg;

    i = 0;
    j = (k = windows[SCAN]) / 2;
    for (msgnum = mp -> curmsg; msgnum <= mp -> hghmsg; msgnum++)
	if (mp -> msgstats[msgnum] & EXISTS)
	    i++;
    if (i-- > 0)
	if (topcur)
	    k = i >= k ? 1 : k - i;
	else
	    k -= i > j ? j : i;

    i = j = 0;
    n = 1;
    for (msgnum = mp -> curmsg; msgnum >= mp -> lowmsg; msgnum--)
	if (mp -> msgstats[msgnum] & EXISTS) {
	    i = msgnum;
	    if (j == 0)
		j = msgnum;
	    if (n++ >= k)
		break;
	}
    for (msgnum = mp -> curmsg + 1; msgnum <= mp -> hghmsg; msgnum++)
	if (mp -> msgstats[msgnum] & EXISTS) {
	    if (i == 0)
		i = msgnum;
	    j = msgnum;
	    if (n++ >= windows[SCAN])
		break;
	}
    if (!topcur
	    && lo > 0
	    && hi > 0
	    && mp -> msgstats[lo] & EXISTS
	    && mp -> msgstats[hi] & EXISTS
	    && (lo < mp -> curmsg
		    || (lo == mp -> curmsg && lo == mp -> lowmsg))
	    && (mp -> curmsg < hi
		    || (hi == mp -> curmsg && hi == mp -> hghmsg))
	    && hi - lo == j - i)
	i = lo, j = hi;

    if (mp -> curmsg != cur || modified)
	switch (winN (NULLCMD, SCAN, 0)) {
	    case NOTOK: 
		return NOTOK;

	    case OK:
		return OK;

	    default: 
		scanrange (lo = i, hi = j);
		cur = mp -> curmsg;
		(void) winR (NULLCMD);
		return OK;
	}

    return OK;
}

/*  */

static int pCMD (str, sw, cmdp)
char   *str;
struct swit *sw;
struct Cmd *cmdp;
{
    int     i;

    if (*str == '?')
	switch (winX (DISPLAY)) {
	    case NOTOK: 
		return NOTOK;

	    case OK: 
		printf ("commands:\n");
		printsw (ALL, sw, "");
		printf ("type ``quit'' to leave %s\n", invo_name);
		(void) fflush (stdout);
		(void) fflush (stderr);
		_exit (0);	/* NOTREACHED */

	    default: 
		(void) rc2peer (RC_EOF, 0, NULLCP);
		return NOTOK;
	}

    if (parse (str, cmdp) == NOTOK)
	return NOTOK;

    switch (i = smatch (cmdp -> args[0], sw)) {
	case AMBIGSW: 
	    switch (winX (DISPLAY)) {
		case NOTOK: 
		    return NOTOK;

		case OK: 
		    ambigsw (cmdp -> args[0], sw);
		    (void) fflush (stdout);
		    (void) fflush (stderr);
		    _exit (0);	/* NOTREACHED */

		default: 
		    (void) rc2peer (RC_EOF, 0, NULLCP);
		    return NOTOK;
	    }

	case UNKWNSW: 
	    (void) fmt2peer (RC_ERR,
		    "say what: ``%s'' -- type ? (or help) for help",
		    cmdp -> args[0]);
	    return NOTOK;

	default: 
	    return i;
    }
}

/*  */

static int  pFIN () {
    int     status;

    switch (setjmp (peerenv)) {
	case OK: 
	    (void) signal (SIGALRM, alrmser);
	    (void) alarm (ALARM);

	    status = peerwait ();

	    (void) alarm (0);
	    return status;

	default: 
	    return NOTOK;
    }
}


static int  peerwait () {
    struct record   rcs,
                   *rc = &rcs;

    initrc (rc);

    switch (peer2rc (rc)) {
	case RC_QRY: 
	case RC_CMD: 
	    (void) rc2peer (RC_FIN, 0, NULLCP);
	    return OK;

	case RC_XXX: 
	    advise (NULLCP, "%s", rc -> rc_data);
	    return NOTOK;

	default: 
	    (void) fmt2peer (RC_FIN, "pLOOP protocol screw-up");
	    return NOTOK;
    }
}


/* ARGSUSED */

static TYPESIG alrmser (i)
int	i;
{
    longjmp (peerenv, DONE);
}

/*  */

static int  ttyNaux (cmdp, s)
register struct Cmd *cmdp;
char   *s;
{
    struct record   rcs,
                   *rc = &rcs;

    initrc (rc);

    if (cmdp && init_io (cmdp, vmh) == NOTOK)
	return NOTOK;

    if (!fmsh)
	(void) fseek (fp, 0L, 0);/* XXX: fseek() too tricky for our own good */

    vmhtty = NOTOK;
    switch (rc2rc (RC_TTY, s ? strlen (s) : 0, s, rc)) {
	case RC_ACK: 
	    vmhtty = OK;	/* fall */
	case RC_ERR: 
	    break;

	case RC_XXX: 
	    padios (NULLCP, "%s", rc -> rc_data);/* NOTREACHED */

	default: 
	    (void) fmt2peer (RC_ERR, "pTTY protocol screw-up");
	    done (1);		/* NOTREACHED */
    }

#ifdef	SIGTSTP
    (void) signal (SIGTSTP, tstat);
#endif	/* SIGTSTP */
    return vmhtty;
}

/*  */

static int  ttyR (cmdp)
register struct Cmd *cmdp;
{
    struct record   rcs,
                   *rc = &rcs;

#ifdef	SIGTSTP
    (void) signal (SIGTSTP, SIG_IGN);
#endif	/* SIGTSTP */

    if (vmhtty != OK)
	return NOTOK;

    initrc (rc);

    if (cmdp)
	fin_io (cmdp, 0);

    vmhtty = NOTOK;
    switch (rc2rc (RC_EOF, 0, NULLCP, rc)) {
	case RC_ACK: 
	    (void) rc2peer (RC_EOF, 0, NULLCP);
	    return OK;

	case RC_XXX: 
	    padios (NULLCP, "%s", rc -> rc_data);/* NOTREACHED */

	default: 
	    (void) fmt2peer (RC_ERR, "pTTY protocol screw-up");
	    done (1);		/* NOTREACHED */
    }
}

/*  */

static int  winN (cmdp, n, eof)
register struct Cmd *cmdp;
int	n,
	eof;
{
    int     i,
            pd[2];
    char    buffer[BUFSIZ];
    struct record   rcs,
                   *rc = &rcs;

    if (vmhpid == NOTOK)
	return OK;

    initrc (rc);

    if (!fmsh)
	(void) fseek (fp, 0L, 0);/* XXX: fseek() too tricky for our own good */

    vmhpid = OK;

    (void) sprintf (buffer, "%d", n);
    switch (str2rc (RC_WIN, buffer, rc)) {
	case RC_ACK: 
	    break;

	case RC_ERR: 
	    return NOTOK;

	case RC_XXX: 
	    padios (NULLCP, "%s", rc -> rc_data);

	default: 
	    (void) fmt2peer (RC_ERR, "pWIN protocol screw-up");
	    done (1);
    }

    if (pipe (pd) == NOTOK) {
	(void) err2peer (RC_ERR, "pipe", "unable to");
	return NOTOK;
    }

    switch (vmhpid = fork ()) {
	case NOTOK: 
	    (void) err2peer (RC_ERR, "fork", "unable to");
	    (void) close (pd[0]);
	    (void) close (pd[1]);
	    return NOTOK;

	case OK: 
	    (void) close (pd[1]);
	    (void) signal (SIGPIPE, SIG_IGN);
	    while ((i = read (pd[0], buffer, sizeof buffer)) > 0)
		switch (rc2rc (RC_DATA, i, buffer, rc)) {
		    case RC_ACK: 
			break;

		    case RC_ERR: 
			_exit (1);

		    case RC_XXX: 
			advise (NULLCP, "%s", rc -> rc_data);
			_exit (2);

		    default: 
			(void) fmt2peer (RC_ERR, "pWIN protocol screw-up");
			_exit (2);
		}
	    if (i == OK)
		switch (rc2rc (RC_EOF, 0, NULLCP, rc)) {
		    case RC_ACK: 
			if (eof)
			    (void) rc2peer (RC_EOF, 0, NULLCP);
			i = 0;
			break;

		    case RC_XXX: 
			advise (NULLCP, "%s", rc -> rc_data);
			i = 2;
			break;

		    default: 
			(void) fmt2peer (RC_ERR, "pWIN protocol screw-up");
			i = 2;
			break;
		}
	    if (i == NOTOK)
		(void) err2peer (RC_ERR, "pipe", "error reading from");
	    (void) close (pd[0]);
	    _exit (i != NOTOK ? i : 1);

	default: 
	    if ((vmhfd0 = dup (fileno (stdin))) == NOTOK)
		padios ("standard input", "unable to dup");
	    if ((vmhfd1 = dup (fileno (stdout))) == NOTOK)
		padios ("standard output", "unable to dup");
	    if ((vmhfd2 = dup (fileno (stderr))) == NOTOK)
		padios ("diagnostic output", "unable to dup");

	    (void) close (0);
	    if ((i = open ("/dev/null", 0)) != NOTOK && i != fileno (stdin)) {
		(void) dup2 (i, fileno (stdin));
		(void) close (i);
	    }

	    (void) fflush (stdout);
	    if (dup2 (pd[1], fileno (stdout)) == NOTOK)
		padios ("standard output", "unable to dup2");
	    clearerr (stdout);

	    (void) fflush (stderr);
	    if (dup2 (pd[1], fileno (stderr)) == NOTOK)
		padios ("diagnostic output", "unable to dup2");
	    clearerr (stderr);

	    if (cmdp && init_io (cmdp, 0) == NOTOK)
		return NOTOK;
	    pstat = signal (SIGPIPE, pipeser);
	    broken_pipe = 1;

	    (void) close (pd[0]);
	    (void) close (pd[1]);

	    return vmhpid;
    }
}

/*  */

static int  winR (cmdp)
register struct Cmd *cmdp;
{
    int     status;

    if (vmhpid <= OK)
	return NOTOK;

    if (cmdp)
	fin_io (cmdp, 0);

    if (dup2 (vmhfd0, fileno (stdin)) == NOTOK)
	padios ("standard input", "unable to dup2");
    clearerr (stdin);
    (void) close (vmhfd0);

    (void) fflush (stdout);
    if (dup2 (vmhfd1, fileno (stdout)) == NOTOK)
	padios ("standard output", "unable to dup2");
    clearerr (stdout);
    (void) close (vmhfd1);

    (void) fflush (stderr);
    if (dup2 (vmhfd2, fileno (stderr)) == NOTOK)
	padios ("diagnostic output", "unable to dup2");
    clearerr (stderr);
    (void) close (vmhfd2);

    (void) signal (SIGPIPE, pstat);

    if ((status = pidwait (vmhpid, OK)) == 2)
	done (1);

    vmhpid = OK;
    return (status == 0 ? OK : NOTOK);
}

/*  */

static int  winX (n)
int	n;
{
    int     i,
            pid,
            pd[2];
    char    buffer[BUFSIZ];
    struct record   rcs,
                   *rc = &rcs;

    initrc (rc);

    if (!fmsh)
	(void) fseek (fp, 0L, 0);/* XXX: fseek() too tricky for our own good */

    (void) sprintf (buffer, "%d", n);
    switch (str2rc (RC_WIN, buffer, rc)) {
	case RC_ACK: 
	    break;

	case RC_ERR: 
	    return NOTOK;

	case RC_XXX: 
	    padios (NULLCP, "%s", rc -> rc_data);

	default: 
	    (void) fmt2peer (RC_ERR, "pWIN protocol screw-up");
	    done (1);
    }

    if (pipe (pd) == NOTOK) {
	(void) err2peer (RC_ERR, "pipe", "unable to");
	return NOTOK;
    }

    switch (pid = fork ()) {
	case NOTOK: 
	    (void) err2peer (RC_ERR, "fork", "unable to");
	    (void) close (pd[0]);
	    (void) close (pd[1]);
	    return NOTOK;

	case OK: 
	    (void) close (fileno (stdin));
	    if ((i = open ("/dev/null", 0)) != NOTOK && i != fileno (stdin)) {
		(void) dup2 (i, fileno (stdin));
		(void) close (i);
	    }
	    (void) dup2 (pd[1], fileno (stdout));
	    (void) dup2 (pd[1], fileno (stderr));
	    (void) close (pd[0]);
	    (void) close (pd[1]);
	    vmhpid = NOTOK;
	    return OK;

	default: 
	    (void) close (pd[1]);
	    while ((i = read (pd[0], buffer, sizeof buffer)) > 0)
		switch (rc2rc (RC_DATA, i, buffer, rc)) {
		    case RC_ACK: 
			break;

		    case RC_ERR: 
			(void) close (pd[0]);
			(void) pidwait (pid, OK);
			return NOTOK;

		    case RC_XXX: 
			padios (NULLCP, "%s", rc -> rc_data);

		    default: 
			(void) fmt2peer (RC_ERR, "pWIN protocol screw-up");
			done (1);
		}
	    if (i == OK)
		switch (rc2rc (RC_EOF, 0, NULLCP, rc)) {
		    case RC_ACK: 
			break;

		    case RC_XXX: 
			padios (NULLCP, "%s", rc -> rc_data);

		    default: 
			(void) fmt2peer (RC_ERR, "pWIN protocol screw-up");
			done (1);
		}
	    if (i == NOTOK)
		(void) err2peer (RC_ERR, "pipe", "error reading from");

	    (void) close (pd[0]);
	    (void) pidwait (pid, OK);
	    return (i != NOTOK ? pid : NOTOK);
    }
}

/*  */

/* VARARGS2 */

void	padios (what, fmt, a, b, c, d, e, f)
char   *what,
       *fmt,
       *a,
       *b,
       *c,
       *d,
       *e,
       *f;
{
    if (vmh) {
	(void) err2peer (RC_FIN, what, fmt, a, b, c, d, e, f);
	(void) rcdone ();
    }
    else
	advise (what, fmt, a, b, c, d, e, f);

    done (1);
}


/* VARARGS2 */

void	padvise (what, fmt, a, b, c, d, e, f)
char   *what,
       *fmt,
       *a,
       *b,
       *c,
       *d,
       *e,
       *f;
{
    if (vmh)
	(void) err2peer (RC_ERR, what, fmt, a, b, c, d, e, f);
    else
	advise (what, fmt, a, b, c, d, e, f);
}