
/*
 * term.c -- termios and termcap handlers
 *
 * Copyright 1990 Michael Sandrof
 * Copyright 1995 Matthew Green
 * Coypright 1996 EPIC Software Labs
 *
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 */


#include "irc.h"
#include "vars.h"
#include "ircterm.h"
#include "window.h"
#include "screen.h"
#include "output.h"
#ifdef TRANSLATE
#include "translat.h"
#endif


#ifndef WINNT
#ifdef HAVE_SYS_TERMIOS_H
#include <sys/termios.h>
#else
#include <termios.h>
#endif

#include <sys/ioctl.h>

static	int		tty_des;		/* descriptor for the tty */
static	struct	termios	oldb, newb;

#ifndef WTERM_C
extern	char	*tgetstr();
extern	int	tgetent();
extern	char	*getenv();
extern	int	foreground;

static	int	term_CE_clear_to_eol 	(void);
static	int	term_CS_scroll 		(int, int, int);
static	int	term_param_ALDL_scroll  (int, int, int);
static	int	term_IC_insert 		(char);
static	int	term_IMEI_insert 	(char);
static	int	term_DC_delete 		(void);
static	int	term_BS_cursor_left 	(void);
static	int	term_LE_cursor_left 	(void);
static	int	term_ND_cursor_right 	(void);
static 	int	term_null_function 	(void);

/*
 * Function variables: each returns 1 if the function is not supported on the
 * current term type, otherwise they do their thing and return 0 
 */
int	(*term_scroll) (int, int, int);	/* this is set to the best scroll available */
int	(*term_insert) (char);		/* this is set to the best insert available */
int	(*term_delete) (void);		/* this is set to the best delete available */
int	(*term_cursor_left) (void);	/* this is set to the best left available */
int	(*term_cursor_right) (void); 	/* this is set to the best right available */
int	(*term_clear_to_eol) (void); 	/* this is set... figure it out */

/* The termcap variables */
char	*CM, *CE, *CL, *CR, *NL, *AL, *DL, *CS, *DC, *IC, *IM, *EI, 
	*SO, *SE, *US, *UE, *MD, *ME, *SF, *SR, *ND, *LE, *BL, *BS;
int	CO = 79,
	LI = 24,
	SG;

/*
 * term_reset_flag: set to true whenever the terminal is reset, thus letter
 * the calling program work out what to do 
 */
	int	need_redraw = 0;
static	int	term_echo_flag = 1;
static	int	li;
static	int	co;
static	char	termcap[1024];


/*
 * 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: puts a character to the screen, and displays control
 * characters as inverse video uppercase letters.  NOTE:  Dont use this to
 * display termcap control sequences!  It won't work! 
 *
 * Um... well, it will work if DISPLAY_ANSI_VAR is set to on... (hop)
 */
void	term_putchar (unsigned char c)
{
	if (term_echo_flag)
	{
#ifdef TRANSLATE
		if (translation)
			c = transToClient[c];
#endif                                        
		/* Sheer, raving paranoia */
		if (!(newb.c_cflag & CS8) && (c & 0x80))
			c &= ~0x80;

		if (get_int_var(EIGHT_BIT_CHARACTERS_VAR) == 0)
			if (c & 128) c &= ~128;

		/* 
		 * The only control character in ascii sequences
		 * is 27, which is the escape -- all the rest of
		 * them are printable (i think).  So we should
		 * print all the rest of the control seqs as
		 * reverse like normal (idea from Genesis K.)
		 *
		 * Why do i know im going to regret this?
		 */
		if ((c != 27) || !get_int_var(DISPLAY_ANSI_VAR))
		{
			if (c < 32)
			{
				term_standout_on();
				c = (c & 127) | 64;
				fputc(c, (current_screen?current_screen->fpout:stdout));
				term_standout_off();
			}
			else if (c == '\177')
			{
				term_standout_on();
				c = '?';
				fputc(c, (current_screen?current_screen->fpout:stdout));
				term_standout_off();
			}
			else
				fputc((int)c, (current_screen?current_screen->fpout:stdout));
		}
		else
			fputc((int)c, (current_screen?current_screen->fpout:stdout));
	}
	else
	{
		c = ' ';
		fputc((int)c, (current_screen?current_screen->fpout:stdout));
	}
}

