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

#include "termGlobal.h"

	/* A private message type. */

struct EmulationMessage
{
	struct Message	ExecMessage;
	STRPTR		Buffer;
	LONG		Size;
};

	/* Hints for the data flow scanner. */

STATIC WORD	ScanStart,
		ScanEnd;

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

	/* FlowFilter8(register STRPTR Data,register LONG Size):
	 *
	 *	Data flow filter, eight bits.
	 */

VOID __regargs
FlowFilter8(register STRPTR Data,register LONG Size)
{
	register UBYTE c;

		/* Run until done. */

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

				/* We already got a `CONNECT' and the
				 * `connect auto-baud' feature is enabled.
				 * Continue scanning the serial output
				 * data for the actual baud rate.
				 */

			if(BaudPending)
			{
				LONG Rate;

				if(c < ' ')
				{
					if(BaudCount)
						BaudCount--;

					while(BaudCount > 0 && BaudBuffer[BaudCount] == ' ')
						BaudCount--;

					BaudBuffer[BaudCount + 1] = 0;

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

					BaudPending = FALSE;

					if(Rate = GetBaudRate(BaudBuffer))
						DTERate = Rate;
				}
				else
				{
					if(c != ' ' || BaudCount)
					{
						BaudBuffer[BaudCount++] = c;

						if(BaudCount == 79)
						{
							BaudCount--;

							while(BaudCount > 0 && BaudBuffer[BaudCount] == ' ')
								BaudCount--;

							BaudBuffer[BaudCount + 1] = 0;

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

							BaudPending = FALSE;

							if(Rate = GetBaudRate(BaudBuffer))
								DTERate = Rate;
						}
					}
				}
			}
			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 probable
							 * match.
							 */

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

							if(c == AttentionBuffers[i][FlowCount])
							{
								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(Config . SerialConfig . CheckCarrier)
											{
												WriteRequest -> IOSer . io_Command = SDCMD_QUERY;

												DoIO(WriteRequest);

													/* Are we to take a look at the carrier bit
													 * and if so, is the carrier still
													 * present?
													 */

												if(!(WriteRequest -> io_Status & (1 << 5)))
													break;
											}

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

											WasOnline = Online;

											Online = FALSE;

												/* Clear the password. */

											Password[0] = 0;
											UserName[0] = 0;

											if(WasOnline)
											{
												StopCall(FALSE);

												if(CurrentPay && ChosenEntry)
													LogAction(LocaleString(MSG_TERMAUX_CARRIER_LOST_COST_TXT),CreateSum(CurrentPay,TRUE));
												else
													LogAction(LocaleString(MSG_TERMAUX_CARRIER_LOST_TXT));

												Say(LocaleString(MSG_TERMAUX_CARRIER_LOST_TXT));

												SetDialMenu(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:

											WasOnline = Online;

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

												BaudPending	= TRUE;
												BaudCount	= 0;
											}

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

	/* FlowFilter7(register STRPTR Data,register LONG Size):
	 *
	 *	Data flow filter, seven bits.
	 */

VOID __regargs
FlowFilter7(register STRPTR Data,register LONG Size)
{
	register UBYTE c;

		/* Run until done. */

	do
	{
		if(c = *Data++ & 0x7F)
		{

				/* We already got a `CONNECT' and the
				 * `connect auto-baud' feature is enabled.
				 * Continue scanning the serial output
				 * data for the actual baud rate.
				 */

			if(BaudPending)
			{
				LONG Rate;

				if(c < ' ')
				{
					if(BaudCount)
						BaudCount--;

					while(BaudCount > 0 && BaudBuffer[BaudCount] == ' ')
						BaudCount--;

					BaudBuffer[BaudCount + 1] = 0;

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

					BaudPending = FALSE;

					if(Rate = GetBaudRate(BaudBuffer))
						DTERate = Rate;
				}
				else
				{
					if(c != ' ' || BaudCount)
					{
						BaudBuffer[BaudCount++] = c;

						if(BaudCount == 79)
						{
							BaudCount--;

							while(BaudCount > 0 && BaudBuffer[BaudCount] == ' ')
								BaudCount--;

							BaudBuffer[BaudCount + 1] = 0;

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

							BaudPending = FALSE;

							if(Rate = GetBaudRate(BaudBuffer))
								DTERate = Rate;
						}
					}
				}
			}
			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 probable
							 * match.
							 */

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

							if(c == AttentionBuffers[i][FlowCount])
							{
								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(Config . SerialConfig . CheckCarrier)
											{
												WriteRequest -> IOSer . io_Command = SDCMD_QUERY;

												DoIO(WriteRequest);

													/* Are we to take a look at the carrier bit
													 * and if so, is the carrier still
													 * present?
													 */

												if(!(WriteRequest -> io_Status & (1 << 5)))
													break;
											}

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

											WasOnline = Online;

											Online = FALSE;

												/* Clear the password. */

											Password[0] = 0;
											UserName[0] = 0;

											if(WasOnline)
											{
												StopCall(FALSE);

												if(CurrentPay && ChosenEntry)
													LogAction(LocaleString(MSG_TERMAUX_CARRIER_LOST_COST_TXT),CreateSum(CurrentPay,TRUE));
												else
													LogAction(LocaleString(MSG_TERMAUX_CARRIER_LOST_TXT));

												Say(LocaleString(MSG_TERMAUX_CARRIER_LOST_TXT));

												SetDialMenu(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:

											WasOnline = Online;

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

												BaudPending	= TRUE;
												BaudCount	= 0;
											}

											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;

					Src++;

					break;

			case '\r':	Src++;

					break;

			case ESC:	HasESC = TRUE;

					Src++;

					break;

			case CSI:	HasCSI = TRUE;

					Src++;

					break;

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

						Size++;
					}

					break;
		}
	}

	return(Size);
}

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

STATIC VOID __regargs
ConWrite(STRPTR Buffer,LONG Size)
{
	if(ControllerActive)
	{
		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
	{
		BYTE Real;

		if(Config . CaptureConfig . CaptureFilterMode || !BufferFrozen || PrinterCapture)
			Real = TRUE;
		else
			Real = FALSE;

		if(EmulationPort && !Real)
		{
			EmulationRequest = EmulationWriteRequest;

			SendEmulation(Buffer,Size);
		}
		else
		{
			UBYTE	Scale,CharScale;
			WORD	Offset,LastChar;

			EmulationRequest = WriteRequest;

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

			ClearCursor();

			Scale = RasterAttr[CursorY];

			if(Config . EmulationConfig . 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;
				}
			}

				/* Is any capture function active? */

			if(Real)
			{
				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 . EmulationConfig . LineWrap)
							{
									/* Move to beginning of next line. */

								CursorX = 0;

								DownLine();

								Capture("\n",1);

								Scale = SCALE_ATTR_NORMAL;

								CharScale = (Config . EmulationConfig . 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 . EmulationConfig . InsertMode)
								{
									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 . EmulationConfig . LineWrap)
							{
									/* Move to beginning of next line. */

								CursorX = 0;

								DownLine();

								Capture("\n",1);

								Scale = SCALE_ATTR_NORMAL;

								CharScale = (Config . EmulationConfig . 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 . EmulationConfig . InsertMode)
								{
									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;
						}
					}
				}
			}
			else
			{
				if(CurrentFont == GFX)
				{
					while(Size)
					{
						if(CursorX > LastChar)
						{
							if(Config . EmulationConfig . LineWrap)
							{
								CursorX = 0;

								DownLine();

								Scale = SCALE_ATTR_NORMAL;

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

								LastChar = LastColumn;

								ClipBlitCursor(FALSE,TRUE);
							}
							else
							{
								CursorX = LastChar;

								SetCursor();

								return;
							}
						}

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

								if(Config . EmulationConfig . InsertMode)
								{
									RasterShiftChar(Offset);

									ScrollLineShiftChar(Offset);

									ShiftChar(Offset);
								}
							}

							RasterPutString(Buffer,Offset);

							ScrollLinePutString(Offset);

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

							Buffer	+= Offset;

							Size	-= Offset;

							CursorX	+= Offset;
						}
					}
				}
				else
				{
					while(Size)
					{
						if(CursorX > LastChar)
						{
							if(Config . EmulationConfig . LineWrap)
							{
								CursorX = 0;

								DownLine();

								Scale = SCALE_ATTR_NORMAL;

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

								LastChar = LastColumn;

								ClipBlitCursor(FALSE,TRUE);
							}
							else
							{
								CursorX = LastChar;

								SetCursor();

								return;
							}
						}

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

								if(Config . EmulationConfig . InsertMode)
								{
									RasterShiftChar(Offset);

									ScrollLineShiftChar(Offset);

									ShiftChar(Offset);
								}
							}

							RasterPutString(Buffer,Offset);

							ScrollLinePutString(Offset);

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

							Buffer	+= Offset;

							Size	-= Offset;

							CursorX	+= Offset;
						}
					}
				}
			}

				/* Make the cursor reappear. */

			SetCursor();
		}
	}
}

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

