/*
**	$Id: termRexx.c,v 1.10 92/08/15 20:14:59 olsen Sta Locker: olsen $
**	$Revision: 1.10 $
**	$Date: 92/08/15 20:14:59 $
**
**	ARexx interface support routines
**
**	Copyright © 1990-1992 by Olaf `Olsen' Barthel & MXM
**		All Rights Reserved
*/

#include "termGlobal.h"

	/* The rexx server commands are contained in lists in which
	 * each command name and the approriate routine is
	 * stored. The following three structure definitions
	 * are required to set up the list properly.
	 */

struct QueryStruct
{
	UBYTE	*String;
	STRPTR 	(*Routine)(VOID);
};

struct ASyncCommandStruct
{
	UBYTE	*String;
	VOID	(*Routine)(VOID);
};

struct CommandStruct
{
	UBYTE	*String;
	STRPTR 	(*Routine)(STRPTR ,STRPTR );
};

	/* Global rexx output buffer. */

STATIC UBYTE RexxTextBuffer[256];

	/* This structure defines a relation between a time unit
	 * (microseconds, seconds, minutes) and a string key.
	 */

struct TimeRel
{
	UBYTE	*Key;
	LONG	 Multi;
};

	/* Relations between qualifying keywords and time multipliers. */

#define NUMTIMEREL 9

STATIC struct TimeRel TimeRelations[NUMTIMEREL] =
{
	"MIC",		1,
	"SEC",		MILLION,
	"MIN",		MILLION * 60,

	"MICROSECONDS",	1,
	"SECONDS",	MILLION,
	"MINUTES",	MILLION * 60,

	"MICROS",	1,
	"SECS",		MILLION,
	"MINS",		MILLION * 60
};

	/* Asynchronous commands which can be executed by the
	 * rexx server process itself and do not require the
	 * term main process to take any action.
	 */

#define NUMASYNCS 8

STATIC struct ASyncCommandStruct ASyncCommands[NUMASYNCS] =
{
	"TERM2FRONT",		(APTR)Term2Front,
	"DEFAULT2FRONT",	(APTR)BumpDefault,
	"WB2FRONT",		(APTR)WBenchToFront,
	"REXX2FRONT",		(APTR)Rexx2Front,
	"DISPLAY2FRONT",	(APTR)Display2Front,
	"CLEARDISPLAY",		(APTR)ClearBuffer,
	"CLEARDOWNLOADLIST",	(APTR)ClearDownloadObjects,
	"QUIETEXIT",		(APTR)QuietExit
};

	/* Query commands which return miscellaneous system
	 * information (these routines are handled asynchronously
	 * as well).
	 */

#define NUMQUERIES 81

STATIC struct QueryStruct QueryCommands[NUMQUERIES] =
{
	"BAUDRATE",		(APTR)QueryBaud,
	"BITSPERCHAR",		(APTR)QueryDataBits,
	"PARITY",		(APTR)QueryParity,
	"STOPBITS",		(APTR)QueryStopBits,
	"HANDSHAKING",		(APTR)QueryHandshaking,
	"DUPLEX",		(APTR)QueryDuplex,
	"HIGHSPEED",		(APTR)QueryHighspeed,
	"BREAKLENGTH",		(APTR)QueryBreakLength,
	"SERIALDEVICE",		(APTR)QuerySerialDevice,
	"UNITNUMBER",		(APTR)QueryUnitNumber,
	"MODEMINIT",		(APTR)QueryModemInit,
	"MODEMEXIT",		(APTR)QueryModemExit,
	"DIALPREFIX",		(APTR)QueryDialPrefix,
	"REDIALDELAY",		(APTR)QueryRedialDelay,
	"DIALRETRIES",		(APTR)QueryDialRetries,
	"DIALTIMEOUT",		(APTR)QueryDialTimeout,
	"CONNECTAUTOBAUD",	(APTR)QueryConnectAutoBaud,
	"NOCARRIER",		(APTR)QueryNoCarrier,
	"CONNECT",		(APTR)QueryConnect,
	"VOICE",		(APTR)QueryVoice,
	"RING",			(APTR)QueryRing,
	"BUSY",			(APTR)QueryBusy,
	"PROTOCOL",		(APTR)QueryProtocol,
	"PROTOCOLOPTIONS",	(APTR)QueryProtocolOptions,
	"MACROFILE",		(APTR)QueryMacroFile,
	"DISPLAYMODE",		(APTR)QueryDisplay,
	"PUBLICSCREEN",		(APTR)QueryPublicScreen,
	"SHANGHAI",		(APTR)QueryShanghai,
	"CAPTUREFILTER",	(APTR)QueryCaptureFilter,
	"DSBACKSPACE",		(APTR)QueryDSBackSpace,
	"AUDBELL",		(APTR)QueryAudBell,
	"VISBELL",		(APTR)QueryVisBell,
	"EIGHTYCOLUMNS",	(APTR)QueryEightyColumns,
	"SENDCR",		(APTR)QuerySendCR,
	"SENDLF",		(APTR)QuerySendLF,
	"COLOURMODE",		(APTR)QueryColourMode,
	"COLORMODE",		(APTR)QueryColourMode,
	"EMULATION",		(APTR)QueryEmulation,
	"FONT",			(APTR)QueryFont,
	"STATUS",		(APTR)QueryStatus,
	"SERIAL",		(APTR)QuerySerial,
	"STARTUP",		(APTR)QueryStartup,
	"REQUESTERS",		(APTR)QueryRequesters,
	"TIMEOUT",		(APTR)QueryTimeout,
	"LINE",			(APTR)QueryLine,
	"COLUMNS",		(APTR)QueryColumns,
	"LINES",		(APTR)QueryLines,
	"CURSOR",		(APTR)QueryCursor,
	"MODEMHANGUP",		(APTR)QueryModemHangup,
	"AUTOCAPTURE",		(APTR)QueryAutoCapture,
	"LOGACTIONS",		(APTR)QueryLogActions,
	"BLINKING",		(APTR)QueryBlinking,
	"CURSORMODE",		(APTR)QueryCursorMode,
	"FONTSCALE",		(APTR)QueryFontScale,
	"SMOOTHSCROLL",		(APTR)QueryJumpScroll,
	"CHARACTERWRAP",	(APTR)QueryCharacterWrap,
	"CURSORWRAP",		(APTR)QueryCursorWrap,
	"NEWLINEMODE",		(APTR)QueryNewLine,
	"INSERTMODE",		(APTR)QueryInsert,
	"NUMERICMODE",		(APTR)QueryNumeric,
	"DEFAULTSTORE",		(APTR)QueryDefaultStore,
	"TUPLOADPATH",		(APTR)QueryTUploadPath,
	"TDOWNLOADPATH",	(APTR)QueryTDownloadPath,
	"AUPLOADPATH",		(APTR)QueryAUploadPath,
	"ADOWNLOADPATH",	(APTR)QueryADownloadPath,
	"BUPLOADPATH",		(APTR)QueryBUploadPath,
	"BDOWNLOADPATH",	(APTR)QueryBDownloadPath,
	"CAPTUREPATH",		(APTR)QueryCapturePath,
	"LOGFILE",		(APTR)QueryLogFile,
	"EDITOR",		(APTR)QueryEditor,
	"BEEPSOUND",		(APTR)QueryBeepSound,
	"CAPTURESTATE",		(APTR)QueryCaptureState,
	"DOWNLOADS",		(APTR)QueryDownloads,
	"SPEECHFILE",		(APTR)QuerySpeechFile,
	"SCREENADDRESS",	(APTR)QueryScreenAddress,
	"SPEECHRATE",		(APTR)QuerySpeechRate,
	"SPEECHPITCH",		(APTR)QuerySpeechPitch,
	"SPEECHFREQUENCY",	(APTR)QuerySpeechFrequency,
	"SPEECHVOLUME",		(APTR)QuerySpeechVolume,
	"SPEECHSEX",		(APTR)QuerySpeechSex,
	"SPEECH",		(APTR)QuerySpeech
};

	/* Any options which are to be set using the `SET' command
	 * are stored in the following list (these commands are
	 * handled synchronously).
	 */

#define NUMSETS		71

	/* How many set commands require more parameters? */

#define SETMOREPARAMS	6

STATIC struct CommandStruct SetCommands[NUMSETS] =
{
	/* The first five commands require more than
	 * one calling parameter and are handled
	 * differently (as compared to the other
	 * set commands).
	 */

	"MACRO",		(APTR)RexxSetMacro,
	"COLOUR",		(APTR)RexxSetColour,
	"COLOR",		(APTR)RexxSetColour,
	"SCREEN",		(APTR)RexxSetScreen,
	"BELL",			(APTR)RexxSetBell,
	"TIMEOUT",		(APTR)RexxSetTimeout,

	"BAUDRATE",		(APTR)RexxSetBaud,
	"BITSPERCHAR",		(APTR)RexxSetDataBits,
	"PARITY",		(APTR)RexxSetParity,
	"STOPBITS",		(APTR)RexxSetStopBits,
	"HANDSHAKING",		(APTR)RexxSetHandshaking,
	"DUPLEX",		(APTR)RexxSetDuplex,
	"HIGHSPEED",		(APTR)RexxSetHighSpeed,
	"BREAKLENGTH",		(APTR)RexxSetBreakLength,
	"SERIALDEVICE",		(APTR)RexxSetSerialDevice,
	"UNITNUMBER",		(APTR)RexxSetUnitNumber,
	"MODEMINIT",		(APTR)RexxSetModemInit,
	"MODEMEXIT",		(APTR)RexxSetModemExit,
	"DIALPREFIX",		(APTR)RexxSetDialPrefix,
	"REDIALDELAY",		(APTR)RexxSetRedialDelay,
	"DIALRETRIES",		(APTR)RexxSetDialRetries,
	"DIALTIMEOUT",		(APTR)RexxSetDialTimeout,
	"CONNECTAUTOBAUD",	(APTR)RexxSetConnectAutoBaud,
	"NOCARRIER",		(APTR)RexxSetNoCarrier,
	"CONNECT",		(APTR)RexxSetConnect,
	"VOICE",		(APTR)RexxSetVoice,
	"RING",			(APTR)RexxSetRing,
	"BUSY",			(APTR)RexxSetBusy,
	"SCREENMODE",		(APTR)RexxSetScreenMode,
	"FILTER",		(APTR)RexxSetFilter,
	"BACKSPACE",		(APTR)RexxSetBackspace,
	"CR",			(APTR)RexxSetCR,
	"LF",			(APTR)RexxSetLF,
	"EIGHTYCOLUMNS",	(APTR)RexxSet80Columns,
	"COLOURMODE",		(APTR)RexxSetColourMode,
	"COLORMODE",		(APTR)RexxSetColourMode,
	"EMULATION",		(APTR)RexxSetEmulation,
	"FONT",			(APTR)RexxSetFont,
	"STARTUP",		(APTR)RexxSetStartup,
	"PROTOCOL",		(APTR)RexxSetProtocol,
	"PROTOCOLOPTIONS",	(APTR)RexxSetProtocolOptions,
	"REQUESTERS",		(APTR)RexxSetRequesters,
	"SERIAL",		(APTR)RexxSetSerial,
	"MODEMHANGUP",		(APTR)RexxSetModemHangup,
	"AUTOCAPTURE",		(APTR)RexxSetAutoCapture,
	"LOGACTIONS",		(APTR)RexxSetLogActions,
	"BLINKING",		(APTR)RexxSetBlinking,
	"CURSORMODE",		(APTR)RexxSetCursorMode,
	"FONTSCALE",		(APTR)RexxSetFontScale,
	"SMOOTHSCROLL",		(APTR)RexxSetJumpScroll,
	"CHARACTERWRAP",	(APTR)RexxSetCharacterWrap,
	"CURSORWRAP",		(APTR)RexxSetCursorWrap,
	"NEWLINEMODE",		(APTR)RexxSetNewLine,
	"INSERTMODE",		(APTR)RexxSetInsert,
	"NUMERICMODE",		(APTR)RexxSetNumeric,
	"DEFAULTSTORE",		(APTR)RexxSetDefaultStore,
	"TUPLOADPATH",		(APTR)RexxSetTUploadPath,
	"TDOWNLOADPATH",	(APTR)RexxSetTDownloadPath,
	"AUPLOADPATH",		(APTR)RexxSetAUploadPath,
	"ADOWNLOADPATH",	(APTR)RexxSetADownloadPath,
	"BUPLOADPATH",		(APTR)RexxSetBUploadPath,
	"BDOWNLOADPATH",	(APTR)RexxSetBDownloadPath,
	"CAPTUREPATH",		(APTR)RexxSetCapturePath,
	"LOGFILE",		(APTR)RexxSetLogFile,
	"EDITOR",		(APTR)RexxSetEditor,
	"SPEECHRATE",		(APTR)RexxSetSpeechRate,
	"SPEECHPITCH",		(APTR)RexxSetSpeechPitch,
	"SPEECHFREQUENCY",	(APTR)RexxSetSpeechFrequency,
	"SPEECHVOLUME",		(APTR)RexxSetSpeechVolume,
	"SPEECHSEX",		(APTR)RexxSetSpeechSex,
	"SPEECH",		(APTR)RexxSetSpeech
};

	/* The following list contains only synchronous commands
	 * the rexx server passes to the term main process.
	 */

