/*
**	$Id: Console.c,v 1.5 92/05/01 12:52:03 olsen Sta Locker: olsen $
**	$Revision: 1.5 $
**	$Date: 92/05/01 12:52:03 $
**
**	High-level console control routines
**
**	Copyright © 1990-1992 by Olaf `Olsen' Barthel & MXM
**		All Rights Reserved
*/

#include "termGlobal.h"

	/* Use a simple address trick instead of the predefined
	 * address in amiga.lib.
	 */

#ifndef custom
#define custom (*(struct Custom *)0xDFF000)
#endif	/* custom */

	/* The characters to abort parsing a control sequence. */

STATIC BYTE AbortTable[256] = 
{
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,1,0,1,1,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
};

	/* StripSequence():
	 *
	 *	Strips a string from ESC and CSI introduced control
	 *	sequences.
	 */

STATIC LONG __regargs
StripSequence(UBYTE *Src,UBYTE *Dst,LONG Length)
{
	STATIC BYTE	HasESC = FALSE,HasCSI = FALSE;
	LONG		Size = 0;

	while(Length--)
	{
		if(*Src == 24 || *Src == 26)
		{
			HasESC = HasCSI = FALSE;

			Src++;

			continue;
		}

		if(*Src == '\r')
		{
			Src++;

			continue;
		}

		if(HasESC)
		{
			if(*Src == '[')
			{
				HasESC = FALSE;
				HasCSI = TRUE;
			}
			else
			{
				if(*Src >= '0')
					HasESC = FALSE;
			}

			Src++;

			continue;
		}

		if(HasCSI)
		{
			if(*Src >= '@')
				HasCSI = FALSE;

			Src++;

			continue;
		}

		switch(*Src)
		{
			case ESC:	HasESC = TRUE;
					Src++;
					continue;

			case CSI:	HasCSI = TRUE;
					Src++;
					continue;

			default:	if(!ValidTab[*Src])
					{
						Src++;

						continue;
					}

					break;
		}

		*Dst++ = *Src++;

		Size++;
	}

	return(Size);
}

	/* CaptureToFile(APTR Buffer,LONG Size):
	 *
	 *	Send data to the capture file.
	 */

VOID __regargs
CaptureToFile(APTR Buffer,LONG Size)
{
	if(FileCapture && Size)
	{
		struct MenuItem	*SomeItem;

		if(BufferWrite(FileCapture,Buffer,Size) != Size)
		{
			BlockWindows();

				/* We had an error writing to the file. */

			switch(MyEasyRequest(NULL,LocaleString(MSG_CONSOLE_ERROR_WRITING_TO_CAPTURE_FILE_TXT),LocaleString(MSG_CONSOLE_IGNORE_DISCARD_CLOSE_TXT),CaptureName))
			{
				case 0:	break;

				case 1:	BufferClose(FileCapture);

					DeleteFile(CaptureName);

					if(SomeItem = FindThisItem(MEN_CAPTUREDISK))
						SomeItem -> Flags &= ~CHECKED;

					FileCapture = NULL;

					break;

				case 2:	BufferClose(FileCapture);

					if(SomeItem = FindThisItem(MEN_CAPTUREDISK))
						SomeItem -> Flags &= ~CHECKED;

					FileCapture = NULL;

					if(!GetFileSize(CaptureName))
						DeleteFile(CaptureName);
					else
						SetProtection(CaptureName,FIBF_EXECUTE);

					break;
			}

			ReleaseWindows();
		}
	}
}

	/* Capture(APTR Buffer,LONG Size):
	 *
	 *	Send the buffer contents to the display/disk capture.
	 */

VOID __regargs
Capture(APTR Buffer,LONG Size)
{
	struct MenuItem *SomeItem;

		/* Send the filtered data to the capture file. */

	if(Config . CaptureFilter)
		CaptureToFile(Buffer,Size);

		/* Store data in the log book. */

	if(!BufferFrozen)
		StoreBuffer(Buffer,Size);

		/* Send the buffer to the printer. */

	if(PrinterCapture && Size)
	{
		if(!FWrite(PrinterCapture,Buffer,Size,1))
		{
			BlockWindows();

			if(!MyEasyRequest(Window,LocaleString(MSG_CONSOLE_ERROR_WRITING_TO_PRINTER_TXT),LocaleString(MSG_CONSOLE_IGNORE_CLOSE_PRINTER_TXT)))
			{
				Close(PrinterCapture);

				if(SomeItem = FindThisItem(MEN_CAPTUREPRINTER))
					SomeItem -> Flags &= ~CHECKED;

				PrinterCapture = NULL;
			}

			ReleaseWindows();
		}
	}
}

	/* ClosePrinterCapture(BYTE Force):
	 *
	 *	Closes printer capture file.
	 */