STATIC VOID __regargs
SubConProcess(register STRPTR String,register LONG Size,register STRPTR TempBuffer)
{
		/* 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 . SerialConfig . StripBit8)
	{
		if(UseFlow)
			FlowFilter7(String,Size);

			/* 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)
		{
			register LONG	Len = 0;
			register UBYTE	c;

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

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

					if(!(InSequence = (*AbortTable[c])(c)))
						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)
							{
								ConWrite(TempBuffer,Len);

								Len = 0;
							}

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

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

									if(!(InSequence = (*AbortTable[c])(c)))
										break;
								}
							}
						}
						else
						{
							/* Put the character into the buffer
							 * and flush it if necessary.
							 */

							TempBuffer[Len++] = c;

							if(Len == 512)
							{
								ConWrite(TempBuffer,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)
							{
								ConWrite(TempBuffer,Len);

								Len = 0;
							}

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

									if(!(InSequence = (*AbortTable[c])(c)))
										break;
								}
							}
						}
						else
						{
							/* Put the character into the buffer
							 * and flush it if necessary.
							 */

							TempBuffer[Len++] = c;

							if(Len == 512)
							{
								ConWrite(TempBuffer,Len);

								Len = 0;
							}
						}
					}
				}
			}

			if(Len)
				ConWrite(TempBuffer,Len);
		}
	}
	else
	{
		if(UseFlow)
			FlowFilter8(String,Size);

		if(!Quiet)
		{
			register LONG	Len = 0;
			register UBYTE	c;

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

					if(!(InSequence = (*AbortTable[c])(c)))
						break;
				}
			}

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

								Len = 0;
							}

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

									if(!(InSequence = (*AbortTable[c])(c)))
										break;
								}
							}
						}
						else
						{
							TempBuffer[Len++] = c;

							if(Len == 512)
							{
								ConWrite(TempBuffer,Len);

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

								Len = 0;
							}

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

									if(!(InSequence = (*AbortTable[c])(c)))
										break;
								}
							}
						}
						else
						{
							TempBuffer[Len++] = c;

							if(Len == 512)
							{
								ConWrite(TempBuffer,Len);

								Len = 0;
							}
						}
					}
				}
			}

			if(Len)
				ConWrite(TempBuffer,Len);
		}
	}
}

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

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

		FlowInfo . Changed = FlowInfo . NoCarrier = FlowInfo . ZModemUpload = FlowInfo . Connect = FlowInfo . Voice = FlowInfo . Ring = FlowInfo . Busy = FlowInfo . NoDialTone = FALSE;
	}

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

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

		/* 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	= SCAN_NOCARRIER;
		ScanEnd		= SCAN_NODIALTONE;
	}
	else
	{
		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;
		}
	}
}

	/* EmulationServer():
	 *
	 *	Asynchronous terminal emulation server.
	 */