/* term_puts: uses term_putchar to print text */
int	term_puts(char *str, int len)
{
	int	i;

	for (i = 0; *str && (i < len); str++, i++)
		term_putchar(*str);
	return (i);
}

/* putchar_x: the putchar function used by tputs */
int putchar_x (int c)
{
	return fputc(c, (current_screen?current_screen->fpout:stdout));
}

void term_flush (void)
{
	fflush((current_screen?current_screen->fpout:stdout));
}

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

	if   (CS)
		tputs_x(tgoto(CS, LI - 1, 0));
	term_move_cursor(0, LI - 1);
	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 
 */
RETSIGTYPE term_cont (int unused)
{
	use_input = foreground = (tcgetpgrp(0) == getpgrp());
	if (foreground)
	{
		need_redraw = 1;
		tcsetattr(tty_des, TCSADRAIN, &newb);
	}
}

/*
 * term_pause: sets terminal back to pre-program days, then SIGSTOPs itself. 
 */
extern 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 term_init (void)
{
#ifndef WTERM_C
	char	bp[2048],
		*term,
		*ptr;

	if ((term = getenv("TERM")) == (char *) 0)
	{
		fprintf(stderr, "irc: No TERM variable found!\n");
		return -1;
	}
	if (tgetent(bp, term) < 1)
	{
		fprintf(stderr, "irc: Current TERM variable for %s has no termcap entry.\n", term);
		return -1;
	}

	if ((co = tgetnum("co")) == -1)
		co = 80;
	if ((li = tgetnum("li")) == -1)
		li = 24;
	ptr = termcap;

	/*	
	 * Get termcap capabilities
	 */	
	SG = tgetnum("sg");		/* Garbage chars gen by Standout */
	CM = tgetstr("cm", &ptr);	/* Cursor Movement */
	CL = tgetstr("cl", &ptr);	/* CLear screen, home cursor */
	CR = tgetstr("cr", &ptr);	/* Carriage Return */
	NL = tgetstr("nl", &ptr);	/* New Line */
	CE = tgetstr("ce", &ptr);	/* Clear to End of line */
	ND = tgetstr("nd", &ptr);	/* NonDestrucive space (cursor right) */
	LE = tgetstr("le", &ptr);	/* move cursor to LEft */
	BS = tgetstr("bs", &ptr);	/* move cursor with Backspace */
	SF = tgetstr("sf", &ptr);	/* Scroll Forward (up) */
	SR = tgetstr("sr", &ptr);	/* Scroll Reverse (down) */
	CS = tgetstr("cs", &ptr);	/* Change Scrolling region */
	AL = tgetstr("AL", &ptr);	/* Add blank Lines */
	DL = tgetstr("DL", &ptr);	/* Delete Lines */
	IC = tgetstr("ic", &ptr);	/* Insert Character */
	IM = tgetstr("im", &ptr);	/* enter Insert Mode */
	EI = tgetstr("ei", &ptr);	/* Exit Insert mode */
	DC = tgetstr("dc", &ptr);	/* Delete Character */
	SO = tgetstr("so", &ptr);	/* StandOut mode */
	SE = tgetstr("se", &ptr);	/* Standout mode End */
	US = tgetstr("us", &ptr);	/* UnderScore mode */
	UE = tgetstr("ue", &ptr);	/* Underscore mode End */
	MD = tgetstr("md", &ptr);	/* bold mode (?) */
	ME = tgetstr("me", &ptr);	/* bold mode End */
	BL = tgetstr("bl", &ptr);	/* BeLl */

	if (!AL) 	{ AL = tgetstr("al", &ptr); }
	if (!DL) 	{ DL = tgetstr("dl", &ptr); }
	if (!CR) 	{ CR = "\r"; }
	if (!NL) 	{ NL = "\n"; }
	if (!BL) 	{ BL = "\007"; }
	if (!SO || !SE) { SO = empty_string; SE = empty_string; }
	if (!US || !UE) { US = empty_string; UE = empty_string; }
	if (!MD || !ME) { MD = empty_string; ME = empty_string; }


	if (!CM || !CL || !CE || !ND || (!LE && !BS) || (!CS && !(AL && DL)))
	{
		fprintf(stderr, "\nYour terminal cannot run IRC II in full screen mode.\n");
		fprintf(stderr, "The following features are missing from your TERM setting.\n");

		if (!CM) fprintf(stderr, "\tCursor Movement\n");
		if (!CL) fprintf(stderr, "\tClear Screen\n");
		if (!CE) fprintf(stderr, "\tClear to end-of-line\n");
		if (!ND) fprintf(stderr, "\tCursor right\n");
		if (!LE && !BS)
			 fprintf(stderr, "\tCursor left\n");
		if (!CS && !(AL && DL))
			 fprintf(stderr, "\tScrolling\n");

		fprintf(stderr, "Try using VT100 emulation or better.\n");
		return -1;
	}

	if (!LE) LE = "\010";

	term_clear_to_eol = term_CE_clear_to_eol;
	term_cursor_right = term_ND_cursor_right;
	term_cursor_left  = (LE) ? term_LE_cursor_left
				 : term_BS_cursor_left;
	term_scroll 	  = (CS) ? term_CS_scroll
			         : term_param_ALDL_scroll;
	term_delete	  = (DC) ? term_DC_delete
				 : term_null_function;
	term_insert       = (IC) ? term_IC_insert
			         : (IM && EI) ? term_IMEI_insert
					      : (int (*) (char)) term_null_function;

#endif /* NOT IN WTERM_C */

	/* Set up the terminal discipline */
/*	if ((tty_des = open("/dev/tty", O_RDWR, 0)) == -1)*/
		tty_des = 0;

	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 */
#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)
		{
			LI = li;
			CO = co;
		}
		else
		{
			if ((LI = window.ws_row) == 0)
				LI = li;
			if ((CO = window.ws_col) == 0)
				CO = co;
		}
	}
