/*
**	termConsole.c
**
**	High-level terminal console routines
**
**	Copyright © 1990-1994 by Olaf `Olsen' Barthel
**		All Rights Reserved
*/

#include "termGlobal.h"

	/* Character set modes. */

enum	{	MODE_STANDARD,MODE_GFX };

	/* Hints for the data flow scanner. */

STATIC WORD		ScanStart,
			ScanEnd;

STATIC WORD __far	AttentionCount[SCAN_COUNT],
			AttentionLength[SCAN_COUNT],
			FlowCount;

	/* Temporary console working buffer. */

STATIC UBYTE __far	ConTempBuffer[512];

	/* Text output data. */

STATIC UBYTE		CurrentFontScale,FontScalingRequired;
STATIC WORD		LastPrintableColumn;

	/* ConTranslateSetup():
	 *
	 *	Set up for buffer translation.
	 */

STATIC VOID __inline
ConTranslateSetup(struct TranslationHandle *Handle,STRPTR SourceBuffer,LONG SourceLen,STRPTR DestinationBuffer,LONG DestinationLen,struct TranslationEntry **Table)
{
	Handle -> LocalBuffer		= NULL;
	Handle -> LocalLen		= 0;

	Handle -> SourceBuffer		= SourceBuffer;
	Handle -> SourceLen		= SourceLen;

	Handle -> DestinationBuffer	= DestinationBuffer;
	Handle -> DestinationLen	= DestinationLen;

	Handle -> Table			= Table;
}

	/* ConTranslateBuffer(struct TranslationHandle *Handle):
	 *
	 *	Translate buffer contents according to
	 *	translation table contents.
	 */

STATIC LONG __inline
ConTranslateBuffer(struct TranslationHandle *Handle)
{
	register STRPTR				 Data		= Handle -> DestinationBuffer;
	register LONG				 BytesWritten	= 0;
	register struct TranslationEntry	*Entry;

		/* Are we to return any translated data? */

	while(Handle -> LocalLen && BytesWritten < Handle -> DestinationLen)
	{
			/* Decrement number of bytes in buffer. */

		Handle -> LocalLen--;

			/* Return next character. */

		*Data++ = *Handle -> LocalBuffer++;

			/* Add another byte. */

		BytesWritten++;
	}

		/* Loop until done. */

	while(Handle -> SourceLen && BytesWritten < Handle -> DestinationLen)
	{
			/* Another byte eaten. */

		Handle -> SourceLen--;

			/* Get table entry. */

		if(Entry = Handle -> Table[*Handle -> SourceBuffer++])
		{
				/* Copy to local data area. */

			Handle -> LocalBuffer	= Entry -> String;
			Handle -> LocalLen	= Entry -> Len;

				/* Translate the data. */

			while(Handle -> LocalLen && BytesWritten < Handle -> DestinationLen)
			{
					/* Decrement number of bytes in buffer. */

				Handle -> LocalLen--;

					/* Return next character. */

				*Data++ = *Handle -> LocalBuffer++;

					/* Add another byte. */

				BytesWritten++;
			}
		}
	}

	return(BytesWritten);
}

	/* TrapFilter(register STRPTR Data,register LONG Size):
	 *
	 *	Handle the trap list, similar to FlowFilter().
	 */

STATIC VOID __regargs
TrapFilter(register STRPTR Data,register LONG Size)
{
	STATIC LONG WaitCount = 0;

	struct List	*List = (struct List *)&GenericListTable[GLIST_TRAP] -> ListHeader;
	struct TrapNode	*Node;

	register UBYTE c,Mask;

	ObtainSemaphore(&GenericListTable[GLIST_TRAP] -> ListSemaphore);

	if(Config -> SerialConfig -> StripBit8)
		Mask = 0x7F;
	else
		Mask = 0xFF;

	do
	{
		register BOOLEAN MatchMade;

		c = (*Data++) & Mask;

		do
		{
			MatchMade = FALSE;

			for(Node = (struct TrapNode *)List -> lh_Head ; Node -> Node . ln_Succ ; Node = (struct TrapNode *)Node -> Node . ln_Succ)
			{
				if(Node -> Count == WaitCount)
				{
					if(c == (Node -> Sequence[WaitCount] & Mask))
					{
						MatchMade = TRUE;

						if(++Node -> Count == Node -> SequenceLen)
						{
							struct DataMsg *Msg;

							Node -> Count = 0;

							if(Msg = (struct DataMsg *)CreateMsgItem(sizeof(struct DataMsg) + strlen(Node -> Command) + 1))
							{
								Msg -> Type = DATAMSGTYPE_SERIALCOMMAND;
								Msg -> Data = (STRPTR)(Msg + 1);

								strcpy(Msg -> Data,Node -> Command);

								PutMsgItem(SpecialQueue,(struct MsgItem *)Msg);
							}
						}
					}
				}
			}

			if(MatchMade)
				WaitCount++;
			else
			{
				if(WaitCount)
				{
					WaitCount = 0;

					for(Node = (struct TrapNode *)List -> lh_Head ; Node -> Node . ln_Succ ; Node = (struct TrapNode *)Node -> Node . ln_Succ)
						Node -> Count = 0;
				}
				else
					break;
			}
		}
		while(!WaitCount);
	}
	while(--Size);

	ReleaseSemaphore(&GenericListTable[GLIST_TRAP] -> ListSemaphore);
}

	/* FlowFilter(register STRPTR Data,register LONG Size,UBYTE Mask):
	 *
	 *	Data flow filter.
	 */

STATIC VOID __regargs
FlowFilter(register STRPTR Data,register LONG Size,register UBYTE Mask)
{
	register UBYTE c;

		/* Run until done. */

	do
	{
		if(c = (*Data++ & Mask))
		{

				/* We already got a `CONNECT',
				 * Continue scanning the serial output
				 * data for the actual baud rate.
				 */

			if(BaudPending)
			{
				if(c < ' ')
				{
					while(BaudCount > 0 && BaudBuffer[BaudCount - 1] == ' ')
						BaudCount--;

					BaudBuffer[BaudCount] = 0;

					FlowInfo . Connect = TRUE;
					FlowInfo . Changed = TRUE;

					BaudPending = FALSE;

					DTERate = GetBaudRate(BaudBuffer);

					if(Quiet && Size > 1)
					{
						DataHold = Data;
						DataSize = Size - 1;
					}
				}
				else
				{
					if(BaudCount || c != ' ')
					{
						BaudBuffer[BaudCount++] = c;

						if(BaudCount == 79)
						{
							while(BaudCount > 0 && BaudBuffer[BaudCount - 1] == ' ')
								BaudCount--;

							BaudBuffer[BaudCount] = 0;

							FlowInfo . Connect = TRUE;
							FlowInfo . Changed = TRUE;

							BaudPending = FALSE;

							DTERate = GetBaudRate(BaudBuffer);

							if(Quiet && Size > 1)
							{
								DataHold = Data;
								DataSize = Size - 1;
							}
						}
					}
				}
			}
			else
			{
				register BYTE MatchMade;
				register WORD i;

				do
				{
					MatchMade = FALSE;

						/* Scan all ID strings for matches. */

					for(i = ScanStart ; i <= ScanEnd ; i++)
					{
							/* This sequence is a likely
							 * match.
							 */

						if(AttentionCount[i] == FlowCount)
						{
								/* Does the character
								 * fit into the sequence?
								 */

							if(c == AttentionBuffers[i][FlowCount] & Mask)
							{
								MatchMade = TRUE;

									/* Did we hit the
									 * last character
									 * in the sequence?
									 */

								if(++AttentionCount[i] == AttentionLength[i])
								{
										/* We've got a valid
										 * sequence, now look
										 * which flags to change.
										 */

									switch(i)
									{
											/* We got a `no carrier' message. */

										case SCAN_NOCARRIER:

											if(!FlowInfo . NoCarrier)
											{
												FlowInfo . NoCarrier	= TRUE;
												FlowInfo . Changed	= TRUE;
											}

											break;

											/* Got another call. */

										case SCAN_RING:

											if(!FlowInfo . Ring)
											{
												FlowInfo . Ring		= TRUE;
												FlowInfo . Changed	= TRUE;
											}

											break;

											/* Got a voice call. */

										case SCAN_VOICE:

											if(!FlowInfo . Voice)
											{
												FlowInfo . Voice	= TRUE;
												FlowInfo . Changed	= TRUE;
											}

											break;

											/* Got a connect message. */

										case SCAN_CONNECT:

											ObtainSemaphore(&OnlineSemaphore);

											if(!Online)
											{
												BaudBuffer[0]	= 0;

												BaudPending	= TRUE;
												BaudCount	= 0;
											}

											ReleaseSemaphore(&OnlineSemaphore);

											break;

											/* Got the ZModem inquiry sequence. */

										case SCAN_ZMODEM:

											if(!FlowInfo . ZModemUpload)
											{
												FlowInfo . ZModemUpload	= TRUE;
												FlowInfo . Changed	= TRUE;
											}

											break;

											/* Line is busy. */

										case SCAN_BUSY:

											if(!FlowInfo . Busy)
											{
												FlowInfo . Busy		= TRUE;
												FlowInfo . Changed	= TRUE;
											}

											break;

										case SCAN_NODIALTONE:

											if(!FlowInfo . NoDialTone)
											{
												FlowInfo . NoDialTone	= TRUE;
												FlowInfo . Changed	= TRUE;
											}

											break;
									}
								}
							}
						}
					}

					if(MatchMade)
						FlowCount++;
					else
					{
						if(FlowCount)
						{
							FlowCount = 0;

							memset(AttentionCount,0,sizeof(AttentionCount));
						}
						else
							break;
					}
				}
				while(!FlowCount);
			}
		}
	}
	while(--Size);
}

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

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

	while(Length--)
	{
		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 CAN:
			case SUB:

				HasESC = HasCSI = FALSE;
				break;

			case '\r':

				break;

			case ESC:

				HasESC = TRUE;
				break;

			case CSI:

				HasCSI = TRUE;
				break;

			default:

				if(IsPrintable[*Src])
				{
					*Dst++ = *Src;

					Size++;
				}

				break;
		}

		Src++;
	}

	return(Size);
}

	/* ConOutputPrinter(STRPTR Buffer,LONG Size):
	 *
	 *	Raw text output routine, printer capture flavour.
	 */