VOID __saveds
EmulationServer()
{
	struct MsgPort *EmulationWritePort;

	if(EmulationWritePort = CreateMsgPort())
	{
		if(EmulationWriteRequest = (struct IOExtSer *)AllocVec(sizeof(struct IOExtSer),MEMF_ANY | MEMF_PUBLIC))
		{
			if(WriteRequest)
				CopyMem(WriteRequest,EmulationWriteRequest,sizeof(struct IOExtSer));

			EmulationWriteRequest -> IOSer . io_Message . mn_ReplyPort = EmulationWritePort;

			if(EmulationPort = CreateMsgPort())
			{
				struct EmulationMessage	*Message;
				ULONG			 Signals;
				BYTE			 Terminated = FALSE;

				Signal(ThisProcess,SIGBREAKF_CTRL_C);

				do
				{
					Signals = Wait((1 << EmulationPort -> mp_SigBit) | SIGBREAKF_CTRL_C);

					if(Signals & SIGBREAKF_CTRL_C)
						Terminated = TRUE;
					else
					{
						register UBYTE	*Buffer;
						register LONG	 Size;
						UBYTE		 Scale,
								 CharScale;
						WORD		 Offset,
								 LastChar;

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

						ClearCursor();

						while(Message = (struct EmulationMessage *)GetMsg(EmulationPort))
						{
							Buffer	= Message -> Buffer;
							Size	= Message -> Size;

							Scale = RasterAttr[CursorY];

							if(Config . EmulationConfig . 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)
							{
								while(Size)
								{
									if(CursorX > LastChar)
									{
										if(Config . EmulationConfig . LineWrap)
										{
											CursorX = 0;

											DownLine();

											Scale = SCALE_ATTR_NORMAL;

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

											LastChar = LastColumn;

											ClipBlitCursor(FALSE,TRUE);
										}
										else
										{
											CursorX = LastChar;

											break;
										}
									}

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

											if(Config . EmulationConfig . InsertMode)
											{
												RasterShiftChar(Offset);

												ScrollLineShiftChar(Offset);

												ShiftChar(Offset);
											}
										}

										RasterPutString(Buffer,Offset);

										ScrollLinePutString(Offset);

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

										Buffer	+= Offset;

										Size	-= Offset;

										CursorX	+= Offset;
									}
								}
							}
							else
							{
								while(Size)
								{
									if(CursorX > LastChar)
									{
										if(Config . EmulationConfig . LineWrap)
										{
											CursorX = 0;

											DownLine();

											Scale = SCALE_ATTR_NORMAL;

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

											LastChar = LastColumn;

											ClipBlitCursor(FALSE,TRUE);
										}
										else
										{
											CursorX = LastChar;

											break;
										}
									}

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

											if(Config . EmulationConfig . InsertMode)
											{
												RasterShiftChar(Offset);

												ScrollLineShiftChar(Offset);

												ShiftChar(Offset);
											}
										}

										RasterPutString(Buffer,Offset);

										ScrollLinePutString(Offset);

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

										Buffer	+= Offset;

										Size	-= Offset;

										CursorX	+= Offset;
									}
								}
							}

							FreeVec(Message);
						}

							/* Make the cursor reappear. */

						SetCursor();
					}
				}
				while(!Terminated);

				{
					struct MsgPort *Port = EmulationPort;

					EmulationPort = NULL;

					while(Message = (struct EmulationMessage *)GetMsg(Port))
						FreeVec(Message);

					DeleteMsgPort(Port);
				}
			}

			FreeVec(EmulationWriteRequest);

			EmulationWriteRequest = NULL;
		}

		DeleteMsgPort(EmulationWritePort);
	}

	Forbid();

	Signal(ThisProcess,SIGBREAKF_CTRL_C);

	EmulationRequest = NULL;

	RemTask(EmulationTask = NULL);
}

	/* EmulationSetup():
	 *
	 *	Start the terminal emulation server task.
	 */

