///////////////////////////////////////////////////////////////////////////////
//Telnet Win32 : an ANSI telnet client.
//Copyright (C) 1998-2000 Paul Brannan
//Copyright (C) 1998 I.Ioannou
//Copyright (C) 1997 Brad Johnson
//
//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either version 2
//of the License, or (at your option) any later version.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
//I.Ioannou
//roryt@hol.gr
//
///////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
//
// Module:		ansiprsr.cpp
//
// Contents:	ANSI parser base class
//
// Product:		telnet
//
// Revisions: August 30, 1998 Paul Brannan <pbranna@clemson.edu>
//            July 29, 1998 pbranna@clemson.edu
//            June 15, 1998 pbranna@clemson.edu
//            May 19, 1998  pbranna@clemson.edu
//            24 Dec, 1997  Andrey.V.Smilianets
//            05. Sep.1997  roryt@hol.gr (I.Ioannou)
//            11.May.1997   roryt@hol.gr (I.Ioannou)
//            6.April.1997  roryt@hol.gr (I.Ioannou)
//            5.April.1997  jbj@nounname.com
//            30.M„rz.1997	Titus_Boxberg@public.uni-hamburg.de
//		      14.Sept.1996  jbj@nounname.com
//            Version 2.0
//
//            13.Jul.1995	igor.milavec@uni-lj.si
//					  Original code
//
///////////////////////////////////////////////////////////////////////////////

//#include <windows.h>
#include <string.h>
#include "ansiprsr.h"

const int ANSIColors[] = {BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE};

// The constructor now takes different arguments and initializes different
// variables (Paul Brannan 6/15/98)
TANSIParser::TANSIParser(TConsole &RefConsole, KeyTranslator &RefKeyTrans,
						 TScroller &RefScroller, TNetwork &RefNetwork,
						 TCharmap &RefCharmap):
TParser(RefConsole, RefKeyTrans, RefScroller, RefNetwork, RefCharmap) {
	Init();
	iSavedAttributes = (unsigned char) 7;
	// must also check to make sure the string is non-NULL
	// (Paul Brannan 5/8/98)
	if ((ini.get_dumpfile() != NULL) && (*ini.get_dumpfile() != '\0')){
		dumpfile = fopen(ini.get_dumpfile(), "wb");
	}else {
		dumpfile = NULL;
	}
	InPrintMode = 0;
	printfile = NULL;

	fast_write = ini.get_fast_write(); // Paul Brannan 6/28/98
	Scroller.init(&StripBuffer);
}

TANSIParser::~TANSIParser(){
	if (dumpfile) fclose (dumpfile);
	// Added I.Ioannou 06 April, 1997
	if (printfile != NULL) fclose (printfile);
}

// Created Init() function to initialize the parser but not clear the screen
// (Paul Brannan 9/23/98)
void TANSIParser::Init() {
	// Paul Brannan 6/25/98
	map_G0 = 'B'; map_G1 = 'B';
	Charmap.setmap(map_G0);
	current_map = 'B';

	ignore_margins = 0;
	vt52_mode = 0;
	print_ctrl = 0;
	newline_mode = false;

	KeyTrans.clear_ext_mode();

	iSavedCurY = 0;							// Reset Variables
	iSavedCurX = 0;
	inGraphMode = 0;
	Console.SetScroll(-1, -1);
	Console.Normal();						// Reset Attributes

	// Set tabs stops
	resetTabStops();
}

void TANSIParser::ResetTerminal() {
	Init();
	Console.ClearScreen();					// Clear Screen
	Console.SetRawCursorPosition(0,0);		// Home Cursor
}
void TANSIParser::SaveCurY(int iY){
	iSavedCurY=iY;
}

void TANSIParser::SaveCurX(int iX){
	iSavedCurX=iX;
}

void TANSIParser::resetTabStops() {
	for(int j = 0; j < MAX_TAB_POSITIONS; j++) {
		tab_stops[j] = 8 + j - (j%8);
	}
}

void TANSIParser::ConSetAttribute(unsigned char TextAttrib){
	// Paul Brannan 5/8/98
	// Made this go a little bit faster by changing from switch{} to an array
	// for the colors
	if(TextAttrib >= 30) {
		if(TextAttrib <= 37) {
			Console.SetForeground(ANSIColors[TextAttrib-30]);
			return;
		} else if((TextAttrib >= 40) && (TextAttrib <= 47)) {
			Console.SetBackground(ANSIColors[TextAttrib-40]);
			return;
		}
	}
	
	switch (TextAttrib){
		// Text Attributes
	case 0: Console.Normal();           break;	// Normal video
	case 1: Console.HighVideo();        break;	// High video
	case 2: Console.LowVideo();         break;	// Low video
	case 4: Console.UnderlineOn();		break;	// Underline on (I.Ioannou)
	case 5: Console.BlinkOn();			break;	// Blink video
		// Corrected by I.Ioannou 11 May, 1997
	case 7: Console.ReverseOn();		break;	// Reverse video
	case 8:								break;	// hidden
		// All from 10 thru 27 are hacked from linux kernel
		// I.Ioannou 06 April, 1997
	case 10:
		//  I.Ioannou 04 Sep 1997 turn on/off high bit
		inGraphMode = 0;
		print_ctrl = 0;
		Charmap.setmap(current_map ? map_G1:map_G0); // Paul Brannan 6/25/98
		break; // ANSI X3.64-1979 (SCO-ish?)
		// Select primary font,
		// don't display control chars
		// if defined, don't set
		// bit 8 on output (normal)
	case 11:
		inGraphMode = 0;
		print_ctrl = 1;
		Charmap.setmap(0); // Paul Brannan 6/25/98
		break; // ANSI X3.64-1979 (SCO-ish?)
		// Select first alternate font,
		// let chars < 32 be displayed
		// as ROM chars
	case 12:
		inGraphMode = 1;
		print_ctrl = 1;
		Charmap.setmap(0); // Paul Brannan 6/25/98
		break; // ANSI X3.64-1979 (SCO-ish?)
		// Select second alternate font,
		// toggle high bit before
		// displaying as ROM char.
		
	case 21:									// not really Low video
	case 22: Console.LowVideo();		break;	// but this works good also
	case 24: Console.UnderlineOff();	break;	// Underline off
	case 25: Console.BlinkOff();		break;	// blink off
		// Corrected by I.Ioannou 11 May, 1997
	case 27: Console.ReverseOff();		break;	//Reverse video off

	// Mutt needs this (Paul Brannan, Peter Jordan 12/31/98)
	// This is from the Linux kernel source
    case 38: /* ANSI X3.64-1979 (SCO-ish?)
			  * Enables underscore, white foreground
			  * with white underscore (Linux - use
			  * default foreground).
			  */
			Console.UnderlineOn();
			Console.SetForeground(ini.get_normal_fg());
			break;
	case 39: /* ANSI X3.64-1979 (SCO-ish?)
			  * Disable underline option.
			  * Reset colour to default? It did this
			  * before...
			  */
			Console.UnderlineOff();
			Console.SetForeground(ini.get_normal_fg());
			break;
	case 49:
			Console.SetBackground(ini.get_normal_bg());
			break;

	}
}

