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

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

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

This is a port of the BSD4.3 based "screen" program to the IRIS 4D.  It works
exactly as described in the man page, except for the "suspend" sequence
(detaching works just fine, though).  More details are available in the
file SGI.README.

Screen allows you multiple virtual terminal sessions on the same physical
display screen, whether that be a terminal or a window.  A group of 
virtual terminals sessions can be detached from the current display and
reattached later, or at a different terminal (as long as it is the same
type of terminal).

This may also be an interesting example of porting total BSD 4.3 oriented
code onto the 4D, for those who are interested.  Note that the load average
code turns itself on automatically on 3.2 and later versions of IRIX.

-- Jim Barton
Silicon Graphics Computer Systems    "UNIX: Live Free Or Die!"
jmb@sgi.sgi.com, sgi!jmb@decwrl.dec.com, ...{decwrl,sun}!sgi!jmb


#--------------------------------CUT HERE-------------------------------------
#! /bin/sh
#
# This is a shell archive.  Save this into a file, edit it
# and delete all lines above this comment.  Then give this
# file to sh by executing the command "sh file".  The files
# will be extracted into the current directory owned by
# you with default permissions.
#
# The files contained herein are:
#
# -rw-r--r--   1 jmb      sys         1815 Aug  3 11:27 Makefile
# -r--r--r--   1 jmb      sys         3987 Aug  3 11:24 README
# -r--r--r--   1 jmb      sys         2033 Aug  3 11:24 SGI.README
# -r--r--r--   1 jmb      sys        36576 Aug  3 11:24 ansi.c
#
echo 'x - Makefile'
if test -f Makefile; then echo 'shar: not overwriting Makefile'; else
sed 's/^X//' << '________This_Is_The_END________' > Makefile
X# The following options can be set:
X#
X# -DLOADAV    -- your system maintains a load average like 4.3 BSD does
X#                (an array of three doubles called _avenrun; it is read
X#                from /dev/kmem; _avenrun is taken from the namelist of
X#                /vmunix).
X# -DSUNLOADAV -- the load average maintained by the kernel is in the
X#                Sun format (three longs).  Set this in addition to
X#                LOADAV.
X# -DGETTTYENT -- your system has the new format /etc/ttys (like 4.3 BSD)
X#                and the getttyent(3) library functions.
X#
X# -DUSEBCOPY  -- use the bcopy() from the system's C-library.  If this
X#                is set, bcopy must support overlapping source and
X#                destination.  If USEBCOPY is not set, screen uses its
X#                own version of bcopy.
X#
X# -DSYSV      -- use System V terminal control and such.  Does not deal with
X#		 the lack of the select(2) system call, but does get around
X#		 needing UNIX-domain sockets.
X#
X# -Dsgi	      -- certain special hacks to make things a bit better on an
X#		 SGI 4D series machine.
X#
X# You should install as set-uid with owner root, so that it can read/write
X# /etc/utmp, read /dev/kmem, and chown/chmod the allocated pseudo-ttys.
X
XOPTIONS= -DUSEBCOPY -DSYSV -I/usr/include/bsd -DLOADAV -DSUNLOADAV
XCFLAGS = -O2
X
XCFILES= screen.c ansi.c
XOFILES= $(CFILES:.c=.o)
X
Xdefault:	hyper
X
Xscreen: $(OFILES)
X	$(CC) $(CFLAGS) -o screen $(OFILES) -ltermcap -lsun -lbsd -lmld -lc_s
X
Xscreen.o: screen.h
X
Xansi.o: screen.h
X
X#
X#	Following rule runs the maximum optimization against the program
X#	to get the best performance.
X#
Xhyper:
X	$(CC) $(OPTIONS) -O3 screen.c ansi.c -o screen -ltermcap -lsun \
X		-lbsd -lmld -lc_s
X
X.c.o:
X	$(CC) $(OPTIONS) $(CFLAGS) -c $*.c
X
Xclean:
X	rm -f *.[oui]
X
Xclobber: clean
X	rm -f screen
________This_Is_The_END________
if test `wc -l < Makefile` -ne 58; then
	echo 'shar: Makefile was damaged during transit (should have been 58 bytes)'