STATIC VOID
ConOutputPrinter(STRPTR Buffer,LONG Size)
{
	if(FWrite(PrinterCapture,Buffer,Size,1) != 1)
	{
		BlockWindows();

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

		ReleaseWindows();
	}
}

	/* ConOutputGFX(STRPTR Buffer,LONG Size):
	 *
	 *	Raw text output routine, GFX text flavour.
	 */

STATIC VOID 
ConOutputGFX(STRPTR Buffer,LONG Size)
{
	register WORD Offset;

		/* 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 > LastPrintableColumn)
		{
				/* Wrap cursor. */

			if(Config -> EmulationConfig -> LineWrap)
			{
					/* Move to beginning of next line. */

				CursorX = 0;

				DownLine();

				CurrentFontScale = SCALE_ATTR_NORMAL;

				FontScalingRequired = (Config -> EmulationConfig -> FontScale != SCALE_NORMAL);

				LastPrintableColumn = LastColumn;

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

				RepositionCursor();
			}
			else
			{
					/* Stop the cursor. */

				CursorX = LastPrintableColumn;

					/* Make it reappear. */

				RepositionCursor();

				return;
			}
		}

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

		if(Config -> EmulationConfig -> InsertMode)
		{
			RasterShiftChar(Offset);

			ScrollLineShiftChar(Offset);

			ShiftChar(Offset);
		}

		RasterPutString(Buffer,Offset);

		ScrollLinePutString(Offset);

		if(FontScalingRequired)
			PrintScaled(Buffer,Offset,CurrentFontScale);
		else
			GfxText(RPort,Buffer,Offset);

		Buffer	+= Offset;

		Size	-= Offset;

		CursorX	+= Offset;
	}

	RepositionCursor();
}

	/* ConOutputNormal(STRPTR Buffer,LONG Size):
	 *
	 *	Raw text output routine, normal text flavour.
	 */

STATIC VOID 
ConOutputNormal(STRPTR Buffer,LONG Size)
{
	register WORD Offset;

	while(Size)
	{
		if(CursorX > LastPrintableColumn)
		{
			if(Config -> EmulationConfig -> LineWrap)
			{
				CursorX = 0;

				DownLine();

				CurrentFontScale = SCALE_ATTR_NORMAL;

				FontScalingRequired = (Config -> EmulationConfig -> FontScale != SCALE_NORMAL);

				LastPrintableColumn = LastColumn;

				RepositionCursor();
			}
			else
			{
				CursorX = LastPrintableColumn;

				RepositionCursor();

				return;
			}
		}

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

		if(Config -> EmulationConfig -> InsertMode)
		{
			RasterShiftChar(Offset);

			ScrollLineShiftChar(Offset);

			ShiftChar(Offset);
		}

		RasterPutString(Buffer,Offset);

		ScrollLinePutString(Offset);

		if(FontScalingRequired)
			PrintScaled(Buffer,Offset,CurrentFontScale);
		else
			Text(RPort,Buffer,Offset);

		Buffer	+= Offset;

		Size	-= Offset;

		CursorX	+= Offset;
	}

	RepositionCursor();
}

	/* ConProcessUpdate():
	 *
	 *	Choose the right console data processing routine.
	 */

VOID
ConProcessUpdate(VOID)
{
	Forbid();

	if(XEmulatorBase && Config -> TerminalConfig -> EmulationMode == EMULATION_EXTERNAL)
	{
		if(ReceiveTable)
			ConProcessData = ConProcessDataTransExternal;
		else
			ConProcessData = ConProcessDataExternal;
	}
	else
	{
		if(Config -> SerialConfig -> StripBit8)
		{
			if(Config -> TerminalConfig -> EmulationMode == EMULATION_HEX)
				ConProcessData = ConProcessDataHex7;
			else
				ConProcessData = ConProcessData7;
		}
		else
		{
			if(Config -> TerminalConfig -> EmulationMode == EMULATION_HEX)
				ConProcessData = ConProcessDataHex8;
			else
				ConProcessData = ConProcessData8;
		}
	}

	Permit();
}

	/* ConOutputUpdate():
	 *
	 *	Choose the right raw text output routine for the job.
	 */

VOID
ConOutputUpdate()
{
	Forbid();

	if(BufferFrozen)
	{
		if(FileCapture)
		{
			if(PrinterCapture)
			{
				if(Config -> CaptureConfig -> CaptureFilterMode)
					CaptureData = CaptureFilteredTo_File_Printer;
				else
					CaptureData = CaptureRawTo_File_Printer;
			}
			else
			{
				if(Config -> CaptureConfig -> CaptureFilterMode)
					CaptureData = CaptureFilteredTo_File;
				else
					CaptureData = CaptureRawTo_File;
			}
		}
		else
		{
			if(PrinterCapture)
			{
				if(Config -> CaptureConfig -> CaptureFilterMode)
					CaptureData = CaptureFilteredTo_Printer;
				else
					CaptureData = CaptureRawTo_Printer;
			}
			else
				CaptureData = NULL;
		}
	}
	else
	{
		if(FileCapture)
		{
			if(PrinterCapture)
			{
				if(Config -> CaptureConfig -> CaptureFilterMode)
					CaptureData = CaptureFilteredTo_Buffer_File_Printer;
				else
					CaptureData = CaptureRawTo_Buffer_File_Printer;
			}
			else
			{
				if(Config -> CaptureConfig -> CaptureFilterMode)
					CaptureData = CaptureFilteredTo_Buffer_File;
				else
					CaptureData = CaptureRawTo_Buffer_File;
			}
		}
		else
		{
			if(PrinterCapture)
			{
				if(Config -> CaptureConfig -> CaptureFilterMode)
					CaptureData = CaptureFilteredTo_Buffer_Printer;
				else
					CaptureData = CaptureRawTo_Buffer_Printer;
			}
			else
			{
				if(Config -> CaptureConfig -> CaptureFilterMode)
					CaptureData = CaptureFilteredTo_Buffer;
				else
					CaptureData = CaptureRawTo_Buffer;
			}
		}
	}

	if(ControllerActive)
		ConOutput = ConOutputPrinter;
	else
	{
		if(CurrentFont == GFX)
			ConOutput = ConOutputGFX;
		else
			ConOutput = ConOutputNormal;
	}

	if(ReceiveTable)
		ConDump = ConTransWrite;
	else
		ConDump = ConOutput;

	Permit();
}

	/* ConFontScaleUpdate():
	 *
	 *	Choose the right font scale for the job.
	 */

