/************************************************************************
 * This program is Copyright (C) 1986 by Jonathan Payne.  JOVE is	*
 * provided to you without charge, and with no warranty.  You may give	*
 * away copies of JOVE, including sources, provided that this notice is *
 * included in all the files.						*
 ************************************************************************/
/*
 * Modified: Sep-Oct 1988, by Tom Hageman [TRH]:
 *
 * extensive fine-tuning;
 *
 * fixed cursor boundary bugs: invariants now:
 *    -	cursend points one beyond last valid position in current line,
 *    - screenline.s_length points one beyond last modified position in line
 *
 * Ins/Del line: in-place swap of screen lines allows us to get rid
 * of Savelines array.
 *
 * explicit handling of standout mode lines (see also disp.c), instead
 * of ORing 8th bit if standout. This both facititates transition to
 * 8 bit characters (some day...), AND alleviates the XS glitch since
 * standout lines are explicitly erased. And since mode lines are the
 * only place where standout is used, no real harm is done. This also
 * allows us to collect the largely duplicate code of swrite() and
 * BufSwrite() in a single routine.
 *
 * Dec 1988 [TRH]:
 *	revised cursor addressing (tabs in particular)
 * Feb 1990 [TRH]:
 *	add color
 */

#define NO_PROCDECL		/* kludge for teensy pdp11 compiler */

#include "jove.h"

RCS("$Id: screen.c,v 14.32.0.9 1994/05/22 16:21:16 tom Exp tom $")

#include "io.h"
#include "ctype.h"
#define Extern	public	/* to define the variables in "screen.h" */
#include "screen.h"
#include "termcap.h"

void
no_op()	{ /* dummy routine */ }

/* These point to functions that make the terminal do the actual line
   insertion/deletion, or to `no_op' if I/D line is not supported. */
void	(*TTins_line)__(( int _(top), int _(bottom), int _(num) )) = no_op,
	(*TTdel_line)__(( int _(top), int _(bottom), int _(num) )) = no_op;

/* Output a character to the terminal at the current (virtual) cursor pos.
   Called whenever a difference is detected between the desired and current
   screen.  This is called through the pointer `TTputc', so that we can
   setup various "delayed-output" actions that are to be executed only if
   a screen update is actually needed, by setting `TTputc' to another
   function.

   This all grew rather convoluted over time, so here is an overview of
   functions that can hook into `TTputc':

     dosputc	- Set physical cursor to i_pos, output character.

     COFFputc	- Count characters output since the last call to `scursoff'.
	          When a certain (small) limit is reached, turn cursor off.
	          Set up by `scursoff'.
#ifdef COLOR
     COLORputc	- Set to requested color if different from `CurrColor'.
		  Set up by `req_color'.
#endif
     SOputc	- Start highlighting (originally: standout only.)
		  Set up by `standout'.

   Each function in this list calls the one listed before it, so `dosputc'
   is ultimately invoked to do the "real" work.  When such a hooked-in
   function has done its dastardly deeds, it unhooks itself from `TTputc'.

   {NOTE: the order of this list depends on the order in which `scursoff',
    `req_color' and `standout' can be invoked in the redisplay process.} */
private void
	dosputc __(( _PI_(char) _(c) )),
	COFFputc __(( _PI_(char) _(c) )),
#ifdef COLOR
	COLORputc __(( _PI_(char) _(c) )),
#endif
	SOputc __(( _PI_(char) _(c) ));
private void
	(*TTputc)__(( _PI_(char) _(c) )) = dosputc;

DEF_INT( "internal-tabstop", tabstop, V_BASE10|V_CLRSCREEN ) = 8;

#ifdef COLOR
#   ifdef TERMCAP
/* Set this if terminal erases a line with a fixed background color, as
   opposed to using the current background color.  Ideally this should be
   determined from the termcap entry... */
DEF_INT( "color-erase-glitch", ColorEraseGlitch, V_BOOL|V_CLRSCREEN ) _IF(def TERMCAP)_IF(def COLOR)_IF(def PRIVATE) ZERO;

/* Set this if terminal attributes affect the current color in an
   unpredictable way.  Ideally this should be determined from the termcap
   entry... */
DEF_INT( "color-standout-glitch", ColorStandoutGlitch, V_BOOL|V_CLRSCREEN ) _IF(def TERMCAP)_IF(def COLOR)_IF(def PRIVATE) ZERO;
#   else
#	ifndef ColorEraseGlitch
#	    define ColorEraseGlitch	0
#	endif
#	ifndef ColorStandoutGlitch
#	    define ColorStandoutGlitch	0
#	endif
#   endif

/* Stuff for delayed color setting. */
private int	ReqColor;
private void	set_req_color __((void));
#endif /* COLOR */

#ifdef ID_CHAR
private int
	IN_INSmode ZERO,
	DClen ZERO,
	MDClen = 9999,
	IClen ZERO,
	MIClen = 9999,
	IMlen ZERO,
	EIlen ZERO,
	CElen ZERO;
#endif

void
make_scr()
{
	register size_t i;
	register struct screenline	*ns;
	register char	*nsp;

	/*
	 * [TRH] This has been changed. We now allocate one big chunk
	 * for all the data structures and divide it ourselves.
	 * This simplifies our administration somewhat. (and saves code...)
	 */
#ifdef RESHAPING
	if (DesiredScreen)
		free((void_*) DesiredScreen);
#endif
	i = LI * (CO + sizeof(struct scrimage) * 2 + sizeof(struct screenline));
	if ((nsp = malloc(i)) == NULL) {
		message("Cannot malloc screen!");
		finish(1);
	}
	bzero(nsp, i);
	i = LI * sizeof(struct scrimage);
	DesiredScreen = (struct scrimage *) nsp;
	nsp += i;
	PhysScreen = (struct scrimage *) nsp;
	nsp += i;
	Screen = ns = (struct screenline *) nsp;
	nsp += LI * sizeof(struct screenline);

	i = LI;
	do {
		ns->s_line = nsp;
		nsp += CO;
		ns->s_length = nsp;	 	/* End of Line */
		ns++;
	} while (--i);

	cl_scr(0);
}

/*
 * Invalidate current line.
 */
void
v_inval()
{
	register char	*cp = cursor;

	while (cp < cursend)
		*cp++ = '\0';

	Curline->s_length = cp;	/* i.e. cursend */
	Curimage->s_id = -1;
}

private void clrline __(( char *_(cp1), char *_(cp2) ));
private void
clrline(cp1, cp2)
register char	*cp1,
		*cp2;
{
	while (cp1 < cp2)
		*cp1++ = ' ';
}

#define sputc(c)	{ if (*cursor != (char)(c)) (*TTputc)(c); cursor++, i_col++; }
#define soutputc(c)	if (--n <= 0) break; else sputc(c)