void TANSIParser::ConSetCursorPos(int x, int y) {
	if(ignore_margins)
		Console.SetRawCursorPosition(x, y);
	else
		Console.SetCursorPosition(x, y);
}

const char* TANSIParser::GetTerminalID()
{
	return "\033[?1;2c";
}

// All of the Telnet protocol stuff has been moved to TTelHndl.cpp
// This is more consistent with what OO should be
// (Paul Brannan 6/15/98)

#ifdef __BORLANDC__
// argsused doesn't work on MSVC++
#pragma argsused
#endif

// Use this for the VT100 flags (Paul Brannan 12/2/98)
#define FLAG_DOLLAR		0x0001
#define FLAG_QMARK		0x0002
#define FLAG_GREATER	0x0004
#define FLAG_LESS		0x0008
#define FLAG_EXCLAM		0x0010
#define FLAG_AMPERSAND	0x0020
#define FLAG_SLASH		0x0040
#define FLAG_EQUAL		0x0080
#define FLAG_QUOTE		0x0100
#define FLAG_OTHER		0x8000

char* TANSIParser::ParseEscapeANSI(char* pszBuffer, char* pszBufferEnd)
{
	
	//	The buffer contains something like <ESC>[pA
	//	where p is an optional decimal number specifying the count by which the
	//	appropriate action should take place.
	//	The pointer pszBuffer points us to the p, <ESC> and [ are
	//	already 'consumed'
	
	//	TITUS: Simplification of the code: Assume default count of 1 in case
	//	there are no parameters.
	char tmpc;
	const int nParam = 10;	// Maximum number of parameters
	int	iParam[nParam] = {1, 0, 0, 0, 0};	// Assume 1 Parameter, Default 1
	int iCurrentParam = 0;
	DWORD flag = 0;
	int missing_param = 0;

	// Get parameters from escape sequence.
	while ((tmpc = *pszBuffer) <= '?') {

		if(tmpc < '0' || tmpc > '9') {
			// Check for parameter delimiter.
			if(tmpc == ';') {
				// This is a hack (Paul Brannan 6/27/98)
				if(*(pszBuffer - 1) == '[') missing_param = iCurrentParam+1;
				pszBuffer++;
				continue;
			}

			// It is legal to have control characters inside ANSI sequences
			// (Paul Brannan 6/26/98)
			if(tmpc < ' ') {
				Console.WriteCtrlChar(tmpc);
				pszBuffer++;
				continue;
			}

			// A new way of handling flags (Paul Brannan 12/2/98)
			switch(tmpc) {
			case '$': flag |= FLAG_DOLLAR; break;
			case '?': flag |= FLAG_QMARK; break;
			case '>': flag |= FLAG_GREATER; break;
			case '<': flag |= FLAG_LESS; break;
			case '!': flag |= FLAG_EXCLAM; break;
			case '&': flag |= FLAG_AMPERSAND; break;
			case '/': flag |= FLAG_SLASH; break;
			case '=': flag |= FLAG_EQUAL; break;
			case '\"': flag |= FLAG_QUOTE; break;
			default: flag |= FLAG_OTHER; break;
			}

			pszBuffer++;
		}

		//  Got Numerical Parameter.
		iParam[iCurrentParam] = strtoul(pszBuffer, &pszBuffer, 10);
		if (iCurrentParam < nParam)
			iCurrentParam++;
	}
	
	//~~~ TITUS: Apparently the digit is optional (look at termcap or terminfo)
	// So: If there is no digit, assume a count of 1
	
	switch ((unsigned char)*pszBuffer++) {
		// Insert Character
		case '@':
			if(iParam[0] == 0) iParam[0] = 1; // Paul Brannan 9/1/98
			Console.InsertCharacter(iParam[0]); break;
		// Move cursor up.
		case 'A':
			if(iParam[0] == 0) iParam[0] = 1;
			Console.MoveCursorPosition(0, -iParam[0]); break;
		// Move cursor down.
		// Added by I.Ioannou 06 April, 1997
		case 'B':
		case 'e':
			if(iParam[0] == 0) iParam[0] = 1;
			Console.MoveCursorPosition(0, iParam[0]);
			break;
		// Move cursor right.
		// Added by I.Ioannou 06 April, 1997
		case 'C':
		case 'a':
			// Handle cursor size sequences (Jose Cesar Otero Rodriquez and
			// Paul Brannan, 3/27/1999)
			if(flag & FLAG_EQUAL) {
				switch(iParam[0]) {
				case 7: Console.SetCursorSize(50); break;
				case 11: Console.SetCursorSize(6); break;
				case 32: Console.SetCursorSize(0); break;
				default: Console.SetCursorSize(13);
				}
			} else {
				if(iParam[0] == 0) iParam[0] = 1;
				Console.MoveCursorPosition(iParam[0], 0);
				break;
			}
		// Move cursor left.
		case 'D':
			if(iParam[0] == 0) iParam[0] = 1;
			Console.MoveCursorPosition(-iParam[0], 0);
			break;
		// Move cursor to beginning of line, p lines down.
		// Added by I.Ioannou 06 April, 1997
		case 'E': 
			Console.MoveCursorPosition(-Console.GetCursorX(), iParam[0]);
			break;
		// Moves active position to beginning of line, p lines up
		// Added by I.Ioannou 06 April, 1997
		// With '=' this changes the default fg color (Paul Brannan 6/27/98)
		case 'F':
			if(flag & FLAG_EQUAL)
				Console.setDefaultFg(iParam[0]);
			else
				Console.MoveCursorPosition(-Console.GetCursorX(), -iParam[0]);
			break;
		// Go to column p
		// Added by I.Ioannou 06 April, 1997
		// With '=' this changes the default bg color (Paul Brannan 6/27/98)
		case '`': 
		case 'G': // 'G' is from Linux kernel sources
			if(flag & FLAG_EQUAL) {
				Console.setDefaultBg(iParam[0]);
			} else {
				if (iCurrentParam < 1)			// Alter Default
					iParam[0] = 0;
				// this was backward, and we should subtract 1 from x
				// (Paul Brannan 5/27/98)
				ConSetCursorPos(iParam[0] - 1, Console.GetCursorY());
			}
			break;
		// Set cursor position.
		case 'f': 
		case 'H':
			if (iCurrentParam < 2 || iParam[1] < 1)
				iParam[1] = 1;
			ConSetCursorPos(iParam[1] - 1, iParam[0] - 1);
			break;
		// Clear screen
		case 'J': 
			if ( iCurrentParam < 1 ) iParam[0] = 0;	// Alter Default
			switch (iParam[0]) {
				case 0: Console.ClearEOScreen(); break;
				case 1: Console.ClearBOScreen(); break;
				case 2:
					Console.ClearScreen();
					Console.SetRawCursorPosition(0, 0);
					break;
			}
			break;
		// Clear line
		case 'K': 
			if (iCurrentParam < 1)			// Alter Default
				iParam[0] = 0;
			switch (iParam[0]) {
				case 0: Console.ClearEOLine(); break;
				case 1: Console.ClearBOLine(); break;
				case 2: Console.ClearLine(); break;
			}
			break;
		//  Insert p new, blank lines.
		// Added by I.Ioannou 06 April, 1997
		case 'L': 
			{
				// for (int i = 1; i <= iParam[0]; i++)
				// This should speed things up a bit (Paul Brannan 9/2/98)
				Console.ScrollDown(Console.GetRawCursorY(), -1, iParam[0]);
				break;
			}
		//  Delete p lines.
		// Added by I.Ioannou 06 April, 1997
		case 'M': 
			{
				for (int i = 1; i <= iParam[0]; i++)
				// This should speed things up a bit (Paul Brannan 9/2/98)
				Console.ScrollDown(Console.GetRawCursorY(), -1, -1);
				break;
			}
		// DELETE CHAR
		case 'P': 
			Console.DeleteCharacter(iParam[0]);
			break;
		// Scrolls screen up (down? -- PB) p lines,
		// Added by I.Ioannou 06 April, 1997
		// ANSI X3.64-1979 references this but I didn't
		// found it in any telnet implementation
		// note 05 Oct 97  : but SCO terminfo uses them, so uncomment them !!
		case 'S': 
			{
				//for (int i = 1; i <= iParam[0]; i++)
				// This should speed things up a bit (Paul Brannan 9/2/98)
				Console.ScrollDown(-1, -1, -iParam[0]);
				break;
			}
		// Scrolls screen up p lines,
		// Added by I.Ioannou 06 April, 1997
		// ANSI X3.64-1979 references this but I didn't
		// found it in any telnet implementation
		// note 05 Oct 97  : but SCO terminfo uses them, so uncomment them !!
		case 'T': 
			{
				// for (int i = 1; i <= iParam[0]; i++)
				// This should speed things up a bit (Paul Brannan 9/2/98)
				Console.ScrollDown(-1, -1, iParam[0]);
				break;
			}
		//  Erases p characters up to the end of line
		// Added by I.Ioannou 06 April, 1997
		case 'X': 
			{
				int iKeepX = Console.GetRawCursorX();
				int iKeepY = Console.GetRawCursorY();
				if (iParam[0] > Console.GetWidth())
					iParam[0] = Console.GetWidth(); // up to the end of line
				for ( int i = 1; i <= iParam[0]; i++ )
					Console.WriteString(" ", 1);
				Console.SetRawCursorPosition(iKeepX , iKeepY);
				break;
			}
		// Go back p tab stops
		// Added by I.Ioannou 06 April, 1997
		// Implemented by Paul Brannan, 4/13/2000
		case 'Z':
			{
				int x = Console.GetCursorX();
				for(int j = 0; x > 0 && j < iParam[0]; j++)
					while(x > 0 && tab_stops[j] == tab_stops[x]) x--;
				Console.SetCursorPosition(x, Console.GetCursorY());
			}
			break;
		// Get Terminal ID
		case 'c': 
			{
				const char* szTerminalId = GetTerminalID();
				Network.WriteString(szTerminalId, strlen(szTerminalId));
				break;
			}
		// TITUS++ 2. November 1998: Repeat Character.
		case 'b':
			// isprint may be causing problems (Paul Brannan 3/27/99)
			// if ( isprint(last_char) ) {
				char    buf[150];       // at most 1 line (max 132 chars)

				if ( iParam[0] > 149 ) iParam[0] = 149;
				memset(buf, last_char, iParam[0]);
				buf[iParam[0]] = 0;
				if ( fast_write )
					Console.WriteStringFast(buf, iParam[0]);
				else
					Console.WriteString(buf, iParam[0]);
			// } /* IF */
		break;
		// Go to line p
		// Added by I.Ioannou 06 April, 1997
		case 'd': 
			if (iCurrentParam < 1)			// Alter Default
				iParam[0] = 0;
			// this was backward, and we should subtract 1 from y
			// (Paul Brannan 5/27/98)
			ConSetCursorPos(Console.GetCursorX(), iParam[0] - 1);
			break;
		// iBCS2 tab erase
		// Added by I.Ioannou 06 April, 1997
		case 'g': 
			if (iCurrentParam < 1)			// Alter Default
				iParam[0] = 0;
			switch (iParam[0]) {
				case 0:
					{
						// Clear the horizontal tab stop at the current active position
						for(int j = 0; j < MAX_TAB_POSITIONS; j++) {
							int x = Console.GetCursorX();
							if(tab_stops[j] == x) tab_stops[j] = tab_stops[x + 1];
						}
					}
					break;
				case 2:
					// I think this might be "set as default?"
					break;
				case 3:
					{
						// Clear all tab stops
						for(int j = 0; j < MAX_TAB_POSITIONS; j++)
							tab_stops[j] = -1;
					}
					break;
			}
			break;
		// Set extended mode
		case 'h': 
			{
				for (int i = 0; i < iCurrentParam; i++) {
					// Changed to a switch statement (Paul Brannan 5/27/98)
					if(flag & FLAG_QMARK) {
						switch(iParam[i]) {
							case 1: // App cursor keys
								KeyTrans.set_ext_mode(APP_KEY);
								break;
							case 2: // VT102 mode
								vt52_mode = 0;
								KeyTrans.unset_ext_mode(APP2_KEY);
								break;
							case 3: // 132 columns
								if(ini.get_wide_enable()) {
									Console.SetWindowSize(132, -1);
								}
								break;
							case 4: // smooth scrolling
								break;
							case 5: // Light background
								Console.Lightbg();
								break;
							case 6: // Stay in margins
								ignore_margins = 0;
								break;
							case 7:
								Console.setLineWrap(true);
								break;
							case 8:	// Auto-repeat keys
								break;
							case 18: // Send FF to printer
								break;
							case 19: // Entire screen legal for printer
								break;
							case 25: // Visible cursor
								break;
							case 66: // Application numeric keypad
								break;
							default:
#ifdef DEBUG
								Console.Beep();
#endif
								break;
						}
					} else {
						switch(iParam[i]) {
							case 2: // Lock keyboard
								break;
							case 3: // Act upon control codes (PB 12/5/98)
								print_ctrl = 0;
								break;
							case 4: // Set insert mode
								Console.InsertMode(1);
								break;
							case 12: // Local echo off
								break;
							case 20: // Newline sends cr/lf
								KeyTrans.set_ext_mode(APP4_KEY);
								newline_mode = true;
								break;
							default:
#ifdef DEBUG
								Console.Beep();
#endif
								break;
						}
					}
				}
			}
			break;
		// Print Screen
		case 'i': 
			if (iCurrentParam < 1)
				iParam[0]=0;
			switch (iParam[0]){
				case 0: break; // Print Screen
				case 1: break; // Print Line
				// Added I.Ioannou 06 April, 1997
				case 4:
					// Stop Print Log
					InPrintMode = 0;
					if ( printfile != NULL )
						fclose(printfile);
					break;
				case 5:
					// Start Print Log
					printfile = fopen(ini.get_printer_name(), "ab");
					if (printfile != NULL) InPrintMode = 1;
					break;
			}
			break;
		// Unset extended mode
		case 'l': 
			{
				for (int i = 0; i < iCurrentParam; i++) {
					// Changed to a switch statement (Paul Brannan 5/27/98)
					if(flag & FLAG_QMARK) {
						switch(iParam[i]) {
							case 1: // Numeric cursor keys
								KeyTrans.unset_ext_mode(APP_KEY);
								break;
							case 2: // VT52 mode
								vt52_mode = 1;
								KeyTrans.set_ext_mode(APP2_KEY);
								break;
							case 3: // 80 columns
								if(ini.get_wide_enable()) {
									Console.SetWindowSize(80, -1);
								}
								break;
							case 4: // jump scrolling
								break;
							case 5: // Dark background
								Console.Darkbg();
								break;
							case 6: // Ignore margins
								ignore_margins = 1;
								break;
							case 7:
								Console.setLineWrap(false);
								break;
							case 8:	// Auto-repeat keys
								break;
							case 19: // Only send scrolling region to printer
								break;
							case 25: // Invisible cursor
								break;
							case 66: // Numeric keypad
								break;
							default:
#ifdef DEBUG
								Console.Beep();
#endif
								break;
						}
					} else {
						switch(iParam[i]) {
							case 2: // Unlock keyboard
								break;
							case 3: // Display control codes (PB 12/5/98)
								print_ctrl = 1;
								break;
							case 4: // Set overtype mode
								Console.InsertMode(0);
								break;
							case 12: // Local echo on
								break;
							case 20: // sends lf only
								KeyTrans.unset_ext_mode(APP4_KEY);
								newline_mode = false;
								break;
							default:
#ifdef DEBUG
								Console.Beep();
#endif
								break;
						}
					}
				}
			}
			break;
		// Set color
		case 'm':
			if(missing_param) Console.Normal();
			if(iCurrentParam == 0) {
				Console.Normal();
			} else {
				for(int i = 0; i < iCurrentParam; i++)
					ConSetAttribute(iParam[i]);
			}
			break;
		// report cursor position Row X Col
		case 'n': 
			if (iCurrentParam == 1 && iParam[0]==5) {
				// report the cursor position
				Network.WriteString("\x1B[0n", 6);
				break;
			}
			if (iCurrentParam == 1 && iParam[0]==6){
				// report the cursor position
				// The cursor position needs to be sent as a single string
				// (Paul Brannan 6/27/98)
				char szCursorReport[40] = "\x1B[";

				itoa(Console.GetCursorY() + 1,
					&szCursorReport[strlen(szCursorReport)], 10);
				strcat(szCursorReport, ";");
				itoa(Console.GetCursorX() + 1,
					&szCursorReport[strlen(szCursorReport)], 10);
				strcat(szCursorReport, "R");

				Network.WriteString(szCursorReport, strlen(szCursorReport));
		
			}
			break;
		// Miscellaneous weird sequences (Paul Brannan 6/27/98)
		case 'p':
			// Set conformance level
			if(flag & FLAG_QUOTE) {
				break;
			}
			// Soft terminal reset
			if(flag & FLAG_EXCLAM) {
				break;
			}
			// Report mode settings
			if(flag & FLAG_DOLLAR) {
				break;
			}
			break;
		// Scroll Screen
		case 'r': 
			if (iCurrentParam < 1) {
				// Enable scrolling for entire display
				Console.SetScroll(-1, -1);
				break;
			}
			if (iCurrentParam >1) {
				// Enable scrolling from row1 to row2
				Console.SetScroll(iParam[0] - 1, iParam[1] - 1);
				// If the cursor is outside the scrolling range, fix it
				// (Paul Brannan 6/26/98)
				// if(Console.GetRawCursorY() < iParam[0] - 1) {
				// 	Console.SetRawCursorPosition(Console.GetCursorX(),
				// 		iParam[0] - 1);
				// }
				// if(Console.GetRawCursorY() > iParam[1] - 1) {
				// 	Console.SetRawCursorPosition(Console.GetCursorX(),
				// 		iParam[1] - 1);
				// }
			}
			// Move the cursor to the home position (Paul Brannan 12/2/98)
			Console.SetCursorPosition(0, 0);
			break;
		// Save cursor position
		case 's': 
			SaveCurY(Console.GetRawCursorY());
			SaveCurX(Console.GetRawCursorX());
			break;
		// Restore cursor position
		case 'u': 
			Console.SetRawCursorPosition(iSavedCurX, iSavedCurY);
			break;
		// DEC terminal report (Paul Brannan 6/28/98)
		case 'x':
			if(iParam[0])
				Network.WriteString("\033[3;1;1;128;128;1;0x", 20);
			else
				Network.WriteString("\033[2;1;1;128;128;1;0x", 20);
			break;
		default:
#ifdef DEBUG
			Console.Beep();
#endif
			break;
	}

	return pszBuffer;
}

