/*   check_port.c		*/

/*		Copyright © 1989 by Donald T. Meyer, Stormgate Software
 *		All Rights Reserved
 */



#include "rxil.h"

#include <string.h>



static void handle_message( struct RexxMsg *rexxmsg );
static void handle_rexx_message( struct RexxMsg *rexxmsg );
static void handle_return( struct RxilInvocation *rxi );

static int parse_inplace( char *p, char **argptr, int maxargs );



/*------------------------------------------------------------------*/
/*					Functions										*/
/*------------------------------------------------------------------*/

/* NAME
 *		RxilCheckPort
 *
 * SYNOPSIS
 *		RxilCheckPort();
 *
 * FUNCTION
 *		Called by the client program's eventloop upon recieving a message
 *		at one of the ports.
 *		This handles the dispatching ( via RxilDispatch() ) of ARexx
 *		commands, the launching of "looped back" macros, and receiving
 *		the replys to macro invocation messages which we have sent to
 *		Rexxmaster.
 *
 *		This is a "safe" call in that if the ARexx library was unopened
 *		or the port opening failed, nothing will go boom!
 *
 * INPUTS
 *		None
 *
 * RESULT
 *		None
 *
 * SIDES
 *
 * HISTORY
 *		01-Aug-89	Creation.
 *		18-Nov-89	Changed an (incorrect) error return code in line 212.
 *
 * BUGS
 *
 * SEE ALSO
 *		RxilInit(), RxilDispatch()
 */

void RxilCheckPort( void )
{
	struct RexxMsg *rexxmsg;
 
 
	/* Make call "safe" even if RxilInit() failed */
	if( global_rdef == NULL )
	{
		return;
	}


	if( RxilCheckCancel() == TRUE )
	{
		/* It would appear that a cancel requester was posted, and
		 * the user clicked it!
		 */
		RxilCancel();
	}


	if( global_rdef->SecretPort )
	{
		while(  rexxmsg = (struct RexxMsg *)
			GetMsg( global_rdef->SecretPort )  )
		{
			global_rdef->FromRexx = RXIL_SECRET;
			handle_message( rexxmsg );
			global_rdef->FromRexx = 0;
		}
	}
 
	if( global_rdef->PublicPort )
	{
		while( rexxmsg = (struct RexxMsg *)
			GetMsg( global_rdef->PublicPort ) )
		{
			global_rdef->FromRexx = RXIL_PUBLIC;
			handle_message( rexxmsg );
			global_rdef->FromRexx = 0;
		}
	}


	/* Do we want to clear the Abort flag? */

	if( RxilPending() == FALSE )
	{
		if( global_rdef->CReq != NULL )
		{
			RxilEndCancel();
		}

		if(  FlagIsClear( global_rdef->Flags, RXIL_NO_CLEARABORT )  )
		{
			global_rdef->Abort = FALSE;
		}
	}
}



/* This routine is called with the message received at any rexx port.
 * These messages will be of two broad classes.
 * The first is a reply from a command or function which we invoked
 * by sending a packet to RexxMaster.
 *
 * The second is a command sent to our application by an ARexx
 * program.  This could potentialy be from the public port or from
 * the uniquely-named private port.
 */
 
static void handle_message( struct RexxMsg *rexxmsg )
{
	struct RxilInvocation *rxi;
 
 
	/* Decide if it is a reply */
 
	for( rxi=global_rdef->Invocations; rxi; rxi=rxi->Next )
	{
		/* Is this an invocation reply? */
		if( rexxmsg == rxi->RexxMsg )
		{
			/* Yes */
			handle_return( rxi );
			return;
		}
	}
 
 
	/* Apparently not a completion message.  Should be a command
	 * from a Rexx program.
	 */
 
	if( IsRexxMsg(rexxmsg) == FALSE )
	{
		/* This message is not from ARexx.  Don't touch it,
		 * just Reply() it and return.
		 */
		ReplyMsg( (struct Message *)rexxmsg );
		return;
	}
 
 
	/* The message is from an ARexx program */
 
	handle_rexx_message( rexxmsg );
}



/* This will take a command message received from an ARexx program
 * and handle the dispatching and replying.
 * This message MUST be from an ARexx program!
 */

