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

# include        "list.h"
# include	<signal.h>
# include	"alt.h"
# include	"tty.h"

# define BEL     0x07                    /* BEL character.               */
# define LF      0x0A                    /* Line feed.                   */

/**********************************************************************/
/*   Size of the output buffer.					      */
/**********************************************************************/
# define NOBUF   136

/**********************************************************************/
/*   This  is  the output buffer. NOBUF is how many bytes we can put  */
/*   in  the  buffer. We leave a lot of room for expansion because a  */
/*   single  character  may  cause  us to output multiple characters  */
/*   (e.g. printing top bit or graphics characters).		      */
/**********************************************************************/
char    obuf[NOBUF+40];

/**********************************************************************/
/*   Number of characters in output buffer waiting to be printed.     */
/**********************************************************************/
int     nobuf;

/**********************************************************************/
/*   The  following  is  the  select  mask  for  reading. We set the  */
/*   terminal  input  file  descriptor  and  the  pty descriptors in  */
/*   this mask.							      */
/**********************************************************************/
fd_set	sel_bits;

long	st_charout;		/* Number of characters output.	*/
int	graphics_mode;		/* We've selected the graphics character set */
static int terminal_open = FALSE;
int	timeout_flag = FALSE;	/* TRUE when we are in VMIN/VTIME mode.*/
unsigned short kbdq = 0;
int old_ega_flag = -1;
/**********************************************************************/
/*   Variable set when we detect the window needs redrawing.	      */
/**********************************************************************/
int	need_resize = FALSE;

/**********************************************************************/
/*   Following  two  variables  hold  the number of rows and columns  */
/*   on the screen.						      */
/**********************************************************************/
u_int16		nrow;
u_int16		ncol;

extern int ega43_flag;
extern ref_t	*push_ref;
extern  int     tttop;
extern  int     ttbot;
extern	int	display_ctrl;
void	do_ega();
void	ega_switch();
void	set_attr();
void	setttysize();
char    *tgetstr();
char    *tgoto();


int     tceeol;                 /* Costs are set later */
int     tcinsl;
int     tcdell;

static  int     insdel;         /* Do we have both insert & delete line? */

#define TCAPSLEN 2048

char tcapbuf[TCAPSLEN];
typedef struct colors {
	char	*fg[16];
	char	*bg[16];
	} COLORS;
COLORS	colors = {
	{"\033[0;30m", "\033[0;34m", "\033[0;32m", "\033[0;36m", /* Foreground. */
	 "\033[0;31m", "\033[0;35m", "\033[0;33m", "\033[0;37m",
	 "\033[30;1m", "\033[34;1m", "\033[32;1m", "\033[36;1m",
	 "\033[31;1m", "\033[35;1m", "\033[33;1m", "\033[37;1m"},
	{"\033[40m", "\033[44m", "\033[42m", "\033[46m", /* Background. */
	 "\033[41m", "\033[45m", "\033[43m", "\033[47m",
	 "\033[40;1m", "\033[44;1m", "\033[42;1m", "\033[46;1m",
	 "\033[41;1m", "\033[45;1m", "\033[43;1m", "\033[47;1m"},
	};

int     LI;                     /* standard # lines */
int	AM;			/* Has auto-margins.		*/
char
	*AL,                    /* add line */
	*BC,			/* Backspace character. */
	*CD,			/* Clear to end of display.		*/
	*CE,			/* Clear to end of line. */
	*CL,			/* Clear screen 			*/
	*termcap_CM,		/* Cursor motion */
	*CS,                    /* set scroll region                    */
	*DC,			/* Delete a character.	*/
	*DL,                    /* del line */
	*EI,                    /* end insert mode */
	*HO,			/* Home cursor.		*/
	*IC,                    /* insert a single space */
	*IM,                    /* insert mode */
	*IS,			/* Init sequence. */
	*MB,			/* Turn on blinking.			*/
	*MD,			/* Make bold 				*/
	*ME,			/* Turn off all attributes.		*/
	*RS,			/* Reset sequence. 			*/
	*SE,			/* End standout mode.			*/
	*SO,			/* Start standout mode.			*/
	*SR,                    /* back index (used with scroll region  */
	*TE,                    /* term end --- end using cursor motion */
	*TI,                    /* term init -- start using cursor motion */
	*UP,			/* Cursor up.		*/
	*pAL,                   /* parameterized add line */
	*pDL;                   /* parameterized delete line */
	