#define NUMREXX		37

	/* How many commands require more parameters. */

#define REXXMOREPARAMS	8

struct CommandStruct RexxCommands[NUMREXX] =
{
	/* The first five commands require more than
	 * one calling parameter.
	 */

	"BUFFER",		(APTR)RexxBuffer,
	"CAPTURE",		(APTR)RexxCapture,
	"CONFIG",		(APTR)RexxConfig,
	"MACROS",		(APTR)RexxMacros,
	"SPEECH",		(APTR)RexxSpeech,
	"PHONE",		(APTR)RexxPhone,
	"DELAY",		(APTR)RexxDelay,
	"WAITSTRING",		(APTR)RexxWaitString,

	"PRINTER",		(APTR)RexxPrinter,
	"BEEP",			(APTR)DoBeep,
	"BREAK",		(APTR)RexxBreak,
	"BUPLOAD",		(APTR)RexxBUpload,
	"BDOWNLOAD",		(APTR)RexxBDownload,
	"CLEARSCREEN",		(APTR)RexxClearScreen,
	"COMMAND",		(APTR)RexxCommand,
	"DIAL",			(APTR)RexxDial,
	"GETSTRING",		(APTR)RexxGetString,
	"HANGUP",		(APTR)RexxHangUp,
	"INPUT",		(APTR)RexxInput,
	"MESSAGE",		(APTR)RexxMessage,
	"RESETSTYLES",		(APTR)RexxResetStyles,
	"SAVEILBM",		(APTR)RexxSaveILBM,
	"TUPLOAD",		(APTR)RexxTUpload,
	"TDOWNLOAD",		(APTR)RexxTDownload,
	"WRITE",		(APTR)RexxWrite,
	"PUTCLIP",		(APTR)RexxPutClip,
	"GETCLIP",		(APTR)RexxGetClip,
	"FIRSTDOWNLOAD",	(APTR)RexxFirstDownload,
	"NEXTDOWNLOAD",		(APTR)RexxNextDownload,
	"LASTDOWNLOAD",		(APTR)RexxLastDownload,
	"TONEDIAL",		(APTR)RexxToneDial,
	"SIMPLEREQUEST",	(APTR)RexxSimpleRequest,
	"TWOGADREQUEST",	(APTR)RexxTwoGadRequest,
	"FILEREQUEST",		(APTR)RexxFileRequest,
	"SPEAK",		(APTR)RexxSpeak,
	"TERMEXIT",		(APTR)TermExit,
	"CLOSEDISPLAY",		(APTR)CloseDisplay,
};

	/* A global error code and a global input timeout. */

STATIC LONG RexxRes1,RexxGlobalTimeout;

	/* The names of the 16 display mode IDs in abbreviated
	 * form.
	 */

STATIC STRPTR ConfigDisplayNames[16] =
{
	"HIRES",
	"HIRESLACE",
	"SUPERHIRES",
	"SUPERHIRESLACE",
	"PRODUCT",
	"PRODUCTLACE",

	"PALHIRES",
	"PALHIRESLACE",
	"PALSUPERHIRES",
	"PALSUPERHIRESLACE",

	"NTSCHIRES",
	"NTSCHIRESLACE",
	"NTSCSUPERHIRES",
	"NTSCSUPERHIRESLACE",

	"A2024TENHZ",
	"A2024FIFTEENHZ"
};

	/* The global duplex modes in abbreviated form. */

STATIC STRPTR ConfigDuplex[2] =
{
	"FULL",
	"HALF"
};

	/* The global handshake modes in abbreviated form. */

STATIC STRPTR ConfigHandshaking[3] =
{
	"XONOFF",
	"RTSCTS",
	"NONE"
};

	/* The global fonts in abbreviated form. */

STATIC STRPTR ConfigFont[2] =
{
	"TOPAZ",
	"IBM"
};

	/* The global colour modes in abbreviated form. */

STATIC STRPTR ConfigColour[4] =
{
	"AMIGA",
	"EIGHT",
	"SIXTEEN",
	"MONO"
};

	/* The emulation types in abbreviated form. */

STATIC STRPTR ConfigEmulation[3] =
{
	"ANSIVT",
	"ATOMIC",
	"TTY"
};

	/* The global parity settings in abbreviated form. */

STATIC STRPTR ConfigParity[5] =
{
	"NONE",
	"EVEN",
	"ODD",
	"MARK",
	"SPACE"
};

	/* The global term status modes in abbreviated form. */

STATIC STRPTR ConfigStatus[7] =
{
	"READY",
	"HOLDING",
	"DIALING",
	"UPLOAD",
	"DOWNLOAD",
	"BREAKING",
	"HANGUP"
};

	/* Two boolean identifiers. */

STATIC STRPTR Booleans[2] =
{
	"OFF",
	"ON"
};

STATIC STRPTR KeyModes[2] =
{
	"STANDARD",
	"APPLICATION"
};

STATIC STRPTR FontSizes[5] =
{
	"NORMAL",
	"HIGHTOP",
	"HIGHBOTTOM",
	"WIDE",
	"HALF"
};

STATIC STRPTR Qualifiers[4] =
{
	"NONE",
	"SHIFT",
	"ALTERNATE",
	"CONTROL"
};

	/* SendRexxCommand():
	 *
	 *	Post a command to an ARexx port.
	 */

BYTE
SendRexxCommand(struct MsgPort *HostPort,STRPTR CommandString,STRPTR FileExtension,STRPTR HostName)
{
	struct MsgPort	*RexxPort = (struct MsgPort *)FindPort(RXSDIR);
	struct RexxMsg	*HostMessage;

	if(RexxPort)
	{
		if(!HostName)
			HostName = (STRPTR)"";

		if(!FileExtension)
			FileExtension = (STRPTR)"";

		if(HostMessage = CreateRexxMsg((struct MsgPort *)HostPort,FileExtension,HostName))
		{
			if(HostMessage -> rm_Args[0] = CreateArgstring(CommandString,strlen(CommandString)))
			{
				HostMessage -> rm_Action = RXCOMM;

				PutMsg(RexxPort,HostMessage);

				return(TRUE);
			}

			DeleteRexxMsg(HostMessage);
		}

		MyEasyRequest(Window,LocaleString(MSG_GLOBAL_TERM_HAS_A_PROBLEM_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT),LocaleString(MSG_TERMREXX_COULD_NOT_SEND_MESSAGE_TXT));

		return(FALSE);
	}
	else
		MyEasyRequest(Window,LocaleString(MSG_GLOBAL_TERM_HAS_A_PROBLEM_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT),LocaleString(MSG_TERMREXX_SERVER_NOT_RUNNING_TXT));
}

	/* FreeRexxCommand(struct RexxMsg *RexxMessage):
	 *
	 *	Free the contents of a RexxMsg.
	 */

VOID
FreeRexxCommand(struct RexxMsg *RexxMessage)
{
	if(RexxMessage -> rm_Args[0])
		DeleteArgstring(RexxMessage -> rm_Args[0]);

	DeleteRexxMsg(RexxMessage);
}

	/* ReplyRexxCommand():
	 *
	 *	Reply a command request the rexx server - or someone else -
	 *	has passed to us.
	 */

VOID
ReplyRexxCommand(struct RexxMsg *RexxMessage,LONG Primary,LONG Secondary,STRPTR Result)
{
	if(Secondary == NULL && (RexxMessage -> rm_Action & RXFF_RESULT))
	{
		if(Result)
		{
			if(Result[0])
				Secondary = (LONG)CreateArgstring(Result,strlen(Result));
		}
	}

	RexxMessage -> rm_Result1 = Primary;
	RexxMessage -> rm_Result2 = Secondary;

	ReplyMsg(RexxMessage);
}

	/* IsReallySpace(UBYTE c):
	 *
	 *	Return if the character passed to this routine is some
	 *	kind of blank space.
	 */

STATIC BYTE
IsReallySpace(UBYTE c)
{
	if((c >= 13 && c <= 17) || c == 32)
		return(TRUE);
	else
		return(FALSE);
}

	/* GetToken():
	 *
	 *	Parse a string for a token (typically used for
	 *	rexx message parsing).
	 */

STRPTR
GetToken(STRPTR String,LONG *StartChar,STRPTR AuxBuff,LONG MaxLength)
{
	WORD	i,StrEnd = 0,MaxPos = strlen(String);
	BYTE	Filter = FALSE;
	UBYTE	Brace = 0;

	if(MaxPos >= MaxLength + *StartChar)
		MaxPos = MaxLength + *StartChar - 1;

	if(*StartChar <= strlen(String) - 1 && String && String[0] && AuxBuff && MaxLength)
	{
		for(i = *StartChar ; i <= MaxPos ; i++)
		{
			if(!Filter)
			{
				if(!StrEnd && IsReallySpace(String[i]))
				{
					while(IsReallySpace(String[i]) && i < MaxPos)
					{
						i++;

						(*StartChar)++;
					}
				}
			}

			if((!Filter && IsReallySpace(String[i])) || String[i] == 0)
			{
				strncpy(AuxBuff,(String + *StartChar),StrEnd);
				AuxBuff[StrEnd] = 0;

				(*StartChar) += StrEnd;

				return(AuxBuff);
			}

			if(String[i] == '\'' || String[i] == '\"')
			{
				if(!Filter)
				{
					Brace = String[i];

					Filter = TRUE;
				}
				else
				{
					if(String[i] == Brace)
						Filter = FALSE;
				}
			}

			StrEnd++;
		}
	}

	return(NULL);
}

	/* A bunch of single rexx server routines is to follow.
	 * Since there are pretty much of subroutines, I didn't
	 * necessarily make a comment on any (figure out yourself
	 * how they work :-)
	 */

