/*
 * ========================================================================
 * Copyright 2006-2008 University of Washington
 * Copyright 2013-2022 Eduardo Chappa
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * ========================================================================
 *
 * Program:	Display routines
 */

#include <system.h>
#include <general.h>


#include "../estruct.h"
#include "../keydefs.h"
#include "../pico.h"
#include "../mode.h"
#include "../edef.h"

#include "raw.h"
#include "color.h"
#include "tty.h"
#include "terminal.h"
#include "getkey.h"

#ifndef _WINDOWS
extern long gmode;


/*
 * Useful Defaults
 */
#define	MARGIN	8			/* size of minimim margin and	*/
#define	MROW	2			/* rows in menu                 */


/* any special properties of the current terminal */
unsigned term_capabilities = (TT_EOLEXIST | TT_SCROLLEXIST | TT_INSCHAR | TT_DELCHAR);


/*
 *
 */
unsigned
tthascap(void)
{
    return(term_capabilities);
}


#if	HAS_TERMINFO

/*
 * terminfo-based terminal i/o and control routines
 */


/* internal prototypes */
static int      tinfomove(int, int);
static int      tinfoeeol(void);
static int      tinfoeeop(void);
static int      tinfobeep(void);
static int      tinforev(int);
static int      tinfoeri(void);
static int      tinfoopen(void);
static int      tinfoterminalinfo(int);
static int      tinfoclose(void);
static void     setup_dflt_esc_seq(void);
static void     tinfoinsert(UCS);
static void     tinfodelete(void);

extern int      tput();
extern int      tputs(char *, int, int (*)(int));
extern char    *tgoto(char *, int, int);
extern char    *tigetstr (char *);
extern int      setupterm(char *, int, int *);
extern int      tigetnum(char *);
extern int      tigetflag(char *);

/**
 ** Note: The tgoto calls should really be replaced by tparm calls for
 ** modern terminfo. tgoto(s, x, y) == tparm(s, y, x).
 **/

int	_tlines, _tcolumns;
char	*_clearscreen, *_moveto, *_up, *_down, *_right, *_left,
	*_setinverse, *_clearinverse,
	*_setunderline, *_clearunderline,
	*_setbold, *_clearallattr,	/* there is no clear only bold! */
	*_cleartoeoln, *_cleartoeos,
	*_deleteline,		/* delete line */
	*_insertline,		/* insert line */
	*_scrollregion,		/* define a scrolling region, vt100 */
	*_insertchar,		/* insert character, preferable to : */
	*_startinsert,		/* set insert mode and, */
	*_endinsert,		/* end insert mode */
	*_deletechar,		/* delete character */
	*_startdelete,		/* set delete mode and, */
	*_enddelete,		/* end delete mode */
	*_scrolldown,		/* scroll down */
	*_scrollup,		/* scroll up */
	*_termcap_init,		/* string to start termcap */
        *_termcap_end,		/* string to end termcap */
	*_op, *_oc, *_setaf, *_setab, *_setf, *_setb, *_scp;
int	_colors, _pairs, _bce, _xhp;
char     term_name[40];

TERM term = {
        NROW-1,
        NCOL,
	MARGIN,
	MROW,
        tinfoopen,
        tinfoterminalinfo,
        tinfoclose,
        ttgetc,
        ttputc,
        ttflush,
        tinfomove,
        tinfoeeol,
        tinfoeeop,
        tinfobeep,
        tinforev,
	tinfoeri
};


/*
 * Add default keypad sequences to the trie.
 */
static void
setup_dflt_esc_seq(void)
{
    /*
     * this is sort of a hack [no kidding], but it allows us to use
     * the function keys on pc's running telnet
     */

    /* 
     * UW-NDC/UCS vt10[02] application mode.
     */
    kpinsert("\033OP", F1, 1);
    kpinsert("\033OQ", F2, 1);
    kpinsert("\033OR", F3, 1);
    kpinsert("\033OS", F4, 1);
    kpinsert("\033Op", F5, 1);
    kpinsert("\033Oq", F6, 1);
    kpinsert("\033Or", F7, 1);
    kpinsert("\033Os", F8, 1);
    kpinsert("\033Ot", F9, 1);
    kpinsert("\033Ou", F10, 1);
    kpinsert("\033Ov", F11, 1);
    kpinsert("\033Ow", F12, 1);

    /*
     * DEC vt100, ANSI and cursor key mode.
     */
    kpinsert("\033OA", KEY_UP, 1);
    kpinsert("\033OB", KEY_DOWN, 1);
    kpinsert("\033OC", KEY_RIGHT, 1);
    kpinsert("\033OD", KEY_LEFT, 1);

    /*
     * special keypad functions
     */
    kpinsert("\033[4J", KEY_PGUP, 1);
    kpinsert("\033[3J", KEY_PGDN, 1);
    kpinsert("\033[2J", KEY_HOME, 1);
    kpinsert("\033[N",  KEY_END, 1);

    /*
     * vt220?
     */
    kpinsert("\033[5~", KEY_PGUP, 1);
    kpinsert("\033[6~", KEY_PGDN, 1);
    kpinsert("\033[1~", KEY_HOME, 1);
    kpinsert("\033[4~", KEY_END, 1);

    /*
     * konsole, XTerm (XFree 4.x.x) keyboard setting
     */
    kpinsert("\033[H", KEY_HOME, 1);
    kpinsert("\033[F", KEY_END, 1);

    /* 
     * gnome-terminal 2.6.0, don't know why it
     * changed from 2.2.1
     */
    kpinsert("\033OH", KEY_HOME, 1);
    kpinsert("\033OF", KEY_END, 1);

    /*
     * "\033[2~" was common for KEY_HOME in a quick survey
     *  of terminals (though typically the Insert key).
     *  Teraterm 2.33 sends the following escape sequences,
     *  which is quite incompatible with everything
     *  else:
     *    Home: "\033[2~" End: "\033[5~" PgUp: "\033[3~"
     *    PgDn: "\033[6~"
     *  The best thing to do would be to fix TeraTerm
     *  keymappings or to tweak terminfo.
     */

    /* 
     * ANSI mode.
     */
    kpinsert("\033[=a", F1, 1);
    kpinsert("\033[=b", F2, 1);
    kpinsert("\033[=c", F3, 1);
    kpinsert("\033[=d", F4, 1);
    kpinsert("\033[=e", F5, 1);
    kpinsert("\033[=f", F6, 1);
    kpinsert("\033[=g", F7, 1);
    kpinsert("\033[=h", F8, 1);
    kpinsert("\033[=i", F9, 1);
    kpinsert("\033[=j", F10, 1);
    kpinsert("\033[=k", F11, 1);
    kpinsert("\033[=l", F12, 1);

    /*
     * DEC vt100, ANSI and cursor key mode reset.
     */
    kpinsert("\033[A", KEY_UP, 1);
    kpinsert("\033[B", KEY_DOWN, 1);
    kpinsert("\033[C", KEY_RIGHT, 1);
    kpinsert("\033[D", KEY_LEFT, 1);

    /*
     * DEC vt52 mode.
     */
    kpinsert("\033A", KEY_UP, 1);
    kpinsert("\033B", KEY_DOWN, 1);
    kpinsert("\033C", KEY_RIGHT, 1);
    kpinsert("\033D", KEY_LEFT, 1);

    /*
     * DEC vt52 application keys, and some Zenith 19.
     */
    kpinsert("\033?r", KEY_DOWN, 1);
    kpinsert("\033?t", KEY_LEFT, 1);
    kpinsert("\033?v", KEY_RIGHT, 1);
    kpinsert("\033?x", KEY_UP, 1);

    /*
     * Some Ctrl-Arrow keys
     */
    kpinsert("\033[5A", CTRL_KEY_UP, 1);
    kpinsert("\033[5B", CTRL_KEY_DOWN, 1);
    kpinsert("\033[5C", CTRL_KEY_RIGHT, 1);
    kpinsert("\033[5D", CTRL_KEY_LEFT, 1);

    kpinsert("\033[1;5A", CTRL_KEY_UP, 1);
    kpinsert("\033[1;5B", CTRL_KEY_DOWN, 1);
    kpinsert("\033[1;5C", CTRL_KEY_RIGHT, 1);
    kpinsert("\033[1;5D", CTRL_KEY_LEFT, 1);

    /*
     * Map some shift+up/down/left/right to their shiftless counterparts
     */
    kpinsert("\033[1;2A", KEY_UP, 1);
    kpinsert("\033[1;2B", KEY_DOWN, 1);
    kpinsert("\033[1;2C", KEY_RIGHT, 1);
    kpinsert("\033[1;2D", KEY_LEFT, 1);
    kpinsert("\033[a", KEY_UP, 1);
    kpinsert("\033[b", KEY_DOWN, 1);
    kpinsert("\033[c", KEY_RIGHT, 1);
    kpinsert("\033[d", KEY_LEFT, 1);

    /*
     * Sun Console sequences.
     */
    kpinsert("\033[1",   KEY_SWALLOW_Z, 1);
    kpinsert("\033[215", KEY_SWAL_UP, 1);
    kpinsert("\033[217", KEY_SWAL_LEFT, 1);
    kpinsert("\033[219", KEY_SWAL_RIGHT, 1);
    kpinsert("\033[221", KEY_SWAL_DOWN, 1);

    /*
     * Kermit App Prog Cmd, gobble until ESC \ (kermit should intercept this)
     */
    kpinsert("\033_", KEY_KERMIT, 1);

    /*
     * Fake a control character.
     */
    kpinsert("\033\033", KEY_DOUBLE_ESC, 1);
}


