/*
 * term.c -- termios and (termcap || terminfo) handlers
 *
 * Copyright 1990 Michael Sandrof
 * Copyright 1995 Matthew Green
 * Coypright 1996 EPIC Software Labs
 * Copyright 1998 J. Kean Johnston, used with permission
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 */

#define __need_putchar_x__
#define __need_term_flush__
#include "irc.h"
#include "ircaux.h"
#include "vars.h"
#include "term.h"
#include "window.h"
#include "screen.h"
#include "output.h"

/*
 * If "HAVE_TERMINFO" is #define'd then we will use terminfo type function
 * calls.  If it is #undef'd, then we will use the more traditional termcap
 * type function calls.  If you can possibly get away with it, use terminfo
 * instead of termcap, as it is muck more likely your terminal descriptions
 * will be closer to reality.
 */


#ifdef _ALL_SOURCE
#include <termios.h>
#else
#include <sys/termios.h>
#endif
#include <sys/ioctl.h>

static	int		tty_des;		/* descriptor for the tty */
static	struct	termios	oldb, newb;
	char		my_PC, *BC, *UP;
	int		BClen, UPlen;

#ifndef WTERM_C

/* Systems cant seem to agree where to put these... */
#ifdef HAVE_TERMINFO
extern	int		setupterm();
extern	char		*tigetstr();
extern	int		tigetnum();
extern	int		tigetflag();
#define Tgetstr(x, y) 	tigetstr(x.iname)
#define Tgetnum(x) 	tigetnum(x.iname);
#define Tgetflag(x) 	tigetflag(x.iname);
#else
extern	int		tgetent();
extern	char		*tgetstr();
extern	int		tgetnum();
extern	int		tgetflag();
#define Tgetstr(x, y) 	tgetstr(x.tname, &y)
#define Tgetnum(x) 	tgetnum(x.tname)
#define Tgetflag(x) 	tgetflag(x.tname)
#endif

extern	char	*getenv();
extern	char	*tparm();

/*
 * The old code assumed termcap. termcap is almost always present, but on
 * many systems that have both termcap and terminfo, termcap is deprecated
 * and its databases often out of date. Configure will try to use terminfo
 * if at all possible. We define here a mapping between the termcap / terminfo
 * names, and where we store the information.
 * NOTE: The terminfo portions are NOT implemented yet.
 */
#define CAP_TYPE_BOOL   0
#define CAP_TYPE_INT    1
#define CAP_TYPE_STR    2

typedef struct cap2info
{
	const char *	iname;
	const char *	tname;
	int 		type;
	char **		ptr;
} cap2info;

/*      Our variable name          Cap / Info   Description             */
int     TI_xmc,                 /* sg  / xmc    Space left by smso/rmso */
        TI_km,                  /* km  / km     Keyboard has meta mode  */
        TI_am,                  /* am  / am     Has auto margins        */
        TI_colors,              /* Co  / colors Number of colors        */
        TI_pairs,               /* pa  / pairs  Number of color pairs   */
        TI_cols,                /* co  / cols   Number of columns       */
        TI_lines                /* li  / lines  Number of lines         */
        ;
char    *TI_cup,                /* cm  / cup    Set cursor position     */
        *TI_mrcup,              /* CM  / mrcup  Relative cursor position*/
        *TI_hpa,                /* ch  / hpa    Set horizontal position */
        *TI_vpa,                /* cv  / vpa    Set vertical position   */
        *TI_ed,                 /* cd  / ed     Erase to end of display */
        *TI_clear,              /* cl  / clear  Clear the screen        */
        *TI_cr,                 /* cr  / cr     Carriage return         */
        *TI_nl,                 /* nl  / nl     New line (linefeed)     */
        *TI_el,                 /* ce  / el     Clear to end of line    */
        *TI_cuf1,               /* nd  / cuf1   Forward one space       */
        *TI_cub1,               /* le  / cub1   Backward one space      */
        *TI_cuf,                /* RI  / cuf    Forwards X spaces       */
        *TI_cub,                /* LE  / cub    Backwards X spaces      */
        *TI_bs,                 /* bs  / bs     Backspace               */
        *TI_ind,                /* sf  / ind    Scroll forwards         */
        *TI_ri,                 /* sr  / ri     Scroll backwards        */
        *TI_indn,               /* SF  / indn   Scroll forwards X lines */
        *TI_rin,                /* SR  / rin    Scroll back X lines     */
        *TI_csr,                /* cs  / csr    Change scrolling region */
        *TI_wind,               /* wi  / wind   Set scrolling window    */
        *TI_il,                 /* AL  / il     Insert lines            */
        *TI_il1,                /* al  / il1    Insert 1 line           */
        *TI_dl,                 /* DL  / dl     Delete lines            */
        *TI_dl1,                /* dl  / dl     Delete 1 line           */
        *TI_ich,                /* IC  / ich    Insert X characters     */
        *TI_ich1,               /* ic  / ich1   Insert 1 character      */
        *TI_smir,               /* im  / smir   Enter insert mode       */
        *TI_rmir,               /* ei  / rmir   Exit insert mode        */
        *TI_smdc,               /* dm  / smdc   Enter delete mode       */
        *TI_rmdc,               /* ed  / rmdc   Exit delete mode        */
        *TI_dch,                /* DC  / dch    Delete X characters     */
        *TI_dch1,               /* dc  / dch1   Delete 1 character      */
        *TI_smso,               /* so  / smso   Start standout (rev) mode */
        *TI_rmso,               /* se  / rmso   End standout (rev) mode */
        *TI_smul,               /* us  / smul   Start underline mode    */
        *TI_rmul,               /* ue  / rmul   End underline mode      */
        *TI_bel,                /* bl  / bel    Sound the bell          */
        *TI_blink,              /* mb  / blink  Start blink mode        */
        *TI_bold,               /* md  / bold   Start bold mode         */
        *TI_sgr0,               /* me  / sgr0   No bold / blink         */
        *TI_op,                 /* op  / op     Original color pair     */
        *TI_rep,                /* rp  / rep    Repeat char X Y times   */
        *TI_setf,               /* Sf  / setf   Set foreground color    */
        *TI_setb,               /* Sb  / setb   Set background color    */
        *TI_setaf,              /* AF  / setaf  Set ANSI foreground     */
        *TI_setab,              /* AB  / setab  Set ANSI background     */
        *TI_dispc,              /* S1  / dispc  Display PC ROM char     */
        *TI_smcup,              /* ti  / smcup  Start using CUP mode    */
        *TI_rmcup,              /* te  / rmcup  Stop using CUP mode     */
        *TI_smacs,              /* as  / smacs  Start ACS mode          */
        *TI_rmacs,              /* ae  / rmacs  End ACS mode            */
        *TI_pad,                /* pc  / pad    Pad character           */
        *TI_cuu1,               /* up  / cuu1   Move up one line        */
        *TI_smam,               /* SA  / smam   Turn on auto margins    */
        *TI_rmam                /* RA  / rmam   Turn off auto margins   */
        ;