STRPTR 
QueryBaud()
{
	SPrintf(RexxTextBuffer,"%ld",Config . BaudRate);

	return(RexxTextBuffer);
}

STRPTR 
QueryDataBits()
{
	SPrintf(RexxTextBuffer,"%ld",Config . BitsPerChar);

	return(RexxTextBuffer);
}

STRPTR 
QueryParity()
{
	return(ConfigParity[Config . Parity]);
}

STRPTR 
QueryStopBits()
{
	SPrintf(RexxTextBuffer,"%ld",Config . StopBits);

	return(RexxTextBuffer);
}

STRPTR 
QueryHandshaking()
{
	return(ConfigHandshaking[Config . Handshaking]);
}

STRPTR 
QueryDuplex()
{
	return(ConfigDuplex[Config . Duplex]);
}

STRPTR 
QueryHighspeed()
{
	return(Booleans[Config . HighSpeed]);
}

STRPTR 
QueryBreakLength()
{
	SPrintf(RexxTextBuffer,"%ld",Config . BreakLength);

	return(RexxTextBuffer);
}

STRPTR 
QuerySerialDevice()
{
	return(Config . SerialDevice);
}

STRPTR 
QueryUnitNumber()
{
	SPrintf(RexxTextBuffer,"%ld",Config . UnitNumber);

	return(RexxTextBuffer);
}

STRPTR 
QueryModemInit()
{
	return(Config . ModemInit);
}

STRPTR 
QueryModemExit()
{
	return(Config . ModemExit);
}

STRPTR 
QueryDialPrefix()
{
	return(Config . DialPrefix);
}

STRPTR 
QueryRedialDelay()
{
	SPrintf(RexxTextBuffer,"%ld",Config . RedialDelay);

	return(RexxTextBuffer);
}

STRPTR 
QueryDialRetries()
{
	SPrintf(RexxTextBuffer,"%ld",Config . DialRetries);

	return(RexxTextBuffer);
}

STRPTR 
QueryDialTimeout()
{
	SPrintf(RexxTextBuffer,"%ld",Config . DialTimeout);

	return(RexxTextBuffer);
}

STRPTR 
QueryConnectAutoBaud()
{
	return(Booleans[Config . ConnectAutoBaud]);
}

STRPTR 
QueryNoCarrier()
{
	return(Config . NoCarrier);
}

STRPTR 
QueryConnect()
{
	return(Config . Connect);
}

STRPTR 
QueryVoice()
{
	return(Config . Voice);
}

STRPTR 
QueryRing()
{
	return(Config . Ring);
}

STRPTR 
QueryBusy()
{
	return(Config . Busy);
}

STRPTR 
QueryProtocol()
{
	return(Config . Protocol);
}

STRPTR 
QueryProtocolOptions()
{
	return(ProtocolOptsBuffer);
}

STRPTR 
QueryMacroFile()
{
	return(Config . MacroFile);
}

STRPTR 
QueryDisplay()
{
	WORD i;

	for(i = 0 ; i < 16 ; i++)
		if(Config . DisplayMode == ModeID[i])
			return(ConfigDisplayNames[i]);

	return(0);
}

STRPTR 
QueryPublicScreen()
{
	return(Booleans[Config . MakeScreenPublic]);
}

STRPTR 
QueryShanghai()
{
	return(Booleans[Config . ShanghaiWindows]);
}

STRPTR 
QueryCaptureFilter()
{
	return(Booleans[Config . HighSpeed]);
}

STRPTR 
QueryDSBackSpace()
{
	return(Booleans[Config . DestructiveBackspace]);
}

STRPTR 
QueryAudBell()
{
	return(Booleans[Config . AudibleBell]);
}

STRPTR 
QueryVisBell()
{
	return(Booleans[Config . VisibleBell]);
}

STRPTR 
QueryEightyColumns()
{
	return(Booleans[Config . EightyColumns != 0]);
}

STRPTR 
QuerySendCR()
{
	switch(Config . SendCR)
	{
		case CR_IGNORE:		return("IGNORE");
		case CR_ASCR:		return("CR");
		case CR_ASCRLF:		return("CRLF");
	}
}

STRPTR 
QuerySendLF()
{
	switch(Config . SendLF)
	{
		case LF_IGNORE:		return("IGNORE");
		case LF_ASLF:		return("LF");
		case LF_ASLFCR:		return("LFCR");
	}
}

STRPTR 
QueryColourMode()
{
	return(ConfigColour[Config . ColourMode]);
}

STRPTR 
QueryEmulation()
{
	return(ConfigEmulation[Config . Emulation]);
}

STRPTR 
QueryFont()
{
	return(ConfigFont[Config . Font]);
}

STRPTR 
QueryStatus()
{
	return(ConfigStatus[Status]);
}

STRPTR 
QuerySerial()
{
	return(Booleans[ReadPort ? 1 : 0]);
}

STRPTR 
QueryStartup()
{
	return(Config . StartupMacro);
}

STRPTR 
QueryRequesters()
{
	return((LONG)ThisProcess -> pr_WindowPtr == -1 ? Booleans[0] : Booleans[1]);
}

STRPTR 
QueryTimeout()
{
	SPrintf(RexxTextBuffer,"%ld",RexxGlobalTimeout);

	return(RexxTextBuffer);
}

STRPTR 
QueryLine()
{
	return(Booleans[Online]);
}

STRPTR 
QueryLines()
{
		/* Note: LastLine is the last accessible line (i.e. for a
		 *       24 line display, LastLine will be 23).
		 */

	SPrintf(RexxTextBuffer,"%ld",LastLine + 1);

	return(RexxTextBuffer);
}

STRPTR 
QueryColumns()
{
		/* The same applies to the number of columns. */

	SPrintf(RexxTextBuffer,"%ld",LastColumn + 1);

	return(RexxTextBuffer);
}

STRPTR 
QueryCursor()
{
	SPrintf(RexxTextBuffer,"%ld %ld",CursorX,CursorY);

	return(RexxTextBuffer);
}

STRPTR 
QueryModemHangup()
{
	return(Config . ModemHangup);
}

STRPTR 
QueryAutoCapture()
{
	return(Booleans[Config . ConnectAutoCapture]);
}

STRPTR 
QueryLogActions()
{
	return(Booleans[Config . LogActions]);
}

STRPTR 
QueryBlinking()
{
	return(Booleans[Config . DisableBlinking ^ TRUE]);
}

STRPTR 
QueryCursorMode()
{
	return(KeyModes[Config . CursorApp]);
}

STRPTR 
QueryFontScale()
{
	return(FontSizes[Config . FontScale]);
}

STRPTR 
QueryJumpScroll()
{
	return(Booleans[Config . JumpScroll ^ TRUE]);
}

STRPTR 
QueryCharacterWrap()
{
	return(Booleans[Config . AutoWrap]);
}

STRPTR 
QueryCursorWrap()
{
	return(Booleans[Config . CursorWrap]);
}

STRPTR 
QueryNewLine()
{
	return(Booleans[Config . NewLine]);
}

STRPTR 
QueryInsert()
{
	return(Booleans[Config . InsertChar]);
}

STRPTR 
QueryNumeric()
{
	return(KeyModes[Config . NumApp]);
}

STRPTR 
QueryDefaultStore()
{
	return(Config . DefaultStorage);
}

STRPTR 
QueryTUploadPath()
{
	return(Config . TextUploadPath);
}

STRPTR 
QueryTDownloadPath()
{
	return(Config . TextDownloadPath);
}

STRPTR 
QueryAUploadPath()
{
	return(Config . ASCIIUploadPath);
}

STRPTR 
QueryADownloadPath()
{
	return(Config . ASCIIDownloadPath);
}

STRPTR 
QueryBUploadPath()
{
	return(Config . BinaryUploadPath);
}

STRPTR 
QueryBDownloadPath()
{
	return(Config . BinaryDownloadPath);
}

STRPTR 
QueryCapturePath()
{
	return(Config . CapturePath);
}

STRPTR 
QueryLogFile()
{
	return(Config . LogFile);
}

STRPTR 
QueryEditor()
{
	return(Config . Editor);
}

STRPTR 
QueryBeepSound()
{
	return(Config . BeepSound);
}

STRPTR 
QueryCaptureState()
{
	RexxTextBuffer[0] = 0;

	if(PrinterCapture)
	{
		strcat(RexxTextBuffer,"PRINTER");

		if(FileCapture)
			strcat(RexxTextBuffer," FILE");
	}
	else
	{
		if(FileCapture)
			strcat(RexxTextBuffer,"FILE");
		else
			strcat(RexxTextBuffer,Booleans[0]);
	}		

	return(RexxTextBuffer);
}

STRPTR 
QueryDownloads()
{
	SPrintf(RexxTextBuffer,"%ld",DownloadLineCount);

	return(RexxTextBuffer);
}

STRPTR 
QueryScreenAddress()
{
	SPrintf(RexxTextBuffer,"%ld",Screen);

	return(RexxTextBuffer);
}

STRPTR 
QuerySpeechFile()
{
	return(LastSpeech);
}

STRPTR 
QuerySpeechRate()
{
	SPrintf(RexxTextBuffer,"%ld",SpeechConfig . Rate);

	return(RexxTextBuffer);
}

STRPTR 
QuerySpeechPitch()
{
	SPrintf(RexxTextBuffer,"%ld",SpeechConfig . Pitch);

	return(RexxTextBuffer);
}

STRPTR 
QuerySpeechFrequency()
{
	SPrintf(RexxTextBuffer,"%ld",SpeechConfig . Frequency);

	return(RexxTextBuffer);
}

STRPTR 
QuerySpeechVolume()
{
	SPrintf(RexxTextBuffer,"%ld",SpeechConfig . Volume);

	return(RexxTextBuffer);
}

STRPTR 
QuerySpeechSex()
{
	return(SpeechConfig . Sex ? "FEMALE" : "MALE");
}

STRPTR 
QuerySpeech()
{
	return(Booleans[SpeechConfig . Enabled]);
}

VOID
RexxSetBaud(STRPTR String)
{
	LONG Value = atol(String);

	if(Config . BaudRate != Value)
	{
		Config . BaudRate = Value;

		ResetSerial = TRUE;
	}
}

VOID
RexxSetDataBits(STRPTR String)
{
	LONG Bits = atol(String);

	if(Bits == 7 || Bits == 8)
	{
		if(Config . BitsPerChar != Bits)
		{
			Config . BitsPerChar = Bits;

			ResetSerial = TRUE;
		}
	}
	else
		RexxRes1 = RC_ERROR;
}