static int
tinfoterminalinfo(int termcap_wins)
{
    char   *_ku, *_kd, *_kl, *_kr,
	   *_kppu, *_kppd, *_kphome, *_kpend, *_kpdel,
	   *_kf1, *_kf2, *_kf3, *_kf4, *_kf5, *_kf6,
	   *_kf7, *_kf8, *_kf9, *_kf10, *_kf11, *_kf12;
    char   *ttnm;

    if (Pmaster) {
	/*
	 *		setupterm() automatically retrieves the value
	 *		of the TERM variable.
	 */
	int err;
	ttnm = getenv("TERM");
	if(!ttnm)
	  return(-1);

	strncpy(term_name, ttnm, sizeof(term_name));
	term_name[sizeof(term_name)-1] = '\0';
	setupterm ((char *) 0, 1, &err);
	if (err != 1) return(err-2);
    }
    else {
	/*
	 *		setupterm() issues a message and exits, if the
	 *		terminfo data base is gone or the term type is
	 *		unknown, if arg2 is 0.
	 */
	setupterm ((char *) 0, 1, (int *) 0);
    }

    _clearscreen	= tigetstr("clear");
    _moveto		= tigetstr("cup");
    _up			= tigetstr("cuu1");
    _down		= tigetstr("cud1");
    _right		= tigetstr("cuf1");
    _left		= tigetstr("cub1");
    _setinverse		= tigetstr("smso");
    _clearinverse	= tigetstr("rmso");
    _setunderline	= tigetstr("smul");
    _clearunderline	= tigetstr("rmul");
    _setbold		= tigetstr("bold");
    _clearallattr	= tigetstr("sgr0");
    _cleartoeoln	= tigetstr("el");
    _cleartoeos		= tigetstr("ed");
    _deletechar		= tigetstr("dch1");
    _insertchar		= tigetstr("ich1");
    _startinsert	= tigetstr("smir");
    _endinsert		= tigetstr("rmir");
    _deleteline		= tigetstr("dl1");
    _insertline		= tigetstr("il1");
    _scrollregion	= tigetstr("csr");
    _scrolldown		= tigetstr("ind");
    _scrollup		= tigetstr("ri");
    _termcap_init	= tigetstr("smcup");
    _termcap_end	= tigetstr("rmcup");
    _startdelete	= tigetstr("smdc");
    _enddelete		= tigetstr("rmdc");
    _ku			= tigetstr("kcuu1");
    _kd			= tigetstr("kcud1");
    _kl			= tigetstr("kcub1");
    _kr			= tigetstr("kcuf1");
    _kppu		= tigetstr("kpp");
    _kppd		= tigetstr("knp");
    _kphome		= tigetstr("khome");
    _kpend		= tigetstr("kend");
    _kpdel		= tigetstr("kdch1");
    _kf1		= tigetstr("kf1");
    _kf2		= tigetstr("kf2");
    _kf3		= tigetstr("kf3");
    _kf4		= tigetstr("kf4");
    _kf5		= tigetstr("kf5");
    _kf6		= tigetstr("kf6");
    _kf7		= tigetstr("kf7");
    _kf8		= tigetstr("kf8");
    _kf9		= tigetstr("kf9");
    _kf10		= tigetstr("kf10");
    _kf11		= tigetstr("kf11");
    _kf12		= tigetstr("kf12");

    _colors		= tigetnum("colors");
    _pairs		= tigetnum("pairs");
    _setaf		= tigetstr("setaf");
    _setab		= tigetstr("setab");
    _setf		= tigetstr("setf");
    _setb		= tigetstr("setb");
    _scp		= tigetstr("scp");
    _op			= tigetstr("op");
    _oc			= tigetstr("oc");
    _bce		= tigetflag("bce");
    _xhp		= tigetflag("xhp");

    _tlines = tigetnum("lines");
    if(_tlines == -1){
	char *er;
	int   rr;

	/* tigetnum failed, try $LINES */
	er = getenv("LINES");
	if(er && (rr = atoi(er)) > 0)
	  _tlines = rr;
    }

    _tcolumns = tigetnum("cols");
    if(_tcolumns == -1){
	char *ec;
	int   cc;

	/* tigetnum failed, try $COLUMNS */
	ec = getenv("COLUMNS");
	if(ec && (cc = atoi(ec)) > 0)
	  _tcolumns = cc;
    }
    
    /*
     * Add default keypad sequences to the trie.
     * Since these come first, they will override any conflicting termcap
     * or terminfo escape sequences defined below.  An escape sequence is
     * considered conflicting if one is a prefix of the other.
     * So, without TERMCAP_WINS, there will likely be some termcap/terminfo
     * escape sequences that don't work, because they conflict with default
     * sequences defined here.
     */
    if(!termcap_wins)
      setup_dflt_esc_seq();

    /*
     * add termcap/info escape sequences to the trie...
     */

    if(_ku != NULL && _kd != NULL && _kl != NULL && _kr != NULL){
	kpinsert(_ku, KEY_UP, termcap_wins);
	kpinsert(_kd, KEY_DOWN, termcap_wins);
	kpinsert(_kl, KEY_LEFT, termcap_wins);
	kpinsert(_kr, KEY_RIGHT, termcap_wins);
    }

    if(_kppu != NULL && _kppd != NULL){
	kpinsert(_kppu, KEY_PGUP, termcap_wins);
	kpinsert(_kppd, KEY_PGDN, termcap_wins);
    }

    kpinsert(_kphome, KEY_HOME, termcap_wins);
    kpinsert(_kpend,  KEY_END, termcap_wins);
    kpinsert(_kpdel,  KEY_DEL, termcap_wins);

    kpinsert(_kf1,  F1, termcap_wins);
    kpinsert(_kf2,  F2, termcap_wins);
    kpinsert(_kf3,  F3, termcap_wins);
    kpinsert(_kf4,  F4, termcap_wins);
    kpinsert(_kf5,  F5, termcap_wins);
    kpinsert(_kf6,  F6, termcap_wins);
    kpinsert(_kf7,  F7, termcap_wins);
    kpinsert(_kf8,  F8, termcap_wins);
    kpinsert(_kf9,  F9, termcap_wins);
    kpinsert(_kf10, F10, termcap_wins);
    kpinsert(_kf11, F11, termcap_wins);
    kpinsert(_kf12, F12, termcap_wins);

    /*
     * Add default keypad sequences to the trie.
     * Since these come after the termcap/terminfo escape sequences above,
     * the termcap/info sequences will override any conflicting default
     * escape sequences defined here.
     * So, with TERMCAP_WINS, some of the default sequences will be missing.
     * This means that you'd better get all of your termcap/terminfo entries
     * correct if you define TERMCAP_WINS.
     */
    if(termcap_wins)
      setup_dflt_esc_seq();

    if(Pmaster)
      return(0);
    else
      return(TRUE);
}