#ifdef MTE_SUPPORT
// Added by Frediano Ziglio, 5/31/2000
// MTE extension
// initially copied from ParseEscapeANSI
char* TANSIParser::ParseEscapeMTE(char* pszBuffer, char* pszBufferEnd)
{
	//      The buffer contains something like <ESC>~pA
	//      where p is an optional decimal number specifying the count by which the
	//      appropriate action should take place.
	//      The pointer pszBuffer points us to the p, <ESC> and ~ are
	//      already 'consumed'
	//      TITUS: Simplification of the code: Assume default count of 1 in case
	//      there are no parameters.
	char tmpc;
	const int nParam = 10;  // Maximum number of parameters
	int     iParam[nParam] = {1, 0, 0, 0, 0};       // Assume 1 parameter, Default 1
	int iCurrentParam = 0;
	char sRepeat[2];
	
	// Get parameters from escape sequence.
	while ((tmpc = *pszBuffer) <= '?') {
		if(tmpc < '0' || tmpc > '9') {
			// Check for parameter delimiter.
			if(tmpc == ';') {
				pszBuffer++;
				continue;
			}
			pszBuffer++;
		}
		
		//  Got Numerical Parameter.
		iParam[iCurrentParam] = strtoul(pszBuffer, &pszBuffer, 10);
		if (iCurrentParam < nParam)
			iCurrentParam++;
	}
	
	//~~~ TITUS: Apparently the digit is optional (look at termcap or terminfo)
	// So: If there is no digit, assume a count of 1
	
	switch ((unsigned char)*pszBuffer++) {
		case 'A':
			// set colors
			if (iCurrentParam < 2 )
				break;
			if (iParam[0] <= 15 && iParam[1] <= 15)
				Console.SetAttrib( (iParam[1] << 4) | iParam[0] );
			break;
			
		case 'R':
			// define region
			mteRegionXF = -1;
			if (iCurrentParam < 2 )
				break;
			mteRegionXF = iParam[1]-1;
			mteRegionYF = iParam[0]-1;
			break;
			
		case 'F':
			// fill with char
			{
				if (mteRegionXF == -1 || iCurrentParam < 1)
					break;
				sRepeat[0] = (char)iParam[0];
				sRepeat[1] = '\0';
				int xi = Console.GetCursorX(),yi = Console.GetCursorY();
				int xf = mteRegionXF;
				int yf = mteRegionYF;
				mteRegionXF = -1;
				for(int y=yi;y<=yf;++y)
				{
					Console.SetCursorPosition(xi,y);
					for(int x=xi;x<=xf;++x)
						
						Console.WriteStringFast(sRepeat,1);
				}
			}
			break;
			
		case 'S':
			// Scroll region
			{
				if (mteRegionXF == -1 || iCurrentParam < 2)
					break;
				int /*x = Console.GetCursorX(),*/y = Console.GetCursorY();
				// int xf = mteRegionXF;
				int yf = mteRegionYF;
				mteRegionXF = -1;
				// !!! don't use x during scroll
				int diff = (iParam[0]-1)-y;
				if (diff<0)
					Console.ScrollDown(y-1,yf,diff);
				else
					Console.ScrollDown(y,yf+1,diff);
			}
			break;
			// Meridian main version ??
		case 'x':
			// disable echo and line mode
			Network.set_local_echo(0);
			Network.set_line_mode(0);
			// Meridian Server handle cursor itself
			Console.SetCursorSize(0);
			break;
			// query ??
		case 'Q':
			if (iParam[0] == 1)
				Network.WriteString("\033vga.",5);
			break;
		default:
#ifdef DEBUG
			Console.Beep();
#endif
			break;
	}
	
	return pszBuffer;
 }
