/**************************************************************
 *
 *	CRISP - Custom Reduced Instruction Set Programmers Editor
 *
 *	(C) Paul Fox, 1989, 1990, 1991
 *
 *    Please See COPYLEFT notice.
 *
 **************************************************************/

# include	"list.h"
# include	"tty.h"

# define	V_CHANGE	0x01

/**********************************************************************/
/*   Following  #define's  are  expressions  which  return  1  or  0  */
/*   depending  on  whether  certain  of the 4 window borders are on  */
/*   or not.							      */
/**********************************************************************/
# define LBORDERS(wp)	(wp->w_tiled == W_POPUP || border_flag || wp->w_x > 1)
# define RBORDERS(wp)	(wp->w_tiled == W_POPUP || border_flag || wp->w_x + wp->w_w < ncol-3)
# define TBORDERS(wp)	(1)
# define BBORDERS(wp)	(wp->w_tiled == W_POPUP || (wp->w_y + wp->w_h != nrow -1) || border_flag)

int	updating = 0;
extern fd_set	sel_bits;
extern int display_ctrl;
extern	int	num_popup_wins;
extern	int	border_flag;
extern	int	imode;
extern	int	start_line, start_col;
extern	int	end_line, end_col;
extern	int	mark_type;
extern	cmap_t	*base_cmap;
int	force_update = FALSE;
static	int	tl;
static	int	mark_areas;	/* TRUE if should mark current window.	*/
extern	int	need_resize;
int	ncol_minus_one;
WINDOW *wp;
static WINDOW	*current_window;

static	vbyte_t	vt_color;

extern	char	*echo_line;
int     screen_garbled  = TRUE;         /* TRUE if screen is garbage.   */
u_int16	vtrow;				/* Virtual cursor row.          */
u_int16	vtcol;				/* Virtual cursor column.       */
u_int16	ttrow   = HUGE_CURSOR_VAL;      /* Physical cursor row.         */
u_int16	ttcol   = HUGE_CURSOR_VAL;	/* Physical cursor column.      */
u_int16	tttop   = HUGE_CURSOR_VAL;	/* Top of scroll region.        */
u_int16	ttbot   = HUGE_CURSOR_VAL;	/* Bottom of scroll region.     */

vbyte_t   **vscreen;
vbyte_t   **pscreen;
vbyte_t   *video;			/* Actual screen data.          */
vbyte_t   *blanks;                      /* Blank line image.            */
int	*vflags;			/* Array of flags.		*/
cmap_t	*win_cmap;			/* Cmap for current window.	*/
vbyte_t	*vp;

/**********************************************************************/
/*   Prototypes.						      */
/**********************************************************************/
void	ttclear();
int	ttinsl();
int	vtgetcolor();
void	upd_scroll_down();
void	upd_scroll_up();
void	upd_update PROTO((WINDOW *));
void	vteeol PROTO((int));
vbyte_t	vtputl PROTO((LINE *));

static	int	window_indent;	/* Indent from start of line for sideways scrolling. */
static	int	indent;		/* Indent from start of line for sideways scrolling. */

/**********************************************************************/
/*   Initialise display and any window system.			      */
/**********************************************************************/
void
vtinit(argcp, argv)
int	*argcp;
char	**argv;
{

	vt_color = FG(col_table.c_normal);

	ttinit();
	
	/***********************************************/
	/*   This  function  may  call a stub routine  */
	/*   if we're not linking with X11.	       */
	/***********************************************/
	if (display_ctrl & DC_WINDOW)
		x11_init(argcp, argv);
}
void
vtinit1()
{
	register int    i;

	/***********************************************/
	/*   If  size  of  window is very silly, then  */
	/*   stop  us  having  lots  of  pathological  */
	/*   cases  to  handle,  i.e.  a  zero height  */
	/*   window, etc.			       */
	/***********************************************/
	if (ncol < 5)
		ncol = 5;
	if (nrow < 5)
		nrow = 5;
	/***********************************************/
	/*   ncol_minus_one  is  used  by  the update  */
	/*   code  to  avoid  writing  to  the bottom  */
	/*   right  hand  corner  of  the  screen  in  */
	/*   case  we  accidentally  scroll  it. On a  */
	/*   windowing system this isn't the case.     */
	/***********************************************/
	ncol_minus_one = display_ctrl & DC_WINDOW ? ncol : (ncol - 1);

	if (video) {
		chk_free((void *) video);
		chk_free((void *) vscreen);
		chk_free((void *) pscreen);
		chk_free((void *) vflags);
		chk_free((void *) blanks);
		}
	video = (vbyte_t *) chk_alloc(sizeof (vbyte_t) * 2 * (nrow + 1) * ncol);
	vscreen = (vbyte_t **) chk_alloc(sizeof (vbyte_t *) * nrow);
	pscreen = (vbyte_t **) chk_alloc(sizeof (vbyte_t *) * nrow);
	vflags = (int *) chk_alloc(sizeof (int) * nrow);
	blanks = (vbyte_t *) chk_alloc(sizeof (vbyte_t) * ncol);
	echo_line = (char *) (echo_line ? chk_realloc(echo_line, ncol + 1)
					: chk_alloc(ncol + 1));
	echo_line[ncol] = 0;

	if (video == NULL || vscreen == NULL || pscreen == NULL || 
		echo_line == NULL || blanks == NULL || vflags == NULL)
		panic("Cannot allocate screen buffer.");

	for (i = 0; i < nrow; ++i) {
		vflags[i] = 0;
		vscreen[i] = video + (2 * i * ncol);
		pscreen[i] = vscreen[i] + ncol;
		}
	vtblanks();
	vp = vscreen[0];
}
void
vtblanks()
{	register int i;
	vbyte_t normal_space = (vbyte_t) 
		(FG(col_table.c_normal) | ' ');
	for (i = 0; i < ncol; )
		blanks[i++] = normal_space;
	ucopy(blanks, pscreen[nrow-1]);
	ucopy(blanks, vscreen[nrow-1]);

}