cap2info tcaps[] =
{
	{ "xmc",      "sg",   CAP_TYPE_INT,   (char **)&TI_xmc },
	{ "km",       "km",   CAP_TYPE_BOOL,  (char **)&TI_km },
	{ "am",       "am",   CAP_TYPE_BOOL,  (char **)&TI_am },
	{ "colors",   "Co",   CAP_TYPE_INT,   (char **)&TI_colors },
	{ "pairs",    "pa",   CAP_TYPE_INT,   (char **)&TI_pairs },
	{ "cols",     "co",   CAP_TYPE_INT,   (char **)&TI_cols },
	{ "lines",    "li",   CAP_TYPE_INT,   (char **)&TI_lines },
	{ "cup",      "cm",   CAP_TYPE_STR,   &TI_cup },
	{ "mrcup",    "CM",   CAP_TYPE_STR,   &TI_mrcup },
	{ "hpa",      "ch",   CAP_TYPE_STR,   &TI_hpa },
	{ "vpa",      "cv",   CAP_TYPE_STR,   &TI_vpa },
	{ "ed",       "cd",   CAP_TYPE_STR,   &TI_ed },
	{ "clear",    "cl",   CAP_TYPE_STR,   &TI_clear },
	{ "cr",       "cr",   CAP_TYPE_STR,   &TI_cr },
	{ "nl",       "nl",   CAP_TYPE_STR,   &TI_nl },
	{ "el",       "ce",   CAP_TYPE_STR,   &TI_el },
	{ "cuf1",     "nd",   CAP_TYPE_STR,   &TI_cuf1 },
	{ "cub1",     "le",   CAP_TYPE_STR,   &TI_cub1 },
	{ "cuf",      "RI",   CAP_TYPE_STR,   &TI_cuf },
	{ "cub",      "LE",   CAP_TYPE_STR,   &TI_cub },
	{ "bs",       "bs",   CAP_TYPE_STR,   &TI_bs },
	{ "ind",      "sf",   CAP_TYPE_STR,   &TI_ind },
	{ "ri",       "sr",   CAP_TYPE_STR,   &TI_ri },
	{ "indn",     "SF",   CAP_TYPE_STR,   &TI_indn },
	{ "rin",      "SR",   CAP_TYPE_STR,   &TI_rin },
	{ "csr",      "cs",   CAP_TYPE_STR,   &TI_csr },
	{ "wind",     "wi",   CAP_TYPE_STR,   &TI_wind },
	{ "il",       "AL",   CAP_TYPE_STR,   &TI_il },
	{ "il1",      "al",   CAP_TYPE_STR,   &TI_il1 },
	{ "dl",       "DL",   CAP_TYPE_STR,   &TI_dl },
	{ "dl1",      "dl",   CAP_TYPE_STR,   &TI_dl1 },
	{ "ich",      "IC",   CAP_TYPE_STR,   &TI_ich },
	{ "ich1",     "ic",   CAP_TYPE_STR,   &TI_ich1 },
	{ "smir",     "im",   CAP_TYPE_STR,   &TI_smir },
	{ "rmir",     "ei",   CAP_TYPE_STR,   &TI_rmir },
	{ "smdc",     "dm",   CAP_TYPE_STR,   &TI_smdc },
	{ "rmdc",     "ed",   CAP_TYPE_STR,   &TI_rmdc },
	{ "dch",      "DC",   CAP_TYPE_STR,   &TI_dch },
	{ "dch1",     "dc",   CAP_TYPE_STR,   &TI_dch1 },
	{ "smso",     "so",   CAP_TYPE_STR,   &TI_smso },
	{ "rmso",     "se",   CAP_TYPE_STR,   &TI_rmso },
	{ "smul",     "us",   CAP_TYPE_STR,   &TI_smul },
	{ "rmul",     "ue",   CAP_TYPE_STR,   &TI_rmul },
	{ "bel",      "bl",   CAP_TYPE_STR,   &TI_bel },
	{ "blink",    "mb",   CAP_TYPE_STR,   &TI_blink },
	{ "bold",     "md",   CAP_TYPE_STR,   &TI_bold },
	{ "sgr0",     "me",   CAP_TYPE_STR,   &TI_sgr0 },
	{ "op",       "op",   CAP_TYPE_STR,   &TI_op },
	{ "rep",      "rp",   CAP_TYPE_STR,   &TI_rep },
	{ "setf",     "Sf",   CAP_TYPE_STR,   &TI_setf },
	{ "setb",     "Sb",   CAP_TYPE_STR,   &TI_setb },
	{ "setaf",    "AF",   CAP_TYPE_STR,   &TI_setaf },
	{ "setab",    "AB",   CAP_TYPE_STR,   &TI_setab },
	{ "dispc",    "S1",   CAP_TYPE_STR,   &TI_dispc },
	{ "smcup",    "ti",   CAP_TYPE_STR,   &TI_smcup },
	{ "rmcup",    "te",   CAP_TYPE_STR,   &TI_rmcup },
	{ "smacs",    "as",   CAP_TYPE_STR,   &TI_smacs },
	{ "rmacs",    "ae",   CAP_TYPE_STR,   &TI_rmacs },
	{ "pad",      "pc",   CAP_TYPE_STR,   &TI_pad },
	{ "cuu1",     "up",   CAP_TYPE_STR,   &TI_cuu1 },
	{ "smam",     "SA",   CAP_TYPE_STR,   &TI_smam },
	{ "rmam",     "RA",   CAP_TYPE_STR,   &TI_rmam }
};
	int 	numcaps = sizeof(tcaps) / sizeof(cap2info);
	char 	TI_normal[256];
	char *	TI_sgrstrs[TERM_SGR_MAXVAL];
	char *	TI_forecolors[16];
	char *	TI_backcolors[16];

	int	meta_mode = 2;
	int	can_color = 0;
	int	need_redraw = 0;