VOID
ConFontScaleUpdate()
{
	CurrentFontScale = RasterAttr[CursorY];

	if(Config -> EmulationConfig -> FontScale == SCALE_HALF)
	{
		FontScalingRequired	= (CurrentFontScale != SCALE_ATTR_2X);
		LastPrintableColumn	= LastColumn;
	}
	else
	{
		if(FontScalingRequired = (CurrentFontScale != SCALE_ATTR_NORMAL))
			LastPrintableColumn = ((LastColumn + 1) / 2) - 1;
		else
			LastPrintableColumn = LastColumn;
	}
}

	/* ConTransWrite(STRPTR Buffer,LONG Size):
	 *
	 *	Frontend for ConWrite(), including character translation.
	 */

VOID
ConTransWrite(STRPTR Buffer,LONG Size)
{
	struct TranslationHandle	Handle;
	UBYTE				LocalBuffer[256];

		/* Set up for translation. */

	ConTranslateSetup(&Handle,Buffer,Size,LocalBuffer,256,ReceiveTable);

		/* Process and output the data. */

	while(Size = ConTranslateBuffer(&Handle))
		(* ConOutput)(LocalBuffer,Size);
}

	/* ConProcessDataTransExternal(register STRPTR String,register LONG Size):
	 *
	 *	Process data, external emulation including translation flavour.
	 */

VOID
ConProcessDataTransExternal(register STRPTR String,register LONG Size)
{
	struct TranslationHandle Handle;

		/* Set up for translation. */

	ConTranslateSetup(&Handle,String,Size,ConTempBuffer,256,ReceiveTable);

	if(StripBuffer)
	{
		register LONG Len;

			/* Process and output the data. */

		while(Size = ConTranslateBuffer(&Handle))
		{
			XEmulatorWrite(XEM_IO,ConTempBuffer,Size);

			XEM_HostData . Source		= ConTempBuffer;
			XEM_HostData . Destination	= StripBuffer;

			if(Len = XEmulatorHostMon(XEM_IO,&XEM_HostData,Size))
			{
				if(CaptureData)
					(*CaptureData)(StripBuffer,Len);
			}
		}
	}
	else
	{
			/* Process and output the data. */

		while(Size = ConTranslateBuffer(&Handle))
			XEmulatorWrite(XEM_IO,ConTempBuffer,Size);
	}
}

	/* ConProcessDataExternal(register STRPTR String,register LONG Size):
	 *
	 *	Process data, external emulation flavour.
	 */

VOID 
ConProcessDataExternal(register STRPTR String,register LONG Size)
{
	XEmulatorWrite(XEM_IO,String,Size);

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

	if(StripBuffer)
	{
		register LONG Len;

		XEM_HostData . Source		= String;
		XEM_HostData . Destination	= StripBuffer;

		if(Len = XEmulatorHostMon(XEM_IO,&XEM_HostData,Size))
		{
			if(CaptureData)
				(*CaptureData)(StripBuffer,Len);
		}
	}
}

	/* ConProcessData7(register STRPTR String,register LONG Size):
	 *
	 *	Process data, 7 bit flavour.
	 */

VOID 
ConProcessData7(register STRPTR String,register LONG Size)
{
	register LONG	Len = 0;
	register UBYTE	c;

		// Check for prescroll/jump scroll

	if(CursorY == Bottom && Bottom > 0)
	{
			// The maximum number of line feeds we need to look for

		register WORD Max = MAX(Config -> EmulationConfig -> MaxJump,Config -> EmulationConfig -> MaxScroll);

			// Any prescroll/jump scroll action wanted?

		if(Max)
		{
			register WORD	 Count	= Size,
					 Lines	= 0;
			register UBYTE	*Buffer	= String;

				// Count the number of line feeds

			while(Count--)
			{
				if((*Buffer++ & 0x7F) == '\n')
				{
					if(++Lines == Max)
						break;
				}
			}

				// Ok, so we have found something

			if(Lines)
			{
				register WORD Scroll,TotalLines;

					// Scroll by default?

				if(Scroll = Config -> EmulationConfig -> MaxJump)
				{
						// If there is more, use it

					if(Lines > Scroll)
						Scroll = Lines;
				}
				else
					Scroll = Lines;

					// How tall is the current scroll region?

				TotalLines = Bottom - Top + 1;

					// Don't scroll more than the entire screenful

				if(Scroll > TotalLines)
					Scroll = TotalLines;

					// Is it worth scrolling?

				if(Scroll > 1)
				{
						// Scroll the region...

					ScrollRegion(Scroll);

						// Reposition the cursor

					if(CursorY > Scroll)
						CursorY -= Scroll;
					else
						CursorY = 0;

					RepositionCursor();
				}
			}
		}
	}

		/* If still parsing a sequence,
		 * continue with it.
		 */

	if(InSequence)
	{
		while(Size--)
		{
			c = *String++ & 0x7F;

			if(!(*AbortTable[c])(c))
			{
				InSequence = FALSE;

				break;
			}
		}
	}

		/* 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 -> TerminalConfig -> FontMode == FONT_STANDARD)
	{
		while(Size-- > 0)
		{
			if(IsPrintable[c = *String++ & 0x7F])
			{
					/* This character is associated with a
					 * special function (bell, xon, xoff, etc.).
					 */

				if(SpecialTable[c])
				{
					if(Len)
					{
						(*ConDump)(ConTempBuffer,Len);

						Len = 0;
					}

						/* Does this character start
						 * a control sequence?
						 */

					if(InSequence = (*SpecialTable[c])(c))
					{
						while(Size-- > 0)
						{
							c = *String++ & 0x7F;

							if(!(*AbortTable[c])(c))
							{
								InSequence = FALSE;

								break;
							}
						}
					}
				}
				else
				{
						/* Put the character into the buffer
						 * and flush it if necessary.
						 */

					ConTempBuffer[Len] = c;

					if(Len++ == 512)
					{
						(*ConDump)(ConTempBuffer,Len);

						Len = 0;
					}
				}
			}
		}
	}
	else
	{
		while(Size-- > 0)
		{
			if(c = (*String++ & 0x7F))
			{
					/* This character is associated with a
					 * special function (bell, xon, xoff, etc.).
					 */

				if(SpecialTable[c])
				{
					if(Len)
					{
						(*ConDump)(ConTempBuffer,Len);

						Len = 0;
					}

					if(InSequence = (*SpecialTable[c])(c))
					{
						while(Size-- > 0)
						{
							c = *String++ & 0x7F;

							if(!(*AbortTable[c])(c))
							{
								InSequence = FALSE;

								break;
							}
						}
					}
				}
				else
				{
						/* Put the character into the buffer
						 * and flush it if necessary.
						 */

					ConTempBuffer[Len] = c;

					if(Len++ == 512)
					{
						(*ConDump)(ConTempBuffer,Len);

						Len = 0;
					}
				}
			}
		}
	}

	if(Len)
		(*ConDump)(ConTempBuffer,Len);
}

	/* ConProcessData8(register STRPTR String,register LONG Size):
	 *
	 *	Process data, 8 bit flavour.
	 */

