/* Copyright (c) 1987,1988 Oliver Laumann, Technical University of Berlin.
 * Not derived from licensed software.
 *
 * Permission is granted to freely use, copy, modify, and redistribute
 * this software, provided that no attempt is made to gain profit from it,
 * the author is not construed to be liable for any results of using the
 * software, alterations are clearly marked as such, and this notice is
 * not modified.
 *
 * This code was modified for use with ELKS and the bcc compiler (which
 * only supports K&R) by Riley Williams <rhw@MemAlpha.cx> with help from
 * the Linux ELKS development team on SourceForge.Net. It is based on the
 * screen 2.0a code as released on 19th October 1988.
 */

static char ScreenVersion[] = "screen 2.0a.1 (ELKS) 19-Sep-2001";

#include <stdio.h>
#include <dirent.h>
#include <signal.h>
#include <errno.h>
#include <ctype.h>
#include <pwd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/file.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/stat.h>

#ifdef ELKS
#include <termios.h>
#include <linuxmt/un.h>
#include <unistd.h>

/*FIXME: check or add equivalent to elks libc */
#define OPEN_MAX	256
#else
#include <sgtty.h>
#include <nlist.h>
#include <sys/un.h>
#include <sys/dir.h>
#endif

#ifdef UTMP
#include <utmp.h>
#endif

#ifdef SUNLOADAV
#include <sys/param.h>
#endif

#include "screen.h"

#ifdef GETTTYENT
#   include <ttyent.h>
#else
    static struct ttyent {
	char *ty_name;
    } *getttyent();
    static char *tt, *ttnext;
    static char ttys[] = "/etc/ttys";
#endif

#define MAXWIN     10
#define MSGWAIT     5

#define Ctrl(c) ((c)&037)

extern char *blank, Term[], **environ;
extern rows, cols;
extern ISO2022;
extern status;
extern time_t TimeDisplayed;
extern char AnsiVersion[];
extern flowctl;
extern errno;
extern sys_nerr;
extern char *sys_errlist[];
extern char *index(), *rindex(), *malloc(), *getenv(), *MakeTermcap();
extern char *getlogin(), *ttyname();
static AttacherFinit(), Finit(), SigHup(), SigChld();
static char *MakeBellMsg(), *Filename(), **SaveArgs(), *GetTtyName();

static char PtyName[32], TtyName[32];
static char *ShellProg;
static char *ShellArgs[2];
static char inbuf[IOSIZE];
static inlen;
static ESCseen;
static GotSignal;
static char DefaultShell[] = "/bin/sh";
static char DefaultPath[] = ":/usr/ucb:/bin:/usr/bin";
static char PtyProto[] = "/dev/ptyXY";
static char TtyProto[] = "/dev/ttyXY";
static int TtyMode = 0622;
static char SockPath[512];
static char SockDir[] = ".screen";
static char *SockNamePtr, *SockName;
static ServerSocket;
static char *NewEnv[MAXARGS];
static char Esc = Ctrl('a');
static char MetaEsc = 'a';
static char *home;
static HasWindow;
static utmp, utmpf;
static char UtmpName[] = "/etc/utmp";
static char *LoginName;
static char *BellString = "Bell in window %";
static mflag, nflag, fflag, rflag;
static char HostName[MAXSTR];
static Detached;
static AttacherPid;	/* Non-Zero in child if we have an attacher */
static DevTty;
#ifdef LOADAV
    static char KmemName[] = "/dev/kmem";
#ifdef sequent
    static char UnixName[] = "/dynix";
#else
    static char UnixName[] = "/vmunix";
#endif
#ifdef alliant
    static char AvenrunSym[] = "_Loadavg";
#else
    static char AvenrunSym[] = "_avenrun";
#endif
    static struct nlist nl[2];
    static avenrun, kmemf;
#ifdef SUNLOADAV
    long loadav[3];
#else
#ifdef alliant
    long loadav[4];
#else
    double loadav[3];
#endif
#endif

#endif

struct mode {
#ifdef BSD_TTY
    struct sgttyb m_ttyb;
    struct tchars m_tchars;
    struct ltchars m_ltchars;
    int m_ldisc;
    int m_lmode;
#else
    int dummy;
#endif
} OldMode, NewMode;

static struct win *curr, *other;
static CurrNum, OtherNum;
static struct win *wtab[MAXWIN];

#define KEY_IGNORE         0
#define KEY_HARDCOPY       1
#define KEY_SUSPEND        2
#define KEY_SHELL          3
#define KEY_NEXT           4
#define KEY_PREV           5
#define KEY_KILL           6
#define KEY_REDISPLAY      7
#define KEY_WINDOWS        8
#define KEY_VERSION        9
#define KEY_OTHER         10
#define KEY_0             11
#define KEY_1             12
#define KEY_2             13
#define KEY_3             14
#define KEY_4             15
#define KEY_5             16
#define KEY_6             17
#define KEY_7             18
#define KEY_8             19
#define KEY_9             20
#define KEY_XON           21
#define KEY_XOFF          22
#define KEY_INFO          23
#define KEY_TERMCAP       24
#define KEY_QUIT          25
#define KEY_DETACH        26
#define KEY_CREATE        27

struct key {
    int type;
    char **args;
} ktab[256];

char *KeyNames[] = {
    "hardcopy", "suspend", "shell", "next", "prev", "kill", "redisplay",
    "windows", "version", "other", "select0", "select1", "select2", "select3",
    "select4", "select5", "select6", "select7", "select8", "select9",
    "xon", "xoff", "info", "termcap", "quit", "detach",
    0
};