static int
tinfoopen(void)
{
    int     row, col;

    if (sendnow){
      term.t_nrow = 23;
      term.t_ncol = 80;
      return 0;
    }

    /*
     * determine the terminal's communication speed and decide
     * if we need to do optimization ...
     */
    if(ttisslow())
      term_capabilities |= TT_OPTIMIZE;
    
    col = _tcolumns;
    row = _tlines;
    if(row >= 0)
      row--;

    ttgetwinsz(&row, &col);
    term.t_nrow = (short) row;
    term.t_ncol = (short) col;

    if(_cleartoeoln != NULL)	/* able to use clear to EOL? */
      term_capabilities |= TT_EOLEXIST;
    else
      term_capabilities &= ~TT_EOLEXIST;

    if(_setinverse != NULL)
      term_capabilities |= TT_REVEXIST;
    else
      term_capabilities &= ~TT_REVEXIST;

    if(_deletechar == NULL && (_startdelete == NULL || _enddelete == NULL))
      term_capabilities &= ~TT_DELCHAR;

    if(_insertchar == NULL && (_startinsert == NULL || _endinsert == NULL))
      term_capabilities &= ~TT_INSCHAR;

    if((_scrollregion == NULL || _scrolldown == NULL || _scrollup == NULL)
       && (_deleteline == NULL || _insertline == NULL))
      term_capabilities &= ~TT_SCROLLEXIST;

    if(_clearscreen == NULL || _moveto == NULL || _up == NULL){
	if(Pmaster == NULL){
	    puts("Incomplete terminfo entry\n");
	    exit(1);
	}
    }

    ttopen();

    if(_termcap_init && !Pmaster) {
	putpad(_termcap_init);		/* any init terminfo requires */
	if (_scrollregion)
	  putpad(tgoto(_scrollregion, term.t_nrow, 0)) ;
    }

    /*
     * Initialize UW-modified NCSA telnet to use its functionkeys
     */
    if((gmode & MDFKEY) && Pmaster == NULL)
      puts("\033[99h");

    /* return ignored */
    return(0);
}


static int
tinfoclose(void)
{
    if(!Pmaster){
	if(gmode&MDFKEY)
	  puts("\033[99l");		/* reset UW-NCSA telnet keys */

	if(_termcap_end)		/* any clean up terminfo requires */
	  putpad(_termcap_end);
    }

    ttclose();
    
    /* return ignored */
    return(0);
}


/*
 * tinfoinsert - insert a character at the current character position.
 *               _insertchar takes precedence.
 */
static void
tinfoinsert(UCS ch)
{
    if(_insertchar != NULL){
	putpad(_insertchar);
	ttputc(ch);
    }
    else{
	putpad(_startinsert);
	ttputc(ch);
	putpad(_endinsert);
    }
}


/*
 * tinfodelete - delete a character at the current character position.
 */
static void
tinfodelete(void)
{
    if(_startdelete == NULL && _enddelete == NULL)
      putpad(_deletechar);
    else{
	putpad(_startdelete);
	putpad(_deletechar);
	putpad(_enddelete);
    }
}


/*
 * o_scrolldown() - open a line at the given row position.
 *               use either region scrolling or deleteline/insertline
 *               to open a new line.
 */