static	int	term_echo_flag = 1;
static	int	li;
static	int	co;
static	char	termcap[2048];	/* bigger than we need, just in case */
static	char	termcap2[2048];	/* bigger than we need, just in case */
static	char	*tptr = termcap2;


/*
 * term_echo: if 0, echo is turned off (all characters appear as blanks), if
 * non-zero, all is normal.  The function returns the old value of the
 * term_echo_flag 
 */
int	term_echo (int flag)
{
	int	echo;

	echo = term_echo_flag;
	term_echo_flag = flag;
	return (echo);
}

/*
 * term_putchar: This is what handles the outputting of the input buffer.
 * It used to handle more, but slowly the stuff in screen.c prepared away
 * all of the nasties that this was intended to handle.  Well anyhow, all
 * we need to worry about here is making sure nothing suspcious, like an
 * escape, makes its way to the output stream.
 */
void	term_putchar (unsigned char c)
{
	if (!term_echo_flag)
	{
		putchar_x(' ');
		return;
	}

	/*
	 * If the user has /set eight_bit_characters off, then we lop off
	 * any 8 bit characters we might stumble across.
	 */
	if (!(newb.c_cflag & CS8) && (c & 0x80))
		c &= ~0x80;

	/*
	 * Any nonprintable characters we lop off.  In addition to this,
	 * we catch the very nasty 0x9b which is the escape character with
	 * the high bit set.  It is possible (indeed, likely) that a terminal
	 * with only 7 bit capabilities wont be reflected in /set eight_bit.
	 * It is therefore our job to head that possibility off at the pass.
	 */
	if (c < 0x20 || c == 0x9b)
	{
		term_standout_on();
		putchar_x((c | 0x40) & 0x7f);
		term_standout_off();
	}

	/*
	 * The delete character is handled especially as well
	 */
	else if (c == 0x7f) 	/* delete char */
	{
		term_standout_on();
		putchar_x('?');
		term_standout_off();
	}

	/*
	 * Everything else is passed through.
	 */
	else
		putchar_x(c);
}


/*
 * term_reset: sets terminal attributed back to what they were before the
 * program started 
 */
void term_reset (void)
{
	tcsetattr(tty_des, TCSADRAIN, &oldb);

	if (TI_csr)
		tputs_x(tparm(TI_csr, 0, TI_lines - 1));
	term_gotoxy(0, TI_lines - 1);
#if use_alt_screen
	if (TI_rmcup)
		tputs_x(TI_rmcup);
#endif
#if use_automargins
	if (TI_am && TI_smam)
		tputs_x(TI_smam);
#endif
	term_flush();
}

