[comp.sys.sgi] BSD "screen" program for the 4D Part 3/3

jmb@patton.sgi.com (Jim Barton) (08/04/89)

"screen" for the IRIS 4D series, Part 3/3

#--------------------------------CUT HERE-------------------------------------
#! /bin/sh
#
# This is a shell archive.  Save this into a file, edit it
# and delete all lines above this comment.  Then give this
# file to sh by executing the command "sh file".  The files
# will be extracted into the current directory owned by
# you with default permissions.
#
# The files contained herein are:
#
# -r--r--r--   1 jmb      sys        43860 Aug  3 11:24 screen.c
# -r--r--r--   1 jmb      sys         2247 Aug  3 11:24 screen.h
# -r--r--r--   1 jmb      sys          633 Aug  3 11:24 screen.tic
#
echo 'x - screen.c'
if test -f screen.c; then echo 'shar: not overwriting screen.c'; else
sed 's/^X//' << '________This_Is_The_END________' > screen.c
X/* Copyright (c) 1987,1988 Oliver Laumann, Technical University of Berlin.
X * Not derived from licensed software.
X *
X * Permission is granted to freely use, copy, modify, and redistribute
X * this software, provided that no attempt is made to gain profit from it,
X * the author is not construed to be liable for any results of using the
X * software, alterations are clearly marked as such, and this notice is
X * not modified.
X */
X
Xstatic char ScreenVersion[] = "screen 2.0a 19-Oct-88 SGI 4D Series";
X
X#include <sys/types.h>
X#include <sys/time.h>
X#include <sys/file.h>
X#include <sys/wait.h>
X#include <sys/socket.h>
X# ifndef sgi
X#include <sys/un.h>
X# endif
X#include <sys/stat.h>
X#include <sys/dir.h>
X#ifdef SUNLOADAV
X#ifndef sgi
X#include <sys/param.h>
X#endif
X#endif
X#ifdef sgi
X#include <sys/sysmacros.h>
X#include <sys/utsname.h>
Xstatic char	is3_3 = 0;
X#define		setpgrp		BSDsetpgrp
X#define		getpgrp		BSDgetpgrp
X#endif
X
X#include <stdio.h>
X#ifndef SYSV
X#include <sgtty.h>
X#endif
X#include <signal.h>
X#include <errno.h>
X#include <ctype.h>
X#include <utmp.h>
X#include <pwd.h>
X#include <nlist.h>
X#include <fcntl.h>
X#ifdef SYSV
X# include	<termio.h>
X# include	<string.h>
X#endif
X
X#include "screen.h"
X
X#ifndef SYSV
X#ifdef GETTTYENT
X#   include <ttyent.h>
X#else
X    static struct ttyent {
X	char *ty_name;
X    } *getttyent();
X    static char *tt, *ttnext;
X    static char ttys[] = "/etc/ttys";
X#endif
X#endif
X
X#define MAXWIN     10
X#define MSGWAIT     5
X
X#define Ctrl(c) ((c)&037)
X
Xextern char *blank, Term[], **environ;
Xextern rows, cols;
Xextern ISO2022;
Xextern status;
Xextern time_t TimeDisplayed;
Xextern char AnsiVersion[];
Xextern short ospeed;
Xextern flowctl;
Xextern errno;
Xextern sys_nerr;
Xextern char *sys_errlist[];
Xextern char *index(), *rindex(), *malloc(), *getenv(), *MakeTermcap();
Xextern char *getlogin(), *ttyname();
Xstatic AttacherFinit(), Finit(), SigHup(), SigChld();
Xstatic char *MakeBellMsg(), *Filename(), **SaveArgs(), *GetTtyName();
X
Xstatic char PtyName[32], TtyName[32];
Xstatic char *ShellProg;
Xstatic char *ShellArgs[2];
Xstatic char inbuf[IOSIZE];
Xstatic inlen;
Xstatic ESCseen;
Xstatic GotSignal;
Xstatic char DefaultShell[] = "/bin/sh";
Xstatic char DefaultPath[] = ":/usr/ucb:/bin:/usr/bin";
Xstatic char PtyProto[] = "/dev/ptyXY";
Xstatic char TtyProto[] = "/dev/ttyXY";
Xstatic int TtyMode = 0622;
Xstatic char SockPath[512];
Xstatic char SockDir[] = ".screen";
Xstatic char *SockNamePtr, *SockName;
Xstatic ServerSocket;
Xstatic char *NewEnv[MAXARGS];
Xstatic char Esc = Ctrl('a');
Xstatic char MetaEsc = 'a';
Xstatic char *home;
Xstatic HasWindow;
Xstatic utmp, utmpf;
Xstatic char UtmpName[] = "/etc/utmp";
Xstatic char *LoginName;
Xstatic char *BellString = "Bell in window %";
Xstatic mflag, nflag, fflag, rflag;
Xstatic char HostName[MAXSTR];
Xstatic Detached;
Xstatic AttacherPid;	/* Non-Zero in child if we have an attacher */
Xstatic DevTty;
X#ifdef SYSV
Xstatic char	proexec = 0;
Xstatic char	chanopen = 0;
X#endif
X#ifdef LOADAV
X    static char KmemName[] = "/dev/kmem";
X#ifdef sequent
X    static char UnixName[] = "/dynix";
X#else
X#ifdef sgi
X    static char UnixName[] = "/unix";
X#else
X    static char UnixName[] = "/vmunix";
X#endif
X#endif
X#ifdef alliant
X    static char AvenrunSym[] = "_Loadavg";
X#else
X#ifdef sgi
X    static char AvenrunSym[] = "avenrun";
X#else
X    static char AvenrunSym[] = "_avenrun";
X#endif
X#endif
X    static struct nlist nl[2];
X    static avenrun, kmemf;
X#ifdef SUNLOADAV
X    long loadav[3];
X#else
X#ifdef alliant
X    long loadav[4];
X#else
X    double loadav[3];
X#endif
X#endif
X
X#ifdef sgi
X#ifndef FSCALE
X#define	FSCALE		1000
X#endif
X#endif
X
X#endif
X
Xstruct mode {
X#ifdef SYSV
X    struct termio	m_tty;
X    int 		m_oldpgrp;
X#else
X    struct sgttyb m_ttyb;
X    struct tchars m_tchars;
X    struct ltchars m_ltchars;
X    int m_ldisc;
X    int m_lmode;
X#endif
X} OldMode, NewMode;
X
Xstatic struct win *curr, *other;
Xstatic CurrNum, OtherNum;
Xstatic struct win *wtab[MAXWIN];
X
X#define KEY_IGNORE         0
X#define KEY_HARDCOPY       1
X#define KEY_SUSPEND        2
X#define KEY_SHELL          3
X#define KEY_NEXT           4
X#define KEY_PREV           5
X#define KEY_KILL           6
X#define KEY_REDISPLAY      7
X#define KEY_WINDOWS        8
X#define KEY_VERSION        9
X#define KEY_OTHER         10
X#define KEY_0             11
X#define KEY_1             12
X#define KEY_2             13
X#define KEY_3             14
X#define KEY_4             15
X#define KEY_5             16
X#define KEY_6             17
X#define KEY_7             18
X#define KEY_8             19
X#define KEY_9             20
X#define KEY_XON           21
X#define KEY_XOFF          22
X#define KEY_INFO          23
X#define KEY_TERMCAP       24
X#define KEY_QUIT          25
X#define KEY_DETACH        26
X#define KEY_CREATE        27
X
Xstruct key {
X    int type;
X    char **args;
X} ktab[256];
X
Xchar *KeyNames[] = {
X    "hardcopy", "suspend", "shell", "next", "prev", "kill", "redisplay",
X    "windows", "version", "other", "select0", "select1", "select2", "select3",
X    "select4", "select5", "select6", "select7", "select8", "select9",
X    "xon", "xoff", "info", "termcap", "quit", "detach",
X    0
X};
X
Xmain (ac, av) char **av; {
X    register n, len;
X    register struct win **pp, *p;
X    char *ap;
X    int s, r, w, x = 0;
X    int aflag = 0;
X    struct timeval tv;
X    time_t now;
X    char buf[IOSIZE], *myname = (ac == 0) ? "screen" : av[0];
X    char rc[256];
X    struct stat st;
X
X    while (ac > 0) {
X	ap = *++av;
X	if (--ac > 0 && *ap == '-') {
X	    switch (ap[1]) {
X	    case 'c':        /* Compatibility with older versions. */
X		break;
X	    case 'a':
X		aflag = 1;
X		break;
X	    case 'm':
X		mflag = 1;
X		break;
X	    case 'n':
X		nflag = 1;
X		break;
X	    case 'f':
X		fflag = 1;
X		break;
X	    case 'r':
X		rflag = 1;
X		if (ap[2]) {
X		    SockName = ap+2;
X		    if (ac != 1) goto help;
X		} else if (--ac == 1) {
X		    SockName = *++av;
X		} else if (ac != 0) goto help;
X		break;
X	    case 'e':
X		if (ap[2]) {
X		    ap += 2;
X		} else {
X		    if (--ac == 0) goto help;
X		    ap = *++av;
X		}
X		if (strlen (ap) != 2)
X		    Msg (0, "Two characters are required with -e option.");
X		Esc = ap[0];
X		MetaEsc = ap[1];
X		break;
X	    default:
X	    help:
X		Msg (0, "Use: %s [-a] [-f] [-n] [-e xy] [cmd args]\n\
X or: %s -r [host.tty.pid]", myname, myname);
X	    }
X	} else break;
X    }
X    if (nflag && fflag)
X	Msg (0, "-f and -n are conflicting options.");
X    if ((ShellProg = getenv ("SHELL")) == 0)
X	ShellProg = DefaultShell;
X#ifdef SYSV
X#define MINUSMAX	32
X    if (ac == 0) {
X       char	shbuf[MINUSMAX];
X       char	*s;
X
X	if ((s = strrchr(ShellProg, '/')) == NULL) s = ShellProg; else s++;
X	strcpy(shbuf, "-");
X	if (strlen(s) + 2 > MINUSMAX)
X		Msg(0, "Path to shell in SHELL variable is too long.");
X	strcat(shbuf, s);
X	ShellArgs[0] = shbuf;
X	ac = 1;
X	av = ShellArgs;
X	proexec = 1;
X    }
X#else
X    ShellArgs[0] = ShellProg;
X    if (ac == 0) {
X	ac = 1;
X	av = ShellArgs;
X    }
X#endif
X    if ((home = getenv ("HOME")) == 0)
X	Msg (0, "$HOME is undefined.");
X#ifdef sgi
X    {
X       struct utsname	ubuf;
X    
X    	/*
X    	 * Releases eariler than 3.3 don't handle
X    	 * SIGCONT properly, so disable the facility.
X    	 */
X	uname(&ubuf);
X	if (strncmp(ubuf.release, "4D1-", 4) == 0) {
X		if (ubuf.release[4] >= '3' &&
X		    ubuf.release[6] >= '3')
X			is3_3 = 1;
X    	}
X    }
X#endif
X    sprintf (SockPath, "%s/%s", home, SockDir);
X    if (stat (SockPath, &st) == -1) {
X	if (errno == ENOENT) {
X	    if (mkdir (SockPath, 0700) == -1)
X		Msg (errno, "Cannot make directory %s", SockPath);
X	    (void) chown (SockPath, getuid (), getgid ());
X	} else Msg (errno, "Cannot get status of %s", SockPath);
X    } else {
X	if ((st.st_mode & S_IFMT) != S_IFDIR)
X	    Msg (0, "%s is not a directory.", SockPath);
X	if ((st.st_mode & 0777) != 0700)
X	    Msg (0, "Directory %s must have mode 700.", SockPath);
X	if (st.st_uid != getuid ())
X	    Msg (0, "You are not the owner of %s.", SockPath);
X    }
X    (void) gethostname (HostName, MAXSTR);
X    HostName[MAXSTR-1] = '\0';
X    if (ap = index (HostName, '.'))
X	*ap = '\0';
X    strcat (SockPath, "/");
X    SockNamePtr = SockPath + strlen (SockPath);
X    if ((DevTty = open ("/dev/tty", O_RDWR|O_NDELAY)) == -1)
X	Msg (errno, "/dev/tty");
X    if (rflag) {
X	Attach (MSG_ATTACH);
X	Attacher ();
X	/*NOTREACHED*/
X    }
X    if (GetSockName ()) {
X	s = MakeClientSocket (1);
X	SendCreateMsg (s, ac, av, aflag);
X	close (s);
X	exit (0);
X    }
X    switch (fork ()) {
X    case -1:
X	Msg (errno, "fork");
X	/*NOTREACHED*/
X    case 0:
X	break;
X    default:
X	Attacher ();
X	/*NOTREACHED*/
X    }
X    AttacherPid = getppid ();
X    ServerSocket = s = MakeServerSocket ();
X    InitTerm ();
X    if (fflag)
X	flowctl = 1;
X    else if (nflag)
X	flowctl = 0;
X    MakeNewEnv ();
X    GetTTY (0, &OldMode);
X#ifdef SYSV
X    ospeed = (short)(OldMode.m_tty.c_cflag & CBAUD);
X#else
X    ospeed = (short)OldMode.m_ttyb.sg_ospeed;
X#endif
X    InitUtmp ();
X#ifdef LOADAV
X    InitKmem ();
X#endif
X    signal (SIGHUP, SigHup);
X    signal (SIGINT, Finit);
X    signal (SIGQUIT, Finit);
X    signal (SIGTERM, Finit);
X    signal (SIGTTIN, SIG_IGN);
X    signal (SIGTTOU, SIG_IGN);
X    InitKeytab ();
X    sprintf (rc, "%.*s/.screenrc", 245, home);
X    ReadRc (rc);
X#ifdef SYSV
X    if ((n = MakeWindow ((proexec ? ShellProg : *av),
X	av, aflag, 0, (char *)0)) == -1) {
X#else
X    if ((n = MakeWindow (ShellProg, av, aflag, 0, (char *)0)) == -1) {
X#endif
X	SetTTY (0, &OldMode);
X	FinitTerm ();
X	Kill (AttacherPid, SIGHUP);
X#ifdef SYSV
X	if (chanopen)
X		unlink(SockPath);
X#endif
X	exit (1);
X    }
X    SetCurrWindow (n);
X    HasWindow = 1;
X    SetMode (&OldMode, &NewMode);
X    SetTTY (0, &NewMode);
X    signal (SIGCHLD, SigChld);
X    tv.tv_usec = 0;
X    while (1) {
X	if (status) {
X	    time (&now);
X	    if (now - TimeDisplayed < MSGWAIT) {
X		tv.tv_sec = MSGWAIT - (now - TimeDisplayed);
X	    } else RemoveStatus (curr);
X	}
X	r = 0;
X	w = 0;
X	if (inlen)
X	    w |= 1 << curr->ptyfd;
X	else
X	    r |= 1 << 0;
X	for (pp = wtab; pp < wtab+MAXWIN; ++pp) {
X	    if (!(p = *pp))
X		continue;
X	    if ((*pp)->active && status)
X		continue;
X	    if ((*pp)->outlen > 0)
X		continue;
X	    r |= 1 << (*pp)->ptyfd;
X	}
X	r |= 1 << s;
X	(void) fflush (stdout);
X	if (GotSignal && !status) {
X	    SigHandler ();
X	    continue;
X	}
X	if (select (32, &r, &w, &x, status ? &tv : (struct timeval *)0) == -1) {
X	    if (errno == EINTR)
X		continue;
X	    HasWindow = 0;
X	    Msg (errno, "select");
X	    /*NOTREACHED*/
X	}
X	if (GotSignal && !status) {
X	    SigHandler ();
X	    continue;
X	}
X	if (r & (1 << s)) {
X	    RemoveStatus (curr);
X	    ReceiveMsg (s);
X	}
X	if (r & (1 << 0)) {
X	    RemoveStatus (curr);
X	    if (ESCseen) {
X		inbuf[0] = Esc;
X		inlen = read (0, inbuf+1, IOSIZE-1) + 1;
X		ESCseen = 0;
X	    } else {
X		inlen = read (0, inbuf, IOSIZE);
X	    }
X	    if (inlen > 0)
X		inlen = ProcessInput (inbuf, inlen);
X	    if (inlen > 0)
X		continue;
X	}
X	if (GotSignal && !status) {
X	    SigHandler ();
X	    continue;
X	}
X	if (w & (1 << curr->ptyfd) && inlen > 0) {
X	    if ((len = write (curr->ptyfd, inbuf, inlen)) > 0) {
X		inlen -= len;
X		bcopy (inbuf+len, inbuf, inlen);
X	    }
X	}
X	if (GotSignal && !status) {
X	    SigHandler ();
X	    continue;
X	}
X	for (pp = wtab; pp < wtab+MAXWIN; ++pp) {
X	    if (!(p = *pp))
X		continue;
X	    if (p->outlen) {
X		WriteString (p, p->outbuf, p->outlen);
X	    } else if (r & 1 << p->ptyfd) {
X		if ((len = read (p->ptyfd, buf, IOSIZE)) == -1) {
X		    if (errno == EWOULDBLOCK)
X			len = 0;
X		}
X		if (len > 0)
X		    WriteString (p, buf, len);
X	    }
X	    if (p->bell) {
X		p->bell = 0;
X		Msg (0, MakeBellMsg (pp-wtab));
X	    }
X	}
X	if (GotSignal && !status)
X	    SigHandler ();
X    }
X    /*NOTREACHED*/
X}
X
Xstatic SigHandler () {
X    while (GotSignal) {
X	GotSignal = 0;
X	DoWait ();
X    }
X}
X
Xstatic SigChld () {
X    GotSignal = 1;
X}
X
Xstatic SigHup () {
X    Detach (0);
X}
X
Xstatic DoWait () {
X    register pid;
X    register struct win **pp;
X    union wait wstat;
X
X    while ((pid = wait3 (&wstat, WNOHANG|WUNTRACED, NULL)) > 0) {
X	for (pp = wtab; pp < wtab+MAXWIN; ++pp) {
X	    if (*pp && pid == (*pp)->wpid) {
X		if (WIFSTOPPED (wstat)) {
X		    (void) killpg (getpgrp ((*pp)->wpid), SIGCONT);
X		} else {
X		    KillWindow (pp);
X		}
X	    }
X	}
X    }
X#ifdef SYSV
X    signal(SIGCHLD, SigChld);
X#endif
X    CheckWindows ();
X}
X
Xstatic KillWindow (pp) struct win **pp; {
X    if (*pp == curr)
X	curr = 0;
X    if (*pp == other)
X	other = 0;
X    FreeWindow (*pp);
X    *pp = 0;
X}
X
Xstatic CheckWindows () {
X    register struct win **pp;
X
X    /* If the current window disappeared and the "other" window is still
X     * there, switch to the "other" window, else switch to the window
X     * with the lowest index.
X     * If there current window is still there, but the "other" window
X     * vanished, "SetCurrWindow" is called in order to assign a new value
X     * to "other".
X     * If no window is alive at all, exit.
X     */
X    if (!curr && other) {
X	SwitchWindow (OtherNum);
X	return;
X    }
X    if (curr && !other) {
X	SetCurrWindow (CurrNum);
X	return;
X    }
X    for (pp = wtab; pp < wtab+MAXWIN; ++pp) {
X	if (*pp) {
X	    if (!curr)
X		SwitchWindow (pp-wtab);
X	    return;
X	}
X    }
X    Finit ();
X}
X
Xstatic Finit () {
X    register struct win *p, **pp;
X
X    for (pp = wtab; pp < wtab+MAXWIN; ++pp) {
X	if (p = *pp)
X	    FreeWindow (p);
X    }
X    SetTTY (0, &OldMode);
X    FinitTerm ();
X    printf ("[screen is terminating]\n");
X    Kill (AttacherPid, SIGHUP);
X#ifdef SYSV
X    if (chanopen)
X	    unlink(SockPath);
X#endif
X    exit (0);
X}
X
Xstatic InitKeytab () {
X    register i;
X
X    ktab['h'].type = ktab[Ctrl('h')].type = KEY_HARDCOPY;
X    ktab['z'].type = ktab[Ctrl('z')].type = KEY_SUSPEND;
X    ktab['c'].type = ktab[Ctrl('c')].type = KEY_SHELL;
X    ktab[' '].type = ktab[Ctrl(' ')].type = 
X    ktab['n'].type = ktab[Ctrl('n')].type = KEY_NEXT;
X    ktab['-'].type = ktab['p'].type = ktab[Ctrl('p')].type = KEY_PREV;
X    ktab['k'].type = ktab[Ctrl('k')].type = KEY_KILL;
X    ktab['l'].type = ktab[Ctrl('l')].type = KEY_REDISPLAY;
X    ktab['w'].type = ktab[Ctrl('w')].type = KEY_WINDOWS;
X    ktab['v'].type = ktab[Ctrl('v')].type = KEY_VERSION;
X    ktab['q'].type = ktab[Ctrl('q')].type = KEY_XON;
X    ktab['s'].type = ktab[Ctrl('s')].type = KEY_XOFF;
X    ktab['t'].type = ktab[Ctrl('t')].type = KEY_INFO;
X    ktab['.'].type = KEY_TERMCAP;
X    ktab[Ctrl('\\')].type = KEY_QUIT;
X    ktab['d'].type = ktab[Ctrl('d')].type = KEY_DETACH;
X    ktab[Esc].type = KEY_OTHER;
X    for (i = 0; i <= 9; i++)
X	ktab[i+'0'].type = KEY_0+i;
X}
X
Xstatic ProcessInput (buf, len) char *buf; {
X    register n, k;
X    register char *s, *p;
X    register struct win **pp;
X
X    for (s = p = buf; len > 0; len--, s++) {
X	if (*s == Esc) {
X	    if (len > 1) {
X		len--; s++;
X		k = ktab[*s].type;
X		if (*s == MetaEsc) {
X		    *p++ = Esc;
X		} else if (k >= KEY_0 && k <= KEY_9) {
X		    p = buf;
X		    SwitchWindow (k - KEY_0);
X		} else switch (ktab[*s].type) {
X		case KEY_TERMCAP:
X		    p = buf;
X		    WriteFile (0);
X		    break;
X		case KEY_HARDCOPY:
X		    p = buf;
X		    WriteFile (1);
X		    break;
X		case KEY_SUSPEND:
X#ifdef sgi
X		    if (!is3_3) {
X    			Msg(0,"Suspend not supported on this release");
X    			break;
X    		    }
X#endif
X		    p = buf;
X		    Detach (1);
X		    break;
X		case KEY_SHELL:
X		    p = buf;
X		    if ((n = MakeWindow (ShellProg, ShellArgs,
X			    0, 0, (char *)0)) != -1)
X			SwitchWindow (n);
X		    break;
X		case KEY_NEXT:
X		    p = buf;
X		    if (MoreWindows ())
X			SwitchWindow (NextWindow ());
X		    break;
X		case KEY_PREV:
X		    p = buf;
X		    if (MoreWindows ())
X			SwitchWindow (PreviousWindow ());
X		    break;
X		case KEY_KILL:
X		    p = buf;
X		    FreeWindow (wtab[CurrNum]);
X		    if (other == curr)
X			other = 0;
X		    curr = wtab[CurrNum] = 0;
X		    CheckWindows ();
X		    break;
X		case KEY_QUIT:
X		    for (pp = wtab; pp < wtab+MAXWIN; ++pp)
X			if (*pp) FreeWindow (*pp);
X		    Finit ();
X		    /*NOTREACHED*/
X		case KEY_DETACH:
X		    p = buf;
X		    Detach (0);
X		    break;
X		case KEY_REDISPLAY:
X		    p = buf;
X		    Activate (wtab[CurrNum]);
X		    break;
X		case KEY_WINDOWS:
X		    p = buf;
X		    ShowWindows ();
X		    break;
X		case KEY_VERSION:
X		    p = buf;
X		    Msg (0, "%s  %s", ScreenVersion, AnsiVersion);
X		    break;
X		case KEY_INFO:
X		    p = buf;
X		    ShowInfo ();
X		    break;
X		case KEY_OTHER:
X		    p = buf;
X		    if (MoreWindows ())
X			SwitchWindow (OtherNum);
X		    break;
X		case KEY_XON:
X		    *p++ = Ctrl('q');
X		    break;
X		case KEY_XOFF:
X		    *p++ = Ctrl('s');
X		    break;
X		case KEY_CREATE:
X		    p = buf;
X		    if ((n = MakeWindow (ktab[*s].args[0], ktab[*s].args,
X			    0, 0, (char *)0)) != -1)
X			SwitchWindow (n);
X		    break;
X		}
X	    } else ESCseen = 1;
X	} else *p++ = *s;
X    }
X    return p - buf;
X}
X
Xstatic SwitchWindow (n) {
X    if (!wtab[n])
X	return;
X    SetCurrWindow (n);
X    Activate (wtab[n]);
X}
X
Xstatic SetCurrWindow (n) {
X    /*
X     * If we come from another window, this window becomes the
X     * "other" window:
X     */
X    if (curr) {
X	curr->active = 0;
X	other = curr;
X	OtherNum = CurrNum;
X    }
X    CurrNum = n;
X    curr = wtab[n];
X    curr->active = 1;
X    /*
X     * If the "other" window is currently undefined (at program start
X     * or because it has died), or if the "other" window is equal to the
X     * one just selected, we try to find a new one:
X     */
X    if (other == 0 || other == curr) {
X	OtherNum = NextWindow ();
X	other = wtab[OtherNum];
X    }
X}
X
Xstatic NextWindow () {
X    register struct win **pp;
X
X    for (pp = wtab+CurrNum+1; pp != wtab+CurrNum; ++pp) {
X	if (pp == wtab+MAXWIN)
X	    pp = wtab;
X	if (*pp)
X	    break;
X    }
X    return pp-wtab;
X}
X
Xstatic PreviousWindow () {
X    register struct win **pp;
X
X    for (pp = wtab+CurrNum-1; pp != wtab+CurrNum; --pp) {
X	if (pp < wtab)
X	    pp = wtab+MAXWIN-1;
X	if (*pp)
X	    break;
X    }
X    return pp-wtab;
X}
X
Xstatic MoreWindows () {
X    register struct win **pp;
X    register n;
X
X    for (n = 0, pp = wtab; pp < wtab+MAXWIN; ++pp)
X	if (*pp) ++n;
X    if (n <= 1)
X	Msg (0, "No other window.");
X    return n > 1;
X}
X
Xstatic FreeWindow (wp) struct win *wp; {
X    register i;
X
X    RemoveUtmp (wp->slot);
X    (void) chmod (wp->tty, 0666);
X    (void) chown (wp->tty, 0, 0);
X    close (wp->ptyfd);
X    for (i = 0; i < rows; ++i) {
X	free (wp->image[i]);
X	free (wp->attr[i]);
X	free (wp->font[i]);
X    }
X    free (wp->image);
X    free (wp->attr);
X    free (wp->font);
X    free (wp);
X}
X
Xstatic MakeWindow (prog, args, aflag, StartAt, dir)
X	char *prog, **args, *dir; {
X    register struct win **pp, *p;
X    register char **cp;
X    register n, f;
X    int tf;
X    int mypid;
X    char ebuf[10];
X
X    pp = wtab+StartAt;
X    do {
X	if (*pp == 0)
X	    break;
X	if (++pp == wtab+MAXWIN)
X	    pp = wtab;
X    } while (pp != wtab+StartAt);
X    if (*pp) {
X	Msg (0, "No more windows.");
X	return -1;
X    }
X    n = pp - wtab;
X    if ((f = OpenPTY ()) == -1) {
X	Msg (0, "No more PTYs.");
X	return -1;
X    }
X    (void) fcntl (f, F_SETFL, FNDELAY);
X    if ((p = *pp = (struct win *)malloc (sizeof (struct win))) == 0) {
Xnomem:
X	Msg (0, "Out of memory.");
X	return -1;
X    }
X    if ((p->image = (char **)malloc (rows * sizeof (char *))) == 0)
X	goto nomem;
X    for (cp = p->image; cp < p->image+rows; ++cp) {
X	if ((*cp = malloc (cols)) == 0)
X	    goto nomem;
X	bclear (*cp, cols);
X    }
X    if ((p->attr = (char **)malloc (rows * sizeof (char *))) == 0)
X	goto nomem;
X    for (cp = p->attr; cp < p->attr+rows; ++cp) {
X	if ((*cp = malloc (cols)) == 0)
X	    goto nomem;
X	bzero (*cp, cols);
X    }
X    if ((p->font = (char **)malloc (rows * sizeof (char *))) == 0)
X	goto nomem;
X    for (cp = p->font; cp < p->font+rows; ++cp) {
X	if ((*cp = malloc (cols)) == 0)
X	    goto nomem;
X	bzero (*cp, cols);
X    }
X    if ((p->tabs = malloc (cols+1)) == 0)  /* +1 because 0 <= x <= cols */
X	goto nomem;
X    ResetScreen (p);
X    p->aflag = aflag;
X    p->active = 0;
X    p->bell = 0;
X    p->outlen = 0;
X    p->ptyfd = f;
X    strncpy (p->cmd, Filename (args[0]), MAXSTR-1);
X    p->cmd[MAXSTR-1] = '\0';
X    strncpy (p->tty, TtyName, MAXSTR-1);
X    (void) chown (TtyName, getuid (), getgid ());
X    (void) chmod (TtyName, TtyMode);
X    p->slot = SetUtmp (TtyName);
X    switch (p->wpid = fork ()) {
X    case -1:
X	Msg (errno, "fork");
X	free ((char *)p);
X	return -1;
X    case 0:
X	signal (SIGHUP, SIG_DFL);
X	signal (SIGINT, SIG_DFL);
X	signal (SIGQUIT, SIG_DFL);
X	signal (SIGTERM, SIG_DFL);
X	signal (SIGTTIN, SIG_DFL);
X	signal (SIGTTOU, SIG_DFL);
X	setuid (getuid ());
X	setgid (getgid ());
X	if (dir && chdir (dir) == -1) {
X	    SendErrorMsg ("Cannot chdir to %s: %s", dir, sys_errlist[errno]);
X	    exit (1);
X	}
X	mypid = getpid ();
X	ioctl (DevTty, TIOCNOTTY, (char *)0);
X	if ((tf = open (TtyName, O_RDWR)) == -1) {
X	    SendErrorMsg ("Cannot open %s: %s", TtyName, sys_errlist[errno]);
X	    exit (1);
X	}
X	(void) dup2 (tf, 0);
X	(void) dup2 (tf, 1);
X	(void) dup2 (tf, 2);
X	for (f = getdtablesize () - 1; f > 2; f--)
X	    close (f);
X	ioctl (0, TIOCSPGRP, &mypid);
X	(void) setpgrp (0, mypid);
X	SetTTY (0, &OldMode);
X	NewEnv[2] = MakeTermcap (aflag);
X	sprintf (ebuf, "WINDOW=%d", n);
X	NewEnv[3] = ebuf;
X	execvpe (prog, args, NewEnv);
X	SendErrorMsg ("Cannot exec %s: %s", prog, sys_errlist[errno]);
X	exit (1);
X    }
X    return n;
X}
X
Xstatic execvpe (prog, args, env) char *prog, **args, **env; {
X    register char *path, *p;
X    char buf[1024];
X    char *shargs[MAXARGS+1];
X    register i, eaccess = 0;
X
X    if (prog[0] == '/')
X	path = "";
X    else if ((path = getenv ("PATH")) == 0)
X	path = DefaultPath;
X    do {
X	p = buf;
X	while (*path && *path != ':')
X	    *p++ = *path++;
X	if (p > buf)
X	    *p++ = '/';
X	strcpy (p, prog);
X	if (*path)
X	    ++path;
X	execve (buf, args, env);
X	switch (errno) {
X	case ENOEXEC:
X	    shargs[0] = DefaultShell;
X	    shargs[1] = buf;
X	    for (i = 1; shargs[i+1] = args[i]; ++i)
X		;
X	    execve (DefaultShell, shargs, env);
X	    return;
X	case EACCES:
X	    eaccess = 1;
X	    break;
X	case ENOMEM: case E2BIG: case ETXTBSY:
X	    return;
X	}
X    } while (*path);
X    if (eaccess)
X	errno = EACCES;
X}
X
Xstatic WriteFile (dump) {   /* dump==0: create .termcap, dump==1: hardcopy */
X    register i, j, k;
X    register char *p;
X    register FILE *f;
X    char fn[1024];
X    int pid, s;
X
X    if (dump)
X	sprintf (fn, "hardcopy.%d", CurrNum);
X    else
X	sprintf (fn, "%s/%s/.termcap", home, SockDir);
X    switch (pid = fork ()) {
X    case -1:
X	Msg (errno, "fork");
X	return;
X    case 0:
X	setuid (getuid ());
X	setgid (getgid ());
X	if ((f = fopen (fn, "w")) == NULL)
X	    exit (1);
X	if (dump) {
X	    for (i = 0; i < rows; ++i) {
X		p = curr->image[i];
X		for (k = cols-1; k >= 0 && p[k] == ' '; --k) ;
X		for (j = 0; j <= k; ++j)
X		    putc (p[j], f);
X		putc ('\n', f);
X	    }
X	} else {
X	    if (p = index (MakeTermcap (curr->aflag), '=')) {
X		fputs (++p, f);
X		putc ('\n', f);
X	    }
X	}
X	(void) fclose (f);
X	exit (0);
X    default:
X	while ((i = wait (&s)) != pid)
X	    if (i == -1) return;
X	if ((s >> 8) & 0377)
X	    Msg (0, "Cannot open \"%s\".", fn);
X	else
X	    Msg (0, "%s written to \"%s\".", dump ? "Screen image" :
X		"Termcap entry", fn);
X    }
X}
X
Xstatic ShowWindows () {
X    char buf[1024];
X    register char *s;
X    register struct win **pp, *p;
X
X    for (s = buf, pp = wtab; pp < wtab+MAXWIN; ++pp) {
X	if ((p = *pp) == 0)
X	    continue;
X	if (s - buf + 5 + strlen (p->cmd) > cols-1)
X	    break;
X	if (s > buf) {
X	    *s++ = ' '; *s++ = ' ';
X	}
X	*s++ = pp - wtab + '0';
X	if (p == curr)
X	    *s++ = '*';
X	else if (p == other)
X	    *s++ = '-';
X	*s++ = ' ';
X	strcpy (s, p->cmd);
X	s += strlen (s);
X    }
X    Msg (0, buf);
X}
X
Xstatic ShowInfo () {
X    char buf[1024], *p;
X    register struct win *wp = curr;
X    register i;
X    struct tm *tp;
X    time_t now;
X
X    time (&now);
X    tp = localtime (&now);
X    sprintf (buf, "%2d:%02.2d:%02.2d %s", tp->tm_hour, tp->tm_min, tp->tm_sec,
X	HostName);
X#ifdef LOADAV
X    if (avenrun && GetAvenrun ()) {
X	p = buf + strlen (buf);
X#ifdef SUNLOADAV
X	sprintf (p, " %2.2f %2.2f %2.2f", (double)loadav[0]/FSCALE,
X	    (double)loadav[1]/FSCALE, (double)loadav[2]/FSCALE);
X#else
X#ifdef alliant
X	sprintf (p, " %2.2f %2.2f %2.2f %2.2f", (double)loadav[0]/100,
X	    (double)loadav[1]/100, (double)loadav[2]/100,
X	    (double)loadav[3]/100);
X#else
X	sprintf (p, " %2.2f %2.2f %2.2f", loadav[0], loadav[1], loadav[2]);
X#endif
X#endif
X    }
X#endif
X    p = buf + strlen (buf);
X    sprintf (p, " (%d,%d) %cflow %cins %corg %cwrap %cpad", wp->y, wp->x,
X	flowctl ? '+' : '-',
X	wp->insert ? '+' : '-', wp->origin ? '+' : '-',
X	wp->wrap ? '+' : '-', wp->keypad ? '+' : '-');
X    if (ISO2022) {
X	p = buf + strlen (buf);
X	sprintf (p, " G%1d [", wp->LocalCharset);
X	for (i = 0; i < 4; i++)
X	    p[i+5] = wp->charsets[i] ? wp->charsets[i] : 'B';
X	p[9] = ']';
X	p[10] = '\0';
X    }
X    Msg (0, buf);
X}
X
X#ifdef sgi
Xstatic int
XOpenPTY()
X{
X        int fd;
X	int pnum;
X        struct stat sb;
X
X        fd = open("/dev/ptc", O_RDWR|O_NDELAY);
X        if (fd >= 0) {
X                if (fstat(fd, &sb) < 0) {
X                        close(fd);
X                        return -1;
X                }
X                pnum = minor(sb.st_rdev);
X                sprintf(TtyName, "/dev/ttyq%d", pnum);
X        }
X        return fd;
X}
X#else
Xstatic OpenPTY () {
X    register char *p, *l, *d;
X    register i, f, tf;
X
X    strcpy (PtyName, PtyProto);
X    strcpy (TtyName, TtyProto);
X    for (p = PtyName, i = 0; *p != 'X'; ++p, ++i) ;
X    for (l = "qpr"; *p = *l; ++l) {
X	for (d = "0123456789abcdef"; p[1] = *d; ++d) {
X	    if ((f = open (PtyName, O_RDWR)) != -1) {
X		TtyName[i] = p[0];
X		TtyName[i+1] = p[1];
X		if ((tf = open (TtyName, O_RDWR)) != -1) {
X		    close (tf);
X		    return f;
X		}
X		close (f);
X	    }
X	}
X    }
X    return -1;
X}
X#endif
X
Xstatic SetTTY (fd, mp) struct mode *mp; {
X#ifdef SYSV
X    ioctl (fd, TCSETA, &mp->m_tty);
X#else
X    ioctl (fd, TIOCSETP, &mp->m_ttyb);
X    ioctl (fd, TIOCSETC, &mp->m_tchars);
X    ioctl (fd, TIOCSLTC, &mp->m_ltchars);
X    ioctl (fd, TIOCLSET, &mp->m_lmode);
X    ioctl (fd, TIOCSETD, &mp->m_ldisc);
X#endif
X}
X
Xstatic GetTTY (fd, mp) struct mode *mp; {
X#ifdef SYSV
X    ioctl (fd, TCGETA, &mp->m_tty);
X#else
X    ioctl (fd, TIOCGETP, &mp->m_ttyb);
X    ioctl (fd, TIOCGETC, &mp->m_tchars);
X    ioctl (fd, TIOCGLTC, &mp->m_ltchars);
X    ioctl (fd, TIOCLGET, &mp->m_lmode);
X    ioctl (fd, TIOCGETD, &mp->m_ldisc);
X#endif
X}
X
Xstatic SetMode (op, np) struct mode *op, *np; {
X    *np = *op;
X#ifdef SYSV
X    np->m_tty.c_lflag = 0;
X    np->m_tty.c_iflag = IGNBRK;
X    np->m_tty.c_oflag = 0;
X    if (flowctl)
X	np->m_tty.c_iflag |= IXON|IXOFF;
X    np->m_tty.c_cc[VMIN] = 1;
X    np->m_tty.c_cc[VTIME] = 0;
X#else
X    np->m_ttyb.sg_flags &= ~(CRMOD|ECHO);
X    np->m_ttyb.sg_flags |= CBREAK;
X    np->m_tchars.t_intrc = -1;
X    np->m_tchars.t_quitc = -1;
X    if (!flowctl) {
X	np->m_tchars.t_startc = -1;
X	np->m_tchars.t_stopc = -1;
X    }
X    np->m_ltchars.t_suspc = -1;
X    np->m_ltchars.t_dsuspc = -1;
X    np->m_ltchars.t_flushc = -1;
X    np->m_ltchars.t_lnextc = -1;
X#endif
X}
X
Xstatic char *GetTtyName () {
X    register char *p;
X    register n;
X
X    for (p = 0, n = 0; n <= 2 && !(p = ttyname (n)); n++)
X	;
X    if (!p || *p == '\0')
X	Msg (0, "screen must run on a tty.");
X    return p;
X}
X
Xstatic ScreenInUse () {
X    Msg (0, "The screen is already attached elsewhere.");
X}
X
Xstatic Attach (how) {
X    register s, lasts, found = 0;
X    register DIR *dirp;
X    register struct direct *dp;
X    struct msg m;
X    char last[MAXNAMLEN+1];
X
X    if (SockName) {
X	if ((lasts = MakeClientSocket (0)) == -1)
X	    if (how == MSG_CONT)
X		Msg (0,
X		    "This screen has already been continued from elsewhere.");
X	    else
X		Msg (0, "There is no screen to be resumed from %s", SockName);
X    } else {
X	if ((dirp = opendir (SockPath)) == NULL)
X	    Msg (0, "Cannot open %s", SockPath);
X	while ((dp = readdir (dirp)) != NULL) {
X	    SockName = dp->d_name;
X	    if (SockName[0] == '.')
X		continue;
X	    if ((s = MakeClientSocket (0)) != -1) {
X		if (found == 0) {
X		    strcpy (last, SockName);
X		    lasts = s;
X		} else {
X		    if (found == 1) {
X			printf ("There are detached screens on:\n");
X			printf ("   %s\n", last);
X			close (lasts);
X		    }
X		    printf ("   %s\n", SockName);
X		    close (s);
X#ifdef SYSV
X		    chanopen = 0;
X#endif
X		}
X		found++;
X	    }
X	}
X	if (found == 0)
X	    Msg (0, "There is no screen to be resumed.");
X	if (found > 1)
X#ifdef SYSV
X	    Msg (0, "Type \"screen -r host.tty.pid\" to resume one of them.");
X#else
X	    Msg (0, "Type \"screen -r host.tty\" to resume one of them.");
X#endif
X	closedir (dirp);
X	strcpy (SockNamePtr, last);
X	SockName = SockNamePtr;
X    }
X    m.type = how;
X    strcpy (m.m.attach.tty, GetTtyName ());
X    m.m.attach.apid = getpid ();
X    /*
X     * Don't overlook the fact that we could get SIGHUP as a result
X     * of this call, if we attempt to attach to a window which is
X     * not detached.  If we do, issue a meaningful message.
X     */
X    signal(SIGHUP, ScreenInUse);
X    if (write (lasts, (char *)&m, sizeof (m)) != sizeof (m))
X	Msg (errno, "write");
X}
X
Xstatic AttacherFinit () {
X    exit (0);
X}
X
Xstatic ReAttach () {
X    Attach (MSG_CONT);
X#ifdef SYSV
X    signal(SIGCONT, ReAttach);
X#endif
X}
X
Xstatic Attacher () {
X    signal (SIGHUP, AttacherFinit);
X    signal (SIGCONT, ReAttach);
X    while (1)
X	pause ();
X}
X
Xstatic Detach (suspend) {
X    register struct win **pp;
X
X    if (Detached)
X	return;
X    signal (SIGHUP, SIG_IGN);
X    SetTTY (0, &OldMode);
X    FinitTerm ();
X    if (suspend) {
X	Kill (AttacherPid, SIGTSTP);
X    } else {
X	for (pp = wtab; pp < wtab+MAXWIN; ++pp)
X	    if (*pp) RemoveUtmp ((*pp)->slot);
X	printf ("\n[detached]\n");
X	Kill (AttacherPid, SIGHUP);
X	AttacherPid = 0;
X    }
X    close (0);
X    close (1);
X    close (2);
X    ioctl (DevTty, TIOCNOTTY, (char *)0);
X    Detached = 1;
X    do {
X	ReceiveMsg (ServerSocket); 
X    } while (Detached);
X    if (!suspend)
X	for (pp = wtab; pp < wtab+MAXWIN; ++pp)
X	    if (*pp) (*pp)->slot = SetUtmp ((*pp)->tty);
X    signal (SIGHUP, SigHup);
X}
X
Xstatic Kill (pid, sig) {
X    if (pid != 0)
X	(void) kill (pid, sig);
X}
X
Xstatic GetSockName () {
X    register client;
X    static char buf[3*MAXSTR];
X
X    if (!mflag && (SockName = getenv ("STY")) != 0 && *SockName != '\0') {
X	client = 1;
X	setuid (getuid ());
X	setgid (getgid ());
X    } else {
X#ifdef SYSV
X	sprintf (buf, "%s.%s.%d", HostName, Filename (GetTtyName ()),
X		getpid());
X#else
X	sprintf (buf, "%s.%s", HostName, Filename (GetTtyName ()));
X#endif
X	SockName = buf;
X	client = 0;
X    }
X    return client;
X}
X
Xstatic MakeServerSocket () {
X    register s;
X#ifndef SYSV
X    struct sockaddr_un a;
X#endif
X    char *p;
X
X#ifdef SYSV
X    int		oflags;
X
X    strcpy (SockNamePtr, SockName);
X    if (access(SockPath, 06) != -1) {
X	p = Filename (SockPath);
X	Msg (0, "You have already a screen running on %s.\n\
XIf it has been detached, try \"screen -r\".", p);
X	/*NOTREACHED*/
X    }
X    if (mknod(SockPath, 010600) == -1)
X	Msg (errno, "named pipe");
X    if ((s = open(SockPath, O_RDWR)) == -1)
X	Msg (errno, "named pipe open");
X    chanopen = 1;
X    (void) chown (SockPath, getuid (), getgid ());
X    return(s);
X#else
X    if ((s = socket (AF_UNIX, SOCK_STREAM, 0)) == -1)
X	Msg (errno, "socket");
X    a.sun_family = AF_UNIX;
X    strcpy (SockNamePtr, SockName);
X    strcpy (a.sun_path, SockPath);
X    if (connect (s, (struct sockaddr *)&a, strlen (SockPath)+2) != -1) {
X	p = Filename (SockPath);
X	Msg (0, "You have already a screen running on %s.\n\
XIf it has been detached, try \"screen -r\".", p);
X	/*NOTREACHED*/
X    }
X    if (bind (s, (struct sockaddr *)&a, strlen (SockPath)+2) == -1)
X	Msg (errno, "bind");
X    (void) chown (SockPath, getuid (), getgid ());
X    if (listen (s, 5) == -1)
X	Msg (errno, "listen");
X    return s;
X#endif
X}
X
Xstatic MakeClientSocket (err) {
X    register s;
X#ifndef SYSV
X    struct sockaddr_un a;
X#endif
X
X#ifdef SYSV
X    int		oflags;
X
X    strcpy (SockNamePtr, SockName);
X    if ((s = open(SockPath, O_WRONLY|O_NDELAY)) == -1) {
X	if (err) {
X	    Msg (errno, "connect: %s", SockPath);
X	} else {
X	    close (s);
X	    return -1;
X	}
X    }
X    chanopen = 1;
X    (void) chown (SockPath, getuid (), getgid ());
X    oflags = fcntl(s, F_GETFL);
X    oflags &= ~O_NDELAY;
X    fcntl(s, F_SETFL, oflags);
X#else
X    if ((s = socket (AF_UNIX, SOCK_STREAM, 0)) == -1)
X	Msg (errno, "socket");
X    a.sun_family = AF_UNIX;
X    strcpy (SockNamePtr, SockName);
X    strcpy (a.sun_path, SockPath);
X    if (connect (s, (struct sockaddr *)&a, strlen (SockPath)+2) == -1) {
X	if (err) {
X	    Msg (errno, "connect: %s", SockPath);
X	} else {
X	    close (s);
X	    return -1;
X	}
X    }
X#endif
X    return s;
X}
X
Xstatic SendCreateMsg (s, ac, av, aflag) char **av; {
X    struct msg m;
X    register char *p;
X    register len, n;
X
X    m.type = MSG_CREATE;
X    p = m.m.create.line;
X#ifdef SYSV
X    if (proexec)
X	strcpy(p, ShellProg);
X    else
X	strcpy(p, *av);
X    p = p + strlen(p) + 1;
X#endif
X    for (n = 0; ac > 0 && n < MAXARGS-1; ++av, --ac, ++n) {
X	len = strlen (*av) + 1;
X	if (p + len >= m.m.create.line+MAXLINE)
X	    break;
X	strcpy (p, *av);
X	p += len;
X    }
X    m.m.create.nargs = n;
X    m.m.create.aflag = aflag;
X    if (getwd (m.m.create.dir) == 0)
X	Msg (0, "%s", m.m.create.dir);
X    if (write (s, (char *)&m, sizeof (m)) != sizeof (m))
X	Msg (errno, "write");
X}
X
X/*VARARGS1*/
Xstatic SendErrorMsg (fmt, p1, p2, p3, p4, p5, p6) char *fmt; {
X    register s;
X    struct msg m;
X
X    s = MakeClientSocket (1);
X    m.type = MSG_ERROR;
X    sprintf (m.m.message, fmt, p1, p2, p3, p4, p5, p6);
X    (void) write (s, (char *)&m, sizeof (m));
X    close (s);
X    sleep (2);
X}
X
Xstatic ReceiveMsg (s) {
X#ifndef SYSV
X    register ns;
X    struct sockaddr_un a;
X    int left, len = sizeof (a);
X#else
X    int left, len;
X#endif
X    struct msg m;
X    char *p;
X
X#ifndef SYSV
X    if ((ns = accept (s, (struct sockaddr *)&a, &len)) == -1) {
X	Msg (errno, "accept");
X	return;
X    }
X    p = (char *)&m;
X    left = sizeof (m);
X    while (left > 0 && (len = read (ns, p, left)) > 0) {
X	p += len;
X	left -= len;
X    }
X    close (ns);
X#else
X    p = (char *)&m;
X    len = read (s, p, sizeof(m));
X    left = sizeof(m) - len;
X#endif
X    if (len == -1)
X	Msg (errno, "read");
X    if (left > 0)
X	return;
X    switch (m.type) {
X    case MSG_CREATE:
X	if (!Detached)
X	    ExecCreate (&m);
X	break;
X    case MSG_CONT:
X	if (m.m.attach.apid != AttacherPid || !Detached)
X	    break;	/* Intruder Alert */
X	/*FALLTHROUGH*/
X    case MSG_ATTACH:
X	if (Detached) {
X	    if (kill (m.m.attach.apid, 0) == 0 &&
X		    open (m.m.attach.tty, O_RDWR) == 0) {
X		(void) dup (0);
X		(void) dup (0);
X		AttacherPid = m.m.attach.apid;
X		Detached = 0;
X		GetTTY (0, &OldMode);
X		SetMode (&OldMode, &NewMode);
X		SetTTY (0, &NewMode);
X		Activate (wtab[CurrNum]);
X	    }
X	} else {
X	    Kill (m.m.attach.apid, SIGHUP);
X	    Msg (0, "Not detached.");
X	}
X	break;
X    case MSG_ERROR:
X	Msg (0, "%s", m.m.message);
X	break;
X    default:
X	Msg (0, "Invalid message (type %d).", m.type);
X    }
X}
X
Xstatic ExecCreate (mp) struct msg *mp; {
X    char *args[MAXARGS];
X    register n;
X    register char **pp = args, *p = mp->m.create.line;
X
X#ifdef SYSV
X    char	*xprog;
X
X    xprog = p;
X    p += strlen(p) + 1;
X#endif
X    for (n = mp->m.create.nargs; n > 0; --n) {
X	*pp++ = p;
X	p += strlen (p) + 1;
X    }
X    *pp = 0;
X#ifdef SYSV
X    if ((n = MakeWindow (xprog, args, mp->m.create.aflag, 0,
X#else
X    if ((n = MakeWindow (mp->m.create.line, args, mp->m.create.aflag, 0,
X#endif
X	    mp->m.create.dir)) != -1)
X	SwitchWindow (n);
X}
X
Xstatic ReadRc (fn) char *fn; {
X    FILE *f;
X    register char *p, **pp, **ap;
X    register argc, num, c;
X    char buf[256];
X    char *args[MAXARGS];
X    int key;
X
X    ap = args;
X    if (access (fn, R_OK) == -1)
X	return;
X    if ((f = fopen (fn, "r")) == NULL)
X	return;
X    while (fgets (buf, 256, f) != NULL) {
X	if (p = rindex (buf, '\n'))
X	    *p = '\0';
X	if ((argc = Parse (fn, buf, ap)) == 0)
X	    continue;
X	if (strcmp (ap[0], "escape") == 0) {
X	    p = ap[1];
X	    if (argc < 2 || strlen (p) != 2)
X		Msg (0, "%s: two characters required after escape.", fn);
X	    Esc = *p++;
X	    MetaEsc = *p;
X	} else if (strcmp (ap[0], "chdir") == 0) {
X	    p = argc < 2 ? home : ap[1];
X	    if (chdir (p) == -1)
X		Msg (errno, "%s", p);
X	} else if (strcmp (ap[0], "mode") == 0) {
X	    if (argc != 2) {
X		Msg (0, "%s: mode: one argument required.", fn);
X	    } else if (!IsNum (ap[1], 7)) {
X		Msg (0, "%s: mode: octal number expected.", fn);
X	    } else (void) sscanf (ap[1], "%o", &TtyMode);
X	} else if (strcmp (ap[0], "bell") == 0) {
X	    if (argc != 2) {
X		Msg (0, "%s: bell: one argument required.", fn);
X	    } else {
X		if ((BellString = malloc (strlen (ap[1]) + 1)) == 0)
X		    Msg (0, "Out of memory.");
X		strcpy (BellString, ap[1]);
X	    }
X	} else if (strcmp (ap[0], "screen") == 0) {
X	    num = 0;
X	    if (argc > 1 && IsNum (ap[1], 10)) {
X		num = atoi (ap[1]);
X		if (num < 0 || num > MAXWIN-1)
X		    Msg (0, "%s: illegal screen number %d.", fn, num);
X		--argc; ++ap;
X	    }
X	    if (argc < 2) {
X		ap[1] = ShellProg; argc = 2;
X	    }
X	    ap[argc] = 0;
X	    (void) MakeWindow (ap[1], ap+1, 0, num, (char *)0);
X	} else if (strcmp (ap[0], "bind") == 0) {
X	    p = ap[1];
X	    if (argc < 2 || *p == '\0')
X		Msg (0, "%s: key expected after bind.", fn);
X	    if (p[1] == '\0') {
X		key = *p;
X	    } else if (p[0] == '^' && p[1] != '\0' && p[2] == '\0') {
X		c = p[1];
X		if (isupper (c))
X		    p[1] = tolower (c);    
X		key = Ctrl(c);
X	    } else if (IsNum (p, 7)) {
X		(void) sscanf (p, "%o", &key);
X	    } else {
X		Msg (0,
X		    "%s: bind: character, ^x, or octal number expected.", fn);
X	    }
X	    if (argc < 3) {
X		ktab[key].type = 0;
X	    } else {
X		for (pp = KeyNames; *pp; ++pp)
X		    if (strcmp (ap[2], *pp) == 0) break;
X		if (*pp) {
X		    ktab[key].type = pp-KeyNames+1;
X		} else {
X		    ktab[key].type = KEY_CREATE;
X		    ktab[key].args = SaveArgs (argc-2, ap+2);
X		}
X	    }
X	} else Msg (0, "%s: unknown keyword \"%s\".", fn, ap[0]);
X    }
X    (void) fclose (f);
X}
X
Xstatic Parse (fn, buf, args) char *fn, *buf, **args; {
X    register char *p = buf, **ap = args;
X    register delim, argc = 0;
X
X    argc = 0;
X    for (;;) {
X	while (*p && (*p == ' ' || *p == '\t')) ++p;
X	if (*p == '\0' || *p == '#')
X	    return argc;
X	if (argc > MAXARGS-1)
X	    Msg (0, "%s: too many tokens.", fn);
X	delim = 0;
X	if (*p == '"' || *p == '\'') {
X	    delim = *p; *p = '\0'; ++p;
X	}
X	++argc;
X	*ap = p; ++ap;
X	while (*p && !(delim ? *p == delim : (*p == ' ' || *p == '\t')))
X	    ++p;
X	if (*p == '\0') {
X	    if (delim)
X		Msg (0, "%s: Missing quote.", fn);
X	    else
X		return argc;
X	}
X	*p++ = '\0';
X    }
X}
X
Xstatic char **SaveArgs (argc, argv) register argc; register char **argv; {
X    register char **ap, **pp;
X
X    if ((pp = ap = (char **)malloc ((argc+1) * sizeof (char **))) == 0)
X	Msg (0, "Out of memory.");
X    while (argc--) {
X	if ((*pp = malloc (strlen (*argv)+1)) == 0)
X	    Msg (0, "Out of memory.");
X	strcpy (*pp, *argv);
X	++pp; ++argv;
X    }
X    *pp = 0;
X    return ap;
X}
X
Xstatic MakeNewEnv () {
X    register char **op, **np = NewEnv;
X    static char buf[MAXSTR];
X    char	envbuf[MAXSTR];
X
X    if (strlen (SockName) > MAXSTR-5)
X	SockName = "?";
X    sprintf (buf, "STY=%s", SockName);
X    *np++ = buf;
X    *np++ = Term;
X    np += 2;
X#ifdef SYSV
X    sprintf(envbuf, "LINES=%d", rows);
X    *np++ = strdup(envbuf);
X    sprintf(envbuf, "COLS=%d", cols);
X    *np++ = strdup(envbuf);
X#endif
X    for (op = environ; *op; ++op) {
X	if (np == NewEnv + MAXARGS - 1)
X	    break;
X	if (!IsSymbol (*op, "TERM") && !IsSymbol (*op, "TERMCAP")
X		&& !IsSymbol (*op, "STY"))
X	    *np++ = *op;
X    }
X    *np = 0;
X}
X
Xstatic IsSymbol (e, s) register char *e, *s; {
X    register char *p;
X    register n;
X
X    for (p = e; *p && *p != '='; ++p) ;
X    if (*p) {
X	*p = '\0';
X	n = strcmp (e, s);
X	*p = '=';
X	return n == 0;
X    }
X    return 0;
X}
X
X/*VARARGS2*/
XMsg (err, fmt, p1, p2, p3, p4, p5, p6) char *fmt; {
X    char buf[1024];
X    register char *p = buf;
X
X    if (Detached)
X	return;
X    sprintf (p, fmt, p1, p2, p3, p4, p5, p6);
X    if (err) {
X	p += strlen (p);
X	if (err > 0 && err < sys_nerr)
X	    sprintf (p, ": %s", sys_errlist[err]);
X	else
X	    sprintf (p, ": Error %d", err);
X    }
X    if (HasWindow) {
X	MakeStatus (buf, curr);
X    } else {
X	printf ("%s\r\n", buf);
X	Kill (AttacherPid, SIGHUP);
X#ifdef SYSV
X	if (chanopen && AttacherPid)
X	    unlink(SockPath);
X#endif
X	exit (1);
X    }
X}
X
Xbclear (p, n) char *p; {
X    bcopy (blank, p, n);
X}
X
Xstatic char *Filename (s) char *s; {
X    register char *p;
X
X    p = s + strlen (s) - 1;
X    while (p >= s && *p != '/') --p;
X    return ++p;
X}
X
Xstatic IsNum (s, base) register char *s; register base; {
X    for (base += '0'; *s; ++s)
X	if (*s < '0' || *s > base)
X	    return 0;
X    return 1;
X}
X
Xstatic char *MakeBellMsg (n) {
X    static char buf[MAXSTR];
X    register char *p = buf, *s = BellString;
X
X    for (s = BellString; *s && p < buf+MAXSTR-1; s++)
X	*p++ = (*s == '%') ? n + '0' : *s;
X    *p = '\0';
X    return buf;
X}
X
Xstatic InitUtmp () {
X    struct passwd *p;
X
X#ifdef SYSV
X    if (setutent() == NULL)
X	return;
X#else
X    if ((utmpf = open (UtmpName, O_WRONLY)) == -1) {
X	if (errno != EACCES)
X	    Msg (errno, UtmpName);
X	return;
X    }
X#endif
X    if ((LoginName = getlogin ()) == 0 || LoginName[0] == '\0') {
X	if ((p = getpwuid (getuid ())) == 0)
X	    return;
X	LoginName = p->pw_name;
X    }
X    utmp = 1;
X}
X
Xstatic SetUtmp (name) char *name; {
X    register char *p;
X    register struct ttyent *tp;
X    register slot = 1;
X    struct utmp u;
X
X    if (!utmp)
X	return 0;
X    if (p = rindex (name, '/'))
X	++p;
X    else p = name;
X#ifndef SYSV
X    setttyent ();
X    while ((tp = getttyent ()) != NULL && strcmp (p, tp->ty_name) != 0)
X	++slot;
X    if (tp == NULL)
X	return 0;
X    strncpy (u.ut_line, p, 8);
X    time (&u.ut_time);
X    strncpy (u.ut_name, LoginName, 8);
X    u.ut_host[0] = '\0';
X    (void) lseek (utmpf, (long)(slot * sizeof (u)), 0);
X    (void) write (utmpf, (char *)&u, sizeof (u));
X    return slot;
X#else
X    {
X       int		len = strlen(p);
X
X	setutent();
X	bzero((char *) &u, sizeof(u));
X	strncpy(u.ut_line, p, 8);
X	u.ut_type = USER_PROCESS;
X        strncpy (u.ut_name, LoginName, 8);
X	u.ut_id[0] = 's'; u.ut_id[3] = '\0';
X	u.ut_id[1] = p[len - 2]; u.ut_id[2] = p[len - 1];
X	slot = *((int *) u.ut_id);
X        time (&u.ut_time);
X	pututline(&u);
X	return slot;
X    }
X#endif
X}
X
Xstatic RemoveUtmp (slot) int slot; {
X   struct utmp u;
X
X    if (slot) {
X#ifdef SYSV
X       struct utmp	*y;
X
X	setutent();
X	bzero((char *) &u, sizeof(u));
X	*((int *) u.ut_id) = slot;
X	u.ut_type = USER_PROCESS;
X	if ((y = getutid(&u)) == 0)
X		return;
X        strncpy (u.ut_name, "screen", 8);
X	strncpy (u.ut_line, y->ut_line, 12);
X	u.ut_type = DEAD_PROCESS;
X        time (&u.ut_time);
X	pututline(&u);
X#else
X	bzero ((char *)&u, sizeof (u));
X	(void) lseek (utmpf, (long)(slot * sizeof (u)), 0);
X	(void) write (utmpf, (char *)&u, sizeof (u));
X#endif
X    }
X}
X
X#ifndef SYSV
X#ifndef GETTTYENT
X
Xstatic setttyent () {
X    struct stat s;
X    register f;
X    register char *p, *ep;
X
X    if (ttnext) {
X	ttnext = tt;
X	return;
X    }
X    if ((f = open (ttys, O_RDONLY)) == -1 || fstat (f, &s) == -1)
X	Msg (errno, ttys);
X    if ((tt = malloc (s.st_size + 1)) == 0)
X	Msg (0, "Out of memory.");
X    if (read (f, tt, s.st_size) != s.st_size)
X	Msg (errno, ttys);
X    close (f);
X    for (p = tt, ep = p + s.st_size; p < ep; ++p)
X	if (*p == '\n') *p = '\0';
X    *p = '\0';
X    ttnext = tt;
X}
X
Xstatic struct ttyent *getttyent () {
X    static struct ttyent t;
X
X    if (*ttnext == '\0')
X	return NULL;
X    t.ty_name = ttnext + 2;
X    ttnext += strlen (ttnext) + 1;
X    return &t;
X}
X
X#endif
X#endif
X
X#ifdef LOADAV
X
Xstatic InitKmem () {
X    if ((kmemf = open (KmemName, O_RDONLY)) == -1)
X	return;
X    nl[0].n_name = AvenrunSym;
X    nlist (UnixName, nl);
X    if (nl[0].n_type == 0 || nl[0].n_value == 0)
X	return;
X    avenrun = 1;
X}
X
Xstatic GetAvenrun () {
X    if (lseek (kmemf, nl[0].n_value & ~0x80000000, 0) == -1)
X	return 0;
X    if (read (kmemf, loadav, sizeof (loadav)) != sizeof (loadav))
X	return 0;
X    return 1;
X}
X
X#endif
X
X#ifndef USEBCOPY
Xbcopy (s1, s2, len) register char *s1, *s2; register len; {
X    if (s1 < s2 && s2 < s1 + len) {
X	s1 += len; s2 += len;
X	while (len-- > 0) {
X	    *--s2 = *--s1;
X	}
X    } else {
X	while (len-- > 0) {
X	    *s2++ = *s1++;
X	}
X    }
X}
X#endif
X
X#ifdef SYSV
Xkillpg(grp, sig)
X   int		grp;
X   int		sig;
X{
X	return( kill (-grp, sig) );
X}
X#endif
________This_Is_The_END________
if test `wc -l < screen.c` -ne 1953; then
	echo 'shar: screen.c was damaged during transit (should have been 1953 bytes)'
fi
fi		; : end of overwriting check
echo 'x - screen.h'
if test -f screen.h; then echo 'shar: not overwriting screen.h'; else
sed 's/^X//' << '________This_Is_The_END________' > screen.h
X/* Copyright (c) 1987,1988 Oliver Laumann, Technical University of Berlin.
X * Not derived from licensed software.
X *
X * Permission is granted to freely use, copy, modify, and redistribute
X * this software, provided that no attempt is made to gain profit from it,
X * the author is not construed to be liable for any results of using the
X * software, alterations are clearly marked as such, and this notice is
X * not modified.
X */
X
Xenum state_t {
X    LIT,         /* Literal input */
X    ESC,         /* Start of escape sequence */
X    STR,         /* Start of control string */
X    TERM,        /* ESC seen in control string */
X    CSI,         /* Reading arguments in "CSI Pn ; Pn ; ... ; XXX" */
X    PRIN,        /* Printer mode */
X    PRINESC,     /* ESC seen in printer mode */
X    PRINCSI,     /* CSI seen in printer mode */
X    PRIN4        /* CSI 4 seen in printer mode */
X};
X
Xenum string_t {
X    NONE,
X    DCS,         /* Device control string */
X    OSC,         /* Operating system command */
X    APC,         /* Application program command */
X    PM,          /* Privacy message */
X};
X
X#define MAXSTR       128
X#define	MAXARGS      64
X
X#define IOSIZE       80
X
Xstruct win {
X    int wpid;
X    int ptyfd;
X    int aflag;
X    char outbuf[IOSIZE];
X    int outlen;
X    char cmd[MAXSTR];
X    char tty[MAXSTR];
X    int args[MAXARGS];
X    char GotArg[MAXARGS];
X    int NumArgs;
X    int slot;
X    char **image;
X    char **attr;
X    char **font;
X    int LocalCharset;
X    int charsets[4];
X    int ss;
X    int active;
X    int x, y;
X    char LocalAttr;
X    int saved;
X    int Saved_x, Saved_y;
X    char SavedLocalAttr;
X    int SavedLocalCharset;
X    int SavedCharsets[4];
X    int top, bot;
X    int wrap;
X    int origin;
X    int insert;
X    int keypad;
X    enum state_t state;
X    enum string_t StringType;
X    char string[MAXSTR];
X    char *stringp;
X    char *tabs;
X    int vbwait;
X    int bell;
X};
X
X#define MAXLINE 1024
X
X#define MSG_CREATE    0
X#define MSG_ERROR     1
X#define MSG_ATTACH    2
X#define MSG_CONT      3
X
Xstruct msg {
X    int type;
X    union {
X	struct {
X	    int aflag;
X	    int nargs;
X	    char line[MAXLINE];
X	    char dir[1024];
X	} create;
X	struct {
X	    int apid;
X	    char tty[1024];
X	} attach;
X	char message[MAXLINE];
X    } m;
X};
________This_Is_The_END________
if test `wc -l < screen.h` -ne 98; then
	echo 'shar: screen.h was damaged during transit (should have been 98 bytes)'
fi
fi		; : end of overwriting check
echo 'x - screen.tic'
if test -f screen.tic; then echo 'shar: not overwriting screen.tic'; else
sed 's/^X//' << '________This_Is_The_END________' > screen.tic
Xscreen,
X	xon,
X	cols#80, lines#24,
X	bel=^G, blink=\E[5m, bold=\E[1m, cbt=\E[Z,
X	clear=\E[2J\E[H, cr=\r, csr=\E[%i%p1%d;%p2%dr,
X	cub=\E[%p1%dD, cub1=\b, cud=\E[%p1%dB, cud1=\n,
X	cuf=\E[%p1%dC, cuf1=\E[C, cup=\E[%i%p1%d;%p2%dH,
X	cuu=\E[%p1%dA, cuu1=\E[A, dl=\E[%p1%dM, dl1=\E[M,
X	el=\E[K, ht=\t, hts=\EH, il=\E[%p1%dL, il1=\E[L,
X	ind=\n, kbs=\b, kcub1=\EOD, kcud1=\EOB, kcuf1=\EOC,
X	kcuu1=\EOA, kf0=\EOy, kf1=\EOP, kf2=\EOQ, kf3=\EOR,
X	kf4=\EOS, kf5=\EOt, kf6=\EOu, kf7=\EOv, kf8=\EOl,
X	kf9=\EOw, rc=\E8, rev=\E[7m, ri=\EM, rmkx=\E>,
X	rmso=\E[23m, rmul=\E[24m, rs2=\Ec, sc=\E7, sgr0=\E[0m,
X	smkx=\E=, smso=\E[3m, smul=\E[4m, tbc=\E[3g,
________This_Is_The_END________
if test `wc -l < screen.tic` -ne 15; then
	echo 'shar: screen.tic was damaged during transit (should have been 15 bytes)'
fi
fi		; : end of overwriting check
exit 0