int
o_scrolldown(int row, int n)
{
    register int i;

    if(_scrollregion != NULL){
	putpad(tgoto(_scrollregion, term.t_nrow - (term.t_mrow+1), row));
	tinfomove(row, 0);
	for(i = 0; i < n; i++)
	  putpad((_scrollup != NULL && *_scrollup != '\0') ? _scrollup : "\n" );
	putpad(tgoto(_scrollregion, term.t_nrow, 0));
	tinfomove(row, 0);
    }
    else{
	/*
	 * this code causes a jiggly motion of the keymenu when scrolling
	 */
	for(i = 0; i < n; i++){
	    tinfomove(term.t_nrow - (term.t_mrow+1), 0);
	    putpad(_deleteline);
	    tinfomove(row, 0);
	    putpad(_insertline);
	}
#ifdef	NOWIGGLYLINES
	/*
	 * this code causes a sweeping motion up and down the display
	 */
	tinfomove(term.t_nrow - term.t_mrow - n, 0);
	for(i = 0; i < n; i++)
	  putpad(_deleteline);
	tinfomove(row, 0);
	for(i = 0; i < n; i++)
	  putpad(_insertline);
#endif
    }

    /* return ignored */
    return(0);
}


/*
 * o_scrollup() - open a line at the given row position.
 *               use either region scrolling or deleteline/insertline
 *               to open a new line.
 */
int
o_scrollup(int row, int n)
{
    register int i;

    if(_scrollregion != NULL){
	putpad(tgoto(_scrollregion, term.t_nrow - (term.t_mrow+1), row));
	/* setting scrolling region moves cursor to home */
	tinfomove(term.t_nrow-(term.t_mrow+1), 0);
	for(i = 0;i < n; i++)
	  putpad((_scrolldown == NULL || _scrolldown[0] == '\0') ? "\n"
								 : _scrolldown);
	putpad(tgoto(_scrollregion, term.t_nrow, 0));
	tinfomove(2, 0);
    }
    else{
	for(i = 0; i < n; i++){
	    tinfomove(row, 0);
	    putpad(_deleteline);
	    tinfomove(term.t_nrow - (term.t_mrow+1), 0);
	    putpad(_insertline);
	}
#ifdef  NOWIGGLYLINES
	/* see note above */
	tinfomove(row, 0);
	for(i = 0; i < n; i++)
	  putpad(_deleteline);
	tinfomove(term.t_nrow - term.t_mrow - n, 0);
	for(i = 0;i < n; i++)
	  putpad(_insertline);
#endif
    }

    /* return ignored */
    return(0);
}



/*
 * o_insert - use terminfo to optimized character insert
 *            returns: true if it optimized output, false otherwise
 */
int
o_insert(UCS c)
{
    if(term_capabilities & TT_INSCHAR){
	tinfoinsert(c);
	return(1);			/* no problems! */
    }

    return(0);				/* can't do it. */
}


/*
 * o_delete - use terminfo to optimized character insert
 *            returns true if it optimized output, false otherwise
 */
int
o_delete(void)
{
    if(term_capabilities & TT_DELCHAR){
	tinfodelete();
	return(1);			/* deleted, no problem! */
    }

    return(0);				/* no dice. */
}


static int
tinfomove(int row, int col)
{
    putpad(tgoto(_moveto, col, row));

    /* return ignored */
    return(0);
}


static int
tinfoeeol(void)
{
    int   c, starting_col, starting_line;
    char *last_bg_color;

    /*
     * If the terminal doesn't have back color erase, then we have to
     * erase manually to preserve the background color.
     */
    if(pico_usingcolor() && (!_bce || !_cleartoeoln)){
	extern int ttcol, ttrow;

	starting_col  = ttcol;
	starting_line = ttrow;
	last_bg_color = pico_get_last_bg_color();
	pico_set_nbg_color();
	for(c = ttcol; c < term.t_ncol; c++)
	  ttputc(' ');
	
        tinfomove(starting_line, starting_col);
	if(last_bg_color){
	    pico_set_bg_color(last_bg_color);
	    free(last_bg_color);
	}
    }
    else if(_cleartoeoln)
      putpad(_cleartoeoln);

    /* return ignored */
    return(0);
}


static int
tinfoeeop(void)
{
    int i, starting_col, starting_row;

    /*
     * If the terminal doesn't have back color erase, then we have to
     * erase manually to preserve the background color.
     */
    if(pico_usingcolor() && (!_bce || !_cleartoeos)){
	extern int ttcol, ttrow;

	starting_col = ttcol;
	starting_row = ttrow;
	tinfoeeol();				  /* rest of this line */
	for(i = ttrow+1; i <= term.t_nrow; i++){  /* the remaining lines */
	    tinfomove(i, 0);
	    tinfoeeol();
	}

	tinfomove(starting_row, starting_col);
    }
    else if(_cleartoeos)
      putpad(_cleartoeos);

    /* return ignored */
    return(0);
}


static int
tinforev(int state)		/* change reverse video status */
{				/* FALSE = normal video, TRUE = rev video */
    if(state)
      StartInverse();
    else
      EndInverse();

    return(1);
}


static int
tinfoeri(void)
{
    if(_xhp && _clearinverse)
      putpad(_clearinverse);

    return(1);
}


static int
tinfobeep(void)
{
    ttputc(BELL);

    /* return ignored */
    return(0);
}


void
putpad(char *str)
{
    tputs(str, 1, putchar);
}

#elif	HAS_TERMCAP

/*
 * termcap-based terminal i/o and control routines
 */


/* internal prototypes */
static int	tcapmove(int, int);
static int      tcapeeol(void);
static int      tcapeeop(void);
static int      tcapbeep(void);
static int      tcaprev(int);
static int      tcaperi(void);
static int      tcapopen(void);
static int      tcapterminalinfo(int);
static int      tcapclose(void);
static void     setup_dflt_esc_seq(void);
static void     tcapinsert(UCS);
static void     tcapdelete(void);

extern int      tput();
extern char     *tgoto(char *, int, int);

/*
 * This number used to be 315. No doubt there was a reason for that but we
 * don't know what it was. It's a bit of a hassle to make it dynamic, and most
 * modern systems seem to be using terminfo, so we'll just change it to 800.
 * We weren't stopping on overflow before, so we'll do that, too. 
 */
