[net.sources] BSD screen manager -- Part 2 of 2

net@tub.UUCP (03/20/87)

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	screen.h
#	ansi.c
# This archive created: Fri Mar 20 18:20:49 1987
export PATH; PATH=/bin:$PATH
echo shar: extracting "'screen.h'" '(1430 characters)'
if test -f 'screen.h'
then
	echo shar: will not over-write existing file "'screen.h'"
else
cat << \SHAR_EOF > 'screen.h'
/* 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.
 */

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;
    char outbuf[IOSIZE];
    int outlen;
    char cmd[MAXSTR];
    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;
};
SHAR_EOF
if test 1430 -ne "`wc -c < 'screen.h'`"
then
	echo shar: error transmitting "'screen.h'" '(should have been 1430 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'ansi.c'" '(29375 characters)'
if test -f 'ansi.c'
then
	echo shar: will not over-write existing file "'ansi.c'"
else
cat << \SHAR_EOF > 'ansi.c'
/* 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.
 */

char AnsiVersion[] = "ansi 1.0c 18-Mar-87";

#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;
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 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];
    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);
    if ((blank = malloc (cols)) == 0 || (OldImage = malloc (cols)) == 0 ||
	    (OldAttr = malloc (cols)) == 0 || (null = malloc (cols)) == 0)
	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;
    PutStr (curr->insert ? IM : EI);
    PutStr (curr->keypad ? KS : KE);
    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 (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); 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)
		PutStr (EI);
	    if (curr->keypad)
		PutStr (KE);
	    if (CS)
		PutStr (tgoto (CS, rows-1, 0));
	    ResetScreen (curr);
	    break;
	case '=':
	    PutStr (KS);
	    curr->keypad = 1;
	    break;
	case '>':
	    PutStr (KE);
	    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 > rows)
		a1 = 1;
	    if (a2 < 1 || a2 > cols)
		a2 = 1;
	    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;
}

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)
		    PutStr (EI);
		for (s = curr->image[y1]+x1; x1 < x2; x1++, s++)
		    putchar (*s);
		if (curr->insert)
		    PutStr (IM);
	    }
	}
    }
    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)
	    PutStr (IM);
	PutStr (IC);
	putchar (c);
	if (!curr->insert)
	    PutStr (EI);
    } 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)
	    PutStr (IM);
	for (i = n; i; i--) {
	    PutStr (IC);
	    putchar (' ');
	}
	if (!curr->insert)
	    PutStr (EI);
	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;
	    PutStr (on ? IM : EI);
	    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)
	PutStr (EI);
}

static RestoreAttr () {
    NewRendition (0, GlobalAttr);
    if (curr->insert)
	PutStr (IM);
}

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, a, x;

    if (to == cols)
	--to;
    if (AM && y == rows-1 && to == cols-1)
	--to;
    for (x = i = from; i <= to; ++i, ++x) {
	if (s[i] == os[i] && as[i] == oa[i])
	    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; {
    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);
}

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;

    for (s = t = msg; *s && t - msg < cols; ++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;
	Goto (curr->y, curr->x, rows-1, 0);
	NewRendition (GlobalAttr, A_SO);
	printf ("%s", msg);
	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, cols-1);
    Goto (rows-1, last_x, curr->y, curr->x);
    curr = ocurr;
    display = odisplay;
}
SHAR_EOF
if test 29375 -ne "`wc -c < 'ansi.c'`"
then
	echo shar: error transmitting "'ansi.c'" '(should have been 29375 characters)'
fi
fi # end of overwriting check
#	End of shell archive
exit 0