/* demo.c	*/



/* A small demo program for the Rxil library.
 *
 * This must be run from the CLI only.  This is due to the diagnostic
 * info that is sent to the CLI.
 *
 * This program implments a fairly full-featured ARexx port.
 * The demo program itself is more along the lines of a "hack" and
 * was not written with robustness in mind (especially the child macro
 * launching stuff).
 * It does show how few the lines of code needed are.
 */



/*		Copyright © 1989 by Donald T. Meyer, Stormgate Software
 *		All Rights Reserved
 *
 *		This source code may be compiled and used in any software
 *		product.
 *		No portion of this source code is to be re-distributed or
 *		sold for profit.
 *
 *		Donald T. Meyer
 *		Stormgate Software
 *		P.O. Box 383
 *		St. Peters, MO  63376
 *
 *		BIX:	donmeyer		(usually daily)
 *		GEnie:	D.MEYER			(weekly)
 *		PLINK:	Stormgate		(weekly)
 */



/*		The Commands this program understands from ARexx:
 *
 *		STATUS <l|b|x|y|z>
 *		A do-nothing command which returns various strings and result
 *		codes.
 *
 *		DUMP [e|c|s]
 *		Display some things about the ARexx environment that this
 *		program has established.
 *
 *		LOCK
 *		Engage this program in an exclusive conversation.
 *		Returns the name of the secret port.
 *
 *		UNLOCK
 *		Releases the lock.
 *
 *		LCMD
 *		Allows the launching of an ARexx program as a "command".
 *
 *		LFUNC
 *		Allows the launching of an ARexx program as a "function".
 *
 *		PTEST
 *		Returns a dummy string.  Works only from a "child" ARexx
 *		program or from an ARexx program which has acquired a lock.
 *
 *		QUIT
 *		Causes the program to terminate.  This is the only way to
 *		exit!
 *
 *
 * These descriptions are terse, I know.  Looking at the code here
 * is the best way to get a feel for just what is happening.
 * To play with this, run it, and then run an ARexx program whose
 * host address is set to "MYREXX_TEST_#1".
 * Then issue some commands!  Note that most things require that the
 * results flag be set via an ARexx "options results" statement.
 */



#include "rxil.h"

#include <graphics/gfxbase.h>
#include <libraries/dos.h>

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#ifdef LATTICE
#include <proto/intuition.h>
#endif



/*--------------------------------------------------------------------*/

void openlibrarys( void );
void closelibrarys( void );
void cleanup( void );
void bailout( char *message, int rc );

void eventloop( void );



void cmd_status( struct RexxMsg *rexxmsg );
void cmd_quit( struct RexxMsg *rexxmsg );
void cmd_launch_cmd( struct RexxMsg *rexxmsg );
void cmd_launch_func( struct RexxMsg *rexxmsg );
void cmd_ptest( struct RexxMsg *rexxmsg );
void cmd_dump( struct RexxMsg *rexxmsg );



/*--------------------------------------------------------------------*/

struct IntuitionBase *IntuitionBase = NULL;
struct GfxBase *GfxBase = NULL;


BOOL finished = FALSE;



/************************************
 *									*
 *    Defined for Rexx Interface	*
 *									*
 ************************************/

struct RxilDef *global_rdef = NULL;


struct RxilFunction rexx_cmd_table[] = {
	{ "lock", &RxilCmdLock, 0, 0, FALSE, RXIL_PUBLIC },
	{ "unlock", &RxilCmdUnlock, 0, 0, FALSE, RXIL_SECRET },

	{ "status", &cmd_status, 1, 1, FALSE, RXIL_PUBLIC },
	{ "quit", &cmd_quit, 0, 0, FALSE, RXIL_PUBLIC },
	{ "dump", &cmd_dump, 0, 1, FALSE, RXIL_PUBLIC },
	{ "lcmd", &cmd_launch_cmd, 0, 0, FALSE, RXIL_PUBLIC },
	{ "lfunc", &cmd_launch_func, 0, 0, FALSE, RXIL_PUBLIC },
	{ "ptest", &cmd_ptest, 0, 0, FALSE, RXIL_SECRET },