int main (ac, av)
char **av;
{
    register n, len;
    register struct win **pp, *p;
    char *ap;
    int s, r, w, x = 0;
    int aflag = 0;
    struct timeval tv;
    time_t now;
    char buf[IOSIZE], *myname = (ac == 0) ? "screen" : av[0];
    char rc[256];
    struct stat st;

    while (ac > 0) {
	ap = *++av;
	if (--ac > 0 && *ap == '-') {
	    switch (ap[1]) {
	    case 'c':        /* Compatibility with older versions. */
		break;
	    case 'a':
		aflag = 1;
		break;
	    case 'm':
		mflag = 1;
		break;
	    case 'n':
		nflag = 1;
		break;
	    case 'f':
		fflag = 1;
		break;
	    case 'r':
		rflag = 1;
		if (ap[2]) {
		    SockName = ap+2;
		    if (ac != 1) goto help;
		} else if (--ac == 1) {
		    SockName = *++av;
		} else if (ac != 0) goto help;
		break;
	    case 'e':
		if (ap[2]) {
		    ap += 2;
		} else {
		    if (--ac == 0) goto help;
		    ap = *++av;
		}
		if (strlen (ap) != 2)
		    Msg (0, "Two characters are required with -e option.");
		Esc = ap[0];
		MetaEsc = ap[1];
		break;
	    default:
	    help:
		Msg (0, "Use: %s [-a] [-f] [-n] [-e xy] [cmd args]\n\
 or: %s -r [host.tty]", myname, myname);
	    }
	} else break;
    }
    if (nflag && fflag)
	Msg (0, "-f and -n are conflicting options.");
    if ((ShellProg = getenv ("SHELL")) == 0)
	ShellProg = DefaultShell;
    ShellArgs[0] = ShellProg;
    if (ac == 0) {
	ac = 1;
	av = ShellArgs;
    }
    if ((home = getenv ("HOME")) == 0)
	Msg (0, "$HOME is undefined.");
    sprintf (SockPath, "%s/%s", home, SockDir);
    if (stat (SockPath, &st) == -1) {
	if (errno == ENOENT) {
	    if (mkdir (SockPath, 0700) == -1)
		Msg (errno, "Cannot make directory %s", SockPath);
	    (void) chown (SockPath, getuid (), getgid ());
	} else Msg (errno, "Cannot get status of %s", SockPath);
    } else {
	if ((st.st_mode & S_IFMT) != S_IFDIR)
	    Msg (0, "%s is not a directory.", SockPath);
	if ((st.st_mode & 0777) != 0700)
	    Msg (0, "Directory %s must have mode 700.", SockPath);
	if (st.st_uid != getuid ())
	    Msg (0, "You are not the owner of %s.", SockPath);
    }
    (void) gethostname (HostName, MAXSTR);
    HostName[MAXSTR-1] = '\0';
    if (ap = index (HostName, '.'))
	*ap = '\0';
    strcat (SockPath, "/");
    SockNamePtr = SockPath + strlen (SockPath);
    if ((DevTty = open ("/dev/tty", O_RDWR|O_NDELAY)) == -1)
	Msg (errno, "/dev/tty");
    if (rflag) {
	Attach (MSG_ATTACH);
	Attacher ();
	/*NOTREACHED*/
    }
    if (GetSockName ()) {
	s = MakeClientSocket (1);
	SendCreateMsg (s, ac, av, aflag);
	close (s);
	exit (0);
    }
    switch (fork ()) {
    case -1:
	Msg (errno, "fork");
	/*NOTREACHED*/
    case 0:
	break;
    default:
	Attacher ();
	/*NOTREACHED*/
    }
    AttacherPid = getppid ();
    ServerSocket = s = MakeServerSocket ();
    InitTerm ();
    if (fflag)
	flowctl = 1;
    else if (nflag)
	flowctl = 0;
    MakeNewEnv ();
    GetTTY (0, &OldMode);
    InitUtmp ();
#ifdef LOADAV
    InitKmem ();
#endif
    signal (SIGHUP, SigHup);
    signal (SIGINT, Finit);
    signal (SIGQUIT, Finit);
    signal (SIGTERM, Finit);
    signal (SIGTTIN, SIG_IGN);
    signal (SIGTTOU, SIG_IGN);
    InitKeytab ();
    sprintf (rc, "%.*s/.screenrc", 245, home);
    ReadRc (rc);
    if ((n = MakeWindow (*av, av, aflag, 0, (char *)0)) == -1) {
	SetTTY (0, &OldMode);
	FinitTerm ();
	Kill (AttacherPid, SIGHUP);
	exit (1);
    }
    SetCurrWindow (n);
    HasWindow = 1;
    SetMode (&OldMode, &NewMode);
    SetTTY (0, &NewMode);
    signal (SIGCHLD, SigChld);
    tv.tv_usec = 0;
    while (1) {
	if (status) {
	    time (&now);
	    if (now - TimeDisplayed < MSGWAIT) {
		tv.tv_sec = MSGWAIT - (now - TimeDisplayed);
	    } else RemoveStatus (curr);
	}
	r = 0;
	w = 0;
	if (inlen)
	    w |= 1 << curr->ptyfd;
	else
	    r |= 1 << 0;
	for (pp = wtab; pp < wtab+MAXWIN; ++pp) {
	    if (!(p = *pp))
		continue;
	    if ((*pp)->active && status)
		continue;
	    if ((*pp)->outlen > 0)
		continue;
	    r |= 1 << (*pp)->ptyfd;
	}
	r |= 1 << s;
	(void) fflush (stdout);
	if (GotSignal && !status) {
	    SigHandler ();
	    continue;
	}
	if (select (32, &r, &w, &x, status ? &tv : (struct timeval *)0) == -1) {
	    if (errno == EINTR)
		continue;
	    HasWindow = 0;
	    Msg (errno, "select");
	    /*NOTREACHED*/
	}
	if (GotSignal && !status) {
	    SigHandler ();
	    continue;
	}
	if (r & 1 << s) {
	    RemoveStatus (curr);
	    ReceiveMsg (s);
	}
	if (r & 1 << 0) {
	    RemoveStatus (curr);
	    if (ESCseen) {
		inbuf[0] = Esc;
		inlen = read (0, inbuf+1, IOSIZE-1) + 1;
		ESCseen = 0;
	    } else {
		inlen = read (0, inbuf, IOSIZE);
	    }
	    if (inlen > 0)
		inlen = ProcessInput (inbuf, inlen);
	    if (inlen > 0)
		continue;
	}
	if (GotSignal && !status) {
	    SigHandler ();
	    continue;
	}
	if (w & 1 << curr->ptyfd && inlen > 0) {
	    if ((len = write (curr->ptyfd, inbuf, inlen)) > 0) {
		inlen -= len;
		bcopy (inbuf+len, inbuf, inlen);
	    }
	}
	if (GotSignal && !status) {
	    SigHandler ();
	    continue;
	}
	for (pp = wtab; pp < wtab+MAXWIN; ++pp) {
	    if (!(p = *pp))
		continue;
	    if (p->outlen) {
		WriteString (p, p->outbuf, p->outlen);
	    } else if (r & 1 << p->ptyfd) {
		if ((len = read (p->ptyfd, buf, IOSIZE)) == -1) {
		    if (errno == EWOULDBLOCK)
			len = 0;
		}
		if (len > 0)
		    WriteString (p, buf, len);
	    }
	    if (p->bell) {
		p->bell = 0;
		Msg (0, MakeBellMsg (pp-wtab));
	    }
	}
	if (GotSignal && !status)
	    SigHandler ();
    }
    /*NOTREACHED*/
}