VOID
ClosePrinterCapture(BYTE Force)
{
	struct MenuItem *Item = FindThisItem(MEN_CAPTUREPRINTER);

	if(PrinterCapture)
	{
		if(ControllerActive && StandardPrinterCapture && !Force)
			FPrintf(PrinterCapture,LocaleString(MSG_CONSOLE_TERMINAL_TRANSCRIPT_ENDING_TXT));

		if(Force)
		{
			Close(PrinterCapture);

			Item -> Flags &= ~CHECKED;

			PrinterCapture = NULL;

			StandardPrinterCapture = FALSE;
		}
	}

	ControllerActive = FALSE;
}

	/* OpenPrinterCapture(BYTE Controller):
	 *
	 *	Opens printer capture file.
	 */

BYTE
OpenPrinterCapture(BYTE Controller)
{
	if(PrinterCapture)
	{
		if(Controller && !ControllerActive)
		{
			ControllerActive = TRUE;

			FPrintf(PrinterCapture,LocaleString(MSG_CONSOLE_TERMINAL_TRANSCRIPT_FOLLOWS_TXT));
		}

		return(TRUE);
	}
	else
	{
		struct MenuItem *Item = FindThisItem(MEN_CAPTUREPRINTER);

		if(PrinterCapture = Open("PRT:",MODE_NEWFILE))
			Item -> Flags |= CHECKED;
		else
		{
			Item -> Flags &= ~CHECKED;

			BlockWindows();

			MyEasyRequest(Window,LocaleString(MSG_TERMMAIN_ERROR_OPENING_PRINTER_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT));

			ReleaseWindows();
		}

		if(Controller)
		{
			ControllerActive	= TRUE;
			StandardPrinterCapture	= FALSE;
		}
		else
		{
			StandardPrinterCapture = FALSE;

			if(ControllerActive)
				FPrintf(PrinterCapture,LocaleString(MSG_CONSOLE_USER_TERMINAL_TRANSCRIPT_FOLLOWS_TXT));
		}

		if(PrinterCapture)
			return(TRUE);
		else
			return(FALSE);
	}
}

	/* PrintRegion(WORD Top,WORD Bottom):
	 *
	 *	Print the contents of a screen region.
	 */

VOID
PrintRegion(WORD Top,WORD Bottom)
{
	BPTR	 SomeFile;
	WORD	 i,j;
	UBYTE	*Buffer;

	if(PrinterCapture)
	{
		if(!FPrintf(PrinterCapture,LocaleString(MSG_CONSOLE_SCREEN_PRINTOUT_FOLLOWS_TXT)))
		{
			MyEasyRequest(Window,LocaleString(MSG_CONSOLE_ERROR_WRITING_TO_PRINTER_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT));

			return;
		}

		SomeFile = PrinterCapture;
	}
	else
	{
		if(!(SomeFile = Open("PRT:",MODE_NEWFILE)))
		{
			MyEasyRequest(Window,LocaleString(MSG_TERMMAIN_FAILED_TO_OPEN_PRINTER_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT));

			return;
		}
	}

	for(i = Top ; i < Bottom ; i++)
	{
		Buffer = &Raster[i * RasterWidth];

		j = LastColumn;

		while(j >= 0 && Buffer[j] == ' ')
			j--;

		if(j >= 0)
		{
			if(!FWrite(SomeFile,Buffer,j + 1,1))
				break;
		}

		if(!FWrite(SomeFile,"\n",1,1))
			break;
	}

	if(PrinterCapture)
		FPrintf(PrinterCapture,LocaleString(MSG_CONSOLE_SCREEN_PRINTOUT_ENDING_TXT));
	else
		Close(SomeFile);
}

	/* HandleCursor(UBYTE Char):
	 *
	 *	This routine handles the somewhat strange behaviour of
	 *	an assorted set of keys in VT100 applications mode.
	 */