void
cl_eol()
{
	register char	*end = Curline->s_length;

	if (cursor >= end || cursor >= cursend)
		return;

#ifdef COLOR
#   if 0 /* This optimization can cause the cursor's color to be different
	    from the line's foreground color, which is distracting. */
	if (CurrColor <= 0 || BG_COLOR(ReqColor) != BG_COLOR(CurrColor)) {
		set_color(ReqColor);
	}
#   else
	set_color(ReqColor);
#   endif
#endif
#ifdef DUMBTERMS
	if (!CE
#   ifdef COLOR
	    || (ColorEraseGlitch && BG_COLOR(CurrColor) != BG_COLOR(DefColor))
#   endif
	    ) {
		/* Ugh.  The slow way for dumb terminals. */
		register char *savecp = cursor;

		while (cursor < end)
			sputc(' ');
		cursor = savecp;
	}
	else	/* the next compound statement */
#endif
		Placur(i_line, i_col),
		putp(CE),
		clrline(cursor, end);

	Curline->s_length = cursor;
}

void
cl_scr(doit)
{
	register struct screenline	*sp = Screen;
	register struct scrimage	*phys_p = PhysScreen;
	register int			i = LI;

	do {
		clrline(sp->s_line, sp->s_length);
		sp->s_length = sp->s_line;
		phys_p->s_id = 0;
		phys_p->s_flags = 0;
#ifdef COLOR
		phys_p->s_color = 0;
#endif
		sp++;
		phys_p++;
	} while (--i);

	if (doit) {
		CapCol = CapLine = 0;
		set_color(0);
		putpad(CL, LI);
		updmesg();
	}
}

/*
 * Clear the line (to eol) if it stands out.
 * For XS braindamaged terminals erasing is the only
 * way to get rid of a standout field. Also it makes the
 * physical screen consistent with our idea of it.
 * (i.e. a normal blank line).
 */
void
cl_standout()
{
	register struct scrimage *phys_p = Curimage;

	if (!(phys_p->s_flags & VIS_ATTRIB))
		return;
	/*
	 * Current line stands out. Wipe it out. For "dumb" terminals that
	 * cannot clear to EOL, zero the line image to force output of all
	 * blanks. (though it is improbable that such a terminal has standout:-)
	 */
	phys_p->s_flags &= ~VIS_ATTRIB;

#ifdef DUMBTERMS
	if (!CE)
		v_inval();
#endif
#ifdef COLOR
	if (XS)
		ReqColor = CurrColor = 0;
#endif
	cl_eol();
}

/*
 * Switch standout/highlight mode on or off.
 * limitations: only one standout/highlight region per screen line is
 * supported (sufficient for standout mode lines)
 * This is all rather hairy due to the (heroic) attempt
 * to handle XS and SG braindamaged terminals correctly.
 */

#ifdef SG		/* saves some code... */
#   define sg	SG
#endif

#if (HIGHLIGHT)
private const char	*so;
#else
#   define	so	SO
#endif
private const char	*se;

private int	so_start;

/* turn on SO mode before putting a char to the screen (setup by standout) */

private void
SOputc(c)
char	c;
{
#ifndef sg
	register int	sg = SG;
#endif
#ifdef COLOR
	/* Set color here, i.e., _before_ the character-attribute is set,
	   to accommodate terminals that implement attributes as
	   color-manipulations. (e.g., MS-DOS ANSI.SYS) */
	if (ReqColor != CurrColor) {
		set_req_color();
	}
#endif
	if (sg || XS) {
		Placur(i_line, sg ? so_start : i_col);
		CapCol += sg;
	}
	putp(so);
	TTputc = COFFputc;	/* to turn cursor off after next char. */
	COFFputc(c);
}

void
standout(which)
int	which;
{
	register int	len;
#ifndef sg
	register int	sg = SG;
#endif

	if (which &= VIS_ATTRIB) {
#if (HIGHLIGHT)
		if (which == HIGHLIGHT)
			so = BO,
			se = ME;
		else /* assume (which == STANDOUT) */
			so = SO,
#endif
			se = SE;
		if (!se) {
			return;
		}
		if (sg) {
			so_start = i_col;
			cursend -= (sg + sg);
		}
		if ((len = (cursend - cursor)) > 0) {
			register struct scrimage *phys_p = Curimage;
			/*
			 * If the line does not stand out, zero the current
			 * line in the screen image to force output of all
			 * standout characters.  Also set standout flag in
			 * physical screen line.
			 */
			if (!(phys_p->s_flags & which)) {
				phys_p->s_flags |= which;
				bzero(cursor, len);
			}
			/*
			 * Replace character output routine by our own that
			 * first puts terminal in SO mode.
			 */
			TTputc = SOputc;
		}
	}
	else if (!se) {	/* nothing to do */
		return;
	}
	/* {dumb cast for dumb compilers} */
	else if ((void_*) TTputc == (void_*) SOputc) {
		/* we didn't do anything. just restore original routine */
		TTputc = dosputc;
	}
	else {
		if (sg || XS) {
			/* position cursor to the end of the standout field */
			Placur(i_line, i_col);
			CapCol += sg;
		}
		putp(se);
		se = NULL;
#ifdef COLOR
		if (ColorStandoutGlitch)
			CurrColor = -1;
#endif
	}
	if (sg) {	/* adjust variables for magic cookie */
		i_col += sg;
		cursor += sg;
		cursend += sg;
	}
}

/*
 * ring a bell N times. Output a visual bell only when enabled,
 * and this is a "normal" bell (N equals 1).
 */
void
dobell(n)
register int	n;
{
	register const char	*bell = BL;

	if ((--n <= 0) && True(VisBell) && (bell = VB) == NULL) {
#ifndef TINY
		/* no visual bell cap. so use the emulation */
#   ifdef FAST_FLASH
		flash();
#   else
    /* kludge up a visible bell if the terminal does not have the capability */

		static const char	Beep[] = "[beep]";
		register int		at = CapCol;

		if ((n = (CO - sizeof Beep)) < at)
			at = n;
		i_set(CapLine, at);
		set_color(MK_COLOR(WHITE, RED));
		Overtype(Beep);
		flusho();
		DoSit(PDelay);		/* NOT SitFor since that redisplays! */

		/* This kludge is to ensure restore of SO modeline (and color) */
		bzero(cursor - (sizeof Beep - 1), (sizeof Beep - 1));

		/* leave further screen restore to redisplay */
#   endif /* FAST_FLASH */
		return;
#else /* TINY */
		bell = BL;
#endif
	}
	do {
		putp(bell);
		flusho();
	} while (--n >= 0);
}