static SigHandler () {
    while (GotSignal) {
	GotSignal = 0;
	DoWait ();
    }
}

static SigChld () {
    GotSignal = 1;
}

static SigHup () {
    Detach (0);
}

static DoWait () {
    register pid;
    register struct win **pp;
    int wstat;

    while ((pid = wait3 (&wstat, WNOHANG|WUNTRACED, NULL)) > 0) {
	for (pp = wtab; pp < wtab+MAXWIN; ++pp) {
	    if (*pp && pid == (*pp)->wpid) {
		if (WIFSTOPPED (wstat)) {
		    (void) killpg (getpgrp ((*pp)->wpid), SIGCONT);
		} else {
		    KillWindow (pp);
		}
	    }
	}
    }
    CheckWindows ();
}

static KillWindow (pp) struct win **pp; {
    if (*pp == curr)
	curr = 0;
    if (*pp == other)
	other = 0;
    FreeWindow (*pp);
    *pp = 0;
}

static CheckWindows () {
    register struct win **pp;

    /* If the current window disappeared and the "other" window is still
     * there, switch to the "other" window, else switch to the window
     * with the lowest index.
     * If there current window is still there, but the "other" window
     * vanished, "SetCurrWindow" is called in order to assign a new value
     * to "other".
     * If no window is alive at all, exit.
     */
    if (!curr && other) {
	SwitchWindow (OtherNum);
	return;
    }
    if (curr && !other) {
	SetCurrWindow (CurrNum);
	return;
    }
    for (pp = wtab; pp < wtab+MAXWIN; ++pp) {
	if (*pp) {
	    if (!curr)
		SwitchWindow (pp-wtab);
	    return;
	}
    }
    Finit ();
}

static Finit () {
    register struct win *p, **pp;

    for (pp = wtab; pp < wtab+MAXWIN; ++pp) {
	if (p = *pp)
	    FreeWindow (p);
    }
    SetTTY (0, &OldMode);
    FinitTerm ();
    printf ("[screen is terminating]\n");
    Kill (AttacherPid, SIGHUP);
    exit (0);
}

static InitKeytab () {
    register i;

    ktab['h'].type = ktab[Ctrl('h')].type = KEY_HARDCOPY;
    ktab['z'].type = ktab[Ctrl('z')].type = KEY_SUSPEND;
    ktab['c'].type = ktab[Ctrl('c')].type = KEY_SHELL;
    ktab[' '].type = ktab[Ctrl(' ')].type = 
    ktab['n'].type = ktab[Ctrl('n')].type = KEY_NEXT;
    ktab['-'].type = ktab['p'].type = ktab[Ctrl('p')].type = KEY_PREV;
    ktab['k'].type = ktab[Ctrl('k')].type = KEY_KILL;
    ktab['l'].type = ktab[Ctrl('l')].type = KEY_REDISPLAY;
    ktab['w'].type = ktab[Ctrl('w')].type = KEY_WINDOWS;
    ktab['v'].type = ktab[Ctrl('v')].type = KEY_VERSION;
    ktab['q'].type = ktab[Ctrl('q')].type = KEY_XON;
    ktab['s'].type = ktab[Ctrl('s')].type = KEY_XOFF;
    ktab['t'].type = ktab[Ctrl('t')].type = KEY_INFO;
    ktab['.'].type = KEY_TERMCAP;
    ktab[Ctrl('\\')].type = KEY_QUIT;
    ktab['d'].type = ktab[Ctrl('d')].type = KEY_DETACH;
    ktab[Esc].type = KEY_OTHER;
    for (i = 0; i <= 9; i++)
	ktab[i+'0'].type = KEY_0+i;
}

static ProcessInput (buf, len) char *buf; {
    register n, k;
    register char *s, *p;
    register struct win **pp;

    for (s = p = buf; len > 0; len--, s++) {
	if (*s == Esc) {
	    if (len > 1) {
		len--; s++;
		k = ktab[*s].type;
		if (*s == MetaEsc) {
		    *p++ = Esc;
		} else if (k >= KEY_0 && k <= KEY_9) {
		    p = buf;
		    SwitchWindow (k - KEY_0);
		} else switch (ktab[*s].type) {
		case KEY_TERMCAP:
		    p = buf;
		    WriteFile (0);
		    break;
		case KEY_HARDCOPY:
		    p = buf;
		    WriteFile (1);
		    break;
		case KEY_SUSPEND:
		    p = buf;
		    Detach (1);
		    break;
		case KEY_SHELL:
		    p = buf;
		    if ((n = MakeWindow (ShellProg, ShellArgs,
			    0, 0, (char *)0)) != -1)
			SwitchWindow (n);
		    break;
		case KEY_NEXT:
		    p = buf;
		    if (MoreWindows ())
			SwitchWindow (NextWindow ());
		    break;
		case KEY_PREV:
		    p = buf;
		    if (MoreWindows ())
			SwitchWindow (PreviousWindow ());
		    break;
		case KEY_KILL:
		    p = buf;
		    FreeWindow (wtab[CurrNum]);
		    if (other == curr)
			other = 0;
		    curr = wtab[CurrNum] = 0;
		    CheckWindows ();
		    break;
		case KEY_QUIT:
		    for (pp = wtab; pp < wtab+MAXWIN; ++pp)
			if (*pp) FreeWindow (*pp);
		    Finit ();
		    /*NOTREACHED*/
		case KEY_DETACH:
		    p = buf;
		    Detach (0);
		    break;
		case KEY_REDISPLAY:
		    p = buf;
		    Activate (wtab[CurrNum]);
		    break;
		case KEY_WINDOWS:
		    p = buf;
		    ShowWindows ();
		    break;
		case KEY_VERSION:
		    p = buf;
		    Msg (0, "%s  %s", ScreenVersion, AnsiVersion);
		    break;
		case KEY_INFO:
		    p = buf;
		    ShowInfo ();
		    break;
		case KEY_OTHER:
		    p = buf;
		    if (MoreWindows ())
			SwitchWindow (OtherNum);
		    break;
		case KEY_XON:
		    *p++ = Ctrl('q');
		    break;
		case KEY_XOFF:
		    *p++ = Ctrl('s');
		    break;
		case KEY_CREATE:
		    p = buf;
		    if ((n = MakeWindow (ktab[*s].args[0], ktab[*s].args,
			    0, 0, (char *)0)) != -1)
			SwitchWindow (n);
		    break;
		}
	    } else ESCseen = 1;
	} else *p++ = *s;
    }
    return p - buf;
}

