/*
**	termARexx.c
**
**	ARexx interface general support routines
**
**	Copyright © 1990-1995 by Olaf `Olsen' Barthel
**		All Rights Reserved
*/

#include "termARexxGlobal.h"

	/* IsNumeric(STRPTR String):
	 *
	 *	Is the string really a number?
	 */

BYTE __regargs
IsNumeric(STRPTR String)
{
	while(*String && (*String == ' ' || *String == '\t'))
		String++;

	while(*String)
	{
		if(*String < '0' || *String > '9')
			return(FALSE);
		else
			String++;
	}

	return(TRUE);
}

	/* CreateResult(STRPTR ResultString,LONG *Results):
	 *
	 *	Create a proper Rexx result string.
	 */

STRPTR __regargs
CreateResult(STRPTR ResultString,LONG *Results)
{
	STRPTR Result;

	if(!(Result = CreateArgstring(ResultString,strlen(ResultString))))
	{
		Results[0] = RC_ERROR;
		Results[1] = ERROR_NO_FREE_STORE;
	}

	return(Result);
}

	/* CreateResultLen(STRPTR ResultString,LONG *Results):
	 *
	 *	Create a proper Rexx result string given the
	 *	length of the source string.
	 */

STRPTR __regargs
CreateResultLen(STRPTR ResultString,LONG *Results,LONG Len)
{
	STRPTR Result;

	if(!(Result = CreateArgstring(ResultString,Len)))
	{
		Results[0] = RC_ERROR;
		Results[1] = ERROR_NO_FREE_STORE;
	}

	return(Result);
}

	/* CreateVarArgs(STRPTR Value,struct RexxPkt *Packet,STRPTR Stem,...):
	 *
	 *	Set a Rexx variable, special treatment.
	 */