/*
 * term_cont: sets the terminal back to IRCII stuff when it is restarted
 * after a SIGSTOP.  Somewhere, this must be used in a signal() call 
 */
SIGNAL_HANDLER(term_cont)
{
	use_input = foreground = (tcgetpgrp(0) == getpgrp());
	if (foreground)
	{
#if use_alt_screen
		if (TI_smcup)
			tputs_x(TI_smcup);
#endif
#if use_automargins
		if (TI_rmam)
			tputs_x(TI_rmam);
#endif
		need_redraw = 1;
		tcsetattr(tty_des, TCSADRAIN, &newb);
	}
}

/*
 * term_pause: sets terminal back to pre-program days, then SIGSTOPs itself. 
 */
void term_pause (char unused, char *not_used)
{
	term_reset();
	kill(getpid(), SIGSTOP);
}
#endif /* NOT IN WTERM_C */



/*
 * term_init: does all terminal initialization... reads termcap info, sets
 * the terminal to CBREAK, no ECHO mode.   Chooses the best of the terminal
 * attributes to use ..  for the version of this function that is called for
 * wserv, we set the termial to RAW, no ECHO, so that all the signals are
 * ignored.. fixes quite a few problems...  -phone, jan 1993..
 */
int	termfeatures = 0;

int 	term_init (void)
{
#ifndef WTERM_C
	int	i,
		desired;
	char	*term;

	if (dumb_mode)
		panic("term_init called in dumb_mode");

	if ((term = getenv("TERM")) == (char *) 0)
	{
		fprintf(stderr, "\n");
		fprintf(stderr, "You do not have a TERM environment variable.\n");
		fprintf(stderr, "So we'll be running in dumb mode...\n");
		return -1;
	}
	else
		fprintf(stdout, "Using terminal type [%s]\n", term);

#ifdef HAVE_TERMINFO
	setupterm(NULL, 1, &i);
	if (i != 1)
	{
		fprintf(stderr, "setupterm failed: %d\n", i);
		fprintf(stderr, "So we'll be running in dumb mode...\n");
		return -1;
	}
#else
	if (tgetent(termcap, term) < 1)
	{
		fprintf(stderr, "\n");
		fprintf(stderr, "Your current TERM setting (%s) does not have a termcap entry.\n", term);
		fprintf(stderr, "So we'll be running in dumb mode...\n");
		return -1;
	}
#endif

	for (i = 0; i < numcaps; i++)
	{
		int *iptr, ival;

		if (tcaps[i].type == CAP_TYPE_INT)
		{
			iptr = (int *)tcaps[i].ptr;
			ival = Tgetnum(tcaps[i]);
			*iptr = ival;
		}
		else if (tcaps[i].type == CAP_TYPE_BOOL)
		{
			iptr = (int *)tcaps[i].ptr;
			ival = Tgetflag(tcaps[i]);
			*iptr = ival;
		}
		else
		{
			if ((*tcaps[i].ptr = Tgetstr(tcaps[i], tptr)) == (char *) -1)
				*tcaps[i].ptr = NULL;
		}
	}

	BC = TI_cub1;
	UP = TI_cuu1;
	if (TI_pad)
		my_PC = TI_pad[0];
	else
		my_PC = 0;

	if (BC)
		BClen = strlen(BC);
	else
		BClen = 0;
	if (UP)
		UPlen = strlen(UP);
	else
		UPlen = 0;

	li = TI_lines;
	co = TI_cols;
	if (!co)
		co = 79;
	if (!li)
		li = 24;

	if (!TI_nl)
		TI_nl = "\n";
	if (!TI_cr)
		TI_cr = "\r";

	TI_normal[0] = 0;
	if (TI_sgr0)
	{
		strcat(TI_normal, TI_sgr0);
	}
	if (TI_rmso)
	{
		if (TI_sgr0 && strcmp(TI_rmso, TI_sgr0))
			strcat(TI_normal, TI_rmso);
	}
	if (TI_rmul)
	{
		if (TI_sgr0 && strcmp(TI_rmul, TI_sgr0))
			strcat (TI_normal, TI_rmul);
	}


	/*
	 * Finally set up the TI_sgrstrs array.  Clean it out first...
	 */
	for (i = 0; i < TERM_SGR_MAXVAL; i++)
		TI_sgrstrs[i] = "";

	/*
	 * Default to no colors. (ick)
	 */
	for (i = 0; i < 16; i++)
		TI_forecolors[i] = TI_backcolors[i] = "";

	if (TI_bold)
		TI_sgrstrs[TERM_SGR_BOLD_ON-1] = TI_bold;
	if (TI_sgr0)
	{
		TI_sgrstrs[TERM_SGR_BOLD_OFF - 1] = TI_sgr0;
		TI_sgrstrs[TERM_SGR_BLINK_OFF - 1] = TI_sgr0;
	}
	if (TI_blink)
		TI_sgrstrs[TERM_SGR_BLINK_ON - 1] = TI_blink;
	if (TI_smul)
		TI_sgrstrs[TERM_SGR_UNDL_ON - 1] = TI_smul;
	if (TI_rmul)
		TI_sgrstrs[TERM_SGR_UNDL_OFF - 1] = TI_rmul;
	if (TI_smso)
		TI_sgrstrs[TERM_SGR_REV_ON - 1] = TI_smso;
	if (TI_rmso)
		TI_sgrstrs[TERM_SGR_REV_OFF - 1] = TI_rmso;
	if (TI_normal[0])
	{
		TI_sgrstrs[TERM_SGR_NORMAL - 1] = TI_normal;
		TI_sgrstrs[TERM_SGR_RESET - 1] = TI_normal;
	}

	/*
	 * Now figure out whether or not this terminal is "capable enough"
	 * for the client. This is a rather complicated set of tests, as
	 * we have many ways of doing the same thing.
	 * To keep the set of tests easier, we set up a bitfield integer
	 * which will have the desired capabilities added to it. If after
	 * all the checks we dont have the desired mask, we dont have a
	 * capable enough terminal.
	 */
	desired = TERM_CAN_CUP | TERM_CAN_CLEAR | TERM_CAN_CLREOL |
		TERM_CAN_RIGHT | TERM_CAN_LEFT | TERM_CAN_SCROLL;

	termfeatures = 0;
	if (TI_cup)
		termfeatures |= TERM_CAN_CUP;
	if (TI_hpa && TI_vpa)
		termfeatures |= TERM_CAN_CUP;
	if (TI_clear)
		termfeatures |= TERM_CAN_CLEAR;
	if (TI_ed)
		termfeatures |= TERM_CAN_CLEAR;
	if (TI_dl || TI_dl1)
		termfeatures |= TERM_CAN_CLEAR;
	if (TI_il || TI_il1)
		termfeatures |= TERM_CAN_CLEAR;
	if (TI_el)
		termfeatures |= TERM_CAN_CLREOL;
	if (TI_cub || TI_mrcup || TI_cub1 || TI_bs)
		termfeatures |= TERM_CAN_LEFT;
	if (TI_cuf  || TI_cuf1 || TI_mrcup)
		termfeatures |= TERM_CAN_RIGHT;
	if (TI_dch || TI_dch1)
		termfeatures |= TERM_CAN_DELETE;
	if (TI_ich || TI_ich1)
		termfeatures |= TERM_CAN_INSERT;
	if (TI_rep)
		termfeatures |= TERM_CAN_REPEAT;
	if (TI_csr && (TI_ri || TI_rin) && (TI_ind || TI_indn))
		termfeatures |= TERM_CAN_SCROLL;
	if (TI_wind && (TI_ri || TI_rin) && (TI_ind || TI_indn))
		termfeatures |= TERM_CAN_SCROLL;
	if ((TI_il || TI_il1) && (TI_dl || TI_dl1))
		termfeatures |= TERM_CAN_SCROLL;
	if (TI_bold && TI_sgr0)
		termfeatures |= TERM_CAN_BOLD;
	if (TI_blink && TI_sgr0)
		termfeatures |= TERM_CAN_BLINK;
	if (TI_smul && TI_rmul)
		termfeatures |= TERM_CAN_UNDL;
	if (TI_smso && TI_rmso)
		termfeatures |= TERM_CAN_REVERSE;
	if (TI_dispc)
		termfeatures |= TERM_CAN_GCHAR;
	if ((TI_setf && TI_setb) || (TI_setaf && TI_setab))
		termfeatures |= TERM_CAN_COLOR;

	if ((termfeatures & desired) != desired)
	{
		fprintf(stderr, "\nYour terminal (%s) cannot run IRC II in full screen mode.\n", term);
		fprintf(stderr, "The following features are missing from your TERM setting.\n");
		if (!(termfeatures & TERM_CAN_CUP))
			fprintf (stderr, "\tCursor movement\n");
		if (!(termfeatures & TERM_CAN_CLEAR))
			fprintf(stderr, "\tClear screen\n");
		if (!(termfeatures & TERM_CAN_CLREOL))
			fprintf(stderr, "\tClear to end-of-line\n");
		if (!(termfeatures & TERM_CAN_RIGHT))
			fprintf(stderr, "\tCursor right\n");
		if (!(termfeatures & TERM_CAN_LEFT))
			fprintf(stderr, "\tCursor left\n");
		if (!(termfeatures & TERM_CAN_SCROLL))
			fprintf(stderr, "\tScrolling\n");

		fprintf(stderr, "So we'll be running in dumb mode...\n");
		return -1;
	}

	if (!TI_cub1)
	{
		if (TI_bs)
			TI_cub1 = TI_bs;
		else
			TI_cub1 = "\b";
	}
	if (!TI_bel)
		TI_bel = "\007";

	/*
	 * Set up colors.
	 * Absolute fallbacks are for ansi-type colors
	 */
	for (i = 0; i < 16; i++)
	{
		char cbuf[128];

		cbuf[0] = '\0';
		if (i >= 8)
			strcpy(cbuf, TI_sgrstrs[TERM_SGR_BOLD_ON-1]);
#if 0
		else
			strcpy(cbuf, TI_sgrstrs[TERM_SGR_BOLD_OFF-1]);
#endif

		if (TI_setaf) 
			strcat(cbuf, tparm(TI_setaf, i & 0x07, 0));
		else if (TI_setf)
			strcat(cbuf, tparm(TI_setf, i & 0x07, 0));
		else if (i >= 8)
			sprintf(cbuf, "\033[1;%dm", (i & 0x07) + 30);
		else
			sprintf(cbuf, "\033[%dm", (i & 0x07) + 30);

		TI_forecolors[i] = m_strdup(cbuf);
	}
            
	for (i = 0; i < 16; i++)
	{
		char cbuf[128];

		cbuf[0] = '\0';
		if (i >= 8)
			strcpy(cbuf, TI_sgrstrs[TERM_SGR_BLINK_ON - 1]);

		if (TI_setab)
			strcat (cbuf, tparm(TI_setab, i & 0x07, 0));
		else if (TI_setb)
			strcat (cbuf, tparm(TI_setb, i & 0x07, 0));
		else if (i >= 8)
			sprintf(cbuf, "\033[1;%dm", (i & 0x07) + 40);
		else
			sprintf(cbuf, "\033[%dm", (i & 0x07) + 40);

		TI_backcolors[i] = m_strdup(cbuf);
	}
#endif /* NOT IN WTERM_C */

/* Set up the terminal discipline */
	tty_des = 0;			/* Has to be. */
	tcgetattr(tty_des, &oldb);

	newb = oldb;
	newb.c_lflag &= ~(ICANON | ECHO); /* set equ. of CBREAK, no ECHO */
	newb.c_cc[VMIN] = 1;	          /* read() satified after 1 char */
	newb.c_cc[VTIME] = 0;	          /* No timer */

#       if !defined(_POSIX_VDISABLE)
#               if defined(HAVE_FPATHCONF)
#                       define _POSIX_VDISABLE fpathconf(tty_des, _PC_VDISABLE)
#               else
#                       define _POSIX_VDISABLE 0
#               endif
#       endif

	newb.c_cc[VQUIT] = _POSIX_VDISABLE;
#	ifdef VDSUSP
		newb.c_cc[VDSUSP] = _POSIX_VDISABLE;
# 	endif
#	ifdef VSUSP
		newb.c_cc[VSUSP] = _POSIX_VDISABLE;
#	endif

#ifndef WTERM_C
	if (!use_flow_control)
		newb.c_iflag &= ~IXON;	/* No XON/XOFF */

#if use_alt_screen
	/* Ugh. =) This is annoying! */
	if (TI_smcup)
		tputs_x(TI_smcup);
#endif
#if use_automargins
	if (TI_rmam)
		tputs_x(TI_rmam);
#endif
#endif

	tcsetattr(tty_des, TCSADRAIN, &newb);
	return 0;
}