static SwitchWindow (n) {
    if (!wtab[n])
	return;
    SetCurrWindow (n);
    Activate (wtab[n]);
}

static SetCurrWindow (n) {
    /*
     * If we come from another window, this window becomes the
     * "other" window:
     */
    if (curr) {
	curr->active = 0;
	other = curr;
	OtherNum = CurrNum;
    }
    CurrNum = n;
    curr = wtab[n];
    curr->active = 1;
    /*
     * If the "other" window is currently undefined (at program start
     * or because it has died), or if the "other" window is equal to the
     * one just selected, we try to find a new one:
     */
    if (other == 0 || other == curr) {
	OtherNum = NextWindow ();
	other = wtab[OtherNum];
    }
}

static NextWindow () {
    register struct win **pp;

    for (pp = wtab+CurrNum+1; pp != wtab+CurrNum; ++pp) {
	if (pp == wtab+MAXWIN)
	    pp = wtab;
	if (*pp)
	    break;
    }
    return pp-wtab;
}

static PreviousWindow () {
    register struct win **pp;

    for (pp = wtab+CurrNum-1; pp != wtab+CurrNum; --pp) {
	if (pp < wtab)
	    pp = wtab+MAXWIN-1;
	if (*pp)
	    break;
    }
    return pp-wtab;
}

static MoreWindows () {
    register struct win **pp;
    register n;

    for (n = 0, pp = wtab; pp < wtab+MAXWIN; ++pp)
	if (*pp) ++n;
    if (n <= 1)
	Msg (0, "No other window.");
    return n > 1;
}

static FreeWindow (wp) struct win *wp; {
    register i;

    RemoveUtmp (wp->slot);
    (void) chmod (wp->tty, 0666);
    (void) chown (wp->tty, 0, 0);
    close (wp->ptyfd);
    for (i = 0; i < rows; ++i) {
	free (wp->image[i]);
	free (wp->attr[i]);
	free (wp->font[i]);
    }
    free (wp->image);
    free (wp->attr);
    free (wp->font);
    free (wp);
}

static MakeWindow (prog, args, aflag, StartAt, dir)
	char *prog, **args, *dir; {
    register struct win **pp, *p;
    register char **cp;
    register n, f;
    int tf;
    int mypid;
    char ebuf[10];

    pp = wtab+StartAt;
    do {
	if (*pp == 0)
	    break;
	if (++pp == wtab+MAXWIN)
	    pp = wtab;
    } while (pp != wtab+StartAt);
    if (*pp) {
	Msg (0, "No more windows.");
	return -1;
    }
    n = pp - wtab;
    if ((f = OpenPTY ()) == -1) {
	Msg (0, "No more PTYs.");
	return -1;
    }
    (void) fcntl (f, F_SETFL, FNDELAY);
    if ((p = *pp = (struct win *)malloc (sizeof (struct win))) == 0) {
nomem:
	Msg (0, "Out of memory.");
	return -1;
    }
    if ((p->image = (char **)malloc (rows * sizeof (char *))) == 0)
	goto nomem;
    for (cp = p->image; cp < p->image+rows; ++cp) {
	if ((*cp = malloc (cols)) == 0)
	    goto nomem;
	bclear (*cp, cols);
    }
    if ((p->attr = (char **)malloc (rows * sizeof (char *))) == 0)
	goto nomem;
    for (cp = p->attr; cp < p->attr+rows; ++cp) {
	if ((*cp = malloc (cols)) == 0)
	    goto nomem;
	bzero (*cp, cols);
    }
    if ((p->font = (char **)malloc (rows * sizeof (char *))) == 0)
	goto nomem;
    for (cp = p->font; cp < p->font+rows; ++cp) {
	if ((*cp = malloc (cols)) == 0)
	    goto nomem;
	bzero (*cp, cols);
    }
    if ((p->tabs = malloc (cols+1)) == 0)  /* +1 because 0 <= x <= cols */
	goto nomem;
    ResetScreen (p);
    p->aflag = aflag;
    p->active = 0;
    p->bell = 0;
    p->outlen = 0;
    p->ptyfd = f;
    strncpy (p->cmd, Filename (args[0]), MAXSTR-1);
    p->cmd[MAXSTR-1] = '\0';
    strncpy (p->tty, TtyName, MAXSTR-1);
    (void) chown (TtyName, getuid (), getgid ());
    (void) chmod (TtyName, TtyMode);
    p->slot = SetUtmp (TtyName);
    switch (p->wpid = fork ()) {
    case -1:
	Msg (errno, "fork");
	free ((char *)p);
	return -1;
    case 0:
	signal (SIGHUP, SIG_DFL);
	signal (SIGINT, SIG_DFL);
	signal (SIGQUIT, SIG_DFL);
	signal (SIGTERM, SIG_DFL);
	signal (SIGTTIN, SIG_DFL);
	signal (SIGTTOU, SIG_DFL);
	setuid (getuid ());
	setgid (getgid ());
	if (dir && chdir (dir) == -1) {
	    SendErrorMsg ("Cannot chdir to %s: %s", dir, sys_errlist[errno]);
	    exit (1);
	}
	mypid = getpid ();
	ioctl (DevTty, TIOCNOTTY, (char *)0);
	if ((tf = open (TtyName, O_RDWR)) == -1) {
	    SendErrorMsg ("Cannot open %s: %s", TtyName, sys_errlist[errno]);
	    exit (1);
	}
	(void) dup2 (tf, 0);
	(void) dup2 (tf, 1);
	(void) dup2 (tf, 2);
	for (f = OPEN_MAX-1; f > 2; f--)
	    close (f);
	ioctl (0, TIOCSPGRP, &mypid);
	(void) setpgrp (0, mypid);
	SetTTY (0, &OldMode);
	NewEnv[2] = MakeTermcap (aflag);
	sprintf (ebuf, "WINDOW=%d", n);
	NewEnv[3] = ebuf;
	execvpe (prog, args, NewEnv);
	SendErrorMsg ("Cannot exec %s: %s", prog, sys_errlist[errno]);
	exit (1);
    }
    return n;
}