#	else
	{
		LI = li;
		CO = co;
	}
#	endif

	CO--;
	if ((old_li != LI) || (old_co != CO))
	{
		old_li = LI;
		old_co = CO;
		return (1);
	}
	return (0);
}


static 	int	term_null_function _((void))
{
	return 1;
}

/* term_CE_clear_to_eol(): the clear to eol function, right? */
static	int term_CE_clear_to_eol _((void))
{
	tputs_x(CE);
	return (0);
}

/*
 * term_CS_scroll: should be used if the terminal has the CS capability by
 * setting term_scroll equal to it 
 */
static	int	term_CS_scroll (int line1, int line2, int n)
{
	int	i;
	char	*thing;

	if (n > 0)
		thing = SF ? SF : NL;
	else if (n < 0)
	{
		if (SR)
			thing = SR;
		else
			return 1;
	}
	else
		return 0;

	tputs_x(tgoto(CS, line2, line1));  /* shouldn't do this each time */
	if (n < 0)
	{
		term_move_cursor(0, line1);
		n = -n;
	}
	else
		term_move_cursor(0, line2);
	for (i = 0; i < n; i++)
		tputs_x(thing);
	tputs_x(tgoto(CS, LI - 1, 0));	/* shouldn't do this each time */
	return (0);
}

/*
 * term_param_ALDL_scroll: Uses the parameterized version of AL and DL 
 */
static	int	term_param_ALDL_scroll (int line1, int line2, int n)
{
	if (n > 0)
	{
		term_move_cursor(0, line1);
		tputs_x(tgoto(DL, n, n));
		term_move_cursor(0, line2 - n + 1);
		tputs_x(tgoto(AL, n, n));
	}
	else if (n < 0)
	{
		n = -n;
		term_move_cursor(0, line2-n+1);
		tputs_x(tgoto(DL, n, n));
		term_move_cursor(0, line1);
		tputs_x(tgoto(AL, n, n));
	}
	return (0);
}

/*
 * term_IC_insert: should be used for character inserts if the term has IC by
 * setting term_insert to it. 
 */
static	int	term_IC_insert (char c)
{
	tputs_x(IC);
	term_putchar(c);
	return (0);
}

/*
 * term_IMEI_insert: should be used for character inserts if the term has IM
 * and EI by setting term_insert to it 
 */