VOID
RexxSetParity(STRPTR String)
{
	WORD i;

	for(i = 0 ; i < 5 ; i++)
	{
		if(!Stricmp(String,ConfigParity[i]))
		{
			if(Config . Parity != i)
			{
				Config . Parity = i;

				ResetSerial = TRUE;
			}

			break;
		}
	}
}

VOID
RexxSetStopBits(STRPTR String)
{
	LONG Bits = atol(String);

	if(Bits == 1 || Bits == 2)
	{
		if(Config . StopBits != Bits)
		{
			Config . StopBits = Bits;

			ResetSerial = TRUE;
		}
	}
	else
		RexxRes1 = RC_ERROR;
}

VOID
RexxSetHandshaking(STRPTR String)
{
	WORD i;

	for(i = 0 ; i < 3 ; i++)
	{
		if(!Stricmp(String,ConfigHandshaking[i]))
		{
			if(Config . Handshaking != i)
			{
				switch(i)
				{
					case 0:	Config . HandshakingProtocol	= HANDSHAKING_NONE;
						Config . xONxOFF		= FLOW_XON_XOFF;
						break;

					case 1:	Config . HandshakingProtocol	= HANDSHAKING_RTSCTS;
						Config . xONxOFF		= FLOW_NONE;
						break;

					case 2:	Config . HandshakingProtocol	= HANDSHAKING_NONE;
						Config . xONxOFF		= FLOW_NONE;
						break;
				}

				ResetSerial = TRUE;
			}

			break;
		}
	}
}

VOID
RexxSetDuplex(STRPTR String)
{
	WORD i;

	for(i = 0 ; i < 2 ; i++)
	{
		if(!Stricmp(String,ConfigDuplex[i]))
		{
			Config . Duplex = i;

			break;
		}
	}
}

VOID
RexxSetHighSpeed(STRPTR String)
{
	WORD i;

	for(i = 0 ; i < 2 ; i++)
	{
		if(!Stricmp(String,Booleans[i]))
		{
			if(Config . HighSpeed != i)
			{
				Config . HighSpeed = i;

				ResetSerial = TRUE;
			}

			break;
		}
	}
}

VOID
RexxSetBreakLength(STRPTR String)
{
	LONG Value = atol(String);

	if(Config . BreakLength != Value)
	{
		Config . BreakLength = Value;

		ResetSerial = TRUE;
	}
}

VOID
RexxSetSerialDevice(STRPTR String)
{
	if(strcmp(Config . SerialDevice,String))
	{
		strcpy(Config . SerialDevice,String);

		ResetSerial = TRUE;
	}
}

VOID
RexxSetUnitNumber(STRPTR String)
{
	LONG Value = atol(String);

	if(Config . UnitNumber != Value)
	{
		Config . UnitNumber = Value;

		ResetSerial = TRUE;
	}
}

VOID
RexxSetModemInit(STRPTR String)
{
	strcpy(Config . ModemInit,String);
}

VOID
RexxSetModemExit(STRPTR String)
{
	strcpy(Config . ModemExit,String);
}

VOID
RexxSetDialPrefix(STRPTR String)
{
	strcpy(Config . DialPrefix,String);
}

VOID
RexxSetRedialDelay(STRPTR String)
{
	Config . RedialDelay = atol(String);
}

VOID
RexxSetDialRetries(STRPTR String)
{
	Config . DialRetries = atol(String);
}

VOID
RexxSetDialTimeout(STRPTR String)
{
	Config . DialTimeout = atol(String);
}

VOID
RexxSetConnectAutoBaud(STRPTR String)
{
	WORD i;

	for(i = 0 ; i < 2 ; i++)
	{
		if(!Stricmp(String,Booleans[i]))
		{
			Config . ConnectAutoBaud = i;

			break;
		}
	}
}

VOID
RexxSetNoCarrier(STRPTR String)
{
	strcpy(Config . NoCarrier,String);
}

VOID
RexxSetConnect(STRPTR String)
{
	strcpy(Config . Connect,String);
}

VOID
RexxSetVoice(STRPTR String)
{
	strcpy(Config . Voice,String);
}

VOID
RexxSetRing(STRPTR String)
{
	strcpy(Config . Ring,String);
}

VOID
RexxSetBusy(STRPTR String)
{
	strcpy(Config . Busy,String);
}

VOID
RexxSetScreenMode(STRPTR String)
{
	WORD i;

	for(i = 0 ; i < 16 ; i++)
	{
		if(!Stricmp(ConfigDisplayNames[i],String))
		{
			if(!ModeNotAvailable(ModeID[i]))
			{
				if(Config . DisplayMode != ModeID[i])
				{
					BYTE Limited;

					if(Config . ColourMode == COLOUR_EIGHT || Config . ColourMode == COLOUR_SIXTEEN)
						Limited = TRUE;
					else
						Limited = FALSE;

					if(!Limited && ((ModeID[i] & ~MONITOR_ID_MASK) != HIRES_KEY) && ((ModeID[i] & ~MONITOR_ID_MASK) != HIRESLACE_KEY))
					{
						Config . DisplayMode = ModeID[i];

						ResetDisplay = TRUE;
					}
				}
			}

			break;
		}
	}
}

VOID
RexxSetFilter(STRPTR String)
{
	WORD i;

	for(i = 0 ; i < 2 ; i++)
	{
		if(!Stricmp(String,Booleans[i]))
		{
			Config . CaptureFilter = i;

			break;
		}
	}
}

VOID
RexxSetBackspace(STRPTR String)
{
	WORD i;

	for(i = 0 ; i < 2 ; i++)
	{
		if(!Stricmp(String,Booleans[i]))
		{
			Config . DestructiveBackspace = i;

			break;
		}
	}
}

VOID
RexxSetCR(STRPTR String)
{
	if(!Stricmp(String,"IGNORE"))
		Config . SendCR = CR_IGNORE;

	if(!Stricmp(String,"CR"))
		Config . SendCR = CR_ASCR;

	if(!Stricmp(String,"CRLF"))
		Config . SendCR = CR_ASCRLF;
}

VOID
RexxSetLF(STRPTR String)
{
	if(!Stricmp(String,"IGNORE"))
		Config . SendLF = LF_IGNORE;

	if(!Stricmp(String,"LF"))
		Config . SendLF = LF_ASLF;

	if(!Stricmp(String,"LFCR"))
		Config . SendLF = LF_ASLFCR;
}

VOID
RexxSet80Columns(STRPTR String)
{
	WORD i;

	for(i = 0 ; i < 2 ; i++)
	{
		if(!Stricmp(String,Booleans[i]))
		{
			Config . EightyColumns = i;

			ConfigSetup();

			break;
		}
	}
}

VOID
RexxSetColourMode(STRPTR String)
{
	WORD i;

	for(i = 0 ; i < 4 ; i++)
	{
		if(!Stricmp(ConfigColour[i],String))
		{
			if(i != Config . ColourMode)
			{
				BYTE Limited;

				switch(Config . DisplayMode & ~MONITOR_ID_MASK)
				{
					case HIRES_KEY:
					case HIRESLACE_KEY:	Limited = FALSE;
								break;

					default:		Limited = TRUE;
								break;
				}

				if(Config . Emulation == EMULATION_ATOMIC && i > COLOUR_AMIGA && i < COLOUR_MONO)
					Config . Emulation = EMULATION_ANSIVT100;

				if(Limited && i != COLOUR_AMIGA && i != COLOUR_MONO)
				{
					if(Config . DisplayMode & LACE)
						Config . DisplayMode = DEFAULT_MONITOR_ID|HIRESLACE_KEY;
					else
						Config . DisplayMode = DEFAULT_MONITOR_ID|HIRES_KEY;
				}

				Config . ColourMode = i;

				ResetDisplay = TRUE;
			}

			break;
		}
	}
}

VOID
RexxSetEmulation(STRPTR String)
{
	WORD i;

	for(i = 0 ; i < 3 ; i++)
	{
		if(!Stricmp(String,ConfigEmulation[i]))
		{
			if(Config . Emulation != i)
			{
				Config . Emulation = i;

				ResetDisplay = TRUE;
			}

			break;
		}
	}
}

VOID
RexxSetStartup(STRPTR String)
{
	strcpy(Config . StartupMacro,String);
}

VOID
RexxSetFont(STRPTR String)
{
	WORD i;

	for(i = 0 ; i < 2 ; i++)
	{
		if(!Stricmp(String,ConfigFont[i]))
		{
			if(Config . Font != i)
			{
				Config . Font = i;

				ResetDisplay = TRUE;
			}

			break;
		}
	}
}

VOID
RexxSetProtocol(STRPTR String)
{
	if(Stricmp(LastXprLibrary,String))
	{
		UBYTE Name[80];

		WORD i;

		for(i = 0 ; i < strlen(String) ; i++)
			Name[i] = ToLower(String[i]);

		Name[i] = 0;

		strcpy(LastXprLibrary,Name);

		strcpy(Config . Protocol,LastXprLibrary);

		ProtocolSetup();
	}
}

VOID
RexxSetProtocolOptions(STRPTR String)
{
	if(XProtocolBase)
	{
		if(Stricmp(ProtocolOptsBuffer,String))
		{
			strcpy(ProtocolOptsBuffer,String);

			XprIO -> xpr_filename = ProtocolOptsBuffer;

			TransferBits = XProtocolSetup(XprIO);

			if(!(TransferBits & XPRS_SUCCESS))
			{
				CloseLibrary(XProtocolBase);

				XProtocolBase = NULL;

				LastXprLibrary[0] = 0;

				TransferBits = 0;

				RexxRes1 = RC_ERROR;
			}
		}
	}
}

VOID
RexxSetMacro(STRPTR String)
{
	UBYTE Arg1[40],Arg2[40],Arg3[256];
	LONG ArgCount,i,Qualifier = -1;

	Arg1[0] = Arg2[0] = Arg3[0] = 0;
	ArgCount = 0;

	GetToken(String,&ArgCount,Arg1,40);
	GetToken(String,&ArgCount,Arg2,40);
	GetToken(String,&ArgCount,Arg3,256);

	for(i = 0 ; i < 4 ; i++)
	{
		if(!Stricmp(Qualifiers[i],Arg1))
		{
			Qualifier = i;
			break;
		}
	}

	if(Qualifier != -1)
	{
		if(Arg2[0] >= '0' && Arg2[0] <= '9')
			strcpy(MacroKeys -> Keys[Qualifier][Arg2[0] - '0'],Arg3);
		else
			RexxRes1 = RC_ERROR;
	}
	else
		RexxRes1 = RC_ERROR;
}