#ifndef WTERM_C
/*
 * term_resize: gets the terminal height and width.  Trys to get the info
 * from the tty driver about size, if it can't... uses the termcap values. If
 * the terminal size has changed since last time term_resize() has been
 * called, 1 is returned.  If it is unchanged, 0 is returned. 
 */
int term_resize (void)
{
	static	int	old_li = -1,
			old_co = -1;

#	if defined (TIOCGWINSZ)
	{
		struct winsize window;

		if (ioctl(tty_des, TIOCGWINSZ, &window) < 0)
		{
			TI_lines = li;
			TI_cols = co;
		}
		else
		{
			if ((TI_lines = window.ws_row) == 0)
				TI_lines = li;
			if ((TI_cols = window.ws_col) == 0)
				TI_cols = co;
		}
	}
#	else
	{
		TI_lines = li;
		TI_cols = co;
	}
#	endif

#if use_automargins
	if (!TI_am || !TI_rmam)
	{
		TI_cols--;
	}
#else
	TI_cols--;
#endif

	if ((old_li != TI_lines) || (old_co != TI_cols))
	{
		old_li = TI_lines;
		old_co = TI_cols;
		if (main_screen)
			main_screen->li = TI_lines;
		if (main_screen)
			main_screen->co = TI_cols;
		return (1);
	}
	return (0);
}