#define TCAPSLEN 800
char tcapbuf[TCAPSLEN];
int	_tlines, _tcolumns;
char	*_clearscreen, *_moveto, *_up, *_down, *_right, *_left,
	*_setinverse, *_clearinverse,
	*_setunderline, *_clearunderline,
	*_setbold, *_clearallattr,	/* there is no clear only bold! */
	*_cleartoeoln, *_cleartoeos,
	*_deleteline,		/* delete line */
	*_insertline,		/* insert line */
	*_scrollregion,		/* define a scrolling region, vt100 */
	*_insertchar,		/* insert character, preferable to : */
	*_startinsert,		/* set insert mode and, */
	*_endinsert,		/* end insert mode */
	*_deletechar,		/* delete character */
	*_startdelete,		/* set delete mode and, */
	*_enddelete,		/* end delete mode */
	*_scrolldown,		/* scroll down */
	*_scrollup,		/* scroll up */
	*_termcap_init,		/* string to start termcap */
        *_termcap_end,		/* string to end termcap */
	*_op, *_oc, *_setaf, *_setab, *_setf, *_setb, *_scp;
int	_colors, _pairs, _bce, _xhp;
char     term_name[40];

TERM term = {
        NROW-1,
        NCOL,
	MARGIN,
	MROW,
        tcapopen,
        tcapterminalinfo,
        tcapclose,
        ttgetc,
        ttputc,
        ttflush,
        tcapmove,
        tcapeeol,
        tcapeeop,
        tcapbeep,
        tcaprev,
	tcaperi
};


/*
 * Add default keypad sequences to the trie.
 */
static void
setup_dflt_esc_seq(void)
{
    /*
     * this is sort of a hack, but it allows us to use
     * the function keys on pc's running telnet
     */

    /* 
     * UW-NDC/UCS vt10[02] application mode.
     */
    kpinsert("\033OP", F1, 1);
    kpinsert("\033OQ", F2, 1);
    kpinsert("\033OR", F3, 1);
    kpinsert("\033OS", F4, 1);
    kpinsert("\033Op", F5, 1);
    kpinsert("\033Oq", F6, 1);
    kpinsert("\033Or", F7, 1);
    kpinsert("\033Os", F8, 1);
    kpinsert("\033Ot", F9, 1);
    kpinsert("\033Ou", F10, 1);
    kpinsert("\033Ov", F11, 1);
    kpinsert("\033Ow", F12, 1);

    /*
     * DEC vt100, ANSI and cursor key mode.
     */
    kpinsert("\033OA", KEY_UP, 1);
    kpinsert("\033OB", KEY_DOWN, 1);
    kpinsert("\033OC", KEY_RIGHT, 1);
    kpinsert("\033OD", KEY_LEFT, 1);

    /*
     * special keypad functions
     */
    kpinsert("\033[4J", KEY_PGUP, 1);
    kpinsert("\033[3J", KEY_PGDN, 1);
    kpinsert("\033[2J", KEY_HOME, 1);
    kpinsert("\033[N",  KEY_END, 1);

    /*
     * vt220?
     */
    kpinsert("\033[5~", KEY_PGUP, 1);
    kpinsert("\033[6~", KEY_PGDN, 1);
    kpinsert("\033[1~", KEY_HOME, 1);
    kpinsert("\033[4~", KEY_END, 1);

    /*
     * konsole, XTerm (XFree 4.x.x) keyboard setting
     */
    kpinsert("\033[H", KEY_HOME, 1);
    kpinsert("\033[F", KEY_END, 1);

    /* 
     * gnome-terminal 2.6.0, don't know why it
     * changed from 2.2.1
     */
    kpinsert("\033OH", KEY_HOME, 1);
    kpinsert("\033OF", KEY_END, 1);

    /*
     * "\033[2~" was common for KEY_HOME in a quick survey
     *  of terminals (though typically the Insert key).
     *  Teraterm 2.33 sends the following escape sequences,
     *  which is quite incompatible with everything
     *  else:
     *    Home: "\033[2~" End: "\033[5~" PgUp: "\033[3~"
     *    PgDn: "\033[6~"
     *  The best thing to do would be to fix TeraTerm
     *  keymappings or to tweak terminfo.
     */

    /* 
     * ANSI mode.
     */
    kpinsert("\033[=a", F1, 1);
    kpinsert("\033[=b", F2, 1);
    kpinsert("\033[=c", F3, 1);
    kpinsert("\033[=d", F4, 1);
    kpinsert("\033[=e", F5, 1);
    kpinsert("\033[=f", F6, 1);
    kpinsert("\033[=g", F7, 1);
    kpinsert("\033[=h", F8, 1);
    kpinsert("\033[=i", F9, 1);
    kpinsert("\033[=j", F10, 1);
    kpinsert("\033[=k", F11, 1);
    kpinsert("\033[=l", F12, 1);

    /*
     * DEC vt100, ANSI and cursor key mode reset.
     */
    kpinsert("\033[A", KEY_UP, 1);
    kpinsert("\033[B", KEY_DOWN, 1);
    kpinsert("\033[C", KEY_RIGHT, 1);
    kpinsert("\033[D", KEY_LEFT, 1);

    /*
     * DEC vt52 mode.
     */
    kpinsert("\033A", KEY_UP, 1);
    kpinsert("\033B", KEY_DOWN, 1);
    kpinsert("\033C", KEY_RIGHT, 1);
    kpinsert("\033D", KEY_LEFT, 1);

    /*
     * DEC vt52 application keys, and some Zenith 19.
     */
    kpinsert("\033?r", KEY_DOWN, 1);
    kpinsert("\033?t", KEY_LEFT, 1);
    kpinsert("\033?v", KEY_RIGHT, 1);
    kpinsert("\033?x", KEY_UP, 1);

    /*
     * Some Ctrl-Arrow keys
     */
    kpinsert("\033[5A", CTRL_KEY_UP, 1);
    kpinsert("\033[5B", CTRL_KEY_DOWN, 1);
    kpinsert("\033[5C", CTRL_KEY_RIGHT, 1);
    kpinsert("\033[5D", CTRL_KEY_LEFT, 1);

    kpinsert("\033[1;5A", CTRL_KEY_UP, 1);
    kpinsert("\033[1;5B", CTRL_KEY_DOWN, 1);
    kpinsert("\033[1;5C", CTRL_KEY_RIGHT, 1);
    kpinsert("\033[1;5D", CTRL_KEY_LEFT, 1);

    /*
     * Map some shift+up/down/left/right to their shiftless counterparts
     */
    kpinsert("\033[1;2A", KEY_UP, 1);
    kpinsert("\033[1;2B", KEY_DOWN, 1);
    kpinsert("\033[1;2C", KEY_RIGHT, 1);
    kpinsert("\033[1;2D", KEY_LEFT, 1);
    kpinsert("\033[a", KEY_UP, 1);
    kpinsert("\033[b", KEY_DOWN, 1);
    kpinsert("\033[c", KEY_RIGHT, 1);
    kpinsert("\033[d", KEY_LEFT, 1);

    /*
     * Sun Console sequences.
     */
    kpinsert("\033[1",   KEY_SWALLOW_Z, 1);
    kpinsert("\033[215", KEY_SWAL_UP, 1);
    kpinsert("\033[217", KEY_SWAL_LEFT, 1);
    kpinsert("\033[219", KEY_SWAL_RIGHT, 1);
    kpinsert("\033[221", KEY_SWAL_DOWN, 1);

    /*
     * Kermit App Prog Cmd, gobble until ESC \ (kermit should intercept this)
     */
    kpinsert("\033_", KEY_KERMIT, 1);

    /*
     * Fake a control character.
     */
    kpinsert("\033\033", KEY_DOUBLE_ESC, 1);
}