static	int	term_IMEI_insert (char c)
{
	tputs_x(IM);
	term_putchar(c);
	tputs_x(EI);
	return (0);
}

/*
 * term_DC_delete: should be used for character deletes if the term has DC by
 * setting term_delete to it 
 */
static	int term_DC_delete (void)
{
	tputs_x(DC);
	return (0);
}

/* term_ND_cursor_right: got it yet? */
static	int term_ND_cursor_right (void)
{
	tputs_x(ND);
	return (0);
}

/* term_LE_cursor_left:  shouldn't you move on to something else? */
static	int term_LE_cursor_left (void)
{
	tputs_x(LE);
	return (0);
}

static	int term_BS_cursor_left (void)
{
	char	c = '\010';

	fputc(c, (current_screen ? current_screen->fpout : stdout));
	return (0);
}

extern	void	copy_window_size (int *lines, int *columns)
{
	*lines = LI;
	*columns = CO;
}

extern	int term_eight_bit (void)
{
	return (((oldb.c_cflag) & CSIZE) == CS8) ? 1 : 0;
}

extern	void term_beep (void)
{
	if (get_int_var(BEEP_VAR))
	{
		tputs_x(BL);
		fflush(current_screen ? current_screen->fpout : stdout);
	}
}

extern	void	set_term_eight_bit (int value)
{
	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);
}
#endif /* NOT IN WTERM_C */

#else
/*
 * term.c -- for windows 95/NT
 *
 * Copyright 1997 Colten Edwards
 *
 */

#include "irc.h"
#include "vars.h"
#include "ircterm.h"
#include "window.h"
#include "screen.h"
#include "output.h"
#ifdef TRANSLATE
#include "translat.h"
#endif

#include <windows.h>

char eolbuf[200];
HANDLE ghstdout;
CONSOLE_SCREEN_BUFFER_INFO gscrbuf;
CONSOLE_CURSOR_INFO gcursbuf;

#ifndef WTERM_C
extern	char	*getenv();

static	int	term_CE_clear_to_eol 	(void);
static	int	term_CS_scroll 		(int, int, int);
static 	int	term_null_function 	(void);

/*
 * Function variables: each returns 1 if the function is not supported on the
 * current term type, otherwise they do their thing and return 0
 */
int	(*term_scroll) (int, int, int);	/* this is set to the best scroll available */
int	(*term_insert) (char);		/* this is set to the best insert available */
int	(*term_delete) (void);		/* this is set to the best delete available */
int	(*term_cursor_left) (void);	/* this is set to the best left available */
int	(*term_cursor_right) (void); 	/* this is set to the best right available */
int	(*term_clear_to_eol) (void); 	/* this is set... figure it out */

int nt_cursor_right(void);
int nt_cursor_left(void);

int	CO = 79,
	LI = 24;
DWORD gdwPlatform;

/*
 * term_reset_flag: set to true whenever the terminal is reset, thus letter
 * the calling program work out what to do
 */
	int	term_reset_flag = 0;
static	int	term_echo_flag = 1;
static	int	li;
static	int	co;
static	int	def_color = 0x07;
	int	CO, LI;


/*
 * 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);
}

void term_flush (void)
{
	return;
}

/*
 * term_reset: sets terminal attributed back to what they were before the
 * program started
 */
void term_reset (void)
{
	term_move_cursor(0, LI - 1);
	term_reset_flag = 1;
}

/*
 * 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
 */

RETSIGTYPE term_cont (int unused)
{
	refresh_screen(0, NULL);
}

/*
 * term_pause: sets terminal back to pre-program days, then SIGSTOPs itself.
 */