	/* Mark end-of-table */
	{ NULL, NULL, 0, 0, FALSE, 0 }
};


/********************************************************************/


struct RxilInvocation *rxi_func;
struct RxilInvocation *rxi_cmd;
	/* For general purpose testing use.  These would not be
	 * needed as other than local, temporary variables if we were
	 * allocating and de-allocating RxilInvocation structures on
	 * the fly (since the Rxil library tracks all RxilInvocation
	 * structures allocated).
	 */


struct RxilInvocation *rxi_startup;
	/* This one is for the startup macro.  We want to retain it's pointer
	 * even after it is launched so that we can "recognize" it when
	 * it terminates and handle it differently.
	 */


/*--------------------------------------------------------------------*/


void main( int argc, char *argv[] )
{
	openlibrarys();


	/* =+=+=+=+=+=+=+=+=+  ARexx Interface Note +=+=+=+=+=+=+=+=+=+=+
	 * This will open the rexx library, and the desired ports.
	 * If all we want to do is receive commands, we probably
	 * would not open the secret port, although we might. 
	 */
	global_rdef = RxilInit( RXIL_PUBLIC | RXIL_SECRET, "MYREXX_TEST" );
	if( global_rdef == NULL )
	{
		bailout( "Unable to init rexx", 0 );
	}


	/* =+=+=+=+=+=+=+=+=+  ARexx Interface Note +=+=+=+=+=+=+=+=+=+=+
	 * Init the members in the RxilDef structure which customize
	 * the way things work.
	 */

/*	global_rdef->Flags = RXIL_NO_REQ | RXIL_NO_ABORT; */
		/* Set flags here to control various characteristics of the
		 * library routines.
		 * The default in this demo is to not set any flags, which
		 * typicaly turn _off_ features.
		 */

	global_rdef->Console = "CON:10/10/400/150/RexxCon";
		/* All commands or functions launched by this program will open
		 * a console window of this specification.  Set this to NULL to
		 * default to no window.
		 */

	global_rdef->Extension = "rexx";
		/* this normally would be an application
		 * specific macro filename extension.
		 */

	global_rdef->HostPort = "REXX";
		/* If this is AREXX, launches will default
		 * to async (as opposed to synchronous).
		 */

	global_rdef->CommandTable = &rexx_cmd_table[0];





	/* =+=+=+=+=+=+=+=+=+  ARexx Interface Note +=+=+=+=+=+=+=+=+=+=+
	 * Pre-allocate some invocation structures.  These could
	 * also be allocated and de-allocated on-the-fly if desired.
	 * One possible philosophy would be to allocate one structure
	 * for each possible macro command.  This would speed macro
	 * invocations by a slight amount.
	 */
	rxi_cmd = RxilCreateRxi( NULL, RXCOMM );
	if( rxi_cmd == NULL )
	{
		bailout( "Unable to allocate command_rxi", RETURN_FAIL );
	}

	rxi_func = RxilCreateRxi( NULL, RXFUNC );
	if( rxi_func == NULL )
	{
		bailout( "Unable to allocate function_rxi", RETURN_FAIL );
	}



	/* =+=+=+=+=+=+=+=+=+  ARexx Interface Note +=+=+=+=+=+=+=+=+=+=+
	 * This is an example of running a "startup" macro each time the
	 * program is invoked.  The RxilInvocation will be allocated and
	 * then freed when it returns.
	 */
	rxi_startup = RxilCreateRxi( "startup.rxil", RXCOMM );
	if( rxi_startup == NULL )
	{
		/* Just because the startup macro did not work, you would
		 * probably not normally give up.
		 */
		printf( "** Unable to allocate startup rxi **\n" );
	}
	else
	{
		/* We want our own console spec, not the default.  In some
		 * (most?) real applications a console would probably not be
		 * opened.  We do it here for illustration purposes.
		 */
		rxi_startup->Console = "CON:0/80/620/100/Demo Startup Macro";

		if(  RxilLaunch( rxi_startup ) != 0  )
		{
			/* Informative message only */
			printf( "** Unable to launch the startup macro **\n" );
		}
	}

	eventloop();

	cleanup();
}