/* term_CE_clear_to_eol(): the clear to eol function, right? */
void	term_clreol	(void)
{
	tputs_x(TI_el);
	return;
}


void term_beep (void)
{
	if (get_int_var(BEEP_VAR))
	{
		tputs_x(TI_bel);
		term_flush();
	}
}

int	orig_term_eight_bit (void)
{
	if (dumb_mode)
		return 1;
	return (((oldb.c_cflag) & CSIZE) == CS8) ? 1 : 0;
}

int 	term_eight_bit (void)
{
	if (dumb_mode)
		return 1;
	return (((newb.c_cflag) & CSIZE) == CS8) ? 1 : 0;
}

void	set_term_eight_bit (int value)
{
	if (dumb_mode)
		return;

	if (value == ON)
	{
		newb.c_cflag |= CS8;
		newb.c_iflag &= ~ISTRIP;
	}
	else
	{
		newb.c_cflag &= ~CS8;
		newb.c_iflag |= ISTRIP;
	}
	tcsetattr(tty_des, TCSADRAIN, &newb);
}

void	set_meta_8bit (int value)
{
	if (dumb_mode)
		return;

	if (value == 0)
		meta_mode = 0;
	else if (value == 1)
		meta_mode = 1;
	else if (value == 2)
		meta_mode = (TI_km == 0 ? 0 : 1);
}