char	*KS, *KE;               /* enter keypad mode, exit keypad mode  */
int     SG;     /* number of glitches, 0 for invisable, -1 for none     */
char	*termcap_strings = 
"AL DL al bc cd ce cl cm cs dc dl ei ho ic im is ks ke mb me mr rs se so sr te ti up ";
char	**termcap_ptrs[] = {
	&pAL, &pDL,
	&AL, &BC, &CD, &CE, &CL, &termcap_CM, &CS, &DC, &DL, &EI, &HO, &IC, 
	&IM, &IS, &KS, &KE, &MB, &MD, &ME, &RS, &SE, &SO, &SR, &TE, &TI, &UP
		};

/*
 * Initialize the terminal when the editor
 * gets started up.
 */
static char tcbuf[TCAPSLEN];

void
ttinit() 
{
	char *ggetenv();
	char *p, *tgetstr();
	char *tv_stype;
	char	*tptr;
	char	***cptr;
	extern int scrolling_regions_enabled;
	
# if	defined(SIGWINCH)
	signal(SIGWINCH, ttwinch);
# endif

	/***********************************************/
	/*   If  we're  on  a  windowing  system then  */
	/*   dont  bother  setting  any of this stuff  */
	/*   up.				       */
	/***********************************************/
	if (display_ctrl & DC_WINDOW)
		return;

	if ((tv_stype = ggetenv("TERM")) == NULL)
		panic("Environment variable TERM not defined!");

	if((tgetent(tcbuf, tv_stype)) != 1) {
		extern char *termcap_dir;
		fprintf(stderr, 
			"Terminal type '%s' not found in %s.\n",
			tv_stype, termcap_dir);
		exit(1);
		}

	p = tcapbuf;

	LI = tgetnum("li");
	AM = tgetflag("am");
	SG = tgetnum("sg");

	cptr = termcap_ptrs;
	for (tptr = termcap_strings; *tptr; tptr += 3) {
		char	*cp;
		char	tmpbuf[3];
		tmpbuf[0] = tptr[0];
		tmpbuf[1] = tptr[1];
		tmpbuf[2] = 0;
		if ((cp = tgetstr(tmpbuf, &p)) && *cp)
			**cptr = cp;
		cptr++;
		}
	if (scrolling_regions_enabled)
		CS = NULL;
	/*PC = pc ? *pc : 0;*/
	if (termcap_CM == NULL)
	    panic("No cursor move (cm) capability in termcap.");
	if (UP == NULL)
	    panic("No upline (up) capability in termcap.");

	if(!CE) tceeol = ncol;
	else    tceeol = charcost(CE);

	/* Estimate cost of inserting a line */
	if (CS && SR)   tcinsl = charcost(CS)*2 + charcost(SR);
	else if (pAL)   tcinsl = charcost(pAL);
	else if (AL)    tcinsl = charcost(AL);
	else            tcinsl = HUGE_CURSOR_VAL;   /* make this cost high enough */

	/* Estimate cost of deleting a line */
	if (CS)         tcdell = charcost(CS)*2 + 1;
	else if (pDL)   tcdell = charcost(pDL);
	else if (DL)    tcdell = charcost(DL);
	else            tcdell = HUGE_CURSOR_VAL;   /* make this cost high enough */

	/* Flag to indicate that we can both insert and delete lines */
	insdel = (AL || pAL) && (DL || pDL);

	if (p >= &tcapbuf[TCAPSLEN])
		panic("Terminal description too big!\n");

	/***********************************************/
	/*   tgoto()  doesnt  work properly with real  */
	/*   termcap -- leave this in for now.	       */
	/***********************************************/
# if	1 || defined(MY_TERMCAP)
	{
	void close_termcap();
	close_termcap();
	}
# endif
}
void
ttinit1(repaint)
int	repaint;
{
	flush_col_cache();
	ttresize();                     /* set nrow & ncol      */
	if (scrfn.scr_print_string)
		return;
	if (repaint) {
		putpad(TI);
		putpad(IS);
		putpad(KS);
		}
}


/**********************************************************************/
/*   Routine  to  decypher  termcap style strings. Needed by code in  */
/*   set_term_escapes.						      */
/**********************************************************************/

# define	XDIGIT(x)	(x <= '9' ? x - '0' : \
				 (x >= 'a' && x <= 'z') ? x - 'a' + 10 : \
				 (x - 'A' + 10))