VOID
EmulationSetup()
{
	if(!EmulationTask && Config . TerminalConfig . EmulationMode != EMULATION_EXTERNAL && Config . MiscConfig . EmulationServer)
	{
		if(EmulationTask = CreateTask("term emulation task",MAX((WORD)SysBase -> ThisTask -> tc_Node . ln_Pri - 1,0),EmulationServer,8192))
			Wait(SIGBREAKF_CTRL_C);
	}
}

	/* EmulationCleanup():
	 *
	 *	Stop the terminal emulation server task.
	 */

VOID
EmulationCleanup()
{
	if(EmulationTask)
	{
		Signal(EmulationTask,SIGBREAKF_CTRL_C);

		Wait(SIGBREAKF_CTRL_C);
	}
}

	/* SendEmulation(register APTR Buffer,register LONG Size):
	 *
	 *	Post a message to the terminal emulation server task.
	 */

VOID __regargs
SendEmulation(register APTR Buffer,register LONG Size)
{
	struct EmulationMessage *Message;

	if(Message = (struct EmulationMessage *)AllocVec(sizeof(struct EmulationMessage) + Size,MEMF_ANY))
	{
		Message -> ExecMessage . mn_Length	= sizeof(struct EmulationMessage) + Size;
		Message -> Buffer			= (STRPTR)(Message + 1);
		Message -> Size				= Size;

		CopyMem(Buffer,Message -> Buffer,Size);

		PutMsg(EmulationPort,&Message -> ExecMessage);
	}
}

	/* 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_CAPTURE_TO_FILE))
						SomeItem -> Flags &= ~CHECKED;

					FileCapture = NULL;

					break;

				case 2:	BufferClose(FileCapture);

					if(SomeItem = FindThisItem(MEN_CAPTURE_TO_FILE))
						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 . CaptureConfig . CaptureFilterMode)
		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_CAPTURE_TO_PRINTER))
					SomeItem -> Flags &= ~CHECKED;

				PrinterCapture = NULL;
			}

			ReleaseWindows();
		}
	}
}

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

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

	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_CAPTURE_TO_PRINTER);

		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),"PRT:");

			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,ULONG Qualifier):
	 *
	 *	This routine handles the somewhat strange behaviour of
	 *	an assorted set of keys in VT100 applications mode.
	 */