/* Cursor visibility */

private int	curs_off,
		curs_count;

#define CUROFF_DELAY	1

private void
COFFputc(c)
char	c;
{
	if (--curs_count == 0) {
		if (!curs_off)
			curs_off++, putp(VI);
		TTputc = dosputc;
	}
	dosputc(c);
}

/*
 * setup to turn off cursor after CUROFF_DELAY characters are written
 * by swrite.
 */
void
scursoff()
{
	if (VI) {
		curs_count = CUROFF_DELAY + 1;
		TTputc = COFFputc;
	}
}

/* turn cursor off immediately (flush always). */

void
cursoff()
{
	if (!curs_off)
		curs_off++, putp(VI);
	flusho();
}

/* turn cursor on immediately, always flush (Also cancels delayed cursoff). */

void
curson()
{
	TTputc = dosputc;
	if (curs_off)
		curs_off = NO, putp(VS);
	flusho();
}

/* move cursor to lower-left corner */

void
curstoLL()
{
	Placur(ILI, 0);
}

/* Output one character at the current position */

private void
dosputc(c)
register char	c;
{
	register File	*screen = stdout;
#ifdef ID_CHAR
	if (IN_INSmode)
		INSmode(0);
#endif
	if (i_line != CapLine || i_col != CapCol)
		Placur(i_line, i_col);
#ifdef DUMBTERMS
	if (UL && (c == '_' && *cursor != ' '))
		putstr(" \b");		/* Erase so '_' looks right. */
#endif
	putc(c, screen);
	*cursor = c;
	CapCol++;
}

/* Write `line' at the current position of `cursor'.  Stop when we
   reach the end of the screen.  Aborts if there is a character
   waiting.  */

private int do_swrite __(( const char *_(line), int _(col),
			   int _(visspace), int _(abortable) ));
private int
do_swrite(line, col, visspace, abortable)
const char	*line;
register int	col;
int		visspace,
		abortable;
{
	register int		c,
				n,
				completed = YES;
	register const char	*lp = line;

	if ((n = cursend - cursor) <= 0)
		return completed;

	OkayAbort = NO;		/* set by _flush */
	while (c = *lp++) {
		if (OkayAbort && abortable) {
			OkayAbort = NO;
			if (InputPending = charp()) {
				completed = NO;
				break;
			}
		}
		if (isctrl(c)) {
			if (c == '\t') {
#				define nchars	c	/* alias! */
				nchars = TabIncr(col);
				col += nchars;
				if (visspace) {
					soutputc('>');
					nchars--;
				}
				while (--nchars >= 0)
					soutputc(' ');
				if (n <= 0)
					break;
#				undef nchars
			} else {
#ifdef NULLCHARS
				if (c == '\n')
					c = '\0';
#endif
				col += 2;
				soutputc('^');
				soutputc(c ^ '@');
			}
		} else {
			col++;
			if (c == ' ' && visspace)
				c = '_';
#ifdef DUMBTERMS
			else if (c == '~' && Hz)
				c = '`';
#endif
			soutputc(c);
		}
	}
	if (n <= 0) {
		if (*lp || isctrl(c))
			c = '!';
		sputc(c);
	}
	if (cursor > Curline->s_length)
		Curline->s_length = cursor;
	return completed;		/* Didn't abort */
}

int
swrite(line, abortable)
const char	*line;
{
	return do_swrite(line, i_col, NO, abortable);
}

/* This is for writing a buffer line to the screen.  This is to
   minimize the amount of copying from one buffer to another buffer.
   This gets the info directly from the disk buffers. */

int
BufSwrite(des_p)
register struct scrimage *des_p;
{
	register const char	*lp = lcontents(des_p->s_lp);
	register int		col = 0;

	if (*lp == '\0')
		return YES;

	if (des_p->s_offset) {
		register char	c;
#ifndef TINY
		swrite("!", NO); /* indicates that part of line is invisible. */
#endif
		do {
			if ((c = *lp++) == '\0')
				return YES;

			if (!isctrl(c))
				++col;
			else if (c == '\t') {
				if ((col += TabIncr(col)) > des_p->s_offset) {
					col = des_p->s_offset;
					--lp;
					break;
				}
			}
			else {
				col += 2;
#ifdef TINY
				if (col > des_p->s_offset) {
					des_p->s_offset = col;
					break;
				}
#endif
			}
		} while (col < des_p->s_offset);
#ifndef TINY
		if (col == des_p->s_offset) {
			if ((c = *lp++) == '\0')
				return YES;
			++col;
			if (isctrl(c)) {
				if (c != '\t') {
#   ifdef NULLCHARS
					if (c == '\n')
						c = '\0';
#   endif
					/* the `^' is replaced by `!' */
					c ^= '@';
					sputc(c);
					++col;
				}
				else if (col % tabstop != 0)
					--lp;
			}
		}
#endif /* !TINY */
	}
	return do_swrite(lp, col, des_p->s_window->w_flags & W_VISSPACE, YES);
}

/*
 * This is for Typeout and the like
 * it invalidates the current screen line, AND does the physical changes
 * it returns the current cursor position
 */
int
Overtype(str)
const char	*str;
{
	if (i_line == ILI)
		updmesg();
	else
		Curimage->s_id = -1;

	cl_standout();
	swrite(str, YES);
#ifdef COLOR
	Curimage->s_color = CurrColor;
#endif
	return i_col;
}

void
i_set(nline, ncol)
register int	nline,
		ncol;
{
	register char	*lp;

	lp = (Curline = &Screen[nline])->s_line;
	Curimage = &PhysScreen[nline];
	cursor	= &lp[ncol];
	cursend = &lp[CO];
	/*
	 * [TRH] some terminals scroll when something is written
	 * in rightmost lower position, so this kludge avoids it.
	 */
	if (AM && nline == ILI)
		cursend--;
	i_line	= nline;
	i_col	= ncol;
}

/* Insert `num' lines at top, but leave all the lines BELOW `bottom'
   alone (at least they won't look any different when we are done).
   This changes the screen array AND does the physical changes. */

void
v_ins_line(num, top, bottom)
{
	struct screenline
		save;
	register struct screenline
		*dest = &Screen[bottom],
		*src = &dest[-num],
		*end = &Screen[top];

	/*
	 * Swap the lines in the range [top .. bottom - num]
	 * with those in [top + num .. bottom]
	 */
	do {
		save = *dest;
		*dest = *src;
		*src = save;
	} while (--dest, --src >= end);
	/*
	 * Now clear the lines in [top .. top + num>
	 */
	do {
		clrline(dest->s_line, dest->s_length);
		dest->s_length = dest->s_line;
	} while (--dest >= end);
	/*
	 * Do the physical change
	 */
	(*TTins_line)(top, bottom, num);
}