BYTE __regargs
HandleCursor(UBYTE Char)
{
	STATIC struct
	{
		UBYTE	 Char;
		UBYTE	*VanillaString;
		UBYTE	*ApplicationString;
	} Table[18] =
	{
		CUP,	"\033[A",	"\033OA",
		CDN,	"\033[B",	"\033OB",
		CFW,	"\033[C",	"\033OC",
		CBK,	"\033[D",	"\033OD",

		'0',	"0",		"\033Op",
		'1',	"1",		"\033Oq",
		'2',	"2",		"\033Or",
		'3',	"3",		"\033Os",
		'4',	"4",		"\033Ot",
		'5',	"5",		"\033Ou",
		'6',	"6",		"\033Ov",
		'7',	"7",		"\033Ow",
		'8',	"8",		"\033Ox",
		'9',	"9",		"\033Oy",
		'-',	"-",		"\033Om",
		'*',	"*",		"\033Ol",
		'.',	".",		"\033On",
		'\r',	"\r",		"\033OM"
	};

	BYTE i;

		/* Look for the cursor keys first. */

	for(i = 0 ; i < 4 ; i++)
	{
		if(Table[i] . Char == Char)
		{
			if(Config . CursorApp)
				SerWrite(Table[i] . ApplicationString,strlen(Table[i] . ApplicationString));
			else
				SerWrite(Table[i] . VanillaString,strlen(Table[i] . VanillaString));

			return(TRUE);
		}
	}

		/* Then take a look at the numeric pad. */

	for(i = 4 ; i < 18 ; i++)
	{
		if(Table[i] . Char == Char)
		{
			if(Config . NumApp)
				SerWrite(Table[i] . ApplicationString,strlen(Table[i] . ApplicationString));
			else
			{
				if(i == 17)
				{
					switch(Config . SendCR)
					{
						case CR_IGNORE:	break;

						case CR_ASCR:	SerWrite("\r",1);
								break;

						case CR_ASCRLF:	SerWrite("\r\n",2);
								break;
					}
				}
				else
					SerWrite(Table[i] . VanillaString,strlen(Table[i] . VanillaString));
			}

			return(TRUE);
		}
	}

	return(FALSE);
}

	/* DoBackspace():
	 *
	 *	Special function: perform backspace.
	 */

VOID
DoBackspace()
{
	Capture("\b",1);

	if(CursorX)
	{
		ClearCursor();

		CursorX--;

			/* If destructive, shift the remaining line
			 * one character to the right.
			 */

		if(Config . DestructiveBackspace)
		{
			WORD DeltaX,MinX;

			BackupRender();

			SetBPen(RPort,0);

			RasterEraseCharacters(1);

			if(Config . FontScale == SCALE_NORMAL)
			{
				if(RasterAttr[CursorY] == SCALE_ATTR_NORMAL)
				{
					DeltaX	= 8;
					MinX	= CursorX << 3;
				}
				else
				{
					DeltaX	= 4;
					MinX	= CursorX << 2;
				}
			}
			else
			{
				if(RasterAttr[CursorY] == SCALE_ATTR_NORMAL)
				{
					DeltaX	= 4;
					MinX	= CursorX << 2;
				}
				else
				{
					DeltaX	= 8;
					MinX	= CursorX << 3;
				}
			}

			ScrollLineEraseCharacters(1);

			ScrollLineRaster(RPort,DeltaX,0,MinX,CursorY * 8,LastPixel,(CursorY + 1) * 8 - 1);

			BackupRender();
		}

		SetCursor();
	}
}

	/* DoSomeBeep():
	 *
	 *	The real interface to the beep routine.
	 */

VOID
DoSomeBeep()
{
		/* Handle the visual part. */

	if(Config . VisibleBell || Config . SystemBeep)
	{
		if(StatusProcess)
			Signal(StatusProcess,SIGBREAKF_CTRL_D);
	}

		/* Let it beep. */

	if(Config . AudibleBell)
		Beep();

		/* Capture the bell. */

	if(!Config . CaptureFilter)
		Capture("\a",1);
}

	/* DoLF():
	 *
	 *	Special function: perform line feed.
	 */

VOID
DoLF()
{
	if(Config . ReceiveLF == LF_ASLFCR)
	{
		ClearCursor();

		CursorX = 0;

		DownLine();

		SetCursor();

		Capture("\n",1);
	}
	else
	{
		if(Config . ReceiveLF == LF_ASLF)
		{
			ClearCursor();

			DownLine();

			SetCursor();
		}
	}
}

	/* DoShiftIn():
	 *
	 *	Special function: Shift into graphics mode
	 */