#endif

char* TANSIParser::ParseEscape(char* pszBuffer, char* pszBufferEnd) {
	char *pszChar;

	// Check if we have enough characters in buffer.
	if ((pszBufferEnd - pszBuffer) < 2)
		return pszBuffer;
	
	//  I.Ioannou 04 Sep 1997
	// there is no need for pszBuffer++; after each command
	
	// Decode the command.
	pszBuffer++;
	
	switch (*pszBuffer++) {
		case 'A': // Cursor up
			Console.MoveCursorPosition(0, -1);
			break;
		// Cursor down
		case 'B': 
  			Console.MoveCursorPosition(0, 1);
			break;
		// Cursor right
		case 'C':
  			Console.MoveCursorPosition(1, 0);
			break;
		// LF *or* cursor left (Paul Brannan 6/27/98)
		case 'D':
			if(vt52_mode)
				Console.MoveCursorPosition(-1, 0);
			else
				Console.index();
			break;
		// CR/LF (Paul Brannan 6/26/98)
		case 'E':
			Console.WriteCtrlString("\r\n", 2);
			break;
		// Special graphics char set (Paul Brannan 6/27/98)
		case 'F':
			Charmap.setmap('0');
			break;
		// ASCII char set (Paul Brannan 6/27/98)
		case 'G':
			Charmap.setmap('B');
			break;
		// Home cursor/tab set
		case 'H': 
			if(ini.get_vt100_mode()) {
				int x = Console.GetCursorX();
				if(x != 0) {
					int t = tab_stops[x - 1];
					for(int j = x - 1; j >= 0 && tab_stops[j] == t; j--)
						tab_stops[j] = x;
				}
			} else {
				//  I.Ioannou 04 Sep 1997 (0,0) not (1,1)
				ConSetCursorPos(0, 0);
			}
			break;
		// Reverse line feed (Paul Brannan 6/27/98)
		// FIX ME!!!  reverse_index is wrong to be calling here
		// (Paul Brannan 12/2/98)
		case 'I':
			Console.reverse_index();
			break;
		// Erase end of screen
		case 'J': 
			Console.ClearEOScreen();
			break;
		// Erase EOL
		case 'K':
			Console.ClearEOLine();
			break;
		// Scroll Up one line //Reverse index
		case 'M':
			Console.reverse_index();
			break;
		// Direct cursor addressing
		case 'Y':
			if ((pszBufferEnd - pszBuffer) >= 2){
				// if we subtract '\x1F', then we may end up with a negative
				// cursor position! (Paul Brannan 6/26/98)
				ConSetCursorPos(pszBuffer[1] - ' ',	pszBuffer[0] - ' ');
				pszBuffer+=2;
			} else {
				pszBuffer--; // Paul Brannan 6/26/98
			}
			break;
		// Terminal ID Request
		case 'Z':
			{
				const char* szTerminalId = GetTerminalID();
				Network.WriteString(szTerminalId, strlen(szTerminalId));
				break;
			}
		// reset terminal to defaults
		case 'c':
			ResetTerminal();
			break;
		// Enter alternate keypad mode
		case '=':
			KeyTrans.set_ext_mode(APP3_KEY);
			break;
		// Exit alternate keypad mode
		case '>':
			KeyTrans.unset_ext_mode(APP3_KEY);
			break;
		// Enter ANSI mode
		case '<':
			KeyTrans.unset_ext_mode(APP2_KEY); // exit vt52 mode
			break;
		// Graphics processor on (See note 3)
		case '1':
			break;
		// Line size commands
		case '#':        //Line size commands
			// (Paul Brannan 6/26/98)
			if(pszBuffer < pszBufferEnd) {
				switch(*pszBuffer++) {
				case '3': break; // top half of a double-height line
				case '4': break; // bottom half of a double-height line
				case '6': break; // current line becomes double-width
				case '8': Console.ClearScreen('E'); break;
				}
			} else {
				pszBuffer--;
			}			
			break;
		// Graphics processor off (See note 3)
		case '2':
			break;
		// Save cursor and attribs
		case '7':
			SaveCurY(Console.GetRawCursorY());
			SaveCurX(Console.GetRawCursorX());
			iSavedAttributes = Console.GetAttrib();
			break;
			// Restore cursor position and attribs
		case '8':
			Console.SetRawCursorPosition(iSavedCurX, iSavedCurY);
			Console.SetAttrib(iSavedAttributes);
			break;
		// Set G0 map (Paul Brannan 6/25/98)
		case '(':
			if (pszBuffer < pszBufferEnd) {
				map_G0 = *pszBuffer;
				if(current_map == 0) Charmap.setmap(map_G0);
				pszBuffer++;
			} else {
				pszBuffer--;
			}
			break;
		// Set G1 map (Paul Brannan 6/25/98)
		case ')':
			if (pszBuffer < pszBufferEnd) {
				map_G1 = *pszBuffer;
				if(current_map == 1) Charmap.setmap(map_G1);
				pszBuffer++;
			} else {
				pszBuffer--;
			}
			break;
		// This doesn't do anything, as far as I can tell, but it does take
		// a parameter (Paul Brannan 6/27/98)
		case '%':
			if (pszBuffer < pszBufferEnd) {
				pszBuffer++;
			} else {
				pszBuffer--;
			}
			break;
		// ANSI escape sequence
		case '[':
			// Check if we have whole escape sequence in buffer.
			// This should not be isalpha anymore (Paul Brannan 9/1/98)
			pszChar = pszBuffer;
			while ((pszChar < pszBufferEnd) && (*pszChar <= '?'))
				pszChar++;
			if (pszChar == pszBufferEnd)
				pszBuffer -= 2;
			else
				pszBuffer = ParseEscapeANSI(pszBuffer, pszBufferEnd);
			break;
#ifdef MTE_SUPPORT
		case '~':
			// Frediano Ziglio, 5/31/2000
			// Meridian Terminal Emulator extension
			// !!! same as ANSI
			// !!! should put in MTE procedure
			pszChar = pszBuffer;
			while ((pszChar < pszBufferEnd) && (*pszChar <= '?'))
				pszChar++;
			if (pszChar == pszBufferEnd)
				pszBuffer -= 2;
			else
				pszBuffer = ParseEscapeMTE(pszBuffer, pszBufferEnd);
			break;
#endif
		default:
#ifdef DEBUG
			Console.Beep();
#endif
			break;
	}

	return pszBuffer;
}

