/* 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 * 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 #include #include #include #include #ifdef LATTICE #include #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 ); }