BYTE __regargs
HandleCursor(UBYTE Char,ULONG Qualifier)
{
	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 . EmulationConfig . CursorMode == KEYMODE_APPLICATION)
				SerWrite(Table[i] . ApplicationString,strlen(Table[i] . ApplicationString));
			else
			{
				WORD QualType = 0;

				if(Qualifier & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT))
					QualType = 1;
				else
				{
					if(Qualifier & (IEQUALIFIER_LALT|IEQUALIFIER_RALT))
						QualType = 2;
					else
					{
						if(Qualifier & IEQUALIFIER_CONTROL)
							QualType = 3;
					}
				}

				SerialCommand(CursorKeys -> Keys[QualType][i]);
			}

			return(TRUE);
		}
	}

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

	for(i = 4 ; i < 18 ; i++)
	{
		if(Table[i] . Char == Char)
		{
			if(Config . EmulationConfig . NumericMode == KEYMODE_APPLICATION)
				SerWrite(Table[i] . ApplicationString,strlen(Table[i] . ApplicationString));
			else
			{
				if(i == 17)
				{
					switch(Config . TerminalConfig . 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);
}

	/* 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)
{
	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 { STRPTR RawCode; UBYTE Result; } ConversionTable[16] =
		{
			(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[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 or cursor key, clear the qualifier. */

			if(Massage -> Code >= 76 && 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);

			memset(ConvertBuffer,0,256);

			if((Actual = RawKeyConvert(FakeInputEvent,(STRPTR)ConvertBuffer,256,KeyMap)) > 0)
			{
				if(ConvertBuffer[0])
				{
					if(Config . EmulationConfig . 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);
}

	/* 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)
{
	if(GFX -> tf_XSize == TextFont -> tf_XSize && GFX -> tf_YSize == TextFont -> tf_YSize)
	{
		BYTE Mode = 1;

		while(Length--)
		{
			if(GfxTable[*Buffer] == Mode)
				Text(RPort,Buffer++,1);
			else
			{
				if(Mode)
					SetFont(RPort,TextFont);
				else
					SetFont(RPort,GFX);

				Mode ^= 1;

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

		if(!Mode)
			SetFont(RPort,GFX);
	}
	else
		Text(RPort,Buffer,Length);
}

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

VOID __regargs
ConProcess(register STRPTR String,register LONG Size)
{
		/* If the capture filter happens to be disabled, write the
		 * raw data.
		 */

	if(!Config . CaptureConfig . CaptureFilterMode)
		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 . TerminalConfig . EmulationMode == EMULATION_EXTERNAL)
	{
			/* Feed the flow filter... */

		if(UseFlow)
		{
			if(Config . SerialConfig . StripBit8)
				FlowFilter7(String,Size);
			else
				FlowFilter8(String,Size);
		}

			/* Are we to output data? */

		if(!Quiet)
		{
			if(ReceiveTable)
			{
				struct TranslationHandle	Handle;
				UBYTE				LocalBuffer[256];

					/* Set up for translation. */

				TranslateSetup(&Handle,String,Size,LocalBuffer,256,ReceiveTable);

				if(StripBuffer)
				{
					register LONG Len;

						/* Process and output the data. */

					while(Size = TranslateBuffer(&Handle))
					{
						XEmulatorWrite(XEM_IO,LocalBuffer,Size);

						XEM_HostData . Source		= LocalBuffer;
						XEM_HostData . Destination	= StripBuffer;

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

					while(Size = TranslateBuffer(&Handle))
						XEmulatorWrite(XEM_IO,LocalBuffer,Size);
				}
			}
			else
			{
				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))
						Capture(StripBuffer,Len);
				}
			}
		}
	}
	else
	{
		STATIC UBYTE TempBuffer[512];

			/* Is the data to be translated? */

		if(ReceiveTable)
		{
			struct TranslationHandle	Handle;
			UBYTE				LocalBuffer[256];

				/* Set up for translation. */

			TranslateSetup(&Handle,String,Size,LocalBuffer,256,ReceiveTable);

				/* Process and output the data. */

			while(Size = TranslateBuffer(&Handle))
				SubConProcess(LocalBuffer,Size,TempBuffer);
		}
		else
		{
				/* 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 . SerialConfig . StripBit8)
			{
				if(UseFlow)
					FlowFilter7(String,Size);

					/* 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)
				{
					register LONG	Len = 0;
					register UBYTE	c;

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

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

							if(!(InSequence = (*AbortTable[c])(c)))
								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)
									{
										ConWrite(TempBuffer,Len);

										Len = 0;
									}

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

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

											if(!(InSequence = (*AbortTable[c])(c)))
												break;
										}
									}
								}
								else
								{
									/* Put the character into the buffer
									 * and flush it if necessary.
									 */

									TempBuffer[Len++] = c;

									if(Len == 512)
									{
										ConWrite(TempBuffer,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)
									{
										ConWrite(TempBuffer,Len);

										Len = 0;
									}

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

											if(!(InSequence = (*AbortTable[c])(c)))
												break;
										}
									}
								}
								else
								{
									/* Put the character into the buffer
									 * and flush it if necessary.
									 */

									TempBuffer[Len++] = c;

									if(Len == 512)
									{
										ConWrite(TempBuffer,Len);

										Len = 0;
									}
								}
							}
						}
					}

					if(Len)
						ConWrite(TempBuffer,Len);
				}
			}
			else
			{
				if(UseFlow)
					FlowFilter8(String,Size);

				if(!Quiet)
				{
					register LONG	Len = 0;
					register UBYTE	c;

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

							if(!(InSequence = (*AbortTable[c])(c)))
								break;
						}
					}

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

										Len = 0;
									}

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

											if(!(InSequence = (*AbortTable[c])(c)))
												break;
										}
									}
								}
								else
								{
									TempBuffer[Len++] = c;

									if(Len == 512)
									{
										ConWrite(TempBuffer,Len);

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

										Len = 0;
									}

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

											if(!(InSequence = (*AbortTable[c])(c)))
												break;
										}
									}
								}
								else
								{
									TempBuffer[Len++] = c;

									if(Len == 512)
									{
										ConWrite(TempBuffer,Len);

										Len = 0;
									}
								}
							}
						}
					}

					if(Len)
						ConWrite(TempBuffer,Len);
				}
			}
		}
	}
}

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

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

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

	ConProcess(SharedBuffer,strlen(SharedBuffer));
}

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

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

	if(CursorX)
	{
		ClearCursor();

		CursorX--;

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

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

			BackupRender();

			SetBPen(RPort,0);

			RasterEraseCharacters(1);

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

			ScrollLineEraseCharacters(1);

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

			BackupRender();
		}

		SetCursor();
	}

	return(FALSE);
}

	/* ClearAudio():
	 *
	 *	Clear the audio control block for reuse.
	 */