// This function now only parses the ANSI buffer and does not do anything
// with IAC sequences.  That code has been moved to TTelHndl.cpp.
// The scroller update routines have been moved to TScroll.cpp.
// (Paul Brannan 6/15/98)
char* TANSIParser::ParseBuffer(char* pszHead, char* pszTail){
	// copy into ANSI buffer
	char * pszResult;
	
	// Parse the buffer for ANSI or display
	while (pszHead < pszTail) {
		if(!ini.get_output_redir()) {
			pszResult = ParseANSIBuffer(pszHead, pszTail);
		} else {
			// Output is being redirected
			if(ini.get_strip_redir()) {
				// Skip the WriteFile() altogether and pass the buffer to a filter
				// Mark Miesfield 09/24/2000
				pszResult = PrintGoodChars(pszHead, pszTail);
			} else {
				DWORD Result;
				// Paul Brannan 7/29/98
				// Note that this has the unforunate effect of printing out
				// NULL (ascii 0) characters onto the screen
				if (!WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),	pszHead,
					pszTail - pszHead, &Result,	NULL)) pszResult = pszHead;
				pszResult = pszHead + Result;
			}
		}
		if (dumpfile)
			fwrite( pszHead, sizeof (char), pszResult-pszHead, dumpfile);
		if(ini.get_scroll_enable()) Scroller.update(pszHead, pszResult);
		if (pszResult == pszHead) break;
		pszHead = pszResult;
	}
	// return the new head to the buffer
	return pszHead;
}