VOID
RexxSetColour(STRPTR String)
{
	UBYTE Arg1[20],Arg2[20];
	LONG ArgCount;

	Arg1[0] = Arg2[0] = 0;
	ArgCount = 0;

	GetToken(String,&ArgCount,Arg1,20);
	GetToken(String,&ArgCount,Arg2,20);

	if(Arg1[0] && Arg2[0])
	{
		LONG Colour,Value;

		Colour	= atol(Arg1);
		Value	= ahtoi(Arg2);

		if(Colour >= 0 && Colour < (1 << (Config . ColourMode == COLOUR_EIGHT ? 3 : Screen -> RastPort . BitMap -> Depth)))
		{
			Config . Colours[Colour] = Value;

			if(Config . ColourMode == COLOUR_AMIGA && Config . Emulation == EMULATION_ANSIVT100)
			{
				if(Colour == 0)
					BlinkColours[3] = Value;

				if(Colour != 3)
					BlinkColours[Colour] = Value;
			}
			else
			{
				if(Colour == 0 && Config . ColourMode == COLOUR_EIGHT && Config . Emulation == EMULATION_ANSIVT100)
				{
					WORD i;

					for(i = 8 ; i < 16 ; i++)
						BlinkColours[i] = Value;
				}
				else
					BlinkColours[Colour] = Value;
			}

			LoadRGB4(VPort,&Config . Colours[0],(1 << Screen -> RastPort . BitMap -> Depth));
		}
		else
			RexxRes1 = RC_ERROR;
	}
	else
		RexxRes1 = RC_ERROR;
}

VOID
RexxSetScreen(STRPTR String)
{
	UBYTE Arg1[20],Arg2[20];
	LONG ArgCount;

	Arg1[0] = Arg2[0] = 0;
	ArgCount = 0;

	GetToken(String,&ArgCount,Arg1,20);
	GetToken(String,&ArgCount,Arg2,20);

	Config . MakeScreenPublic	= FALSE;
	Config . ShanghaiWindows	= FALSE;

	if(!Stricmp(Arg1,"PUBLIC") || !Stricmp(Arg2,"PUBLIC"))
		Config . MakeScreenPublic = TRUE;

	if(!Stricmp(Arg1,"SHANGHAI") || !Stricmp(Arg2,"SHANGHAI"))
		Config . ShanghaiWindows = TRUE;

	PubScreenStuff();
}

VOID
RexxSetBell(STRPTR String)
{
	UBYTE Arg1[20],Arg2[20];
	LONG ArgCount;

	Arg1[0] = Arg2[0] = 0;
	ArgCount = 0;

	GetToken(String,&ArgCount,Arg1,20);
	GetToken(String,&ArgCount,Arg2,20);

	Config . AudibleBell	= FALSE;
	Config . VisibleBell	= FALSE;

	if(!Stricmp(Arg1,"VISIBLE") || !Stricmp(Arg2,"VISIBLE"))
		Config . VisibleBell = TRUE;

	if(!Stricmp(Arg1,"AUDIBLE") || !Stricmp(Arg2,"AUDIBLE"))
		Config . AudibleBell = TRUE;
}

VOID
RexxSetRequesters(STRPTR String)
{
	WORD i;

	for(i = 0 ; i < 2 ; i++)
	{
		if(!Stricmp(String,Booleans[i]))
		{
			ThisProcess -> pr_WindowPtr = (APTR)(i ? Window : -1);

			break;
		}
	}
}

VOID
RexxSetSerial(STRPTR String)
{
	if(!Stricmp(String,Booleans[0]) && ReadPort)
		DeleteSerial();

	if(!Stricmp(String,Booleans[1]) && !ReadPort)
	{
		if(CreateSerial())
			RexxRes1 = RC_ERROR;
		else
			SerialMessage = NULL;
	}
}

VOID
RexxSetTimeout(STRPTR String)
{
	UBYTE	Arg1[40],Arg2[40];
	LONG	ArgCount;

	ULONG	Time = 0;
	WORD	i;

	Arg1[0] = Arg2[0] = 0;
	ArgCount = 0;

	GetToken(String,&ArgCount,Arg1,40);
	GetToken(String,&ArgCount,Arg2,40);

	for(i = 0 ; i < NUMTIMEREL ; i++)
	{
		if(!Stricmp(Arg2,TimeRelations[i] . Key))
		{
			Time = atol(String) * TimeRelations[i] . Multi;
			break;
		}
	}

	if(Time > 0)
		RexxGlobalTimeout = Time;
	else
		RexxRes1 = RC_ERROR;
}

VOID
RexxSetModemHangup(STRPTR String)
{
	strcpy(Config . ModemHangup,String);
}

VOID
RexxSetAutoCapture(STRPTR String)
{
	WORD i;

	for(i = 0 ; i < 2 ; i++)
	{
		if(!Stricmp(String,Booleans[i]))
		{
			Config . ConnectAutoCapture = i;
			break;
		}
	}
}

VOID
RexxSetLogActions(STRPTR String)
{
	WORD i;

	for(i = 0 ; i < 2 ; i++)
	{
		if(!Stricmp(String,Booleans[i]))
		{
			Config . LogActions = i;
			break;
		}
	}
}

VOID
RexxSetBlinking(STRPTR String)
{
	WORD i;

	for(i = 0 ; i < 2 ; i++)
	{
		if(!Stricmp(String,Booleans[i]))
		{
			Config . DisableBlinking = TRUE ^ i;
			break;
		}
	}
}

VOID
RexxSetCursorMode(STRPTR String)
{
	WORD i;

	for(i = 0 ; i < 2 ; i++)
	{
		if(!Stricmp(String,KeyModes[i]))
		{
			Config . CursorApp = i;
			break;
		}
	}
}

VOID
RexxSetFontScale(STRPTR String)
{
	WORD i;

	for(i = 0 ; i < 5 ; i++)
	{
		if(!Stricmp(String,FontSizes[i]))
		{
			WORD NewScale,Scale;

			Scale = RasterAttr[CursorY];

			ClearCursor();

			NewScale = Scale;

			switch(i)
			{
				case 0:	NewScale = SCALE_NORMAL;
					break;

				case 1:	NewScale = SCALE_ATTR_TOP2X;
					break;

				case 2:	NewScale = SCALE_ATTR_BOT2X;
					break;

				case 3:	NewScale = SCALE_ATTR_2X;
					break;
	
				case 4:	NewScale = SCALE_NORMAL;
					break;
			}

			if(Scale != NewScale)
			{
				UBYTE	*RasterPtr	= &Raster[CursorY * RasterWidth];
				WORD	 RightMargin	= LastColumn + 1,
					 CursorXSave	= CursorX;

				if(NewScale != SCALE_ATTR_NORMAL)
					RightMargin /= 2;

				RasterAttr[CursorY] = NewScale;

				if(((Config . FontScale == SCALE_NORMAL) && (NewScale == SCALE_ATTR_NORMAL)) || ((Config . FontScale == SCALE_HALF) && (NewScale == SCALE_ATTR_2X)))
				{
					Move(RPort,0,CursorY * TextFontHeight + TextFontBase);
					Text(RPort,RasterPtr,RightMargin);
				}
				else
				{
					CursorX = 0;

					PrintScaled(RasterPtr,RightMargin,NewScale);
				}

				if(CursorXSave >= RightMargin)
					CursorX = RightMargin - 1;
				else
					CursorX = CursorXSave;
			}

			SetCursor();

			break;
		}
	}
}

VOID
RexxSetJumpScroll(STRPTR String)
{
	WORD i;

	for(i = 0 ; i < 2 ; i++)
	{
		if(!Stricmp(String,Booleans[i]))
		{
			Config . JumpScroll = TRUE ^ i;
			break;
		}
	}
}

VOID
RexxSetCharacterWrap(STRPTR String)
{
	WORD i;

	for(i = 0 ; i < 2 ; i++)
	{
		if(!Stricmp(String,Booleans[i]))
		{
			Config . AutoWrap = i;
			break;
		}
	}
}

VOID
RexxSetCursorWrap(STRPTR String)
{
	WORD i;

	for(i = 0 ; i < 2 ; i++)
	{
		if(!Stricmp(String,Booleans[i]))
		{
			Config . CursorWrap = i;
			break;
		}
	}
}

VOID
RexxSetNewLine(STRPTR String)
{
	WORD i;

	for(i = 0 ; i < 2 ; i++)
	{
		if(!Stricmp(String,Booleans[i]))
		{
			Config . NewLine = i;
			break;
		}
	}
}

VOID
RexxSetInsert(STRPTR String)
{
	WORD i;

	for(i = 0 ; i < 2 ; i++)
	{
		if(!Stricmp(String,Booleans[i]))
		{
			Config . InsertChar = i;
			break;
		}
	}
}

VOID
RexxSetNumeric(STRPTR String)
{
	WORD i;

	for(i = 0 ; i < 2 ; i++)
	{
		if(!Stricmp(String,KeyModes[i]))
		{
			Config . NumApp = i;
			break;
		}
	}
}

VOID
RexxSetDefaultStore(STRPTR String)
{
	strcpy(Config . DefaultStorage,String);
}

VOID
RexxSetTUploadPath(STRPTR String)
{
	strcpy(Config . TextUploadPath,String);
}

VOID
RexxSetTDownloadPath(STRPTR String)
{
	strcpy(Config . TextDownloadPath,String);
}

VOID
RexxSetAUploadPath(STRPTR String)
{
	strcpy(Config . ASCIIUploadPath,String);
}

VOID
RexxSetADownloadPath(STRPTR String)
{
	strcpy(Config . ASCIIDownloadPath,String);
}

VOID
RexxSetBUploadPath(STRPTR String)
{
	strcpy(Config . BinaryUploadPath,String);
}

VOID
RexxSetBDownloadPath(STRPTR String)
{
	strcpy(Config . BinaryDownloadPath,String);
}

VOID
RexxSetCapturePath(STRPTR String)
{
	strcpy(Config . CapturePath,String);
}

VOID
RexxSetLogFile(STRPTR String)
{
	strcpy(Config . LogFile,String);
}

VOID
RexxSetEditor(STRPTR String)
{
	strcpy(Config . Editor,String);
}

VOID
RexxSetSpeechRate(STRPTR String)
{
	SpeechConfig . Rate = atol(String);

	SpeechSetup();
}

VOID
RexxSetSpeechPitch(STRPTR String)
{
	SpeechConfig . Pitch = atol(String);

	SpeechSetup();
}

VOID
RexxSetSpeechFrequency(STRPTR String)
{
	SpeechConfig . Frequency = atol(String);

	SpeechSetup();
}

VOID
RexxSetSpeechVolume(STRPTR String)
{
	SpeechConfig . Volume = atol(String);

	SpeechSetup();
}

VOID
RexxSetSpeechSex(STRPTR String)
{
	if(!Stricmp(String,"MALE"))
	{
		SpeechConfig . Sex = 0;

		SpeechSetup();
	}
	else
	{
		if(!Stricmp(String,"FEMALE"))
		{
			SpeechConfig . Sex = 1;

			SpeechSetup();
		}
		else
			RexxRes1 = RC_ERROR;
	}
}

VOID
RexxSetSpeech(STRPTR String)
{
	WORD i;

	for(i = 0 ; i < 2 ; i++)
	{
		if(!Stricmp(String,Booleans[i]))
		{
			SpeechConfig . Enabled = i;

			SpeechSetup();

			break;
		}
	}
}

STRPTR 
RexxBreak()
{
	BYTE OldStatus = Status;

	if(WriteRequest)
	{
		Status = STATUS_BREAKING;

		WriteRequest -> IOSer . io_Command = SDCMD_BREAK;

		DoIO(WriteRequest);

		Status = OldStatus;
	}

	return(NULL);
}