static execvpe (prog, args, env) char *prog, **args, **env; {
    register char *path, *p;
    char buf[1024];
    char *shargs[MAXARGS+1];
    register i, eaccess = 0;

    if (prog[0] == '/')
	path = "";
    else if ((path = getenv ("PATH")) == 0)
	path = DefaultPath;
    do {
	p = buf;
	while (*path && *path != ':')
	    *p++ = *path++;
	if (p > buf)
	    *p++ = '/';
	strcpy (p, prog);
	if (*path)
	    ++path;
	execve (buf, args, env);
	switch (errno) {
	case ENOEXEC:
	    shargs[0] = DefaultShell;
	    shargs[1] = buf;
	    for (i = 1; shargs[i+1] = args[i]; ++i)
		;
	    execve (DefaultShell, shargs, env);
	    return;
	case EACCES:
	    eaccess = 1;
	    break;
	case ENOMEM: case E2BIG: case ETXTBSY:
	    return;
	}
    } while (*path);
    if (eaccess)
	errno = EACCES;
}

static WriteFile (dump) {   /* dump==0: create .termcap, dump==1: hardcopy */
    register i, j, k;
    register char *p;
    register FILE *f;
    char fn[1024];
    int pid, s;

    if (dump)
	sprintf (fn, "hardcopy.%d", CurrNum);
    else
	sprintf (fn, "%s/%s/.termcap", home, SockDir);
    switch (pid = fork ()) {
    case -1:
	Msg (errno, "fork");
	return;
    case 0:
	setuid (getuid ());
	setgid (getgid ());
	if ((f = fopen (fn, "w")) == NULL)
	    exit (1);
	if (dump) {
	    for (i = 0; i < rows; ++i) {
		p = curr->image[i];
		for (k = cols-1; k >= 0 && p[k] == ' '; --k) ;
		for (j = 0; j <= k; ++j)
		    putc (p[j], f);
		putc ('\n', f);
	    }
	} else {
	    if (p = index (MakeTermcap (curr->aflag), '=')) {
		fputs (++p, f);
		putc ('\n', f);
	    }
	}
	(void) fclose (f);
	exit (0);
    default:
	while ((i = wait (&s)) != pid)
	    if (i == -1) return;
	if ((s >> 8) & 0377)
	    Msg (0, "Cannot open \"%s\".", fn);
	else
	    Msg (0, "%s written to \"%s\".", dump ? "Screen image" :
		"Termcap entry", fn);
    }
}

static ShowWindows () {
    char buf[1024];
    register char *s;
    register struct win **pp, *p;

    for (s = buf, pp = wtab; pp < wtab+MAXWIN; ++pp) {
	if ((p = *pp) == 0)
	    continue;
	if (s - buf + 5 + strlen (p->cmd) > cols-1)
	    break;
	if (s > buf) {
	    *s++ = ' '; *s++ = ' ';
	}
	*s++ = pp - wtab + '0';
	if (p == curr)
	    *s++ = '*';
	else if (p == other)
	    *s++ = '-';
	*s++ = ' ';
	strcpy (s, p->cmd);
	s += strlen (s);
    }
    Msg (0, buf);
}

static ShowInfo () {
    char buf[1024], *p;
    register struct win *wp = curr;
    register i;
    struct tm *tp;
    time_t now;

    time (&now);
    tp = localtime (&now);
    sprintf (buf, "%2d:%02.2d:%02.2d %s", tp->tm_hour, tp->tm_min, tp->tm_sec,
	HostName);
#ifdef LOADAV
    if (avenrun && GetAvenrun ()) {
	p = buf + strlen (buf);
#ifdef SUNLOADAV
	sprintf (p, " %2.2f %2.2f %2.2f", (double)loadav[0]/FSCALE,
	    (double)loadav[1]/FSCALE, (double)loadav[2]/FSCALE);
#else
#ifdef alliant
	sprintf (p, " %2.2f %2.2f %2.2f %2.2f", (double)loadav[0]/100,
	    (double)loadav[1]/100, (double)loadav[2]/100,
	    (double)loadav[3]/100);
#else
	sprintf (p, " %2.2f %2.2f %2.2f", loadav[0], loadav[1], loadav[2]);
#endif
#endif
    }
#endif
    p = buf + strlen (buf);
    sprintf (p, " (%d,%d) %cflow %cins %corg %cwrap %cpad", wp->y, wp->x,
	flowctl ? '+' : '-',
	wp->insert ? '+' : '-', wp->origin ? '+' : '-',
	wp->wrap ? '+' : '-', wp->keypad ? '+' : '-');
    if (ISO2022) {
	p = buf + strlen (buf);
	sprintf (p, " G%1d [", wp->LocalCharset);
	for (i = 0; i < 4; i++)
	    p[i+5] = wp->charsets[i] ? wp->charsets[i] : 'B';
	p[9] = ']';
	p[10] = '\0';
    }
    Msg (0, buf);
}

static OpenPTY () {
    register char *p, *l, *d;
    register i, f, tf;

    strcpy (PtyName, PtyProto);
    strcpy (TtyName, TtyProto);
    for (p = PtyName, i = 0; *p != 'X'; ++p, ++i) ;
    for (l = "qpr"; *p = *l; ++l) {
	for (d = "0123456789abcdef"; p[1] = *d; ++d) {
	    if ((f = open (PtyName, O_RDWR)) != -1) {
		TtyName[i] = p[0];
		TtyName[i+1] = p[1];
		if ((tf = open (TtyName, O_RDWR)) != -1) {
		    close (tf);
		    return f;
		}
		close (f);
	    }
	}
    }
    return -1;
}

static SetTTY (fd, mp) struct mode *mp; {
#ifdef BSD_TTY
    ioctl (fd, TIOCSETP, &mp->m_ttyb);
    ioctl (fd, TIOCSETC, &mp->m_tchars);
    ioctl (fd, TIOCSLTC, &mp->m_ltchars);
    ioctl (fd, TIOCLSET, &mp->m_lmode);
    ioctl (fd, TIOCSETD, &mp->m_ldisc);
#endif
}

static GetTTY (fd, mp) struct mode *mp; {
#ifdef BSD_TTY
    ioctl (fd, TIOCGETP, &mp->m_ttyb);
    ioctl (fd, TIOCGETC, &mp->m_tchars);
    ioctl (fd, TIOCGLTC, &mp->m_ltchars);
    ioctl (fd, TIOCLGET, &mp->m_lmode);
    ioctl (fd, TIOCGETD, &mp->m_ldisc);
#endif
}

static SetMode (op, np) struct mode *op, *np; {
    *np = *op;
#ifdef BSD_TTY
    np->m_ttyb.sg_flags &= ~(CRMOD|ECHO);
    np->m_ttyb.sg_flags |= CBREAK;
    np->m_tchars.t_intrc = -1;
    np->m_tchars.t_quitc = -1;
    if (!flowctl) {
	np->m_tchars.t_startc = -1;
	np->m_tchars.t_stopc = -1;
    }
    np->m_ltchars.t_suspc = -1;
    np->m_ltchars.t_dsuspc = -1;
    np->m_ltchars.t_flushc = -1;
    np->m_ltchars.t_lnextc = -1;
#endif
}