// A simple routine to strip ANSI sequences
// This isn't perfect, but it does an okay job (Paul Brannan 7/5/98)
// Fixed a line counting bug (Paul Brannan 12/4/98)
int TANSIParser::StripBuffer(char* pszHead, char* pszTail, int width) {
	int lines = 0, c = 0;
	char *pszBuf = pszHead;

	while(pszHead < pszTail) {
		if(iscntrl(*pszHead)) {
			switch(*(pszHead++)) {
			case 8:
			case 127:
				if(c>0) {
					if(!(c%width)) lines--;
					c--;
					pszBuf--;
				}
				break;
			case 10: lines++;
			case 13:
				*(pszBuf++) = *(pszHead - 1);
				c = 0;
				break;
			case 27:
				switch(*(pszHead++)) {
				case 'Y': pszHead += 2; break;
				case '#':
				case '(':
				case ')':
				case '%': pszHead++; break;
				case '[':
					while((pszHead < pszTail) && (*pszHead < '?'))
						pszHead++;
					pszHead++;
					break;
				}
			}
		} else {
			*(pszBuf++) = *(pszHead++);
			c++;
		}
		if(c != 0 && !(c%width))
			lines++;
	}
	
	// Fill in the end of the buffer with blanks
	while(pszBuf <= pszTail) *pszBuf++ = ' ';

	return lines;
}