char *
tcopy_string(dp, bp, delim)
register	char	*dp, *bp;
int	delim;
{	register int n;

	while (*bp != delim && *bp) {
		if (*bp == '^') {
			*dp++ = *++bp & 0x1f;
			bp++;
			continue;
			}
		if (*bp != '\\') {
			*dp++ = *bp++;
			continue;
			}
		switch (*++bp) {
		  case 'E':	*dp++ = 0x1b; break;
		  case 'r':	*dp++ = '\r'; break;
		  case 'n':	*dp++ = '\n'; break;
		  case 't':	*dp++ = '\t'; break;
		  case 'b':	*dp++ = '\b'; break;
		  case 'f':	*dp++ = '\f'; break;
		  case '0': case '1': case '2': case '3':
			n = 0;
			while (*bp >= '0' && *bp <= '7')
				n = 8*n + *bp++ - '0';
			bp--;
			*dp++ = (char) n;
			break;
		  case 'x':
			bp++;
			n = XDIGIT(*bp);
			bp++;
			n = n * 16 + XDIGIT(*bp);
			*dp++ = (char) n;
			break;
		  default:	*dp++ = *bp; break;
		  }
		bp++; 
		}
	*dp++ = NULL;
	return dp;
}

void
tttidy() 
{
	if (scrfn.scr_print_string)
		return;
	if (RS && *RS) putpad (RS);     /* set the term back to normal mode */
	if (TE && *TE) putpad (TE);     /* set the term back to normal mode */
	putpad(KE);
}

/*
 * Move the cursor to the specified
 * origin 0 row and column position. Try to
 * optimize out extra moves; redisplay may
 * have left the cursor in the right
 * location last time!
 */
void
ttmove(row, col) 
u_int16	row;
u_int16	col;
{
	char        *tgoto();
	extern char *HO;
	if (ttrow == row && ttcol == col)
		return;

	if (scrfn.scr_show_cursor) {
		(*scrfn.scr_show_cursor)(row, col);
		}
	else if (col == 0 && row == 0 && HO)
		putpad(HO);
# if !defined(LINEFEED_DOES_CR)
	/***********************************************/
	/*   DO  NOT  DO  A  LINEFEED IF WE'RE IN THE  */
	/*   LAST  COLUMN  OF  THE  SCREEN. This gets  */
	/*   over  a  problem  on  Dec  terminals and  */
	/*   Xterm  where  if  we  move  down  a line  */
	/*   when   we're  in  column  zero,  we  may  */
	/*   actually  find  we're  in  column  80 of  */
	/*   previous line -- yeuuuuuuckkkk!!	       */
	/***********************************************/
	else if (ttcol && ttcol == col && row == ttrow+1) {
		ttputpad('\n');
		}
	else if (ttcol == col+1 && row == ttrow+1) {
		ttputpad('\n');
		ttputpad('\b');
		}
# endif
	else if (ttcol-1 == col && row == ttrow)
		ttputpad('\b');
	else if (ttrow == row && pt.pt_escC[0] && col > ttcol && ttcol > 1) {
		putpad(tgoto(pt.pt_escC, 0, col - ttcol));
		}
	else {
		char *cp;
		if (termcap_CM) {
			cp = tgoto(termcap_CM, (int) col, (int) row);
			putpad(cp);
			}
		}
	ttrow = row;
	ttcol = col;
}
/*
 * Erase to end of line.
 */
void
tteeol() 
{
	if (scrfn.scr_print_string)
		return;
	if (CE)
		putpad(CE);
	else {
		register int i=ncol-ttcol;
		while(i--) ttputc(' ');
		ttrow = ttcol = HUGE_CURSOR_VAL;
		}
}
/**********************************************************************/
/*   Function  to  clear  the  screen.  Make  sure  that  we set the  */
/*   appropriate  background  color  before  clearing,  otherwise if  */
/*   user  goes  from  a  non-black  background  to black, we end up  */
/*   with the blank spaces on the screen in the old color style.      */
/**********************************************************************/
void
ttclear()
{

	if (scrfn.scr_print_string)
		return;	
	ttcolor(FG(col_table.c_normal) | BG(col_table.c_background));
	if (CL == NULL) {
		ttmove((u_int16) 0, (u_int16) 0);
		tteeop();
		}
	else {
		ttrow = 0;
		ttcol = 0;
		putpad(CL);
		}
}
/*
 * Erase to end of page.
 */