static char *GetTtyName () {
    register char *p;
    register n;

    for (p = 0, n = 0; n <= 2 && !(p = ttyname (n)); n++)
	;
    if (!p || *p == '\0')
	Msg (0, "screen must run on a tty.");
    return p;
}

static Attach (how) {
    register s, lasts, found = 0;
    register DIR *dirp;
    register struct dirent *dp;
    struct msg m;
    char last[MAXNAMLEN+1];

    if (SockName) {
	if ((lasts = MakeClientSocket (0)) == -1)
	    if (how == MSG_CONT)
		Msg (0,
		    "This screen has already been continued from elsewhere.");
	    else
		Msg (0, "There is no screen to be resumed from %s.", SockName);
    } else {
	if ((dirp = opendir (SockPath)) == NULL)
	    Msg (0, "Cannot open %s", SockPath);
	while ((dp = readdir (dirp)) != NULL) {
	    SockName = dp->d_name;
	    if (SockName[0] == '.')
		continue;
	    if ((s = MakeClientSocket (0)) != -1) {
		if (found == 0) {
		    strcpy (last, SockName);
		    lasts = s;
		} else {
		    if (found == 1) {
			printf ("There are detached screens on:\n");
			printf ("   %s\n", last);
			close (lasts);
		    }
		    printf ("   %s\n", SockName);
		    close (s);
		}
		found++;
	    }
	}
	if (found == 0)
	    Msg (0, "There is no screen to be resumed.");
	if (found > 1)
	    Msg (0, "Type \"screen -r host.tty\" to resume one of them.");
	closedir (dirp);
	strcpy (SockNamePtr, last);
	SockName = SockNamePtr;
    }
    m.type = how;
    strcpy (m.m.attach.tty, GetTtyName ());
    m.m.attach.apid = getpid ();
    if (write (lasts, (char *)&m, sizeof (m)) != sizeof (m))
	Msg (errno, "write");
}

static AttacherFinit () {
    exit (0);
}

static ReAttach () {
    Attach (MSG_CONT);
}

static Attacher () {
    signal (SIGHUP, AttacherFinit);
    signal (SIGCONT, ReAttach);
    while (1)
	pause ();
}

static Detach (suspend) {
    register struct win **pp;

    if (Detached)
	return;
    signal (SIGHUP, SIG_IGN);
    SetTTY (0, &OldMode);
    FinitTerm ();
    if (suspend) {
	Kill (AttacherPid, SIGTSTP);
    } else {
	for (pp = wtab; pp < wtab+MAXWIN; ++pp)
	    if (*pp) RemoveUtmp ((*pp)->slot);
	printf ("\n[detached]\n");
	Kill (AttacherPid, SIGHUP);
	AttacherPid = 0;
    }
    close (0);
    close (1);
    close (2);
    ioctl (DevTty, TIOCNOTTY, (char *)0);
    Detached = 1;
    do {
	ReceiveMsg (ServerSocket); 
    } while (Detached);
    if (!suspend)
	for (pp = wtab; pp < wtab+MAXWIN; ++pp)
	    if (*pp) (*pp)->slot = SetUtmp ((*pp)->tty);
    signal (SIGHUP, SigHup);
}

static Kill (pid, sig) {
    if (pid != 0)
	(void) kill (pid, sig);
}

static GetSockName () {
    register client;
    static char buf[2*MAXSTR];

    if (!mflag && (SockName = getenv ("STY")) != 0 && *SockName != '\0') {
	client = 1;
	setuid (getuid ());
	setgid (getgid ());
    } else {
	sprintf (buf, "%s.%s", HostName, Filename (GetTtyName ()));
	SockName = buf;
	client = 0;
    }
    return client;
}

static MakeServerSocket () {
    register s;
    struct sockaddr_un a;
    char *p;

    if ((s = socket (AF_UNIX, SOCK_STREAM, 0)) == -1)
	Msg (errno, "socket");
    a.sun_family = AF_UNIX;
    strcpy (SockNamePtr, SockName);
    strcpy (a.sun_path, SockPath);
    if (connect (s, (struct sockaddr *)&a, strlen (SockPath)+2) != -1) {
	p = Filename (SockPath);
	Msg (0, "You have already a screen running on %s.\n\
If it has been detached, try \"screen -r\".", p);
	/*NOTREACHED*/
    }
    (void) unlink (SockPath);
    if (bind (s, (struct sockaddr *)&a, strlen (SockPath)+2) == -1)
	Msg (errno, "bind");
    (void) chown (SockPath, getuid (), getgid ());
    if (listen (s, 5) == -1)
	Msg (errno, "listen");
    return s;
}

static MakeClientSocket (err) {
    register s;
    struct sockaddr_un a;

    if ((s = socket (AF_UNIX, SOCK_STREAM, 0)) == -1)
	Msg (errno, "socket");
    a.sun_family = AF_UNIX;
    strcpy (SockNamePtr, SockName);
    strcpy (a.sun_path, SockPath);
    if (connect (s, (struct sockaddr *)&a, strlen (SockPath)+2) == -1) {
	if (err) {
	    Msg (errno, "connect: %s", SockPath);
	} else {
	    close (s);
	    return -1;
	}
    }
    return s;
}

static SendCreateMsg (s, ac, av, aflag) char **av; {
    struct msg m;
    register char *p;
    register len, n;

    m.type = MSG_CREATE;
    p = m.m.create.line;
    for (n = 0; ac > 0 && n < MAXARGS-1; ++av, --ac, ++n) {
	len = strlen (*av) + 1;
	if (p + len >= m.m.create.line+MAXLINE)
	    break;
	strcpy (p, *av);
	p += len;
    }
    m.m.create.nargs = n;
    m.m.create.aflag = aflag;
    if (getcwd (m.m.create.dir, sizeof(m.m.create.dir)) == 0)
	Msg (0, "%s", m.m.create.dir);
    if (write (s, (char *)&m, sizeof (m)) != sizeof (m))
	Msg (errno, "write");
}

/*VARARGS1*/
static SendErrorMsg (fmt, p1, p2, p3, p4, p5, p6) char *fmt; {
    register s;
    struct msg m;

    s = MakeClientSocket (1);
    m.type = MSG_ERROR;
    sprintf (m.m.message, fmt, p1, p2, p3, p4, p5, p6);
    (void) write (s, (char *)&m, sizeof (m));
    close (s);
    sleep (2);
}