/* Delete `num' lines starting at `top' leaving the lines below `bottom'
   alone.  This updates the internal image as well as the physical image.  */

void
v_del_line(num, top, bottom)
int	num;
{
	struct screenline
		save;
	register struct screenline
		*dest = &Screen[top],
		*src = &dest[num],
		*end = &Screen[bottom];

	/*
	 * Swap the lines in the range [top + num .. bottom]
	 * with those in [top .. bottom - num]
	 */
	do {
		save = *dest;
		*dest = *src;
		*src = save;
	} while (++dest, ++src <= end);
	/*
	 * Now clear the lines in <top + num .. bottom]
	 */
	do {
		clrline(dest->s_line, dest->s_length);
		dest->s_length = dest->s_line;
	} while (++dest <= end);
	/*
	 * Do the physical change
	 */
	(*TTdel_line)(top, bottom, num);
}

/* The cursor optimization happens here.  You may decide that this
   is going too far with cursor optimization, or perhaps it should
   limit the amount of checking to when the output speed is slow.
   What ever turns you on ...
   [TRH June 90] the more arcane cursor optimizations are conditionally
   compiled on the presence of CURSOPT */

#define home()		Placur(0, 0)
#define LowLine()	putp(LL), CapLine = ILI, CapCol = 0
#define PrintHo()	putp(HO), CapLine = CapCol = 0

private void ForMotion __(( int _(destcol) ));
private void
ForMotion(destcol)
register int	destcol;
{
	register int	nchars = destcol - CapCol;
	register char	*cp = &Screen[CapLine].s_line[CapCol];

	/* PRE: destcol != CapCol */
	do  putch(*cp++);  while (--nchars);
	CapCol = destcol;
}

private void BackMotion __(( int _(destcol) ));
private void
BackMotion(destcol)
register int	destcol;
{
	register int	nchars = CapCol - destcol;

	/* PRE: destcol != CapCol */
	if (BC)
		do  putp(BC);  while (--nchars);
	else
		do putch('\b');  while (--nchars);
	CapCol = destcol;
}

#ifdef CURSOPT

DEF_INT( "physical-tabstop", phystab, V_BASE10|V_CLRSCREEN ) = 8; _IF(def CURSOPT)

/* Tries to move forward using tabs (if possible).  It tabs to the
   closest tabstop which means it may go past 'destcol' and backspace
   to it. */

private int NumTab __(( int *_(from), int _(to) ));
private int
NumTab(from, to)	/* calc. nr. of tabs to insert, and update start pos. */
register int	*from;
{
	register int	tabgoal,
			tabstop,
			numchars;

#if 0	/* [check this before calling NumTab] */
	if (!TABS || *from >= to)
		return 0;
#endif
	if ((tabstop = phystab) <= 0)	/* sanity check... */
		return 0;

	tabgoal = to + ((tabstop - 1) >> 1);
	tabgoal -= (tabgoal % tabstop);

	/* Don't tab to last place or else it is likely to screw up. */
	if (tabgoal >= CO)
		tabgoal -= tabstop;
	if (numchars = (tabgoal - *from + tabstop - 1) / tabstop)
		*from = tabgoal;

	return numchars;
}

private int ForNum __(( int _(from), int _(to) ));
private int
ForNum(from, to)	/* calc. cost of using ForTab() */
register int	to;
{
	register int	ntab, nchr;

#if 0	/* [check this before calling ForNum] */
	if (!TABS || from >= to)
		ntab = 0;
	else
#endif
	ntab = NumTab(&from, to);
	if ((nchr = to - from) < 0)
		nchr = -nchr;

	return ntab + nchr;
}

private void ForTab __(( int _(destcol) ));
private void
ForTab(destcol)
register int	destcol;
{
	register int	n;

	if (TABS && (n = NumTab(&CapCol, destcol))) do {
		putch('\t');
	} while (--n);

	if ((n = destcol - CapCol) < 0)
		BackMotion(destcol);
	else if (n > 0)
		ForMotion(destcol);
}
#endif /* CURSOPT */

private void DownMotion __(( int _(destline) ));
private void
DownMotion(destline)
register int	destline;
{
	register int	nlines = destline - CapLine;

	while (--nlines >= 0)
		putch('\n');
	CapLine = destline;
}

private void UpMotion __(( int _(destline) ));
private void
UpMotion(destline)
register int	destline;
{
	register int	nchars = CapLine - destline;

	while (--nchars >= 0)
		putp(UP);
	CapLine = destline;
}

private char	*Cmstr;

#define	GoDirect(line, col)	(putp(Cmstr), CapLine = (line), CapCol = (col))

#if 0	/* obsolete */
private void GoDirect __(( int _(line), int _(col) ));
private void
GoDirect(line, col)
{
	putp(Cmstr);
	CapLine = line;
	CapCol = col;
}
#endif

#ifdef CURSOPT

private void RetTab __(( int _(col) ));
private void
RetTab(col)
{
	putch('\r');
	CapCol = 0;
	ForTab(col);
}

private void HomeGo __(( int _(line), int _(col) ));
private void
HomeGo(line, col)
{
	PrintHo();
	DownMotion(line);
	ForTab(col);
}

private void BottomUp __(( int _(line), int _(col) ));
private void
BottomUp(line, col)
{
	LowLine();
	UpMotion(line);
	ForTab(col);
}
#endif /* CURSOPT */

/*
 * [TRH] Dec-88, Optimize cursor optimization
 */