STRPTR 
RexxTUpload(STRPTR String)
{
	BinaryTransfer = FALSE;

	RexxBUpload(String);

	return(NULL);
}

STRPTR 
RexxTDownload(STRPTR String)
{
	BinaryTransfer = FALSE;

	RexxBDownload(String);

	return(NULL);
}

STRPTR 
RexxBDownload(STRPTR String)
{
	BYTE OldStatus = Status;

	if(XProtocolBase)
	{
		if(!(TransferBits & XPRS_NORECREQ))
		{
			XprIO -> xpr_filename = String;

			DownloadPath = "";
		}
		else
		{
			if(BinaryTransfer)
				DownloadPath = &Config . BinaryDownloadPath[0];
			else
				DownloadPath = &Config . TextDownloadPath[0];
		}

		if(TransferPanel(BinaryTransfer ? "Download File(s)" : "Download Text"))
		{
			Status = STATUS_DOWNLOAD;

			ClearSerial();

			Uploading = FALSE;

			XProtocolReceive(XprIO);

			Status = OldStatus;

			if(ReadRequest)
			{
				ReadRequest -> IOSer . io_Command	= CMD_READ;
				ReadRequest -> IOSer . io_Data		= ReadBuffer;
				ReadRequest -> IOSer . io_Length	= 1;

				SetSignal(0,SIG_SERIAL);

				SendIO(ReadRequest);
			}

			DeleteTransferPanel();

			if(Config . DownloadMacro[0])
				SerialCommand(Config . DownloadMacro);
		}

		DownloadPath = NULL;
	}

	BinaryTransfer = TRUE;

	return(NULL);
}

STRPTR 
RexxBUpload(STRPTR String)
{
	BYTE OldStatus = Status;

	if(XProtocolBase)
	{
		if(!(TransferBits & XPRS_NOSNDREQ))
			XprIO -> xpr_filename = String;

		if(TransferPanel(BinaryTransfer ? "Upload File(s)" : "Upload Text"))
		{
			Status = STATUS_UPLOAD;

			ClearSerial();

			Uploading = TRUE;

			XProtocolSend(XprIO);

			Uploading = FALSE;

			Status = OldStatus;

			DeleteTransferPanel();

			if(ReadRequest)
			{
				ReadRequest -> IOSer . io_Command	= CMD_READ;
				ReadRequest -> IOSer . io_Data		= ReadBuffer;
				ReadRequest -> IOSer . io_Length	= 1;

				SetSignal(0,SIG_SERIAL);

				SendIO(ReadRequest);
			}

			if(Config . UploadMacro[0])
				SerialCommand(Config . UploadMacro);
		}
	}

	BinaryTransfer = TRUE;

	return(NULL);
}

STRPTR 
RexxWrite(STRPTR String,STRPTR String2)
{
	if(String2[0])
		SerWrite(String2,strlen(String2));
	else
		RexxRes1 = RC_ERROR;

	return(NULL);
}

STRPTR 
RexxResetStyles()
{
	ClearCursor();

	SetAttributes("0m");

	Config . FontScale = SCALE_NORMAL;

	switch(Config . ColourMode)
	{
		case COLOUR_EIGHT:	FgPen = 7;
					break;

		case COLOUR_SIXTEEN:	FgPen = 15;
					break;

		case COLOUR_AMIGA:
		default:		FgPen = 1;
					break;
	}

	BgPen = 0;

	if(RPort -> FgPen != FgPen)
		SetAPen(RPort,FgPen);

	if(RPort -> BgPen != BgPen)
		SetBPen(RPort,BgPen);

	SetWrMsk(RPort,0xFF);

	SetCursor();

	return(NULL);
}

STRPTR
RexxClearScreen()
{
	EraseScreen("2J");
	SetAbsolutePosition("H");

	return(NULL);
}

STRPTR 
RexxSaveILBM(STRPTR String)
{
	if(!SaveRPort(&Screen -> RastPort,VPort,0,Window -> TopEdge,Window -> Width,Window -> Height,Screen -> Width,Screen -> Height,FALSE,String))
		RexxRes1 = RC_ERROR;

	return(NULL);
}

STRPTR 
RexxHangUp()
{
	BYTE OldStatus = Status;

	Status = STATUS_HANGUP;

		/* Are we to drop the DTR line
		 * before sending the hangup
		 * string?
		 */

	if(Config . DropDTR)
	{
		/* Let's be nice and try to transmit the
		 * `drop the line' command before
		 * trying to close and reopen the driver.
		 */

		WriteRequest -> IOSer . io_Command	= SIOCMD_SETCTRLLINES;
		WriteRequest -> IOSer . io_Offset	= SIOB_DTRF;
		WriteRequest -> IOSer . io_Length	= 0;

		/* Transmit the command. */

		if(!DoIO(WriteRequest))
		{
			/* Wait a bit... */

			WaitTime(1,0);

			/* Raise the line again. */

			WriteRequest -> IOSer . io_Command	= SIOCMD_SETCTRLLINES;
			WriteRequest -> IOSer . io_Offset	= SIOB_DTRF;
			WriteRequest -> IOSer . io_Length	= SIOB_DTRF;

			DoIO(WriteRequest);
		}
		else
		{
			/* Do it the standard way: close and reopen
			 * the serial driver (the serial.device is
			 * supposed to drop the DTR line when closed).
			 */

			DropDTR();
		}
	}

		/* Transmit the hangup command. */

	SerialCommand(Config . ModemHangup);

		/* Reset to old status. */

	Status = OldStatus;

		/* We are no longer online. */

	Online = FALSE;

		/* Clear the password. */

	Password[0] = 0;

		/* Clear the user name as well. */

	UserName[0] = 0;

		/* Note the  last action. */

	LogAction("Hung up the line.");

	return(NULL);
}

STRPTR 
RexxGetString(STRPTR String)
{
	RexxTextBuffer[0] = 0;

	if(xpr_gets(String[0] ? String : LocaleString(MSG_TERMREXX_ENTER_TEXT_TXT),RexxTextBuffer))
		return(RexxTextBuffer);
	else
		return(NULL);
}

STRPTR 
RexxCommand(STRPTR String,STRPTR String2)
{
	if(String2[0])
		SerialCommand(String2);
	else
		RexxRes1 = RC_ERROR;

	return(NULL);
}

STRPTR 
RexxMessage(STRPTR String,STRPTR String2)
{
	if(String2[0])
		ConProcess(String2,strlen(String2));
	else
		RexxRes1 = RC_ERROR;

	return(NULL);
}

STRPTR 
RexxPutClip(STRPTR String,STRPTR String2)
{
	if(String2[0])
		SaveClip(String2,strlen(String2));
	else
		RexxRes1 = RC_ERROR;

	return(NULL);
}

STRPTR 
RexxGetClip()
{
	LONG Size;

	if(Size = LoadClip(RexxTextBuffer,255))
	{
		RexxTextBuffer[Size] = 0;

		return(RexxTextBuffer);
	}
	else
		return(NULL);
}

STRPTR 
RexxDelay(STRPTR String)
{
	UBYTE	Arg1[40],Arg2[40];
	LONG	ArgCount;

	ULONG	Time = 0;
	WORD	i;

	Arg1[0] = Arg2[0] = 0;

	ArgCount = 0;

	GetToken(String,&ArgCount,Arg1,40);
	GetToken(String,&ArgCount,Arg2,40);

	for(i = 0 ; i < NUMTIMEREL ; i++)
	{
		if(!Stricmp(Arg2,TimeRelations[i] . Key))
		{
			Time = atol(String) * TimeRelations[i] . Multi;
			break;
		}
	}

	if(Time)
	{
		ULONG SignalSet;

		TimeRequest -> tr_node . io_Command	= TR_ADDREQUEST;
		TimeRequest -> tr_time . tv_secs	= Time / MILLION;
		TimeRequest -> tr_time . tv_micro	= Time % MILLION;

		SendIO(TimeRequest);

		SignalSet = Wait(SIGBREAKF_CTRL_D | SIG_TIMER);

		if(SignalSet & SIGBREAKF_CTRL_D)
		{
			if(!CheckIO(TimeRequest))
				AbortIO(TimeRequest);

			WaitIO(TimeRequest);

			return(NULL);
		}

		WaitIO(TimeRequest);
	}
	else
		RexxRes1 = RC_ERROR;

	return(NULL);
}

STRPTR 
RexxDial(STRPTR String)
{
	LONG i;

	strcpy(RexxTextBuffer,Config . DialPrefix);

	for(i = 0 ; i < NumPhoneEntries ; i++)
	{
		if(!Stricmp(Phonebook[i] -> Name,String))
		{
			strcat(RexxTextBuffer,Phonebook[i] -> Number);

			goto DialIt;
		}
	}

	strcat(RexxTextBuffer,String);

DialIt:	strcat(RexxTextBuffer,"\\r");

	SerialCommand(RexxTextBuffer);

	return(NULL);
}

STRPTR 
RexxInput(STRPTR String)
{
	LONG Length = atol(String);

	if(Length > 255)
		Length = 255;

	ClearSerial();

	if(Length = xpr_sread(RexxTextBuffer,Length,RexxGlobalTimeout))
	{
		RexxTextBuffer[Length] = 0;

		if(ReadRequest)
		{
			ReadRequest -> IOSer . io_Command	= CMD_READ;
			ReadRequest -> IOSer . io_Data		= ReadBuffer;
			ReadRequest -> IOSer . io_Length	= 1;

			SetSignal(0,SIG_SERIAL);

			SendIO(ReadRequest);
		}

		return(RexxTextBuffer);
	}

	if(ReadRequest)
	{
		ReadRequest -> IOSer . io_Command	= CMD_READ;
		ReadRequest -> IOSer . io_Data		= ReadBuffer;
		ReadRequest -> IOSer . io_Length	= 1;

		SetSignal(0,SIG_SERIAL);

		SendIO(ReadRequest);
	}

	return(NULL);
}

STRPTR 
RexxPrinter(STRPTR String)
{
	struct MenuItem *SomeItem;

	SomeItem = FindThisItem(MEN_CAPTURE_TO_PRINTER);

	if(!Stricmp(String,Booleans[0]) && PrinterCapture)
		ClosePrinterCapture(TRUE);

	if(!Stricmp(String,Booleans[1]) && !PrinterCapture)
	{
		if(!OpenPrinterCapture(FALSE))
			RexxRes1 = RC_ERROR;
	}

	return(NULL);
}

STRPTR 
RexxMacros(STRPTR String)
{
	UBYTE Arg1[40],Arg2[256];
	LONG ArgCount;

	Arg1[0] = Arg2[0] = 0;
	ArgCount = 0;

	GetToken(String,&ArgCount,Arg1,40);
	GetToken(String,&ArgCount,Arg2,256);

	if(!Stricmp(Arg1,"SAVE"))
	{
		if(Arg2[0])
		{
			if(WriteIFFData(Arg2,MacroKeys,sizeof(struct MacroKeys),'KEYS'))
				strcpy(LastMacros,Arg2);
			else
				RexxRes1 = RC_ERROR;
		}
	}

	if(!Stricmp(Arg1,"LOAD"))
	{
		if(Arg2[0])
		{
			if(LoadMacros(Arg2,MacroKeys))
				strcpy(LastMacros,Arg2);
			else
				RexxRes1 = RC_ERROR;
		}
	}

	return(NULL);
}