VOID
DoShiftIn()
{
	if(CharMode[1] == TABLE_GFX && GFX)
		CurrentFont = GFX;

	if(CharMode[1] == TABLE_ASCII)
	{
		if(Config . Font == FONT_IBM && IBM)
			CurrentFont = IBM;
		else
			CurrentFont = Topaz;
	}

	SetFont(RPort,CurrentFont);

	Charset = 1;
}

	/* DoShiftOut():
	 *
	 *	Special function: Shift out of graphics mode
	 */

VOID
DoShiftOut()
{
	if(CharMode[0] == TABLE_GFX && GFX)
		CurrentFont = GFX;

	if(CharMode[0] == TABLE_ASCII)
	{
		if(Config . Font == FONT_IBM && IBM)
			CurrentFont = IBM;
		else
			CurrentFont = Topaz;
	}

	SetFont(RPort,CurrentFont);

	Charset = 0;
}

	/* DoIgnore():
	 *
	 *	Special function: don't do anything.
	 */

VOID
DoIgnore()
{
}

	/* DoCR_LF():
	 *
	 *	Special function: perform carriage return and line feed.
	 */

VOID
DoCR_LF()
{
	ClearCursor();

	CursorX = 0;

	DownLine();

	SetCursor();

	Capture("\n",1);
}

	/* DoFF():
	 *
	 *	Special function: perform form feed.
	 */

VOID
DoFF()
{
	if(Config . NewLine)
	{
		CursorX = 0;

		DoCR_LF();
	}
	else
	{
		EraseScreen("2");

		ClearCursor();

		CursorX = CursorY = 0;

		SetCursor();

		Capture("\n",1);
	}
}

	/* DoLF_FF_VT():
	 *
	 *	Special function: handle line feed, form feed and vertical
	 *	tab.
	 */

VOID
DoLF_FF_VT()
{
	if(Config . NewLine)
		DoCR_LF();
	else
		DoLF();
}

	/* DoCR():
	 *
	 *	Special function: handle carriage return.
	 */

VOID
DoCR()
{
	if(Config . NewLine || Config . ReceiveCR == CR_ASCRLF)
		DoCR_LF();
	else
	{
		if(Config . ReceiveCR == CR_ASCR)
		{
			ClearCursor();

			CursorX = 0;

			SetCursor();

			Capture("\n",1);
		}
	}
}

	/* DoTab():
	 *
	 *	Special function: handle tab, move cursor to next
	 *	tab stop.
	 */

VOID
DoTab()
{
	WORD Column;

	if(RasterAttr[CursorY] == SCALE_ATTR_NORMAL)
		Column = LastColumn;
	else
		Column = ((LastColumn + 1) / 2) - 1;

	ClearCursor();

	if(Config . AutoWrap)
	{
		if(CursorX >= LastColumn)
		{
			CursorX = 0;

			DownLine();
		}
		else
		{
			while(CursorX < Column)
			{
				CursorX++;

				if(TabStops[CursorX])
					break;
			}
		}
	}
	else
	{
		while(CursorX < Column)
		{
			CursorX++;

			if(TabStops[CursorX])
				break;
		}
	}

	SetCursor();

	Capture("\t",1);
}

	/* DoEnq():
	 *
	 *	Special function: send answerback message.
	 */

VOID
DoEnq()
{
	if(Config . AnswerBack[0])
		SerialCommand(Config . AnswerBack);
}

	/* GfxText(struct RastPort *RPort,STRPTR Buffer,LONG Length):
	 *
	 *	Text output, if necessary switching from gfx font
	 *	to current default font.
	 */

VOID __regargs
GfxText(struct RastPort *RPort,STRPTR Buffer,LONG Length)
{
	BYTE Mode = 1;

	while(Length--)
	{
		if(GfxTable[*Buffer] == Mode)
			Text(RPort,Buffer++,1);
		else
		{
			if(Mode)
			{
				if(Config . Font == FONT_IBM && IBM)
					SetFont(RPort,IBM);
				else
					SetFont(RPort,Topaz);
			}
			else
				SetFont(RPort,GFX);

			Mode ^= 1;

			Text(RPort,Buffer++,1);
		}
	}

	if(!Mode)
		SetFont(RPort,GFX);
}

	/* SpillTheBeans(UBYTE *Buffer,LONG Size):
	 *
	 *	Output a buffer of given size to the terminal
	 *	window.
	 */