void
Placur(line, col)
{
	register int	dline,		/* Number of lines to move */
			dcol,		/* Number of columns to move */
			Cost,		/* cost of movement */
			n;
#ifdef CURSOPT
	int		RetForward;	/* Cost of tabbing from begin of line */
#endif
	void		(*HorProc)__(( int _(col) )),
			(*VertProc)__(( int _(line) ));
#ifdef ID_CHAR
	int		XHorCost = 0,	/* Misc addition to cost. */
			XDirCost = 0;
#else
#	define		XHorCost   0
#	define		XDirCost   0
#endif

 	/* [TRH] handle right margin */
	if (CapCol == CO)
		if (AM)
			CapCol = 0, CapLine++;
		else if (!XN)
			CapCol--;

	/* [TRH] we cannot move beyond the end of the line */
	if (col >= CO)
		if (line != ILI)
			col = 0, line++;
		else
			col = CO - 1;
#ifdef ID_CHAR
	if (IN_INSmode)
		if (MI)
			XHorCost = (EIlen + IMlen);
		else
			XDirCost = (EIlen + IMlen);
	/* If we're already in insert mode, it is likely that we will
	   want to be in insert mode again, after the insert. */
#endif
#ifdef CURSOPT
	RetForward = -1;
#endif
	/*
	 * Handle horizontal motion.
	 * Forward motion:
	 *   1.	Just move forward by typing the right character on the screen.
	 *   2. Try tabbing to the correct place.
	 * Backward motion:
	 *   1. Move backward by printing backspaces.
	 *   2. Try going to the beginning of the line, and then tab.
	 * Whichever is the least costly.
	 */
	dline = line - CapLine;
	if ((dcol = col - CapCol) == 0) {
		if (dline == 0)		/* we are already there */
			return;
		Cost = 0;
	}
	else if (dcol > 0) {
		HorProc = ForMotion;
		Cost = dcol + XHorCost;
#ifdef CURSOPT
		if (dcol > 1 && TABS) {
			if ((n = ForNum(CapCol, col) + XHorCost) < Cost) {
				HorProc = ForTab;
				Cost = n;
			}
		}
#endif
	}
	else /* (dcol < 0) */ {
		HorProc = BackMotion;
		Cost = -dcol + XHorCost;
#ifdef CURSOPT
		if (dcol < -1) {
			if ((RetForward = (TABS ? ForNum(0, col) : col)
						+ XHorCost) + 1 < Cost) {
				HorProc = RetTab;
				Cost = RetForward + 1;
			}
		}
#endif
	}
	/*
	 * Moving vertically is more simple.
	 */
	if (dline) {
		if (dline > 0) {
			VertProc = DownMotion;
			Cost += dline;
		}
		else {
			VertProc = UpMotion;
			Cost -= dline * UPlen;
		}
	}
	if (Cost > 3) {
#ifdef CURSOPT
		void	(*DirectProc)__((int _(line), int _(col))) = NULL;

		/*
		 * Homing first and lowering first are considered
		 * direct motions.
		 * Homing first's total is the sum of the cost of homing
		 * and the sum of tabbing (if possible) to the right.
		 */
		if (HO || LL) {
			if (RetForward < 0)	/* not yet calculated? */
				RetForward = (TABS ? ForNum(0, col) : col) + XHorCost;
			if (HO && (n = HOlen + line + RetForward) < Cost) {
				DirectProc = HomeGo;
				Cost = n;
			}
			if (LL && (n = LLlen + (ILI - line) * UPlen + RetForward) < Cost) {
				DirectProc = BottomUp;
				Cost = n;
			}
		}
#endif /* CURSOPT */
		if (CM && (strlen(Cmstr = tgoto(CM, col, line)) + XDirCost) <= Cost) {
			/* prefer Direct if HV and Direct costs are equal */
#ifdef ID_CHAR
			if (IN_INSmode && !MI)
				INSmode(0);
#endif
			GoDirect(line, col);
			return;
		}
#ifdef CURSOPT
		if (DirectProc) {
#   ifdef ID_CHAR
			if (IN_INSmode)
				INSmode(0);
#   endif
			(*DirectProc)(line, col);
			return;
		}
#endif /* CURSOPT */
	}
	/* if we get here, we didn't go Direct */
	if (dline)
		(*VertProc)(line);
	if (dcol) {
#ifdef ID_CHAR
		if (IN_INSmode)	/* We may use real characters ... */
			INSmode(0);
#endif
		(*HorProc)(col);
	}
}

#ifndef FAST_IDLINE

#define scr_reg(top, bot)  (putp(tgoto(CS, bot, top)), CapCol = CapLine = 1000)

private void GENi_lines __(( int _(top), int _(bottom), int _(num) ));
private void
GENi_lines(top, bottom, num)
register int	top, num, bottom;
{
	if (CS) {
		scr_reg(top, bottom);
		Placur(top, 0);
		Nputpad(SR, M_SR, num, bottom - top);
		scr_reg(0, ILI);
	} else {
		bottom += 1 - num;
		Placur(bottom, 0);
		Nputpad(DL, M_DL, num, ILI - bottom);
		Placur(top, 0);
		Nputpad(AL, M_AL, num, ILI - top);
	}
}

private void GENd_lines __(( int _(top), int _(bottom), int _(num) ));
private void
GENd_lines(top, bottom, num)
register int	top, num, bottom;
{
	if (CS) {
		scr_reg(top, bottom);
		Placur(bottom, 0);
		Nputpad(SF, M_SF, num, bottom - top);
		scr_reg(0, ILI);
	} else {
		Placur(top, 0);
		Nputpad(DL, M_DL, num, ILI - top);
		bottom += 1 - num;
		Placur(bottom, 0);
		Nputpad(AL, M_AL, num, ILI - bottom);
	}
}

#ifdef WIRED_TERMS

private void BGi_lines __(( int _(top), int _(bottom), int _(num) ));
private void
BGi_lines(top, bottom, num)
{
	printf("\033[%d;%dr\033[%dL\033[r", top + 1, bottom + 1, num);
	CapCol = CapLine = 0;
}

private void BGd_lines __(( int _(top), int _(bottom), int _(num) ));
private void
BGd_lines(top, bottom, num)
{
	printf("\033[%d;%dr\033[%dM\033[r", top + 1, bottom + 1, num);
	CapCol = CapLine = 0;
}


private void SUNi_lines __(( int _(top), int _(bottom), int _(num) ));
private void
SUNi_lines(top, bottom, num)
{
	Placur(bottom - num + 1, 0);
	printf("\033[%dM", num);
	Placur(top, 0);
	printf("\033[%dL", num);
}

private void SUNd_lines __(( int _(top), int _(bottom), int _(num) ));
private void
SUNd_lines(top, bottom, num)
{
	Placur(top, 0);
	printf("\033[%dM", num);
	Placur(bottom + 1 - num, 0);
	printf("\033[%dL", num);
}


private void C100i_lines __(( int _(top), int _(bottom), int _(num) ));
private void
C100i_lines(top, bottom, num)
{
	if (num <= 1) {
		GENi_lines(top, bottom, num);
		return;
	}
	printf("\033v  %c%c", ' ' + bottom + 1, ' ' + CO);
	CapLine = CapCol = 0;
	Placur(top, 0);
	do putpad(AL, ILI - top); while (--num);
	printf("\033v  %c%c", ' ' + LI, ' ' + CO);
	CapLine = CapCol = 0;
}

private void C100d_lines __(( int _(top), int _(bottom), int _(num) ));
private void
C100d_lines(top, bottom, num)
{
	if (num <= 1) {
		GENd_lines(top, bottom, num);
		return;
	}
	printf("\033v  %c%c", ' ' + bottom + 1, ' ' + CO);
	CapLine = CapCol = 0;
	Placur(top, 0);
	do putpad(DL, ILI - top); while (--num);
	printf("\033v  %c%c", ' ' + LI, ' ' + CO);
	CapLine = CapCol = 0;
}