fi
fi		; : end of overwriting check
echo 'x - README'
if test -f README; then echo 'shar: not overwriting README'; else
sed 's/^X//' << '________This_Is_The_END________' > README
X"screen" is a window manager that allows you to handle several independent
Xscreens (UNIX ttys) on a single physical terminal; each screen has its own
Xset of processes connected to it (typically interactive shells).  Each
Xvirtual terminal created by "screen" emulates a DEC VT100 plus several ANSI
XX3.64 and ISO 2022 functions (including DEC VT102 features such as line and
Xcharacter deletion and insertion).
X
XSince "screen" uses pseudo-ttys, the select system call, and UNIX-domain
Xsockets, it will not run under a system that does not include these
Xfeatures of 4.2 and 4.3 BSD UNIX.
X
XIf you want to get a quick idea how "screen" works but don't want to read
Xthe entire manual, do the following:
X 
X     -  call "screen" without arguments
X     -  wait for the shell prompt; execute some commands
X     -  type ^A ^C (Control-A followed by Control-C)
X     -  wait for the shell prompt; do something in the new window
X     -  type ^A ^A repeatedly to switch between the two windows
X     -  terminate the first shell ("screen" switches to the other window)
X     -  terminate the second shell
X
XIf you have got "vttest" (the VT100 test program from mod.sources) you
Xmay want to run it from within "screen" to verify that it correctly
Xemulates a VT100 on your terminal (except for 132 column mode and
Xdouble width/height characters, of course).
X
XBy the way, "screen" can be used to compensate for certain bugs of "real"
XVT100 terminals.  For instance, our 4.2 BSD version of mille(6) garbles
Xthe display on terminals of the VT100 family, but it works quite fine
Xwhen it is invoked from within "screen".  In addition, "screen" enables
Xyou to use EMACS on terminals that are unable to generate Control-S and
XControl-Q from the keyboard or that require flow control using Control-S
Xand Control-Q.  This is the reason why I have an alias like
X     alias emacs "screen emacs"
Xin my .cshrc file.
X
X
XMajor changes between this and the previous release are:
X
X*  "screen" allows you to `detach' the "screen" session from the physical
X   terminal and resume it at a later point in time (possibly on a
X   different terminal or in a different login session).
X
X  To get an impression of this functionality do the following:
X
X     - call "screen" and create a couple of windows
X     - type Control-A Control-D (screen terminates; you are back
X       in the shell)
X     - call "screen -r" to resume the detached "screen"
X
X*  "screen" supports multiple character sets and the ISO 2022 control
X   functions to designate and switch between character sets.
X   This allows you, for instance, to make use of the VT100 graphics
X   character set or national character sets
X
X
XBefore typing "make", you should have a look into the Makefile.
XIf your system maintains a 4.3-BSD-style load average, add -DLOADAV to
Xthe compiler options.  If the load average maintained by your system
Xis in the Sun format (three longs; not three doubles), set -DSUNLOADAV
Xin addition to -DLOADAV.
X
XIn addition, you must set -DGETTTYENT if your system has the new format
X/etc/ttys and the getttyent(3) routines.
X
XIf the bcopy() from your system's C library supports overlapping
Xsource and destination addresses, add -DUSEBCOPY.  Otherwise screen
Xuses its own (possibly slower) version of bcopy().  If you are in
Xdoubt or don't know what bcopy() is, leave the option out.
X
X"screen" should be granted read and write access to /etc/utmp and, if
X-DLOADAV has been specified, read access to /vmunix and /dev/kmem.
X"screen" should be installed with set-uid and owner root to enable it
Xto correctly change the owner of newly allocated virtual terminals.
XFailing to do this (e.g. if you fear a trojan horse) doesn't have any
Xmajor disadvantages, except that w(1) and some other utilities may have
Xsome problems with the tty files of your virtual terminals.
X
X
XRegards,
X    Oliver Laumann
X    Technical University of Berlin,
X    Communications and Operating Systems Research Group.
X
X    net@tub.BITNET     US: pyramid!tub!net     Europe: unido!tub!net
________This_Is_The_END________
if test `wc -l < README` -ne 86; then
	echo 'shar: README was damaged during transit (should have been 86 bytes)'
fi
fi		; : end of overwriting check
echo 'x - SGI.README'
if test -f SGI.README; then echo 'shar: not overwriting SGI.README'; else
sed 's/^X//' << '________This_Is_The_END________' > SGI.README
XBSD 'screen' program for the IRIS 4D
X------------------------------------
X
XThe version of 'screen' in this directory was ported to the Silicon Graphics
X4D series computers for releases 3.1 and later of IRIX.  Everything works
Xas advertised, except there are some "System V-isms" that change some things.
X
XI have supplied a tic file for the 'screen' terminal definition.  If you
Xwant, just run "tic screen.tic" as root and the terminal description will
Xbe automatically installed in /usr/lib/terminfo.  If you want it somewhere
Xelse, set and export the TERMINFO environment variable with a path to a
Xdirectory where you wish to keep the terminal descriptions.  Remember to
Xexport TERMINFO out of your profile to make sure that your local area
Xis searched first whenever curses is invoked.  An "ansi" terminal description
Xshould probably work instead, but it won't be as slick.
X
XThe suspend command, ^Az, is automatically disabled in pre-3.3 releases
Xof IRIX due to a bug in SIGCONT handling.  Use the "detach" command (^Ad)
Xcommand in this case.  Also, the ^At time status line will include the load
Xaverage on 3.2 and later systems if the program is setuid-to-root.
X
XIf you make 'screen' setuid-to-root, then it will update /etc/utmp
Xas you create and destroy screens as well.
X
XTo build the program, simply type 'make' and copy the program into your
Xpath somewhere.  Each user who uses 'screen' must have a directory in
Xtheir home directory called ".screen".  This is where 'screen' creates
Xthe communication pipes it uses for each session.  You should probably
Xmake the directory mode 700 for safety, although 'screen' does have
Xbuilt in checks against someone else attaching to your screens.
X
XIf problems arise, send me some mail and I'll try to help you out.  But,
Xas usual with all free software, there is no warranty that this program,
Xas ported, is suitable for any purpose.  To the best of my knowledge,
Xthere are no bugs.
X
XJim Barton
XSilicon Graphics Computer Systems
Xjmb@sgi.sgi.com
X
XJuly 25, 1989
XMountain View, California
________This_Is_The_END________
if test `wc -l < SGI.README` -ne 42; then
	echo 'shar: SGI.README was damaged during transit (should have been 42 bytes)'
fi
fi		; : end of overwriting check
echo 'x - ansi.c'
if test -f ansi.c; then echo 'shar: not overwriting ansi.c'; else
sed 's/^X//' << '________This_Is_The_END________' > ansi.c
X/* Copyright (c) 1987,1988 Oliver Laumann, Technical University of Berlin.
X * Not derived from licensed software.
X *
X * Permission is granted to freely use, copy, modify, and redistribute
X * this software, provided that no attempt is made to gain profit from it,
X * the author is not construed to be liable for any results of using the
X * software, alterations are clearly marked as such, and this notice is
X * not modified.
X */
X
Xchar AnsiVersion[] = "ansi 2.0a 19-Oct-88";
X
X#include <stdio.h>
X#include <ctype.h>
X#include <sys/types.h>
X#include "screen.h"
X
X#define A_SO     0x1    /* Standout mode */
X#define A_US     0x2    /* Underscore mode */
X#define A_BL     0x4    /* Blinking */
X#define A_BD     0x8    /* Bold mode */
X#define A_DI    0x10    /* Dim mode */
X#define A_RV    0x20    /* Reverse mode */
X#define A_MAX   0x20
X
X/* Types of movement used by Goto() */
Xenum move_t {
X    M_NONE,
X    M_UP,
X    M_DO,
X    M_LE,
X    M_RI,
X    M_RW,
X    M_CR,
X};
X
X#define EXPENSIVE    1000
X
X#define G0           0
X#define G1           1
X#define G2           2
X#define G3           3
X
X#define ASCII        0
X
Xextern char *getenv(), *tgetstr(), *tgoto(), *malloc();
X
Xint rows, cols;
Xint status;
Xint flowctl;
Xchar Term[] = "TERM=screen";
Xchar Termcap[1024];
Xchar *blank;
Xchar PC;
Xint ISO2022;
Xtime_t TimeDisplayed;
X
Xstatic char tbuf[1024], tentry[1024];
Xstatic char *tp = tentry;
Xstatic char *TI, *TE, *BL, *VB, *BC, *CR, *NL, *CL, *IS, *CM;
Xstatic char *US, *UE, *SO, *SE, *CE, *CD, *DO, *SR, *SF, *AL;
Xstatic char *CS, *DL, *DC, *IC, *IM, *EI, *UP, *ND, *KS, *KE;
Xstatic char *MB, *MD, *MH, *MR, *ME, *PO, *PF;
Xstatic char *CDC, *CDL, *CAL;
Xstatic AM;
Xstatic char GlobalAttr, TmpAttr, GlobalCharset, TmpCharset;
Xstatic char *OldImage, *OldAttr, *OldFont;
Xstatic last_x, last_y;
Xstatic struct win *curr;
Xstatic display = 1;
Xstatic StrCost;
Xstatic UPcost, DOcost, LEcost, NDcost, CRcost, IMcost, EIcost;
Xstatic tcLineLen = 100;
Xstatic char *null;
Xstatic StatLen;
Xstatic insert;
Xstatic keypad;
X
Xstatic char *KeyCaps[] = {
X    "k0", "k1", "k2", "k3", "k4", "k5", "k6", "k7", "k8", "k9",
X    "kb", "kd", "kh", "kl", "ko", "kr", "ku",
X    0
X};
X
X/*
X * Altered this string and AddCap below to enable captoinfo on System V
X * to generate a terminfo entry.  Only get it if you define WANTCAP,
X * otherwise don't slow down shell fork, etc., by passing all this mess
X * in the environment.
X */
X#ifndef SYSV
Xstatic char TermcapConst[] = "TERMCAP=\
XSC|screen|VT 100/ANSI X3.64 virtual terminal|\\\n\
X\t:DO=\\E[%dB:LE=\\E[%dD:RI=\\E[%dC:UP=\\E[%dA:bs:bt=\\E[Z:\\\n\
X\t:cd=\\E[J:ce=\\E[K:cl=\\E[2J\\E[H:cm=\\E[%i%d;%dH:ct=\\E[3g:\\\n\
X\t:do=\\E[B:nd=\\E[C:pt:rc=\\E8:rs=\\Ec:sc=\\E7:st=\\EH:up=\\E[A:le=^H:";
X#else
X#ifdef WANTCAP
Xstatic char TermcapConst[] = "TERMCAP=\
XSC|screen|VT 100/ANSI X3.64 virtual terminal|\
X:DO=\\E[%dB:LE=\\E[%dD:RI=\\E[%dC:UP=\\E[%dA:bs:bt=\\E[Z:\
X:cd=\\E[J:ce=\\E[K:cl=\\E[2J\\E[H:cm=\\E[%i%d;%dH:ct=\\E[3g:\
X:do=\\E[B:nd=\\E[C:pt:rc=\\E8:rs=\\Ec:sc=\\E7:st=\\EH:up=\\E[A:le=^H:";
X#else
Xstatic char TermcapConst[] = "X=Y";
X#endif
X#endif
X
XInitTerm () {
X    register char *s;
X
X    if ((s = getenv ("TERM")) == 0)
X	Msg (0, "No TERM in environment.");
X    if (tgetent (tbuf, s) != 1)
X	Msg (0, "Cannot find termcap entry for %s.", s);
X    cols = tgetnum ("co");
X    rows = tgetnum ("li");
X    if (cols <= 0)
X	cols = 80;
X    if (rows <= 0)
X	rows = 24;
X    if (tgetflag ("hc"))
X	Msg (0, "You can't run screen on a hardcopy terminal.");
X    if (tgetflag ("os"))
X	Msg (0, "You can't run screen on a terminal that overstrikes.");
X    if (tgetflag ("ns"))
X	Msg (0, "Terminal must support scrolling.");
X    if (!(CL = tgetstr ("cl", &tp)))
X	Msg (0, "Clear screen capability required.");
X    if (!(CM = tgetstr ("cm", &tp)))
X	Msg (0, "Addressable cursor capability required.");
X    if (s = tgetstr ("ps", &tp))
X	PC = s[0];
X    flowctl = !tgetflag ("NF");
X    AM = tgetflag ("am");
X    if (tgetflag ("LP"))
X	AM = 0;
X    TI = tgetstr ("ti", &tp);
X    TE = tgetstr ("te", &tp);
X    if (!(BL = tgetstr ("bl", &tp)))
X	BL = "\007";
X    VB = tgetstr ("vb", &tp);
X    if (!(BC = tgetstr ("bc", &tp))) {
X	if (tgetflag ("bs"))
X	    BC = "\b";
X	else
X	    BC = tgetstr ("le", &tp);
X    }
X    if (!(CR = tgetstr ("cr", &tp)))
X	CR = "\r";
X    if (!(NL = tgetstr ("nl", &tp)))
X	NL = "\n";
X    IS = tgetstr ("is", &tp);
X    if (tgetnum ("sg") <= 0) {
X	US = tgetstr ("us", &tp);
X	UE = tgetstr ("ue", &tp);
X	SO = tgetstr ("so", &tp);
X	SE = tgetstr ("se", &tp);
X	MB = tgetstr ("mb", &tp);
X	MD = tgetstr ("md", &tp);
X	MH = tgetstr ("mh", &tp);
X	MR = tgetstr ("mr", &tp);
X	ME = tgetstr ("me", &tp);
X	/*
X	 * Does ME also reverse the effect of SO and/or US?  This is not
X	 * clearly specified by the termcap manual.
X	 * Anyway, we should at least look whether ME and SE/UE are equal:
X	 */
X	if (SE && UE && ME && (strcmp (SE, UE) == 0 || strcmp (ME, UE) == 0))
X	    UE = 0;
X	if (SE && ME && strcmp (SE, ME) == 0)
X	    SE = 0;
X    }
X    CE = tgetstr ("ce", &tp);
X    CD = tgetstr ("cd", &tp);
X    if (!(DO = tgetstr ("do", &tp)))
X	DO = NL;
X    UP = tgetstr ("up", &tp);
X    ND = tgetstr ("nd", &tp);
X    SR = tgetstr ("sr", &tp);
X    if (!(SF = tgetstr ("sf", &tp)))
X	SF = NL;
X    AL = tgetstr ("al", &tp);
X    DL = tgetstr ("dl", &tp);
X    CS = tgetstr ("cs", &tp);
X    DC = tgetstr ("dc", &tp);
X    IC = tgetstr ("ic", &tp);
X    CDC = tgetstr ("DC", &tp);
X    CDL = tgetstr ("DL", &tp);
X    CAL = tgetstr ("AL", &tp);
X    IM = tgetstr ("im", &tp);
X    EI = tgetstr ("ei", &tp);
X    if (tgetflag ("in"))
X	IC = IM = 0;
X    if (IC && IC[0] == '\0')
X	IC = 0;
X    if (IM && IM[0] == '\0')
X	IM = 0;
X    if (EI && EI[0] == '\0')
X	EI = 0;
X    KS = tgetstr ("ks", &tp);
X    KE = tgetstr ("ke", &tp);
X    ISO2022 = tgetflag ("G0");
X    PO = tgetstr ("po", &tp);
X    if (!(PF = tgetstr ("pf", &tp)))
X	PO = 0;
X    blank = malloc (cols);
X    null = malloc (cols);
X    OldImage = malloc (cols);
X    OldAttr = malloc (cols);
X    OldFont = malloc (cols);
X    if (!(blank && null && OldImage && OldAttr && OldFont))
X	Msg (0, "Out of memory.");
X    MakeBlankLine (blank, cols);
X    bzero (null, cols);
X    UPcost = CalcCost (UP);
X    DOcost = CalcCost (DO);
X    LEcost = CalcCost (BC);
X    NDcost = CalcCost (ND);
X    CRcost = CalcCost (CR);
X    IMcost = CalcCost (IM);
X    EIcost = CalcCost (EI);
X    PutStr (IS);
X    PutStr (TI);
X    PutStr (CL);
X}
X
XFinitTerm () {
X    PutStr (TE);
X    PutStr (IS);
X}
X
Xstatic AddCap (s) char *s; {
X    register n;
X
X#ifdef WANTCAP
X#ifndef SYSV
X    if (tcLineLen + (n = strlen (s)) > 55) {
X	strcat (Termcap, "\\\n\t:");
X	tcLineLen = 0;
X    }
X#endif
X    strcat (Termcap, s);
X    tcLineLen += n;
X#endif
X}
X
Xchar *MakeTermcap (aflag) {
X    char buf[1024];
X    register char **pp, *p;
X
X    strcpy (Termcap, TermcapConst);
X    sprintf (buf, "li#%d:co#%d:", rows, cols);
X    AddCap (buf);
X    if (VB)
X	AddCap ("vb=\\E[?5h\\E[?5l:");
X    if (US) {
X	AddCap ("us=\\E[4m:");
X	AddCap ("ue=\\E[24m:");
X    }
X    if (SO) {
X	AddCap ("so=\\E[3m:");
X	AddCap ("se=\\E[23m:");
X    }
X    if (MB)
X	AddCap ("mb=\\E[5m:");
X    if (MD)
X	AddCap ("md=\\E[1m:");
X    if (MH)
X	AddCap ("mh=\\E[2m:");
X    if (MR)
X	AddCap ("mr=\\E[7m:");
X    if (MB || MD || MH || MR)
X	AddCap ("me=\\E[0m:");
X    if ((CS && SR) || AL || CAL || aflag) {
X	AddCap ("sr=\\EM:");
X	AddCap ("al=\\E[L:");
X	AddCap ("AL=\\E[%dL:");
X    }
X    if (CS || DL || CDL || aflag) {
X	AddCap ("dl=\\E[M:");
X	AddCap ("DL=\\E[%dM:");
X    }
X    if (CS)
X	AddCap ("cs=\\E[%i%d;%dr:");
X    if (DC || CDC || aflag) {
X	AddCap ("dc=\\E[P:");
X	AddCap ("DC=\\E[%dP:");
X    }
X    if (IC || IM || aflag) {
X	AddCap ("im=\\E[4h:");
X	AddCap ("ei=\\E[4l:");
X	AddCap ("ic=:");
X	AddCap ("IC=\\E[%d@:");
X    }
X    if (KS)
X	AddCap ("ks=\\E=:");
X    if (KE)
X	AddCap ("ke=\\E>:");
X    if (ISO2022)
X	AddCap ("G0:");
X    if (PO) {
X	AddCap ("po=\\E[5i:");
X	AddCap ("pf=\\E[4i:");
X    }
X    for (pp = KeyCaps; *pp; ++pp)
X	if (p = tgetstr (*pp, &tp)) {
X	    MakeString (*pp, buf, p);
X	    AddCap (buf);
X	}
X    return Termcap;
X}
X
Xstatic MakeString (cap, buf, s) char *cap, *buf; register char *s; {
X    register char *p = buf;
X    register unsigned c;
X
X    *p++ = *cap++; *p++ = *cap; *p++ = '=';
X    while (c = *s++) {
X	switch (c) {
X	case '\033':
X	    *p++ = '\\'; *p++ = 'E'; break;
X	case ':':
X	    sprintf (p, "\\072"); p += 4; break;
X	case '^': case '\\':
X	    *p++ = '\\'; *p++ = c; break;
X	default:
X	    if (c >= 200) {
X		sprintf (p, "\\%03o", c & 0377); p += 4;
X	    } else if (c < ' ') {
X		*p++ = '^'; *p++ = c + '@';
X	    } else *p++ = c;
X	}
X    }
X    *p++ = ':'; *p = '\0';
X}
X
XActivate (wp) struct win *wp; {
X    RemoveStatus (wp);
X    curr = wp;
X    display = 1;
X    NewRendition (GlobalAttr, curr->LocalAttr);
X    GlobalAttr = curr->LocalAttr;
X    NewCharset (GlobalCharset, curr->charsets[curr->LocalCharset]);
X    GlobalCharset = curr->charsets[curr->LocalCharset];
X    if (CS)
X	PutStr (tgoto (CS, curr->bot, curr->top));
X    Redisplay ();
X    KeypadMode (curr->keypad);
X}
X
XResetScreen (p) register struct win *p; {
X    register i;
X
X    bzero (p->tabs, cols);
X    for (i = 8; i < cols; i += 8)
X	p->tabs[i] = 1;
X    p->wrap = 1;
X    p->origin = 0;
X    p->insert = 0;
X    p->vbwait = 0;
X    p->keypad = 0;
X    p->top = 0;
X    p->bot = rows - 1;
X    p->saved = 0;
X    p->LocalAttr = 0;
X    p->x = p->y = 0;
X    p->state = LIT;
X    p->StringType = NONE;
X    p->ss = 0;
X    p->LocalCharset = G0;
X    for (i = G0; i <= G3; i++)
X	p->charsets[i] = ASCII;
X}
X
XWriteString (wp, buf, len) struct win *wp; register char *buf; {
X    register c, intermediate = 0;
X
X    if (!len)
X	return;
X    curr = wp;
X    display = curr->active;
X    if (display)
X	RemoveStatus (wp);
X    do {
X	c = *buf++;
X	if (c == '\0' || c == '\177')
X	    continue;
XNextChar:
X	switch (curr->state) {
X	case PRIN:
X	    switch (c) {
X	    case '\033':
X		curr->state = PRINESC; break;
X	    default:
X		PrintChar (c);
X	    }
X	    break;
X	case PRINESC:
X	    switch (c) {
X	    case '[':
X		curr->state = PRINCSI; break;
X	    default:
X		PrintChar ('\033'); PrintChar (c);
X		curr->state = PRIN;
X	    }
X	    break;
X	case PRINCSI:
X	    switch (c) {
X	    case '4':
X		curr->state = PRIN4; break;
X	    default:
X		PrintChar ('\033'); PrintChar ('['); PrintChar (c);
X		curr->state = PRIN;
X	    }
X	    break;
X	case PRIN4:
X	    switch (c) {
X	    case 'i':
X		curr->state = LIT;
X		PrintFlush ();
X		break;
X	    default:
X		PrintChar ('\033'); PrintChar ('['); PrintChar ('4');
X		PrintChar (c);
X		curr->state = PRIN;
X	    }
X	    break;
X	case TERM:
X	    switch (c) {
X	    case '\\':
X		curr->state = LIT;
X		*(curr->stringp) = '\0';
X		if (curr->StringType == PM && display) {
X		    MakeStatus (curr->string, curr);
X		    if (status && len > 1) {
X			curr->outlen = len-1;
X			bcopy (buf, curr->outbuf, curr->outlen);
X			return;
X		    }
X		}
X		break;
X	    default:
X		curr->state = STR;
X		AddChar ('\033');
X		AddChar (c);
X	    }
X	    break;
X	case STR:
X	    switch (c) {
X	    case '\0':
X		break;
X	    case '\033':
X		curr->state = TERM; break;
X	    default:
X		AddChar (c);
X	    }
X	    break;
X	case ESC:
X	    switch (c) {
X	    case '[':
X		curr->NumArgs = 0;
X		intermediate = 0;
X		bzero ((char *)curr->args, MAXARGS * sizeof (int));
X		bzero (curr->GotArg, MAXARGS);
X		curr->state = CSI;
X		break;
X	    case ']':
X		StartString (OSC); break;
X	    case '_':
X		StartString (APC); break;
X	    case 'P':
X		StartString (DCS); break;
X	    case '^':
X		StartString (PM); break;
X	    default:
X		if (Special (c))
X		    break;
X		if (c >= ' ' && c <= '/') {
X		    intermediate = intermediate ? -1 : c;
X		} else if (c >= '0' && c <= '~') {
X		    DoESC (c, intermediate);
X		    curr->state = LIT;
X		} else {
X		    curr->state = LIT;
X		    goto NextChar;
X		}
X	    }
X	    break;
X	case CSI:
X	    switch (c) {
X	    case '0': case '1': case '2': case '3': case '4':
X	    case '5': case '6': case '7': case '8': case '9':
X		if (curr->NumArgs < MAXARGS) {
X		    curr->args[curr->NumArgs] =
X                        10 * curr->args[curr->NumArgs] + c - '0';
X		    curr->GotArg[curr->NumArgs] = 1;
X		}
X		break;
X	    case ';': case ':':
X		curr->NumArgs++; break;
X	    default:
X		if (Special (c))
X		    break;
X		if (c >= '@' && c <= '~') {
X		    curr->NumArgs++;
X		    DoCSI (c, intermediate);
X		    if (curr->state != PRIN)
X			curr->state = LIT;
X		} else if ((c >= ' ' && c <= '/') || (c >= '<' && c <= '?')) {
X		    intermediate = intermediate ? -1 : c;
X		} else {
X		    curr->state = LIT;
X		    goto NextChar;
X		}
X	    }
X	    break;
X	default:
X	    if (!Special (c)) {
X		if (c == '\033') {
X		    intermediate = 0;
X		    curr->state = ESC;
X		} else if (c < ' ') {
X		    break;
X		} else {
X		    if (curr->ss)
X			NewCharset (GlobalCharset, curr->charsets[curr->ss]);
X		    if (curr->x < cols-1) {
X			if (curr->insert) {
X			    InsertAChar (c);
X			} else {
X			    if (display)
X				putchar (c);
X			    SetChar (c);
X			}
X			curr->x++;
X		    } else if (curr->x == cols-1) {
X			SetChar (c);
X			if (!(AM && curr->y == curr->bot)) {
X			    if (display)
X				putchar (c);
X			    Goto (-1, -1, curr->y, curr->x);
X			}
X			curr->x++;
X		    } else {
X			if (curr->wrap) {
X			    Return ();
X			    LineFeed ();
X			    if (curr->insert) {
X				InsertAChar (c);
X			    } else {
X				if (display)
X				    putchar (c);
X				SetChar (c);
X			    }
X			    curr->x = 1;
X			} else curr->x = cols;
X		    }
X		    if (curr->ss) {
X			NewCharset (curr->charsets[curr->ss], GlobalCharset);
X			curr->ss = 0;
X		    }
X		}
X	    }
X	}
X    } while (--len);
X    curr->outlen = 0;
X    if (curr->state == PRIN)
X	PrintFlush ();
X}
X
Xstatic Special (c) register c; {
X    switch (c) {
X    case '\b':
X	BackSpace (); return 1;
X    case '\r':
X	Return (); return 1;
X    case '\n':
X	LineFeed (); return 1;
X    case '\007':
X	PutStr (BL);
X	if (!display)
X	    curr->bell = 1;
X	return 1;
X    case '\t':
X	ForwardTab (); return 1;
X    case '\017':   /* SI */
X	MapCharset (G0); return 1;
X    case '\016':   /* SO */
X	MapCharset (G1); return 1;
X    }
X    return 0;
X}
X
Xstatic DoESC (c, intermediate) {
X    switch (intermediate) {
X    case 0:
X	switch (c) {
X	case 'E':
X	    Return ();
X	    LineFeed ();
X	    break;
X	case 'D':
X	    LineFeed (); 
X	    break;
X	case 'M':
X	    ReverseLineFeed ();
X	    break;
X	case 'H':
X	    curr->tabs[curr->x] = 1;
X	    break;
X	case '7':
X	    SaveCursor ();
X	    break;
X	case '8':
X	    RestoreCursor ();
X	    break;
X	case 'c':
X	    ClearScreen ();
X	    Goto (curr->y, curr->x, 0, 0);
X	    NewRendition (GlobalAttr, 0);
X	    SetRendition (0);
X	    NewCharset (GlobalCharset, ASCII);
X	    GlobalCharset = ASCII;
X	    if (curr->insert)
X		InsertMode (0);
X	    if (curr->keypad)
X		KeypadMode (0);
X	    if (CS)
X		PutStr (tgoto (CS, rows-1, 0));
X	    ResetScreen (curr);
X	    break;
X	case '=':
X	    KeypadMode (1);
X	    curr->keypad = 1;
X	    break;
X	case '>':
X	    KeypadMode (0);
X	    curr->keypad = 0;
X	    break;
X	case 'n':   /* LS2 */
X	    MapCharset (G2); break;
X	case 'o':   /* LS3 */
X	    MapCharset (G3); break;
X	case 'N':   /* SS2 */
X	    if (GlobalCharset == curr->charsets[G2])
X		curr->ss = 0;
X	    else
X		curr->ss = G2;
X	    break;
X	case 'O':   /* SS3 */
X	    if (GlobalCharset == curr->charsets[G3])
X		curr->ss = 0;
X	    else
X		curr->ss = G3;
X	    break;
X	}
X	break;
X    case '#':
X	switch (c) {
X	case '8':
X	    FillWithEs ();
X	    break;
X	}
X	break;
X    case '(':
X	DesignateCharset (c, G0); break;
X    case ')':
X	DesignateCharset (c, G1); break;
X    case '*':
X	DesignateCharset (c, G2); break;
X    case '+':
X	DesignateCharset (c, G3); break;
X    }
X}
X
Xstatic DoCSI (c, intermediate) {
X    register i, a1 = curr->args[0], a2 = curr->args[1];
X
X    if (curr->NumArgs >= MAXARGS)
X	curr->NumArgs = MAXARGS;
X    for (i = 0; i < curr->NumArgs; ++i)
X	if (curr->args[i] == 0)
X	    curr->GotArg[i] = 0;
X    switch (intermediate) {
X    case 0:
X	switch (c) {
X	case 'H': case 'f':
X	    if (!curr->GotArg[0]) a1 = 1;
X	    if (!curr->GotArg[1]) a2 = 1;
X	    if (curr->origin)
X		a1 += curr->top;
X	    if (a1 < 1)
X		a1 = 1;
X	    if (a1 > rows)
X		a1 = rows;
X	    if (a2 < 1)
X		a2 = 1;
X	    if (a2 > cols)
X		a2 = cols;
X	    a1--; a2--;
X	    Goto (curr->y, curr->x, a1, a2);
X	    curr->y = a1;
X	    curr->x = a2;
X	    break;
X	case 'J':
X	    if (!curr->GotArg[0] || a1 < 0 || a1 > 2)
X		a1 = 0;
X	    switch (a1) {
X	    case 0:
X		ClearToEOS (); break;
X	    case 1:
X		ClearFromBOS (); break;
X	    case 2:
X		ClearScreen ();
X		Goto (0, 0, curr->y, curr->x);
X		break;
X	    }
X	    break;
X	case 'K':
X	    if (!curr->GotArg[0] || a1 < 0 || a1 > 2)
X		a1 %= 3;
X	    switch (a1) {
X	    case 0:
X		ClearToEOL (); break;
X	    case 1:
X		ClearFromBOL (); break;
X	    case 2:
X		ClearLine (); break;
X	    }
X	    break;
X	case 'A':
X	    CursorUp (curr->GotArg[0] ? a1 : 1);
X	    break;
X	case 'B':
X	    CursorDown (curr->GotArg[0] ? a1 : 1);
X	    break;
X	case 'C':
X	    CursorRight (curr->GotArg[0] ? a1 : 1);
X	    break;
X	case 'D':
X	    CursorLeft (curr->GotArg[0] ? a1 : 1);
X	    break;
X	case 'm':
X	    SelectRendition ();
X	    break;
X	case 'g':
X	    if (!curr->GotArg[0] || a1 == 0)
X		curr->tabs[curr->x] = 0;
X	    else if (a1 == 3)
X		bzero (curr->tabs, cols);
X	    break;
X	case 'r':
X	    if (!CS)
X		break;
X	    if (!curr->GotArg[0]) a1 = 1;
X	    if (!curr->GotArg[1]) a2 = rows;
X	    if (a1 < 1 || a2 > rows || a1 >= a2)
X		break;
X	    curr->top = a1-1;
X	    curr->bot = a2-1;
X	    PutStr (tgoto (CS, curr->bot, curr->top));
X	    if (curr->origin) {
X		Goto (-1, -1, curr->top, 0);
X		curr->y = curr->top;
X		curr->x = 0;
X	    } else {
X		Goto (-1, -1, 0, 0);
X		curr->y = curr->x = 0;
X	    }
X	    break;
X	case 'I':
X	    if (!curr->GotArg[0]) a1 = 1;
X	    while (a1--)
X		ForwardTab ();
X	    break;
X	case 'Z':
X	    if (!curr->GotArg[0]) a1 = 1;
X	    while (a1--)
X		BackwardTab ();
X	    break;
X	case 'L':
X	    InsertLine (curr->GotArg[0] ? a1 : 1);
X	    break;
X	case 'M':
X	    DeleteLine (curr->GotArg[0] ? a1 : 1);
X	    break;
X	case 'P':
X	    DeleteChar (curr->GotArg[0] ? a1 : 1);
X	    break;
X	case '@':
X	    InsertChar (curr->GotArg[0] ? a1 : 1);
X	    break;
X	case 'h':
X	    SetMode (1);
X	    break;
X	case 'l':
X	    SetMode (0);
X	    break;
X	case 'i':
X	    if (PO && curr->GotArg[0] && a1 == 5) {
X		curr->stringp = curr->string;
X		curr->state = PRIN;
X	    }
X	    break;
X	}
X	break;
X    case '?':
X	if (c != 'h' && c != 'l')
X	    break;
X	if (!curr->GotArg[0])
X	    break;
X	i = (c == 'h');
X	if (a1 == 5) {
X	    if (i) {
X		curr->vbwait = 1;
X	    } else {
X		if (curr->vbwait)
X		    PutStr (VB);
X		curr->vbwait = 0;
X	    }
X	} else if (a1 == 6) {
X	    curr->origin = i;
X	    if (curr->origin) {
X		Goto (curr->y, curr->x, curr->top, 0);
X		curr->y = curr->top;
X		curr->x = 0;
X	    } else {
X		Goto (curr->y, curr->x, 0, 0);
X		curr->y = curr->x = 0;
X	    }
X	} else if (a1 == 7) {
X	    curr->wrap = i;
X	}
X	break;
X    }
X}
X
Xstatic PutChar (c) {
X    putchar (c);
X}
X
Xstatic PutStr (s) char *s; {
X    if (display && s)
X	tputs (s, 1, PutChar);
X}
X
Xstatic CPutStr (s, c) char *s; {
X    if (display && s)
X	tputs (tgoto (s, 0, c), 1, PutChar);  /* XXX */
X}
X
Xstatic SetChar (c) register c; {
X    register struct win *p = curr;
X
X    p->image[p->y][p->x] = c;
X    p->attr[p->y][p->x] = p->LocalAttr;
X    p->font[p->y][p->x] = p->charsets[p->ss ? p->ss : p->LocalCharset];
X}
X
Xstatic StartString (type) enum string_t type; {
X    curr->StringType = type;
X    curr->stringp = curr->string;
X    curr->state = STR;
X}
X
Xstatic AddChar (c) {
X    if (curr->stringp >= curr->string+MAXSTR-1)
X	curr->state = LIT;
X    else
X	*(curr->stringp)++ = c;
X}
X
Xstatic PrintChar (c) {
X    if (curr->stringp >= curr->string+MAXSTR-1)
X	PrintFlush ();
X    else
X	*(curr->stringp)++ = c;
X}
X
Xstatic PrintFlush () {
X    if (curr->stringp > curr->string) {
X	tputs (PO, 1, PutChar);
X	(void) fflush (stdout);
X	(void) write (1, curr->string, curr->stringp - curr->string);
X	tputs (PF, 1, PutChar);
X	(void) fflush (stdout);
X	curr->stringp = curr->string;
X    }
X}
X
X/* Insert mode is a toggle on some terminals, so we need this hack:
X */
Xstatic InsertMode (on) {
X    if (on) {
X	if (!insert)
X	    PutStr (IM);
X    } else if (insert)
X	PutStr (EI);
X    insert = on;
X}
X
X/* ...and maybe keypad application mode is a toggle, too:
X */
Xstatic KeypadMode (on) {
X    if (on) {
X	if (!keypad)
X	    PutStr (KS);
X    } else if (keypad)
X	PutStr (KE);
X    keypad = on;
X}
X
Xstatic DesignateCharset (c, n) {
X    curr->ss = 0;
X    if (c == 'B')
X	c = ASCII;
X    if (curr->charsets[n] != c) {
X	curr->charsets[n] = c;
X	if (curr->LocalCharset == n) {
X	    NewCharset (GlobalCharset, c);
X	    GlobalCharset = c;
X	}
X    }
X}
X
Xstatic MapCharset (n) {
X    curr->ss = 0;
X    if (curr->LocalCharset != n) {
X	curr->LocalCharset = n;
X	NewCharset (GlobalCharset, curr->charsets[n]);
X	GlobalCharset = curr->charsets[n];
X    }
X}
X
Xstatic NewCharset (old, new) {
X    char buf[8];
X
X    if (old == new)
X	return;
X    if (ISO2022) {
X	sprintf (buf, "\033(%c", new == ASCII ? 'B' : new);
X	PutStr (buf);
X    }
X}
X
Xstatic SaveCursor () {
X    curr->saved = 1;
X    curr->Saved_x = curr->x;
X    curr->Saved_y = curr->y;
X    curr->SavedLocalAttr = curr->LocalAttr;
X    curr->SavedLocalCharset = curr->LocalCharset;
X    bcopy ((char *)curr->charsets, (char *)curr->SavedCharsets,
X	4 * sizeof (int));
X}
X
Xstatic RestoreCursor () {
X    if (curr->saved) {
X	curr->LocalAttr = curr->SavedLocalAttr;
X	NewRendition (GlobalAttr, curr->LocalAttr);
X	GlobalAttr = curr->LocalAttr;
X	bcopy ((char *)curr->SavedCharsets, (char *)curr->charsets,
X	    4 * sizeof (int));
X	curr->LocalCharset = curr->SavedLocalCharset;
X	NewCharset (GlobalCharset, curr->charsets[curr->LocalCharset]);
X	GlobalCharset = curr->charsets[curr->LocalCharset];
X	Goto (curr->y, curr->x, curr->Saved_y, curr->Saved_x);
X	curr->x = curr->Saved_x;
X	curr->y = curr->Saved_y;
X    }
X}
X
X/*ARGSUSED*/
Xstatic CountChars (c) {
X    StrCost++;
X}
X
Xstatic CalcCost (s) register char *s; {
X    if (s) {
X	StrCost = 0;
X	tputs (s, 1, CountChars);
X	return StrCost;
X    } else return EXPENSIVE;
X}
X
Xstatic Goto (y1, x1, y2, x2) {
X    register dy, dx;
X    register cost = 0;
X    register char *s;
X    int CMcost, n, m;
X    enum move_t xm = M_NONE, ym = M_NONE;
X
X    if (!display)
X	return;
X    if (x1 == cols || x2 == cols) {
X	if (x2 == cols) --x2;
X	goto DoCM;
X    }
X    dx = x2 - x1;
X    dy = y2 - y1;
X    if (dy == 0 && dx == 0)
X	return;
X    if (y1 == -1 || x1 == -1 || y2 >= curr->bot || y2 <= curr->top) {
XDoCM:
X	PutStr (tgoto (CM, x2, y2));
X	return;
X    }
X    CMcost = CalcCost (tgoto (CM, x2, y2));
X    if (dx > 0) {
X	if ((n = RewriteCost (y1, x1, x2)) < (m = dx * NDcost)) {
X	    cost = n;
X	    xm = M_RW;
X	} else {
X	    cost = m;
X	    xm = M_RI;
X	}
X    } else if (dx < 0) {
X	cost = -dx * LEcost;
X	xm = M_LE;
X    }
X    if (dx && (n = RewriteCost (y1, 0, x2) + CRcost) < cost) {
X	cost = n;
X	xm = M_CR;
X    }
X    if (cost >= CMcost)
X	goto DoCM;
X    if (dy > 0) {
X	cost += dy * DOcost;
X	ym = M_DO;
X    } else if (dy < 0) {
X	cost += -dy * UPcost;
X	ym = M_UP;
X    }
X    if (cost >= CMcost)
X	goto DoCM;
X    if (xm != M_NONE) {
X	if (xm == M_LE || xm == M_RI) {
X	    if (xm == M_LE) {
X		s = BC; dx = -dx;
X	    } else s = ND;
X	    while (dx-- > 0)
X		PutStr (s);
X	} else {
X	    if (xm == M_CR) {
X		PutStr (CR);
X		x1 = 0;
X	    }
X	    if (x1 < x2) {
X		if (curr->insert)
X		    InsertMode (0);
X		for (s = curr->image[y1]+x1; x1 < x2; x1++, s++)
X		    putchar (*s);
X		if (curr->insert)
X		    InsertMode (1);
X	    }
X	}
X    }
X    if (ym != M_NONE) {
X	if (ym == M_UP) {
X	    s = UP; dy = -dy;
X	} else s = DO;
X	while (dy-- > 0)
X	    PutStr (s);
X    }
X}
X
Xstatic RewriteCost (y, x1, x2) {
X    register cost, dx;
X    register char *p = curr->attr[y]+x1, *f = curr->font[y]+x1;
X
X    if (AM && y == rows-1 && x2 == cols-1)
X	return EXPENSIVE;
X    cost = dx = x2 - x1;
X    if (dx == 0)
X	return 0;
X    if (curr->insert)
X	cost += EIcost + IMcost;
X    do {
X	if (*p++ != GlobalAttr || *f++ != GlobalCharset)
X	    return EXPENSIVE;
X    } while (--dx);
X    return cost;
X}
X
Xstatic BackSpace () {
X    if (curr->x > 0) {
X	if (curr->x < cols) {
X	    if (BC)
X		PutStr (BC);
X	    else
X		Goto (curr->y, curr->x, curr->y, curr->x-1);
X	}
X	curr->x--;
X    }
X}
X
Xstatic Return () {
X    if (curr->x > 0) {
X	curr->x = 0;
X	PutStr (CR);
X    }
X}
X
Xstatic LineFeed () {
X    if (curr->y == curr->bot) {
X	ScrollUpMap (curr->image);
X	ScrollUpMap (curr->attr);
X	ScrollUpMap (curr->font);
X    } else if (curr->y < rows-1) {
X	curr->y++;
X    }
X    PutStr (NL);
X}
X
Xstatic ReverseLineFeed () {
X    if (curr->y == curr->top) {
X	ScrollDownMap (curr->image);
X	ScrollDownMap (curr->attr);
X	ScrollDownMap (curr->font);
X	if (SR) {
X	    PutStr (SR);
X	} else if (AL) {
X	    Goto (curr->top, curr->x, curr->top, 0);
X	    PutStr (AL);
X	    Goto (curr->top, 0, curr->top, curr->x);
X	} else Redisplay ();
X    } else if (curr->y > 0) {
X	CursorUp (1);
X    }
X}
X
Xstatic InsertAChar (c) {
X    register y = curr->y, x = curr->x;
X
X    if (x == cols)
X	x--;
X    bcopy (curr->image[y], OldImage, cols);
X    bcopy (curr->attr[y], OldAttr, cols);
X    bcopy (curr->font[y], OldFont, cols);
X    bcopy (curr->image[y]+x, curr->image[y]+x+1, cols-x-1);
X    bcopy (curr->attr[y]+x, curr->attr[y]+x+1, cols-x-1);
X    bcopy (curr->font[y]+x, curr->font[y]+x+1, cols-x-1);
X    SetChar (c);
X    if (!display)
X	return;
X    if (IC || IM) {
X	if (!curr->insert)
X	    InsertMode (1);
X	PutStr (IC);
X	putchar (c);
X	if (!curr->insert)
X	    InsertMode (0);
X    } else {
X	RedisplayLine (OldImage, OldAttr, OldFont, y, x, cols-1);
X	++x;
X	Goto (y, last_x, y, x);
X    }
X}
X
Xstatic InsertChar (n) {
X    register i, y = curr->y, x = curr->x;
X
X    if (x == cols)
X	return;
X    bcopy (curr->image[y], OldImage, cols);
X    bcopy (curr->attr[y], OldAttr, cols);
X    bcopy (curr->font[y], OldFont, cols);
X    if (n > cols-x)
X	n = cols-x;
X    bcopy (curr->image[y]+x, curr->image[y]+x+n, cols-x-n);
X    bcopy (curr->attr[y]+x, curr->attr[y]+x+n, cols-x-n);
X    bcopy (curr->font[y]+x, curr->font[y]+x+n, cols-x-n);
X    ClearInLine (0, y, x, x+n-1);
X    if (!display)
X	return;
X    if (IC || IM) {
X	if (!curr->insert)
X	    InsertMode (1);
X	for (i = n; i; i--) {
X	    PutStr (IC);
X	    putchar (' ');
X	}
X	if (!curr->insert)
X	    InsertMode (0);
X	Goto (y, x+n, y, x);
X    } else {
X	RedisplayLine (OldImage, OldAttr, OldFont, y, x, cols-1);
X	Goto (y, last_x, y, x);
X    }
X}
X
Xstatic DeleteChar (n) {
X    register i, y = curr->y, x = curr->x;
X
X    if (x == cols)
X	return;
X    bcopy (curr->image[y], OldImage, cols);
X    bcopy (curr->attr[y], OldAttr, cols);
X    bcopy (curr->font[y], OldFont, cols);
X    if (n > cols-x)
X	n = cols-x;
X    bcopy (curr->image[y]+x+n, curr->image[y]+x, cols-x-n);
X    bcopy (curr->attr[y]+x+n, curr->attr[y]+x, cols-x-n);
X    bcopy (curr->font[y]+x+n, curr->font[y]+x, cols-x-n);
X    ClearInLine (0, y, cols-n, cols-1);
X    if (!display)
X	return;
X    if (CDC && !(n == 1 && DC)) {
X	CPutStr (CDC, n);
X    } else if (DC) {
X	for (i = n; i; i--)
X	    PutStr (DC);
X    } else {
X	RedisplayLine (OldImage, OldAttr, OldFont, y, x, cols-1);
X	Goto (y, last_x, y, x);
X    }
X}
X
Xstatic DeleteLine (n) {
X    register i, old = curr->top;
X
X    if (n > curr->bot-curr->y+1)
X	n = curr->bot-curr->y+1;
X    curr->top = curr->y;
X    for (i = n; i; i--) {
X	ScrollUpMap (curr->image);
X	ScrollUpMap (curr->attr);
X	ScrollUpMap (curr->font);
X    }
X    if (DL || CDL) {
X	Goto (curr->y, curr->x, curr->y, 0);
X	if (CDL && !(n == 1 && DL)) {
X	    CPutStr (CDL, n);
X	} else {
X	    for (i = n; i; i--)
X		PutStr (DL);
X	}
X	Goto (curr->y, 0, curr->y, curr->x);
X    } else if (CS) {
X	PutStr (tgoto (CS, curr->bot, curr->top));
X	Goto (-1, -1, curr->bot, 0);
X	for (i = n; i; i--)
X	    PutStr (SF);
X	PutStr (tgoto (CS, curr->bot, old));
X	Goto (-1, -1, curr->y, curr->x);
X    } else Redisplay ();
X    curr->top = old;
X}
X
Xstatic InsertLine (n) {
X    register i, old = curr->top;
X
X    if (n > curr->bot-curr->y+1)
X	n = curr->bot-curr->y+1;
X    curr->top = curr->y;
X    for (i = n; i; i--) {
X	ScrollDownMap (curr->image);
X	ScrollDownMap (curr->attr);
X	ScrollDownMap (curr->font);
X    }
X    if (AL || CAL) {
X	Goto (curr->y, curr->x, curr->y, 0);
X	if (CAL && !(n == 1 && AL)) {
X	    CPutStr (CAL, n);
X	} else {
X	    for (i = n; i; i--)
X		PutStr (AL);
X	}
X	Goto (curr->y, 0, curr->y, curr->x);
X    } else if (CS && SR) {
X	PutStr (tgoto (CS, curr->bot, curr->top));
X	Goto (-1, -1, curr->y, 0);
X	for (i = n; i; i--)
X	    PutStr (SR);
X	PutStr (tgoto (CS, curr->bot, old));
X	Goto (-1, -1, curr->y, curr->x);
X    } else Redisplay ();
X    curr->top = old;
X}
X
Xstatic ScrollUpMap (pp) char **pp; {
X    register char *tmp = pp[curr->top];
X
X    bcopy ((char *)(pp+curr->top+1), (char *)(pp+curr->top),
X	(curr->bot-curr->top) * sizeof (char *));
X    if (pp == curr->image)
X	bclear (tmp, cols);
X    else
X	bzero (tmp, cols);
X    pp[curr->bot] = tmp;
X}
X
Xstatic ScrollDownMap (pp) char **pp; {
X    register char *tmp = pp[curr->bot];
X
X    bcopy ((char *)(pp+curr->top), (char *)(pp+curr->top+1),
X	(curr->bot-curr->top) * sizeof (char *));
X    if (pp == curr->image)
X	bclear (tmp, cols);
X    else
X	bzero (tmp, cols);
X    pp[curr->top] = tmp;
X}
X
Xstatic ForwardTab () {
X    register x = curr->x;
X
X    if (curr->tabs[x] && x < cols-1)
X	++x;
X    while (x < cols-1 && !curr->tabs[x])
X	x++;
X    Goto (curr->y, curr->x, curr->y, x);
X    curr->x = x;
X}
X
Xstatic BackwardTab () {
X    register x = curr->x;
X
X    if (curr->tabs[x] && x > 0)
X	x--;
X    while (x > 0 && !curr->tabs[x])
X	x--;
X    Goto (curr->y, curr->x, curr->y, x);
X    curr->x = x;
X}
X
Xstatic ClearScreen () {
X    register i;
X
X    PutStr (CL);
X    for (i = 0; i < rows; ++i) {
X	bclear (curr->image[i], cols);
X	bzero (curr->attr[i], cols);
X	bzero (curr->font[i], cols);
X    }
X}
X
Xstatic ClearFromBOS () {
X    register n, y = curr->y, x = curr->x;
X
X    for (n = 0; n < y; ++n)
X	ClearInLine (1, n, 0, cols-1);
X    ClearInLine (1, y, 0, x);
X    Goto (curr->y, curr->x, y, x);
X    curr->y = y; curr->x = x;
X}
X
Xstatic ClearToEOS () {
X    register n, y = curr->y, x = curr->x;
X
X    if (CD)
X	PutStr (CD);
X    ClearInLine (!CD, y, x, cols-1);
X    for (n = y+1; n < rows; n++)
X	ClearInLine (!CD, n, 0, cols-1);
X    Goto (curr->y, curr->x, y, x);
X    curr->y = y; curr->x = x;
X}
X
Xstatic ClearLine () {
X    register y = curr->y, x = curr->x;
X
X    ClearInLine (1, y, 0, cols-1);
X    Goto (curr->y, curr->x, y, x);
X    curr->y = y; curr->x = x;
X}
X
Xstatic ClearToEOL () {
X    register y = curr->y, x = curr->x;
X
X    ClearInLine (1, y, x, cols-1);
X    Goto (curr->y, curr->x, y, x);
X    curr->y = y; curr->x = x;
X}
X
Xstatic ClearFromBOL () {
X    register y = curr->y, x = curr->x;
X
X    ClearInLine (1, y, 0, x);
X    Goto (curr->y, curr->x, y, x);
X    curr->y = y; curr->x = x;
X}
X
Xstatic ClearInLine (displ, y, x1, x2) {
X    register i, n;
X
X    if (x1 == cols) x1--;
X    if (x2 == cols) x2--;
X    if (n = x2 - x1 + 1) {
X	bclear (curr->image[y]+x1, n);
X	bzero (curr->attr[y]+x1, n);
X	bzero (curr->font[y]+x1, n);
X	if (displ && display) {
X	    if (x2 == cols-1 && CE) {
X		Goto (curr->y, curr->x, y, x1);
X		curr->y = y; curr->x = x1;
X		PutStr (CE);
X		return;
X	    }
X	    if (y == rows-1 && AM)
X		--n;
X	    if (n == 0)
X		return;
X	    SaveAttr (0);
X	    Goto (curr->y, curr->x, y, x1);
X	    for (i = n; i > 0; i--)
X		putchar (' ');
X	    curr->y = y; curr->x = x1 + n;
X	    RestoreAttr (0);
X	}
X    }
X}
X
Xstatic CursorRight (n) register n; {
X    register x = curr->x;
X
X    if (x == cols)
X	return;
X    if ((curr->x += n) >= cols)
X	curr->x = cols-1;
X    Goto (curr->y, x, curr->y, curr->x);
X}
X
Xstatic CursorUp (n) register n; {
X    register y = curr->y;
X
X    if ((curr->y -= n) < curr->top)
X	curr->y = curr->top;
X    Goto (y, curr->x, curr->y, curr->x);
X}
X
Xstatic CursorDown (n) register n; {
X    register y = curr->y;
X
X    if ((curr->y += n) > curr->bot)
X	curr->y = curr->bot;
X    Goto (y, curr->x, curr->y, curr->x);
X}
X
Xstatic CursorLeft (n) register n; {
X    register x = curr->x;
X
X    if ((curr->x -= n) < 0)
X	curr->x = 0;
X    Goto (curr->y, x, curr->y, curr->x);
X}
X
Xstatic SetMode (on) {
X    register i;
X
X    for (i = 0; i < curr->NumArgs; ++i) {
X	switch (curr->args[i]) {
X	case 4:
X	    curr->insert = on;
X	    InsertMode (on);
X	    break;
X	}
X    }
X}
X
Xstatic SelectRendition () {
X    register i, old = GlobalAttr;
X
X    if (curr->NumArgs == 0)
X	SetRendition (0);
X    else for (i = 0; i < curr->NumArgs; ++i)
X	SetRendition (curr->args[i]);
X    NewRendition (old, GlobalAttr);
X}
X
Xstatic SetRendition (n) register n; {
X    switch (n) {
X    case 0:
X	GlobalAttr = 0; break;
X    case 1:
X	GlobalAttr |= A_BD; break;
X    case 2:
X	GlobalAttr |= A_DI; break;
X    case 3:
X	GlobalAttr |= A_SO; break;
X    case 4:
X	GlobalAttr |= A_US; break;
X    case 5:
X	GlobalAttr |= A_BL; break;
X    case 7:
X	GlobalAttr |= A_RV; break;
X    case 22:
X	GlobalAttr &= ~(A_BD|A_SO|A_DI); break;
X    case 23:
X	GlobalAttr &= ~A_SO; break;
X    case 24:
X	GlobalAttr &= ~A_US; break;
X    case 25:
X	GlobalAttr &= ~A_BL; break;
X    case 27:
X	GlobalAttr &= ~A_RV; break;
X    }
X    curr->LocalAttr = GlobalAttr;
X}
X
Xstatic NewRendition (old, new) register old, new; {
X    register i;
X
X    if (old == new)
X	return;
X    for (i = 1; i <= A_MAX; i <<= 1) {
X	if ((old & i) && !(new & i)) {
X	    PutStr (UE);
X	    PutStr (SE);
X	    PutStr (ME);
X	    if (new & A_US) PutStr (US);
X	    if (new & A_SO) PutStr (SO);
X	    if (new & A_BL) PutStr (MB);
X	    if (new & A_BD) PutStr (MD);
X	    if (new & A_DI) PutStr (MH);
X	    if (new & A_RV) PutStr (MR);
X	    return;
X	}
X    }
X    if ((new & A_US) && !(old & A_US))
X	PutStr (US);
X    if ((new & A_SO) && !(old & A_SO))
X	PutStr (SO);
X    if ((new & A_BL) && !(old & A_BL))
X	PutStr (MB);
X    if ((new & A_BD) && !(old & A_BD))
X	PutStr (MD);
X    if ((new & A_DI) && !(old & A_DI))
X	PutStr (MH);
X    if ((new & A_RV) && !(old & A_RV))
X	PutStr (MR);
X}
X
Xstatic SaveAttr (newattr) {
X    NewRendition (GlobalAttr, newattr);
X    NewCharset (GlobalCharset, ASCII);
X    if (curr->insert)
X	InsertMode (0);
X}
X
Xstatic RestoreAttr (oldattr) {
X    NewRendition (oldattr, GlobalAttr);
X    NewCharset (ASCII, GlobalCharset);
X    if (curr->insert)
X	InsertMode (1);
X}
X
Xstatic FillWithEs () {
X    register i;
X    register char *p, *ep;
X
X    curr->y = curr->x = 0;
X    SaveAttr (0);
X    for (i = 0; i < rows; ++i) {
X	bzero (curr->attr[i], cols);
X	bzero (curr->font[i], cols);
X	p = curr->image[i];
X	ep = p + cols;
X	for ( ; p < ep; ++p)
X	    *p = 'E';
X    }
X    RestoreAttr (0);
X    Redisplay ();
X}
X
Xstatic Redisplay () {
X    register i;
X
X    PutStr (CL);
X    TmpAttr = GlobalAttr;
X    TmpCharset = GlobalCharset;
X    InsertMode (0);
X    last_x = last_y = 0;
X    for (i = 0; i < rows; ++i)
X	DisplayLine (blank, null, null, curr->image[i], curr->attr[i],
X	    curr->font[i], i, 0, cols-1);
X    if (curr->insert)
X	InsertMode (1);
X    NewRendition (TmpAttr, GlobalAttr);
X    NewCharset (TmpCharset, GlobalCharset);
X    Goto (last_y, last_x, curr->y, curr->x);
X}
X
Xstatic DisplayLine (os, oa, of, s, as, fs, y, from, to)
X	register char *os, *oa, *of, *s, *as, *fs; {
X    register i, x, a, f;
X
X    if (to == cols)
X	--to;
X    if (AM && y == rows-1 && to == cols-1)
X	--to;
X    a = TmpAttr;
X    f = TmpCharset;
X    for (x = i = from; i <= to; ++i, ++x) {
X	if (s[i] == os[i] && as[i] == oa[i] && as[i] == a
X		          && of[i] == fs[i] && fs[i] == f)
X	    continue;
X	Goto (last_y, last_x, y, x);
X	last_y = y;
X	last_x = x;
X	if ((a = as[i]) != TmpAttr) {
X	    NewRendition (TmpAttr, a);
X	    TmpAttr = a;
X	}
X	if ((f = fs[i]) != TmpCharset) {
X	    NewCharset (TmpCharset, f);
X	    TmpCharset = f;
X	}
X	putchar (s[i]);
X	last_x++;
X    }
X}
X
Xstatic RedisplayLine (os, oa, of, y, from, to) char *os, *oa, *of; {
X    if (curr->insert)
X	InsertMode (0);
X    NewRendition (GlobalAttr, 0);
X    TmpAttr = 0;
X    NewCharset (GlobalCharset, ASCII);
X    TmpCharset = ASCII;
X    last_y = y;
X    last_x = from;
X    DisplayLine (os, oa, of, curr->image[y], curr->attr[y],
X	curr->font[y], y, from, to);
X    NewRendition (TmpAttr, GlobalAttr);
X    NewCharset (TmpCharset, GlobalCharset);
X    if (curr->insert)
X	InsertMode (1);
X}
X
Xstatic MakeBlankLine (p, n) register char *p; register n; {
X    do *p++ = ' ';
X    while (--n);
X}
X
XMakeStatus (msg, wp) char *msg; struct win *wp; {
X    struct win *ocurr = curr;
X    int odisplay = display;
X    register char *s, *t;
X    register max = AM ? cols-1 : cols;
X
X    for (s = t = msg; *s && t - msg < max; ++s)
X	if (isprint(*s)) *t++ = *s;
X    *t = '\0';
X    curr = wp;
X    display = 1;
X    if (status) {
X	if (time ((time_t *)0) - TimeDisplayed < 2)
X	    sleep (2);
X	RemoveStatus (wp);
X    }
X    if (t > msg) {
X	status = 1;
X	StatLen = t - msg;
X	Goto (curr->y, curr->x, rows-1, 0);
X	SaveAttr (A_SO);
X	printf ("%s", msg);
X	RestoreAttr (A_SO);
X	(void) fflush (stdout);
X	time (&TimeDisplayed);
X    }
X    curr = ocurr;
X    display = odisplay;
X}
X
XRemoveStatus (p) struct win *p; {
X    struct win *ocurr = curr;
X    int odisplay = display;
X
X    if (!status)
X	return;
X    status = 0;
X    curr = p;
X    display = 1;
X    Goto (-1, -1, rows-1, 0);
X    RedisplayLine (null, null, null, rows-1, 0, StatLen);
X    Goto (rows-1, last_x, curr->y, curr->x);
X    curr = ocurr;
X    display = odisplay;
X}
________This_Is_The_END________
if test `wc -l < ansi.c` -ne 1680; then
	echo 'shar: ansi.c was damaged during transit (should have been 1680 bytes)'
fi
fi		; : end of overwriting check
exit 0