/*-----------------------------------------------------------------*/


void eventloop( void )
{
	struct RxilInvocation *rxi;
	ULONG flags;


	while( !finished )
	{
		/* =+=+=+=+=+=+=+=+=+  ARexx Interface Note +=+=+=+=+=+=+=+=+=+=+
		 * Note that we include the rexx port signal bit(s) in the wait().
		 */
		flags = Wait( RXIL_WAITFLAG );


		/* =+=+=+=+=+=+=+=+=+  ARexx Interface Note +=+=+=+=+=+=+=+=+=+=+
		 * Now do the ARexx port stuff.
		 */
		if(  FlagIsSet( flags, RXIL_WAITFLAG )  )
		{
			RxilCheckPort();
		}


		/* Some info for debugging purposes... */

		printf( "---Event---\n" );
		printf( "rxi_cmd state: %d\n", (int)rxi_cmd->State );
		printf( "rxi_func state: %d\n", (int)rxi_func->State );


		/* =+=+=+=+=+=+=+=+=+  ARexx Interface Note +=+=+=+=+=+=+=+=+=+=+
		 * Get any ARexx invocation returns which may be back.
		 * This code would not be present on a minimal implementation
		 * which does not launch macros, just responds to simple
		 * commands.
		 */
		while(  ( rxi = RxilGetReturn() ) != NULL  )
		{
			if( rxi->Type == RXCOMM )
			{
				printf( "Command Invocation completed\n" );
			}
			else
			{
				printf( "Function Invocation completed\n" );
			}

			if( rxi == rxi_startup )
			{
				/* This is our startup macro terminating.  We handle
				 * this differently since we don't want to display
				 * an error if not found (which the standard routine
				 * RxilHandleReturn would do).  And we also want to
				 * de-allocate the RxilInvocation structure.
				 */

				/* =+=+=+=+=+=+=+=+=+  ARexx Interface Note +=+=+=+=+=+=+
				 * Deallocate things, close console windows, etc.
				 * Then free the structure.
				 */
				if(  ( rxi->RexxMsg->rm_Result1 != RC_WARN ) ||
					( rxi->RexxMsg->rm_Result2 != ERR10_001 )  )
				{
					/* The error is not just "program not found".
					 * This is a true error, tell the user.
					 */
					RxilHandleReturn( rxi );
				}

				RxilCleanupReturn( rxi );
				RxilDeleteRxi( rxi );
				rxi_startup = NULL;		/* To be tidy... */
			}
			else
			{
				/* =+=+=+=+=+=+=+=+=+  ARexx Interface Note +=+=+=+=+=+=+
				 * Tell user about error returns, etc.
				 * This can either be replaced by client code to handle
				 * returns in a specific way, or not done at all (not very
				 * friendly) and nothing will break...
				 */
				RxilHandleReturn( rxi );

				/* =+=+=+=+=+=+=+=+=+  ARexx Interface Note +=+=+=+=+=+=+
				 * Deallocate things, close console windows, etc.
				 */
				RxilCleanupReturn( rxi );
			}
		}
	}
}



void cmd_status( struct RexxMsg *rexxmsg )
{
	printf( "Cmd status\n" );
	printf( "The FromRexx variable is %d\n", RXIL_FROM_REXX );

	switch( *RXIL_ARGV(1) )
	{
		case 'L':
		case 'l':
			RxilSetResult( rexxmsg, "Hi there!" );
			break;

		case 'B':
		case 'b':
			RxilSetResult( rexxmsg, "I'm NOT Fine!" );
			break;

		case 'X':
		case 'x':
			rexxmsg->rm_Result1 = 20;
			break;

		case 'Y':
		case 'y':
			rexxmsg->rm_Result1 = 100;
			break;

		case 'Z':
		case 'z':
			rexxmsg->rm_Result1 = 200;
			break;

		default:
			rexxmsg->rm_Result1 = RXERR_INVALID_ARG;
	}
}