extern void term_pause (char unused, char *not_used)
{
char *shell;
DWORD dwmode;
HANDLE hstdin=GetStdHandle(STD_INPUT_HANDLE);
STARTUPINFO si = { 0 };
PROCESS_INFORMATION pi = { 0 };

	si.cb = sizeof(si);

	if (!(shell = get_string_var(SHELL_VAR)))
	{
		if (gdwPlatform == VER_PLATFORM_WIN32_WINDOWS)
			shell = "command.com";
		else
			shell = "cmd.exe";
	}
	if (!(GetConsoleMode(hstdin,&dwmode)))
		return;

	if (!SetConsoleMode(hstdin, dwmode | ((ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT|ENABLE_PROCESSED_INPUT) & ~ENABLE_WINDOW_INPUT)))
		return;

	if(!CreateProcess(NULL, shell, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi) )
		return;
	CloseHandle(pi.hThread);
	WaitForSingleObject(pi.hProcess,INFINITE);
	CloseHandle(pi.hProcess);

	if (!SetConsoleMode(hstdin, dwmode ) )
		return;
	refresh_screen(0,NULL);
}
#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 term_init (void)
{
#ifndef WTERM_C

	CONSOLE_SCREEN_BUFFER_INFO scrbuf;
	HANDLE hinput =GetStdHandle(STD_INPUT_HANDLE);
	DWORD dwmode;
	OSVERSIONINFO osver;

	ghstdout = GetStdHandle(STD_OUTPUT_HANDLE);

	if(!GetConsoleScreenBufferInfo(ghstdout, &gscrbuf) ) 
	{
		fprintf(stderr,"Error from getconsolebufinfo %d\n",GetLastError());
		abort();
	}

	GetConsoleMode(hinput,&dwmode);
	SetConsoleMode(hinput,dwmode & (~(ENABLE_LINE_INPUT |ENABLE_ECHO_INPUT
				| ENABLE_PROCESSED_INPUT)| ENABLE_WINDOW_INPUT/* | ENABLE_WRAP_AT_EOL_OUTPUT*/)
				);

	osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
	GetVersionEx(&osver);
	gdwPlatform = osver.dwPlatformId;

	GetConsoleCursorInfo(hinput, &gcursbuf);
	SetConsoleTitle("BitchX The Ultimate IRC client");

	GetConsoleScreenBufferInfo(ghstdout, &scrbuf);
	li = scrbuf.srWindow.Bottom - scrbuf.srWindow.Top + 1;
	co = scrbuf.srWindow.Right - scrbuf.srWindow.Left + 1;
	LI = li;
	CO = co - 1;
	memset(eolbuf,' ',sizeof(eolbuf)-2);
	eolbuf[sizeof(eolbuf)-1] = 0;
	def_color = gscrbuf.wAttributes;

	term_clear_to_eol = term_CE_clear_to_eol;
	term_cursor_right = nt_cursor_right;
	term_cursor_left  = nt_cursor_left;
	term_scroll 	  = term_CS_scroll/* : term_param_ALDL_scroll*/;
	term_delete	  = term_null_function;
	term_insert       = term_null_function;
#endif /* NOT IN WTERM_C */
	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;

	CONSOLE_SCREEN_BUFFER_INFO scrbuf;
	ghstdout = GetStdHandle(STD_OUTPUT_HANDLE);

	GetConsoleScreenBufferInfo(ghstdout, &scrbuf);
	li = scrbuf.srWindow.Bottom - scrbuf.srWindow.Top + 1;
	co = scrbuf.srWindow.Right - scrbuf.srWindow.Left + 1;
	LI = li;
	CO = co - 1;
	if ((old_li != LI) || (old_co != CO))
	{
		old_li = LI;
		old_co = CO;
		return (1);
	}
	return (0);
}


static 	int	term_null_function _((void))
{
	return 1;
}

/* term_CE_clear_to_eol(): the clear to eol function, right? */

static	int term_CE_clear_to_eol _((void))
{
	CONSOLE_SCREEN_BUFFER_INFO scrbuf;
	HANDLE hStdout = ghstdout;
	DWORD numwrote;
	int num=0;
	COORD savepos;

	if(!GetConsoleScreenBufferInfo(hStdout, &scrbuf) )
		return 0 ;

	num = scrbuf.srWindow.Right - scrbuf.dwCursorPosition.X;
	savepos = scrbuf.dwCursorPosition;

	if (!WriteConsole(hStdout,eolbuf,num,&numwrote,NULL))
		return 0;

	SetConsoleCursorPosition(hStdout, savepos);
	return (0);
}

/*
 * term_CS_scroll: should be used if the terminal has the CS capability by
 * setting term_scroll equal to it
 */
