home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Club Amiga de Montreal - CAM
/
CAM_CD_1.iso
/
files
/
291.lha
/
RexxFunctionHostPack_v1.2
/
rexxfunchost.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-09-02
|
14KB
|
672 lines
/* RexxFuncHost.c */
/* Version 1.2 */
/* 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 without the written
* permission of the author, Donald T. Meyer.
*
* Stormgate Software
* PO Box 383
* St. Peters, MO 63376
*
* E-Mail can be sent to via the following:
* BIX: donmeyer (almost daily)
* GEnie: D.MEYER (weekly)
* PLINK: Stormgate (weekly)
*/
#include <exec/types.h>
#include <libraries/dos.h>
#include <workbench/startup.h>
#include <proto/intuition.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <string.h>
#include <stdlib.h>
#include "rexxfunchost.h"
/*------------------------------------------------------------------*/
/* External Function Declarations */
/*------------------------------------------------------------------*/
/* These are (better be!) in the user function module(s) */
char *client_init( void );
void client_cleanup( void );
void client_event_handler( ULONG flags );
/*------------------------------------------------------------------*/
/* External Variable Declarations */
/*------------------------------------------------------------------*/
/* These are also in the user function module(s) */
extern char *hello_string;
extern char *success_string;
extern char *removal_string;
extern char *redundant_string;
extern char *noclone_string;
extern struct RexxFunction func_table[];
extern int hostpri;
extern char *hostname;
extern BPTR _Backstdout;
extern char *console_def_string;
extern ULONG client_event_flags;
/* Other misc. external variables. */
extern struct WBStartup *WBenchMsg;
/*------------------------------------------------------------------*/
/* Local Function Declarations */
/*------------------------------------------------------------------*/
static void openlibrarys( void );
static void closelibrarys( void );
static void bailout( char *message, int rc );
static void cleanup( void );
static void init_port( void );
static void eventloop( void );
static int to_rexx( ULONG cmd, STRPTR arg0, STRPTR arg1, STRPTR arg2,
STRPTR arg3 );
static void rexx_delete_port( struct MsgPort *port );
static void check_result( struct Rexxmsg *rexxmsg );
static void dispatch( struct RexxMsg *rexxmsg );
static void check_clone( int argc, char *argv[] );
static BOOL remove_clone( void );
/*------------------------------------------------------------------*/
/* Variable Definitions */
/*------------------------------------------------------------------*/
struct RxsLib *RexxSysBase = NULL;
static struct MsgPort *hostport = NULL;
static BPTR message_console = NULL;
static BOOL finished = FALSE;
static BOOL on_list = FALSE;
/* For detaching */
long _BackGroundIO = 1;
/*------------------------------------------------------------------*/
/* Functions */
/*------------------------------------------------------------------*/
void main( int argc, char *argv[] )
{
char *errmsg;
if( _Backstdout == NULL )
{
/* Probably child of workbench, open our own window */
/* Open a window to print hello and copyright stuff to */
message_console = Open( console_def_string, MODE_NEWFILE );
/* After some thought, I decided to quit if we could not
* post our hello banner, etc.
* We could just as easily continue, but this _should_ never
* fail, so probably moot.
*/
if( message_console == NULL )
bailout( NULL, RETURN_FAIL );
}
else
{
/* Send banner and error messages to the CLI that started us */
message_console = _Backstdout;
}
openlibrarys();
Write( message_console, hello_string, strlen(hello_string) );
check_clone( argc, argv );
init_port();
/* Allow the client to do his initializations */
if( ( errmsg = client_init() ) != NULL )
{
bailout( errmsg, RETURN_FAIL );
}
Write( message_console, success_string, strlen(success_string) );
/* Done with window, close it */
Delay( 200 );
Close( message_console );
message_console = NULL;
eventloop();
cleanup();
exit( RETURN_OK );
}
/* We come here after all of the initialization is done, and don't
* leave until we receive a termination signal.
*/
static void eventloop( void )
{
struct RexxMsg *rexxmsg;
ULONG flags;
while( ! finished )
{
flags = Wait( ( 1L<<hostport->mp_SigBit ) | SIGBREAKF_CTRL_C
| client_event_flags );
/* Call a routine in the client to allow any application-
* specific things such as window events to be dealt with.
*/
client_event_handler( flags );
while( rexxmsg = (struct RexxMsg *)GetMsg( hostport ) )
{
if( IsRexxMsg(rexxmsg) )
{
/* The message is from an ARexx program */
/* Handle the message contents */
if( (rexxmsg->rm_Action & RXCODEMASK) == RXFUNC )
{
/* We only respond to function invocations, as
* opposed to anything else (i.e. a command
* invocation if our host port was set as the
* ARexx address).
*/
dispatch( rexxmsg );
check_result( rexxmsg );
}
}
/* We always reply, even if the message is not really
* something we want to handle
*/
ReplyMsg( (struct Message *)rexxmsg );
}
if( flags & SIGBREAKF_CTRL_C )
{
/* Somebody wants us to quit. Probably another us! */
finished = TRUE;
}
}
}
/* This will take the function name from the RexxMsg and try to
* find a function in our command table which matches. This is
* also where the argument count verification is done.
* If a function which matches is found, and it has the correct
* number of arguments, it is vectored to.
*/
static void dispatch( struct RexxMsg *rexxmsg )
{
int i, match = -1;
char *cmd;
cmd = ARG0(rexxmsg);
for( i=0; func_table[i].fname != NULL; i++ )
{
if( func_table[i].caseflag == TRUE )
{
/* Case sensitive */
if( strcmp( func_table[i].fname, cmd ) == 0 )
{
/* A match */
match = i;
break;
}
}
else
{
/* Not case sensitive */
if( stricmp( func_table[i].fname, cmd ) == 0 )
{
/* A match */
match = i;
break;
}
}
}
if( match == -1 )
{
/* No match found */
rexxmsg->rm_Result1 = RC_WARN;
rexxmsg->rm_Result2 = ERR10_001;
return;
}
/* Found a match. Let's do the argument checking */
if( ( func_table[match].argcount != -1 ) &&
( ( rexxmsg->rm_Action & RXARGMASK ) !=
func_table[match].argcount ) )
{
/* Wrong number of args */
rexxmsg->rm_Result1 = RC_ERROR;
rexxmsg->rm_Result2 = ERR10_017;
return;
}
/* Since we found a function, default to a "success" code. */
rexxmsg->rm_Result1 = RC_OK;
rexxmsg->rm_Result2 = 0;
/* Call the function. */
(*(func_table[match].func))(rexxmsg);
}
/* To ease the burden on a function, the checking for wether or not
* an argstring was requested is done here. This will ensure that
* one is never returned if ARexx did not ask for one.
*/
static void check_result( struct RexxMsg *rexxmsg )
{
/* Make sure that we don't return an argstring
* if one was not requested
*/
if( rexxmsg->rm_Action & RXFF_RESULT )
{
/* Result string wanted */
return;
}
/* A result string was not requested. Let's see if there is one */
if( (rexxmsg->rm_Result1==0) && (rexxmsg->rm_Result2!=0) )
{
/* There is one, so we need to delete it. */
DeleteArgstring( (struct RexxArg *)(rexxmsg->rm_Result2) );
rexxmsg->rm_Result2 = 0;
}
}
/* Open a public port and add it to the ARexx Library List. This is
* where ARexx will send messages for functions it wants resolved.
*
* Note that we create our port with a priority higher than zero.
* This should give a _very_slight_ increase in speed when ARexx
* FindPort()'s us.
*/
static void init_port( void )
{
/* Setup the public host port */
if( ( hostport = CreatePort( hostname, 5 ) ) == NULL )
{
bailout( "Unable to create message port", RETURN_FAIL );
}
/* Add ourselves to the arexx library list */
if( to_rexx( (ULONG)RXADDFH, hostname, (STRPTR)hostpri, NULL, NULL ) )
{
/* Could not add our port to the library list */
bailout( "Unable to add self to ARexx library list",
RETURN_FAIL );
}
on_list = TRUE;
}
/* This function does some fairly convoluted things involving
* terminating another previously running invocation of ourselves.
* Depending on wether we are launched from CLI or WorkBench and
* command line options (only if from the CLI of course).
* The logic is:
*
* IF from WorkBench
* IF already invoked
* remove existing invocation
* exit
*
* IF from CLI
* IF -q option
* IF already invoked
* remove existing invocation
* exit
* ELSE
* tell user there is no existing invocation
* exit
* ELSE
* IF already invoked
* tell user that we cannot install again
* exit
*
*/
static void check_clone( int argc, char *argv[] )
{
if( WBenchMsg )
{
/* We are running from workbench, see if we want to
* kill off the existing invocation.
*/
if( remove_clone() )
{
/* There was and we did */
bailout( removal_string, RETURN_OK );
}
/* Else, there was no clone, continue execution. */
}
else
{
/* From the CLI. If the argument "-q" is given, we try to kill
* the existing invocation. Otherwise we just refuse
* to start another invocation.
*/
if( ( argc > 1 ) && ( stricmp(argv[1],"-q") == 0 ) )
{
/* User wants us to remove ourselves */
if( remove_clone() )
{
/* There was and we did */
bailout( removal_string, RETURN_OK );
}
else
{
/* There was no previous invocation */
bailout( noclone_string, RETURN_WARN );
}
}
else
{
/* The user is trying to invoke us. */
if( FindPort( hostname ) )
{
/* But we already exist */
bailout( redundant_string, RETURN_WARN );
}
}
}
}
/* If there is already another instance of ourselves running, kill it
* and return TRUE, otherwise return FALSE.
*/
static BOOL remove_clone( void )
{
struct MsgPort *otherport;
if( ( otherport = FindPort( hostname ) ) != NULL )
{
/* Yes, we are */
Signal( otherport->mp_SigTask, SIGBREAKF_CTRL_C );
return( TRUE );
}
return( FALSE );
}
/* Open any librarys needed. In this case, just one. The client
* modules may also open additional librarys as needed of course.
*/
static void openlibrarys( void )
{
/* ARexx library */
if( ! ( RexxSysBase = (struct RxsLib *)OpenLibrary( RXSNAME, 0L) ) )
{
bailout( "Unable to open the ARexx Library", RETURN_FAIL );
}
}
/* Complement to openlibrarys()
*/
static void closelibrarys( void )
{
if( RexxSysBase )
CloseLibrary( (struct Library *)RexxSysBase );
}
/* This routine will do everything neccessary to see that we make an
* orderly exit, regardless of what point in the program we were at.
*/
static void cleanup( void )
{
client_cleanup();
if( on_list == TRUE )
{
/* Remove ourselves from the arexx library list */
to_rexx( (ULONG)RXREMLIB, hostname, NULL, NULL, NULL );
}
if( hostport )
{
rexx_delete_port( hostport );
}
closelibrarys();
if( message_console )
{
Close( message_console );
}
}
/* Typicaly the place we come when something has gone awry, although
* some fairly innocous events (like already being installed) will
* call this also as their way out.
* If there is a message, and a way to display it, it will be displayed.
* Then free any resources, and exit!
*/
static void bailout( char *message, int rc )
{
if( message_console && message )
{
Write( message_console, message, strlen(message) );
Delay( 200 );
}
cleanup();
exit( rc );
}
/* General purpose utility function to send messages to the ARexx
* resident process. For simplicity's sake, we ask ARexx to not
* respond. This does mean that a request could fail and we would
* never know. Since there is _very_ little reason for anything
* we are doing here to fail, it seems a reasonable risk. Else
* why would Bill have given us the RXFB_NONRET flag? :-)
*/
static int to_rexx( ULONG cmd, STRPTR arg0, STRPTR arg1, STRPTR arg2,
STRPTR arg3 )
{
struct MsgPort *rmast;
struct RexxMsg *rexxmsg;
/* Allocate a packet to send to rexxmaster */
rexxmsg = CreateRexxMsg( NULL, NULL, NULL );
if( rexxmsg == NULL )
{
return( 1 );
}
rexxmsg->rm_Action = cmd | RXFF_NONRET;
rexxmsg->rm_Args[0] = arg0;
rexxmsg->rm_Args[1] = arg1;
rexxmsg->rm_Args[2] = arg2;
rexxmsg->rm_Args[3] = arg3;
Forbid();
if( rmast = FindPort( "REXX" ) )
{
PutMsg( rmast, (struct Message *)rexxmsg );
}
Permit();
if( rmast == NULL )
{
/* we could not find the REXX port, this failed! */
DeleteRexxMsg( rexxmsg );
return( 2 );
}
return( 0 );
}
/* This will safely delete a message port used to recieve commands
* from ARexx.
* This is acomplished by setting a failure return code and replying the
* message if it is from ARexx. If the message is not from ARexx, it
* is merely replied.
*
* The Forbid() Permit() bracketing is done to insure that the port
* is empty when we delete it. It would be *very* rude to cause
* the ARexx resident process to hang waiting for us to reply a
* message that we never will...
*/
static void rexx_delete_port( struct MsgPort *port )
{
struct RexxMsg *rexxmsg;
Forbid();
while( rexxmsg = (struct RexxMsg *)GetMsg( port ) )
{
if( IsRexxMsg( rexxmsg ) )
{
/* Set failure code */
rexxmsg->rm_Result1 = RC_ERROR;
rexxmsg->rm_Result2 = ERR10_015;
}
ReplyMsg( (struct Message *)rexxmsg );
}
DeletePort( port );
Permit();
}
/* This can be called to handle the details of placing a result
* string into the rexx message packet as the result Argstring.
* This will deal with inability to allocate the Argstring.
* If the string pointer is NULL, a general failure code will
* be set.
*/
void SetResultString( struct RexxMsg *rexxmsg, char *string )
{
if( string )
{
rexxmsg->rm_Result1 = RC_OK;
rexxmsg->rm_Result2 =
(ULONG)CreateArgstring( string, strlen(string) );
if( rexxmsg->rm_Result2 == NULL )
{
/* Unable to create the argstring */
rexxmsg->rm_Result1 = RC_ERROR;
rexxmsg->rm_Result2 = ERR10_012;
}
}
else
{
/* Result string is null, set general failure code */
rexxmsg->rm_Result1 = RC_ERROR;
rexxmsg->rm_Result2 = ERR10_012;
}
}