STRPTR 
RexxSpeech(STRPTR String)
{
	UBYTE Arg1[40],Arg2[256];
	LONG ArgCount;

	Arg1[0] = Arg2[0] = 0;
	ArgCount = 0;

	GetToken(String,&ArgCount,Arg1,40);
	GetToken(String,&ArgCount,Arg2,256);

	if(!Stricmp(Arg1,"SAVE"))
	{
		if(Arg2[0])
		{
			if(!WriteIFFData(Arg2,&SpeechConfig,sizeof(struct SpeechConfig),'SPEK'))
				RexxRes1 = RC_ERROR;
			else
				strcpy(LastSpeech,Arg2);
		}
	}

	if(!Stricmp(Arg1,"LOAD"))
	{
		if(Arg2[0])
		{
			if(!ReadIFFData(Arg2,&SpeechConfig,sizeof(struct SpeechConfig),'SPEK'))
				RexxRes1 = RC_ERROR;
			else
			{
				strcpy(LastSpeech,Arg2);

				SpeechSetup();
			}
		}
	}

	return(NULL);
}

STRPTR 
RexxConfig(STRPTR String)
{
	UBYTE Arg1[40],Arg2[256];
	LONG ArgCount;

	Arg1[0] = Arg2[0] = 0;
	ArgCount = 0;

	GetToken(String,&ArgCount,Arg1,40);
	GetToken(String,&ArgCount,Arg2,256);

	if(!Stricmp(Arg1,"SAVE"))
	{
		if(Arg2[0])
		{
			if(WriteIFFData(Arg2,&Config,sizeof(struct Configuration),'PREF'))
				strcpy(LastConfig,Arg2);
			else
				RexxRes1 = RC_ERROR;
		}
	}

	if(!Stricmp(Arg1,"LOAD"))
	{
		if(Arg2[0])
		{
			struct Configuration PrivateConfig;

			SetPrefToDefaults(&PrivateConfig,Config . DefaultStorage);

			if(ReadIFFData(Arg2,&PrivateConfig,sizeof(struct Configuration),'PREF'))
			{
				Config = PrivateConfig;

				ConfigSetup();

				strcpy(LastConfig,Arg2);
			}
			else
				RexxRes1 = RC_ERROR;
		}
	}

	return(NULL);
}

STRPTR 
RexxPhone(STRPTR String)
{
	UBYTE Arg1[40],Arg2[256];
	LONG ArgCount;

	Arg1[0] = Arg2[0] = 0;
	ArgCount = 0;

	GetToken(String,&ArgCount,Arg1,40);
	GetToken(String,&ArgCount,Arg2,256);

	if(!Stricmp(Arg1,"SAVE"))
	{
		if(Arg2[0])
		{
			if(SavePhonebook(Arg2))
				strcpy(LastPhone,Arg2);
			else
				RexxRes1 = RC_ERROR;
		}
	}

	if(!Stricmp(Arg1,"LOAD"))
	{
		if(Arg2[0])
		{
			if(LoadPhonebook(Arg2))
				strcpy(LastPhone,Arg2);
			else
				RexxRes1 = RC_ERROR;
		}
	}

	return(NULL);
}