void
tteeop() 
{
	if (scrfn.scr_print_string)
		return;
    if(CD) putpad(CD);
    else {
	putpad(CE);
	if (insdel) ttdell(ttrow + 1, LI, LI - ttrow - 1);
	else {          /* do it by hand */
	    register int line;
	    for (line = ttrow + 1; line <= LI; ++line) {
		ttmove((u_int16) line, (u_int16) 0);
		tteeol();
	    }
	}
	ttrow = ttcol = HUGE_CURSOR_VAL;
    }
}

/*
 * Make a noise.
 */
void
ttbeep() 
{

	if (scrfn.scr_beep) {
		(*scrfn.scr_beep)();
		return;
		}
	ttputc(BEL);

	/***********************************************/
	/*   Adjust  cursor  because  ttputc() thinks  */
	/*   BEL moves the cursor.		       */
	/***********************************************/
	ttcol--;
	ttflush();
}
int
beep()
{
	ttbeep();
	return 0;
}

/*
 * Insert nchunk blank line(s) onto the
 * screen, scrolling the last line on the
 * screen off the bottom.  Use the scrolling
 * region if possible for a smoother display.
 * If no scrolling region, use a set
 * of insert and delete line sequences
 */
int
ttinsl(row, bot, nchunk) 
int	row;
int	bot;
int	nchunk;
{
	register int        i;

	if (scrfn.scr_insert_line)
		return (*scrfn.scr_insert_line)(row, bot, nchunk);

	if (scrfn.scr_print_string)
		return FALSE;
	if (row == bot) {           /* Case of one line insert is special  */
		ttmove((u_int16) row, (u_int16) 0);
		tteeol();
		return TRUE;
	}
	if (CS && SR) {             /* Use scroll region and back index     */
		ttwindow(row,bot);
		ttmove((u_int16) row, (u_int16) 0);
		while (nchunk--) putpad(SR);
		/*ttnowindow();*/
		return TRUE;
		}
	if (insdel) {
		ttmove((u_int16) (1+bot-nchunk), (u_int16) 0);
		if (pDL) 
			putpad (tgoto(pDL, 0, nchunk));
		else 
			for (i=0; i<nchunk; i++)/* For all lines in the chunk */
				putpad(DL);
		ttmove((u_int16) row, (u_int16) 0);
		if (pAL) 
			putpad (tgoto(pAL, 0, nchunk));
		else 
			for (i=0; i<nchunk; i++)/* For all lines in the chunk*/
				putpad(AL);
		ttrow = HUGE_CURSOR_VAL;
		ttcol = HUGE_CURSOR_VAL;
		return TRUE;
		}
	return FALSE;
}

/**********************************************************************/
/*   Delete  nchunk  line(s)  from  "row", replacing the bottom line  */
/*   on  the  screen  with  a  blank  line.  Unless  we're using the  */
/*   scrolling  region,  this  is  done  with  a crafty sequences of  */
/*   insert  and  delete  lines. The presence of the echo area makes  */
/*   a boundry condition go away.				      */
/**********************************************************************/
int
ttdell(row, bot, nchunk)
int	row;
int	bot;
int	nchunk;
{	register int        i;

	if (scrfn.scr_delete_line)
		return (*scrfn.scr_delete_line)(row, bot, nchunk);

	if (scrfn.scr_print_string)
		return FALSE;
	if (row == bot) {           /* One line special case        */
		ttmove((u_int16) row, (u_int16) 0);
		tteeol();
		return TRUE;
		}
	if (CS) {                   /* scrolling region     */
		ttwindow(row, bot);
		ttmove((u_int16) bot, (u_int16) 0);
		while (nchunk--) ttputc(LF);
		return TRUE;
		}
	if(insdel) {
		ttmove((u_int16) row, (u_int16) 0);
		if (pDL) 
			putpad (tgoto(pDL, 0, nchunk));
		else 
			for (i=0; i<nchunk; i++)
				putpad(DL);
		ttmove((u_int16) (1+bot-nchunk), (u_int16) 0);
		if (pAL) 
			putpad (tgoto(pAL, 0, nchunk));
		else 
			for (i=0; i<nchunk; i++)
				putpad(AL);
		ttrow = HUGE_CURSOR_VAL;
		ttcol = HUGE_CURSOR_VAL;
		return TRUE;
		}
	return FALSE;	
}