/* Set the cursor position */
void term_gotoxy (int col, int row)
{
	if (TI_cup)
		tputs_x(tparm(TI_cup, row, col));
	else
	{
		tputs_x(tparm(TI_hpa, col));
		tputs_x(tparm(TI_vpa, row));
	}
}

/* A no-brainer. Clear the screen. */
void term_clrscr (void)
{
	int i;

	/* We have a specific cap for clearing the screen */
	if (TI_clear)
	{
		tputs_x(TI_clear);
		term_gotoxy (0, 0);
		return;
	}

	term_gotoxy (0, 0);
	/* We can clear by doing an erase-to-end-of-display */
	if (TI_ed)
	{
		tputs_x (TI_ed);
		return;
	}
	/* We can also clear by deleteing lines ... */
	else if (TI_dl)
	{
		tputs_x(tparm(TI_dl, TI_lines));
		return;
	}
	/* ... in this case one line at a time */
	else if (TI_dl1)
	{
		for (i = 0; i < TI_lines; i++)
			tputs_x (TI_dl1);
		return;
	}
	/* As a last resort we can insert lines ... */
	else if (TI_il)
	{
		tputs_x (tparm(TI_il, TI_lines));
		term_gotoxy (0, 0);
		return;
	}
	/* ... one line at a time */
	else if (TI_il1)
	{
		for (i = 0; i < TI_lines; i++)
			tputs_x (TI_il1);
		term_gotoxy (0, 0);
	}
}

/*
 * Move the cursor NUM spaces to the left, non-destruvtively if we can.
 */
void term_left (int num)
{
	if (TI_cub)
		tputs_x (tparm(TI_cub, num));
	else if (TI_mrcup)
		tputs_x (tparm(TI_mrcup, -num, 0));
	else if (TI_cub1)
		while (num--)
			tputs_x(TI_cub1);
	else if (TI_bs)
		while (num--)
			tputs_x (TI_bs);
}

/*
 * Move the cursor NUM spaces to the right
 */
void term_right (int num)
{
	if (TI_cuf)
		tputs_x (tparm(TI_cuf, num));
	else if (TI_mrcup)
		tputs_x (tparm(TI_mrcup, num, 0));
	else if (TI_cuf1)
		while (num--)
			tputs_x(TI_cuf1);
}

/*
 * term_delete (int num)
 * Deletes NUM characters at the current position
 */
void term_delete (int num)
{
	if (TI_smdc)
		tputs_x(TI_smdc);

	if (TI_dch)
		tputs_x (tparm (TI_dch, num));
	else if (TI_dch1)
		while (num--)
			tputs_x (TI_dch1);

	if (TI_rmdc)
		tputs_x (TI_rmdc);
}

/*
 * Insert character C at the curent cursor position.
 */
void term_insert (unsigned char c)
{
	if (TI_smir)
		tputs_x (TI_smir);
	else if (TI_ich1)
		tputs_x (TI_ich1);
	else if (TI_ich)
		tputs_x (tparm(TI_ich, 1));

	term_putchar (c);

	if (TI_rmir)
		tputs_x(TI_rmir);
}

/*
 * Repeat the character C REP times in the most efficient way.
 */
void term_repeat (unsigned char c, int rep)
{
	if (TI_rep)
		tputs_x(tparm (TI_rep, (int)c, rep));
	else
		while (rep--)
			putchar_x (c);
}

/*
 * Scroll the screen N lines between lines TOP and BOT.
 */