VOID
ClearAudio()
{
		/* Remove the request. */

	WaitIO(AudioBlock);

		/* Clear the signal bit. */

	SetSignal(0,SIG_AUDIO);

		/* Free the channels we had allocated. */

	AudioBlock -> ioa_Request . io_Command = ADCMD_FREE;

	DoIO(AudioBlock);

		/* No sound running. */

	SoundPlayed = FALSE;
}

	/* Beep():
	 *
	 *	Produce a decent bell sound.
	 */

VOID
Beep()
{
	if(AudioBlock)
	{
		BYTE PlayItAgainSam;	/* Note: `Sam' is Sam Dicker, the author of
					 *       the original audio.device implementation.
					 */

			/* AudioRequest has returned. */

		if(CheckSignal(SIG_AUDIO))
			ClearAudio();

			/* Check whether we are to play the sound or not. */

		if(!SoundPlayed)
			PlayItAgainSam = TRUE;
		else
		{
			if(CheckIO(AudioBlock))
				PlayItAgainSam = TRUE;
			else
				PlayItAgainSam = FALSE;
		}

			/* May we play the sound? */

		if(PlayItAgainSam)
		{
				/* Allocate a sound channel, we don't want to
				 * wait for it, the `beep' sound is to played
				 * right now.
				 */

			AudioBlock -> ioa_Request . io_Command				= ADCMD_ALLOCATE;
			AudioBlock -> ioa_Request . io_Flags				= ADIOF_NOWAIT | IOF_QUICK;
			AudioBlock -> ioa_Request . io_Message . mn_Node . ln_Pri	= 80;
			AudioBlock -> ioa_Data						= AnyChannel;
			AudioBlock -> ioa_Length					= 4;

				/* Try the allocation. */

			BeginIO(AudioBlock);

				/* If still in progress, no channel is available yet. */

			if(!CheckIO(AudioBlock))
			{
					/* Abort the allocation. */

				AbortIO(AudioBlock);

					/* Wait for request to be returned. */

				WaitIO(AudioBlock);
			}
			else
			{
					/* Wait for request to be returned. */

				if(!WaitIO(AudioBlock))
				{
						/* Set up the sound IO data. */

					if(HasSound)
					{
						AudioBlock -> ioa_Period	= SoundInfo . Rate;
						AudioBlock -> ioa_Volume	= SoundInfo . Volume;
						AudioBlock -> ioa_Cycles	= 1;
						AudioBlock -> ioa_Data		= SoundInfo . Data;
						AudioBlock -> ioa_Length	= SoundInfo . Length;
					}
					else
					{
						AudioBlock -> ioa_Period	= 223;
						AudioBlock -> ioa_Volume	= 64 / 2;
						AudioBlock -> ioa_Cycles	= 150;
						AudioBlock -> ioa_Data		= SineWave;
						AudioBlock -> ioa_Length	= 8;
					}

					AudioBlock -> ioa_Request . io_Command	= CMD_WRITE;
					AudioBlock -> ioa_Request . io_Flags	= ADIOF_PERVOL;

						/* Start the sound. */

					BeginIO(AudioBlock);

					SoundPlayed = TRUE;
				}
			}
		}
	}
}
	/* DoBeep():
	 *
	 *	The real interface to the beep routine.
	 */