void
vttidy(clear) 
int	clear;
{
	tttidy();
	
	/***********************************************/
	/*   Force  ttmove()  to  actually  move  the  */
	/*   cursor.  Otherwise  on some terminals we  */
	/*   exit  with  cursor  at  the  top  of the  */
	/*   screen  instead  of  the bottom where we  */
	/*   think  it  should  be.  This  happens on  */
	/*   VT-100   compatibles  where  we've  sent  */
	/*   the scrolling region escape sequence.     */
	/***********************************************/
	ttrow = ttcol = 0;
	ttmove(nrow-1, (u_int16) 0);
	ttputc(FG(col_table.c_normal) | BG(col_table.c_background));
	if (clear)
		tteeol();
	ttclose();
}

/**********************************************************************/
/*   Move  virtual  cusor  to a point on the screen but make sure it  */
/*   doesn't end up off the screen.				      */
/**********************************************************************/
void
vtmove(row, col) 
int	row;
int	col;
{
	if (row > nrow)
		row = nrow;
	if (col >= ncol - 1)
		col = ncol - 1;
	vtrow = (u_int16) row;
	vtcol = (u_int16) col;
	vp = vscreen[vtrow];
}

# define	ADDC(ch)	indent ? indent-- : (vp[vtcol++] = ch)
/**********************************************************************/
/*   Function  to  write  characters which are not part of a buffer.  */
/*   These ones should not be translated when in binary mode.	      */
/**********************************************************************/
void
vtputb(c) 
register int c; 
{

	/***********************************************/
	/*   If  cursor  gone past right hand edge of  */
	/*   window then ignore the character.	       */
	/***********************************************/
	if (vtcol >= ncol)
		return;
	
	ADDC(c);
}
/**********************************************************************/
/*   Function  to  write  a  character which is part of a window. We  */
/*   need to respect the character map for this window.		      */
/**********************************************************************/
void
vtputw(c) 
register int c; 
{	int	attr;

	/***********************************************/
	/*   If  cursor  gone past right hand edge of  */
	/*   window then ignore the character.	       */
	/***********************************************/
	if (vtcol >= ncol)
		return;

	/***********************************************/
	/*   If  no  special processing involved then  */
	/*   just write the characters to the screen.  */
	/***********************************************/
	attr = c & COLOR_MASK;
	if (win_cmap->cm_flags[c &= 0xff] != CMAP_TAB) {
		unsigned char *cp = (unsigned char *) win_cmap->cm_str[c];
		int	right_edge = wp->w_x + wp->w_w;
		while (*cp && vtcol < right_edge) {
			ADDC(*cp | attr);
			cp++;
			}
		return;
		}
		
	/***********************************************/
	/*   Handle a tab.			       */
	/***********************************************/
	{int	i = vtcol + window_indent - indent;
	int	w_x = wp->w_x - !LBORDERS(wp);
	int	ch_width;
	int	ch;
	int	right;
	
	ch_width = next_tab_stop(i - w_x + 1) + w_x - i;
	ch = (vbyte_t) (' ' | attr);
	right = w_x + wp->w_w;

	if (ch_width + vtcol >= right)
		ch_width = right - vtcol;
	while (ch_width-- > 0)
		ADDC(ch);
	return;
	}

}

/*******************************************************************/
/*   Function  to  display  a  character  forming  a  part of the  */
/*   background  shadow  of  a window. If the show thru option is  */
/*   on,  then  display  the  reverse  of  the  character at this  */
/*   character  position.  Otherwise display the character passed  */
/*   to us.							   */
/*******************************************************************/
void
vtshadow(ch)
int	ch;
{
	if ((display_ctrl & DC_SHADOW) == 0)
		return;
	if (vtcol < ncol) {
		if (display_ctrl & DC_SHADOW_SHOWTHRU) {
			vp[vtcol] = (vp[vtcol] & ~COLOR_MASK) | 
				BG(col_table.c_normal);
			vtcol++;
			}
		else 
			vp[vtcol++] = ch | BG(col_table.c_normal);
		}
}

/*******************************************************************/
/*   Erase  to  end  of line within a window. Draw the right hand  */
/*   margin and possibly the shadow to the right of the window.	   */
/*******************************************************************/
void
vteeol(space)
int	space;
{	vbyte_t	normal_space = space;
	register vbyte_t	*bp;
	register u_int16 col;

	col = wp->w_x + wp->w_w;
	if (col >= ncol)
		col = ncol;
	bp = &vp[vtcol];

	if (vtcol < col)
		indent = 0;
	while (vtcol < col)
		vtcol++, *bp++ = normal_space;

	if (RBORDERS(wp)) {
		vtputb(CH_VERTICAL | FG(col_table.c_normal) | BG(col_table.c_background));
		/***********************************************/
		/*   If   we  got  a  popup,  then  draw  the  */
		/*   shadow  to  the  right  of the window if  */
		/*   shadowing is enabled.		       */
		/***********************************************/
		if (wp->w_popup)
			vtshadow(' ');
		}

}