VOID 
ConProcessData8(register STRPTR String,register LONG Size)
{
	register LONG	Len = 0;
	register UBYTE	c;

		// Check for prescroll/jump scroll

	if(CursorY == Bottom && Bottom > 0)
	{
			// The maximum number of line feeds we need to look for

		register WORD Max = MAX(Config -> EmulationConfig -> MaxJump,Config -> EmulationConfig -> MaxScroll);

			// Any prescroll/jump scroll action wanted?

		if(Max)
		{
			register WORD	 Count	= Size,
					 Lines	= 0;
			register UBYTE	*Buffer	= String;

				// Count the number of line feeds

			while(Count--)
			{
				if(*Buffer++ == '\n')
				{
					if(++Lines == Max)
						break;
				}
			}

				// Ok, so we have found something

			if(Lines)
			{
				register WORD Scroll,TotalLines;

					// Scroll by default?

				if(Scroll = Config -> EmulationConfig -> MaxJump)
				{
						// If there is more, use it

					if(Lines > Scroll)
						Scroll = Lines;
				}
				else
					Scroll = Lines;

					// How tall is the current scroll region?

				TotalLines = Bottom - Top + 1;

					// Don't scroll more than the entire screenful

				if(Scroll > TotalLines)
					Scroll = TotalLines;

					// Is it worth scrolling?

				if(Scroll > 1)
				{
						// Scroll the region...

					ScrollRegion(Scroll);

						// Reposition the cursor

					if(CursorY > Scroll)
						CursorY -= Scroll;
					else
						CursorY = 0;

					RepositionCursor();
				}
			}
		}
	}

	if(InSequence)
	{
		while(Size--)
		{
			c = *String++;

			if(!(*AbortTable[c])(c))
			{
				InSequence = FALSE;

				break;
			}
		}
	}

	if(Config -> TerminalConfig -> FontMode == FONT_STANDARD)
	{
		while(Size-- > 0)
		{
			if(IsPrintable[c = *String++])
			{
				if(SpecialTable[c])
				{
					if(Len)
					{
						(*ConDump)(ConTempBuffer,Len);

						Len = 0;
					}

					if(InSequence = (*SpecialTable[c])(c))
					{
						while(Size-- > 0)
						{
							c = *String++;

							if(!(*AbortTable[c])(c))
							{
								InSequence = FALSE;

								break;
							}
						}
					}
				}
				else
				{
					ConTempBuffer[Len] = c;

					if(Len++ == 512)
					{
						(*ConDump)(ConTempBuffer,Len);

						Len = 0;
					}
				}
			}
		}
	}
	else
	{
		while(Size-- > 0)
		{
			if(c = *String++)
			{
				if(SpecialTable[c])
				{
					if(Len)
					{
						(*ConDump)(ConTempBuffer,Len);

						Len = 0;
					}

					if(InSequence = (*SpecialTable[c])(c))
					{
						while(Size-- > 0)
						{
							c = *String++;

							if(!(*AbortTable[c])(c))
							{
								InSequence = FALSE;

								break;
							}
						}
					}
				}
				else
				{
					ConTempBuffer[Len] = c;

					if(Len++ == 512)
					{
						(*ConDump)(ConTempBuffer,Len);

						Len = 0;
					}
				}
			}
		}
	}

	if(Len)
		(*ConDump)(ConTempBuffer,Len);
}

	/* ConProcessDataHex7(register STRPTR String,register LONG Size):
	 *
	 *	Process data, hex mode flavour, seven bits.
	 */

VOID 
ConProcessDataHex7(register STRPTR String,register LONG Size)
{
	UBYTE	DummyBuffer[40],c;
	LONG	Fit,Spaces,Current;

		/* How many characters will fit into the line? */

	Fit	= (LastColumn + 1) / 4;

		/* Which position are we currently in? */

	Current	= CursorX / 3;

		/* Weird cursor position? */

	if(Current >= Fit || (CursorX % 3))
	{
		ConProcessData7("\r\n",2);

		Current = 0;
	}

		/* Check the font type. */

	if(Config -> TerminalConfig -> FontMode == FONT_STANDARD)
	{
		while(Size-- > 0)
		{
			c = (*String++) & 0x7F;

			Spaces = (Fit - Current) * 3 + Current - 3;

			if(c > ' ' && c < 127)
				SPrintf(DummyBuffer,"%02lx \33[%ldC%lc\33[%ldD",c,Spaces,c,Spaces + 1);
			else
				SPrintf(DummyBuffer,"%02lx \33[%ldC.\33[%ldD",c,Spaces,Spaces + 1);

			ConProcessData7(DummyBuffer,strlen(DummyBuffer));

			if(Current++ == Fit - 1)
			{
				Current = 0;

				ConProcessData7("\r\n",2);
			}
		}
	}
	else
	{
		while(Size-- > 0)
		{
			c = (*String++) & 0x7F;

			Spaces = (Fit - Current) * 3 + Current - 3;

			if(c && c != ' ')
				SPrintf(DummyBuffer,"%02lx \33[%ldC%lc\33[%ldD",c,Spaces,c,Spaces + 1);
			else
				SPrintf(DummyBuffer,"%02lx \33[%ldC.\33[%ldD",c,Spaces,Spaces + 1);

			ConProcessData7(DummyBuffer,strlen(DummyBuffer));

			if(Current++ == Fit - 1)
			{
				Current = 0;

				ConProcessData7("\r\n",2);
			}
		}
	}
}

	/* ConProcessDataHex8(register STRPTR String,register LONG Size):
	 *
	 *	Process data, hex mode flavour, eight bits.
	 */

VOID 
ConProcessDataHex8(register STRPTR String,register LONG Size)
{
	UBYTE	DummyBuffer[40],c;
	LONG	Fit,Spaces,Current;

		/* How many characters will fit into the line? */

	Fit	= (LastColumn + 1) / 4;

		/* Which position are we currently in? */

	Current	= CursorX / 3;

		/* Weird cursor position? */

	if(Current >= Fit || (CursorX % 3))
	{
		ConProcessData8("\r\n",2);

		Current = 0;
	}

		/* Check the font type. */

	if(Config -> TerminalConfig -> FontMode == FONT_STANDARD)
	{
		while(Size-- > 0)
		{
			c = *String++;

			Spaces = (Fit - Current) * 3 + Current - 3;

			if((c > ' ' && c < 127) || c > 160)
				SPrintf(DummyBuffer,"%02lx \33[%ldC%lc\33[%ldD",c,Spaces,c,Spaces + 1);
			else
				SPrintf(DummyBuffer,"%02lx \33[%ldC.\33[%ldD",c,Spaces,Spaces + 1);

			ConProcessData8(DummyBuffer,strlen(DummyBuffer));

			if(Current++ == Fit - 1)
			{
				Current = 0;

				ConProcessData8("\r\n",2);
			}
		}
	}
	else
	{
		while(Size-- > 0)
		{
			c = *String++;

			Spaces = (Fit - Current) * 3 + Current - 3;

			if(c && c != ' ')
				SPrintf(DummyBuffer,"%02lx \33[%ldC%lc\33[%ldD",c,Spaces,c,Spaces + 1);
			else
				SPrintf(DummyBuffer,"%02lx \33[%ldC.\33[%ldD",c,Spaces,Spaces + 1);

			ConProcessData8(DummyBuffer,strlen(DummyBuffer));

			if(Current++ == Fit - 1)
			{
				Current = 0;

				ConProcessData8("\r\n",2);
			}
		}
	}
}

	/* ConTransferHost(STRPTR Buffer,LONG Len):
	 *
	 *	Process data read from the serial line,
	 *	special XPR flavour.
	 */

VOID 
ConTransferHost(STRPTR Buffer,LONG Len)
{
	Len = XProtocolHostMon(XprIO,Buffer,Len,SerialBufferSize);

	if(TransferWindow)
		TransferCleanup();

	if(Len)
		ConProcess(Buffer,Len);
}

	/* ConsoleCommand(STRPTR String):
	 *
	 *	Just like SerialCommand(), but addresses the local
	 *	terminal side.
	 */