STATIC VOID __regargs
SpillTheBeans(UBYTE *Buffer,LONG Size)
{
	if(ControllerActive)
	{
		if(Size)
		{
			if(!FWrite(PrinterCapture,Buffer,Size,1))
			{
				BlockWindows();

				if(!MyEasyRequest(NULL,LocaleString(MSG_CONSOLE_ERROR_WRITING_TO_PRINTER_TXT),LocaleString(MSG_CONSOLE_IGNORE_CLOSE_PRINTER_TXT)))
					ClosePrinterCapture(TRUE);

				ReleaseWindows();
			}
		}
	}
	else
	{
		UBYTE	Scale,CharScale;
		WORD	Offset,LastChar;

			/* Reposition the cursor and turn it off. */

		ClearCursor();

		Scale = RasterAttr[CursorY];

		if(Config . FontScale == SCALE_HALF)
		{
			switch(Scale)
			{
				case SCALE_ATTR_TOP2X:
				case SCALE_ATTR_BOT2X:
				case SCALE_ATTR_NORMAL:	CharScale	= TRUE;
							LastChar	= LastColumn;
							break;

				case SCALE_ATTR_2X:	CharScale	= FALSE;
							LastChar	= LastColumn;
							break;
			}
		}
		else
		{
			switch(Scale)
			{
				case SCALE_ATTR_TOP2X:
				case SCALE_ATTR_BOT2X:
				case SCALE_ATTR_2X:	CharScale	= TRUE;
							LastChar	= ((LastColumn + 1) / 2) - 1;
							break;

				case SCALE_ATTR_NORMAL:	CharScale	= FALSE;
							LastChar	= LastColumn;
							break;
			}
		}

		if(CurrentFont == GFX)
		{
				/* Do we still have a character in the
				 * magnificient buffer?
				 */

			while(Size)
			{
				/* Cursor is positioned at
				 * the right hand side of the
				 * display. If auto-wrap is
				 * enabled, perform some
				 * kind of CR/LF, else leave
				 * the cursor where it is and
				 * quit the show.
				 */

				if(CursorX > LastChar)
				{
						/* Wrap cursor. */

					if(Config . AutoWrap)
					{
							/* Move to beginning of next line. */

						CursorX = 0;

						DownLine();

						Capture("\n",1);

						Scale = SCALE_ATTR_NORMAL;

						CharScale = (Config . FontScale == SCALE_NORMAL) ? FALSE : TRUE;

						LastChar = LastColumn;

							/* Reposition cursor, don't redraw it. */

						ClipBlitCursor(FALSE,TRUE);
					}
					else
					{
							/* Stop the cursor. */

						CursorX = LastChar;

						Capture(Buffer,Size);

							/* Make it reappear. */

						SetCursor();

						return;
					}
				}

				if(CursorX <= LastChar)
				{
						/* We won't have to take
						 * care of characters to shift.
						 * We'll collect as many
						 * characters in the buffer as will
						 * fit into the current line
						 * and print them.
						 */

					if((Offset = LastChar + 1 - CursorX) > Size)
					{
						Offset = Size;

						if(Config . InsertChar)
						{
							RasterShiftChar(Offset);

							ScrollLineShiftChar(Offset);

							ShiftChar(Offset);
						}
					}

					RasterPutString(Buffer,Offset);

					ScrollLinePutString(Offset);

					if(CharScale)
						PrintScaled(Buffer,Offset,Scale);
					else
						GfxText(RPort,Buffer,Offset);

					Capture(Buffer,Offset);

					Buffer	+= Offset;

					Size	-= Offset;

					CursorX	+= Offset;
				}
			}
		}
		else
		{
				/* Do we still have a character in the
				 * magnificient buffer?
				 */

			while(Size)
			{
				/* Cursor is positioned at
				 * the right hand side of the
				 * display. If auto-wrap is
				 * enabled, perform some
				 * kind of CR/LF, else leave
				 * the cursor where it is and
				 * quit the show.
				 */

				if(CursorX > LastChar)
				{
						/* Wrap cursor. */

					if(Config . AutoWrap)
					{
							/* Move to beginning of next line. */

						CursorX = 0;

						DownLine();

						Capture("\n",1);

						Scale = SCALE_ATTR_NORMAL;

						CharScale = (Config . FontScale == SCALE_NORMAL) ? FALSE : TRUE;

						LastChar = LastColumn;

							/* Reposition cursor, don't redraw it. */

						ClipBlitCursor(FALSE,TRUE);
					}
					else
					{
							/* Stop the cursor. */

						CursorX = LastChar;

						Capture(Buffer,Size);

							/* Make it reappear. */

						SetCursor();

						return;
					}
				}

				if(CursorX <= LastChar)
				{
						/* We won't have to take
						 * care of characters to shift.
						 * We'll collect as many
						 * characters in the buffer as will
						 * fit into the current line
						 * and print them.
						 */

					if((Offset = LastChar + 1 - CursorX) > Size)
					{
						Offset = Size;

						if(Config . InsertChar)
						{
							RasterShiftChar(Offset);

							ScrollLineShiftChar(Offset);

							ShiftChar(Offset);
						}
					}

					RasterPutString(Buffer,Offset);

					ScrollLinePutString(Offset);

					if(CharScale)
						PrintScaled(Buffer,Offset,Scale);
					else
						Text(RPort,Buffer,Offset);

					Capture(Buffer,Offset);

					Buffer	+= Offset;

					Size	-= Offset;

					CursorX	+= Offset;
				}
			}
		}

			/* Make the cursor reappear. */

		SetCursor();
	}
}

	/* ConWrite(APTR Buffer,LONG Size):
	 *
	 *	Write a string to the console window.
	 */