STRPTR 
RexxCapture(STRPTR String)
{
	UBYTE Arg1[40],Arg2[256];
	LONG ArgCount;

	Arg1[0] = Arg2[0] = 0;
	ArgCount = 0;

	GetToken(String,&ArgCount,Arg1,40);
	GetToken(String,&ArgCount,Arg2,256);

	if(!Stricmp(Arg1,"CLOSE") && FileCapture)
	{
		struct MenuItem *SomeItem;

		SomeItem = FindThisItem(MEN_CAPTURE_TO_FILE);

		BufferClose(FileCapture);

		SomeItem -> Flags &= ~CHECKED;

		FileCapture = NULL;

		if(!GetFileSize(CaptureName))
			DeleteFile(CaptureName);
		else
			SetProtection(CaptureName,FIBF_EXECUTE);
	}
	else
	{
		if(!Stricmp(Arg1,"NEW") && Arg2[0])
		{
			if(FileCapture)
			{
				BufferClose(FileCapture);

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

			FileCapture = BufferOpen(Arg2,"w");
		}
		else
		{
			if(!Stricmp(Arg1,"APPEND") && Arg2[0])
			{
				if(FileCapture)
				{
					BufferClose(FileCapture);

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

				FileCapture = BufferOpen(Arg2,"a");
			}
		}

		if(FileCapture)
		{
			struct MenuItem *SomeItem;

			strcpy(CaptureName,Arg2);

			SomeItem = FindThisItem(MEN_CAPTURE_TO_FILE);

			SomeItem -> Flags |= CHECKED;
		}
		else
		{
			struct MenuItem *SomeItem;

			SomeItem = FindThisItem(MEN_CAPTURE_TO_FILE);

			SomeItem -> Flags &= ~CHECKED;

			RexxRes1 = RC_ERROR;
		}
	}

	return(NULL);
}

STRPTR 
RexxBuffer(STRPTR String)
{
	UBYTE Arg1[40],Arg2[256];
	LONG ArgCount;

	Arg1[0] = Arg2[0] = 0;
	ArgCount = 0;

	GetToken(String,&ArgCount,Arg1,40);
	GetToken(String,&ArgCount,Arg2,256);

	if(!Stricmp(Arg1,"NEW") || !Stricmp(Arg1,"APPEND"))
	{
		if(GetFileSize(Arg2))
		{
			BPTR SomeFile;

			if(SomeFile = Open(Arg2,MODE_OLDFILE))
			{
				LONG Len;

				if(Lines)
				{
					if(!Stricmp(Arg1,"NEW"))
						ClearBuffer();
				}

				LineRead(NULL,NULL,NULL);

				while(Len = LineRead(SomeFile,Arg2,80))
					StoreBuffer(Arg2,Len);

				Close(SomeFile);
			}
			else
				RexxRes1 = RC_ERROR;
		}
		else
			RexxRes1 = RC_ERROR;
	}
	else
	{
		if(!Stricmp(Arg1,"DISPLAY"))
			LaunchBuffer();
	}

	return(NULL);
}

STRPTR 
RexxFirstDownload()
{
	ObtainSemaphore(DownloadSemaphore);

	if(DownloadLineCount)
	{
		DownloadNode = DownloadList . lh_Head;

		if(DownloadNode -> ln_Succ)
		{
			strcpy(RexxTextBuffer,DownloadNode -> ln_Name);

			ReleaseSemaphore(DownloadSemaphore);

			return(RexxTextBuffer);
		}
	}

	ReleaseSemaphore(DownloadSemaphore);

	return("");
}

STRPTR 
RexxNextDownload()
{
	ObtainSemaphore(DownloadSemaphore);

	if(!DownloadNode)
		DownloadNode = DownloadList . lh_Head;
	else
		DownloadNode = DownloadNode -> ln_Succ;

	if(DownloadNode -> ln_Succ)
	{
		strcpy(RexxTextBuffer,DownloadNode -> ln_Name);

		ReleaseSemaphore(DownloadSemaphore);

		return(RexxTextBuffer);
	}
	else
		DownloadNode = NULL;

	ReleaseSemaphore(DownloadSemaphore);

	return("");
}

STRPTR 
RexxLastDownload()
{
	ObtainSemaphore(DownloadSemaphore);

	if(DownloadLineCount)
	{
		DownloadNode = DownloadList . lh_TailPred;

		strcpy(RexxTextBuffer,DownloadNode -> ln_Name);

		ReleaseSemaphore(DownloadSemaphore);

		return(RexxTextBuffer);
	}

	ReleaseSemaphore(DownloadSemaphore);

	return("");
}

STRPTR 
RexxWaitString(STRPTR String)
{
	UBYTE		 Arg1[40];
	LONG		 ArgCount;

	struct ScanNode	*Matching = NULL;

	ULONG		 SignalSet;
	WORD		 i;

	ULONG		 Timeout = RexxGlobalTimeout;

	if(ReadPort)
	{
		Arg1[0] = 0;

		ArgCount = 0;

			/* Parse the argument looking for sequences
			 * to wait for.
			 */

		while(GetToken(String,&ArgCount,Arg1,40))
			AddSequenceObject(Arg1);

			/* If no timeout is set this routine becomes
			 * a real trap: suppose the string to wait for
			 * never happens to arrive -> we'll wait
			 * forever. For this reason the default timeout
			 * is set to five seconds.
			 */

		if(Timeout < 1)
			Timeout = 5 * MILLION;

		TimeRequest -> tr_node . io_Command	= TR_ADDREQUEST;
		TimeRequest -> tr_time . tv_secs	= Timeout >= MILLION ? Timeout / MILLION : 0;
		TimeRequest -> tr_time . tv_micro	= Timeout % MILLION;

		SetSignal(0,SIG_TIMER);

		SendIO(TimeRequest);

		FOREVER
		{
			SignalSet = Wait(SIG_TIMER | SIG_SERIAL | SIGBREAKF_CTRL_D);

				/* We are to abort the job. */

			if(SignalSet & SIGBREAKF_CTRL_D)
			{
				Matching = NULL;
				break;
			}

				/* Serial data came in... */

			if(SignalSet & SIG_SERIAL)
			{
				if(Status == STATUS_HOLDING)
					Status = STATUS_READY;

				/* Any news? */

				if(CheckIO(ReadRequest))
				{
					LONG Length;

					if(!WaitIO(ReadRequest))
					{
						BytesIn++;

						/* Send the byte to the console. */

						ConProcess(ReadBuffer,1);

						if(Matching == NULL)
							Matching = SequenceFilter(((STRPTR )ReadBuffer)[0]);

						/* Loop until all data has been processed. */

Loop:						do
						{
							/* Check how many bytes are still in
							 * the serial buffer.
							 */

							WriteRequest -> IOSer . io_Command = SDCMD_QUERY;

							DoIO(WriteRequest);

							if(Length = WriteRequest -> IOSer . io_Actual)
							{
								if(Length > Config . SerBuffSize)
									Length = Config . SerBuffSize;

								ReadRequest -> IOSer . io_Command	= CMD_READ;
								ReadRequest -> IOSer . io_Data		= ReadBuffer;
								ReadRequest -> IOSer . io_Length	= Length;

								if(!DoIO(ReadRequest))
								{
									BytesIn += ReadRequest -> IOSer . io_Actual;

									/* Send the data to the console. */

									ConProcess(ReadBuffer,Length);
								}

								if(!Matching)
								{
									for(i = 0 ; i < Length ; i++)
									{
										if(Matching = SequenceFilter(((STRPTR )ReadBuffer)[i]))
											break;
									}
								}
							}
						}
						while(Length);
					}

						/* Ask for another byte. */

					ReadRequest -> IOSer . io_Command	= CMD_READ;
					ReadRequest -> IOSer . io_Data		= ReadBuffer;
					ReadRequest -> IOSer . io_Length 	= 1;

					SetSignal(0,SIG_SERIAL);

					SendIO(ReadRequest);
				}
			}

				/* Timeout has occured. */

			if(SignalSet & SIG_TIMER)
				break;

				/* We've made a match! */

			if(Matching)
				break;
		}

			/* Is the timer still active? If so, cancel it. */

		if(!CheckIO(TimeRequest))
			AbortIO(TimeRequest);

			/* Wait for timer to return. */

		WaitIO(TimeRequest);

			/* Determine the result code if a match has been
			 * made.
			 */

		if(Matching)
			strcpy(RexxTextBuffer,Matching -> Sequence);
		else
		{
			RexxTextBuffer[0] = 0;

			RexxRes1 = RC_WARN;
		}
	}

		/* Clear the list of sequences to check. */

	ClearSequenceObjects();

	return(RexxTextBuffer);
}

STRPTR 
RexxToneDial(STRPTR String)
{
	if(ToneDial(String))
		DeleteTone();
	else
		RexxRes1 = RC_ERROR;

	return(NULL);
}

STRPTR 
RexxSimpleRequest(STRPTR String,STRPTR String2)
{
	BlockWindows();

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

	ReleaseWindows();

	return(NULL);
}

STRPTR 
RexxTwoGadRequest(STRPTR String,STRPTR String2)
{
	BYTE Result;

	BlockWindows();

	Result = MyEasyRequest(Window,String2,LocaleString(MSG_GLOBAL_YES_NO_TXT));

	ReleaseWindows();

	if(Result)
		return("YES");
	else
		return("NO");
}

STRPTR 
RexxFileRequest(STRPTR String,STRPTR String2)
{
	struct AslFileRequest *FileRequest;

	BlockWindows();

	MoveScreen(Screen,0,-Screen -> TopEdge);

	ScreenToFront(Screen);

	if(FileRequest = GetFile(String2,"","",RexxTextBuffer,NULL,FALSE,FALSE,FALSE,NULL))
	{
		if(RexxTextBuffer[0])
		{
			FreeAslRequest(FileRequest);

			ReleaseWindows();

			return(RexxTextBuffer);
		}
		else
		{
			FreeAslRequest(FileRequest);

			ReleaseWindows();

			return(NULL);
		}
	}
	else
	{
		ReleaseWindows();

		return(NULL);
	}
}

STRPTR 
RexxSpeak(STRPTR String,STRPTR String2)
{
	Say(String2);

	return(NULL);
}

VOID
Rexx2Front()
{
	if(RexxWindow)
		BumpWindow(RexxWindow);
}

VOID
Term2Front()
{
	BumpWindow(Window);
}

VOID
Display2Front()
{
	if(BufferProcess)
		Signal(BufferProcess,SIGBREAKF_CTRL_D);
}

VOID
CloseDisplay()
{
	if(BufferProcess)
	{
		Signal(BufferProcess,SIGBREAKF_CTRL_C);

		Wait(SIGBREAKF_CTRL_C);
	}
}

VOID
QuietExit()
{
	ExitQuietly = TRUE;
}

VOID
TermExit()
{
	MainTerminated = TRUE;
}

	/* RexxASyncCommand(struct RexxMsg *RexxMsg):
	 *
	 *	This routine handles the asynchronous Rexx
	 *	commands and complains if the command passed
	 *	to it cannot be executed asynchronously.
	 */

BYTE
RexxASyncCommand(struct RexxMsg *RexxMsg,STRPTR Arg1,STRPTR Arg2)
{
	LONG	ArgCount = 0;
	STRPTR	StringResult = NULL;
	WORD	i;
	LONG	RexxRes1 = RC_OK;

	GetToken(RexxMsg -> rm_Args[0],&ArgCount,Arg1,80);
	GetToken(RexxMsg -> rm_Args[0],&ArgCount,Arg2,80);

	if(!Stricmp(Arg1,"QUERY"))
	{
		if(!Stricmp(Arg2,"COLOUR"))
		{
			LONG Colour;

			GetToken(RexxMsg -> rm_Args[0],&ArgCount,Arg1,80);

			Colour = atol(Arg1);

			if(Colour >= 0 && Colour < (1 << (Config . ColourMode == COLOUR_EIGHT ? 3 : Screen -> RastPort . BitMap -> Depth)))
			{
				SPrintf(RexxTextBuffer,"%03lx",Config . Colours[Colour]);

				StringResult = RexxTextBuffer;
			}
			else
				RexxRes1 = RC_ERROR;
		}
		else
		{
			if(!Stricmp(Arg2,"MACRO"))
			{
				LONG Value,Qualifier = -1;

				GetToken(RexxMsg -> rm_Args[0],&ArgCount,Arg2,80);

				for(i = 0 ; i < 4 ; i++)
				{
					if(!Stricmp(Qualifiers[i],Arg2))
					{
						Qualifier = i;
						break;
					}
				}

				if(Qualifier != -1)
				{
					GetToken(RexxMsg -> rm_Args[0],&ArgCount,Arg2,80);

					Value = atol(Arg2);

					if(Value >= 0 && Value <= 9)
						StringResult = MacroKeys -> Keys[Qualifier][Value];
					else
						RexxRes1 = RC_ERROR;
				}
				else
					RexxRes1 = RC_ERROR;
			}
			else
			{
				for(i = 0 ; i < NUMQUERIES ; i++)
				{
					if(!Stricmp(Arg2,QueryCommands[i] . String))
						StringResult = (*QueryCommands[i] . Routine)();
				}
			}
		}
	}
	else
	{
		for(i = 0 ; i < NUMASYNCS ; i++)
		{
			if(!Stricmp(Arg1,ASyncCommands[i] . String))
			{
				(*ASyncCommands[i] . Routine)();

				i = -1;

				break;
			}
		}

		if(i != -1)
			return(FALSE);
	}

	ReplyRexxCommand(RexxMsg,RexxRes1,0,StringResult);

	return(TRUE);
}

	/* RexxSyncCommand(struct RexxMsg *RexxMsg):
	 *
	 *	Handles the synchronous Rexx commands and returns the
	 *	message if no matching command is found.
	 */

VOID
RexxSyncCommand(struct RexxMsg *RexxMsg,STRPTR Arg1,STRPTR Arg2)
{
	LONG	ArgCount = 0,Count2;
	STRPTR	StringResult = NULL;
	WORD	i;

	GetToken(RexxMsg -> rm_Args[0],&ArgCount,Arg1,80);

		/* Save position of second argument for the
		 * Rexx commands.
		 */

	Count2 = ArgCount;

	GetToken(RexxMsg -> rm_Args[0],&ArgCount,Arg2,80);

	RexxRes1 = RC_OK;

		/* Look if it's a `set' command. */

	if(!Stricmp(Arg1,"SET"))
	{
		for(i = 0 ; i < SETMOREPARAMS ; i++)
		{
			if(!Stricmp(Arg2,SetCommands[i] . String))
			{
				(*SetCommands[i] . Routine)(&RexxMsg -> rm_Args[0][ArgCount],NULL);

				ReplyRexxCommand(RexxMsg,RexxRes1,0,NULL);

				return;
			}
		}

		for(i = SETMOREPARAMS ; i < NUMSETS ; i++)
		{
			if(!Stricmp(Arg2,SetCommands[i] . String))
			{
				GetToken(RexxMsg -> rm_Args[0],&ArgCount,Arg2,80);

				(*SetCommands[i] . Routine)(Arg2,NULL);

				ReplyRexxCommand(RexxMsg,RexxRes1,0,NULL);

				return;
			}
		}
	}

	for(i = 0 ; i < REXXMOREPARAMS ; i++)
	{
		if(!Stricmp(Arg1,RexxCommands[i] . String))
		{
			StringResult = (*RexxCommands[i] . Routine)(&RexxMsg -> rm_Args[0][Count2],NULL);

			ReplyRexxCommand(RexxMsg,RexxRes1,0,StringResult);

			return;
		}
	}

	for(i = REXXMOREPARAMS ; i < NUMREXX ; i++)
	{
		if(!Stricmp(Arg1,RexxCommands[i] . String))
		{
			StringResult = (*RexxCommands[i] . Routine)(Arg2,&RexxMsg -> rm_Args[0][Count2 + 1]);

			ReplyRexxCommand(RexxMsg,RexxRes1,0,StringResult);

			return;
		}
	}

	ReplyRexxCommand(RexxMsg,RC_ERROR,0,NULL);
}

	/* RexxServer(VOID):
	 *
	 *	Asynchronous ARexx host server.
	 */

VOID __saveds
RexxServer(VOID)
{
	UBYTE		 Arg1[80],Arg2[80];

	struct MsgPort	*RexxPort;
	struct RexxMsg	*RexxMsg;

	ULONG		 SignalSet;

	BYTE		 Terminated = FALSE;

		/* Create the public host port. */

	if(RexxPort = (struct MsgPort *)CreateMsgPort())
	{
		RexxPort -> mp_Node . ln_Name	= TermIDString;
		RexxPort -> mp_Node . ln_Pri	= 1;

			/* Make it a public port. */

		AddPort(RexxPort);

			/* Signal our father that we're running. */

		Signal(ThisProcess,SIGBREAKF_CTRL_C);

			/* Go into loop and wait for input. */

		while(!Terminated)
		{
			SignalSet = Wait(SIGBREAKF_CTRL_C | (1 << RexxPort -> mp_SigBit));

				/* This is probably a Rexx command. */

			if(SignalSet & (1 << RexxPort -> mp_SigBit))
			{
					/* Pick up all the messages. */

				while(RexxMsg = (struct RexxMsg *)GetMsg(RexxPort))
				{
					Arg1[0] = Arg2[0] = 0;

						/* At first try to run the
						 * command asynchronously.
						 * If this turns out to be
						 * somewhat `impossible' pass
						 * it to the `term' main process
						 * or - if in batch mode - try
						 * to deal with the message
						 * on our own.
						 */

					if(!RexxASyncCommand(RexxMsg,Arg1,Arg2))
					{
						if(!BatchMode)
							PutMsg(TermRexxPort,RexxMsg);
						else
							ReplyRexxCommand(RexxMsg,-1,0,NULL);
					}
				}
			}

			if(SignalSet & SIGBREAKF_CTRL_C)
				Terminated = TRUE;
		}

		Forbid();

		while(RexxMsg = (struct RexxMsg *)GetMsg(RexxPort))
			ReplyRexxCommand(RexxMsg,-1,0,NULL);

		RemPort(RexxPort);

		DeleteMsgPort(RexxPort);
	}

	Forbid();

	RexxProcess = NULL;

	Signal(ThisProcess,SIGBREAKF_CTRL_C);
}

	/* HandleRexx():
	 *
	 *	Tiny & simple subroutine to read and examine all
	 *	messages coming in to be processed synchronously
	 *	by the `term' main process.
	 */

VOID
HandleRexx()
{
	struct RexxMsg	*RexxMsg;
	UBYTE		 Arg1[80],Arg2[256];

	while(RexxMsg = (struct RexxMsg *)GetMsg(TermRexxPort))
	{
		Arg1[0] = Arg2[0] = 0;

		RexxSyncCommand(RexxMsg,Arg1,Arg2);
	}
}