#endif /* WIRED_TERMS */

private const struct ID_lookup {
  const	char	*ID_name;
	void	(*I_proc)__(( int _(t), int _(b), int _(n) )),	/* proc to insert lines */
		(*D_proc)__(( int _(t), int _(b), int _(n) ));	/* proc to delete lines */
	int	ID_scr_penalty;
} ID_trms[] = {
#ifdef WIRED_TERMS
	"sun",		SUNi_lines,	SUNd_lines,	15,
	"bg",		BGi_lines,	BGd_lines,	0,
	"c1",		C100i_lines,	C100d_lines,	10,
#endif /* WIRED_TERMS */
	NULL,		GENi_lines,	GENd_lines,	0	/* Default */
};

void
IDline_setup(tname)
const char	*tname;
{
	register const struct ID_lookup	*idp = &ID_trms[0];
	register int			penalty = 0,
					cps;
#ifdef WIRED_TERMS
	while (!LookingAt(idp->ID_name, tname, 0) && (++idp)->ID_name)
		;
	penalty = idp->ID_scr_penalty;

    if (idp->ID_name == NULL)	/* the next `if' */
#endif
	/*
	 * apply a penalty to scroll limit for absence of capabilities
	 * (I [TRH] admit the values are rather arbitrary)
	 */
	if (CS) {
		if (!M_SF)
			penalty = 10;
		else if (!M_SR)
			penalty = 5;
	} else {
		/* I don't like "dancing mode lines" that much... */
		penalty = 15;
		if (!(M_AL && M_DL))
			penalty += 35;
	}

	/* scale penalty down for slower terminals */
	if ((cps = CharsPerSec) < 480)
		penalty = penalty * cps / 480;

	ScrollLimit = 100 - penalty;

	TTins_line = idp->I_proc;
	TTdel_line = idp->D_proc;

	CanScroll++;	/* say we can do it */
}
#endif /* FAST_IDLINE */

#ifdef ID_CHAR

/* From here to the end of the file is code that tries to utilize the
   insert/delete character feature on some terminals.  It is very confusing
   and not so well written code, AND there is a lot of it.  You may want
   to use the space for something else. */

DEF_INT( "use-i/d-char", UseIC, V_BOOL ) _IF(def ID_CHAR) ZERO;

int	CanIC ZERO;

void
disp_opt_init()
{
#   define INIT(len, cap)	{if (cap) len = strlen(cap);}
#   define M_INIT(len, cap)	{if (cap) len = strlen(tgoto(cap,0,10));}
	INIT(DClen, DC);
	M_INIT(MDClen, M_DC);
	INIT(IClen, IC);
	M_INIT(MIClen, M_IC);
	INIT(IMlen, IM);
	INIT(CElen, CE);
	INIT(EIlen, EI);
#   undef INIT
#   undef M_INIT
	if (IC || IM || M_IC) {
		CanIC = YES;
		if (CharsPerSec < 480)		/* [TRH] slow terminals only */
			UseIC = YES;
	}
}

void
INSmode(on)
{

	if (on) {
		if (!IN_INSmode) {
			putp(IM);
			IN_INSmode++;
		}
	} else {
		if (IN_INSmode) {
			putp(EI);
			IN_INSmode = 0;
		}
	}
}

/* This is largely a duplicate of BufSWrite/do_swrite */

void
DeTab(s_offset, buf, outbuf, limit, visspace)
register const char	*buf;
char			*outbuf;
register int		limit;
{
	register char	*phys_p = outbuf,
			c;
	register int	pos = 0;

#define OkayOut(ch)	{ if (--limit >= 0) *phys_p++ = ch; }

	if (s_offset) {
#ifndef TINY
		OkayOut('!');	/* indicates that part of line is invisible. */
#endif
		do {
			if ((c = *buf++) == '\0') {
				phys_p = '\0';
				return;
			}
			if (!isctrl(c))
				++pos;
			else if (c == '\t') {
				if ((pos += TabIncr(pos)) > s_offset) {
					pos = s_offset;
					--buf;
					break;
				}
			}
			else {
				pos += 2;
#ifdef TINY
				if (pos > s_offset) {
					s_offset = pos;
					break;
				}
#endif
			}
		} while (pos < s_offset);
#ifndef TINY
		if (pos == s_offset) {
			if ((c = *buf++) == '\0') {
				phys_p = '\0';
				return;
			}
			++pos;
			if (isctrl(c)) {
				if (c != '\t') {
					/* the `^' is replaced by `!' */
					c ^= '@';
					OkayOut(c);
					++pos;
				}
				else if (pos % tabstop != 0)
					--buf;
			}
		}
#endif /* !TINY */
	}

	while (c = *buf++) {
		if (c == '\t') {
			int	nchars = TabIncr(pos);

			pos += nchars;
			if (visspace) {
				OkayOut('>');
				--nchars;
			}
			while (--nchars >= 0)
				OkayOut(' ');

		} else if (isctrl(c)) {
			OkayOut('^');
#ifdef NULLCHARS
			if (c == '\n')
				c = '\0';
#endif
			OkayOut(c ^ '@');
			pos += 2;
		} else {
			if (c == ' ' && visspace)
				c = '_';
#ifdef DUMBTERMS
			else if (c == '~' && Hz)
				c = '`';
#endif
			OkayOut(c);
			pos++;
		}

		if (limit < 0) {
			phys_p[-1] = '!';
			break;
		}
	}
	*phys_p = '\0';
}

private NumSimilar __(( const char *_(s), const char *_(t), int _(n) ));
private
NumSimilar(s, t, num)
register const char	*s,
			*t;
{
	register int	n = num;

	while (--n >= 0 && *s++ == *t++) ;

	return num - n + 1;
}

private IDcomp __(( const char *_(s), const char *_(t), int _(len) ));
private
IDcomp(s, t, len)
register const char	*s,
  			*t;
{
	register int	i,
			num = 0,
			nonspace = 0;
	register char	c;

	for (i = 0; i < len; i++) {
		if ((c = *s++) != *t++)
			break;
		if (c != ' ')
			nonspace++;
		if (nonspace)
			num++;
	}

	return num;
}

#define OkayDelete(Saved, num, samelength) \
	(((Saved) + (!(samelength) ? CElen : 0)) > min(MDClen, DClen * (num)))
#ifndef OkayDelete
private OkayDelete __(( int _(Saved), int _(num), int _(samelength) ));
private
OkayDelete(Saved, num, samelength)
{
	/* If the old and the new are the same length, then we don't
	 * have to clear to end of line.  We take that into consideration.
	 */
	return ((Saved + (!samelength ? CElen : 0))
		> min(MDClen, DClen * num));
}
#endif