char* TANSIParser::ParseANSIBuffer(char* pszBuffer, char* pszBufferEnd)
{
	if(InPrintMode) {
		return PrintBuffer(pszBuffer, pszBufferEnd);
	}
	
	unsigned char tmpc = *(unsigned char *)pszBuffer;

	if(tmpc == 27) {
		return ParseEscape(pszBuffer, pszBufferEnd);
	}
	
//	if((fast_write && tmpc < 32) ||
//		!print_ctrl && (tmpc < 32 || (EightBit_Ansi &&
//		(tmpc > 128 && tmpc < 128 + ' ')))) {

	// We shouldn't print ctrl characters when fast write is enabled
	// and ctrl chars are disabled (Paul Brannan 9/1/98)
	if(tmpc < 32) {
		// From the Linux kernel (Paul Brannan 12/5/98):
		/* A bitmap for codes <32. A bit of 1 indicates that the code
		 * corresponding to that bit number invokes some special action
		 * (such as cursor movement) and should not be displayed as a
		 * glyph unless the disp_ctrl mode is explicitly enabled.
		 */
		const long CTRL_ACTION = 0x0d00ff81;
		const long CTRL_ALWAYS = 0x0800f501;
		if(!(((print_ctrl?CTRL_ALWAYS:CTRL_ACTION)>>tmpc)&1)) {
			
			Console.WriteString((char *)&tmpc, 1);
			pszBuffer++;
			return pszBuffer;
		}

		switch (tmpc) {
		case 0:
			pszBuffer++;
			break;
		
		// I.Ioannou 5/30/98
		case 7:
			Console.Beep();
			pszBuffer++;
			break;
		
		// destructive backspace
		case 8:
			// Added option for destructive backspace (Paul Brannan 5/13/98)
			// Changed to ConWriteCtrlString so that the cursor position can be
			// updated (Paul Brannan 5/25/98)
			if(ini.get_dstrbksp()) {
				Console.WriteCtrlChar('\b');
				Console.WriteString(" ", 1);
				Console.WriteCtrlChar('\b');
			}
			else Console.WriteCtrlChar('\b');
			pszBuffer++;
			break;
		
		// horizontal tab
		case 9:
			{
				pszBuffer++;
				int x = Console.GetCursorX();
				if(x != -1)
					Console.SetCursorPosition(tab_stops[x], Console.GetCursorY());
			}
			break;
		
		// Line Feed Char
		case 10:
			// Test for local echo (Paul Brannan 8/25/98)
			if(Network.get_local_echo() || newline_mode) // &&
				Console.WriteCtrlChar('\x0d');
			Console.WriteCtrlChar('\x0a');
			pszBuffer++;
			break;
		
		// form feed
		case 12:
			pszBuffer++;
			Console.ClearScreen();
			Console.SetRawCursorPosition(Console.GetCursorX(), 1); // changed fm 1
			break;
		
		case 13:
			Console.WriteCtrlChar('\x0d');
			pszBuffer++;

			break;

		case 14:  // shift out of alternate chararcter set
			pszBuffer++;
			Charmap.setmap(map_G1); // Paul Brannan 6/25/98
			current_map = 1;
			break;
	
		case 15:  // shift in
			pszBuffer++;
			Charmap.setmap(map_G0); // Paul Brannan 6/25/98
			current_map = 0;
			break;
		
		// Paul Brannan 9/1/98 - Is this okay?
		default:
			pszBuffer++;
		}

		return pszBuffer;
	}

	//  added by I.Ioannou 06 April, 1997
	//  In 8 bit systems the server may send 0x9b instead of ESC[
	//  Well, this will produce troubles in Greek 737 Code page
	//  which uses 0x9b as the small "delta" - and I thing that there
	//  is another European country with the same problem.
	//  If we have to stay 8-bit clean we may have to
	//  give the ability of ROM characters (ESC[11m),
	//  for striped 8'th bit (ESC[12m) as SCO does,
	//  or a parameter at compile (or run ?) time.
	// We now check for a flag in the ini file (Paul Brannan 5/13/98)
	// We also handle any 8-bit ESC sequence (Paul Brannan 6/28/98)
	if(ini.get_eightbit_ansi() && (tmpc > 128 && tmpc < 128 + ' ')) {
		// There's a chance the sequence might not parse.  If this happens
		// then pszBuffer will be one character too far back, since
		// ParseEscape is expecting two characters, not one.
		// In that case we must handle it.
		char *pszCurrent = pszBuffer;
		pszBuffer = ParseEscape(pszBuffer, pszBufferEnd);
		if(pszBuffer < pszCurrent) pszBuffer = pszCurrent;
	}

	char* pszCurrent = pszBuffer + 1;
	// I.Ioannou 04 Sep 1997 FIXME with ESC[11m must show chars < 32
	// Fixed (Paul Brannan 6/28/98)
	while ((pszCurrent < pszBufferEnd) && (!iscntrl(*pszCurrent))) {
		// I.Ioannou 04 Sep 1997 strip on high bit
		if ( (inGraphMode) && (*pszCurrent > (char)32) )
			*pszCurrent |= 0x80 ;
		pszCurrent++;
	}
	
	// Note that this may break dumpfiles slightly.
	// If 'B' is set to anything other than ASCII, this will cause problems
	// (Paul Brannan 6/28/98)
	if(current_map != 'B' && Charmap.enabled)
		Charmap.translate_buffer(pszBuffer, pszCurrent);    
	
	last_char = *(pszCurrent-1);    // TITUS++: Remember last char

	if(fast_write) {
		pszBuffer += Console.WriteStringFast(pszBuffer,
			pszCurrent - pszBuffer);
	} else {
		pszBuffer += Console.WriteString(pszBuffer,
			pszCurrent - pszBuffer);
	}

	return pszBuffer;
}