VOID 
ConsoleCommand(STRPTR String)
{
	STATIC UBYTE __far LocalBuffer[256];

	LONG	Count = 0,i,Len = strlen(String);

	BYTE	GotControl	= FALSE,
		GotEscape	= FALSE;

		/* Scan the string. */

	for(i = 0 ; i < Len ; i++)
	{
			/* We are looking for plain characters
			 * and the control ('\') and escape
			 * ('^') characters.
			 */

		if(!GotControl && !GotEscape)
		{
				/* Got a control character,
				 * the next byte will probably be
				 * a command sequence.
				 */

			if(String[i] == '\\')
			{
				GotControl = TRUE;

				continue;
			}

				/* Got an escape character,
				 * the next byte will be some
				 * kind of control character
				 * (such as XON, XOF, bell, etc.).
				 */

			if(String[i] == '^')
			{
				GotEscape = TRUE;

				continue;
			}

				/* This tells us to wait another
				 * second before continuing with
				 * the scanning.
				 */

			if(String[i] == '~')
				continue;

				/* Stuff the character into the
				 * buffer.
				 */

			LocalBuffer[Count++] = String[i];
		}
		else
		{
				/* Convert the character to a control
				 * style character (^C, etc.).
				 */

			if(GotEscape)
			{
				if(ToUpper(String[i]) >= 'A' && ToUpper(String[i]) <= '_')
					LocalBuffer[Count++] = ToUpper(String[i]) - '@';
				else
					LocalBuffer[Count++] = String[i];

				GotEscape = FALSE;
			}

				/* The next character represents a command. */

			if(GotControl)
			{
				switch(ToUpper(String[i]))
				{
						/* Translate code. */

					case '*':

						i++;

						while(i < Len && String[i] == ' ')
							i++;

						if(i < Len)
						{
							UBYTE DummyBuffer[5],j = 0,Char;

							if(String[i] >= '0' && String[i] <= '9')
							{
								while(j < 3 && i < Len)
								{
									Char = String[i++];

									if(Char >= '0' && Char <= '9')
										DummyBuffer[j++] = Char;
									else
									{
										i--;

										break;
									}
								}
							}
							else
							{
								while(j < 4 && i < Len)
								{
									Char = ToLower(String[i++]);

									if((Char >= '0' && Char <= '9') || (Char >= 'a' && Char <= 'z'))
										DummyBuffer[j++] = Char;
									else
									{
										i--;

										break;
									}
								}
							}

							DummyBuffer[j] = 0;

							LocalBuffer[Count++] = NameToCode(DummyBuffer);
						}

						i--;

						break;

						/* Execute an AmigaDOS command. */

					case 'D':

						return;

						/* Execute an ARexx command. */

					case 'A':

						return;

						/* Add the control character ('\'). */

					case '\\':

						LocalBuffer[Count++] = '\\';
						break;

						/* This is a backspace. */

					case 'B':

						LocalBuffer[Count++] = '\b';
						break;

						/* This is a form feed. */

					case 'F':

						LocalBuffer[Count++] = '\f';
						break;

						/* This is a line feed. */

					case 'N':

						LocalBuffer[Count++] = '\n';
						break;

						/* Send the current password. */

					case 'P':

						break;

						/* This is a carriage return. */

					case 'R':

						LocalBuffer[Count++] = '\r';
						break;

						/* This is a tab. */

					case 'T':

						LocalBuffer[Count++] = '\t';
						break;

						/* Send the current user name. */

					case 'U':

						break;

						/* Send a break across the serial line. */

					case 'X':

						break;

						/* Feed the contents of the
						 * clipboard into the input
						 * stream.
						 */

					case 'I':

						if(Count)
							ConProcess(LocalBuffer,Count);

						Count = LoadClip(LocalBuffer,256);

						break;

						/* Send a string to the clipboard. */

					case 'G':

						if(String[i + 1])
							SaveClip(&String[i + 1],strlen(&String[i + 1]));

						return;

						/* Produce the escape character. */

					case 'E':

						LocalBuffer[Count++] = ESC;
						break;

						/* Call a menu item. */

					case 'C':

						break;

						/* Stuff the character into the buffer. */

					default:

						LocalBuffer[Count++] = String[i];
						break;
				}

				GotControl = FALSE;
			}
		}

			/* If the buffer is full, release it. */

		if(Count == 256)
		{
			ConProcess(LocalBuffer,Count);

			Count = 0;
		}
	}

	if(Count)
		ConProcess(LocalBuffer,Count);
}

	/* ConBypass(STRPTR String,LONG Size):
	 *
	 *	Bypass the `normal' ConOutput() data processing.
	 */

VOID __regargs
ConBypass(STRPTR String,LONG Size)
{
	if(Size < 0)
		Size = strlen(String);

	if(Size > 0)
	{
		VOID (* ProcessData)(STRPTR,LONG);

		if(Config -> SerialConfig -> StripBit8)
			ProcessData = ConProcessData7;
		else
			ProcessData = ConProcessData8;

		ObtainSemaphore(&TerminalSemaphore);

		if(Marking)
			DropMarker();

		ClearCursor();

		(*ProcessData)(String,Size);

		DrawCursor();

		ReleaseSemaphore(&TerminalSemaphore);
	}
}

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

VOID 
ConProcess(register STRPTR String,register LONG Size)
{
		/* Feed the flow filter. */

	if(UseFlow)
	{
		if(Config -> SerialConfig -> StripBit8)
			FlowFilter(String,Size,0x7F);
		else
			FlowFilter(String,Size,0xFF);
	}

		/* 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)
	{
			/* Capture the data. */

		if(CaptureData)
			(*CaptureData)(String,Size);

			/* Remember the data. */

		if(RememberOutput)
			RememberOutputText(String,Size);

			/* Check the traps if necessary. */

		if(WatchTraps)
			TrapFilter(String,Size);

		if(Marking)
			DropMarker();

		if(TerminalQueue)
		{
			struct DataMsg *Msg;

			if(Msg = (struct DataMsg *)CreateMsgItem(sizeof(struct DataMsg) + Size))
			{
				Msg -> Data = (APTR)(Msg + 1);
				Msg -> Size = Size;

				CopyMem(String,Msg -> Data,Msg -> Size);

				PutMsgItem(TerminalQueue,Msg);
			}
			else
			{
				ClearCursor();

				(*ConProcessData)(String,Size);

				DrawCursor();
			}
		}
		else
		{
			ClearCursor();

			(*ConProcessData)(String,Size);

			DrawCursor();
		}
	}
}

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

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

	if(Marking)
		DropMarker();

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

	ConProcess(SharedBuffer,strlen(SharedBuffer));
}

	/* FlowInit():
	 *
	 *	Set up the data flow parser. The parser scans the serial
	 *	output data for more or less interesting modem output
	 *	(carrier lost, connect, etc.).
	 */