void
update() 
{
	register vbyte_t  *vp1;
	register vbyte_t  *vp2;
	register int    i;
	extern int display_enabled;

	if (updating || !display_enabled)
		return;
	updating++;
	if (force_update == FALSE && typeahead()) {
		updating--;
		return;
		}

	/***********************************************/
	/*   need_resize    may   be   set   from   a  */
	/*   windowing  system  telling us the window  */
	/*   has  been  redrawn.  We  try  and  avoid  */
	/*   doing  it  off  of  SIGWINCH  because we  */
	/*   may interrupt something we shouldn't.     */
	/***********************************************/
	if (need_resize)
		ttwinch();

	current_window = curwp;
	
	if (screen_garbled)			/* must update everything */
		for (wp = wheadp; wp; wp = wp->w_wndp) 
			wp->w_flag |= WFHARD;
	else if (curbp && curbp->b_anchor && curbp->b_anchor->a_type == MK_COLUMN)
		curwp->w_flag |= WFHARD;
	for (wp = wheadp; wp; wp = wp->w_wndp) {
		WINDOW *wp1;
		if (wp->w_flag) {
			/***********************************************/
			/*   A  buffer  character  map  has  priority  */
			/*   over a window map.			       */
			/***********************************************/
			win_cmap = wp->w_bufp->b_cmap ? wp->w_bufp->b_cmap : wp->w_cmap;
			upd_update(wp);
			
			/***********************************************/
			/*   Dirty  all  windows after this, cos this  */
			/*   could  be  a  process  window and we may  */
			/*   obscure the current window.	       */
			/***********************************************/
			for (wp1 = wp->w_wndp; wp1; wp1 = wp1->w_wndp)
				wp1->w_flag |= WFHARD;
			}
		wp->w_old_line = wp->w_line;
		wp->w_mined = wp->w_maxed = 0;
		wp->w_flag = 0;
		}

	if (screen_garbled) {
		tttop  = HUGE_CURSOR_VAL;   /* Forget where you set */
		ttbot  = HUGE_CURSOR_VAL;   /* scroll region.       */
		ttclear();
		for (i=0; i<nrow-1; ++i) {
			vflags[i] = 0;
			uline(i, vscreen[i], blanks);
			ucopy(vscreen[i], pscreen[i]);
			}
		redraw_echo_line();
		ucopy(blanks, pscreen[i]);
		}
	else {
		for (i=0; i<nrow-1; ++i) {
			if (vflags[i] & V_CHANGE) {
				vp1 = vscreen[i];
				vp2 = pscreen[i];
				uline(i, vp1, vp2);
				ucopy(vp1, vp2);
				}
			vflags[i] = 0;
			}
		}
	line_col(screen_garbled);
	set_cursor();

	screen_garbled = FALSE;                 /* Erase-page clears    */
	
	updating--;
}
void
upd_update(wp)
register WINDOW *wp;
{	int	old_line = wp->w_old_line;
	register int	line     = wp->w_line;
	register int	top	 = wp->w_top_line;
	int	height   = wp->w_h;
	int	half_height;
	int	bborder = BBORDERS(wp);
	int	bottom   = wp->w_y + wp->w_h + !bborder;
	int	flag;

	if (!bborder)
		height--;
	half_height = height / 2;
	
	/***********************************************/
	/*   Sideways scrolling check.		       */
	/***********************************************/
	window_indent = wp->w_indent;
	if (wp->w_col - window_indent > wp->w_w) {
		if ((wp->w_indent = wp->w_col - (int) wp->w_w) < 0)
			wp->w_indent = 0;
		}
	else if (wp->w_col <= window_indent) {
		if ((wp->w_indent = wp->w_col - 1) < 0)
			wp->w_indent = 0;
		}
	if (window_indent != wp->w_indent) {
		wp->w_flag |= WFHARD;
		window_indent = wp->w_indent;
		}
	indent = window_indent;

	if (top < 1)
		top = wp->w_top_line = 1;

	flag     = wp->w_flag;
# if 1
# define WORTHIT	(wp->w_w == ncol - 2)
# else
	/************************************************************************
	 *    This  definition  means  that  window  must  be  at  least half	*
	 *    screen  wide  in  order  to do scrolling. However, it can cause	*
	 *    display  bugs  in  that another window maybe destroyed in doing	*
	 *    that.  I  cant  work  out  an  easy fix, so we'll use the above	*
	 *    definition  that  only  allows  us to do scrolling if window is	*
	 *    full width.							*
	 ************************************************************************/
# define WORTHIT	(wp->w_w > ncol / 2 + 1)
# endif
	if (line < top && line > top - half_height &&
		old_line == top && WORTHIT && 
		num_popup_wins == 0 && pt.pt_noscroll[0] == 0 &&
		ttinsl(wp->w_y+1, bottom - !bborder, top - line)) {
		int diff = top - line;
		int j;
		wp->w_top_line = line;
		for (j = 0; j++ < diff; ) {
			upd_scroll_down(wp->w_y+2, pscreen, bottom);
			upd_scroll_down(wp->w_y+2, vscreen, bottom);
			}
		if ((flag & (WFHARD|WFEDIT)) == 0) {
			int i = wp->w_y + TBORDERS(wp);
			vtupdate(i, wp->w_line, i + diff, FALSE);
			return;
			}
		}
	else if (line >= top + height &&
	    line - old_line < half_height &&
	    line < top + height + half_height && WORTHIT &&
	    num_popup_wins == 0 && pt.pt_noscroll[0] == 0 &&
	    ttdell(wp->w_y+1, bottom - !bborder, line - top - height + 1)) {
		int diff = line - top - height;
		int j;
		wp->w_top_line += diff + 1;
		for (j = 0; j++ <= diff; ) {
			upd_scroll_up(wp->w_y+1, pscreen, bottom);
			upd_scroll_up(wp->w_y+1, vscreen, bottom);
			}
		if ((flag & (WFHARD|WFEDIT)) == 0) {
			int i = wp->w_y + TBORDERS(wp) +
				wp->w_line - wp->w_top_line;
			vtupdate(i - diff,  wp->w_line - diff, i, FALSE);
			return;
			}
		}
	else if (!(line >= top && line < top + height)) {
		int	rel_line = old_line - top;
		if (rel_line < 0)
			wp->w_top_line = wp->w_line;
		else if ((wp->w_top_line = line - rel_line) < 1)
			wp->w_top_line = 1;
		if (line >= wp->w_top_line + height)
			wp->w_top_line = line - height + bborder;
		flag |= WFHARD;
		}
	else if (flag & WFDELL) {
		int rel_line = line - top + 1;
		if (flag == WFDELL && WORTHIT &&
		    pt.pt_noscroll[0] == 0 &&
			ttdell(wp->w_y + rel_line, bottom, 1)) {
			int	j, i;
			upd_scroll_up(wp->w_y+rel_line, pscreen, bottom);
			upd_scroll_up(wp->w_y+rel_line, vscreen, bottom);
			i = bottom + TBORDERS(wp) - 1 - !bborder;
# define	XX top + bottom - wp->w_y - 1 - !bborder
			for (j = 0; j < nrow; j++) 
				vflags[j] = TRUE;
			vtupdate(i, XX, i, FALSE);
			return;
			}
		flag |= WFHARD;
		}
	else if ((flag & (WFINSL|WFHARD)) == WFINSL) {
		/***********************************************/
		/*   Handle  case  of  user  hitting <Enter>.  */
		/*   We  must  be  very  conservative that we  */
		/*   capture   only  an  <Enter>  and  not  a  */
		/*   combination  of  other  editing actions,  */
		/*   (e.g.  from  a  playback macro). We also  */
		/*   want  the  window  to be full width with  */
		/*   no  popups  in  view  and  the window to  */
		/*   not be indented to make this worthwhile.  */
		/***********************************************/
		int rel_line = line - top;
		if (wp->w_maxed == wp->w_mined + 1 && 
		    indent == 0 && rel_line > 0 &&
		    num_popup_wins == 0 && WORTHIT &&
		    wp->w_line + (wp->w_h / 3) < wp->w_bufp->b_numlines &&
		    rel_line < (wp->w_h * 3) / 4 &&
		    ttinsl(wp->w_y+rel_line, bottom, 1)) {
			int i;
			upd_scroll_down(wp->w_y + rel_line + 1, pscreen, bottom);
			upd_scroll_down(wp->w_y + rel_line + 1, vscreen, bottom);
			i = wp->w_y + rel_line;
			vtupdate(i, wp->w_line - 1, i + 1, FALSE);
			return;
			}
		flag |= WFHARD;
		}
		
	
	if (flag & WFHARD) {
		vtupdate(wp->w_y + 1, wp->w_top_line, 
			 wp->w_y+height, TRUE);
		}
	else if (flag & WFEDIT) {
		int i = wp->w_y + 1 - wp->w_top_line;
		int mined = wp->w_mined;
		int maxed = wp->w_maxed;
		
		if (mined < wp->w_top_line)
			mined = wp->w_top_line;
		if (maxed >= wp->w_top_line + wp->w_h)
			maxed = wp->w_top_line + wp->w_h - 1;
		vtupdate(i + mined,  mined, i + maxed, FALSE);
		}
}
void
upd_scroll_down(end, screen, bottom)
vbyte_t **screen;
int	end;
int	bottom;
{	register vbyte_t **bp = &screen[bottom];
	register vbyte_t *bp1 = *bp;
	register int i;
	for (i = bottom; i >= end; i--) {
		bp[0] = bp[-1];
		bp--;
		}
	*bp = bp1;
	ucopy(blanks, bp1);
}
void
upd_scroll_up(end, screen, bottom)
int	end;
vbyte_t **screen;
int	bottom;
{	register vbyte_t **bp = &screen[end];
	register vbyte_t *bp1 = *bp;
	register int i;

	for (i = end; i < bottom; i++) {
		bp[0] = bp[1];
		bp++;
		}
	if (screen == vscreen) {
		int *ip = &vflags[end];
		for (i = end+1; i < bottom; i++) {
			ip[0] = ip[1];
			ip++;
			}
		*ip = 0;
		}
	*bp = bp1;
	ucopy(blanks, bp1);
}
void
set_cursor()
{
	register u_int16    currow;
	register u_int16    curcol;
	u_int16	c;
	extern int prompting;
	extern int echo_col;
	
	if (prompting) {
		/***********************************************/
		/*   Put  cursor  back  where  it  was on the  */
		/*   command line.			       */
		/***********************************************/
		if (scrfn.scr_show_cursor)
			(*scrfn.scr_show_cursor)(nrow - 1, echo_col);
		else {
			ttmove(nrow-1, echo_col);
			ttflush();
			}
		return;
		}
	print_cursor(imode);
	if (curwp) {
		currow = curwp->w_y + (curwp->w_line - curwp->w_top_line) + 1;
		curcol = curwp->w_col - 1 - curwp->w_indent;
	
		if (curcol >= ncol)
			curcol = ncol-1;
	
		c = curcol + curwp->w_x;
		if (curwp->w_indent == 0)
			c -= !LBORDERS(curwp);
		if (c >= ncol)
			c = ncol - 1;
		if (c > curwp->w_x + curwp->w_w)
			c = (u_int16) curwp->w_x + curwp->w_w;
		if (scrfn.scr_show_cursor)
			(*scrfn.scr_show_cursor)(currow, c);
		else
			ttmove(currow, c);
		}
	ttflush();
}
void
ucopy(vvp, pvp) 
register vbyte_t *vvp; 
register vbyte_t *pvp; 
{
	register struct s80 {vbyte_t	b[80];} *sp1, *sp2;
	int	n = ncol;

	sp1 = (struct s80 *) vvp;
	sp2 = (struct s80 *) pvp;

	while (n >= 80) {
		*sp2++ = *sp1++;
		n -= 80;
		}
	if (n)
		memcpy((char *) sp2, (char *) sp1, sizeof (vbyte_t) * n);
}