void term_scroll (int top, int bot, int n)
{
	int i,oneshot=0,rn,sr,er;
	char thing[128], final[128], start[128];

	/* Some basic sanity checks */
	if (n == 0 || top == bot || bot < top)
		return;

	sr = er = 0;
	final[0] = start[0] = thing[0] = 0;

	if (n < 0)
		rn = -n;
	else
		rn = n;

	/*
	 * First thing we try to do is set the scrolling region on a 
	 * line granularity.  In order to do this, we need to be able
	 * to have termcap 'cs', and as well, we need to have 'sr' if
	 * we're scrolling down, and 'sf' if we're scrolling up.
	 */
	if (TI_csr && (TI_ri || TI_rin) && (TI_ind || TI_indn))
	{
		/*
		 * Previously there was a test to see if the entire scrolling
		 * region was the full screen.  That test *always* fails,
		 * because we never scroll the bottom line of the screen.
		 */
		strcpy(start, tparm(TI_csr, top, bot));
		strcpy(final, tparm(TI_csr, 0, TI_lines-1));

		if (n > 0)
		{
			sr = bot;
			er = top;
			if (TI_indn)
			{
				oneshot = 1;
				strcpy(thing, tparm(TI_indn, rn, rn));
			}
			else
				strcpy(thing, TI_ind);
		}
		else
		{
			sr = top;
			er = bot;
			if (TI_rin)
			{
				oneshot = 1;
				strcpy (thing, tparm(TI_rin, rn, rn));
			}
			else
				strcpy (thing, TI_ri);
		}
	}

	else if (TI_wind && (TI_ri || TI_rin) && (TI_ind || TI_indn))
	{
		strcpy(start, tparm(TI_wind, top, bot, 0, TI_cols-1));
		strcpy(final, tparm(TI_wind, 0, TI_lines-1, 0, TI_cols-1));

		if (n > 0)
		{
			sr = bot;
			er = top;
			if (TI_indn)
			{
				oneshot = 1;
				strcpy (thing, tparm(TI_indn, rn, rn));
			}
			else
				strcpy (thing, TI_ind);
		}
		else
		{
			sr = top;
			er = bot;
			if (TI_rin)
			{
				oneshot = 1;
				strcpy (thing,tparm(TI_rin, rn, rn));
			}
			else
				strcpy (thing, TI_ri);
		}
	}

	else if ((TI_il || TI_il1) && (TI_dl || TI_dl1))
	{
		if (n > 0)
		{
			sr = top;
			er = bot;

			if (TI_dl)
			{
				oneshot = 1;
				strcpy (thing, tparm(TI_dl, rn, rn));
			}
			else
				strcpy (thing, TI_dl1);

			if (TI_il)
			{
				oneshot = 1;
				strcpy(final, tparm(TI_il, rn, rn));
			}
			else
				strcpy(final, TI_il1);
		}
		else
		{
			sr = bot;
			er = top;
			if (TI_il)
			{
				oneshot = 1;
				strcpy (thing, tparm(TI_il, rn, rn));
			}
			else
				strcpy (thing, TI_il1);

			if (TI_dl)
			{
				oneshot = 1;
				strcpy(final, tparm(TI_dl, rn, rn));
			}
			else
				strcpy(final, TI_dl1);
		}
	}


	if (!thing[0])
		return;

	/* Do the actual work here */
	if (start[0])
		tputs_x (start);
	term_gotoxy (0, sr);

	if (oneshot)
		tputs_x (thing);
	else
	{
		for (i = 0; i < rn; i++)
			tputs_x(thing);
	}
	term_gotoxy (0, er);
	if (final[0])
		tputs_x(final);
}

/*
 * term_getsgr(int opt, int fore, int back)
 * Return the string required to set the given mode. OPT defines what
 * we really want it to do. It can have these values:
 * TERM_SGR_BOLD_ON     - turn bold mode on
 * TERM_SGR_BOLD_OFF    - turn bold mode off
 * TERM_SGR_BLINK_ON    - turn blink mode on
 * TERM_SGR_BLINK_OFF   - turn blink mode off
 * TERM_SGR_UNDL_ON     - turn underline mode on
 * TERM_SGR_UNDL_OFF    - turn underline mode off
 * TERM_SGR_REV_ON      - turn reverse video on
 * TERM_SGR_REV_OFF     - turn reverse video off
 * TERM_SGR_NORMAL      - turn all attributes off
 * TERM_SGR_RESET       - all attributes off and back to default colors
 * TERM_SGR_FOREGROUND  - set foreground color
 * TERM_SGR_BACKGROUND  - set background color
 * TERM_SGR_COLORS      - set foreground and background colors
 * TERM_SGR_GCHAR       - print graphics character
 *
 * The colors are defined as:
 * 0    - black
 * 1    - red
 * 2    - green
 * 3    - brown
 * 4    - blue
 * 5    - magenta
 * 6    - cyan
 * 7    - white
 * 8    - grey (foreground only)
 * 9    - bright red (foreground only)
 * 10   - bright green (foreground only)
 * 11   - bright yellow (foreground only)
 * 12   - bright blue (foreground only)
 * 13   - bright magenta (foreground only)
 * 14   - bright cyan (foreground only)
 * 15   - bright white (foreground only)
 */
char *term_getsgr (int opt, int fore, int back)
{
	char *ret = empty_string;

	switch (opt)
	{
		case TERM_SGR_BOLD_ON:
		case TERM_SGR_BOLD_OFF:
		case TERM_SGR_BLINK_OFF:
		case TERM_SGR_BLINK_ON:
		case TERM_SGR_UNDL_ON:
		case TERM_SGR_UNDL_OFF:
		case TERM_SGR_REV_ON:
		case TERM_SGR_REV_OFF:
		case TERM_SGR_NORMAL:
		case TERM_SGR_RESET:
			ret = TI_sgrstrs[opt-1];
			break;
		case TERM_SGR_FOREGROUND:
			ret = TI_forecolors[fore & 0x0f];
			break;
		case TERM_SGR_BACKGROUND:
			ret = TI_backcolors[fore & 0x0f];
			break;
		case TERM_SGR_GCHAR:
			if (TI_dispc)
				ret = tparm(TI_dispc, fore);
			break;
		default:
			panic ("Unknown option '%d' to term_getsgr", opt);
			break;
	}
	return (ret);
}

#endif /* NOT IN WTERM_C */