VOID __regargs
ConWrite(APTR Buffer,LONG Size)
{
	STATIC UBYTE	 StringBuffer[200],BufSize = 0;
	UBYTE		*Char = Buffer;

		/* Run down the buffer. */

	while(Size--)
	{
			/* Are we parsing a control sequence? */

		if(InSequence)
		{
			if(AbortTable[*Char])
			{
				DoCancel();

				if(*Char == ESC || *Char == CSI)
					InSequence = TRUE;
			}
			else
			{
					/* See if we are already done. */

				InSequence = ParseCode(*Char++);
			}
		}
		else
		{
				/* This looks like a control sequence
				 * introducing character.
				 */

			if(*Char == ESC || *Char == CSI)
			{
				if(BufSize)
				{
					SpillTheBeans(&StringBuffer[0],BufSize);

					BufSize = 0;
				}

					/* So we're in TTY mode,
					 * escape the `escape' character
					 * and continue.
					 */

				if(Config . Emulation == EMULATION_TTY)
				{
					StringBuffer[BufSize++] = '^';
					StringBuffer[BufSize++] = '[';
				}
				else
				{
					if(*Char == ESC)
						InSequence = TRUE;
					else
						CSIFake();
				}

				Char++;
			}
			else
			{
					/* Stuff the character into
					 * the buffer.
					 */

				StringBuffer[BufSize++] = *Char++;

					/* If buffer is full, spill it. */

				if(BufSize == 200)
				{
					SpillTheBeans(&StringBuffer[0],BufSize);

					BufSize = 0;
				}
			}
		}
	}

		/* Any characters in the buffer we didn't process yet? */

	if(!InSequence && BufSize)
	{
		SpillTheBeans(&StringBuffer[0],BufSize);

		BufSize = 0;
	}
}

	/* ConProcess(UBYTE *String,LONG Size):
	 *
	 *	Process the contents of a string to be sent to the
	 *	console window.
	 */