/**********************************************************************/
/*   Following  two  routines  try  to  avoid  moving  cursor  until  */
/*   absolutely necessary.					      */
/**********************************************************************/
static int ucol = -1;
static int urow = -1;

/**********************************************************************/
/*   Put  a  character  on the screen, possibly moving cursor first.  */
/*   Also  if  cursor is on same line and 'not far' from the current  */
/*   character,  it  may  be  better  to  paint  the  characters  in  */
/*   between rather than trying to move the cursor.		      */
/**********************************************************************/
void
uputc(x)
vbyte_t x;
{	vbyte_t	*vp;

	if (ucol >= 0) {
		if (ttrow == urow && ucol > ttcol && ucol < ttcol + 5) {
			vp = vscreen[ttrow];
			vp += ttcol;
			while (ttrow == urow && ttcol < ucol) {
				ttputc(*vp++);
				}
			ttputc(x);
			ucol = -1;
			return;
			}
		else
			ttmove((u_int16) urow, (u_int16) ucol);
		ucol = -1;
		}
	if (x)
		ttputc(x);
}
void
umove(row, col)
u_int16 row, col;
{
	ucol = col;
	urow = row;
}	

/**********************************************************************/
/*   Routine  to  update  a  single row. Called from mouse driver to  */
/*   update the line where the cursor has moved from/to.	      */
/**********************************************************************/
void
upd_row(row)
int	row;
{
	uline(row, vscreen[row], pscreen[row]);
	ucopy(vscreen[row], pscreen[row]);
}
vbyte_t
disp_get_ch()
{
	return vp[vtcol];
}
void
uline(row, vvp, pvp) 
int	row;
vbyte_t *vvp; 
vbyte_t *pvp; 
{
	register int	i;
	register int	right, left;
	register vbyte_t   *leftv = vvp;
	register vbyte_t   *bp = pvp;
	register vbyte_t   *rightv;
	register vbyte_t   *cp5;
	vbyte_t normal_space;
	register int    nbflag;
	vbyte_t	space;
	int	cleared_space;
	int	black_space;
	extern char *CE;
	int	last_col = row == nrow - 1 ? ncol_minus_one : ncol;

	i = last_col;
	while (--i > 0 && *leftv == *bp)
		leftv++, bp++;
	/***********************************************/
	/*   We   can   return   straight   away   if  */
	/*   everything matches.		       */
	/***********************************************/
	if (i == 0)
		return;

	black_space = FG(col_table.c_normal) | ' ';
	rightv = &vvp[last_col];
	
	/***********************************************/
	/*   Look for the right most match.	       */
	/***********************************************/
	bp = &pvp[last_col];
	nbflag = FALSE;
	while (rightv[-1] == bp[-1]) {
		--bp;
		if (*--rightv != ' ')              /* Note non-blanks in   */
			nbflag = TRUE;          /* the right match.     */
		}

	cp5 = rightv;                              /* Is erase good?       */
	if (nbflag == FALSE) {
		while (cp5 != leftv && cp5[-1] == ' ')
			--cp5;
		if ((int)(rightv-cp5) <= tceeol)
			cp5 = rightv;
		}

	left = leftv - vvp;
	right = cp5 - vvp;
	
	/***********************************************/
	/*   If  we've  got  a  hook  to  draw to the  */
	/*   screen,  then  we'll  just  draw  in the  */
	/*   text   between   the   left   and  right  */
	/*   margin.  We  also  need  to  update  the  */
	/*   pscreen   first,   because   thats   the  */
	/*   reference thats used for the drawing.     */
	/***********************************************/
	if (scrfn.scr_print_string) {
		(*scrfn.scr_print_string)((int) row, (int) left, 
			(int) (right - left), &vvp[left]);
		return;
		}
	if (screen_garbled && pt.pt_clr_color_is_black[0] == NULL)
		cleared_space = FG(col_table.c_normal) | BG(col_table.c_background) | ' ';
	else
		cleared_space = -1;
	umove((u_int16) row, (u_int16)left);
	normal_space = (vbyte_t)
		(BG(col_table.c_background) | FG(col_table.c_normal) | ' ');
	while (left < right) {
		register vbyte_t *vp = &vvp[left];
		register vbyte_t *pp = &pvp[left];
		register int	n = 0;
		register int	bytes = right - left;
		int	oleft;
		
		while (bytes-- > 0) {
			if (*vp++ != *pp++)
				break;
			n++;
			}
		if (bytes < 0)
			break;
		left += n;
		
		umove((u_int16) row, left);
		space = vvp[left];
		if ((space & 0xff) != ' ') {
			uputc(vvp[left++]);
			continue;
			}
		/***********************************************/
		/*   See how many consecutive spaces we have.  */
		/***********************************************/
		oleft = left;
		while (vvp[left] == space && left != right)
			left++;
		i = left - oleft;
		/***********************************************/
		/*   If  we've  already  got  a  clear  line,  */
		/*   then just move the cursor.		       */
		/***********************************************/
		if (space == cleared_space) {
			continue;
			}

		/***********************************************/
		/*   If  we  only have a few spaces then just  */
		/*   draw  them  in.  Also its better to draw  */
		/*   spaces  than  erase  to  end  of line if  */
		/*   theres  more  to  the right of the blank  */
		/*   space than there is blank space.	       */
		/***********************************************/
		if (space != normal_space || pt.pt_space[0] || i < 10 || 
		    (pt.pt_clr_color_is_black[0] && space != black_space) ||
		    CE == NULL || i < last_col - right) {
		    	/***********************************************/
		    	/*   Force  color  and  cursor position to be  */
		    	/*   correct.				       */
		    	/***********************************************/
		    	uputc(space & ~0xff);
			ttspace(i, space, 2);
			if (pvp == blanks)
				oleft = left;
			else {
				while (oleft != left) {
					pvp[oleft++] = space;
					}
				}
			continue;
			}

		/***********************************************/
		/*   We've  decided  to  go  for erase to end  */
		/*   of line.				       */
		/***********************************************/
		{register vbyte_t *pp1, *ppend;
		right = last_col;
		/***********************************************/
		/*   Make  sure  we  do  the  erase to end of  */
		/*   line in the correct color combination.    */
		/***********************************************/
		uputc(space & ~0xff);
		tteeol();
		/***********************************************/
		/*   Make  internal  physical  buffer reflect  */
		/*   whats on the screen.		       */
		/***********************************************/
		if (pvp != blanks) {
			for (pp1 = &pvp[oleft], ppend = &pvp[right]; pp1 < ppend; )
				*pp1++ = space;
			}
		umove((u_int16) row, (u_int16)left);
		cleared_space = space;
		}
		}
	ucol = -1;
}