VOID __regargs
FlowInit(BYTE FullReset)
{
	WORD i;

		/* Set up `NO CARRIER' message. */

	if(Config -> ModemConfig -> NoCarrier[0])
		SPrintf(AttentionBuffers[SCAN_NOCARRIER],"%s\r",Config -> ModemConfig -> NoCarrier);
	else
		AttentionBuffers[SCAN_NOCARRIER][0] = 0;

		/* Set up ZRQINIT message. */

	strcpy(AttentionBuffers[SCAN_ZMODEM],"*\030B01");

		/* Set up `CONNECT' message. */

	strcpy(AttentionBuffers[SCAN_CONNECT],Config -> ModemConfig -> Connect);

		/* Set up `VOICE' message. */

	if(Config -> ModemConfig -> Voice[0])
		SPrintf(AttentionBuffers[SCAN_VOICE],"%s\r",Config -> ModemConfig -> Voice);
	else
		AttentionBuffers[SCAN_VOICE][0] = 0;

		/* Set up `RING' message. */

	if(Config -> ModemConfig -> Ring[0])
		SPrintf(AttentionBuffers[SCAN_RING],"%s\r",Config -> ModemConfig -> Ring);
	else
		AttentionBuffers[SCAN_RING][0] = 0;

		/* Set up `BUSY' message. */

	if(Config -> ModemConfig -> Busy[0])
		SPrintf(AttentionBuffers[SCAN_BUSY],"%s\r",Config -> ModemConfig -> Busy);
	else
		AttentionBuffers[SCAN_BUSY][0] = 0;

		/* Set up `NO DIALTONE' message. */

	if(Config -> ModemConfig -> NoDialTone[0])
		SPrintf(AttentionBuffers[SCAN_NODIALTONE],"%s\r",Config -> ModemConfig -> NoDialTone);
	else
		AttentionBuffers[SCAN_NODIALTONE][0] = 0;

		/* Set up `OK' message. */

	if(Config -> ModemConfig -> Ok[0])
		SPrintf(AttentionBuffers[SCAN_OK],"%s\r",Config -> ModemConfig -> Ok);
	else
		AttentionBuffers[SCAN_OK][0] = 0;

		/* Set up `ERROR' message. */

	if(Config -> ModemConfig -> Error[0])
		SPrintf(AttentionBuffers[SCAN_ERROR],"%s\r",Config -> ModemConfig -> Error);
	else
		AttentionBuffers[SCAN_ERROR][0] = 0;

		/* Reset match counter. */

	FlowCount = 0;

		/* Reset indices. */

	memset(AttentionCount,0,sizeof(AttentionCount));

		/* Determine lengths. */

	for(i = 0 ; i < SCAN_COUNT ; i++)
		AttentionLength[i] = strlen(AttentionBuffers[i]);

	if(FullReset)
	{
			/* No, we are not yet looking for a baud rate. */

		BaudPending = FALSE;

			/* Reset the flags. */

		memset(&FlowInfo,FALSE,sizeof(struct FlowInfo));
	}

		/* Is it safe to disable the flow filter? */

	ObtainSemaphore(&OnlineSemaphore);

	if(!FullCheck && Online && Config -> SerialConfig -> CheckCarrier && !Config -> MiscConfig -> AutoUpload)
		UseFlow = FALSE;
	else
		UseFlow = TRUE;

	ReleaseSemaphore(&OnlineSemaphore);

		/* Full data check is a lot slower than looking for
		 * just a single sequence (such as the `CONNECT'
		 * below). This mode is reserved for the dial panel.
		 */

	if(FullCheck)
	{
		ScanStart	= 0;
		ScanEnd		= SCAN_COUNT - 1;
	}
	else
	{
		ObtainSemaphore(&OnlineSemaphore);

		if(Online)
		{
			ScanStart = SCAN_NOCARRIER;

			if(UsesZModem)
				ScanEnd = SCAN_ZMODEM;
			else
				ScanEnd = SCAN_NOCARRIER;
		}
		else
		{
			if(UsesZModem)
				ScanStart = SCAN_ZMODEM;
			else
				ScanStart = SCAN_CONNECT;

			ScanEnd = SCAN_RING;
		}

		ReleaseSemaphore(&OnlineSemaphore);
	}
}

	/* 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 TextMode;
	LONG SameMode;

		/* Determine current text rendering mode. */

	if(GfxTable[Buffer[0]] == MODE_GFX)
		TextMode = MODE_GFX;
	else
	{
		TextMode = MODE_STANDARD;

		SetFont(RPort,TextFont);
	}

		/* Reset number of characters in common mode. */

	SameMode = 0;

		/* Scan until all input is processed. */

	FOREVER
	{
			/* Scan for characters in the current mode. */

		while(GfxTable[Buffer[SameMode]] == TextMode && SameMode < Length)
			SameMode++;

			/* Output the text found. */

		Text(RPort,Buffer,SameMode);

			/* Decrement number of remaining bytes. */

		Length -= SameMode;

			/* Anything left? */

		if(Length)
		{
				/* Skip to next character. */

			Buffer		+= SameMode;
			SameMode	 = 0;

				/* Change text output mode. */

			if(TextMode == MODE_GFX)
			{
				SetFont(RPort,TextFont);

				TextMode = MODE_STANDARD;
			}
			else
			{
				SetFont(RPort,GFX);

				TextMode = MODE_GFX;
			}
		}
		else
			break;
	}

		/* Reset font type. */

	if(TextMode == MODE_STANDARD)
		SetFont(RPort,GFX);
}

	/* ConvertKey():
	 *
	 *	The actual key conversion routine.
	 */

STATIC LONG __inline
ConvertKey(ULONG Qualifier,UWORD Code,ULONG Prev,STRPTR Buffer,LONG Len)
{
	LONG Actual;

		/* Fill in the defaults. */

	FakeInputEvent -> ie_Code		= Code;
	FakeInputEvent -> ie_Qualifier		= Qualifier;
	FakeInputEvent -> ie_position . ie_addr	= (APTR)Prev;

		/* Clear the buffer (sortof). */

	Buffer[0] = 0;

		/* Convert the codes. */

	if((Actual = RawKeyConvert(FakeInputEvent,Buffer,Len,KeyMap)) > 0)
		return(Actual);
	else
		return(0);
}

	/* ConvertTheKey(STRPTR Buffer,LONG *Len,UWORD Code,UWORD Qualifier,ULONG Prev):
	 *
	 *	Much more simplified raw key conversion routine.
	 */