VOID __regargs
ConProcess(UBYTE *String,LONG Size)
{
	LONG i;

		/* If the capture filter happens to be disabled, write the
		 * raw data.
		 */

	if(!Config . CaptureFilter)
		CaptureToFile(String,Size);

		/* Oh dear, an external emulation will take care of
		 * displaying the data so we will need to make sure
		 * that the data flow filter and the capture streams
		 * are properly fed.
		 */

	if(XEmulatorBase && Config . Emulation == EMULATION_EXTERNAL)
	{
			/* Feed the flow filter... */

		if(Config . StripBit8)
		{
			for(i = 0 ; i < Size ; i++)
				FlowFilter(String[i] & 0x7F);
		}
		else
		{
			for(i = 0 ; i < Size ; i++)
				FlowFilter(String[i]);
		}

			/* Are we to output data? */

		if(!Quiet)
		{
			XEmulatorWrite(XEM_IO,String,Size);

				/* Build another string to contain
				 * the pure ASCII contents, i.e.
				 * not including any ESC control
				 * sequences.
				 */

			if(StripBuffer)
			{
				XEM_HostData . Source		= String;
				XEM_HostData . Destination	= StripBuffer;

				if(i = XEmulatorHostMon(XEM_IO,&XEM_HostData,Size))
					Capture(StripBuffer,i);
			}
		}
	}
	else
	{
		LONG j;

			/* If the eighth bit is to be stripped from incoming
			 * characters, let's process the whole stuff
			 * in a different loop... (and that's all for the
			 * sake of speed).
			 */

		if(Config . StripBit8)
		{
				/* In quiet mode no characters are echoed to the
				 * console window, they are just passed through
				 * the data flow filter. Usually, this mode is
				 * enabled by the dial panel.
				 */

			if(Quiet)
			{
				for(i = 0 ; i < Size ; i++)
					FlowFilter(String[i] & 0x7F);
			}
			else
			{
				UBYTE c;

					/* Check which font we are in, if other than Topaz
					 * the only invalid char is a Null (0) which will
					 * display as a space if let to continue.
					 */

				if(Config . Font == FONT_TOPAZ)
				{
					for(i = 0, j = 0 ; i < Size ; i++)
					{
						c = String[i] & 0x7F;

						FlowFilter(c);

						if(IsPrintable(c))
						{
							/* This character is associated with a
							 * special function (bell, xon, xoff, etc.).
							 */

							if(SpecialMap[c] != -1)
							{
								if(j)
								{
									ConWrite(SharedBuffer,j);

									j = 0;
								}

								SpecialKeys[SpecialMap[c]] . Routine();
							}
							else
							{
								/* Put the character into the buffer
								 * and flush it if necessary.
								 */

								SharedBuffer[j++] = c;

								if(j == 512)
								{
									ConWrite(SharedBuffer,j);

									j = 0;
								}
							}
						}
					}
				}
				else
				{
					for(i = 0, j = 0 ; i < Size ; i++)
					{
						if(c = (String[i] & 0x7F))
						{
							FlowFilter(c);

							/* This character is associated with a
							 * special function (bell, xon, xoff, etc.).
							 */

							if(SpecialMap[c] != -1)
							{
								if(j)
								{
									ConWrite(SharedBuffer,j);

									j = 0;
								}

								SpecialKeys[SpecialMap[c]] . Routine();
							}
							else
							{
								/* Put the character into the buffer
								 * and flush it if necessary.
								 */

								SharedBuffer[j++] = c;

								if(j == 512)
								{
									ConWrite(SharedBuffer,j);

									j = 0;
								}
							}
						}
					}
				}

				if(j)
					ConWrite(SharedBuffer,j);
			}
		}
		else
		{
			if(Quiet)
			{
				for(i = 0 ; i < Size ; i++)
					FlowFilter(String[i]);
			}
			else
			{
				UBYTE c;

				if(Config . Font == FONT_TOPAZ)
				{
					for(i = 0, j = 0 ; i < Size ; i++)
					{
						c = String[i];

						FlowFilter(c);

						if(IsPrintable(c))
						{
							if(SpecialMap[c] != -1)
							{
								if(j)
								{
									ConWrite(SharedBuffer,j);

									j = 0;
								}

								SpecialKeys[SpecialMap[c]] . Routine();
							}
							else
							{
								SharedBuffer[j++] = c;

								if(j == 512)
								{
									ConWrite(SharedBuffer,j);

									j = 0;
								}
							}
						}
					}
				}
				else
				{
					for(i = 0, j = 0 ; i < Size ; i++)
					{
						if(c = String[i])
						{
							FlowFilter(c);

							if(SpecialMap[c] != -1)
							{
								if(j)
								{
									ConWrite(SharedBuffer,j);

									j = 0;
								}

								SpecialKeys[SpecialMap[c]] . Routine();
							}
							else
							{
								SharedBuffer[j++] = c;

								if(j == 512)
								{
									ConWrite(SharedBuffer,j);

									j = 0;
								}
							}
						}
					}
				}

				if(j)
					ConWrite(SharedBuffer,j);
			}
		}
	}
}

	/* ConWrites(UBYTE *String,...):
	 *
	 *	Output a string to the console.
	 */

VOID __stdargs
ConWrites(UBYTE *String,...)
{
	va_list	VarArgs;

	va_start(VarArgs,String);
	VSPrintf(SharedBuffer,String,VarArgs);
	va_end(VarArgs);

	ConProcess(SharedBuffer,strlen(SharedBuffer));
}

	/* KeyConvert(struct IntuiMessage *Massage,UBYTE *Buffer):
	 *
	 *	Convert a raw key information according to the
	 *	current keymap settings.
	 */