/**********************************************************************/
/*   This  routine  sets  the  scrolling window on the display to go  */
/*   from  line  "top"  to  line  bot"  (origin  0,  inclusive). The  */
/*   caller  checks  for  the pathalogical 1 line scroll window that  */
/*   doesn't  work  right,  and  avoids  it. The "ttrow" and "ttcol"  */
/*   variables  are  set  to  a  crazy value to ensure that the next  */
/*   call  to  "ttmove"  does  not  turn  into  a  no-op (the window  */
/*   adjustment moves the cursor).				      */
/**********************************************************************/
void
ttwindow(top, bot)
int	top;
int	bot;
{
	if (scrfn.scr_print_string)
		return;
	if (CS && (tttop!=top || ttbot!=bot)) {
		putpad(tgoto(CS, bot, top));
		ttrow = HUGE_CURSOR_VAL;                   /* Unknown.             */
		ttcol = HUGE_CURSOR_VAL;
		tttop = top;                    /* Remember region.     */
		ttbot = bot;
	}
}

static	int	tthue = -1;
static	int 	tt_fg = -1;
static	int	tt_bg = -1;
void
flush_col_cache()
{
	tthue = tt_fg = tt_bg = -1;
}
/*
 * Set the current writing color to the
 * specified color. Watch for color changes that are
 * not going to do anything (the color is already right)
 * and don't send anything to the display.
 */
void
ttcolor(color)
int	color;
{	int	fg, bg;

	if (tthue == color)
		return;
	tthue = color;
	if (!pt.pt_color) {
		int	b = BG(col_table.c_background);
		if (color == (FG(col_table.c_normal) | b) ||
		    color == (FG(col_table.c_messages) | b)) {
			putpad(SE); 
			}
		else if (color == (FG(col_table.c_select) | b) ||
		    color == (FG(col_table.c_error) | b)) {
			putpad(SE);
			putpad(MD);
			}
		else {
			putpad(SO);
			}
		return;
		}
	fg = (color & FG_COLOR) >> FG_SHIFT;
	bg = (color & BG_COLOR) >> BG_SHIFT;
	if (fg != tt_fg) {
		putpad(colors.fg[fg]);
		tt_fg = fg;
		/***********************************************/
		/*   If  setting  foreground color may affect  */
		/*   the  background  then  we  have to force  */
		/*   the background color to be set again.     */
		/***********************************************/
		if (bg == tt_bg && pt.pt_0m)
			tt_bg = -1;
		}
	if (bg != tt_bg) {
		putpad(colors.bg[bg]);
		tt_bg = bg;
		}
}
void
ttresize() 
{	int	orow = nrow;
	int	ocol = ncol;
	
	setttysize();
	if (ncol != ocol || nrow != orow) {
		vtinit1();
		ttwinch1(ocol, orow);
		}
}
/**********************************************************************/
/*   Called  when  we  receive  a  SIGWINCH  signal  telling  us the  */
/*   windows changed size.					      */
/**********************************************************************/
void
ttwinch()
{
# if	defined(SIGWINCH)
	need_resize = FALSE;
	ttresize();
	signal(SIGWINCH, ttwinch);
	update();
# endif
}
/**********************************************************************/
/*   Function  called  to  scale  windows after a window size change  */
/*   (SIGWINCH).  This  is  called  when  we  run  under a windowing  */
/*   system,  e.g.  SunView  or  X-Windows and the terminal emulator  */
/*   window  is  changed. We try and rescale the windows so that the  */
/*   layout  is  the  same, but we use the real-estate properly. Its  */
/*   not   guaranteed  to  work  under  silly  window  resizes,  but  */
/*   hopefully it'll work 99% of the time.			      */
/**********************************************************************/
void
ttwinch1(oncol, onrow)
int	oncol;
int	onrow;
{	register WINDOW *wp;
	WINDOW	*last_wp;
	extern	int	screen_garbled;
	int	diffcol, diffrow;
	float	height_frac = (float) (nrow - 1) / (float) (onrow - 1);
	
	diffcol = ncol - oncol;
	diffrow = nrow - onrow;
	for (wp = wheadp; wp; wp = wp->w_wndp) {
		int 	right_edge;
		int	last_line;

		/***********************************************/
		/*   Adjust  window  width  only if its right  */
		/*   hand  edge  touches  the right hand edge  */
		/*   of the window.			       */
		/***********************************************/
		right_edge = wp->w_x + wp->w_w;
		last_line = wp->w_y + wp->w_h;
		if (wp->w_tiled)
			right_edge++;
		if (right_edge == oncol)
			wp->w_w += diffcol;
			
		wp->w_old_line = -1;

		/***********************************************/
		/*   Scale  the  height of the window so that  */
		/*   it    keeps   approximately   the   same  */
		/*   fraction of the real estate.	       */
		/***********************************************/
		wp->w_y *= height_frac;
		
		/***********************************************/
		/*   Don't   adjust   height  for  popups  --  */
		/*   we'll  get  it  wrong  no matter what we  */
		/*   do.  If  the  popup  is  too big for the  */
		/*   new  window,  then just clip it, and let  */
		/*   the user try and recover the garbage.     */
		/***********************************************/
		if (!wp->w_popup)
			wp->w_h = (wp->w_h + 2) * height_frac - 2;
		}
	/***********************************************/
	/*   Having  scaled  the  background windows,  */
	/*   try and adjust for overlapping errors.    */
	/***********************************************/
	for (wp = wheadp; wp && wp->w_wndp; wp = wp->w_wndp) {
		if (wp->w_popup || wp->w_wndp->w_popup)
			continue;
		/***********************************************/
		/*   If  bottom  of  this window extends past  */
		/*   top  of  next  window and windows aren't  */
		/*   side  by  side  then  set bottom of this  */
		/*   window   to  line  before  top  of  next  */
		/*   window.				       */
		/***********************************************/
		if (wp->w_y + wp->w_h + 2 >= wp->w_wndp->w_y && 
		    wp->w_y != wp->w_wndp->w_y)
			wp->w_h = wp->w_wndp->w_y - 1 - wp->w_y;
		}
	/***********************************************/
	/*   If  we've  got some spare blank lines at  */
	/*   the  bottom  of  the screen then add the  */
	/*   spare  lines  all  windows  which  don't  */
	/*   quite reach the bottom of the screen.     */
	/***********************************************/
	last_wp = wp;
	for (wp = wheadp; wp; wp = wp->w_wndp)
		if (!wp->w_popup)
			last_wp = wp;
	if (last_wp) {
		for (wp = wheadp; wp; wp = wp->w_wndp)
			if (!wp->w_popup && 
			   wp->w_y + wp->w_h == last_wp->w_y + last_wp->w_h &&
			   wp != last_wp)
			wp->w_h = nrow - 3 - wp->w_y;
		last_wp->w_h = nrow - 3 - last_wp->w_y;
		}
		
	/***********************************************/
	/*   Make sure whole screen gets redrawn.      */
	/***********************************************/
	screen_garbled = TRUE;
}