static	int	term_CS_scroll (int line1, int line2, int n)
{
	HANDLE hStdout = ghstdout;
	SMALL_RECT src;
	SMALL_RECT clip;
	COORD dest = {0};
	CHAR_INFO chr;

	src.Left = gscrbuf.srWindow.Left;
	src.Right = gscrbuf.srWindow.Right;
	chr.Char.AsciiChar = ' ';
	chr.Attributes = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
	if (n > 0)
	{
		src.Bottom = gscrbuf.srWindow.Top + line2;
		src.Top =  gscrbuf.srWindow.Top + line1 + n;
		dest.Y =  gscrbuf.srWindow.Top + line1;
		ScrollConsoleScreenBuffer(hStdout, &src, NULL, dest, &chr);
	}
	else
	{
		clip = src;
        	src.Bottom = gscrbuf.srWindow.Top + line2 + line1 + n;
		src.Top = gscrbuf.srWindow.Top + line1;
		dest.Y = gscrbuf.srWindow.Top + line1 - n;
		clip.Top = gscrbuf.srWindow.Top + line1;
		clip.Bottom = gscrbuf.srWindow.Top + line1 + line2;
		ScrollConsoleScreenBuffer(hStdout, &src, &clip, dest, &chr);
	}
	return 0;
}

extern	void	copy_window_size (int *lines, int *columns)
{
	*lines = LI;
	*columns = CO;
}

extern	int term_eight_bit (void)
{
	return 1;
}

extern	void term_beep (void)
{
	if (get_int_var(BEEP_VAR))
	{
       		Beep(0x637, 60/8);
	}
}

void ScrollBuf(HANDLE hOut, CONSOLE_SCREEN_BUFFER_INFO *scrbuf,int where)
{
	SMALL_RECT src;
	COORD dest;
	CHAR_INFO chr;

	src.Left = scrbuf->srWindow.Left;
	src.Top = scrbuf->srWindow.Top+where ;
	src.Right = scrbuf->srWindow.Right;
	src.Bottom = scrbuf->srWindow.Bottom;

	dest.X = 0;
	dest.Y = 0;

	chr.Char.AsciiChar = ' ';
	chr.Attributes = 0;
	ScrollConsoleScreenBuffer(hOut, &src, NULL, dest, &chr);

}

void NT_MoveToLineOrChar(void *fd, int where,int line)
{
	CONSOLE_SCREEN_BUFFER_INFO gscrbuf;
	GetConsoleScreenBufferInfo((HANDLE)fd, &gscrbuf);
	if (line)
	{
		if ( ((gscrbuf.dwCursorPosition.Y+where)> (gscrbuf.srWindow.Bottom-1))
			&&( where >0))
			ScrollBuf((HANDLE)fd,&gscrbuf,where);
		else
			gscrbuf.dwCursorPosition.Y += where;
	}
	else
		gscrbuf.dwCursorPosition.X += where;
	SetConsoleCursorPosition((HANDLE)fd, gscrbuf.dwCursorPosition);
}

void NT_MoveToLineOrCharAbs(void *fd, int where,int line)
{

	CONSOLE_SCREEN_BUFFER_INFO scrbuf;
        GetConsoleScreenBufferInfo((HANDLE)fd, &scrbuf);

	if (line)
		scrbuf.dwCursorPosition.Y = where+scrbuf.srWindow.Top;
	else
		scrbuf.dwCursorPosition.X = where;
	SetConsoleCursorPosition((HANDLE)fd, scrbuf.dwCursorPosition);
}

void term_move_cursor(int cursor, int line)
{
	HANDLE hStdout =ghstdout;
	COORD dest;
	dest.X = cursor;
	dest.Y = line + gscrbuf.srWindow.Top;
	SetConsoleCursorPosition(hStdout, dest);
}


void NT_ClearScreen(void)
{
	CONSOLE_SCREEN_BUFFER_INFO scrbuf;
	DWORD numwrote;
	COORD origin = {0, 0};
	HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

	GetConsoleScreenBufferInfo(hStdout, &scrbuf);
	FillConsoleOutputCharacter(hStdout,' ',scrbuf.dwSize.X*scrbuf.dwSize.Y,
		origin,&numwrote);
	FillConsoleOutputAttribute(hStdout,scrbuf.wAttributes,
		scrbuf.dwSize.X*scrbuf.dwSize.Y,scrbuf.dwCursorPosition,&numwrote);
	origin.X = scrbuf.srWindow.Left;
	origin.Y = scrbuf.srWindow.Top;
	SetConsoleCursorPosition(hStdout, origin);
	return;
}