UBYTE __regargs
KeyConvert(struct IntuiMessage *Massage,UBYTE *Buffer,LONG *Len)
{
	if(Buffer)
		Buffer[0] = 0;

	if(Len)
		*Len = 0;

	if(Massage -> Class == IDCMP_RAWKEY)
	{
			/* These are the sequences mapped to special
			 * control keys (cursor keys, function keys,
			 * the help key).
			 */

		STATIC struct
		{
			UBYTE *RawCode;
			UBYTE Result;
		} ConversionTable[16] =
		{
			(UBYTE *)"A",	CUP,
			(UBYTE *)"B",	CDN,
			(UBYTE *)"C",	CFW,
			(UBYTE *)"D",	CBK,

			(UBYTE *)"?~",	HLP,

			(UBYTE *)"0~",	FN1,
			(UBYTE *)"1~",	FN2,
			(UBYTE *)"2~",	FN3,
			(UBYTE *)"3~",	FN4,
			(UBYTE *)"4~",	FN5,
			(UBYTE *)"5~",	FN6,
			(UBYTE *)"6~",	FN7,
			(UBYTE *)"7~",	FN8,
			(UBYTE *)"8~",	FN9,
			(UBYTE *)"9~",	F10
		};

		STATIC UBYTE SeqLens[16] = {1,1,1,1,2,2,2,2,2,2,2,2,2,2,2 };

			/* Key was pressed, not released. */

		if(!(Massage -> Code & IECODE_UP_PREFIX))
		{
			UBYTE	ConvertBuffer[257],i;
			ULONG	Qualifier = Massage -> Qualifier;
			LONG	Actual;

				/* If it's a function key clear the qualifier. */

			if(Massage -> Code >= 80 && Massage -> Code <= 89)
				Qualifier = NULL;

				/* Convert the key. */

			FakeInputEvent -> ie_Code		= Massage -> Code;
			FakeInputEvent -> ie_Qualifier		= Qualifier;
			FakeInputEvent -> ie_position . ie_addr	= *((APTR *)Massage -> IAddress);

			if((Actual = RawKeyConvert(FakeInputEvent,(UBYTE *)ConvertBuffer,256,KeyMap)) > 0)
			{
				if(ConvertBuffer[0])
				{
					if(Config . SwapBSDelete)
					{
						for(i = 0 ; i < Actual ; i++)
						{
							if(ConvertBuffer[i] == BKS)
								ConvertBuffer[i] = DEL;
							else
							{
								if(ConvertBuffer[i] == DEL)
									ConvertBuffer[i] = BKS;
							}
						}
					}

					if(Len)
					{
						if(Actual == 1 && (Qualifier & IEQUALIFIER_CONTROL))
						{
							if(ConvertBuffer[0] == '@' || ConvertBuffer[0] == ' ')
							{
								ConvertBuffer[0] = Buffer[0] = 0;

								*Len = 1;
							}
							else
							{
								*Len = Actual;

								if(Buffer)
									memcpy(Buffer,ConvertBuffer,Actual);
							}
						}
						else
						{
							*Len = Actual;

							memcpy(Buffer,ConvertBuffer,Actual);
						}
					}
					else
					{
						if(Buffer)
							memcpy(Buffer,ConvertBuffer,Actual);
					}

						/* Translated sequence starts
						 * with a CSI, let's have a look
						 * at the associated control
						 * key.
						 */

					if(ConvertBuffer[0] == CSI)
					{
						for(i = 0 ; i < sizeof(SeqLens) ; i++)
						{
							if(!Strnicmp(&ConvertBuffer[1],ConversionTable[i] . RawCode,SeqLens[i]))
							{
								ConvertBuffer[0] = ConversionTable[i] . Result;

								if(Buffer)
								{
									Buffer[0] = ConversionTable[i] . Result;
									Buffer[1] = 0;

									if(Len)
										*Len = 1;
								}

								break;
							}
						}
					}

					return(ConvertBuffer[0]);
				}
			}

				/* If nothing came from the key conversion,
				 * check for shift-tab.
				 */

			if(Massage -> Code == 66 && (Qualifier & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT)))
			{
				*Len = 2;

				strcpy(ConvertBuffer,"\33\t");

				if(Buffer)
					memcpy(Buffer,ConvertBuffer,2);

				return(ConvertBuffer[0]);
			}
		}
	}

	return(0);
}