/* This will only succeed if the command comes in at the private port.
 */

void cmd_ptest( struct RexxMsg *rexxmsg )
{
	printf( "Cmd privilege test\n" );

	RxilSetResult( rexxmsg, "You are privileged indeed!" );
}



/* Cause the demo program to exit. */

void cmd_quit( struct RexxMsg *rexxmsg )
{
	printf( "Cmd quit\n" );

	finished = TRUE;
}



void cmd_dump( struct RexxMsg *rexxmsg )
{
	ULONG flags = 0;


	if( RXIL_ARGC > 1 )
	{
		if(  strnicmp( RXIL_ARGV(1), "ENVIRONMENT",
			strlen(RXIL_ARGV(1)) ) == 0  )
		{
			flags = RXIL_DUMP_ENV;
		}

		if(  strnicmp( RXIL_ARGV(1), "STATE",
			strlen(RXIL_ARGV(1)) ) == 0  )
		{
			flags = RXIL_DUMP_STATE;
		}

		if(  strnicmp( RXIL_ARGV(1), "COMMANDS",
			strlen(RXIL_ARGV(1)) ) == 0  )
		{
			flags = RXIL_DUMP_CMDS;
		}
	}
	else
	{
		flags = RXIL_DUMP_ENV | RXIL_DUMP_CMDS | RXIL_DUMP_STATE;
	}

	if( flags == 0 )
	{
		/* The keyword was not valid */
		rexxmsg->rm_Result1 = RXERR_INVALID_ARG;
		return;
	}

	RxilDumpRdef( global_rdef, flags );
}



void cmd_launch_cmd( struct RexxMsg *rexxmsg )
{
	static char buf[100];
	LONG rc;


	printf( "Cmd launch command:" );
	gets( buf );

	rxi_cmd->Name = buf;
	rc = RxilLaunch( rxi_cmd );
	printf( "Launch returned %d\n", rc );
}



static char buf[16][100];


void cmd_launch_func( struct RexxMsg *rexxmsg )
{
	LONG rc;
	int i;


	printf( "Cmd launch function:" );
	gets( &buf[0][0] );

	for( i=1; i<16; i++ )
	{
		printf( "Enter arg %d: ", i );
		gets( &buf[i][0] );
		if( strlen(&buf[i][0]) > 0 )
		{
			rxi_func->FuncArg[i] = &buf[i][0];
		}
		else
		{
			rxi_func->FuncArg[i] = NULL;
			break;
		}
	}

	rxi_func->Name = &buf[0][0];

	rc = RxilLaunch( rxi_func );

	printf( "Launch returned %d\n", rc );
}



/*-----------------------------------------------------------------*/




void openlibrarys( void )
{
	/*   Intuition   */
	if(  ! ( IntuitionBase = (struct IntuitionBase *)
		OpenLibrary("intuition.library", 33 ) )  )
	{
		bailout( "Unable to open the Intuition Library", RETURN_FAIL );
	}

	/*   Graphics   */
	if(  ! ( GfxBase = (struct GfxBase *)
		OpenLibrary("graphics.library", 33 ) )  )
	{
		bailout("Unable to open the Graphics Library", RETURN_FAIL );
	}
}



void closelibrarys( void )
{
	if( IntuitionBase )   CloseLibrary( (struct Library *)IntuitionBase );
	if( GfxBase )   CloseLibrary( (struct Library *)GfxBase );
}



void cleanup( void )
{
	/* =+=+=+=+=+=+=+=+=+  ARexx Interface Note +=+=+=+=+=+=+=+=+=+=+
	 * This will handle ALL the cleanup details.  If we wished to make
	 * sure that there were no macro program invocations running before
	 * we call this routine (and it waits, which may distress the user)
	 * we should test our ability to terminate by calling RxilPending().
	 */
	RxilCleanup( global_rdef );
	global_rdef = NULL;

	closelibrarys();
}



void bailout( char *message, int rc )
{
	printf( "%s/n", message );
	cleanup();
	exit( rc );
}