UBYTE __regargs
ConvertTheKey(STRPTR Buffer,LONG *Len,UWORD Code,UWORD Qualifier,ULONG Prev)
{
	if(Buffer)
		Buffer[0] = 0;

	if(Len)
		*Len = 0;

		/* Key was pressed, not released. */

	if(!(Code & IECODE_UP_PREFIX))
	{
			/* These are the sequences mapped to special
			 * control keys (cursor keys, function keys,
			 * the help key).
			 */

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

			(STRPTR)"?~",	HLP,

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

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

		UBYTE	ConvertBuffer[257];
		LONG	Actual;

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

		if(Code >= CURSOR_UP_CODE && Code <= F10_CODE)
			Qualifier = NULL;
		else
		{
				/* Does it have a numeric keypad qualifier set? */

			if((Qualifier & (IEQUALIFIER_NUMERICPAD | IEQUALIFIER_CONTROL)) == (IEQUALIFIER_NUMERICPAD | IEQUALIFIER_CONTROL))
			{
				STRPTR String = NULL;

					/* Look at the vanilla result. */

				if(ConvertKey(Qualifier & ~(IEQUALIFIER_CONTROL | IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT | IEQUALIFIER_LALT | IEQUALIFIER_RALT),Code,Prev,ConvertBuffer,1))
				{
						/* Take a look at the results. */

					switch(ConvertBuffer[0])
					{
						case '(':
						case '[':
						case '{':

							String = "\033OP";
							break;

						case ')':
						case ']':
						case '}':

							String = "\033OQ";
							break;

						case '/':

							String = "\033OR";
							break;

						case '*':

							String = "\033OS";
							break;

						default:

							String = NULL;
							break;
					}
				}

				if(!String)
				{
					switch(Code)
					{
						case 0x5A:

							String = "\033OP";
							break;

						case 0x5B:

							String = "\033OQ";
							break;

						case 0x5C:

							String = "\033OR";
							break;

						case 0x5D:

							String = "\033OS";
							break;
					}
				}

					/* Return the PFx key code. */

				if(String)
				{
					if(Buffer)
						memcpy(Buffer,String,3);

					if(Len)
						*Len = 3;

					return('\033');
				}
			}

				/* Does it have a shift qualifier set? */

			if(Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
			{
					/* Do the conversion... */

				if(ConvertKey(Qualifier & ~(IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT),Code,Prev,ConvertBuffer,1))
				{
						/* Did it produce a tab key? If so, transfer
						 * Esc tab instead.
						 */

					if(ConvertBuffer[0] == '\t')
					{
						if(Len)
							*Len = 2;

						if(Buffer)
							memcpy(Buffer,"\033\t",2);

						return('\033');
					}
				}
			}

				/* Does it have the control qualifier set? */

			if(Qualifier & IEQUALIFIER_CONTROL)
			{
					/* Do the conversion... */

				if(ConvertKey(Qualifier & ~IEQUALIFIER_CONTROL,Code,Prev,ConvertBuffer,1))
				{
						/* Did it produce a space or an `at' sign? */

					switch(ConvertBuffer[0])
					{
							/* NUL */

						case ' ':
						case '@':
						case '2':

							if(Len)
								*Len = 1;

							if(Buffer)
								Buffer[0] = 0;

							return(0);

							/* Escape */

						case '3':
						case '[':

							if(Len)
								*Len = 1;

							if(Buffer)
								Buffer[0] = 0x1B;

							return(0x1B);

							/* FS */

						case '4':
						case '/':

							if(Len)
								*Len = 1;

							if(Buffer)
								Buffer[0] = 0x1C;

							return(0x1C);

							/* GS */

						case '5':
						case ']':

							if(Len)
								*Len = 1;

							if(Buffer)
								Buffer[0] = 0x1D;

							return(0x1D);

							/* RS */

						case '6':
						case '~':

							if(Len)
								*Len = 1;

							if(Buffer)
								Buffer[0] = 0x1E;

							return(0x1E);


							/* US */

						case '7':
						case '?':

							if(Len)
								*Len = 1;

							if(Buffer)
								Buffer[0] = 0x1F;

							return(0x1F);
					}
				}
			}
		}

			/* Do the final conversion... */

		if(Actual = ConvertKey(Qualifier,Code,Prev,ConvertBuffer,256))
		{
				/* Are we to swap the backspace and
				 * delete keys?
				 */

			if(Config -> EmulationConfig -> SwapBSDelete)
			{
				register WORD i;

				for(i = 0 ; i < Actual ; i++)
				{
					if(ConvertBuffer[i] == BKS)
						ConvertBuffer[i] = DEL;
					else
					{
						if(ConvertBuffer[i] == DEL)
							ConvertBuffer[i] = BKS;
					}
				}
			}

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

			if(ConvertBuffer[0] == CSI)
			{
				register WORD i;

				for(i = 0 ; i < sizeof(SeqLens) ; i++)
				{
						/* Does it match? */

					if(!memcmp(&ConvertBuffer[1],ConversionTable[i] . RawCode,SeqLens[i]))
					{
							/* Store the length. */

						if(Len)
							*Len = 1;

							/* Store the result. */

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

						return(ConversionTable[i] . Result);
					}
				}
			}

				/* Store the number of characters converted. */

			if(Len)
				*Len = Actual;

				/* Store the converted characters. */

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

			return(ConvertBuffer[0]);
		}
	}

	return(0);
}

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

UBYTE __regargs
KeyConvert(struct IntuiMessage *Massage,STRPTR Buffer,LONG *Len)
{
		/* Is this really a keyboard event? */

	if(Massage -> Class == IDCMP_RAWKEY)
		return(ConvertTheKey(Buffer,Len,Massage -> Code,Massage -> Qualifier,*(ULONG *)Massage -> IAddress));
	else
	{
		if(Buffer)
			Buffer[0] = 0;

		if(Len)
			*Len = 0;

		return(0);
	}
}

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

BYTE 
DoBackspace()
{
	if(CursorX)
	{
		WORD DeltaX,MinX;

		CursorX--;

			// What backspace mode are we in?

		switch(Config -> EmulationConfig -> DestructiveBackspace)
		{
				// Do nothing

			case 0:

				RepositionCursor();
				break;

				// Shift the line to the left

			case 2:

				BackupRender();

				RasterEraseCharacters(1);

				if(Config -> EmulationConfig -> FontScale == SCALE_NORMAL)
				{
					if(RasterAttr[CursorY] == SCALE_ATTR_NORMAL)
					{
						DeltaX	= TextFontWidth;
						MinX	= MUL_X(CursorX);
					}
					else
					{
						DeltaX	= TextFontWidth / 2;
						MinX	= MUL_X(CursorX) / 2;
					}
				}
				else
				{
					if(RasterAttr[CursorY] == SCALE_ATTR_NORMAL)
					{
						DeltaX	= TextFontWidth / 2;
						MinX	= MUL_X(CursorX) / 2;
					}
					else
					{
						DeltaX	= TextFontWidth;
						MinX	= MUL_X(CursorX);
					}
				}

				ScrollLineEraseCharacters(1);

				ScrollLineRaster(RPort,DeltaX,0,MinX,MUL_Y(CursorY),LastPixel,MUL_Y(CursorY + 1) - 1,FALSE);

				BackupRender();

				RepositionCursor();

				break;

				// Clear the character below the cursor

			default:

				RepositionCursor();

				ObtainSemaphore(RasterSemaphore);

				Raster[CursorY * RasterWidth + CursorX] = ' ';

				ReleaseSemaphore(RasterSemaphore);

				BackupRender();

				if(FontScalingRequired)
					PrintScaled(" ",1,CurrentFontScale);
				else
					Text(RPort," ",1);

				BackupRender();

				break;
		}
	}

	return(FALSE);
}

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

BYTE 
DoBeep()
{
	BellSignal();

	return(FALSE);
}

	/* DoxON():
	 *
	 *	Perform XON (stop data flow).
	 */

BYTE 
DoxON()
{
	if(Config -> SerialConfig -> xONxOFF)
		Status = STATUS_HOLDING;

	return(FALSE);
}

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

BYTE 
DoLF()
{
	DownLine();

	RepositionCursor();

	return(FALSE);
}

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

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

	if(CharMode[1] == TABLE_ASCII)
		CurrentFont = TextFont;

	SetFont(RPort,CurrentFont);

	ConOutputUpdate();

	Charset = 1;

	return(FALSE);
}

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

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

	if(CharMode[0] == TABLE_ASCII)
		CurrentFont = TextFont;

	SetFont(RPort,CurrentFont);

	ConOutputUpdate();

	Charset = 0;

	return(FALSE);
}

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

BYTE 
DoCR_LF()
{
	CursorX = 0;

	DownLine();

	RepositionCursor();

	return(FALSE);
}

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

BYTE 
DoFF()
{
	if(Config -> EmulationConfig -> NewLineMode)
	{
		CursorX = 0;

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

		CursorX = CursorY = 0;

		RepositionCursor();

		ConFontScaleUpdate();
	}

	return(FALSE);
}

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

BYTE 
DoLF_FF_VT()
{
	if(Config -> EmulationConfig -> NewLineMode)
		DoCR_LF();
	else
		DoLF();

	return(FALSE);
}

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

BYTE 
DoCR()
{
	if(Config -> EmulationConfig -> NewLineMode)
		DoCR_LF();
	else
	{
		CursorX = 0;

		RepositionCursor();
	}

	return(FALSE);
}

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

BYTE 
DoTab()
{
	WORD Column;

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

	if(Config -> EmulationConfig -> LineWrap)
	{
		if(CursorX >= LastColumn)
		{
			CursorX = 0;

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

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

			if(TabStops[CursorX])
				break;
		}
	}

	RepositionCursor();

	return(FALSE);
}

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

BYTE 
DoEnq()
{
	if(Config -> EmulationConfig -> AnswerBack[0])
		SerialCommand(Config -> EmulationConfig -> AnswerBack);

	return(FALSE);
}

	/* DoEsc():
	 *
	 *	Start new control sequence.
	 */

BYTE 
DoEsc()
{
	if(Config -> TerminalConfig -> EmulationMode == EMULATION_TTY)
		(* ConDump)("^",1);

	return(TRUE);
}

	/* DoCsi():
	 *
	 *	Start new control sequence.
	 */