BYTE __stdargs
CreateVarArgs(STRPTR Value,struct RexxPkt *Packet,STRPTR Stem,...)
{
	UBYTE	Name[256];
	va_list	VarArgs;
	LONG	Result;

	va_start(VarArgs,Stem);
	VSPrintf(Name,Stem,VarArgs);
	va_end(VarArgs);

	if(Result = SetRexxVar(Packet -> RexxMsg,Name,Value,strlen(Value)))
	{
		Packet -> Results[0] = RC_ERROR;
		Packet -> Results[1] = Result;

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

	/* CreateVar(STRPTR Value,struct RexxPkt *Packet,STRPTR Name):
	 *
	 *	Set a Rexx variable, simple version.
	 */

STRPTR __regargs
CreateVar(STRPTR Value,struct RexxPkt *Packet,STRPTR Name)
{
	LONG Result;

	if(Result = SetRexxVar(Packet -> RexxMsg,Name,Value,strlen(Value)))
	{
		Packet -> Results[0] = RC_ERROR;
		Packet -> Results[1] = Result;
	}

	return(NULL);
}

	/* CreateMatchBuffer(STRPTR Pattern):
	 *
	 *	Create a pattern buffer suitable for pattern matching.
	 */

STRPTR __regargs
CreateMatchBuffer(STRPTR Pattern)
{
	WORD	Len = strlen(Pattern) + 1;
	STRPTR	Buffer;

	if(Buffer = (STRPTR)AllocVecPooled(2 * Len,MEMF_ANY))
	{
		if(ParsePatternNoCase(Pattern,Buffer,2 * Len) != -1)
			return(Buffer);
		else
			FreeVecPooled(Buffer);
	}

	return(NULL);
}

	/* MatchBuffer(STRPTR Buffer,STRPTR Name):
	 *
	 *	Match a pattern against a string.
	 */

BYTE __regargs
MatchBuffer(STRPTR Buffer,STRPTR Name)
{
	return((BYTE)MatchPatternNoCase(Buffer,Name));
}

	/* DeleteMatchBuffer(STRPTR Buffer):
	 *
	 *	Free a pattern matching buffer.
	 */

VOID __regargs
DeleteMatchBuffer(STRPTR Buffer)
{
	FreeVecPooled(Buffer);
}

	/* ToMode(STRPTR Name):
	 *
	 *	Turn a transfer mode name into a key.
	 */

WORD __regargs
ToMode(STRPTR Name)
{
	STATIC STRPTR TransferModes[] =
	{
		"BINARY",
		"TEXT",
		"ASCII",
		NULL
	};

	WORD i;

	for(i = 0 ; TransferModes[i] ; i++)
	{
		if(!Stricmp(Name,TransferModes[i]))
			return(i);
	}

	return(-1);
}

	/* ToList(STRPTR Name):
	 *
	 *	Turn a list name into a key.
	 */

WORD __regargs
ToList(STRPTR Name)
{
	STATIC STRPTR ListTypes[GLIST_COUNT] =
	{
		"UPLOAD",
		"DOWNLOAD",
		"DIAL",
		"WAIT",
		"TRAP"
	};

	WORD i;

	for(i = 0 ; i < GLIST_COUNT ; i++)
	{
		if(!Stricmp(Name,ListTypes[i]))
			return(i);
	}

	return(-1);
}

	/* ToConfig(STRPTR Name):
	 *
	 *	Turn a configuration name into a key.
	 */

WORD __regargs
ToConfig(STRPTR Name)
{
	STATIC STRPTR DataTypes[DATATYPE_COUNT] =
	{
		"TRANSLATIONS",
		"FUNCTIONKEYS",
		"CURSORKEYS",
		"FASTMACROS",
		"HOTKEYS",
		"SPEECH",
		"SOUND",
		"BUFFER",
		"CONFIGURATION",
		"PHONE",
		"SCREENTEXT",
		"SCREENIMAGE"
	};

	WORD i;

	for(i = 0 ; i < DATATYPE_COUNT ; i++)
	{
		if(!Stricmp(Name,DataTypes[i]))
			return(i);
	}

	return(-1);
}

	/* ToRequester(STRPTR Name):
	 *
	 *	Turn a requester name into a key.
	 */

WORD __regargs
ToRequester(STRPTR Name)
{
	STRPTR RequesterTypes[REQUESTER_COUNT] =
	{
		"SERIAL",
		"MODEM",
		"SCREEN",
		"TERMINAL",
		"EMULATION",
		"CLIPBOARD",
		"CAPTURE",
		"COMMANDS",
		"MISC",
		"PATH",
		"TRANSFER",
		"TRANSLATIONS",
		"FUNCTIONKEYS",
		"CURSORKEYS",
		"FASTMACROS",
		"HOTKEYS",
		"SPEECH",
		"SOUND",
		"PHONE"
	};

	WORD i;

	for(i = 0 ; i < REQUESTER_COUNT ; i++)
	{
		if(!Stricmp(Name,RequesterTypes[i]))
			return(i);
	}

	return(-1);
}

	/* ToWindow(STRPTR Name):
	 *
	 *	Turn a window name into a key.
	 */

WORD __regargs
ToWindow(STRPTR Name)
{
	STATIC STRPTR WindowTypes[WINDOWID_COUNT] =
	{
		"BUFFER",
		"REVIEW",
		"PACKET",
		"FASTMACROS",
		"STATUS",
		"MAIN",
		"UPLOADQUEUE"
	};

	WORD i;

	for(i = 0 ; i < WINDOWID_COUNT ; i++)
	{
		if(!Stricmp(WindowTypes[i],Name))
			return(i);
	}

	return(-1);
}

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

STATIC VOID __regargs
ReplyRexxCommand(struct RexxMsg *RexxMessage,LONG Primary,LONG Secondary)
{
	if(RexxMessage)
	{
		RexxMessage -> rm_Result1 = Primary;
		RexxMessage -> rm_Result2 = Secondary;

		ReplyMsg(RexxMessage);
	}
}

	/* RexxToolServer(VOID):
	 *
	 *	Asynchronous tool/rexx command execution process.
	 */

STATIC VOID __saveds
RexxToolServer(VOID)
{
	struct Process	*ThisProcess = (struct Process *)SysBase -> ThisTask;
	struct RexxPkt	*Packet;
	BPTR		 OldCOS,
			 NewCOS	= NULL;

		/* Wait for startup packet. */

	WaitPort(&ThisProcess -> pr_MsgPort);

	Packet = (struct RexxPkt *)GetMsg(&ThisProcess -> pr_MsgPort);

		/* Increment usage count. */

	ObtainSemaphore(&RexxLaunchSemaphore);

	RexxLaunchCount++;

	ReleaseSemaphore(&RexxLaunchSemaphore);

		/* Create proper output stream if necessary. */

	if(!ThisProcess -> pr_COS && ThisProcess -> pr_ConsoleTask)
	{
		if(NewCOS = Open("*",MODE_NEWFILE))
		{
			OldCOS = ThisProcess -> pr_COS;

			ThisProcess -> pr_COS = NewCOS;
		}
	}

		/* Execute the command. */

	(*Packet -> CommandInfo -> Routine)(Packet);

	Forbid();

		/* Decrement usage count. */

	ObtainSemaphore(&RexxLaunchSemaphore);

	RexxLaunchCount--;

	ReleaseSemaphore(&RexxLaunchSemaphore);

		/* Close the output stream. */

	if(NewCOS)
	{
		ThisProcess -> pr_COS = OldCOS;

		Close(NewCOS);
	}

		/* Return the message packet. */

	ReplyMsg((struct Message *)Packet);
}

	/* InvokeRexxCommand(struct RexxPkt *Packet):
	 *
	 *	Invoke an ARexx command.
	 */

STATIC BYTE __regargs
InvokeRexxCommand(struct RexxPkt *Packet)
{
		/* Asynchronous command? */

	if(Packet -> CommandInfo -> Async)
	{
			/* Requires special execution code? */

		if(Packet -> CommandInfo -> Tool)
		{
			struct RexxPkt *NewPacket;

				/* Create a command packet. */

			if(NewPacket = (struct RexxPkt *)AllocVecPooled(sizeof(struct RexxPkt),MEMF_ANY | MEMF_PUBLIC))
			{
				enum	{	ARG_CONSOLE };

				struct Process	*NewProcess;
				BPTR		 Stream;

					/* Set up the command packet. */

				CopyMem(Packet,NewPacket,sizeof(struct RexxPkt));

				NewPacket -> VanillaMessage . mn_ReplyPort	= RexxPort;
				NewPacket -> VanillaMessage . mn_Length		= sizeof(struct RexxPkt);

					/* Create I/O streams. */

				if(Packet -> Array[ARG_CONSOLE] && WindowName[0] && Packet -> CommandInfo -> Console)
					Stream = Open(WindowName,MODE_NEWFILE);
				else
					Stream = NULL;

					/* Launch the process. */

				if(Stream && GoodStream(Stream))
				{
					struct FileHandle *Handle = (struct FileHandle *)BADDR(Stream);

					NewProcess = CreateNewProcTags(
						NP_Entry,	RexxToolServer,
						NP_Input,	Stream,
						NP_Output,	NULL,
						NP_ConsoleTask,	Handle -> fh_Type,
						NP_StackSize,	8000,
						NP_Name,	"term Rexx Tool Process",
						NP_Cli,		TRUE,
					TAG_DONE);
				}
				else
				{
					NewProcess = CreateNewProcTags(
						NP_Entry,	RexxToolServer,
						NP_StackSize,	8000,
						NP_ConsoleTask,	NULL,
						NP_Name,	"term Rexx Tool Process",
						NP_Cli,		TRUE,
					TAG_DONE);
				}

					/* Send the command packet. */

				if(NewProcess)
					PutMsg(&NewProcess -> pr_MsgPort,(struct Message *)NewPacket);
				else
				{
					FreeVecPooled(NewPacket);

					ReplyRexxCommand(Packet -> RexxMsg,-1,0);

					if(Stream)
						Close(Stream);
				}
			}
			else
				ReplyRexxCommand(Packet -> RexxMsg,-1,0);
		}
		else
		{
				/* Execute the command on the schedule of the
				 * rexx server process.
				 */

			STRPTR Result = (*Packet -> CommandInfo -> Routine)(Packet);

			RexxPktCleanup(Packet,Result);
		}
	}
	else
	{
		struct RexxPkt *NewPacket;

			/* Create message packet. */

		if(NewPacket = (struct RexxPkt *)AllocVecPooled(sizeof(struct RexxPkt),MEMF_ANY | MEMF_PUBLIC))
		{
				/* Set up message packet. */

			CopyMem(Packet,NewPacket,sizeof(struct RexxPkt));

			NewPacket -> VanillaMessage . mn_ReplyPort	= RexxPort;
			NewPacket -> VanillaMessage . mn_Length		= sizeof(struct RexxPkt);

				/* Post it. */

			PutMsg(TermRexxPort,(struct Message *)NewPacket);
		}
		else
			ReplyRexxCommand(Packet -> RexxMsg,-1,0);
	}

	return(TRUE);
}

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

STATIC BYTE __regargs
ParseRexxCommand(struct RexxMsg *RexxMsg)
{
	UBYTE	CommandBuffer[30];
	STRPTR	Command,
		CommandArgs;
	LONG	Len = 0;

		/* Clear the local variables. */

	CommandBuffer[0]	= 0;
	Command			= RexxMsg -> rm_Args[0];
	CommandArgs		= NULL;

		/* Skip leading blank spaces. */

	while(*Command && (*Command == ' ' || *Command == '\t'))
		Command++;

		/* Extract the command name. */

	do
	{
			/* Found the end of the string? */

		if(!Command[Len])
		{
				/* Copy the command name. */

			strcpy(CommandBuffer,Command);

				/* No arguments are provided. */

			CommandArgs = NULL;

			break;
		}

			/* Found a blank space? */

		if(Command[Len] == ' ' || Command[Len] == '\t')
		{
				/* Copy the command name. */

			memcpy(CommandBuffer,Command,Len);

			CommandBuffer[Len] = 0;

				/* Look for any arguments. */

			CommandArgs = &Command[Len + 1];

				/* Skip blank spaces. */

			while(*CommandArgs && (*CommandArgs == ' ' || *CommandArgs == '\t'))
				CommandArgs++;

			break;
		}
	}
	while(++Len < 30);

		/* Did we find a command name? */

	if(CommandBuffer[0])
	{
		struct CommandInfo	*CommandInfo = NULL;
		LONG			 CommandIndex;

			/* Which command is it? */

		for(CommandIndex = 0 ; CommandIndex < CommandTableSize ; CommandIndex++)
		{
			if(!Stricmp(CommandBuffer,CommandTable[CommandIndex] . Name))
			{
				CommandInfo = &CommandTable[CommandIndex];

				break;
			}
		}

			/* Did we find the command? */

		if(CommandInfo)
		{
			struct RexxPkt __aligned	Packet;
			BYTE				Processed = FALSE;

				/* Set the result codes to defaults. */

			Packet . Results[0]	= RC_OK;
			Packet . Results[1]	= 0;

				/* Fill in the rest. */

			Packet . CommandInfo	= CommandInfo;
			Packet . RexxMsg	= RexxMsg;

				/* Does this command accept any arguments? */

			if(CommandInfo -> Arguments)
			{
				LONG *Array;

					/* Determine length of argument string. */

				if(CommandArgs)
					Len = strlen(CommandArgs);
				else
					Len = 0;

					/* Allocate temporary buffer, we will need to
					 * attach a line-feed character to the argument
					 * string.
					 */

				if(Array = (LONG *)AllocVecPooled(12 * sizeof(LONG) + Len + 2,MEMF_ANY | MEMF_CLEAR | MEMF_PUBLIC))
				{
					STRPTR		 Buffer;
					struct RDArgs	*Args;

						/* Get the argument buffer. */

					Buffer = (STRPTR)&Array[12];

						/* Copy the argument string. */

					if(CommandArgs && Len)
						memcpy(Buffer,CommandArgs,Len);

						/* Attach the line-feed character. */

					Buffer[Len] = '\n';

						/* Allocate argument parser data. */

					if(Args = (struct RDArgs *)AllocDosObjectTags(DOS_RDARGS,TAG_DONE))
					{
						Packet . Array	= (STRPTR *)Array;
						Packet . Args	= Args;

							/* Don't prompt for input! */

						Args -> RDA_Flags |= RDAF_NOPROMPT;

							/* Set up parser data. */

						Args -> RDA_Source . CS_Buffer	= Buffer;
						Args -> RDA_Source . CS_Length	= Len + 1;
						Args -> RDA_Source . CS_CurChr	= 0;

							/* Parse the arguments. */

						if(ReadArgs(CommandInfo -> Arguments,Array,Args))
						{
							UWORD	Inclusion	= InclusionTable[CommandIndex];
							BYTE	ArgsRequired	= FALSE;
							WORD	i,Counted;

								/* Look for required arguments. */

							for(i = Counted = 0 ; i < 12 ; i++)
							{
								if(Inclusion & (1L << i))
								{
									ArgsRequired = TRUE;

									if(Array[i])
										Counted++;
								}
							}

								/* Are any arguments required
								 * but not provided?
								 */

							if(ArgsRequired && !Counted)
							{
								Packet . Results[0] = RC_ERROR;
								Packet . Results[1] = ERROR_REQUIRED_ARG_MISSING;
							}
							else
							{
								struct ExclusionInfo *Exclusion = ExclusionTable[CommandIndex];

									/* Any mutually-exclusive arguments? */

								if(Exclusion)
								{
									BYTE ArgsOkay = TRUE;

									i = 0;

										/* Look for arguments to
										 * exclude each other.
										 */

									while(ArgsOkay && Exclusion[i] . A != -1)
									{
										if(Array[Exclusion[i] . A] && Array[Exclusion[i] . B])
											ArgsOkay = FALSE;
										else
											i++;
									}

										/* All arguments correct? */

									if(ArgsOkay)
										Processed = InvokeRexxCommand(&Packet);
									else
									{
										Packet . Results[0] = RC_ERROR;
										Packet . Results[1] = ERROR_TOO_MANY_ARGS;
									}
								}
								else
									Processed = InvokeRexxCommand(&Packet);
							}

								/* Free allocated parser data. */

							if(!Processed)
								FreeArgs(Args);
						}
						else
						{
							LONG Error = IoErr();

							SetIoErr(Error);

							Packet . Results[0] = RC_ERROR;
							Packet . Results[1] = IoErr();
						}

							/* Free parser data. */

						if(!Processed)
							FreeDosObject(DOS_RDARGS,Args);
					}
					else
					{
						Packet . Results[0] = RC_ERROR;
						Packet . Results[1] = ERROR_NO_FREE_STORE;
					}

						/* Free temporary buffer. */

					if(!Processed)
						FreeVecPooled(Array);
				}
				else
				{
					Packet . Results[0] = RC_ERROR;
					Packet . Results[1] = ERROR_NO_FREE_STORE;
				}
			}
			else
			{
				Packet . Array	= NULL;
				Packet . Args	= NULL;

				Processed = InvokeRexxCommand(&Packet);
			}

			if(!Processed)
			{
				if(Packet . Results[0] && Packet . Results[1])
					LastRexxError = Packet . Results[1];

				ReplyRexxCommand(RexxMsg,Packet . Results[0],Packet . Results[1]);
			}

			return(TRUE);
		}
	}

	return(FALSE);
}

	/* RexxPktCleanup(struct RexxPkt *Packet,STRPTR Result):
	 *
	 *	Free the memory allocated for a message packet.
	 */

VOID __regargs
RexxPktCleanup(struct RexxPkt *Packet,STRPTR Result)
{
	if(Packet -> Args)
	{
		FreeArgs(Packet -> Args);

		FreeDosObject(DOS_RDARGS,Packet -> Args);
	}

	if(Packet -> Array)
		FreeVecPooled(Packet -> Array);

	if(Packet -> Results[0])
	{
			/* Store error code. */

		if(Packet -> Results[1])
		{
			UBYTE Buffer[10];

			SPrintf(Buffer,"%ld",LastRexxError = Packet -> Results[1]);

			if(Packet -> RexxMsg)
				SetRexxVar(Packet -> RexxMsg,"TERM.LASTERROR",Buffer,strlen(Buffer));
		}

		ReplyRexxCommand(Packet -> RexxMsg,Packet -> Results[0],Packet -> Results[1]);

		if(Result)
			DeleteArgstring(Result);
	}
	else
	{
		if(Result)
		{
			if(Packet -> RexxMsg)
			{
				if(Packet -> RexxMsg -> rm_Action & RXFF_RESULT)
					ReplyRexxCommand(Packet -> RexxMsg,0,(LONG)Result);
				else
				{
					DeleteArgstring(Result);

					ReplyRexxCommand(Packet -> RexxMsg,0,0);
				}
			}
			else
				DeleteArgstring(Result);
		}
		else
			ReplyRexxCommand(Packet -> RexxMsg,0,0);
	}
}

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

VOID __saveds
RexxServer(VOID)
{
		/* Create the public host port. */

	if(RexxPort = CreateMsgPort())
	{
		struct RexxMsg	*RexxMsg;
		ULONG		 SignalSet;
		BYTE		 Done = FALSE;

		InitSemaphore(&RexxLaunchSemaphore);

		RexxPort -> mp_Node . ln_Name	= RexxPortName;
		RexxPort -> mp_Node . ln_Pri	= 1;

			/* Make it a public port. */

		AddPort(RexxPort);

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

		Signal(ThisProcess,SIG_HANDSHAKE);

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

		do
		{
			SignalSet = Wait(SIG_KILL | PORTMASK(RexxPort));

				/* Are we to quit? */

			if(SignalSet & SIG_KILL)
				Done = TRUE;

				/* This is probably a Rexx command. */

			if(SignalSet & PORTMASK(RexxPort))
			{
					/* Pick up all the messages. */

				while(RexxMsg = (struct RexxMsg *)GetMsg(RexxPort))
				{
						/* This is probably the reply to some
						 * synchronous function invocation.
						 */

					if(RexxMsg -> rm_Node . mn_Node . ln_Type == NT_REPLYMSG)
						FreeVecPooled(RexxMsg);
					else
					{
							/* 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(!ParseRexxCommand(RexxMsg))
						{
							UBYTE Buffer[10];

							SPrintf(Buffer,"%ld",LastRexxError = TERMERROR_UNKNOWN_COMMAND);

							SetRexxVar(RexxMsg,"TERM.LASTERROR",Buffer,strlen(Buffer));

							ReplyRexxCommand(RexxMsg,RC_ERROR,TERMERROR_UNKNOWN_COMMAND);
						}
					}
				}
			}
		}
		while(!Done);

		Done = FALSE;

			/* Process remaining messages. */

		do
		{
			while(RexxMsg = (struct RexxMsg *)GetMsg(RexxPort))
			{
				if(RexxMsg -> rm_Node . mn_Node . ln_Type == NT_REPLYMSG)
					FreeVecPooled(RexxMsg);
				else
					ReplyRexxCommand(RexxMsg,-1,0);
			}

			ObtainSemaphore(&RexxLaunchSemaphore);

			if(!RexxLaunchCount)
			{
				Done = TRUE;

				ReleaseSemaphore(&RexxLaunchSemaphore);
			}
			else
			{
				ReleaseSemaphore(&RexxLaunchSemaphore);

				WaitPort(RexxPort);
			}
		}
		while(!Done);

		DeleteMsgPort(RexxPort);

		RexxPort = NULL;
	}

	Forbid();

	RexxProcess = NULL;

	Signal(ThisProcess,SIG_HANDSHAKE);
}

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

BYTE
HandleRexx()
{
	struct RexxPkt *Packet;

		/* Obtain the message packet. */

	if(Packet = (struct RexxPkt *)GetMsg(TermRexxPort))
	{
		STRPTR Result;

		InRexx = TRUE;

		UpdateRequired = TransferUpdateRequired = FALSE;

			/* Execute the command. */

		Result = (*Packet -> CommandInfo -> Routine)(Packet);

			/* Free the packet data. */

		RexxPktCleanup(Packet,Result);

			/* Update the configuration if necessary. */

		if(UpdateRequired)
			ConfigSetup();

			/* Update the XPR options if necessary. */

		if(TransferUpdateRequired)
		{
			if(ProtocolSetup(TRUE))
				SaveProtocolOpts();
		}

			/* Return the message packet. */

		ReplyMsg((struct Message *)Packet);

		InRexx = FALSE;

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