/*
 * Read termcap and set some global variables. Initialize input trie to
 * decode escape sequences.
 */
static int
tcapterminalinfo(int termcap_wins)
{
    char   *p, *tgetstr();
    char    tcbuf[2*1024];
    char   *tv_stype;
    char    err_str[72];
    int     err;
    char   *_ku, *_kd, *_kl, *_kr,
	   *_kppu, *_kppd, *_kphome, *_kpend, *_kpdel,
	   *_kf1, *_kf2, *_kf3, *_kf4, *_kf5, *_kf6,
	   *_kf7, *_kf8, *_kf9, *_kf10, *_kf11, *_kf12;

    if (!(tv_stype = getenv("TERM")) || !strncpy(term_name, tv_stype, sizeof(term_name))){
	if(Pmaster){
	    return(-1);
	}
	else{
	    puts("Environment variable TERM not defined!");
	    exit(1);
	}
    }

    term_name[sizeof(term_name)-1] = '\0';

    if((err = tgetent(tcbuf, tv_stype)) != 1){
	if(Pmaster){
	    return(err - 2);
	}
	else{
	    snprintf(err_str, sizeof(err_str), "Unknown terminal type %s!", tv_stype);
	    puts(err_str);
	    exit(1);
	}
    }

    p = tcapbuf;

    _clearscreen	= tgetstr("cl", &p);
    _moveto		= tgetstr("cm", &p);
    _up			= tgetstr("up", &p);
    _down		= tgetstr("do", &p);
    _right		= tgetstr("nd", &p);
    _left		= tgetstr("bs", &p);
    _setinverse		= tgetstr("so", &p);
    _clearinverse	= tgetstr("se", &p);
    _setunderline	= tgetstr("us", &p);
    _clearunderline	= tgetstr("ue", &p);
    _setbold		= tgetstr("md", &p);
    _clearallattr	= tgetstr("me", &p);
    _cleartoeoln	= tgetstr("ce", &p);
    _cleartoeos		= tgetstr("cd", &p);
    _deletechar		= tgetstr("dc", &p);
    _insertchar		= tgetstr("ic", &p);
    _startinsert	= tgetstr("im", &p);
    _endinsert		= tgetstr("ei", &p);
    _deleteline		= tgetstr("dl", &p);
    _insertline		= tgetstr("al", &p);
    _scrollregion	= tgetstr("cs", &p);
    _scrolldown		= tgetstr("sf", &p);
    _scrollup		= tgetstr("sr", &p);
    _termcap_init	= tgetstr("ti", &p);
    _termcap_end	= tgetstr("te", &p);
    _startdelete	= tgetstr("dm", &p);
    _enddelete		= tgetstr("ed", &p);
    _ku			= tgetstr("ku", &p);
    _kd			= tgetstr("kd", &p);
    _kl			= tgetstr("kl", &p);
    _kr			= tgetstr("kr", &p);
    _kppu		= tgetstr("kP", &p);
    _kppd		= tgetstr("kN", &p);
    _kphome		= tgetstr("kh", &p);
    _kpend		= tgetstr("kH", &p);
    _kpdel		= tgetstr("kD", &p);
    _kf1		= tgetstr("k1", &p);
    _kf2		= tgetstr("k2", &p);
    _kf3		= tgetstr("k3", &p);
    _kf4		= tgetstr("k4", &p);
    _kf5		= tgetstr("k5", &p);
    _kf6		= tgetstr("k6", &p);
    _kf7		= tgetstr("k7", &p);
    _kf8		= tgetstr("k8", &p);
    _kf9		= tgetstr("k9", &p);
    if((_kf10		= tgetstr("k;", &p)) == NULL)
      _kf10		= tgetstr("k0", &p);
    _kf11		= tgetstr("F1", &p);
    _kf12		= tgetstr("F2", &p);

    _colors		= tgetnum("Co");
    _pairs		= tgetnum("pa");
    _setaf		= tgetstr("AF", &p);
    _setab		= tgetstr("AB", &p);
    _setf		= tgetstr("Sf", &p);
    _setb		= tgetstr("Sb", &p);
    _scp		= tgetstr("sp", &p);
    _op			= tgetstr("op", &p);
    _oc			= tgetstr("oc", &p);
    _bce		= tgetflag("ut");
    _xhp		= tgetflag("xs");

    if (p >= &tcapbuf[TCAPSLEN]){
	puts("Terminal description too big!\n");
	if(Pmaster)
	  return(-3);
	else
	  exit(1);
    }

    _tlines = tgetnum("li");
    if(_tlines == -1){
	char *er;
	int   rr;

	/* tgetnum failed, try $LINES */
	er = getenv("LINES");
	if(er && (rr = atoi(er)) > 0)
	  _tlines = rr;
    }

    _tcolumns = tgetnum("co");
    if(_tcolumns == -1){
	char *ec;
	int   cc;

	/* tgetnum failed, try $COLUMNS */
	ec = getenv("COLUMNS");
	if(ec && (cc = atoi(ec)) > 0)
	  _tcolumns = cc;
    }

    /*
     * Add default keypad sequences to the trie.
     * Since these come first, they will override any conflicting termcap
     * or terminfo escape sequences defined below.  An escape sequence is
     * considered conflicting if one is a prefix of the other.
     * So, without TERMCAP_WINS, there will likely be some termcap/terminfo
     * escape sequences that don't work, because they conflict with default
     * sequences defined here.
     */
    if(!termcap_wins)
      setup_dflt_esc_seq();

    /*
     * add termcap/info escape sequences to the trie...
     */

    if(_ku != NULL && _kd != NULL && _kl != NULL && _kr != NULL){
	kpinsert(_ku, KEY_UP, termcap_wins);
	kpinsert(_kd, KEY_DOWN, termcap_wins);
	kpinsert(_kl, KEY_LEFT, termcap_wins);
	kpinsert(_kr, KEY_RIGHT, termcap_wins);
    }

    if(_kppu != NULL && _kppd != NULL){
	kpinsert(_kppu, KEY_PGUP, termcap_wins);
	kpinsert(_kppd, KEY_PGDN, termcap_wins);
    }

    kpinsert(_kphome, KEY_HOME, termcap_wins);
    kpinsert(_kpend,  KEY_END, termcap_wins);
    kpinsert(_kpdel,  KEY_DEL, termcap_wins);

    kpinsert(_kf1,  F1, termcap_wins);
    kpinsert(_kf2,  F2, termcap_wins);
    kpinsert(_kf3,  F3, termcap_wins);
    kpinsert(_kf4,  F4, termcap_wins);
    kpinsert(_kf5,  F5, termcap_wins);
    kpinsert(_kf6,  F6, termcap_wins);
    kpinsert(_kf7,  F7, termcap_wins);
    kpinsert(_kf8,  F8, termcap_wins);
    kpinsert(_kf9,  F9, termcap_wins);
    kpinsert(_kf10, F10, termcap_wins);
    kpinsert(_kf11, F11, termcap_wins);
    kpinsert(_kf12, F12, termcap_wins);

    /*
     * Add default keypad sequences to the trie.
     * Since these come after the termcap/terminfo escape sequences above,
     * the termcap/info sequences will override any conflicting default
     * escape sequences defined here.
     * So, with TERMCAP_WINS, some of the default sequences will be missing.
     * This means that you'd better get all of your termcap/terminfo entries
     * correct if you define TERMCAP_WINS.
     */
    if(termcap_wins)
      setup_dflt_esc_seq();

    if(Pmaster)
      return(0);
    else
      return(TRUE);
}