#define OkayInsert(Saved, num) \
    ((Saved) > (num) + min((num) * IClen, MIClen) + (IN_INSmode ? 0 : IMlen))
#ifndef OkayInsert
private int OkayInsert __(( int _(Saved), int _(num) ));
private int
OkayInsert(Saved, num)
{
	register int	n = num;	/* The characters themselves */

	if (IC) 	/* Per character prefixes */
		n = min(num * IClen, MIClen);

	if (IM && !IN_INSmode) {
		/* Good terminal.  Fewer characters in this case */
		n += IMlen;
	}
	return Saved > n;
}
#endif

private void DelChar __(( int _(lineno), int _(col), int _(num) ));
private void
DelChar(lineno, col, num)
{
	register char	*from,
			*to;
	struct screenline *sp = (&Screen[lineno]);

	Placur(lineno, col);
	Nputpad(DC, M_DC, num, 1);

	to = sp->s_line + col;
	from = to + num;

	byte_copy(from, to, (int)(sp->s_length - from) + 1);
	clrline(sp->s_length - num, sp->s_length);
	sp->s_length -= num;
}

private void InsChar __(( int _(lineno), int _(col), int _(num), const char *_(new) ));
private void
InsChar(lineno, col, num, new)
const char	*new;
{
	register char
			*sp1,
			*sp2,	/* To push over the array. */
			*sp3;	/* Last character to push over. */
	register int	i;

	i_set(lineno, 0);
	sp2 = Curline->s_length + num;

	if (sp2 >= cursend) {
		i_set(lineno, CO - num - 1);
		cl_eol();
		sp2 = cursend - 1;
	}
	Curline->s_length = sp2;
	sp1 = sp2 - num;
	sp3 = Curline->s_line + col;

	while (sp1 >= sp3)
		*sp2-- = *sp1--;

	new += col;
	byte_copy(new, sp3, num);
	/* The internal screen is correct, and now we have to do
	   the physical stuff. */

	Placur(lineno, col);
	if (IM) {
		if (!IN_INSmode)
			INSmode(1);
	} else
		Nputpad(IC, M_IC, num, 1);

	for (i = 0; i < num; i++) {
		putch(new[i]);
		if (IN_INSmode)
			putp(IP);
	}
	CapCol += num;
}

/* ID character routines full of special cases and other fun stuff like that.
   It actually works though ...

	Returns Non-Zero if you are finished (no differences left). */

IDchar(new, lineno, col)
register const char	*new;
{
	register const char	*old = Screen[lineno].s_line;
	register int	i,
			j = col,
			oldlen = (Screen[lineno].s_length - old),
			newlen = strlen(new),
			NumSaved;

	if (!CanIC)			/* [TRH] sanity check */
		return (UseIC = NO);

again:
	i = j - 1;			/* [TRH] to remove tail recursion */

	do {
		if (++i >= oldlen)
			return (new[i] == 0);
		if (new[i] == 0)
			return NO;
	} while (old[i] == new[i]);

	for (j = i + 1; j < oldlen && new[j]; j++) {
		if (new[j] == old[i]) {
			NumSaved = IDcomp(new + j, old + i, newlen) +
				   NumSimilar(new + i, old + i, j - i);

			if (OkayInsert(NumSaved, j - i)) {
				InsChar(lineno, i, j - i, new);
				oldlen += j - i;
				goto again;
			}
		}
	}

	for (j = i + 1; j < oldlen && new[i]; j++) {
		if (new[i] == old[j]) {
			NumSaved = IDcomp(new + i, old + j, oldlen - j);
			if (OkayDelete(NumSaved, j - i, new[oldlen] == 0)) {
				DelChar(lineno, i, j - i);
				oldlen -= j - i;
				goto again;
			}
		}
	}
	return NO;
}

#endif /* ID_CHAR */

#ifdef COLOR
/*
 * set_color(color) physically switches to the requested color.
 * Additionally, req_color(color) first sets up the line for a color change.
 */

DEF_INT( "use-color", UseColor, V_BOOL|V_CLRSCREEN ) _IF(def COLOR) ZERO;

#   ifdef TERMCAP
char		*Colors[2][NCOLORS];	/* for GENcolor. */

private int	color_map[2][NCOLORS];	/* color remap for {PARM,FB}color */

private int	prev_color[2] = { -1, -1 };

void	(*TTcolor)__(( int _(fg_color), int _(bg_color) )) = no_op;

void
set_color(color)
register int	color;
{
	register int	fg_color, bg_color;

	if (color == 0)
		color = DefColor;
	if (CurrColor == 0)
		CurrColor = DefColor;

	if (color == CurrColor)		/* fast reject */
		return;

	if (False(UseColor))
		return;

	if (XS && CurrColor >= 0) {
		CurrColor = color;
		return;
	}
	if (CurrColor < 0)		/* fool optimization... */
		prev_color[FG] = prev_color[BG] = -1;

	CurrColor = color;

	fg_color = FG_COLOR(color);
	bg_color = BG_COLOR(color);

	if (fg_color == bg_color)	/* make sure they are different */
		fg_color ^= WHITE;	/* chooses its complementary color */

	(*TTcolor)(fg_color, bg_color);
}

private void GENcolor __(( int _(fg_color), int _(bg_color) ));
private void
GENcolor(fg_color, bg_color)
register int fg_color, bg_color;
{
	if (prev_color[BG] != bg_color || XS) {
		putp(Colors[BG][bg_color]);
		prev_color[BG] = bg_color;
	}
	if (prev_color[FG] != fg_color || XS) {
		putp(Colors[FG][fg_color]);
		prev_color[FG] = fg_color;
	}
}

private void PARMcolor __(( int _(fg_color), int _(bg_color) ));
private void
PARMcolor(fg_color, bg_color)
register int fg_color, bg_color;
{
	bg_color = color_map[BG][bg_color];
	fg_color = color_map[FG][fg_color];

	if (prev_color[BG] != bg_color || prev_color[FG] != fg_color || XS) {
		putp(tgoto(COlor, bg_color, fg_color));
		prev_color[BG] = bg_color;
		prev_color[FG] = fg_color;
	}
}

private void FBcolor __(( int _(fg_color), int _(bg_color) ));
private void
FBcolor(fg_color, bg_color)
register int fg_color, bg_color;
{
	bg_color = color_map[BG][bg_color];
	fg_color = color_map[FG][fg_color];

	if (prev_color[BG] != bg_color || XS) {
		putp(tgoto(CObg, 0, bg_color));
		prev_color[BG] = bg_color;
	}
	if (prev_color[FG] != fg_color || XS) {
		putp(tgoto(COfg, 0, fg_color));
		prev_color[FG] = fg_color;
	}
}