static void handle_rexx_message( struct RexxMsg *rexxmsg )
{
	char *cmdbuf;
	struct RxilInvocation *child_rxi;


	/* Default to okay code. */
	rexxmsg->rm_Result1 = RC_OK;
	rexxmsg->rm_Result2 = 0;


	/* Are we still open for business? */
	if( global_rdef->Abort == TRUE )
	{
		/* Nope! */
		rexxmsg->rm_Result1 = RXERR_ABORTED;
		ReplyMsg( (struct Message *)rexxmsg );
		return;
	}


	/* Handle the message contents */

	/* Just to be squeaky clean, since Bill says to affect only the
	 * result fields, copy this into a buffer before parsing.
	 *
	 * NOTE: Even though an Argstring can by definition contain	
	 * embedded null's, the command line is treated as though it
	 * were null terminated.
	 */
	cmdbuf = AllocMem(  strlen( ARG0(rexxmsg) )+1, 0  );
	if( cmdbuf == NULL )
	{
		rexxmsg->rm_Result1 = RXERR_NO_MEMORY;
		return;
	}

	strcpy( cmdbuf, ARG0(rexxmsg) );

	/* Parse the command string */
	global_rdef->ArgCount =
		parse_inplace( cmdbuf, &(global_rdef->Arg[0]), RXIL_MAX_ARGS );

	if( global_rdef->ArgCount == 0 )
	{
		rexxmsg->rm_Result1 = RXERR_UNKNOWN_CMD;
		FreeMem(  cmdbuf, strlen( ARG0(rexxmsg) )  );
		return;
	}

	RxilDispatch( rexxmsg, global_rdef->Arg[0] );

	FreeMem(  cmdbuf, strlen( ARG0(rexxmsg)+1 )  );


	/* Look at macro loopback */

	if(  ( rexxmsg->rm_Result1 == RXERR_UNKNOWN_CMD ) &&
		FlagIsClear( global_rdef->Flags, RXIL_NOLAUNCH )  )
	{
		/* Unknown command.  Try to launch it as an ARexx macro. */
		child_rxi = RxilCreateRxi(  ARG0(rexxmsg), RXCOMM  );

		if( child_rxi != NULL )
		{
			if(  FlagIsSet( rexxmsg->rm_Action, RXFF_RESULT )  )
			{
				SetFlag( child_rxi->ActionFlags, RXFF_RESULT );
			}

			child_rxi->Parent = rexxmsg;

			if(  RxilLaunch( child_rxi ) == 0  )
			{
				/* Launched successfully */
				return;			/* Return without replying */
			}
			else
			{
				/* Launch failure */
				RxilDeleteRxi( child_rxi );
				rexxmsg->rm_Result1 = RXERR_FAILED;
			}
		}
		else
		{
			/* Unable to create RxilInvocation structure */
			rexxmsg->rm_Result1 = RXERR_NO_MEMORY;
		}
	}


	RxilCheckResult( rexxmsg );

	ReplyMsg( (struct Message *)rexxmsg );
}



static void handle_return( struct RxilInvocation *rxi )
{
	rxi->State = RXIL_STATE_RETURNED;

	if( rxi->CommAddr == global_rdef->SecretPortName )
	{
		/* This implies that the locked flag was set while
		 * this ran.  Decrement the lock count.
		 */
		--(global_rdef->LockCount);
	}


	if( rxi->Parent == NULL )
	{
		/* This was originated by the client.  Let the client
		 * worry about handling the return and doing the cleanup.
		 */
		return;
	}


	/* This is the return for a program which was "looped" back out
	 * to ARexx.
	 */

	/* Pass thru?? the result */

	rxi->Parent->rm_Result1 = rxi->RexxMsg->rm_Result1;
	rxi->Parent->rm_Result2 = rxi->RexxMsg->rm_Result2;

	RxilCheckResult( rxi->Parent );


	/* Reply to the parent */
	ReplyMsg(  (struct Message *)(rxi->Parent)  );


	/* Cleanup the invocation RexxMsg */

	/* Zero these to prevent the cleanup routine from freeing
	 * the return Argstring.
	 */
	rxi->RexxMsg->rm_Result1 = 0;
	rxi->RexxMsg->rm_Result2 = 0;

	/* Clear these to prevent the cleanup routine from closing
	 * streams which were inherited from the parent.
	 */
	rxi->RexxMsg->rm_Stdin = NULL;
	rxi->RexxMsg->rm_Stdout = NULL;

	RxilCleanupReturn( rxi );


	/* And free the RxilInvocation structure */
	RxilDeleteRxi( rxi );
}



/* This is intended to be a very memory efficient parsing procedure.
 * The length of the options is limited only by the length of the
 * input line buffer (owned by the caller).  The number of arguments which
 * can be parsed out of an input string is limited by the size of the
 * pointer array, which is also owned by the caller.
 * Note that this WILL MODIFY the contents of the line passed in the
 * buffer.  If this is un-desirable, copy the string to a scratch buffer
 * and pass that.
 *
 * The calling should look something like:
 *		char *myargpointers[10];
 *		int myargcount;
 *		char buf[100];
 *
 *		gets( buf );
 *		myargcount = inplace_parse( buf, myargpointers, 10 );
 */

static int parse_inplace( char *p, char **argptr, int maxargs )
{
	BOOL inparen = FALSE, inarg = FALSE;
	char *argstart = 0;		/* Does not really need to be initialized */
	int argcount = 0;
 
 
	while(  (*p != '\0') && (argcount < maxargs)  )
	{
		switch( *p )
		{
			case ' ':
			case '\t':
				/* Whitespace */
				if(  (inarg == TRUE) && (inparen == FALSE)  )
				{
					/* We got one! */
					*p = '\0';		/* place a delimiting null */
					argptr[argcount++] = argstart;
					inarg = FALSE;
					inparen = FALSE;
				}
				break;
 
			case '"':
				if( inparen == TRUE )
				{
					/* This is closing paren */
					/* We got one! */
					*p = '\0';		/* place a delimiting null */
					argptr[argcount++] = argstart;
					inarg = FALSE;
					inparen = FALSE;
				}
				else
				{
					/* Opening paren */
					if( inarg == TRUE )
					{
						/* Deal with the previous arg first */
						*p = '\0';		/* place a delimiting null */
						argptr[argcount++] = argstart;
					}
 
					inparen = TRUE;
					inarg = TRUE;
					argstart = p+1;
				}
				break;
 
			default:
				/* A character we want in an argument string */
				if( inarg == FALSE )
				{
					inarg = TRUE;
					argstart = p;
				}
				break;
		}
 
		p++;
	}
 
	if(  (inarg == TRUE) && (argcount < maxargs)  )
		argptr[argcount++] = argstart;
 
	return( argcount );
}

