summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorjnweiger <jnweiger>2005-12-16 15:10:17 +0000
committerjnweiger <jnweiger>2005-12-16 15:10:17 +0000
commit8bc5213d76b3acfbb8d0cddc2c91b7685aab731e (patch)
tree78a8d136bd8cf3ed5e8c2b4c0198d62218e4482c /src
parent30f597b9f18d355253b9484bb2f4a1fa5e2e3fb8 (diff)
downloadscreen-8bc5213d76b3acfbb8d0cddc2c91b7685aab731e.tar.gz
historic version screen-1.1
Diffstat (limited to 'src')
-rw-r--r--src/Makefile59
-rw-r--r--src/README94
-rw-r--r--src/ansi.c1450
-rw-r--r--src/screen.1619
-rw-r--r--src/screen.c1655
-rw-r--r--src/screen.h68
6 files changed, 3945 insertions, 0 deletions
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000..b77fa3c
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,59 @@
+# @(#)Makefile 1.5 88/12/13 22:13:36
+# The following options can be set:
+#
+# -DLOCK -- you want to have the possibility to lock your terminal
+# while you are running screen
+# (Please provide a suitable value for "DefaultLock" in
+# screen.c - reflecting your sytem responsibilities)
+# -DLOADAV -- your system maintains a load average like 4.3 BSD does
+# (an array of three doubles called _avenrun; it is read
+# from /dev/kmem; _avenrun is taken from the namelist of
+# /vmunix). Don't set this on a Sun under SunOS 3.x.
+# -DGETTTYENT -- your system has the new format /etc/ttys (like 4.3 BSD)
+# and the getttyent(3) library functions.
+# -DSUNOS -- If you have SunOS 4.0 or up, you should set this to use
+# the kernel virtual memory library, when you want
+# to have LOADAV ( set LOADAV also).
+# -DSEQUENT -- eliminates bugs in DYNIX and chooses different
+# pty naming for the sequent.
+#
+# You should install as set-uid with owner root, so that it can read/write
+# /etc/utmp, read /dev/kmem, and chown/chmod the allocated pseudo-ttys.
+SHELL=/bin/sh
+CFLAGS= -g
+
+BINDIR=/usr/local/bin
+PART=1
+MANDIR=/usr/local/man/man$(PART)
+
+OPTIONS=-DLOCK -DSEQUENT -DSEQUENT -DLOADAV
+LIBS= -ltermcap
+
+CFILES= screen.c ansi.c
+OFILES= screen.o ansi.o
+
+screen:$(P) $(OFILES) screen.1
+ $(CC) $(CFLAGS) -o screen $(OFILES) $(LIBS)
+
+screen.o:$(P) screen.c screen.h
+ $(CC) $(OPTIONS) $(CFLAGS) -c screen.c
+
+ansi.o:$(P) ansi.c screen.h
+ $(CC) $(CFLAGS) -c ansi.c
+
+install: screen screen.1
+ install -c -s -o root -g wheel -m 4755 screen $(BINDIR)
+ install -c -m 444 screen.1 $(MANDIR)/screen.$(PART)
+
+clean:
+ rm -f *.o screen
+ sccs clean
+
+screen.c:
+ sccs get screen.c
+
+screen.h:
+ sccs get screen.h
+
+ansi.c:
+ sccs get ansi.c
diff --git a/src/README b/src/README
new file mode 100644
index 0000000..a342467
--- /dev/null
+++ b/src/README
@@ -0,0 +1,94 @@
+"screen" is a window manager that allows you to handle several independent
+screens (UNIX ttys) on a single physical terminal; each screen has its own
+set of processes connected to it (typically interactive shells). Each
+virtual terminal created by "screen" emulates a DEC VT100 plus several ANSI
+X3.64 functions (including DEC VT102 features such as line and character
+deletion and insertion).
+
+Since "screen" uses pseudo-ttys, the select system call, and UNIX-domain
+sockets, it will not run under a system that does not include these
+features of 4.2 and 4.3 BSD UNIX.
+
+If you want to get a quick idea how "screen" works but don't want to read
+the entire manual, do the following:
+
+ - call "screen" without arguments
+ - wait for the shell prompt; execute some commands
+ - type ^A ^C (Control-A followed by Control-C)
+ - wait for the shell prompt; do something in the new window
+ - type ^A ^A repeatedly to switch between the two windows
+ - terminate the first shell ("screen" switches to the other window)
+ - terminate the second shell
+
+If you have got "vttest" (the VT100 test program from mod.sources) you
+may want to run it from within "screen" to verify that it correctly
+emulates a VT100 on your terminal (except for 132 column mode and
+double width/height characters, of course).
+
+By the way, "screen" can be used to compensate for certain bugs of "real"
+VT100 terminals. For instance, our 4.2 BSD version of mille(6) garbles
+the display on terminals of the VT100 family, but it works quite fine
+when it is invoked from within "screen". In addition, "screen" enables
+you to use EMACS on terminals that are unable to generate Control-S and
+Control-Q from the keyboard or that require flow control using Control-S
+and Control-Q. This is the reason why I have an alias like
+ alias emacs "screen emacs"
+in my .cshrc file.
+
+
+I have published a beta-test release of screen in the non-moderated
+sources newsgroup four months ago. Since then I have received numerous
+suggestions for enhancements and improvements, many of which I have
+included in this release of "screen". One person reported that screen
+does not work correctly on the Ann Arbor Guru-XL; I have not been able
+to track the problem down without having more detailed information than
+just the termcap entry.
+
+Major changes between this and the beta-test release are:
+
+ - "screen" now creates an entry in /etc/utmp for each virtual
+ terminal
+ - the owner of the tty file for a virtual terminal is set properly
+ (provided that "screen" is set-uid with owner root)
+ - the -c option has been eliminated; you can now start a command
+ in a new window with "screen [cmd [args]]"
+ - a (user-settable) notification can be displayed in the current
+ window's message line when the bell is rung in a background window
+ - a "chdir" command can be placed into ".screenrc" to facilitate
+ creation of windows in specific directories
+ - flow control can be disabled by means of a command line option or
+ a special termcap symbol (necessary for EMACS)
+ - "C-a s" and "C-a q" can be used to send a Control-S or a Control-Q,
+ respectively (for certain terminals)
+ - it is no longer necessary to specify the full pathname when a
+ command is started in a new window (and with the "bind" command)
+ - "C-a ." can be used to write the current termcap entry to a file
+ (useful for "rlogin" which does not propagate TERMCAP)
+ - "C-a C-t" displays status information (e.g. the load average and
+ the virtual terminal's parameters) in the message line
+ - "C-a C-\" closes all windows and terminates screen
+ - "C-a x" calls a lock program intended to lock the terminal.
+ Screen wait for this program to terminate before proceeding.
+ This is available by the compiler option -DLOCK
+
+Before typing "make", you should have a look into the Makefile.
+If your system maintains a 4.3-BSD-style load average, add -DLOADAV to
+the compiler options. In addition, you must set -DGETTTYENT if your
+system has the new format /etc/ttys and the getttyent(3) routines.
+
+"screen" should be granted read and write access to /etc/utmp and, if
+-DLOADAV has been specified, read access to /vmunix and /dev/kmem.
+"screen" should be installed with set-uid and owner root to enable it
+to correctly change the owner of newly allocated virtual terminals.
+Failing to do this (e.g. if you fear a trojan horse) doesn't have any
+major disadvantages, except that w(1) and some other utilities may have
+some problems with the tty files of your virtual terminals.
+
+
+Have fun,
+ Oliver Laumann
+ Technical University of Berlin,
+ Communications and Operating Systems Research Group.
+
+ net@tub.BITNET US: pyramid!tub!net Europe: unido!tub!net
+
diff --git a/src/ansi.c b/src/ansi.c
new file mode 100644
index 0000000..dc278f1
--- /dev/null
+++ b/src/ansi.c
@@ -0,0 +1,1450 @@
+/* Copyright (c) 1987, 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.
+ */
+
+#ifndef NO_SCCS_ID
+static char sccs_id[]="@(#)ansi.c 1.3 88/07/20 01:08:40";
+#endif /* NO_SCCS_ID */
+
+char AnsiVersion[] = "ansi 1.0g 1.3 88/07/20";
+
+#include <stdio.h>
+#include <sys/types.h>
+#include "screen.h"
+
+#define A_SO 0x1 /* Standout mode */
+#define A_US 0x2 /* Underscore mode */
+#define A_BL 0x4 /* Blinking */
+#define A_BD 0x8 /* Bold mode */
+#define A_DI 0x10 /* Dim mode */
+#define A_RV 0x20 /* Reverse mode */
+#define A_MAX 0x20
+
+/* Types of movement used by Goto() */
+enum move_t {
+ M_NONE,
+ M_UP,
+ M_DO,
+ M_LE,
+ M_RI,
+ M_RW,
+ M_CR,
+};
+
+#define MAXARGS 64
+#define EXPENSIVE 1000
+
+extern char *getenv(), *tgetstr(), *tgoto(), *malloc();
+
+int rows, cols;
+int status;
+int flowctl;
+char Term[] = "TERM=screen";
+char Termcap[1024];
+char *blank;
+char PC;
+time_t TimeDisplayed;
+
+static char tbuf[1024], tentry[1024];
+static char *tp = tentry;
+static char *TI, *TE, *BL, *VB, *BC, *CR, *NL, *CL, *IS, *CM;
+static char *US, *UE, *SO, *SE, *CE, *CD, *DO, *SR, *SF, *AL;
+static char *CS, *DL, *DC, *IC, *IM, *EI, *UP, *ND, *KS, *KE;
+static char *MB, *MD, *MH, *MR, *ME;
+static AM;
+static args[MAXARGS];
+static char GotArg[MAXARGS];
+static NumArgs;
+static char GlobalAttr, TmpAttr;
+static char *OldImage, *OldAttr;
+static last_x, last_y;
+static struct win *curr;
+static display = 1;
+static StrCost;
+static UPcost, DOcost, LEcost, NDcost, CRcost, IMcost, EIcost;
+static tcLineLen = 100;
+static char *null;
+static StatLen;
+static insert;
+static keypad;
+
+static char *KeyCaps[] = {
+ "k0", "k1", "k2", "k3", "k4", "k5", "k6", "k7", "k8", "k9",
+ "kb", "kd", "kh", "kl", "ko", "kr", "ku",
+ 0
+};
+
+static char TermcapConst[] = "TERMCAP=\
+SC|screen|VT 100/ANSI X3.64 virtual terminal|\\\n\
+\t:DO=\\E[%dB:LE=\\E[%dD:RI=\\E[%dC:UP=\\E[%dA:bs:bt=\\E[Z:\\\n\
+\t:cd=\\E[J:ce=\\E[K:cl=\\E[2J\\E[H:cm=\\E[%i%d;%dH:ct=\\E[3g:\\\n\
+\t:do=\\E[B:nd=\\E[C:pt:rc=\\E8:rs=\\Ec:sc=\\E7:st=\\EH:up=\\E[A:";
+
+InitTerm () {
+ register char *s;
+
+ if ((s = getenv ("TERM")) == 0)
+ Msg (0, "No TERM in environment.");
+ if (tgetent (tbuf, s) != 1)
+ Msg (0, "Cannot find termcap entry for %s.", s);
+ cols = tgetnum ("co");
+ rows = tgetnum ("li");
+ if (cols <= 0)
+ cols = 80;
+ if (rows <= 0)
+ rows = 24;
+ if (tgetflag ("hc"))
+ Msg (0, "You can't run screen on a hardcopy terminal.");
+ if (tgetflag ("os"))
+ Msg (0, "You can't run screen on a terminal that overstrikes.");
+ if (tgetflag ("ns"))
+ Msg (0, "Terminal must support scrolling.");
+ if (!(CL = tgetstr ("cl", &tp)))
+ Msg (0, "Clear screen capability required.");
+ if (!(CM = tgetstr ("cm", &tp)))
+ Msg (0, "Addressable cursor capability required.");
+ if (s = tgetstr ("ps", &tp))
+ PC = s[0];
+ flowctl = !tgetflag ("NF");
+ AM = tgetflag ("am");
+ if (tgetflag ("LP"))
+ AM = 0;
+ TI = tgetstr ("ti", &tp);
+ TE = tgetstr ("te", &tp);
+ if (!(BL = tgetstr ("bl", &tp)))
+ BL = "\007";
+ VB = tgetstr ("vb", &tp);
+ if (!(BC = tgetstr ("bc", &tp))) {
+ if (tgetflag ("bs"))
+ BC = "\b";
+ else
+ BC = tgetstr ("le", &tp);
+ }
+ if (!(CR = tgetstr ("cr", &tp)))
+ CR = "\r";
+ if (!(NL = tgetstr ("nl", &tp)))
+ NL = "\n";
+ IS = tgetstr ("is", &tp);
+ if (tgetnum ("sg") <= 0) {
+ US = tgetstr ("us", &tp);
+ UE = tgetstr ("ue", &tp);
+ SO = tgetstr ("so", &tp);
+ SE = tgetstr ("se", &tp);
+ MB = tgetstr ("mb", &tp);
+ MD = tgetstr ("md", &tp);
+ MH = tgetstr ("mh", &tp);
+ MR = tgetstr ("mr", &tp);
+ ME = tgetstr ("me", &tp);
+ /*
+ * Does ME also reverse the effect of SO and/or US? This is not
+ * clearly specified by the termcap manual.
+ * Anyway, we should at least look whether ME and SE/UE are equal:
+ */
+ if (SE && UE && ME && (strcmp (SE, UE) == 0 || strcmp (ME, UE) == 0))
+ UE = 0;
+ if (SE && ME && strcmp (SE, ME) == 0)
+ SE = 0;
+ }
+ CE = tgetstr ("ce", &tp);
+ CD = tgetstr ("cd", &tp);
+ if (!(DO = tgetstr ("do", &tp)))
+ DO = NL;
+ UP = tgetstr ("up", &tp);
+ ND = tgetstr ("nd", &tp);
+ SR = tgetstr ("sr", &tp);
+ if (!(SF = tgetstr ("sf", &tp)))
+ SF = NL;
+ AL = tgetstr ("al", &tp);
+ DL = tgetstr ("dl", &tp);
+ CS = tgetstr ("cs", &tp);
+ DC = tgetstr ("dc", &tp);
+ IC = tgetstr ("ic", &tp);
+ IM = tgetstr ("im", &tp);
+ EI = tgetstr ("ei", &tp);
+ if (tgetflag ("in"))
+ IC = IM = 0;
+ if (IC && IC[0] == '\0')
+ IC = 0;
+ if (IM && IM[0] == '\0')
+ IM = 0;
+ if (EI && EI[0] == '\0')
+ EI = 0;
+ KS = tgetstr ("ks", &tp);
+ KE = tgetstr ("ke", &tp);
+ blank = malloc (cols);
+ null = malloc (cols);
+ OldImage = malloc (cols);
+ OldAttr = malloc (cols);
+ if (!(blank && null && OldImage && OldAttr))
+ Msg (0, "Out of memory.");
+ MakeBlankLine (blank, cols);
+ bzero (null, cols);
+ UPcost = CalcCost (UP);
+ DOcost = CalcCost (DO);
+ LEcost = CalcCost (BC);
+ NDcost = CalcCost (ND);
+ CRcost = CalcCost (CR);
+ IMcost = CalcCost (IM);
+ EIcost = CalcCost (EI);
+ PutStr (IS);
+ PutStr (TI);
+ PutStr (CL);
+}
+
+FinitTerm () {
+ PutStr (TE);
+ PutStr (IS);
+}
+
+static AddCap (s) char *s; {
+ register n;
+
+ if (tcLineLen + (n = strlen (s)) > 55) {
+ strcat (Termcap, "\\\n\t:");
+ tcLineLen = 0;
+ }
+ strcat (Termcap, s);
+ tcLineLen += n;
+}
+
+char *MakeTermcap (aflag) {
+ char buf[1024];
+ register char **pp, *p;
+
+ strcpy (Termcap, TermcapConst);
+ sprintf (buf, "li#%d:co#%d:", rows, cols);
+ AddCap (buf);
+ if (VB)
+ AddCap ("vb=\\E[?5h\\E[?5l:");
+ if (US) {
+ AddCap ("us=\\E[4m:");
+ AddCap ("ue=\\E[24m:");
+ }
+ if (SO) {
+ AddCap ("so=\\E[3m:");
+ AddCap ("se=\\E[23m:");
+ }
+ if (MB)
+ AddCap ("mb=\\E[5m:");
+ if (MD)
+ AddCap ("md=\\E[1m:");
+ if (MH)
+ AddCap ("mh=\\E[2m:");
+ if (MR)
+ AddCap ("mr=\\E[7m:");
+ if (MB || MD || MH || MR)
+ AddCap ("me=\\E[0m:");
+ if ((CS && SR) || AL || aflag) {
+ AddCap ("sr=\\EM:");
+ AddCap ("al=\\E[L:");
+ AddCap ("AL=\\E[%dL:");
+ }
+ if (CS || DL || aflag) {
+ AddCap ("dl=\\E[M:");
+ AddCap ("DL=\\E[%dM:");
+ }
+ if (CS)
+ AddCap ("cs=\\E[%i%d;%dr:");
+ if (DC || aflag) {
+ AddCap ("dc=\\E[P:");
+ AddCap ("DC=\\E[%dP:");
+ }
+ if (IC || IM || aflag) {
+ AddCap ("im=\\E[4h:");
+ AddCap ("ei=\\E[4l:");
+ AddCap ("ic=:");
+ AddCap ("IC=\\E[%d@:");
+ }
+ if (KS)
+ AddCap ("ks=\\E=:");
+ if (KE)
+ AddCap ("ke=\\E>:");
+ for (pp = KeyCaps; *pp; ++pp)
+ if (p = tgetstr (*pp, &tp)) {
+ MakeString (*pp, buf, p);
+ AddCap (buf);
+ }
+ return Termcap;
+}
+
+MakeString (cap, buf, s) char *cap, *buf; register char *s; {
+ register char *p = buf;
+ register unsigned c;
+
+ *p++ = *cap++; *p++ = *cap; *p++ = '=';
+ while (c = *s++) {
+ switch (c) {
+ case '\033':
+ *p++ = '\\'; *p++ = 'E'; break;
+ case ':':
+ sprintf (p, "\\072"); p += 4; break;
+ case '^': case '\\':
+ *p++ = '\\'; *p++ = c; break;
+ default:
+ if (c >= 200) {
+ sprintf (p, "\\%03o", c & 0377); p += 4;
+ } else if (c < ' ') {
+ *p++ = '^'; *p++ = c + '@';
+ } else *p++ = c;
+ }
+ }
+ *p++ = ':'; *p = '\0';
+}
+
+Activate (wp) struct win *wp; {
+ RemoveStatus (wp);
+ curr = wp;
+ display = 1;
+ NewRendition (GlobalAttr, curr->LocalAttr);
+ GlobalAttr = curr->LocalAttr;
+ InsertMode (curr->insert);
+ KeypadMode (curr->keypad);
+ if (CS)
+ PutStr (tgoto (CS, curr->bot, curr->top));
+ Redisplay ();
+}
+
+ResetScreen (p) register struct win *p; {
+ register i;
+
+ bzero (p->tabs, cols);
+ for (i = 8; i < cols; i += 8)
+ p->tabs[i] = 1;
+ p->wrap = 1;
+ p->origin = 0;
+ p->insert = 0;
+ p->vbwait = 0;
+ p->keypad = 0;
+ p->top = 0;
+ p->bot = rows - 1;
+ p->saved = 0;
+ p->LocalAttr = 0;
+ p->x = p->y = 0;
+ p->state = LIT;
+ p->StringType = NONE;
+}
+
+WriteString (wp, buf, len) struct win *wp; register char *buf; {
+ register c, intermediate = 0;
+
+ if (!len)
+ return;
+ curr = wp;
+ display = curr->active;
+ if (display)
+ RemoveStatus (wp);
+ do {
+ c = *buf++;
+ if (c == '\0' || c == '\177')
+ continue;
+NextChar:
+ switch (curr->state) {
+ case TERM:
+ switch (c) {
+ case '\\':
+ curr->state = LIT;
+ *(curr->stringp) = '\0';
+ if (curr->StringType == PM && display) {
+ MakeStatus (curr->string, curr);
+ if (status && len > 1) {
+ curr->outlen = len-1;
+ bcopy (buf, curr->outbuf, curr->outlen);
+ return;
+ }
+ }
+ break;
+ default:
+ curr->state = STR;
+ AddChar ('\033');
+ AddChar (c);
+ }
+ break;
+ case STR:
+ switch (c) {
+ case '\0':
+ break;
+ case '\033':
+ curr->state = TERM; break;
+ default:
+ AddChar (c);
+ }
+ break;
+ case ESC:
+ switch (c) {
+ case '[':
+ NumArgs = 0;
+ intermediate = 0;
+ bzero ((char *)args, MAXARGS * sizeof (int));
+ bzero (GotArg, MAXARGS);
+ curr->state = CSI;
+ break;
+ case ']':
+ StartString (OSC); break;
+ case '_':
+ StartString (APC); break;
+ case 'P':
+ StartString (DCS); break;
+ case '^':
+ StartString (PM); break;
+ default:
+ if (Special (c))
+ break;
+ if (c >= ' ' && c <= '/') {
+ intermediate = intermediate ? -1 : c;
+ } else if (c >= '0' && c <= '~') {
+ DoESC (c, intermediate);
+ curr->state = LIT;
+ } else {
+ curr->state = LIT;
+ goto NextChar;
+ }
+ }
+ break;
+ case CSI:
+ switch (c) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ if (NumArgs < MAXARGS) {
+ args[NumArgs] = 10 * args[NumArgs] + c - '0';
+ GotArg[NumArgs] = 1;
+ }
+ break;
+ case ';': case ':':
+ NumArgs++; break;
+ default:
+ if (Special (c))
+ break;
+ if (c >= '@' && c <= '~') {
+ NumArgs++;
+ DoCSI (c, intermediate);
+ curr->state = LIT;
+ } else if ((c >= ' ' && c <= '/') || (c >= '<' && c <= '?')) {
+ intermediate = intermediate ? -1 : c;
+ } else {
+ curr->state = LIT;
+ goto NextChar;
+ }
+ }
+ break;
+ default:
+ if (!Special (c)) {
+ if (c == '\033') {
+ intermediate = 0;
+ curr->state = ESC;
+ } else if (c < ' ') {
+ break;
+ } else if (curr->x < cols-1) {
+ if (curr->insert) {
+ InsertAChar (c);
+ } else {
+ if (display)
+ putchar (c);
+ SetChar (c);
+ }
+ curr->x++;
+ } else if (curr->x == cols-1) {
+ SetChar (c);
+ if (!(AM && curr->y == rows-1)) {
+ if (display)
+ putchar (c);
+ Goto (-1, -1, curr->y, curr->x);
+ }
+ curr->x++;
+ } else {
+ if (curr->wrap) {
+ Return ();
+ LineFeed ();
+ if (curr->insert) {
+ InsertAChar (c);
+ } else {
+ if (display)
+ putchar (c);
+ SetChar (c);
+ }
+ curr->x = 1;
+ } else curr->x = cols;
+ }
+ }
+ }
+ } while (--len);
+ curr->outlen = 0;
+}
+
+static Special (c) register c; {
+ switch (c) {
+ case '\b':
+ BackSpace (); return 1;
+ case '\r':
+ Return (); return 1;
+ case '\n':
+ LineFeed (); return 1;
+ case '\007':
+ PutStr (BL);
+ if (!display)
+ curr->bell = 1;
+ return 1;
+ case '\t':
+ ForwardTab (); return 1;
+ }
+ return 0;
+}
+
+static DoESC (c, intermediate) {
+ switch (intermediate) {
+ case 0:
+ switch (c) {
+ case 'E':
+ Return ();
+ LineFeed ();
+ break;
+ case 'D':
+ LineFeed ();
+ break;
+ case 'M':
+ ReverseLineFeed ();
+ break;
+ case 'H':
+ curr->tabs[curr->x] = 1;
+ break;
+ case '7':
+ SaveCursor ();
+ break;
+ case '8':
+ RestoreCursor ();
+ break;
+ case 'c':
+ ClearScreen ();
+ Goto (curr->y, curr->x, 0, 0);
+ NewRendition (GlobalAttr, 0);
+ SetRendition (0);
+ if (curr->insert)
+ InsertMode (0);
+ if (curr->keypad)
+ KeypadMode (0);
+ if (CS)
+ PutStr (tgoto (CS, rows-1, 0));
+ ResetScreen (curr);
+ break;
+ case '=':
+ KeypadMode (1);
+ curr->keypad = 1;
+ break;
+ case '>':
+ KeypadMode (0);
+ curr->keypad = 0;
+ break;
+ }
+ break;
+ case '#':
+ switch (c) {
+ case '8':
+ FillWithEs ();
+ break;
+ }
+ break;
+ }
+}
+
+static DoCSI (c, intermediate) {
+ register i, a1 = args[0], a2 = args[1];
+
+ if (NumArgs >= MAXARGS)
+ NumArgs = MAXARGS;
+ for (i = 0; i < NumArgs; ++i)
+ if (args[i] == 0)
+ GotArg[i] = 0;
+ switch (intermediate) {
+ case 0:
+ switch (c) {
+ case 'H': case 'f':
+ if (!GotArg[0]) a1 = 1;
+ if (!GotArg[1]) a2 = 1;
+ if (curr->origin)
+ a1 += curr->top;
+ if (a1 < 1)
+ a1 = 1;
+ if (a1 > rows)
+ a1 = rows;
+ if (a2 < 1)
+ a2 = 1;
+ if (a2 > cols)
+ a2 = cols;
+ a1--; a2--;
+ Goto (curr->y, curr->x, a1, a2);
+ curr->y = a1;
+ curr->x = a2;
+ break;
+ case 'J':
+ if (!GotArg[0] || a1 < 0 || a1 > 2)
+ a1 = 0;
+ switch (a1) {
+ case 0:
+ ClearToEOS (); break;
+ case 1:
+ ClearFromBOS (); break;
+ case 2:
+ ClearScreen ();
+ Goto (0, 0, curr->y, curr->x);
+ break;
+ }
+ break;
+ case 'K':
+ if (!GotArg[0] || a1 < 0 || a2 > 2)
+ a1 = 0;
+ switch (a1) {
+ case 0:
+ ClearToEOL (); break;
+ case 1:
+ ClearFromBOL (); break;
+ case 2:
+ ClearLine (); break;
+ }
+ break;
+ case 'A':
+ CursorUp (GotArg[0] ? a1 : 1);
+ break;
+ case 'B':
+ CursorDown (GotArg[0] ? a1 : 1);
+ break;
+ case 'C':
+ CursorRight (GotArg[0] ? a1 : 1);
+ break;
+ case 'D':
+ CursorLeft (GotArg[0] ? a1 : 1);
+ break;
+ case 'm':
+ SelectRendition ();
+ break;
+ case 'g':
+ if (!GotArg[0] || a1 == 0)
+ curr->tabs[curr->x] = 0;
+ else if (a1 == 3)
+ bzero (curr->tabs, cols);
+ break;
+ case 'r':
+ if (!CS)
+ break;
+ if (!GotArg[0]) a1 = 1;
+ if (!GotArg[1]) a2 = rows;
+ if (a1 < 1 || a2 > rows || a1 >= a2)
+ break;
+ curr->top = a1-1;
+ curr->bot = a2-1;
+ PutStr (tgoto (CS, curr->bot, curr->top));
+ if (curr->origin) {
+ Goto (-1, -1, curr->top, 0);
+ curr->y = curr->top;
+ curr->x = 0;
+ } else {
+ Goto (-1, -1, 0, 0);
+ curr->y = curr->x = 0;
+ }
+ break;
+ case 'I':
+ if (!GotArg[0]) a1 = 1;
+ while (a1--)
+ ForwardTab ();
+ break;
+ case 'Z':
+ if (!GotArg[0]) a1 = 1;
+ while (a1--)
+ BackwardTab ();
+ break;
+ case 'L':
+ InsertLine (GotArg[0] ? a1 : 1);
+ break;
+ case 'M':
+ DeleteLine (GotArg[0] ? a1 : 1);
+ break;
+ case 'P':
+ DeleteChar (GotArg[0] ? a1 : 1);
+ break;
+ case '@':
+ InsertChar (GotArg[0] ? a1 : 1);
+ break;
+ case 'h':
+ SetMode (1);
+ break;
+ case 'l':
+ SetMode (0);
+ break;
+ }
+ break;
+ case '?':
+ if (c != 'h' && c != 'l')
+ break;
+ if (!GotArg[0])
+ break;
+ i = (c == 'h');
+ if (a1 == 5) {
+ if (i) {
+ curr->vbwait = 1;
+ } else {
+ if (curr->vbwait)
+ PutStr (VB);
+ curr->vbwait = 0;
+ }
+ } else if (a1 == 6) {
+ curr->origin = i;
+ if (curr->origin) {
+ Goto (curr->y, curr->x, curr->top, 0);
+ curr->y = curr->top;
+ curr->x = 0;
+ } else {
+ Goto (curr->y, curr->x, 0, 0);
+ curr->y = curr->x = 0;
+ }
+ } else if (a1 == 7) {
+ curr->wrap = i;
+ }
+ break;
+ }
+}
+
+static PutChar (c) {
+ putchar (c);
+}
+
+static PutStr (s) char *s; {
+ if (display && s)
+ tputs (s, 1, PutChar);
+}
+
+static SetChar (c) register c; {
+ register struct win *p = curr;
+
+ p->image[p->y][p->x] = c;
+ p->attr[p->y][p->x] = p->LocalAttr;
+}
+
+static StartString (type) enum string_t type; {
+ curr->StringType = type;
+ curr->stringp = curr->string;
+ curr->state = STR;
+}
+
+static AddChar (c) {
+ if (curr->stringp > curr->string+MAXSTR-1)
+ curr->state = LIT;
+ else
+ *(curr->stringp)++ = c;
+}
+
+/* Insert mode is a toggle on some terminals, so we need this hack:
+ */
+InsertMode (on) {
+ if (on) {
+ if (!insert)
+ PutStr (IM);
+ } else if (insert)
+ PutStr (EI);
+ insert = on;
+}
+
+/* ...and maybe keypad application mode is a toggle, too:
+ */
+KeypadMode (on) {
+ if (on) {
+ if (!keypad)
+ PutStr (KS);
+ } else if (keypad)
+ PutStr (KE);
+ keypad = on;
+}
+
+static SaveCursor () {
+ curr->saved = 1;
+ curr->Saved_x = curr->x;
+ curr->Saved_y = curr->y;
+ curr->SavedLocalAttr = curr->LocalAttr;
+}
+
+static RestoreCursor () {
+ if (curr->saved) {
+ curr->LocalAttr = curr->SavedLocalAttr;
+ NewRendition (GlobalAttr, curr->LocalAttr);
+ GlobalAttr = curr->LocalAttr;
+ Goto (curr->y, curr->x, curr->Saved_y, curr->Saved_x);
+ curr->x = curr->Saved_x;
+ curr->y = curr->Saved_y;
+ }
+}
+
+/*ARGSUSED*/
+CountChars (c) {
+ StrCost++;
+}
+
+CalcCost (s) register char *s; {
+ if (s) {
+ StrCost = 0;
+ tputs (s, 1, CountChars);
+ return StrCost;
+ } else return EXPENSIVE;
+}
+
+static Goto (y1, x1, y2, x2) {
+ register dy, dx;
+ register cost = 0;
+ register char *s;
+ int CMcost, n, m;
+ enum move_t xm = M_NONE, ym = M_NONE;
+
+ if (!display)
+ return;
+ if (x1 == cols || x2 == cols) {
+ if (x2 == cols) --x2;
+ goto DoCM;
+ }
+ dx = x2 - x1;
+ dy = y2 - y1;
+ if (dy == 0 && dx == 0)
+ return;
+ if (y1 == -1 || x1 == -1) {
+DoCM:
+ PutStr (tgoto (CM, x2, y2));
+ return;
+ }
+ CMcost = CalcCost (tgoto (CM, x2, y2));
+ if (dx > 0) {
+ if ((n = RewriteCost (y1, x1, x2)) < (m = dx * NDcost)) {
+ cost = n;
+ xm = M_RW;
+ } else {
+ cost = m;
+ xm = M_RI;
+ }
+ } else if (dx < 0) {
+ cost = -dx * LEcost;
+ xm = M_LE;
+ }
+ if (dx && (n = RewriteCost (y1, 0, x2) + CRcost) < cost) {
+ cost = n;
+ xm = M_CR;
+ }
+ if (cost >= CMcost)
+ goto DoCM;
+ if (dy > 0) {
+ cost += dy * DOcost;
+ ym = M_DO;
+ } else if (dy < 0) {
+ cost += -dy * UPcost;
+ ym = M_UP;
+ }
+ if (cost >= CMcost)
+ goto DoCM;
+ if (xm != M_NONE) {
+ if (xm == M_LE || xm == M_RI) {
+ if (xm == M_LE) {
+ s = BC; dx = -dx;
+ } else s = ND;
+ while (dx-- > 0)
+ PutStr (s);
+ } else {
+ if (xm == M_CR) {
+ PutStr (CR);
+ x1 = 0;
+ }
+ if (x1 < x2) {
+ if (curr->insert)
+ InsertMode (0);
+ for (s = curr->image[y1]+x1; x1 < x2; x1++, s++)
+ putchar (*s);
+ if (curr->insert)
+ InsertMode (1);
+ }
+ }
+ }
+ if (ym != M_NONE) {
+ if (ym == M_UP) {
+ s = UP; dy = -dy;
+ } else s = DO;
+ while (dy-- > 0)
+ PutStr (s);
+ }
+}
+
+static RewriteCost (y, x1, x2) {
+ register cost, dx;
+ register char *p = curr->attr[y]+x1;
+
+ if (AM && y == rows-1 && x2 == cols-1)
+ return EXPENSIVE;
+ cost = dx = x2 - x1;
+ if (dx == 0)
+ return 0;
+ if (curr->insert)
+ cost += EIcost + IMcost;
+ do {
+ if (*p++ != GlobalAttr)
+ return EXPENSIVE;
+ } while (--dx);
+ return cost;
+}
+
+static BackSpace () {
+ if (curr->x > 0) {
+ if (curr->x < cols) {
+ if (BC)
+ PutStr (BC);
+ else
+ Goto (curr->y, curr->x, curr->y, curr->x-1);
+ }
+ curr->x--;
+ }
+}
+
+static Return () {
+ if (curr->x > 0) {
+ curr->x = 0;
+ PutStr (CR);
+ }
+}
+
+static LineFeed () {
+ if (curr->y == curr->bot) {
+ ScrollUpMap (curr->image);
+ ScrollUpMap (curr->attr);
+ } else if (curr->y < rows-1) {
+ curr->y++;
+ }
+ PutStr (NL);
+}
+
+static ReverseLineFeed () {
+ if (curr->y == curr->top) {
+ ScrollDownMap (curr->image);
+ ScrollDownMap (curr->attr);
+ if (SR) {
+ PutStr (SR);
+ } else if (AL) {
+ Goto (curr->top, curr->x, curr->top, 0);
+ PutStr (AL);
+ Goto (curr->top, 0, curr->top, curr->x);
+ } else Redisplay ();
+ } else if (curr->y > 0) {
+ CursorUp (1);
+ }
+}
+
+static InsertAChar (c) {
+ register y = curr->y, x = curr->x;
+
+ if (x == cols)
+ x--;
+ bcopy (curr->image[y], OldImage, cols);
+ bcopy (curr->attr[y], OldAttr, cols);
+ bcopy (curr->image[y]+x, curr->image[y]+x+1, cols-x-1);
+ bcopy (curr->attr[y]+x, curr->attr[y]+x+1, cols-x-1);
+ SetChar (c);
+ if (!display)
+ return;
+ if (IC || IM) {
+ if (!curr->insert)
+ InsertMode (1);
+ PutStr (IC);
+ putchar (c);
+ if (!curr->insert)
+ InsertMode (0);
+ } else {
+ RedisplayLine (OldImage, OldAttr, y, x, cols-1);
+ ++x;
+ Goto (y, last_x, y, x);
+ }
+}
+
+static InsertChar (n) {
+ register i, y = curr->y, x = curr->x;
+
+ if (x == cols)
+ return;
+ bcopy (curr->image[y], OldImage, cols);
+ bcopy (curr->attr[y], OldAttr, cols);
+ if (n > cols-x)
+ n = cols-x;
+ bcopy (curr->image[y]+x, curr->image[y]+x+n, cols-x-n);
+ bcopy (curr->attr[y]+x, curr->attr[y]+x+n, cols-x-n);
+ ClearInLine (0, y, x, x+n-1);
+ if (!display)
+ return;
+ if (IC || IM) {
+ if (!curr->insert)
+ InsertMode (1);
+ for (i = n; i; i--) {
+ PutStr (IC);
+ putchar (' ');
+ }
+ if (!curr->insert)
+ InsertMode (0);
+ Goto (y, x+n, y, x);
+ } else {
+ RedisplayLine (OldImage, OldAttr, y, x, cols-1);
+ Goto (y, last_x, y, x);
+ }
+}
+
+static DeleteChar (n) {
+ register i, y = curr->y, x = curr->x;
+
+ if (x == cols)
+ return;
+ bcopy (curr->image[y], OldImage, cols);
+ bcopy (curr->attr[y], OldAttr, cols);
+ if (n > cols-x)
+ n = cols-x;
+ bcopy (curr->image[y]+x+n, curr->image[y]+x, cols-x-n);
+ bcopy (curr->attr[y]+x+n, curr->attr[y]+x, cols-x-n);
+ ClearInLine (0, y, cols-n, cols-1);
+ if (!display)
+ return;
+ if (DC) {
+ for (i = n; i; i--)
+ PutStr (DC);
+ } else {
+ RedisplayLine (OldImage, OldAttr, y, x, cols-1);
+ Goto (y, last_x, y, x);
+ }
+}
+
+static DeleteLine (n) {
+ register i, old = curr->top;
+
+ if (n > curr->bot-curr->y+1)
+ n = curr->bot-curr->y+1;
+ curr->top = curr->y;
+ for (i = n; i; i--) {
+ ScrollUpMap (curr->image);
+ ScrollUpMap (curr->attr);
+ }
+ if (DL) {
+ Goto (curr->y, curr->x, curr->y, 0);
+ for (i = n; i; i--)
+ PutStr (DL);
+ Goto (curr->y, 0, curr->y, curr->x);
+ } else if (CS) {
+ PutStr (tgoto (CS, curr->bot, curr->top));
+ Goto (-1, -1, curr->bot, 0);
+ for (i = n; i; i--)
+ PutStr (SF);
+ PutStr (tgoto (CS, curr->bot, old));
+ Goto (-1, -1, curr->y, curr->x);
+ } else Redisplay ();
+ curr->top = old;
+}
+
+static InsertLine (n) {
+ register i, old = curr->top;
+
+ if (n > curr->bot-curr->y+1)
+ n = curr->bot-curr->y+1;
+ curr->top = curr->y;
+ for (i = n; i; i--) {
+ ScrollDownMap (curr->image);
+ ScrollDownMap (curr->attr);
+ }
+ if (AL) {
+ Goto (curr->y, curr->x, curr->y, 0);
+ for (i = n; i; i--)
+ PutStr (AL);
+ Goto (curr->y, 0, curr->y, curr->x);
+ } else if (CS && SR) {
+ PutStr (tgoto (CS, curr->bot, curr->top));
+ Goto (-1, -1, curr->y, 0);
+ for (i = n; i; i--)
+ PutStr (SR);
+ PutStr (tgoto (CS, curr->bot, old));
+ Goto (-1, -1, curr->y, curr->x);
+ } else Redisplay ();
+ curr->top = old;
+}
+
+static ScrollUpMap (pp) char **pp; {
+ register char *tmp = pp[curr->top];
+
+ bcopy (pp+curr->top+1, pp+curr->top,
+ (curr->bot-curr->top) * sizeof (char *));
+ if (pp == curr->image)
+ bclear (tmp, cols);
+ else
+ bzero (tmp, cols);
+ pp[curr->bot] = tmp;
+}
+
+static ScrollDownMap (pp) char **pp; {
+ register char *tmp = pp[curr->bot];
+
+ bcopy (pp+curr->top, pp+curr->top+1,
+ (curr->bot-curr->top) * sizeof (char *));
+ if (pp == curr->image)
+ bclear (tmp, cols);
+ else
+ bzero (tmp, cols);
+ pp[curr->top] = tmp;
+}
+
+static ForwardTab () {
+ register x = curr->x;
+
+ if (curr->tabs[x] && x < cols-1)
+ ++x;
+ while (x < cols-1 && !curr->tabs[x])
+ x++;
+ Goto (curr->y, curr->x, curr->y, x);
+ curr->x = x;
+}
+
+static BackwardTab () {
+ register x = curr->x;
+
+ if (curr->tabs[x] && x > 0)
+ x--;
+ while (x > 0 && !curr->tabs[x])
+ x--;
+ Goto (curr->y, curr->x, curr->y, x);
+ curr->x = x;
+}
+
+static ClearScreen () {
+ register i;
+
+ PutStr (CL);
+ for (i = 0; i < rows; ++i) {
+ bclear (curr->image[i], cols);
+ bzero (curr->attr[i], cols);
+ }
+}
+
+static ClearFromBOS () {
+ register n, y = curr->y, x = curr->x;
+
+ for (n = 0; n < y; ++n)
+ ClearInLine (1, n, 0, cols-1);
+ ClearInLine (1, y, 0, x);
+ Goto (curr->y, curr->x, y, x);
+ curr->y = y; curr->x = x;
+}
+
+static ClearToEOS () {
+ register n, y = curr->y, x = curr->x;
+
+ if (CD)
+ PutStr (CD);
+ ClearInLine (!CD, y, x, cols-1);
+ for (n = y+1; n < rows; n++)
+ ClearInLine (!CD, n, 0, cols-1);
+ Goto (curr->y, curr->x, y, x);
+ curr->y = y; curr->x = x;
+}
+
+static ClearLine () {
+ register y = curr->y, x = curr->x;
+
+ ClearInLine (1, y, 0, cols-1);
+ Goto (curr->y, curr->x, y, x);
+ curr->y = y; curr->x = x;
+}
+
+static ClearToEOL () {
+ register y = curr->y, x = curr->x;
+
+ ClearInLine (1, y, x, cols-1);
+ Goto (curr->y, curr->x, y, x);
+ curr->y = y; curr->x = x;
+}
+
+static ClearFromBOL () {
+ register y = curr->y, x = curr->x;
+
+ ClearInLine (1, y, 0, x);
+ Goto (curr->y, curr->x, y, x);
+ curr->y = y; curr->x = x;
+}
+
+static ClearInLine (displ, y, x1, x2) {
+ register i, n;
+
+ if (x1 == cols) x1--;
+ if (x2 == cols) x2--;
+ if (n = x2 - x1 + 1) {
+ bclear (curr->image[y]+x1, n);
+ bzero (curr->attr[y]+x1, n);
+ if (displ && display) {
+ if (x2 == cols-1 && CE) {
+ Goto (curr->y, curr->x, y, x1);
+ curr->y = y; curr->x = x1;
+ PutStr (CE);
+ return;
+ }
+ if (y == rows-1 && AM)
+ --n;
+ if (n == 0)
+ return;
+ SaveAttr ();
+ Goto (curr->y, curr->x, y, x1);
+ for (i = n; i > 0; i--)
+ putchar (' ');
+ curr->y = y; curr->x = x1 + n;
+ RestoreAttr ();
+ }
+ }
+}
+
+static CursorRight (n) register n; {
+ register x = curr->x;
+
+ if (x == cols)
+ return;
+ if ((curr->x += n) >= cols)
+ curr->x = cols-1;
+ Goto (curr->y, x, curr->y, curr->x);
+}
+
+static CursorUp (n) register n; {
+ register y = curr->y;
+
+ if ((curr->y -= n) < curr->top)
+ curr->y = curr->top;
+ Goto (y, curr->x, curr->y, curr->x);
+}
+
+static CursorDown (n) register n; {
+ register y = curr->y;
+
+ if ((curr->y += n) > curr->bot)
+ curr->y = curr->bot;
+ Goto (y, curr->x, curr->y, curr->x);
+}
+
+static CursorLeft (n) register n; {
+ register x = curr->x;
+
+ if ((curr->x -= n) < 0)
+ curr->x = 0;
+ Goto (curr->y, x, curr->y, curr->x);
+}
+
+static SetMode (on) {
+ register i;
+
+ for (i = 0; i < NumArgs; ++i) {
+ switch (args[i]) {
+ case 4:
+ curr->insert = on;
+ InsertMode (on);
+ break;
+ }
+ }
+}
+
+static SelectRendition () {
+ register i, old = GlobalAttr;
+
+ if (NumArgs == 0)
+ SetRendition (0);
+ else for (i = 0; i < NumArgs; ++i)
+ SetRendition (args[i]);
+ NewRendition (old, GlobalAttr);
+}
+
+static SetRendition (n) register n; {
+ switch (n) {
+ case 0:
+ GlobalAttr = 0; break;
+ case 1:
+ GlobalAttr |= A_BD; break;
+ case 2:
+ GlobalAttr |= A_DI; break;
+ case 3:
+ GlobalAttr |= A_SO; break;
+ case 4:
+ GlobalAttr |= A_US; break;
+ case 5:
+ GlobalAttr |= A_BL; break;
+ case 7:
+ GlobalAttr |= A_RV; break;
+ case 22:
+ GlobalAttr &= ~(A_BD|A_SO|A_DI); break;
+ case 23:
+ GlobalAttr &= ~A_SO; break;
+ case 24:
+ GlobalAttr &= ~A_US; break;
+ case 25:
+ GlobalAttr &= ~A_BL; break;
+ case 27:
+ GlobalAttr &= ~A_RV; break;
+ }
+ curr->LocalAttr = GlobalAttr;
+}
+
+static NewRendition (old, new) register old, new; {
+ register i;
+
+ if (old == new)
+ return;
+ for (i = 1; i <= A_MAX; i <<= 1) {
+ if ((old & i) && !(new & i)) {
+ PutStr (UE);
+ PutStr (SE);
+ PutStr (ME);
+ if (new & A_US) PutStr (US);
+ if (new & A_SO) PutStr (SO);
+ if (new & A_BL) PutStr (MB);
+ if (new & A_BD) PutStr (MD);
+ if (new & A_DI) PutStr (MH);
+ if (new & A_RV) PutStr (MR);
+ return;
+ }
+ }
+ if ((new & A_US) && !(old & A_US))
+ PutStr (US);
+ if ((new & A_SO) && !(old & A_SO))
+ PutStr (SO);
+ if ((new & A_BL) && !(old & A_BL))
+ PutStr (MB);
+ if ((new & A_BD) && !(old & A_BD))
+ PutStr (MD);
+ if ((new & A_DI) && !(old & A_DI))
+ PutStr (MH);
+ if ((new & A_RV) && !(old & A_RV))
+ PutStr (MR);
+}
+
+static SaveAttr () {
+ NewRendition (GlobalAttr, 0);
+ if (curr->insert)
+ InsertMode (0);
+}
+
+static RestoreAttr () {
+ NewRendition (0, GlobalAttr);
+ if (curr->insert)
+ InsertMode (1);
+}
+
+static FillWithEs () {
+ register i;
+ register char *p, *ep;
+
+ curr->y = curr->x = 0;
+ NewRendition (GlobalAttr, 0);
+ SetRendition (0);
+ for (i = 0; i < rows; ++i) {
+ bzero (curr->attr[i], cols);
+ p = curr->image[i];
+ ep = p + cols;
+ for ( ; p < ep; ++p)
+ *p = 'E';
+ }
+ Redisplay ();
+}
+
+static Redisplay () {
+ register i;
+
+ PutStr (CL);
+ TmpAttr = GlobalAttr;
+ last_x = last_y = 0;
+ for (i = 0; i < rows; ++i)
+ DisplayLine (blank, null, curr->image[i], curr->attr[i], i, 0, cols-1);
+ NewRendition (TmpAttr, GlobalAttr);
+ Goto (last_y, last_x, curr->y, curr->x);
+}
+
+static DisplayLine (os, oa, s, as, y, from, to)
+ register char *os, *oa, *s, *as; {
+ register i, x, a;
+
+ if (to == cols)
+ --to;
+ if (AM && y == rows-1 && to == cols-1)
+ --to;
+ a = TmpAttr;
+ for (x = i = from; i <= to; ++i, ++x) {
+ if (s[i] == os[i] && as[i] == oa[i] && as[i] == a)
+ continue;
+ Goto (last_y, last_x, y, x);
+ last_y = y;
+ last_x = x;
+ if ((a = as[i]) != TmpAttr) {
+ NewRendition (TmpAttr, a);
+ TmpAttr = a;
+ }
+ putchar (s[i]);
+ last_x++;
+ }
+}
+
+static RedisplayLine (os, oa, y, from, to) char *os, *oa; {
+ if (curr->insert)
+ InsertMode (0);
+ NewRendition (GlobalAttr, 0);
+ TmpAttr = 0;
+ last_y = y;
+ last_x = from;
+ DisplayLine (os, oa, curr->image[y], curr->attr[y], y, from, to);
+ NewRendition (TmpAttr, GlobalAttr);
+ if (curr->insert)
+ InsertMode (1);
+}
+
+static MakeBlankLine (p, n) register char *p; register n; {
+ do *p++ = ' ';
+ while (--n);
+}
+
+MakeStatus (msg, wp) char *msg; struct win *wp; {
+ struct win *ocurr = curr;
+ int odisplay = display;
+ register char *s, *t;
+ register max = AM ? cols-1 : cols;
+
+ for (s = t = msg; *s && t - msg < max; ++s)
+ if (*s >= ' ' && *s <= '~')
+ *t++ = *s;
+ *t = '\0';
+ curr = wp;
+ display = 1;
+ if (status) {
+ if (time ((time_t *)0) - TimeDisplayed < 2)
+ sleep (1);
+ RemoveStatus (wp);
+ }
+ if (t > msg) {
+ status = 1;
+ StatLen = t - msg;
+ Goto (curr->y, curr->x, rows-1, 0);
+ NewRendition (GlobalAttr, A_SO);
+ if (curr->insert)
+ InsertMode (0);
+ printf ("%s", msg);
+ if (curr->insert)
+ InsertMode (1);
+ NewRendition (A_SO, GlobalAttr);
+ fflush (stdout);
+ time (&TimeDisplayed);
+ }
+ curr = ocurr;
+ display = odisplay;
+}
+
+RemoveStatus (p) struct win *p; {
+ struct win *ocurr = curr;
+ int odisplay = display;
+
+ if (!status)
+ return;
+ status = 0;
+ curr = p;
+ display = 1;
+ Goto (-1, -1, rows-1, 0);
+ RedisplayLine (null, null, rows-1, 0, StatLen);
+ Goto (rows-1, last_x, curr->y, curr->x);
+ curr = ocurr;
+ display = odisplay;
+}
diff --git a/src/screen.1 b/src/screen.1
new file mode 100644
index 0000000..a7171bf
--- /dev/null
+++ b/src/screen.1
@@ -0,0 +1,619 @@
+.\" @(#)screen.1 1.3 88/07/20 01:04:04
+.if n .ds Q \&"
+.if n .ds U \&"
+.if t .ds Q ``
+.if t .ds U ''
+.TH SCREEN 1 "2 March 1987"
+.UC 4
+.SH NAME
+screen \- screen manager with VT100/ANSI terminal emulation
+.SH SYNOPSIS
+.B screen
+[
+.B \-a
+] [
+.B \-f
+] [
+.B \-n
+] [
+.B \-e\fIxy\fP
+] [
+.B \fIcmd args\fP ]
+.ta .5i 1.8i
+.SH DESCRIPTION
+.I screen
+is a full-screen window manager that
+multiplexes a physical terminal between several processes (typically
+interactive shells). Each virtual terminal provides the functions
+of the DEC VT100 terminal and, in addition, several control functions
+from the ANSI X3.64 (ISO 6429) standard (e.g. insert/delete line).
+.PP
+When
+.I screen
+is called, it creates a single window with a shell; the pathname of the
+shell is taken from the environment symbol $SHELL; if this is not
+defined, \*Q/bin/sh\*U is used.
+New windows can be created at any time by calling
+.I screen
+from within a previously created window.
+The program to be started in a newly created
+window and optional arguments to the program can be supplied when
+.I screen
+is invoked.
+For instance,
+.IP
+screen csh
+.PP
+will create a window with a C-Shell and switch to that window.
+When the process associated with the currently displayed window
+terminates (e.g. ^D has been typed to a shell),
+.I screen
+switches to the previously displayed window;
+when no more windows are left,
+.I screen
+exits.
+.PP
+When \*Q/etc/utmp\*U is writable by
+.IR screen ,
+an appropriate record is written to this file for each window and
+removed when the window is terminated.
+.SH "COMMAND KEYS"
+The standard way to create a new window is to type \*QC-a c\*U (the notation
+\*QC-x\*U will be used as a shorthand for Control-x in this manual; x is
+an arbitrary letter).
+\*QC-a c\*U creates a new window running a shell and switches to that
+window immediately, regardless of the state of the process running
+in the current window.
+.I Screen
+recognizes several such commands; each command consists of
+\*QC-a\*U followed by a one-letter function.
+For convenience, the letter after a \*QC-a\*U can be entered both with or
+without the control key pressed (with the exception of
+\*QC-a C-a\*U and \*QC-a a\*U; see below), thus, \*QC-a c\*U as well as
+\*QC-a C-c\*U can be used to create a window.
+.PP
+The following commands are recognized by
+.IR screen :
+.IP "\fBC-a c\fP or \fBC-a C-c\fP"
+Create a new window with a shell and switch to that window.
+.IP "\fBC-a x\fP or \fBC-a C-x\fP"
+Lock the terminal and don not continue execution until the terminal is released
+.IP "\fBC-a k\fP or \fBC-a C-k\fP"
+Kill the current window and switch to the previously displayed window.
+.IP "\fBC-a C-\e\fP"
+Kill all windows and terminate
+.IR screen .
+.IP "\fBC-a C-a\fP\0\0\0\0\0"
+Switch to the previously displayed window.
+.IP "\fBC-a 0\fP to \fBC-a 9\fP"
+Switch to the window with the number 0 (1, 2, .., 9, respectively).
+When a new window is established, the first available number from the
+range 0..9 is assigned to this window.
+Thus, the first window can be activated by \*QC-a 0\*U; at most
+10 windows can be present at any time.
+.IP "\fBC-a space\fP or \fBC-a C-space\fP or \fBC-a n\fP or \fBC-a C-n\fP"
+Switch to the next window. This function can be used repeatedly to
+cycle through the list of windows.
+(Control-space is not supported by all terminals.)
+.IP "\fBC-a p\fP or \fBC-a C-p\fP or \fBC-a -\fP"
+Switch to the previous window (the opposite of \fBC-a space\fP).
+.IP "\fBC-a l\fP or \fBC-a C-l\fP"
+Redisplay the current window.
+.IP "\fBC-a z\fP or \fBC-a C-z\fP"
+Suspend
+.IR screen .
+.IP "\fBC-a h\fP or \fBC-a C-h\fP"
+Write a hardcopy of the current window to the file \*Qhardcopy.\fIn\fP\*U
+in the window's current directory,
+where \fIn\fP is the number of the current window.
+.IP "\fBC-a .\fP (Control-a dot)"
+Write the termcap entry for the virtual terminal of the currently active
+window to the file \*Q.termcap\*U in the directory \*Q$HOME/.screen\*U.
+This termcap entry is identical to the value of the environment symbol
+TERMCAP that is set up by
+.I screen
+for each window.
+.IP "\fBC-a w\fP or \fBC-a C-w\fP"
+Display a list of all windows.
+For each window, the number of the window and the process that has been
+started in the window is displayed; the current window is marked with a
+`*'.
+.IP "\fBC-a t\fP or \fBC-a C-t\fP"
+Print in the message line the time of day, the host name, the load averages
+over 1, 5, and 15 minutes (if this is available on your system),
+the cursor position of the current window in the form \*Q(colum,row)\*U
+starting with \*U(0,0)\*U, and an indication if flow control
+and (for the current window)
+insert mode, origin mode, wrap mode, and keypad application
+mode are enabled or not (indicated by a '+' or '-').
+.IP "\fBC-a v\fP or \fBC-a C-v\fP"
+Display the version.
+.IP "\fBC-a a\fP\0\0\0\0\0"
+Send the character \*QC-a\*U to the processes running in the window.
+.IP "\fBC-a s\fP or \fBC-a C-s\fP"
+Send a Control-s to the program running in the window.
+.IP "\fBC-a q\fP or \fBC-a C-q\fP"
+Send a Control-q to the program running in the window.
+.IP
+.PP
+The
+.B -e
+option can be used to specify a different command character and
+a character which, when typed immediately after the command character,
+generates a literal command character.
+The defaults for these two characters are \*QC-a\*U and `a'.
+(Note that the function to switch to the previous window is actually the
+command character typed twice; for instance, when
+.I screen
+is called with the option \*Q\fB-e]x\fP\*U (or \*Q\fB-e ]x\fP\*U),
+this function becomes \*Q]]\*U).
+.SH CUSTOMIZATION
+When
+.I screen
+is invoked, it executes initialization commands from the file
+\*Q.screenrc\*U in the user's home directory.
+Commands in \*Q.screenrc\*U are mainly used to automatically
+establish a number of windows each time
+.I screen
+is called, and to bind functions to specific keys.
+Each line in \*Q.screenrc\*U contains one initialization command; lines
+starting with `#' are ignored.
+Commands can have arguments; arguments are separated by tabs and spaces
+and can be surrounded by single quotes or double quotes.
+.PP
+The following initialization commands are recognized by
+.IR screen :
+.PP
+.ne 3
+.B "escape \fIxy\fP"
+.PP
+Set the command character to \fIx\fP and the character generating a literal
+command character to \fIy\fP (see the -e option above).
+.PP
+.ne 3
+.B "bell \fImessage\fP"
+.PP
+When a bell character is sent to a background window,
+.I screen
+displays a notification in the message line (see below).
+The notification message can be re-defined by means of the \*Qbell\*U
+command; each occurrence of `%' in \fImessage\fP is replaced by
+the number of the window to which a bell has been sent.
+The default message is
+.PP
+ Bell in window %
+.PP
+An empty message can be supplied to the \*Qbell\*U command to suppress
+output of a message line (bell "").
+.PP
+.ne 3
+.B "screen [\fIn\fP] [\fIcmds args\fP]"
+.PP
+Establish a window.
+If an optional number \fIn\fP in the range 0..9 is given, the window
+number \fIn\fP is assigned to the newly created window (or, if this
+number is already in use, the next higher number).
+Note that \fIn\fP has a value of zero for the standard shell window
+created after \*Q.screenrc\*U has been read.
+If a command is specified after \*Qscreen\*U, this command (with the given
+arguments) is started in the window; if no command is given, a shell
+is created in the window.
+Thus, if your \*Q.screenrc\*U contains the lines
+.PP
+.nf
+ # example for .screenrc:
+ screen 1
+ screen 2 telnet foobar
+.fi
+.PP
+.I screen
+creates a shell window (window #1), a window with a TELNET connection
+to the machine foobar (window #2), and, finally, a second shell window
+(the default window) which gets a window number of zero.
+When the initialization is completed,
+.I screen
+always switches to the default window, so window #0 is displayed
+when the above \*Q.screenrc\*U is used.
+.PP
+.ne 3
+.B "chdir [\fIdirectory\fP]"
+.PP
+Change the \fIcurrent directory\fP of
+.I screen
+to the specified directory or, if called without an argument,
+to the home directory (the value of the environment symbol $HOME).
+All windows that are created by means of the \*Qscreen\*U command
+from within \*Q.screenrc\*U or by means of \*QC-a c'' are running
+in the \fIcurrent directory\fP; the \fIcurrent directory\fP is
+initially the directory from which the shell command
+.I screen
+has been invoked.
+Hardcopy files are always written to the directory in which the current
+window has been created (that is, \fInot\fP in the current directory
+of the shell running in the window).
+.PP
+.ne 3
+.B "bind \fIkey\fP [\fIfunction\fP | \fIcmd args\fP]"
+.PP
+Bind a function to a key.
+By default, each function provided by
+.I screen
+is bound to one or more keys as indicated by the above table, e.g. the
+function to create a new window is bound to \*QC-c\*U and \*Qc\*U.
+The \*Qbind\*U command can be used to redefine the key bindings and to
+define new bindings.
+The \fIkey\fP
+argument is either a single character, a sequence of the form
+\*Q^x\*U meaning \*QC-x\*U, or an octal number specifying the
+ASCII code of the character.
+If no further argument is given, any previously established binding
+for this key is removed.
+The \fIfunction\fP argument can be one of the following keywords:
+.PP
+.nf
+ shell Create new window with a shell
+ lock Lock the terminal
+ kill Kill the current window
+ quit Kill all windows and terminate
+ other Switch to previously displayed window
+ next Switch to the next window
+ prev Switch to the previous window
+ redisplay Redisplay current window
+ hardcopy Make hardcopy of current window
+ termcap Write termcap entry to $HOME/.screen/.termcap
+ suspend Suspend \fIscreen\fP
+ windows Display list of window
+ info Print useful information in the message line
+ xon Send Control-q
+ xoff Send Control-s
+ version Display the version
+ select0 Switch to window #0
+ \0\0...
+ select9 Switch to window #9
+.fi
+.PP
+In addition, a key can be bound such that a window is created running
+a different command than the shell when that key is pressed.
+In this case, the command optionally followed by
+arguments must be given instead of one of the above-listed keywords.
+For example, the commands
+.PP
+.nf
+ bind ' ' windows
+ bind ^f telnet foobar
+ bind 033 su
+.fi
+.PP
+would bind the space key to the function that displays a list
+of windows (that is, the function usually invoked by \*QC-a C-w\*U
+or \*QC-a w\*U would also be available as \*QC-a space\*U),
+bind \*QC-f\*U to the function \*Qcreate a window with a TELNET
+connection to foobar\*U, and bind \*Qescape\*U to the function
+that creates a window with a super-user shell.
+.SH "VIRTUAL TERMINAL"
+.I Screen
+prints error messages and other diagnostics in a \fImessage line\fP above
+the bottom of the screen.
+The message line is removed when a key is pressed or, automatically,
+after a couple of seconds.
+The message line facility can be used by an application running in
+the current window by means of the ANSI \fIPrivacy message\fP
+control sequence (for instance, from within the shell, something like
+.IP
+echo '^[^Hello world^[\e' (where ^[ is an \fIescape\fP)
+.PP
+can be used to display a message line.
+.PP
+When the `NF' capability is found in the termcap entry of the
+terminal on which
+.I screen
+has been started, flow control is turned off for the terminal.
+This enables the user to send XON and XOFF characters to the
+program running in a window (this is required by the \fIemacs\fP
+editor, for instance).
+The command line options
+.B -n
+and
+.B -f
+can be used to turn flow control off or on, respectively, independently
+of the `NF' capability.
+.PP
+.I
+Screen
+never writes in the last position of the screen, unless the boolean
+capability `LP' is found in the termcap entry of the terminal.
+Usually,
+.I screen
+cannot predict whether or not a particular terminal scrolls when
+a character is written in the last column of the last line;
+`LP' indicates that it is safe to write in this position.
+Note that the `LP' capability is independent of `am' (automatic
+margins); for certain terminals, such as the VT100, it is reasonable
+to set `am' as well as `LP' in the corresponding termcap entry
+(the VT100 does not move the cursor when a character is written in
+the last column of each line).
+.PP
+.I Screen
+puts into the environment of each process started in a newly created
+window the symbols \*QWINDOW=\fIn\fP\*U (where \fIn\fP is the number
+of the respective window), \*QTERM=screen\*U, and a TERMCAP variable
+reflecting the capabilities of the virtual terminal emulated by
+.IR screen .
+The actual set of capabilities supported by the virtual terminal
+depends on the capabilities supported by the physical terminal.
+If, for instance, the physical terminal does not support standout mode,
+.I screen
+does not put the `so' and `se' capabilities into the window's TERMCAP
+variable, accordingly.
+However, a minimum number of capabilities must be supported by a
+terminal in order to run
+.IR screen ,
+namely scrolling, clear screen, and direct cursor addressing
+(in addition,
+.I screen
+does not run on hardcopy terminals or on terminals that overstrike).
+.PP
+Some capabilities are only put into the TERMCAP
+variable of the virtual terminal if they can be efficiently
+implemented by the physical terminal.
+For instance, `dl' (delete line) is only put into the TERMCAP
+variable if the terminal supports either delete line itself or
+scrolling regions.
+If
+.I screen
+is called with the
+.B -a
+option, \fIall\fP capabilities are put into the environment,
+even if
+.I screen
+must redraw parts of the display in order to implement a function.
+.PP
+The following is a list of control sequences recognized by
+.IR screen .
+\*Q(V)\*U and \*Q(A)\*U indicate VT100-specific and ANSI-specific
+functions, respectively.
+.PP
+.nf
+.TP 20
+.B "ESC E"
+ Next Line
+.TP 20
+.B "ESC D"
+ Index
+.TP 20
+.B "ESC M"
+ Reverse Index
+.TP 20
+.B "ESC H"
+ Horizontal Tab Set
+.TP 20
+.B "ESC 7"
+(V) Save Cursor and attributes
+.TP 20
+.B "ESC 8"
+(V) Restore Cursor and Attributes
+.TP 20
+.B "ESC c"
+ Reset to Initial State
+.TP 20
+.B "ESC ="
+(V) Application Keypad Mode
+.TP 20
+.B "ESC >"
+(V) Numeric Keypad Mode
+.TP 20
+.B "ESC # 8"
+(V) Fill Screen with E's
+.TP 20
+.B "ESC \e"
+(A) String Terminator
+.TP 20
+.B "ESC ^"
+(A) Privacy Message (Message Line)
+.TP 20
+.B "ESC P"
+(A) Device Control String (not used)
+.TP 20
+.B "ESC _"
+(A) Application Program Command (not used)
+.TP 20
+.B "ESC ]"
+(A) Operating System Command (not used)
+.TP 20
+.B "ESC [ Pn ; Pn H"
+ Direct Cursor Addressing
+.TP 20
+.B "ESC [ Pn ; Pn f"
+ Direct Cursor Addressing
+.TP 20
+.B "ESC [ Pn J"
+ Erase in Display
+.TP 20
+\h'\w'ESC 'u'Pn = None or \fB0\fP
+ From Cursor to End of Screen
+.TP 20
+\h'\w'ESC 'u'\fB1\fP
+ From Beginning of Screen to Cursor
+.TP 20
+\h'\w'ESC 'u'\fB2\fP
+ Entire Screen
+.TP 20
+.B "ESC [ Pn K"
+ Erase in Line
+.TP 20
+\h'\w'ESC 'u'Pn = None or \fB0\fP
+ From Cursor to End of Line
+.TP 20
+\h'\w'ESC 'u'\fB1\fP
+ From Beginning of Line to Cursor
+.TP 20
+\h'\w'ESC 'u'\fB2\fP
+ Entire Line
+.TP 20
+.B "ESC [ Pn A"
+ Cursor Up
+.TP 20
+.B "ESC [ Pn B"
+ Cursor Down
+.TP 20
+.B "ESC [ Pn C"
+ Cursor Right
+.TP 20
+.B "ESC [ Pn D"
+ Cursor Left
+.TP 20
+.B "ESC [ Ps ;...; Ps m"
+ Select Graphic Rendition
+.TP 20
+\h'\w'ESC 'u'Ps = None or \fB0\fP
+ Default Rendition
+.TP 20
+\h'\w'ESC 'u'\fB1\fP
+ Bold
+.TP 20
+\h'\w'ESC 'u'\fB2\fP
+(A) Faint
+.TP 20
+\h'\w'ESC 'u'\fB3\fP
+(A) \fIStandout\fP Mode (ANSI: Italicised)
+.TP 20
+\h'\w'ESC 'u'\fB4\fP
+ Underlined
+.TP 20
+\h'\w'ESC 'u'\fB5\fP
+ Blinking
+.TP 20
+\h'\w'ESC 'u'\fB7\fP
+ Negative Image
+.TP 20
+\h'\w'ESC 'u'\fB22\fP
+(A) Normal Intensity
+.TP 20
+\h'\w'ESC 'u'\fB23\fP
+(A) \fIStandout\fP Mode off (ANSI: Italicised off)
+.TP 20
+\h'\w'ESC 'u'\fB24\fP
+(A) Not Underlined
+.TP 20
+\h'\w'ESC 'u'\fB25\fP
+(A) Not Blinking
+.TP 20
+\h'\w'ESC 'u'\fB27\fP
+(A) Positive Image
+.TP 20
+.B "ESC [ Pn g"
+ Tab Clear
+.TP 20
+\h'\w'ESC 'u'Pn = None or \fB0\fP
+ Clear Tab at Current Position
+.TP 20
+\h'\w'ESC 'u'\fB3\fP
+ Clear All Tabs
+.TP 20
+.B "ESC [ Pn ; Pn r"
+(V) Set Scrolling Region
+.TP 20
+.B "ESC [ Pn I"
+(A) Horizontal Tab
+.TP 20
+.B "ESC [ Pn Z"
+(A) Backward Tab
+.TP 20
+.B "ESC [ Pn L"
+(A) Insert Line
+.TP 20
+.B "ESC [ Pn M"
+(A) Delete Line
+.TP 20
+.B "ESC [ Pn @"
+(A) Insert Character
+.TP 20
+.B "ESC [ Pn P"
+(A) Delete Character
+.TP 20
+.B "ESC [ Ps ;...; Ps h"
+ Set Mode
+.TP 20
+.B "ESC [ Ps ;...; Ps l"
+ Reset Mode
+.TP 20
+\h'\w'ESC 'u'Ps = \fB4\fP
+(A) Insert Mode
+.TP 20
+\h'\w'ESC 'u'\fB?5\fP
+(V) Visible Bell (\fIOn\fP followed by \fIOff\fP)
+.TP 20
+\h'\w'ESC 'u'\fB?6\fP
+(V) \fIOrigin\fP Mode
+.TP 20
+\h'\w'ESC 'u'\fB?7\fP
+(V) \fIWrap\fP Mode
+.fi
+.SH ENVIRONMENT
+.nf
+LOCKPRG path of the lock-program used in lock-function
+.br
+LOCKMSG message to be issued if the terminal is unlocked again
+.fi
+.SH FILES
+.nf
+.ta 2i
+$(HOME)/.screenrc \fIscreen\fP initialization commands
+.br
+$(HOME)/.screen Directory created by \fIscreen\fP
+.br
+$(HOME)/.screen/\fItty\fP Socket created by \fIscreen\fP
+.br
+hardcopy.[0-9] Screen images created by the hardcopy function
+.br
+/etc/termcap Terminal capability data base
+.br
+/etc/utmp Login records
+.br
+/etc/ttys Terminal initialization data
+.fi
+.SH "SEE ALSO"
+termcap(5), utmp(5)
+.SH AUTHOR
+Oliver Laumann
+.SH BUGS
+Standout mode is not cleared before newline or cursor addressing.
+.PP
+If `LP' is not set but `am' is set, the last character in the last line is never
+written, and it is not correctly re-displayed when the screen is
+scrolled up or when a character is deleted in the last line.
+.PP
+The VT100 \*Qwrap around with cursor addressing\*U bug is not compensated
+when
+.I screen
+is running on a VT100.
+.PP
+`AL,' `DL', and similar parameterized capabilities are not used if present.
+.PP
+`dm' (delete mode), `xn', and `xs' are not handled
+correctly (they are ignored).
+.PP
+Different character sets are not supported.
+.PP
+`ms' is not advertised in the termcap entry (in order to compensate
+a bug in
+.IR curses (3X)).
+.PP
+Scrolling regions are only emulated if the physical terminal supports
+scrolling regions.
+.PP
+.I Screen
+does not make use of hardware tabs.
+.PP
+.I Screen
+must be installed as set-uid with owner root in order to be able
+to correctly change the owner of the tty device file for each
+window.
+Special permission may also be required to write the file \*Q/etc/utmp\*U.
+.PP
+Entries in \*Q/etc/utmp\*U are not removed when
+.I screen
+is killed with SIGKILL.
+.PP
+The lock-function should not depend on external programs but call an internal
+function to avoid security troubles
diff --git a/src/screen.c b/src/screen.c
new file mode 100644
index 0000000..0870f36
--- /dev/null
+++ b/src/screen.c
@@ -0,0 +1,1655 @@
+/* Copyright (c) 1987, 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.
+ */
+
+static char ScreenVersion[] = "screen 1.1i 1.7 88/12/13";
+
+#ifdef SUNOS
+#define KVMLIB
+#endif SUNOS
+
+#include <stdio.h>
+#include <sgtty.h>
+#include <signal.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <utmp.h>
+#include <pwd.h>
+#include <nlist.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include "screen.h"
+#ifdef KVMLIB
+#include <kvm.h>
+#include <sys/param.h>
+#endif KVMLIB
+
+#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 MAXARGS 64
+#define MAXLINE 1024
+#define MSGWAIT 5
+
+#define Ctrl(c) ((c)&037)
+
+extern char *blank, Term[], **environ;
+extern rows, cols;
+extern status;
+extern time_t TimeDisplayed;
+extern char AnsiVersion[];
+extern short ospeed;
+extern flowctl;
+extern errno;
+extern sys_nerr;
+extern char *sys_errlist[];
+extern char *index(), *rindex(), *malloc(), *getenv(), *MakeTermcap();
+extern char *getlogin(), *ttyname();
+extern struct passwd *getpwuid();
+#ifdef pe3200
+extern struct utmp *SetUtmp();
+#endif /* pe3200 */
+static Finit(), SigChld();
+static char *MakeBellMsg(), *Filename(), **SaveArgs();
+#ifdef LOCK
+static void LockTerminal();
+static char LockEnd[] = "Welcome back to screen !!";
+#endif LOCK
+
+static char PtyName[32], TtyName[32];
+static char *ShellProg;
+static char *ShellArgs[2];
+static char inbuf[IOSIZE];
+static inlen;
+static ESCseen;
+static GotSignal;
+#ifdef LOCK
+static char DefaultLock[] = "/usr/local/bin/lck";
+#endif LOCK
+static char DefaultShell[] = "/bin/sh";
+#ifdef pe3200
+static char DefaultPath[] = ":/usr/local/bin:/bin:/usr/bin";
+#else
+static char DefaultPath[] = ":/usr/ucb:/bin:/usr/bin";
+#endif
+static char PtyProto[] = "/dev/ptyXY";
+static char TtyProto[] = "/dev/ttyXY";
+#ifdef SEQUENT
+#define PTYCHAR1 "p"
+#define PTYCHAR2 "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+#endif SEQUENT
+#ifndef PTYCHAR1
+#define PTYCHAR1 "pqrst"
+#endif PTYCHAR1
+#ifndef PTYCHAR2
+#define PTYCHAR2 "01213456789abcdef"
+#endif PTYCHAR2
+static char SockPath[512];
+static char SockDir[] = ".screen";
+static char *SockName;
+static char *NewEnv[MAXARGS];
+static char Esc = Ctrl('a');
+static char MetaEsc = 'a';
+static char *home;
+static HasWindow;
+static utmp;
+#ifndef pe3200
+static utmpf;
+static char UtmpName[] = "/etc/utmp";
+#endif
+static char *LoginName;
+static char *BellString = "Bell in window %";
+static mflag, nflag, fflag;
+static char HostName[MAXSTR];
+#ifdef LOADAV
+#ifdef SEQUENT
+ static char AvenrunSym[] = "_loadav";
+#else SEQUENT
+ static char AvenrunSym[] = "_avenrun";
+#endif
+ static struct nlist nl[2];
+ static avok;
+#ifdef KVMLIB
+ static kvm_t *kmemf;
+#else
+ static char KmemName[] = "/dev/kmem";
+#ifdef SEQUENT
+ static char UnixName[] = "/dynix";
+#else SEQUENT
+ static char UnixName[] = "/vmunix";
+#endif SEQUENT
+ static kmemf;
+#endif KVMLIB
+#ifndef vax
+#define AVvar(X) long X[3]
+#ifndef FSCALE
+#define FSCALE 1
+#endif FSCALE
+#define AVfmt(X) (((double)X)/FSCALE)
+#else
+#define AVvar(X) double X[3]
+#define AVfmt(X) X
+#endif vax
+#endif LOADAV
+
+struct mode {
+ struct sgttyb m_ttyb;
+ struct tchars m_tchars;
+ struct ltchars m_ltchars;
+ int m_ldisc;
+ int m_lmode;
+} OldMode, NewMode;
+
+static struct win *curr, *other;
+static CurrNum, OtherNum;
+static struct win *wtab[MAXWIN];
+
+#define MSG_CREATE 0
+#define MSG_ERROR 1
+
+struct msg {
+ int type;
+ union {
+ struct {
+ int aflag;
+ int nargs;
+ char line[MAXLINE];
+ char dir[1024];
+ } create;
+ char message[MAXLINE];
+ } m;
+};
+
+#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
+#ifdef LOCK
+#define KEY_LOCK 26
+#define KEY_CREATE 27
+#else LOCK
+#define KEY_CREATE 26
+#endif LOCK
+
+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",
+#ifdef LOCK
+ "lock",
+#endif LOCK
+ 0
+};
+
+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];
+
+ 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 '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] [-exy] [cmd args]", 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 (GetSockName ()) {
+ /* Client */
+ s = MakeClientSocket ();
+ SendCreateMsg (s, ac, av, aflag);
+ close (s);
+ exit (0);
+ }
+ (void) gethostname (HostName, MAXSTR);
+ HostName[MAXSTR-1] = '\0';
+ if (ap = index (HostName, '.'))
+ *ap = '\0';
+ s = MakeServerSocket ();
+ InitTerm ();
+ if (fflag)
+ flowctl = 1;
+ else if (nflag)
+ flowctl = 0;
+ MakeNewEnv ();
+ GetTTY (0, &OldMode);
+ ospeed = (short)OldMode.m_ttyb.sg_ospeed;
+ InitUtmp ();
+#ifdef LOADAV
+ InitKmem ();
+#endif
+ signal (SIGHUP, Finit);
+ signal (SIGINT, Finit);
+ signal (SIGQUIT, Finit);
+ signal (SIGTERM, Finit);
+ InitKeytab ();
+ sprintf (rc, "%.*s/.screenrc", 245, home);
+ signal (SIGCHLD, SigChld);
+ ReadRc (rc);
+ if ((n = MakeWindow (*av, av, aflag, 0, (char *)0)) == -1) {
+ SetTTY (0, &OldMode);
+ FinitTerm ();
+ exit (1);
+ }
+ SetCurrWindow (n);
+ HasWindow = 1;
+ SetMode (&OldMode, &NewMode);
+ SetTTY (0, &NewMode);
+ tv.tv_usec = 0;
+ while (1) {
+#ifdef EBUG
+fprintf(stderr,"DEBUG: next while. ");
+#endif * EBUG */
+ 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;
+ fflush (stdout);
+#ifdef EBUG
+fprintf(stderr,"DEBUG: selecting.. ");
+#endif * EBUG */
+ if (select (32, &r, &w, &x, status ? &tv : (struct timeval *)0) == -1) {
+ if (errno == EINTR)
+ continue;
+ HasWindow = 0;
+ Msg (errno, "select");
+ /*NOTREACHED*/
+ }
+#ifdef EBUG
+fprintf(stderr,"DEBUG: select DONE. ");
+#endif * EBUG */
+ if (GotSignal && !status) {
+ SigHandler ();
+#ifdef EBUG
+fprintf(stderr,"DEBUG: handle sigs ");
+#endif * EBUG */
+ continue;
+ }
+ if (r & 1 << s) {
+ RemoveStatus (curr);
+ ReceiveMsg (s);
+ }
+ if (r & 1 << 0) {
+ RemoveStatus (curr);
+ if (ESCseen) {
+ inbuf[0] = Esc;
+#ifdef EBUG
+fprintf(stderr,"DEBUG: read(ESC) ");
+#endif * EBUG */
+ inlen = read (0, inbuf+1, IOSIZE-1) + 1;
+ ESCseen = 0;
+ } else {
+#ifdef EBUG
+fprintf(stderr,"DEBUG: read(nonESC) ");
+#endif * EBUG */
+ inlen = read (0, inbuf, IOSIZE);
+ }
+ if (inlen > 0)
+ inlen = ProcessInput (inbuf, inlen);
+#ifdef EBUG
+fprintf(stderr,"DEBUG: Input DONE. \n");
+#endif * EBUG */
+ if (inlen > 0)
+ continue;
+ }
+ if (GotSignal && !status) {
+ SigHandler ();
+#ifdef EBUG
+fprintf(stderr,"DEBUG: handle sigs2 ");
+#endif * EBUG */
+ 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 ();
+#ifdef EBUG
+fprintf(stderr,"DEBUG: handle sigs3");
+#endif * EBUG */
+ 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 DoWait () {
+ register pid;
+ register struct win **pp;
+ union wait wstat;
+
+ while ((pid = wait3 (&wstat, WNOHANG|WUNTRACED, NULL)) > 0) {
+ for (pp = wtab; pp < wtab+MAXWIN; ++pp) {
+ if (*pp && pid == (*pp)->wpid) {
+ if (WIFSTOPPED (wstat)) {
+ kill((*pp)->wpid, SIGCONT);
+ } else {
+ if (*pp == curr)
+ curr = 0;
+ if (*pp == other)
+ other = 0;
+ FreeWindow (*pp);
+ *pp = 0;
+ }
+ }
+ }
+ }
+ CheckWindows ();
+}
+
+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");
+ 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;
+#ifdef LOCK
+ ktab['x'].type = ktab[Ctrl('x')].type = KEY_LOCK;
+#endif LOCK
+ ktab['.'].type = KEY_TERMCAP;
+ ktab[Ctrl('\\')].type = KEY_QUIT;
+ 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;
+ SetTTY (0, &OldMode);
+ FinitTerm ();
+ kill (getpid (), SIGTSTP);
+ SetTTY (0, &NewMode);
+ Activate (wtab[CurrNum]);
+ 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_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;
+#ifdef LOCK
+ case KEY_LOCK:
+ p = buf;
+ LockTerminal();
+ break;
+#endif LOCK
+ 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->image);
+ free (wp->attr);
+ 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];
+
+#ifdef EBUG
+fprintf(stderr,"DEBUG: makewindow(%s)\n",prog);
+#endif /* EBUG */
+ 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;
+ }
+ 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->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, 0622);
+ p->slot = SetUtmp (TtyName);
+ switch (p->wpid = fork ()) {
+ case -1:
+ Msg (errno, "Cannot fork");
+ free (p);
+ return -1;
+ case 0:
+ signal (SIGHUP, SIG_DFL);
+ signal (SIGINT, SIG_DFL);
+ signal (SIGQUIT, SIG_DFL);
+ signal (SIGTERM, 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 ();
+ if ((f = open ("/dev/tty", O_RDWR)) != -1) {
+ ioctl (f, TIOCNOTTY, (char *)0);
+ close (f);
+ }
+ if ((tf = open (TtyName, O_RDWR)) == -1) {
+ SendErrorMsg ("Cannot open %s: %s", TtyName, sys_errlist[errno]);
+ exit (1);
+ }
+ dup2 (tf, 0);
+ dup2 (tf, 1);
+ dup2 (tf, 2);
+ for (f = getdtablesize () - 1; f > 2; f--)
+ close (f);
+ ioctl (0, TIOCSPGRP, &mypid);
+ 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;
+}
+
+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);
+ }
+ }
+ 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;
+ struct tm *tp;
+ time_t now;
+ int i;
+#ifdef LOADAV
+ AVvar(av);
+#endif
+
+ 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 (avok && GetAvenrun (av)) {
+ p = buf + strlen (buf);
+ for (i = 0; i < sizeof(av)/sizeof(av[0]); i++) {
+ /* sprintf (p, " %2.2f" , AVfmt(av[i])), */
+ sprintf (p, " %g" , AVfmt(av[i])),
+ p = buf + strlen (buf);
+ }
+ }
+#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 ? '+' : '-');
+ 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 = PTYCHAR1; *p = *l; ++l) {
+ for (d = PTYCHAR2; 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; {
+ 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);
+}
+
+static GetTTY (fd, mp) struct mode *mp; {
+ 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);
+}
+
+static SetMode (op, np) struct mode *op, *np; {
+ *np = *op;
+ 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;
+#ifdef pe3200
+ /* this is because this is really a System V
+ * In CBREAK mode eof = min number of chars to be read before return
+ * eol = time in 1/10 seconds waiting for input
+ */
+ np->m_tchars.t_eofc = 1;
+ np->m_tchars.t_brkc = 1;
+#endif
+ 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;
+}
+
+static GetSockName () {
+ struct stat s;
+ register client;
+ register char *p;
+
+ if (!mflag && (SockName = getenv ("STY")) != 0 && *SockName != '\0') {
+ client = 1;
+ setuid (getuid ());
+ setgid (getgid ());
+ } else {
+ if ((p = ttyname (0)) == 0 || (p = ttyname (1)) == 0 ||
+ (p = ttyname (2)) == 0 || *p == '\0')
+ Msg (0, "screen must run on a tty.");
+ SockName = Filename (p);
+ client = 0;
+ }
+ if ((home = getenv ("HOME")) == 0)
+ Msg (0, "$HOME is undefined.");
+#ifdef SEQUENT
+ /* the sequent has problems with binding AF_UNIX sockets
+ * to NFS files. So we changed the path to point to a (hopefully)
+ * local directory
+ */
+ sprintf (SockPath, "/tmp/.%s.%d", SockDir, getuid());
+#else SEQUENT
+ sprintf (SockPath, "/tmp/.%s.%d", home, SockDir);
+#endif SEQUENT
+ if (stat (SockPath, &s) == -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 ((s.st_mode & S_IFMT) != S_IFDIR)
+ Msg (0, "%s is not a directory.", SockPath);
+ if ((s.st_mode & 0777) != 0700)
+ Msg (0, "Directory %s must have mode 700.", SockPath);
+ if (s.st_uid != getuid ())
+ Msg (0, "You are not the owner of %s.", SockPath);
+ }
+ strcat (SockPath, "/");
+ strcat (SockPath, SockName);
+ return client;
+}
+
+static MakeServerSocket () {
+ register s;
+ struct sockaddr_un a;
+
+ (void) unlink (SockPath);
+ if ((s = socket (AF_UNIX, SOCK_STREAM, 0)) == -1)
+ Msg (errno, "socket");
+ a.sun_family = AF_UNIX;
+ strcpy (a.sun_path, 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 () {
+ register s;
+ struct sockaddr_un a;
+
+ if ((s = socket (AF_UNIX, SOCK_STREAM, 0)) == -1)
+ Msg (errno, "socket");
+ a.sun_family = AF_UNIX;
+ strcpy (a.sun_path, SockPath);
+ if (connect (s, (struct sockaddr *)&a, strlen (SockPath)+2) == -1)
+ Msg (errno, "connect: %s", SockPath);
+ 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 (getwd (m.m.create.dir) == 0)
+ Msg (0, "%s", m.m.create.dir);
+ if (write (s, &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 ();
+ m.type = MSG_ERROR;
+ sprintf (m.m.message, fmt, p1, p2, p3, p4, p5, p6);
+ (void) write (s, &m, sizeof (m));
+ close (s);
+ sleep (2);
+}
+
+static ReceiveMsg (s) {
+ register ns;
+ struct sockaddr_un a;
+ int len = sizeof (a);
+ struct msg m;
+
+ if ((ns = accept (s, (struct sockaddr *)&a, &len)) == -1) {
+ Msg (errno, "accept");
+ return;
+ }
+ if ((len = read (ns, &m, sizeof (m))) != sizeof (m)) {
+ if (len == -1)
+ Msg (errno, "read");
+ else
+ Msg (0, "Short message (%d bytes)", len);
+ close (ns);
+ return;
+ }
+ switch (m.type) {
+ case MSG_CREATE:
+ ExecCreate (&m);
+ break;
+ case MSG_ERROR:
+ Msg (0, "%s", m.m.message);
+ break;
+ default:
+ Msg (0, "Invalid message (type %d).", m.type);
+ }
+ close (ns);
+}
+
+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], "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]);
+ }
+ 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;
+
+ 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);
+ 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;
+}
+
+static InitUtmp () {
+ struct passwd *p;
+
+#ifndef pe3200
+ if ((utmpf = open (UtmpName, O_WRONLY)) == -1) {
+ if (errno != EACCES)
+ Msg (errno, UtmpName);
+ return;
+ }
+#endif
+ if ((LoginName = getlogin ()) == 0 || LoginName[0] == '\0') {
+ if ((p = getpwuid (getuid ())) == 0)
+ LoginName = "SCREEN";
+ else
+ LoginName = p->pw_name;
+ }
+ utmp = 1;
+}
+
+static
+#ifdef pe3200
+ struct utmp *
+#endif
+ SetUtmp (name) char *name; {
+ register char *p;
+#ifdef pe3200
+ struct utmp *u;
+#else
+ register struct ttyent *tp;
+ register slot = 1;
+ struct utmp u;
+
+ if (!utmp)
+ return 0;
+#endif
+ if (p = rindex (name, '/'))
+ ++p;
+ else p = name;
+#ifndef pe3200
+ 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_name, LoginName, 8);
+ time (&u.ut_time);
+ u.ut_host[0] = '\0';
+ (void) lseek (utmpf, (long)(slot * sizeof (u)), 0);
+ (void) write (utmpf, &u, sizeof (u));
+ return slot;
+#else
+ if ( (u = (struct utmp *)malloc(sizeof(struct utmp)) == (struct utmp *)NULL) )
+ {
+ return NULL;
+ }
+ strncpy (u->ut_line, p, 12);
+ u->ut_id[0] = *p;
+ strncpy(&u->ut_id[1],strlen(p) > 1 ? p + strlen(p) - 2 : p, 2);
+ u->ut_id[3] = '\0';
+ strncpy (u->ut_user, LoginName, 8);
+ time (&u->ut_time);
+ u->ut_type = USER_PROCESS;
+ u->ut_pid = getpid();
+ pututline(u);
+ return u;
+#endif
+}
+
+static RemoveUtmp (slot)
+#ifdef pe3200
+ struct utmp *slot;
+#endif
+{
+#ifdef pe3200
+ if (slot)
+ {
+ slot->ut_type = DEAD_PROCESS;
+ pututline(slot);
+ }
+#else
+ struct utmp u;
+
+ if (slot) {
+ bzero (&u, sizeof (u));
+ (void) lseek (utmpf, (long)(slot * sizeof (u)), 0);
+ (void) write (utmpf, &u, sizeof (u));
+ }
+#endif
+}
+
+#ifdef LOCK
+
+/* ADDED by Rainer Pruy 10/15/87 */
+static void LockTerminal()
+{
+ char *prg;
+ int sig, pid;
+ void * sigs[NSIG];
+
+ for ( sig = 1; sig < NSIG; sig++ )
+ {
+ sigs[sig] = (void *)signal(sig,SIG_IGN);
+ }
+ SetTTY (0, &OldMode);
+ FinitTerm ();
+ printf("\n");
+
+ if ( (pid = fork()) == 0)
+ {
+ /* Child */
+ if ( (prg = getenv("LOCKPRG")) == NULL)
+ prg = DefaultLock;
+
+ execl(prg,"SCREEN-LOCK",NULL);
+ Msg(errno,"%s",prg);
+ sleep(2);
+ exit(0);
+ }
+ if (pid == -1)
+ {
+ SetTTY (0, &NewMode);
+ Activate (wtab[CurrNum]);
+ Msg(errno,"Cannot lock terminal - fork failed");
+ }
+ else
+ {
+ union wait status;
+ int wret, saverrno;
+
+ errno = 0;
+ while ( ((wret=wait(&status)) != pid) ||
+ ((wret == -1) && (errno = EINTR))
+ ) errno = 0;
+
+ saverrno = errno;
+ SetTTY (0, &NewMode);
+ Activate (wtab[CurrNum]);
+
+ if (saverrno)
+ Msg(saverrno,"Lock");
+ else
+ if (status.w_T.w_Termsig != 0)
+ Msg(0,"Lock: %s: Killed by signal: %d%s",prg,
+ status.w_T.w_Termsig,status.w_T.w_Coredump?" (Core dumped)":"");
+ else
+ if (status.w_T.w_Retcode)
+ Msg(0,"Lock: %s: Returnecode = %d",prg,status.w_T.w_Retcode);
+ else
+ {
+ int startpos;
+ char *endmsg = getenv("LOCKMSG");
+
+ if ( endmsg == NULL ) endmsg = LockEnd;
+ startpos = (cols - strlen(endmsg)) / 2 - 1;
+ if ( startpos < 0 ) startpos = 0;
+ Msg(0,"%*s%-*s", startpos, "", cols - startpos, endmsg);
+ }
+ }
+ /* reset signals */
+ for ( sig = 1; sig < NSIG; sig++ )
+ {
+ signal(sig,(int (*)())sigs[sig]);
+ }
+} /* LockTerminal */
+
+#endif LOCK
+
+
+#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 () {
+ avok = 0;
+ nl[0].n_name = AvenrunSym;
+#ifdef KVMLIB
+ if ((kmemf = kvm_open (NULL, NULL, NULL, O_RDONLY, "screen")) == NULL)
+ return;
+ kvm_nlist (kmemf, nl);
+#else KVMLIB
+ if ((kmemf = open (KmemName, O_RDONLY)) == -1)
+ return;
+ nlist (UnixName, nl);
+#endif KVMLIB
+ if (nl[0].n_type == 0 || nl[0].n_value == 0)
+ return;
+ avok = 1;
+}
+
+static GetAvenrun (av) AVvar(av); {
+#ifdef KVMLIB
+ if (kvm_read( kmemf, nl[0].n_value, av, sizeof (av)) != sizeof(av))
+ return 0;
+#else KVMLIB
+ if (lseek (kmemf, nl[0].n_value, 0) == -1)
+ return 0;
+ if (read (kmemf, av, sizeof (av)) != sizeof (av))
+ return 0;
+#endif KVMLIB
+ return 1;
+}
+
+#endif
diff --git a/src/screen.h b/src/screen.h
new file mode 100644
index 0000000..9cf4aea
--- /dev/null
+++ b/src/screen.h
@@ -0,0 +1,68 @@
+/* Copyright (c) 1987, 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.
+ */
+
+#ifndef NO_SCCS_ID
+static char SCCS_screen_h[]="@(#)screen.h 1.3 88/07/20 01:58:01";
+#endif /* NO_SCCS_ID */
+
+enum state_t {
+ LIT, /* Literal input */
+ ESC, /* Start of escape sequence */
+ STR, /* Start of control string */
+ TERM, /* ESC seen in control string */
+ CSI, /* Reading arguments in "CSI Pn ; Pn ; ... ; XXX" */
+};
+
+enum string_t {
+ NONE,
+ DCS, /* Device control string */
+ OSC, /* Operating system command */
+ APC, /* Application program command */
+ PM, /* Privacy message */
+};
+
+#define MAXSTR 128
+
+#define IOSIZE 80
+
+struct win {
+ int wpid;
+ int ptyfd;
+ int aflag;
+ char outbuf[IOSIZE];
+ int outlen;
+ char cmd[MAXSTR];
+ char tty[MAXSTR];
+#ifdef pe3200
+ struct utmp *slot;
+#else
+ int slot;
+#endif
+ char **image;
+ char **attr;
+ int active;
+ int x, y;
+ char LocalAttr;
+ int saved;
+ int Saved_x, Saved_y;
+ char SavedLocalAttr;
+ int top, bot;
+ int wrap;
+ int origin;
+ int insert;
+ int keypad;
+ enum state_t state;
+ enum string_t StringType;
+ char string[MAXSTR];
+ char *stringp;
+ char *tabs;
+ int vbwait;
+ int bell;
+};