static ReceiveMsg (s) {
    register ns;
    struct sockaddr_un a;
    int left, len = sizeof (a);
    struct msg m;
    char *p;

    if ((ns = accept (s, (struct sockaddr *)&a, &len)) == -1) {
	Msg (errno, "accept");
	return;
    }
    p = (char *)&m;
    left = sizeof (m);
    while (left > 0 && (len = read (ns, p, left)) > 0) {
	p += len;
	left -= len;
    }
    close (ns);
    if (len == -1)
	Msg (errno, "read");
    if (left > 0)
	return;
    switch (m.type) {
    case MSG_CREATE:
	if (!Detached)
	    ExecCreate (&m);
	break;
    case MSG_CONT:
	if (m.m.attach.apid != AttacherPid || !Detached)
	    break;	/* Intruder Alert */
	/*FALLTHROUGH*/
    case MSG_ATTACH:
	if (Detached) {
	    if (kill (m.m.attach.apid, 0) == 0 &&
		    open (m.m.attach.tty, O_RDWR) == 0) {
		(void) dup (0);
		(void) dup (0);
		AttacherPid = m.m.attach.apid;
		Detached = 0;
		GetTTY (0, &OldMode);
		SetMode (&OldMode, &NewMode);
		SetTTY (0, &NewMode);
		Activate (wtab[CurrNum]);
	    }
	} else {
	    Kill (m.m.attach.apid, SIGHUP);
	    Msg (0, "Not detached.");
	}
	break;
    case MSG_ERROR:
	Msg (0, "%s", m.m.message);
	break;
    default:
	Msg (0, "Invalid message (type %d).", m.type);
    }
}

static ExecCreate (mp) struct msg *mp; {
    char *args[MAXARGS];
    register n;
    register char **pp = args, *p = mp->m.create.line;

    for (n = mp->m.create.nargs; n > 0; --n) {
	*pp++ = p;
	p += strlen (p) + 1;
    }
    *pp = 0;
    if ((n = MakeWindow (mp->m.create.line, args, mp->m.create.aflag, 0,
	    mp->m.create.dir)) != -1)
	SwitchWindow (n);
}

static ReadRc (fn) char *fn; {
    FILE *f;
    register char *p, **pp, **ap;
    register argc, num, c;
    char buf[256];
    char *args[MAXARGS];
    int key;

    ap = args;
    if (access (fn, R_OK) == -1)
	return;
    if ((f = fopen (fn, "r")) == NULL)
	return;
    while (fgets (buf, 256, f) != NULL) {
	if (p = rindex (buf, '\n'))
	    *p = '\0';
	if ((argc = Parse (fn, buf, ap)) == 0)
	    continue;
	if (strcmp (ap[0], "escape") == 0) {
	    p = ap[1];
	    if (argc < 2 || strlen (p) != 2)
		Msg (0, "%s: two characters required after escape.", fn);
	    Esc = *p++;
	    MetaEsc = *p;
	} else if (strcmp (ap[0], "chdir") == 0) {
	    p = argc < 2 ? home : ap[1];
	    if (chdir (p) == -1)
		Msg (errno, "%s", p);
	} else if (strcmp (ap[0], "mode") == 0) {
	    if (argc != 2) {
		Msg (0, "%s: mode: one argument required.", fn);
	    } else if (!IsNum (ap[1], 7)) {
		Msg (0, "%s: mode: octal number expected.", fn);
	    } else (void) sscanf (ap[1], "%o", &TtyMode);
	} else if (strcmp (ap[0], "bell") == 0) {
	    if (argc != 2) {
		Msg (0, "%s: bell: one argument required.", fn);
	    } else {
		if ((BellString = malloc (strlen (ap[1]) + 1)) == 0)
		    Msg (0, "Out of memory.");
		strcpy (BellString, ap[1]);
	    }
	} else if (strcmp (ap[0], "screen") == 0) {
	    num = 0;
	    if (argc > 1 && IsNum (ap[1], 10)) {
		num = atoi (ap[1]);
		if (num < 0 || num > MAXWIN-1)
		    Msg (0, "%s: illegal screen number %d.", fn, num);
		--argc; ++ap;
	    }
	    if (argc < 2) {
		ap[1] = ShellProg; argc = 2;
	    }
	    ap[argc] = 0;
	    (void) MakeWindow (ap[1], ap+1, 0, num, (char *)0);
	} else if (strcmp (ap[0], "bind") == 0) {
	    p = ap[1];
	    if (argc < 2 || *p == '\0')
		Msg (0, "%s: key expected after bind.", fn);
	    if (p[1] == '\0') {
		key = *p;
	    } else if (p[0] == '^' && p[1] != '\0' && p[2] == '\0') {
		c = p[1];
		if (isupper (c))
		    p[1] = tolower (c);    
		key = Ctrl(c);
	    } else if (IsNum (p, 7)) {
		(void) sscanf (p, "%o", &key);
	    } else {
		Msg (0,
		    "%s: bind: character, ^x, or octal number expected.", fn);
	    }
	    if (argc < 3) {
		ktab[key].type = 0;
	    } else {
		for (pp = KeyNames; *pp; ++pp)
		    if (strcmp (ap[2], *pp) == 0) break;
		if (*pp) {
		    ktab[key].type = pp-KeyNames+1;
		} else {
		    ktab[key].type = KEY_CREATE;
		    ktab[key].args = SaveArgs (argc-2, ap+2);
		}
	    }
	} else Msg (0, "%s: unknown keyword \"%s\".", fn, ap[0]);
    }
    (void) fclose (f);
}

static Parse (fn, buf, args) char *fn, *buf, **args; {
    register char *p = buf, **ap = args;
    register delim, argc = 0;

    argc = 0;
    for (;;) {
	while (*p && (*p == ' ' || *p == '\t')) ++p;
	if (*p == '\0' || *p == '#')
	    return argc;
	if (argc > MAXARGS-1)
	    Msg (0, "%s: too many tokens.", fn);
	delim = 0;
	if (*p == '"' || *p == '\'') {
	    delim = *p; *p = '\0'; ++p;
	}
	++argc;
	*ap = p; ++ap;
	while (*p && !(delim ? *p == delim : (*p == ' ' || *p == '\t')))
	    ++p;
	if (*p == '\0') {
	    if (delim)
		Msg (0, "%s: Missing quote.", fn);
	    else
		return argc;
	}
	*p++ = '\0';
    }
}