static int
tcapopen(void)
{
    int     row, col;

    if (sendnow){
      term.t_nrow = 23;
      term.t_ncol = 80;
      return 0;
    }

    /*
     * determine the terminal's communication speed and decide
     * if we need to do optimization ...
     */
    if(ttisslow())
      term_capabilities |= TT_OPTIMIZE;

    col = _tcolumns;
    row = _tlines;
    if(row >= 0)
      row--;

    ttgetwinsz(&row, &col);
    term.t_nrow = (short) row;
    term.t_ncol = (short) col;

    if(_cleartoeoln != NULL)	/* able to use clear to EOL? */
      term_capabilities |= TT_EOLEXIST;
    else
      term_capabilities &= ~TT_EOLEXIST;

    if(_setinverse != NULL)
      term_capabilities |= TT_REVEXIST;
    else
      term_capabilities &= ~TT_REVEXIST;

    if(_deletechar == NULL && (_startdelete == NULL || _enddelete == NULL))
      term_capabilities &= ~TT_DELCHAR;

    if(_insertchar == NULL && (_startinsert == NULL || _endinsert == NULL))
      term_capabilities &= ~TT_INSCHAR;

    if((_scrollregion == NULL || _scrolldown == NULL || _scrollup == NULL)
       && (_deleteline == NULL || _insertline == NULL))
      term_capabilities &= ~TT_SCROLLEXIST;

    if(_clearscreen == NULL || _moveto == NULL || _up == NULL){
	if(Pmaster == NULL){
	    puts("Incomplete termcap entry\n");
	    exit(1);
	}
    }

    ttopen();

    if(_termcap_init && !Pmaster) {
	putpad(_termcap_init);		/* any init termcap requires */
	if (_scrollregion)
	  putpad(tgoto(_scrollregion, term.t_nrow, 0)) ;
    }

    /*
     * Initialize UW-modified NCSA telnet to use it's functionkeys
     */
    if(gmode&MDFKEY && Pmaster == NULL)
      puts("\033[99h");
    
    /* return ignored */
    return(0);
}


static int
tcapclose(void)
{
    if(!Pmaster){
	if(gmode&MDFKEY)
	  puts("\033[99l");		/* reset UW-NCSA telnet keys */

	if(_termcap_end)		/* any cleanup termcap requires */
	  putpad(_termcap_end);
    }

    ttclose();
    
    /* return ignored */
    return(0);
}



/*
 * tcapinsert - insert a character at the current character position.
 *              _insertchar takes precedence.
 */
static void
tcapinsert(UCS ch)
{
    if(_insertchar != NULL){
	putpad(_insertchar);
	ttputc(ch);
    }
    else{
	putpad(_startinsert);
	ttputc(ch);
	putpad(_endinsert);
    }
}


/*
 * tcapdelete - delete a character at the current character position.
 */
static void
tcapdelete(void)
{
    if(_startdelete == NULL && _enddelete == NULL)
      putpad(_deletechar);
    else{
	putpad(_startdelete);
	putpad(_deletechar);
	putpad(_enddelete);
    }
}


/*
 * o_scrolldown - open a line at the given row position.
 *                use either region scrolling or deleteline/insertline
 *                to open a new line.
 */
int
o_scrolldown(int row, int n)
{
    register int i;

    if(_scrollregion != NULL){
	putpad(tgoto(_scrollregion, term.t_nrow - (term.t_mrow+1), row));
	tcapmove(row, 0);
	for(i = 0; i < n; i++)
	  putpad( (_scrollup != NULL && *_scrollup != '\0')
	          ? _scrollup : "\n" );
	putpad(tgoto(_scrollregion, term.t_nrow, 0));
	tcapmove(row, 0);
    }
    else{
	/*
	 * this code causes a jiggly motion of the keymenu when scrolling
	 */
	for(i = 0; i < n; i++){
	    tcapmove(term.t_nrow - (term.t_mrow+1), 0);
	    putpad(_deleteline);
	    tcapmove(row, 0);
	    putpad(_insertline);
	}
#ifdef	NOWIGGLYLINES
	/*
	 * this code causes a sweeping motion up and down the display
	 */
	tcapmove(term.t_nrow - term.t_mrow - n, 0);
	for(i = 0; i < n; i++)
	  putpad(_deleteline);
	tcapmove(row, 0);
	for(i = 0; i < n; i++)
	  putpad(_insertline);
#endif
    }
    
    /* return ignored */
    return(0);
}


/*
 * o_scrollup - open a line at the given row position.
 *              use either region scrolling or deleteline/insertline
 *              to open a new line.
 */