// Added by I.Ioannou 06 April, 1997
// Print the buffer until you reach ESC[4i
char* TANSIParser::PrintBuffer(char* pszBuffer, char* pszBufferEnd) {
	// Check if we have enough characters in buffer.
	if ((pszBufferEnd - pszBuffer) < 4)
		return pszBuffer;
	char *tmpChar;
	
	tmpChar = pszBuffer;
	if ( *tmpChar == 27 ) {
		tmpChar++;
		if ( *tmpChar == '[' ) {
			tmpChar++;
			if ( *tmpChar == '4' ) {
				tmpChar++;
				if ( *tmpChar == 'i' ) {
					InPrintMode = 0; // Stop Print Log
					if ( printfile != NULL )
						fclose(printfile);
					pszBuffer += 4;
					return pszBuffer;
				}
			}
		}
	}
	
	if (printfile != NULL) {
		fputc( *pszBuffer, printfile);
		pszBuffer++;
	} else
		InPrintMode = 0;
	
	return pszBuffer;
}

/* - PrintGoodChars( pszHead, pszTail ) - - - - - - - - - - - - - - - - - - -
-*

  Mark Miesfield 09/24/2000

  Prints the characters in a buffer, from the specified head to the specified
  tail, to standard out, skipping any control characters or ANSI escape
  sequences.

  Parameters on entry:
    pszHead  ->  Starting point in buffer.

    pszTail  ->  Ending point in buffer.

  Returns:
    Pointer to the first character in the buffer that was not output to
    standard out.  (Since no error checking is done, this is in effect
    pszTail.)

  Side Effects:
    None.
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
char * TANSIParser::PrintGoodChars( char * pszHead, char * pszTail )  {

  while ( pszHead < pszTail )  {
    if ( iscntrl( *pszHead ) )  {
      switch ( *(pszHead++) )  {
        case 10 :
          putc( 10, stdout );
          break;

        case 13 :
          putc( 13, stdout );
          break;

        case 27:
          switch ( *(pszHead++) )  {
            case 'Y':
              pszHead += 2;
              break;

            case '#':
            case '(':
            case ')':
            case '%': pszHead++; break;
            case '[':
              while ( (pszHead < pszTail) && (*pszHead < '?') )
                pszHead++;
              pszHead++;
              break;

            default :
              break;
          }
          break;

        default :
          break;
      }
    }
    else
      putc( *(pszHead++), stdout );
  }
  return ( pszTail );
}
// End of function:  PrintGoodChars( pszHead, pszTail )