static int cci;

/* fake char output for charcost() */
static void
fakec(c) 
char c; 
{
#ifdef  lint
	c++;
#endif
	cci++;
}

/* calculate the cost of doing string s */
int
charcost (s)
char *s; 
{
    cci = 0;

    tputs(s, nrow, (int (*)()) fakec);
    return cci;
}
void
putpad(str) 
char *str; 
{
	if (str == NULL)
		return;
	tputs(str, 1, (int (*)()) ttputpad);
}
/**********************************************************************/
/*   Function  to  print  some  spaces.  'stay' says whether we want  */
/*   the  cursor  to stick at the starting point or stick at the end  */
/*   of  the  region.  If set to 2 then this is a don't care -- i.e.  */
/*   leave cursor as it is.					      */
/**********************************************************************/
void
ttspace(n, space, stay)
int	n;
int	space;
int	stay;
{	u_int16	oldrow = ttrow;
	u_int16	oldcol = ttcol;
	int	num_moves;
	
	if (n <= 0)
		return;
	if (stay != TRUE)
		num_moves = 4;
	else
		num_moves = 0;
	ttputc(space & ~0xff);
	if (scrfn.scr_repeat && n > 5 + num_moves) {
		ttflush();
		(*scrfn.scr_repeat)(space, n, ttrow, ttcol);
		if (stay != TRUE)
			ttmove(oldrow, oldcol + n);
		}
	else if (pt.pt_space[0] && n > 4 + num_moves) {
		putpad(tgoto(pt.pt_space, 0, n));
		if (stay != TRUE)
			ttmove(oldrow, oldcol + n);
		}
	else {
		while (n-- > 0)
			ttputc(space);
		}
	if (stay == TRUE)
		ttmove(oldrow, oldcol);
	
}
/*
void
ttrepeat(ch, n)
int	ch;
int	n;
{
	if (n > 12 && pt.pt_repeat[0]) {
		ttputc(ch);
		putpad(tgoto(pt.pt_repeat, 0, n-1));
		ttmove(ttrow, ttcol + n - 1);
		}
	while (n-- > 0)
		ttputc(ch);


}
*/
void
ttopen(repaint)
int	repaint;
{
	if (display_ctrl & DC_WINDOW)
		return;
	terminal_open = TRUE;	
	add_input_device(TTY_FD);
	sys_open();
	ttinit1(repaint);
}
void
ttclose()
{
	if (!terminal_open)
		return;
	ttflush();
	sys_close();
	old_ega_flag = -1;
}