BYTE 
DoCsi()
{
	if(Config -> TerminalConfig -> EmulationMode == EMULATION_TTY)
		(* ConDump)("^[",2);

	ParseCode('[');

	return(TRUE);
}

	/* DoNewEsc(UBYTE Char):
	 *
	 *	Start new control sequence.
	 */

BYTE 
DoNewEsc(UBYTE Char)
{
	if(Config -> TerminalConfig -> EmulationMode == EMULATION_TTY)
		(* ConDump)("^",1);

	DoCancel();

	return(TRUE);
}

	/* DoNewCsi(UBYTE Char):
	 *
	 *	Start new control sequence.
	 */

BYTE 
DoNewCsi(UBYTE Char)
{
	if(Config -> TerminalConfig -> EmulationMode == EMULATION_TTY)
		(* ConDump)("^[",2);

	DoCancel();

	return(ParseCode('['));
}

STATIC STRPTR	CR_Translation,
		LF_Translation;
STATIC WORD	CR_Trans_Len,
		LF_Trans_Len;

STATIC LONG __regargs
Translate_CR_LF_1(register STRPTR Data,register LONG Size)
{
	register STRPTR	String	= Data;
	register LONG	Count	= 0;
	register UBYTE	c;

	while(Size--)
	{
		switch(c = *Data++)
		{
			case '\r':

				if(CR_Trans_Len)
				{
					*String++ = *CR_Translation;

					Count++;
				}

				break;

			case '\n':

				if(LF_Trans_Len)
				{
					*String++ = *LF_Translation;

					Count++;
				}

				break;

			default:

				*String++ = c;

				Count++;

				break;
		}
	}

	return(Count);
}

STATIC LONG __regargs
Translate_CR_LF_2x2(register STRPTR Data,register LONG Size)
{
	register STRPTR	String	= Data;
	register LONG	Count	= 0;
	register UBYTE	c;

	/* ALWAYS */
	{
		register BOOL GotIt = FALSE;
		register LONG i;

		for(i = 0 ; i < Size ; i++)
		{
			if(String[i] == '\r' || String[i] == '\n')
			{
				GotIt = TRUE;

				break;
			}
		}

		if(GotIt)
		{
			CopyMem(Data,StripBuffer,Size);

			String	= Data;
			Data	= StripBuffer;
		}
		else
			return(Size);
	}

	while(Size--)
	{
		switch(c = *Data++)
		{
			case '\r':

				if(CR_Trans_Len)
				{
					register WORD i;

					for(i = 0 ; i < CR_Trans_Len ; i++)
					{
						*String++ = CR_Translation[i];

						Count++;
					}
				}

				break;

			case '\n':

				if(LF_Trans_Len)
				{
					register WORD i;

					for(i = 0 ; i < LF_Trans_Len ; i++)
					{
						*String++ = LF_Translation[i];

						Count++;
					}
				}

				break;

			default:

				*String++ = c;

				Count++;

				break;
		}
	}

	return(Count);
}

STATIC LONG __regargs
Translate_CR_LF_2LF(register STRPTR Data,register LONG Size)
{
	register STRPTR	String	= Data;
	register LONG	Count	= 0;
	register UBYTE	c;

	/* ALWAYS */
	{
		register BOOL GotIt = FALSE;
		register LONG i;

		for(i = 0 ; i < Size ; i++)
		{
			if(String[i] == '\n')
			{
				GotIt = TRUE;

				break;
			}
		}

		if(GotIt)
		{
			CopyMem(Data,StripBuffer,Size);

			String	= Data;
			Data	= StripBuffer;
		}
		else
			return(Size);
	}

	while(Size--)
	{
		switch(c = *Data++)
		{
			case '\r':

				if(CR_Trans_Len)
				{
					register WORD i;

					for(i = 0 ; i < CR_Trans_Len ; i++)
					{
						*String++ = CR_Translation[i];

						Count++;
					}
				}

				break;

			case '\n':

				if(LF_Trans_Len)
				{
					register WORD i;

					for(i = 0 ; i < LF_Trans_Len ; i++)
					{
						*String++ = LF_Translation[i];

						Count++;
					}
				}

				break;

			default:

				*String++ = c;

				Count++;

				break;
		}
	}

	return(Count);
}

STATIC LONG __regargs
Translate_CR_LF_2CR(register STRPTR Data,register LONG Size)
{
	register STRPTR	String	= Data;
	register LONG	Count	= 0;
	register UBYTE	c;

	/* ALWAYS */
	{
		register BOOL GotIt = FALSE;
		register LONG i;

		for(i = 0 ; i < Size ; i++)
		{
			if(String[i] == '\r')
			{
				GotIt = TRUE;

				break;
			}
		}

		if(GotIt)
		{
			CopyMem(Data,StripBuffer,Size);

			String	= Data;
			Data	= StripBuffer;
		}
		else
			return(Size);
	}

	while(Size--)
	{
		switch(c = *Data++)
		{
			case '\r':

				if(CR_Trans_Len)
				{
					register WORD i;

					for(i = 0 ; i < CR_Trans_Len ; i++)
					{
						*String++ = CR_Translation[i];

						Count++;
					}
				}

				break;

			case '\n':

				if(LF_Trans_Len)
				{
					register WORD i;

					for(i = 0 ; i < LF_Trans_Len ; i++)
					{
						*String++ = LF_Translation[i];

						Count++;
					}
				}

				break;

			default:

				*String++ = c;

				Count++;

				break;
		}
	}

	return(Count);
}

VOID
Update_CR_LF_Translation()
{
	Forbid();

	if(Config -> TerminalConfig -> ReceiveCR == EOL_CRLF || Config -> TerminalConfig -> ReceiveCR == EOL_LFCR || Config -> TerminalConfig -> ReceiveLF == EOL_LFCR || Config -> TerminalConfig -> ReceiveLF == EOL_CRLF)
		SerialBufferSize = Config -> SerialConfig -> SerialBufferSize / 2;
	else
		SerialBufferSize = Config -> SerialConfig -> SerialBufferSize;

	switch(Config -> TerminalConfig -> ReceiveCR)
	{
		case EOL_IGNORE:

			CR_Trans_Len = 0;
			break;

		case EOL_CR:

			CR_Trans_Len	= 1;
			CR_Translation	= "\r";
			break;

		case EOL_LF:

			CR_Trans_Len	= 1;
			CR_Translation	= "\n";
			break;

		case EOL_CRLF:

			CR_Trans_Len	= 2;
			CR_Translation	= "\r\n";
			break;

		case EOL_LFCR:

			CR_Trans_Len	= 2;
			CR_Translation	= "\n\r";
			break;
	}

	switch(Config -> TerminalConfig -> ReceiveLF)
	{
		case EOL_IGNORE:

			LF_Trans_Len = 0;
			break;

		case EOL_LF:

			LF_Trans_Len	= 1;
			LF_Translation	= "\n";
			break;

		case EOL_CR:

			LF_Trans_Len	= 1;
			LF_Translation	= "\r";
			break;

		case EOL_LFCR:

			LF_Trans_Len	= 2;
			LF_Translation	= "\n\r";
			break;

		case EOL_CRLF:

			LF_Trans_Len	= 2;
			LF_Translation	= "\r\n";
			break;
	}

	if(Config -> TerminalConfig -> ReceiveCR == EOL_CR && Config -> TerminalConfig -> ReceiveLF == EOL_LF)
		Translate_CR_LF = NULL;
	else
	{
		if(LF_Trans_Len <= 1 && CR_Trans_Len <= 1)
			Translate_CR_LF = Translate_CR_LF_1;
		else
		{
			if(LF_Trans_Len == 2 && CR_Trans_Len == 2)
				Translate_CR_LF = Translate_CR_LF_2x2;
			else
			{
				if(LF_Trans_Len == 2)
					Translate_CR_LF = Translate_CR_LF_2LF;
				else
				{
					if(CR_Trans_Len == 2)
						Translate_CR_LF = Translate_CR_LF_2CR;
					else
						Translate_CR_LF = Translate_CR_LF_1;
				}
			}
		}
	}

	Permit();
}