int nt_cursor_right(void)
{
	NT_MoveToLineOrChar(GetStdHandle(STD_OUTPUT_HANDLE), 1,0);
	return 0;
}

int nt_cursor_left(void)
{
	NT_MoveToLineOrChar(GetStdHandle(STD_OUTPUT_HANDLE), -1,0);
	return 0;
}

extern	void	set_term_eight_bit (int value)
{
	return;
}

#define NPAR 16

static unsigned int color_table[] = {
        0x00,   0x04,   0x02,   0x06,    0x01,   0x05,   0x03,   0x07,
        0x00|8, 0x04|8, 0x02|8, 0x06|8,  0x01|8, 0x05|8, 0x03|8, 0x07|8
};

enum VC_STATES { ESnormal = 0, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey };
enum VC_STATES vc_state;
unsigned long par[NPAR+1] = {0};
unsigned long npar = 0;
int intensity = 1;
int blink = 0;
int underline = 0;
int reverse_ch = 0;


void reset_attrib(void *fd, int *fg, int *bg)
{
	intensity = 0;
	underline = 0;
	reverse_ch = 0;
	blink = 0;
	*fg=FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
	*bg=0;
}


void csi_m(void *fd)
{
int i;
static int fg = 7;
static int bg = 0;
	CONSOLE_SCREEN_BUFFER_INFO scrbuf;
	GetConsoleScreenBufferInfo((HANDLE)fd, &scrbuf);
	for (i=0; i <= npar; i++)
	{
        	switch(par[i])
        	{
        		case 0:
                        	/* all off */
				reset_attrib(fd, &fg, &bg);
                        	break;
        		case 1: /* bold */
        			intensity = FOREGROUND_INTENSITY;
        			break;
			case 2: /* all off */
				fg = 0;
				bg = 0;
				intensity = 0;
				break;
        		case 4: /* underline */
		               	fg=FOREGROUND_BLUE | FOREGROUND_RED;
		               	intensity=FOREGROUND_INTENSITY;
		               	bg=0;
                        	break;
        		case 5: /* blink */
				fg=FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
				bg=0;
				intensity=FOREGROUND_INTENSITY | BACKGROUND_INTENSITY;
        			break;
        		case 7: /* reverse */
				fg=0;
				bg=BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED;
				intensity=0;
				break;
        		case 49:
        			fg = (def_color & 0xf0) | bg;
        			break;
        		default:
				if (par[i] >=30 && par[i] <= 37)
					fg = color_table[par[i]-30];
				else if (par[i] >=40 && par[i] <= 47)
					bg = color_table[par[i]-40]<<4;
        			break;

        	}
	}
	/* update attribs now */
	SetConsoleTextAttribute(fd, fg | bg | intensity);
}

void set_term_intensity(int flag)
{
	par[0] = flag ? 1 : 0;
	npar = 0;
	csi_m(GetStdHandle(STD_OUTPUT_HANDLE));
}
void set_term_inverse(int flag)
{
	par[0] = flag ? 7 : 0;
	npar = 0;
	csi_m(GetStdHandle(STD_OUTPUT_HANDLE));
}
void set_term_underline(int flag)
{
	par[0] = flag ? 4 : 0;
	npar = 0;
	csi_m(GetStdHandle(STD_OUTPUT_HANDLE));
}

void csi_K(void *fd, int parm)
{
COORD save_curs = {0};
CONSOLE_SCREEN_BUFFER_INFO scrbuf;
DWORD bytes_rtn;
int num = 0;
	switch(parm)
	{
		case 2:
			NT_MoveToLineOrCharAbs(fd, 0, 0);
		case 0:
			term_CE_clear_to_eol();
			break;
		case 1:
			GetConsoleScreenBufferInfo((HANDLE)fd, &scrbuf);
			save_curs.Y = scrbuf.dwCursorPosition.Y;
			SetConsoleCursorPosition((HANDLE)fd, save_curs);
			num = scrbuf.dwCursorPosition.X - 1;
			if (num > 0)
	                        WriteConsole((HANDLE)fd, eolbuf, num, &bytes_rtn, NULL);
			SetConsoleCursorPosition((HANDLE)fd, scrbuf.dwCursorPosition);
			break;
	}
}