private void PAIRcolor __(( int _(fg_color), int _(bg_color) ));
private void
PAIRcolor(fg_color, bg_color)
int fg_color, bg_color;
{
	/*
	 * assume default color settings i.e. black background except when
	 * text color is black.
	 */
	if (fg_color != BLACK)
		CurrColor = MK_COLOR(fg_color, BLACK);

	/*
	 * map ANSI to PAIR color: only black and white are interchanged.
	 */
	if (fg_color == WHITE || fg_color == BLACK)
		fg_color ^= WHITE;

	putp(tgoto(COpair, 0, fg_color));
}

private void HPcolor __(( int _(fg_color), int _(bg_color) ));
private void
HPcolor(fg_color, bg_color)
int fg_color, bg_color;
{
	/*
	 * HP terminals have 8 default "color pairs".
	 * Although it is possible to change the colors assigned to
	 * any color pair, this is rather involved (and we would have to
	 * restore the default on exit) so we select a pair according to
	 * foreground color only.
	 */

	/*
	 * assume default color settings i.e. black background except when
	 * text color is black.
	 */
	if (fg_color != BLACK)
		CurrColor = MK_COLOR(fg_color, BLACK);

	/*
	 * map ANSI to HP color: only black and white are interchanged.
	 */
	if (fg_color == WHITE || fg_color == BLACK)
		fg_color ^= WHITE;

	printf("\33&v%dS", fg_color);
}

private void ANSIcolor __(( int _(fg_color), int _(bg_color) ));
private void
ANSIcolor(fg_color, bg_color)
register int fg_color, bg_color;
{
	static char	Fmt0[] = "\33[%d",
			Fmt1[] = ";%d";

	register char	*fmt = Fmt0;

	if (prev_color[FG] != fg_color) {
		prev_color[FG] = fg_color;
		printf(fmt, 30 + fg_color);
		fmt = Fmt1;
	}
	if (prev_color[BG] != bg_color) {
		prev_color[BG] = bg_color;
		printf(fmt, 40 + bg_color);
		fmt = NULL;
	}
	if (fmt != Fmt0)	/* i.e. ANY color sent */
		putch('m');
}

/* determine terminal type from CM capability */

private struct	COLOR_lookup {
	char	*C_CMid;
	void	(*C_proc)__(( int _(fg_color), int _(bg_color) ));
} COLOR_trms[] = {
	"[0-9.*]*\33&a", 	HPcolor,
	"[0-9.*]*\33\\[",	ANSIcolor,
};
#define NCOLOR_TRMS	(sizeof COLOR_trms / sizeof COLOR_trms[0])

private void init_colormap __(( const char *_(desc), int *_(map) ));
private void
init_colormap(desc, map)
const char	*desc;
register int	*map;
{
	register const char	*s;
	register int		n = 0;
	register int		*end = &map[NCOLORS];

	/* Interpret colormap description, if any. */
	if (s = desc) do {
		if (isdigit(*s)) {
			n = 0;
			do {
				n = n * 10 + *s++ - '0';
			} while (isdigit(*s));
		}
		*map++ = n++;
	} while (*s++ == ',' && map < end);

	/* now fill in the remainder of the color map. */
	while (map < end)
		*map++ = n++;
}

void
COLOR_setup(tname)
const char	*tname;
{
	if (Colors[FG][0])
		TTcolor = GENcolor, ++UseColor;
	else if (COlor)
		TTcolor = PARMcolor, ++UseColor;
	else if (COfg && CObg)
		TTcolor = FBcolor, ++UseColor;
	else if (COpair)
		TTcolor = PAIRcolor, ++UseColor;
	else if (CM) {
		register struct COLOR_lookup *p = COLOR_trms;

		do {
			if (LookingAt(p->C_CMid, CM, 0)) {
				TTcolor = p->C_proc/*, ++UseColor*/;
				break;
			}
		} while (++p < &COLOR_trms[NCOLOR_TRMS]);
	}
	init_colormap(CObgmap, color_map[BG]);
	init_colormap(COfgmap, color_map[FG]);

	if (XS)
		ColorEraseGlitch = YES;
}
#   endif /* TERMCAP */

private int color_col;

void
req_color(color)
register int	color;
{
	register int	col;

	if (False(UseColor))
		return;

	col = -1;		/* means don't set cursor. */

	/* Is color of current line changed? */

	if (Curimage->s_color != color) {
		v_inval();
		if (XS) {
			CurrColor = -1;
		}
		col = i_col;
	}
	color_col = col;
	ReqColor = color;
	TTputc = COLORputc;
}

private void
set_req_color()
{
	register int	col;

	if ((col = color_col) >= 0)
		Placur(i_line, col);
	set_color(ReqColor);
}

private void
COLORputc(c)
char c;
{
	if (ReqColor != CurrColor) {
		set_req_color();
	}
	TTputc = COFFputc;
	COFFputc(c);
}

#endif /* COLOR */

/*======================================================================
 * $Log: screen.c,v $
 * Revision 14.32.0.9  1994/05/22  16:21:16  tom
 * (cl_eol): disable color optimization that had undesirable side-effects on
 *  the cursor's color; (SOputc): remove redundant COLORputc.
 *
 * Revision 14.32.0.8  1993/11/05  17:21:09  tom
 * (SOputc): add color hack;
 * (set_req_color): new function, factored out of COLORputc() for above hack.
 *
 * Revision 14.32.0.3  1993/07/28  15:59:39  tom
 * fix lazy COLOR mode *right* (finally); document what the hell is going on;-)
 * (use-color): add clear-screen flag;
 * (COLOR_setup): don't enable use-color for builtin maybe-color terminals.
 *
 * Revision 14.32.0.2  1993/07/15  00:24:17  tom
 *  (set_color): `CurrColor' defaults to `DefColor' if 0;
 * (req_color): remember current column, in `color_col';
 * (COLORputc): set cursor to `color_col' before changing color.
 *
 * Revision 14.32  1993/05/19  01:51:43  tom
 * (color-erase-glitch, color-standout-glitch): new terminal-descriptive vars;
 * optimize color setting.
 *
 * Revision 14.31  1993/02/11  01:04:10  tom
 * some cosmetic surgery.
 *
 * Revision 14.30  1993/02/05  00:55:22  tom
 * cleanup whitespace; some random optimizations; fix bug in [beep].
 *
 * Revision 14.28  1992/10/24  01:24:23  tom
 * convert to "port{ansi,defs}.h" conventions; add default-color and
 * color-remap support.
 *
 * Revision 14.26  1992/08/26  23:56:58  tom
 * add RCS directives.
 *
 */