int
o_scrollup(int row, int n)
{
    register int i;

    if(_scrollregion != NULL){
	putpad(tgoto(_scrollregion, term.t_nrow - (term.t_mrow+1), row));
	/* setting scrolling region moves cursor to home */
	tcapmove(term.t_nrow-(term.t_mrow+1), 0);
	for(i = 0;i < n; i++)
	  putpad((_scrolldown == NULL || _scrolldown[0] == '\0')
		 ? "\n" : _scrolldown);
	putpad(tgoto(_scrollregion, term.t_nrow, 0));
	tcapmove(2, 0);
    }
    else{
	for(i = 0; i < n; i++){
	    tcapmove(row, 0);
	    putpad(_deleteline);
	    tcapmove(term.t_nrow - (term.t_mrow+1), 0);
	    putpad(_insertline);
	}
#ifdef  NOWIGGLYLINES
	/* see note above */
	tcapmove(row, 0);
	for(i = 0; i < n; i++)
	  putpad(_deleteline);
	tcapmove(term.t_nrow - term.t_mrow - n, 0);
	for(i = 0;i < n; i++)
	  putpad(_insertline);
#endif
    }

    /* return ignored */
    return(0);
}


/*
 * o_insert - use termcap info to optimized character insert
 *            returns: true if it optimized output, false otherwise
 */
int
o_insert(UCS c)
{
    if(term_capabilities & TT_INSCHAR){
	tcapinsert(c);
	return(1);			/* no problems! */
    }

    return(0);				/* can't do it. */
}


/*
 * o_delete - use termcap info to optimized character insert
 *            returns true if it optimized output, false otherwise
 */
int
o_delete(void)
{
    if(term_capabilities & TT_DELCHAR){
	tcapdelete();
	return(1);			/* deleted, no problem! */
    }

    return(0);				/* no dice. */
}


static int
tcapmove(int row, int col)
{
    putpad(tgoto(_moveto, col, row));

    /* return ignored */
    return(0);
}


static int
tcapeeol(void)
{
    int   c, starting_col, starting_line;
    char *last_bg_color;

    /*
     * If the terminal doesn't have back color erase, then we have to
     * erase manually to preserve the background color.
     */
    if(pico_usingcolor() && (!_bce || !_cleartoeoln)){
	extern int ttcol, ttrow;

	starting_col  = ttcol;
	starting_line = ttrow;
	last_bg_color = pico_get_last_bg_color();
	pico_set_nbg_color();
	for(c = ttcol; c < term.t_ncol; c++)
	  ttputc(' ');
	
        tcapmove(starting_line, starting_col);
	if(last_bg_color){
	    pico_set_bg_color(last_bg_color);
	    free(last_bg_color);
	}
    }
    else if(_cleartoeoln)
      putpad(_cleartoeoln);

    /* return ignored */
    return(0);
}


static int
tcapeeop(void)
{
    int i, starting_col, starting_row;

    /*
     * If the terminal doesn't have back color erase, then we have to
     * erase manually to preserve the background color.
     */
    if(pico_usingcolor() && (!_bce || !_cleartoeos)){
	extern int ttcol, ttrow;

	starting_col = ttcol;
	starting_row = ttrow;
	tcapeeol();				  /* rest of this line */
	for(i = ttrow+1; i <= term.t_nrow; i++){  /* the remaining lines */
	    tcapmove(i, 0);
	    tcapeeol();
	}

	tcapmove(starting_row, starting_col);
    }
    else if(_cleartoeos)
      putpad(_cleartoeos);

    /* return ignored */
    return(0);
}


static int
tcaperi(void)
{
    if(_xhp && _clearinverse)
      putpad(_clearinverse);

    return(1);
}


static int
tcaprev(int state)		/* change reverse video status */
{				/* FALSE = normal video, TRUE = reverse video */
    if(state)
      StartInverse();
    else
      EndInverse();

    return(1);
}


static int
tcapbeep(void)
{
    ttputc(BELL);

    /* return ignored */
    return(0);
}


void
putpad(char *str)
{
    tputs(str, 1, putchar);
}

#else  /* HARD_CODED_ANSI_TERMINAL */

/*
 * ANSI-specific terminal i/o and control routines
 */


#define BEL     0x07                    /* BEL character.               */
#define ESC     0x1B                    /* ESC character.               */


extern  int     ttflush();

extern  int     ansimove(int, int);
extern  int     ansieeol(void);
extern  int     ansieeop(void);
extern  int     ansibeep(void);
extern  int     ansiparm(int);
extern  int     ansiopen(void);
extern  int     ansiterminalinfo(int);
extern	int	ansirev(int);

/*
 * Standard terminal interface dispatch table. Most of the fields point into
 * "termio" code.
 */
#if defined(VAX) && !defined(__ALPHA)
globaldef
#endif

TERM    term    = {
        NROW-1,
        NCOL,
	MARGIN,
	MROW,
        ansiopen,
        ansiterminalinfo,
        ttclose,
        ttgetc,
        ttputc,
        ttflush,
        ansimove,
        ansieeol,
        ansieeop,
        ansibeep,
	ansirev
};

int
ansimove(int row, int col)
{
        ttputc(ESC);
        ttputc('[');
        ansiparm(row+1);
        ttputc(';');
        ansiparm(col+1);
        ttputc('H');
}

int
ansieeol(void)
{
        ttputc(ESC);
        ttputc('[');
        ttputc('K');
}

int
ansieeop(void)
{
        ttputc(ESC);
        ttputc('[');
        ttputc('J');
}

int
ansirev(int state)	/* change reverse video state */
{			/* TRUE = reverse, FALSE = normal */
	static int PrevState = 0;

	if(state != PrevState) {
		PrevState = state ;
		ttputc(ESC);
		ttputc('[');
		ttputc(state ? '7': '0');
		ttputc('m');
	}
}

int
ansibeep(void)
{
        ttputc(BEL);
        ttflush();
}

int
ansiparm(int n)
{
        register int    q;

        q = n/10;
        if (q != 0)
                ansiparm(q);
        ttputc((n%10) + '0');
}


int
ansiterminalinfo(int termcap_wins)
{
#if     V7
        register char *cp;
        char *getenv();

        if ((cp = getenv("TERM")) == NULL) {
                puts("Shell variable TERM not defined!");
                exit(1);
        }
        if (strcmp(cp, "vt100") != 0) {
                puts("Terminal type not 'vt100'!");
                exit(1);
        }
#endif
	/* revexist = TRUE; dead code? */
}

int
ansiopen(void)
{
        ttopen();
}


#endif /* HARD_CODED_ANSI_TERMINAL */
#else /* _WINDOWS */

/* These are all just noops in Windows */

unsigned
tthascap(void)
{
    return(0);
}

/*
 * o_insert - optimize screen insert of char c
 */
int
o_insert(UCS c)
{
    return(0);
}


/*
 * o_delete - optimized character deletion
 */
int
o_delete(void)
{
    return(0);
}



#endif /* _WINDOWS */