int nt_write(void * fd, unsigned char *buf, register int howmany)
{
	DWORD bytes_rtn;
	int num = 0;
	int len = -1;
	register int c;
	int ok;
	register unsigned char *buf1 = buf;
	vc_state = ESnormal;

	while (howmany)
	{
		howmany--;
                num++;
                ok = 0;
		c = *buf1;
		buf1++;
		len++;
		if (!ok && c)
		{
		        switch(c)
			{
		                case 0x1b:
		                case REV_TOG:
		                case UND_TOG:
		                case BOLD_TOG:
		                case ALL_OFF:
		                	ok = 0;
                                	break;
		                default:
					ok = 1;
                	}
		}
		if (vc_state == ESnormal && ok)
		{
                        WriteConsole((HANDLE)fd, buf+len,1, &bytes_rtn, NULL);
			continue;
		}

        	switch(c)
        	{
			case REV_TOG:
				if (reverse_ch) reverse_ch = 0; else reverse_ch = 1;
				set_term_inverse(reverse_ch);
				continue;
			case BOLD_TOG:
				if (intensity) intensity = 0; else intensity = 1;
				set_term_intensity(intensity);
				continue;
			case UND_TOG:
				if (underline) underline = 0; else underline = 1;
                        	set_term_underline(underline);
                        	continue;
			case ALL_OFF:
				set_term_underline(0);
				set_term_intensity(0);
				set_term_inverse(0); /* This turns it all off */
				intensity = 0;
				underline = 0;
				reverse_ch = 0;
				if (!buf1)	/* We're the end of line */
					term_bold_off();
				continue;
                	case 7:
                		Beep(0x637, 60/8);
                        	continue;
                	case 8:
                        	continue;
                	case 9:
                        	continue;
                	case 10: case 11: case 12:
				NT_MoveToLineOrChar(fd, 1, 1);
			case 13:
				NT_MoveToLineOrCharAbs(fd, 0, 0);
                        	continue;
                	case 24: case 26:
                		vc_state = ESnormal;
                        	continue;
			case 27:
				vc_state = ESesc;
                        	continue;
                	case 128+27:
                		vc_state = ESsquare;
                        	continue;
        	}
        	switch(vc_state)
        	{
        		case ESesc:
        			vc_state = ESnormal;
                        	switch(c)
                        	{
                        		case '[':
                        			vc_state = ESsquare;
                        			continue;
                        	}
                        	continue;
                	case ESsquare:
                		vc_state = ESgetpars;
				for(npar = 0 ; npar < NPAR ; npar++)
					par[npar] = 0;
				npar = 0;
				vc_state = ESgetpars;
				if (c == '[') { /* Function key */
					vc_state=ESfunckey;
					continue;
				}
			case ESgetpars:
				if (c==';' && npar<NPAR-1)
				{
					npar++;
					continue;
				} else if (c>='0' && c<='9')
				{
					par[npar] *= 10;
					par[npar] += c-'0';
					continue;
				} else
					vc_state=ESgotpars;
			case ESgotpars:
				vc_state = ESnormal;
				switch(c)
				{
					case 'C': case 'a':
						NT_MoveToLineOrChar(fd, par[0], 0);
						continue;
					case 'K':
						csi_K(fd, par[0]);
						continue;
                                	case 'm':
                                		csi_m(fd);
	                               		continue;
					case 'r':
					{
						int top, bottom;
						if (!par[0]) par[0]++;
						if (!par[1]) par[1] = li;
						if (par[0] < par[1] && par[1] < li)
						{
							top = par[0]-1;
							bottom = par[1];
                                                	term_CS_scroll(top, bottom, 1);
						}
						continue;
					}
				}
			        continue;
			default:
				vc_state = ESnormal;

        	}
	}
	return num;
}

#endif /* NOT IN WTERM_C */
#endif