BYTE
DoBeep()
{
		/* Handle the visual part. */

	if(Config . TerminalConfig . BellMode == BELL_VISIBLE || Config . TerminalConfig . BellMode == BELL_BOTH)
	{
		if(StatusProcess)
			Signal(StatusProcess,SIGBREAKF_CTRL_D);
	}

		/* Let it beep. */

	if(Config . TerminalConfig . BellMode == BELL_AUDIBLE || Config . TerminalConfig . BellMode == BELL_BOTH)
		Beep();

		/* Capture the bell. */

	if(Config . CaptureConfig . CaptureFilterMode == FILTER_NONE)
		Capture("\a",1);

	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()
{
	if(Config . TerminalConfig . ReceiveLF == LF_ASLFCR)
	{
		ClearCursor();

		CursorX = 0;

		DownLine();

		SetCursor();

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

			DownLine();

			SetCursor();
		}
	}

	return(FALSE);
}

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

BYTE
DoShiftIn()
{
	if(CharMode[1] == TABLE_GFX && GFX)
	{
		if(GFX -> tf_XSize == TextFont -> tf_XSize && GFX -> tf_YSize == TextFont -> tf_YSize)
			CurrentFont = GFX;
		else
			CurrentFont = TextFont;
	}

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

	SetFont(RPort,CurrentFont);

	Charset = 1;

	return(FALSE);
}

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