void
vtputs(s, col) 
register unsigned char *s; 
int	col;
{

	for (; *s != NULL; s++)
		vtputb(*s | col);
}
void
vtleft(i, x)
int	i;
int	x;
{
	vtmove(i, x);
	if (x != 0 || LBORDERS(wp)) {
		vtputb(CH_VERTICAL | FG(col_table.c_normal) | BG(col_table.c_background));
		}
}
void
vtupdate(i, line, n, redraw)
int	i;
int	line;
int	n;
int	redraw;
{	register int	x = wp->w_x - 1;
	WINDOW	*saved_wp = curwp;
	BUFFER	*saved_bp = curbp;
	vbyte_t	normal_space = FG(col_table.c_normal) | BG(col_table.c_background) | ' ';

	mark_areas = curwp == wp;
	curwp = wp;
	curbp = wp->w_bufp;
	if (curbp && mark_areas && (mark_areas = get_marked_areas(wp)))
		start_col--, end_col--;
	tl = line;

	if (redraw && wp->w_ttitle) 
		draw_title(TRUE, wp->w_y, wp);
		
	/***********************************************/
	/*   If  bottom  of  window  is  off  of  the  */
	/*   screen,   then   truncate   the   window  */
	/*   updating.				       */
	/***********************************************/
	if (n > nrow - 2)
		n = nrow - 2;
	for (; i <= n; i++) {
		vflags[i] = V_CHANGE;
		indent = 0;
		vtleft(i, x);
		indent = window_indent;
		if (curbp) {
			LINE *lp = vm_lock_line(line);
			normal_space = vtputl(lp);
			tl++;
			vm_unlock(line);
			line++;
			}
		vteeol(normal_space);
		}
	curwp = saved_wp;
	curbp = saved_bp;
	if (redraw && wp->w_btitle && BBORDERS(wp)) {
		line = wp->w_y + wp->w_h + BBORDERS(wp);
		if (line <= nrow - 2 && (line == nrow-2 || wp->w_popup))
			draw_title(FALSE, line, wp);

		if (line >= nrow - 1)
			return;
		/***********************************************/
		/*   If   we've   got  a  popup  window,  and  */
		/*   background  shadowing  is  enabled  then  */
		/*   print  the  line below the bottom of the  */
		/*   window.				       */
		/***********************************************/
		if (wp->w_popup) {
			int	j;
			vtshadow(' ');
			if (line + 1 != nrow - 1) {
				vflags[line + 1] = V_CHANGE;
				vtmove(line + 1, x + 1);
				for (j = wp->w_w + 1; j-- > 0; )
					vtshadow(' ');
				vtshadow('\\');
				}
			}
		
		}
}
void
draw_title(top, line, wp)
int	top;
int	line;
WINDOW	*wp;
{	register unsigned char *cp = (unsigned char *) 
		(top ? wp->w_ttitle : wp->w_btitle);
	int	denom = 0, i, j;
	int	l = cp ? strlen((char *) cp) : 0;
	extern int b_level;
	int	digits;
	int	ch;
	int	col = FG(col_table.c_normal) | BG(col_table.c_background);
static unsigned char corner_map[] = {
	'e',
	'a',
	'b',
	CH_BOT_LEFT,
/* 4 */	'c',
	CH_VERTICAL,
	CH_TOP_LEFT,
	CH_RIGHT_JOIN,
/* 8 */	'd',
	CH_BOT_RIGHT,
	CH_HORIZONTAL,
	CH_BOT_JOIN,
/*12 */	CH_TOP_RIGHT,
	CH_LEFT_JOIN,
	CH_TOP_JOIN,
	CH_CROSS
	};
	indent = 0;
	if (l == 0) 
		i = wp->w_w;
	else {
		if ((denom = wp->w_w - l - 2) < 0) {
			l = wp->w_w - 2;
			i = 0;
			}
		else
			i = denom / 2;
		}

	vtmove(line, wp->w_x-1);
	digits = (b_level > 1 && vtrow == nrow - 2)
			? (b_level > 9 ? 2 : 1) : 0;
	vflags[line] = V_CHANGE;

	ch = col | corner_map[wp->w_corner_hints[top ? TL_CORNER : BL_CORNER]];
	vtputb(ch);
	if (digits)
		j = i - digits + 1;
	else
		j = i;

	ch = CH_HORIZONTAL | col;
	while (j-- > 0)
		vtputb(ch);

	if (l) {
		cmap_t *cmp = win_cmap;
		int	title_col = wp == current_window && pt.pt_color ? 
			BG(col_table.c_background) | FG(col_table.c_select) : col;
		ch = col | ' ';
		vtputb(ch);
		/***********************************************/
		/*   Print  the  buffer  name  title. We need  */
		/*   to   watch   out   for  the  unprintable  */
		/*   characters  in  the  title. Also we want  */
		/*   to  use  the  base  char  map because if  */
		/*   user  is  viewing buffer in hex mode, we  */
		/*   dont want buffer name in hex.	       */
		/***********************************************/
		win_cmap = base_cmap;
		while (l-- > 0) {
			int	old_col = vtcol;
			vtputw(*cp++ | title_col);
			i -= (vtcol - old_col) - 1;
			}
		win_cmap = cmp;
		vtputb(ch);
		}
	else
		i = 0;

	ch = CH_HORIZONTAL | col;
	for (j = i + (denom & 1); j-- > 0; )
		vtputb(ch);
	if (digits) {
		char numbuf[4];
		sprintf(numbuf, "%d", b_level);
		vtputs((unsigned char *) numbuf, col);
		}
	else {
		ch = col | corner_map[wp->w_corner_hints[top ? TR_CORNER : BR_CORNER]];
		vtputb(ch);
		}
}
vbyte_t
vtputl(lp)
register LINE	*lp;
{	int	attr;
	u_int16	ch;
	register u_int16	col = ncol;
	register u_char *cp = lp->l_text;
	register u_char *cpend = cp + llength(lp);
	int	mc;
	int	dc;
	int	column;
	int	fg, bg;
	int	eol, inc;
	int	isansi = curbp->b_flag & BFANSI;
	int	start_vtcol = vtcol;
	int	lborder = LBORDERS(wp);
	void	(*put_fn)();
	
	fg = col_table.c_fg << FG_SHIFT;
	bg = col_table.c_bg << BG_SHIFT;

	fg |= bg;	

	if (wp->w_color >= 0)
		attr = FG(col_table.c_normal) | BG(wp->w_color);
	else
		attr = FG(col_table.c_normal) | BG(col_table.c_background);
	indent = window_indent;
	col = wp->w_x + wp->w_w;
	if (!RBORDERS(wp))
		col += 1;
	/***********************************************/
	/*   If  this  line  is  past  the end of the  */
	/*   buffer,    then    don't    allow    the  */
	/*   end-of-line  marker  to be drawn so user  */
	/*   can  see  which lines are real and which  */
	/*   are not.				       */
	/***********************************************/
	eol = lp->l_fp ? win_cmap->cm_eol[0] : ' ';

	/***********************************************/
	/*   If   we   dont   have   a  color  change  */
	/*   somewhere  in  the  middle  of  the line  */
	/*   (i.e.   its   all  normal  text  or  all  */
	/*   hilighted  text),  then  we can draw the  */
	/*   line without so many tests.	       */
	/***********************************************/
	if (!mark_areas || tl < start_line || tl > end_line) {
no_change:
		column = ncol < col ? ncol : col;
		for (; vtcol < column; cp++) {
			if (cp >= cpend) {
				vtputb(eol | attr);
				return attr | ' ';
				}
			ch = *cp;
			switch (win_cmap->cm_flags[ch]) {
				/***********************************************/
				/*   If  no  special  processing  needed  for  */
				/*   this  character  then just plaster it on  */
				/*   the window.			       */
				/***********************************************/
			  case 0:
			  case CMAP_TAB:
				vtputw(ch | attr);
				break;
			  case CMAP_ESCAPE:
			  	if (!isansi) {
					vtputw(ch | attr);
					break;
					}
				inc = vtgetcolor(cp+1, cpend, attr);
				if (inc == 0) {
					vtputw(ch | attr);
					break;
					}
				cp += inc;
				if (mark_areas && !(tl < start_line || tl > end_line)) {
					bg = (vt_color & BG_COLOR) >> BG_SHIFT;
					fg = (vt_color & FG_COLOR) >> FG_SHIFT;
					attr = (bg << FG_SHIFT) | (fg << BG_SHIFT);
					}
				else
					attr = vt_color;
				break;
			  case CMAP_BACKSPACE:
			  	if (isansi) {
					if (vtcol > start_vtcol)
						vtcol--;
					}
				else
					vtputw(ch | attr);
				break;
			  }
			}
		return attr | ' ';
		}


	if (mark_type == MK_LINE ||
	    (tl > start_line && tl < end_line && mark_type != MK_COLUMN)) {
	    	attr = fg;
		goto no_change;
		}

	mc = start_col + wp->w_x - window_indent - !lborder;
	dc = end_col + wp->w_x - window_indent - !lborder;
	column = ncol < col ? ncol : col;
	cp = lp->l_text;
	
	for (; vtcol < column; cp++) {
		int	ch_width = 1;
		int	mask;

		put_fn = vtputw;
		if (cp < cpend)
			ch = *cp;
		else {
			ch = eol;
			eol = ' ';
			put_fn = vtputb;
			}
		/***********************************************/
		/*   Special    character    processing    is  */
		/*   dependant on the current character map.   */
		/***********************************************/
		switch (win_cmap->cm_flags[ch]) {
		  case CMAP_ESCAPE:
		  	if (isansi) {
				inc = vtgetcolor(cp+1, cpend, attr);
				if (inc) {
					cp += inc;
					attr = vt_color;
					continue;
					}
				}
			break;
		  case CMAP_BACKSPACE:
		  	if (isansi) {
				if (vtcol > start_vtcol)
					vtcol--;
				continue;
				}
			break;
		  case CMAP_TAB: {
			int i = vtcol + window_indent - indent;
			ch_width = next_tab_stop(i - wp->w_x + 1) + 
				wp->w_x - i - !lborder;
			if (ch_width + vtcol >= col)
				ch_width = col - vtcol;
			ch = ' ';
			}
		  default:
		  	break;
		  }
		/***********************************************/
		/*   We  do  a  while  loop because we may be  */
		/*   filling  in  a  tab.  Only one iteration  */
		/*   for all other characters.		       */
		/***********************************************/
		while (ch_width-- > 0) {
			/***********************************************/
			/*   If  we  have  a  mark  which has a start  */
			/*   and  end  point  on  this  line  then do  */
			/*   this if.				       */
			/***********************************************/
			if ((tl == start_line && tl == end_line) || 
			    mark_type == MK_COLUMN) {
				if ((int) vtcol < mc)
					mask = attr;
				else if ((int) vtcol <= dc)
					mask = fg;
				else
					mask = attr;
				}
			else if (tl == start_line) {
				/***********************************************/
				/*   We  have  normal to the left and reverse  */
				/*   video to the right of the marker.	       */
				/***********************************************/
				mask = ((int) vtcol < mc) ? attr : fg;
				}
			else {
				/***********************************************/
				/*   We  have  reverse to the left and normal  */
				/*   to the right of the mark.		       */
				/***********************************************/
				mask = ((int) vtcol <= dc) ? fg : attr;
				}
			(*put_fn)(ch | mask);
			}
		}
	return FG(col_table.c_normal) | BG(col_table.c_background) | ' ';
}