/*
 * Write character to the display.
 * Characters are buffered up, to make things
 * a little bit more efficient.
 */
void
ttputpad(c)
int	c;
{
	if (nobuf >= NOBUF)
		ttflush();
	obuf[nobuf++] = (char) c;
}
/*
void
ttputs(str)
register char	*str;
{
	while (*str)
		ttputc(*str++);
}
*/
void
ttputc(c)
register int c;
{
	register int	color = c & COLOR_MASK;
	extern int AM;

	if (nobuf >= NOBUF)
		ttflush();

	ttcolor(color);

	c &= 0xff;
	if (c & 0x80) {
		if (!graphics_mode)
			enter_graphics_mode();
		if (c >= CH_MIN && c <= CH_MAX) {
			char	*cp = "?";
			switch (c) {
			  case CH_HORIZONTAL:	cp = pt.pt_horizontal; break;
			  case CH_VERTICAL:	cp = pt.pt_vertical; break;
			  case CH_TOP_LEFT:	cp = pt.pt_top_left; break;
			  case CH_TOP_RIGHT:	cp = pt.pt_top_right; break;
			  case CH_BOT_LEFT:	cp = pt.pt_bot_left; break;
			  case CH_BOT_RIGHT:	cp = pt.pt_bot_right; break;
			  case CH_TOP_JOIN:	cp = pt.pt_top_join; break;
			  case CH_BOT_JOIN:	cp = pt.pt_bot_join; break;
			  case CH_LEFT_JOIN:	cp = pt.pt_left_join; break;
			  case CH_RIGHT_JOIN:	cp = pt.pt_right_join; break;
			  case CH_CROSS:	cp = pt.pt_cross; break;
			  	  }
			strcpy(obuf + nobuf, cp);
			nobuf += strlen(cp);
			}
		else if (scrfn.scr_8bit) {
			(*scrfn.scr_8bit)(c);
			}
		else if (c >= 0x80 + ' ')
			goto hell;
		else	{
			if (c == 0x1b && pt.pt_escape[0])
				strcpy(obuf+nobuf, pt.pt_escape);
			else
				sprintf(obuf+nobuf, pt.pt_character, c ? c : 0x80);
			nobuf += strlen(obuf+nobuf);
			}
		goto end_of_function;
		}

	if (graphics_mode) {
		exit_graphics_mode();
		}		
	/***********************************************/
	/*   Null   character  may  be  sent  by  the  */
	/*   display  code  to  force  side effect of  */
	/*   color    change.   (Shouldnt   do   this  */
	/*   anymore but we'll leave it here).	       */
	/***********************************************/
	if (c == 0)
		return;
	/***********************************************/
	/*   Control  characters  under  DOS  &  OS/2  */
	/*   require special support.		       */
	/***********************************************/
	if (c < ' ' && scrfn.scr_8bit) {
		(*scrfn.scr_8bit)(c);
		goto end_of_function;
		}
hell:
	obuf[nobuf++] = (char) c;
	if (c == '\b') {
		ttcol--;
		return;
		}
end_of_function:
	if (++ttcol >= ncol && AM) {
		ttcol = 0;
		ttrow++;
		}		
}
/**********************************************************************/
/*   Switch terminal into graphics character set mode.		      */
/**********************************************************************/
void
enter_graphics_mode()
{
	if (pt.pt_graphics[0]) {
		graphics_mode = TRUE;
		strcpy(obuf + nobuf, pt.pt_graphics);
		nobuf += strlen(pt.pt_graphics);
		}
}
/**********************************************************************/
/*   Take  terminal  out  of  graphics  character  set  mode. We get  */
/*   called during screen display and if user hits Ctrl-Z, etc.	      */
/**********************************************************************/
void
exit_graphics_mode()
{
	if (graphics_mode && pt.pt_text[0]) {
		graphics_mode = FALSE;
		strcpy(obuf + nobuf, pt.pt_text);
		nobuf += strlen(pt.pt_text);
		}
}
void
ttflush()
{	extern int background;

	if (scrfn.scr_flush) {
		(*scrfn.scr_flush)();
		}
	if (nobuf != 0) {
		st_charout += nobuf;
		if (background == FALSE)
			sys_write(1, obuf, nobuf);
		nobuf = 0;
	}
}
/**********************************************************************/
/*   Function  which  returns non-zero if we have more characters to  */
/*   process  from  the  various input streams, e.g. the keyboard or  */
/*   keyboard macros.						      */
/**********************************************************************/
int
typeahead()					     
{
	if (get_push(push_ref, FALSE))
		return TRUE;
	if (grab_char(FALSE))
		return TRUE;

	if (kbdq)
		return TRUE;

	if (scrfn.scr_event_pending)
		return kbdq = (*scrfn.scr_event_pending)();
	kbdq = sys_checkc();
	return kbdq;
}
/**********************************************************************/
/*   Function  to  get  a  character from the typeahead or push back  */
/*   buffer.  flag16  indicates  whether we have a 16-bit keycode or  */
/*   whether we got a raw unprocessed keystroke.		      */
/**********************************************************************/
int
get_typeahead(flag16)
int	*flag16;
{	int	ch;

	if ((ch = get_push(push_ref, TRUE)) != 0) {
		*flag16 = TRUE;
		return ch;
		}
	if ((ch = grab_char(TRUE)) != 0) {
		*flag16 = TRUE;
		return ch;
		}
	if (kbdq) { 
		ch = kbdq;
		kbdq = 0;
		*flag16 = ch & ~0xff;
		return ch;
		}
	return -1;
}
void
panic(s)
char *s;
{
	ewprintf("CRISP PANIC: %s", s);
	u_close();
	vttidy(FALSE);
	printf("\n");
	abort();
	exit(1);
}
void
do_ega()
{	int	arg = argv[1].l_flags == F_NULL ? 0 : argv[1].l_int;

	acc_assign_int((long) (ega43_flag ? 43 : 25));
	if (argv[1].l_flags == F_NULL)
		return;
		
	if (arg == 43)
		arg = 1;
	else if (arg == 25)
		arg = 0;
	ega_switch(arg);
}
void
setttysize()
{	extern char *ggetenv();
	extern int	AM;
#if  defined(TIOCGWINSZ) && defined(WINDOW_SIZE_SUPPORTED)
	struct winsize	winsize;
# endif

	if (scrfn.scr_window_size)
		(*scrfn.scr_window_size)(&nrow, &ncol);
	else
#if  defined(TIOCGWINSZ) && defined(WINDOW_SIZE_SUPPORTED)
	if (ioctl(0, TIOCGWINSZ, (char *) &winsize) == 0) {
		nrow = winsize.ws_row;
		ncol = winsize.ws_col;
		}
	if (nrow == 0 || ncol == 0)
#endif
	if ((nrow= (u_int16) tgetnum ("li")) == 0
	   || (ncol= (u_int16) tgetnum ("co")) == 0) {
		nrow = 24;
		ncol = 80;
		}
# if	defined(CONS_GET)
	{extern int console_mode;
	if (console_mode > 0) {
		switch (console_mode) {
		  case M_ENH_C80x43:
			ega_switch(1);
			nrow = 43;
			break;
		  case M_ENH_C80x25:
			ega_switch(0);
			nrow = 25;
			break;
		  }
		}
	}
# endif

	/***********************************************/
	/*   Ignore  LINES  and  COLUMNS  environment  */
	/*   variables  if  running  from a windowing  */
	/*   system.				       */
	/***********************************************/
	if (scrfn.scr_window_size == NULL) {
		if (ggetenv("LINES")) {
			nrow = atoi(ggetenv("LINES"));
			AM = FALSE;
			}
		if (ggetenv("COLUMNS")) {
			ncol = atoi(ggetenv("COLUMNS"));
			AM = FALSE;
			}
		}
}
void
ega_switch(flag)
int	flag;
{
# if 0
	/***********************************************/
	/*   Code  commented  out  for  now because I  */
	/*   never  use  it and it gets in the way of  */
	/*   ^Z in ttwinch1()			       */
	/***********************************************/
	int	onrow = nrow;
		
	if (flag)
		flag = 1;

	if (old_ega_flag == flag)
		return;
	
	old_ega_flag = ega43_flag = flag;
	if (flag) {
# if	defined(SW_ENHC80x43)
		ioctl(1, SW_ENHC80x43, 1);
		nrow = 43;
# endif
		}
	else {
# if	defined(SW_ENHC80x25)
		ioctl(1, SW_ENHC80x25, 1);
		nrow = 25;
# endif
		}
	ttwinch1(ncol, onrow);
	flush_col_cache();
# endif
}