BYTE
DoShiftOut()
{
	if(CharMode[0] == TABLE_GFX && GFX)
	{
		if(GFX -> tf_XSize == TextFont -> tf_XSize && GFX -> tf_YSize == TextFont -> tf_YSize)
			CurrentFont = GFX;
		else
			CurrentFont = TextFont;
	}

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

	SetFont(RPort,CurrentFont);

	Charset = 0;

	return(FALSE);
}

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

BYTE
DoCR_LF()
{
	ClearCursor();

	CursorX = 0;

	DownLine();

	SetCursor();

	Capture("\n",1);

	return(FALSE);
}

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

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

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

		ClearCursor();

		CursorX = CursorY = 0;

		SetCursor();

		Capture("\n",1);
	}

	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 || Config . TerminalConfig . ReceiveCR == CR_ASCRLF)
		DoCR_LF();
	else
	{
		if(Config . TerminalConfig . ReceiveCR == CR_ASCR)
		{
			ClearCursor();

			CursorX = 0;

			SetCursor();

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

	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;

	ClearCursor();

	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;
		}
	}

	SetCursor();

	Capture("\t",1);

	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)
		ConWrite("^",1);

	return(TRUE);
}

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

BYTE
DoCsi()
{
	if(Config . TerminalConfig . EmulationMode == EMULATION_TTY)
		ConWrite("^[",2);

	ParseCode('[');

	return(TRUE);
}

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

BYTE __regargs
DoNewEsc(UBYTE Char)
{
	if(Config . TerminalConfig . EmulationMode == EMULATION_TTY)
		ConWrite("^",1);

	DoCancel();

	return(TRUE);
}

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

BYTE __regargs
DoNewCsi(UBYTE Char)
{
	if(Config . TerminalConfig . EmulationMode == EMULATION_TTY)
		ConWrite("^[",2);

	DoCancel();

	return(ParseCode('['));
}