/**********************************************************************/
/*   Following array maps ANSI colors to BRIEFs.		      */
/**********************************************************************/
int	ab_color_map[8] = {0, 4, 2, 6, 1, 5, 3, 7};
int	ba_color_map[8] = {0, 4, 2, 6, 1, 5, 3, 7};
int
vtgetcolor(cp, cpend, attr)
register u_char *cp, *cpend;
int	attr;
{	u_char 	*start = cp;
	DISPLAY dp;
	int	ch;
	int	bold = vt_color & FG(8);
	int	fg = (vt_color & FG_COLOR) >> FG_SHIFT;
	int	bg = (vt_color & BG_COLOR) >> BG_SHIFT;
	
	dp.d_escptr = dp.d_escape;
	dp.d_attr = bold ? 1 : 0;
	fg = ba_color_map[fg];
	bg = ba_color_map[bg];
	dp.d_color = attr;
	for ( ; cp < cpend; cp++) {
		ch = *cp;
		*dp.d_escptr = ch;
		if (dp.d_escptr < &dp.d_escape[MAX_ESCAPE-2])
			dp.d_escptr++;
		if (isalpha(ch)) {
			if (ch != 'm')
				return 0;
			*dp.d_escptr = NULL;
			p_escape(&dp, FALSE);
			vt_color = dp.d_color;
			if (dp.d_attr & 1)
				bold = FG(8);
			else
				bold = 0;
			vt_color |= bold;
			return cp - start + 1;
			}
		}
	return 0;
		
}
void
screen_dump()
{	char *filename = get_str(1);
	FILE *fp;
	register int line, col;
	vbyte_t	*lineptr;

	if (filename == NULL || *filename == NULL)
		filename = "/tmp/crisp.scr";
		
	if ((fp = fopen(filename, "w")) == NULL) {
		acc_assign_int(-1L);
		return;
		}
	for (line = 0; line < nrow-1; line++) {
		lineptr = vscreen[line];
		for (col = 0; col < ncol; col++) {
			int c = lineptr[col] & 0xff;
			switch (c) {
			  case CH_HORIZONTAL:	c = '-'; break;
			  case CH_VERTICAL:	c = '|'; break;
			  case CH_TOP_LEFT:
			  case CH_TOP_RIGHT:
			  case CH_BOT_LEFT:
			  case CH_BOT_RIGHT:
			  case CH_TOP_JOIN:
			  case CH_BOT_JOIN:
			  case CH_LEFT_JOIN:
			  case CH_RIGHT_JOIN:
			  case CH_CROSS:
			  		c = '+';
					break;
			  }
			fputc(c, fp);
			}
		fputc('\n', fp);
		}
		
	acc_assign_int(0L);
	fclose(fp);
}
/**********************************************************************/
/*   Function  to  add  a  file-descriptor  as  a  source  of input.  */
/*   Encapsulate  the  dirty  work  because  we may need to tell the  */
/*   operating system in a funny way (e.g. X-Windows).		      */
/**********************************************************************/
void
add_input_device(fd)
int	fd;
{
	FD_SET(fd, &sel_bits);
}

/**********************************************************************/
/*   Function  to  remove  a  file-descriptor from being a source of  */
/*   input.  Encapsulate  the dirty work because we may need to tell  */
/*   the operating system in a funny way (e.g. X-Windows).	      */
/**********************************************************************/
void
remove_input_device(fd)
int	fd;
{
	FD_CLR(fd, &sel_bits);
}