static char **SaveArgs (argc, argv) register argc; register char **argv; {
    register char **ap, **pp;

    if ((pp = ap = (char **)malloc ((argc+1) * sizeof (char **))) == 0)
	Msg (0, "Out of memory.");
    while (argc--) {
	if ((*pp = malloc (strlen (*argv)+1)) == 0)
	    Msg (0, "Out of memory.");
	strcpy (*pp, *argv);
	++pp; ++argv;
    }
    *pp = 0;
    return ap;
}

static MakeNewEnv () {
    register char **op, **np = NewEnv;
    static char buf[MAXSTR];

    if (strlen (SockName) > MAXSTR-5)
	SockName = "?";
    sprintf (buf, "STY=%s", SockName);
    *np++ = buf;
    *np++ = Term;
    np += 2;
    for (op = environ; *op; ++op) {
	if (np == NewEnv + MAXARGS - 1)
	    break;
	if (!IsSymbol (*op, "TERM") && !IsSymbol (*op, "TERMCAP")
		&& !IsSymbol (*op, "STY"))
	    *np++ = *op;
    }
    *np = 0;
}

static IsSymbol (e, s) register char *e, *s; {
    register char *p;
    register n;

    for (p = e; *p && *p != '='; ++p) ;
    if (*p) {
	*p = '\0';
	n = strcmp (e, s);
	*p = '=';
	return n == 0;
    }
    return 0;
}

/*VARARGS2*/
Msg (err, fmt, p1, p2, p3, p4, p5, p6) char *fmt; {
    char buf[1024];
    register char *p = buf;

    if (Detached)
	return;
    sprintf (p, fmt, p1, p2, p3, p4, p5, p6);
    if (err) {
	p += strlen (p);
	if (err > 0 && err < sys_nerr)
	    sprintf (p, ": %s", sys_errlist[err]);
	else
	    sprintf (p, ": Error %d", err);
    }
    if (HasWindow) {
	MakeStatus (buf, curr);
    } else {
	printf ("%s\r\n", buf);
	Kill (AttacherPid, SIGHUP);
	exit (1);
    }
}

bclear (p, n) char *p; {
    bcopy (blank, p, n);
}

static char *Filename (s) char *s; {
    register char *p;

    p = s + strlen (s) - 1;
    while (p >= s && *p != '/') --p;
    return ++p;
}

static IsNum (s, base) register char *s; register base; {
    for (base += '0'; *s; ++s)
	if (*s < '0' || *s > base)
	    return 0;
    return 1;
}

static char *MakeBellMsg (n) {
    static char buf[MAXSTR];
    register char *p = buf, *s = BellString;

    for (s = BellString; *s && p < buf+MAXSTR-1; s++)
	*p++ = (*s == '%') ? n + '0' : *s;
    *p = '\0';
    return buf;
}

#ifdef UTMP
static InitUtmp () {
    struct passwd *p;

    if ((utmpf = open (UtmpName, O_WRONLY)) == -1) {
	if (errno != EACCES)
	    Msg (errno, UtmpName);
	return;
    }
    if ((LoginName = getlogin ()) == 0 || LoginName[0] == '\0') {
	if ((p = getpwuid (getuid ())) == 0)
	    return;
	LoginName = p->pw_name;
    }
    utmp = 1;
}

static SetUtmp (name) char *name; {
    register char *p;
    register struct ttyent *tp;
    register slot = 1;
    struct utmp u;

    if (!utmp)
	return 0;
    if (p = rindex (name, '/'))
	++p;
    else p = name;
    setttyent ();
    while ((tp = getttyent ()) != NULL && strcmp (p, tp->ty_name) != 0)
	++slot;
    if (tp == NULL)
	return 0;
    strncpy (u.ut_line, p, 8);
    strncpy (u.ut_user, LoginName, 8);
    u.ut_host[0] = '\0';
    time (&u.ut_time);
    (void) lseek (utmpf, (long)(slot * sizeof (u)), 0);
    (void) write (utmpf, (char *)&u, sizeof (u));
    return slot;
}

static RemoveUtmp (slot) {
    struct utmp u;

    if (slot) {
	bzero ((char *)&u, sizeof (u));
	(void) lseek (utmpf, (long)(slot * sizeof (u)), 0);
	(void) write (utmpf, (char *)&u, sizeof (u));
    }
}

#else
static InitUtmp ()
{
}

static SetUtmp (name)
	char *name;
{
	return 0;
}

static RemoveUtmp (slot)
{
}

#endif

#ifndef GETTTYENT

static setttyent () {
    struct stat s;
    register f;
    register char *p, *ep;

    if (ttnext) {
	ttnext = tt;
	return;
    }
    if ((f = open (ttys, O_RDONLY)) == -1 || fstat (f, &s) == -1)
	Msg (errno, ttys);
    if ((tt = malloc (s.st_size + 1)) == 0)
	Msg (0, "Out of memory.");
    if (read (f, tt, s.st_size) != s.st_size)
	Msg (errno, ttys);
    close (f);
    for (p = tt, ep = p + s.st_size; p < ep; ++p)
	if (*p == '\n') *p = '\0';
    *p = '\0';
    ttnext = tt;
}

static struct ttyent *getttyent () {
    static struct ttyent t;

    if (*ttnext == '\0')
	return NULL;
    t.ty_name = ttnext + 2;
    ttnext += strlen (ttnext) + 1;
    return &t;
}

#endif

#ifdef LOADAV

static InitKmem () {
    if ((kmemf = open (KmemName, O_RDONLY)) == -1)
	return;
    nl[0].n_name = AvenrunSym;
    nlist (UnixName, nl);
    if (nl[0].n_type == 0 || nl[0].n_value == 0)
	return;
    avenrun = 1;
}

static GetAvenrun () {
    if (lseek (kmemf, nl[0].n_value, 0) == -1)
	return 0;
    if (read (kmemf, loadav, sizeof (loadav)) != sizeof (loadav))
	return 0;
    return 1;
}

#endif

#ifndef USEBCOPY
bcopy (s1, s2, len) register char *s1, *s2; register len; {
    if (s1 < s2 && s2 < s1 + len) {
	s1 += len; s2 += len;
	while (len-- > 0) {
	    *--s2 = *--s1;
	}
    } else {
	while (len-- > 0) {
	    *s2++ = *s1++;
	}
    }
}
#endif

#ifndef GETHOSTNAME
int gethostname (host, size)
	char *host;
	int size;
{
	strncpy (HostName, "elks", size);
	return 0;
}
#endif

#ifndef GETPGID
gid_t getpgid(pid_t pid)
{
	return pid;
}
#endif

#ifndef SETPGID
void setpgid(pid_t pid)
{
}
#endif
