/*
**         $RCSfile$
**      Description: BOOPSI ARexx interface class.
**        Copyright: (C) Copyright 1994 Jaba Development.
**                   (C) Copyright 1994 Jan van den Baard.
**                   All Rights Reserved.
**
**          $Author$
**        $Revision$
**            $Date$
**/

/*
**      Uncomment the following line if you want the
**      class to use memory pools and you have got
**      the 3.1 amiga.lib.
**/

/* #define POOLS */

#ifdef POOLS
#define AllocMemory(s)          GetMem( ad, s )
#define FreeMemory(p)           PutMem( ad, p )
#else
#define AllocMemory(s)          AllocVec( s, MEMF_PUBLIC | MEMF_CLEAR )
#define FreeMemory(p)           FreeVec( p )
#endif

#include "ARexxClass.h"

#include <proto/intuition.h>
#include <proto/utility.h>
#include <proto/rexxsyslib.h>
#include <proto/exec.h>
#include <proto/dos.h>

#include <clib/alib_protos.h>

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

/*
**      Compiler crap. Should compile under SAS this way...
**/
#ifdef _DCC
#define SAVEDS __geta4
#define ASM
#define REG(x) __ ## x
#else
#define SAVEDS __saveds
#define ASM __asm
#define REG(x) register __ ## x
#endif

/*
**      Object instance data.
**/
typedef struct {
        struct MsgPort                  *ad_Port;       /* Host message port. */
        UBYTE                           *ad_HostName;   /* Host name. */
        UBYTE                           *ad_FileExt;    /* File extention. */
        ULONG                            ad_PendCnt;    /* Messages still un-reply'd. */
        REXXCOMMAND                     *ad_Commands;   /* Host command list. */
        struct RDArgs                   *ad_DOSParser;  /* ReadArgs() parser. */
        UWORD                            ad_Flags;      /* See below. */
#ifdef POOLS
        APTR                             ad_MemPool;    /* Memory pool. */
#endif
} AD;

/*
**      Module prototypes.
**/
STATIC ULONG SAVEDS ASM ARexxDispatch( REG(A0) Class *cl, REG(A2) Object *obj, REG(A1) Msg );

/*
**      Class initialization.
**/
Class *InitARexxClass( void )
{
        Class                   *cl;

        if ( cl = MakeClass( NULL, ROOTCLASS, NULL, sizeof( AD ), 0L ))
                cl->cl_Dispatcher.h_Entry = (HOOKFUNC)ARexxDispatch;

        return( cl );
}

/*
**      Free the class.
**/
BOOL FreeARexxClass( Class *cl )
{
        return( FreeClass( cl ));
}

#ifdef POOLS
/*
**      Pool allocation.
**/
STATIC APTR GetMem( AD *ad, ULONG size )
{
        ULONG                   *ptr;

        /*
        **      Allocate the memory.
        **/
        if ( ptr = LibAllocPooled( ad->ad_MemPool, size + sizeof( ULONG ))) {
                /*
                **      Store allocation size.
                **/
                *ptr = size + sizeof( ULONG );
                /*
                **      Adjust pointer.
                **/
                ptr++;
        }
        return(( APTR )ptr );
}

/*
**      Pool freeing.
**/
STATIC VOID PutMem( AD *ad, APTR mem )
{
        ULONG                   *ptr = ( ULONG * )mem;

        /*
        **      Retrieve the original allocation.
        **/
        ptr--;

        /*
        **      Deallocate.
        **/
        LibFreePooled( ad->ad_MemPool, ptr, *ptr );
}
#endif

/*
**      Duplicate a string.
**/
STATIC UBYTE *DupStr( AD *ad, UBYTE *str )
{
        UBYTE                   *dup = AllocMemory( strlen( str ) + 1 );

        if ( dup )
                strcpy( dup, str );

        return( dup );
}

/*
**      Reply a rexx command.
**/
STATIC VOID ReplyRexxCommand( struct RexxMsg *rxm, LONG prim, LONG sec, UBYTE *res )
{
        UBYTE           buf[ 16 ];

        /*
        **      Result wanted?
        **/
        if ( rxm->rm_Action & RXFF_RESULT ) {
                /*
                **      Primary result?
                **/
                if ( ! prim ) {
                        /*
                        **      We setup the secondary result to
                        **      the result string when one was passed.
                        **/
                        sec = res ? ( LONG )CreateArgstring( res, strlen( res )) : 0L;
                } else {
                        /*
                        **      Primary result bigger than 0?
                        **/
                        if ( prim > 0 ) {
                                /*
                                **      Setup the result field
                                **      to point to a string containing
                                **      the secondary result number.
                                **/
                                sprintf( buf, "%ld", sec );
                                res = buf;
                        } else {
                                /*
                                **      Negate primary result and
                                **      setup the result field to
                                **      the secondary result.
                                **/
                                prim = -prim;
                                res = ( UBYTE * )sec;
                        }

                        /*
                        **      Setup ARexx it's "RC2" variable to
                        **      the result.
                        **/
                        SetRexxVar(( struct Message * )rxm, "RC2", res, strlen( res ));

                        /*
                        **      Clear secondary result.
                        **/
                        sec = 0L;
                }
        } else if ( prim < 0 )
                /*
                **      Negate primary result.
                **/
                prim = -prim;

        /*
        **      Setup result fields.
        **/
        rxm->rm_Result1 = prim;
        rxm->rm_Result2 = sec;

        /*
        **      Reply the RexxMsg.
        **/
        ReplyMsg(( struct Message * )rxm );
}

/*
**      Free a RexxMsg command.
**/
STATIC VOID FreeRexxCommand( struct RexxMsg *rxm )
{
        /*
        **      Delete the result Argstring.
        **/
        if ( ! rxm->rm_Result1 && rxm->rm_Result2 )
                DeleteArgstring(( UBYTE * )rxm->rm_Result2 );

        /*
        **      Close input handle.
        **/
        if ( rxm->rm_Stdin && rxm->rm_Stdin != Input()) {
                /*
                **      If the output handle is the
                **      same as the input handle we
                **      can safely clear it.
                **/
                if ( rxm->rm_Stdout == rxm->rm_Stdin )
                        rxm->rm_Stdout = NULL;
                Close( rxm->rm_Stdin );
                rxm->rm_Stdin = NULL;
        }

        /*
        **      Close output handle if it is not
        **      the same as Stdin.
        **/
        if ( rxm->rm_Stdout && rxm->rm_Stdout != Output()) {
                Close( rxm->rm_Stdout );
                rxm->rm_Stdout = NULL;
        }

        /*
        **      Delete the command Argstring.
        **/
        DeleteArgstring(( UBYTE * )ARG0( rxm ));
        /*
        **      Delete the message itself.
        **/
        DeleteRexxMsg( rxm );
}

/*
**      Create a RexxMsg command.
**/
STATIC struct RexxMsg *CreateRexxCommand( AD *ad, UBYTE *comname, BPTR handle )
{
        struct RexxMsg                  *rxm;

        /*
        **      Create the RexxMsg.
        **/
        if ( rxm = CreateRexxMsg( ad->ad_Port, ad->ad_FileExt, ad->ad_HostName )) {
                /*
                **      Create the Argstring.
                **/
                if ( rxm->rm_Args[ 0 ] = CreateArgstring( comname, strlen( comname ))) {
                        /*
                        **      Setup action flags.
                        **/
                        rxm->rm_Action = RXCOMM | RXFF_RESULT;
                        /*
                        **      Setup file handles.
                        **/
                        rxm->rm_Stdin  = rxm->rm_Stdout = handle;
                        return( rxm );
                }
                /*
                **      Argstring creation failed.
                **/
                DeleteRexxMsg( rxm );
        }
        return( NULL );
}

/*
**      Send a RexxMsg command to the
**      ARexx server.
**/
STATIC struct RexxMsg *CommandToRexx( AD *ad, struct RexxMsg *rxm )
{
        struct MsgPort          *rxp;

        /*
        **      Try to find the "REXX"
        **      message port.
        **/
        Forbid();

        if ( ! ( rxp = FindPort( RXSDIR ))) {
                /*
                **      Oops. ARexx server
                **      not active.
                **/
                Permit();
                return( NULL );
        }

        /*
        **      Send off the message.
        **/
        PutMsg( rxp, &rxm->rm_Node );

        Permit();

        /*
        **      Increase pending counter.
        **/
        ad->ad_PendCnt++;

        return( rxm );
}

/*
**      Send a command to the ARexx server.
**/
STATIC struct RexxMsg *SendRexxCommand( AD *ad, UBYTE *comname, BPTR handle )
{
        struct RexxMsg                  *rxm;

        /*
        **      Create a RexxMsg command and
        **      send it off to the ARexx server.
        **/
        if ( rxm = CreateRexxCommand( ad, comname, handle ))
                return( CommandToRexx( ad, rxm ));

        return( NULL );
}

STATIC REXXCOMMAND *FindRXCommand( AD *ad, UBYTE *comname, UWORD len )
{
        REXXCOMMAND             *rc = ad->ad_Commands;

        while ( rc->rc_Func ) {
                if ( ! strnicmp( comname, rc->rc_Name, len ) && isspace( comname[ strlen( rc->rc_Name ) ] ))
                        return( rc );
                rc++;
        }

        return( NULL );
}

/*
**      Execute a command.
**/
STATIC VOID DoRXCommand( AD *ad, struct RexxMsg *rxm )
{
        struct RexxMsg                  *rm;
        REXXCOMMAND                     *rco;
        REXXARGS                        *ra;
        UBYTE                           *comname, *args, *tmp, *result = NULL;
        LONG                             rc = 20, rc2 = 0;
        UWORD                            numargs = 0, len = 0;

        /*
        **      Allocate memory for the command
        **      name and it's argument string.
        **/
        if ( ! ( comname = ( UBYTE * )AllocMemory( strlen(( UBYTE * )ARG0( rxm )) + 2 ))) {
                rc2 = ERROR_NO_FREE_STORE;
                return;
        }

        /*
        **      Copy command name and argument string.
        **/
        strcpy( comname, ( UBYTE * )ARG0( rxm ));

        /*
        **      ReadArgs() requires the argument
        **      string to end with a newline.
        **/
        strcat( comname, "\n" );

        /*
        **      Find the length of the command
        **      the start of the arguments.
        **/
        args = comname;
        while ( isspace( *args )) args++;
        tmp  = args;
        while ( ! isspace( *args )) { len++; args++; }

        /*
        **      Find the command.
        **/
        if ( rco = FindRXCommand( ad, tmp, len )) {
                /*
                **      Allocate REXXARGS structure.
                **/
                if ( ra = ( REXXARGS * )AllocMemory( sizeof( REXXARGS ))) {
                        /*
                        **      Count number of expected args.
                        **/
                        if ( rco->rc_ArgTemplate ) {
                                tmp = rco->rc_ArgTemplate;
                                while ( *tmp != '\n' ) {
                                        if ( *tmp++ == ',' ) numargs++;
                                }
                                numargs++;
                                /*
                                **      Allocate arg array.
                                **/
                                if ( ra->ra_ArgList = ( ULONG * )AllocMemory( numargs * sizeof( ULONG ))) {
                                        /*
                                        **      Setup RDArgs.
                                        **/
                                        ad->ad_DOSParser->RDA_Source.CS_Buffer = args;
                                        ad->ad_DOSParser->RDA_Source.CS_Length = strlen( args );
                                        ad->ad_DOSParser->RDA_Source.CS_CurChr = 0;
                                        ad->ad_DOSParser->RDA_DAList           = NULL;
                                        ad->ad_DOSParser->RDA_Buffer           = NULL;
                                        /*
                                        **      Parse args.
                                        **/
                                        if ( ReadArgs( rco->rc_ArgTemplate, ra->ra_ArgList, ad->ad_DOSParser )) {
                                                /*
                                                **      Call the REXX routine.
                                                **/
                                                ( rco->rc_Func )( ra, rxm );

                                                rc     = ra->ra_RC;
                                                rc2    = ra->ra_RC2;
                                                result = ra->ra_Result;

                                                FreeArgs( ad->ad_DOSParser );
                                        } else {
                                                rc = 10;
                                                rc2 = IoErr();
                                        }
                                        FreeMemory( ra->ra_ArgList );
                                } else
                                        rc2 = ERROR_NO_FREE_STORE;
                        } else {
                                /*
                                **      No args.
                                **/
                                ( rco->rc_Func )( ra, rxm );

                                rc     = ra->ra_RC;
                                rc2    = ra->ra_RC2;
                                result = ra->ra_Result;
                        }
                        FreeMemory( ra );
                } else
                        rc2 = ERROR_NO_FREE_STORE;
        } else {
                /*
                **      Not found in our list?
                **      Maybe it's a script.
                **/
                if ( rm = CreateRexxCommand( ad, ( UBYTE * )ARG0( rxm ), NULL )) {
                        /*
                        **      Save original message.
                        **/
                        rm->rm_Args[ 15 ] = ( STRPTR )rxm;
                        /*
                        **      Let the REXX server see what
                        **      it can do with this.
                        **/
                        if ( ! CommandToRexx( ad, rm ))
                                rc2 = ERROR_NOT_IMPLEMENTED;
                } else
                        rc2 = ERROR_NO_FREE_STORE;

                goto byeBye;
        }

        ReplyRexxCommand( rxm, rc, rc2, result );

        byeBye:

        FreeMemory( comname );
}

/*
**      OM_NEW.
**/
STATIC ULONG ARexxNew( Class *cl, Object *obj, struct opSet *ops )
{
        struct TagItem         *attr = ops->ops_AttrList, *tag;
        struct MsgPort         *mp;
        AD                     *ad;
        ULONG                   rc, *ecode, ext = 1L;
        UBYTE                   unique_name[ 80 ], *tmp;

        /*
        **      Let the superclass set us up...
        **/
        if ( rc = ( ULONG )DoSuperMethodA( cl, obj, ( Msg )ops )) {
                /*
                **      Get the instance data.
                **/
                ad = ( AD * )INST_DATA( cl, rc );
                /*
                **      Safety precautions.
                **/
                bzero(( char * )ad, sizeof( AD ));

                /*
                **      First see if we got error storage.
                **/
                if ( tag = FindTagItem( AC_ErrorCode, attr ))
                        ecode = ( ULONG * )tag->ti_Data;

#ifdef POOLS
                /*
                **      Create a memory pool.
                **/
                if ( ad->ad_MemPool = LibCreatePool( MEMF_PUBLIC | MEMF_CLEAR, 4096L, 4096L )) {
#endif
                /*
                **      Create port if a
                **      host name is supplied.
                **/
                if ( tag = FindTagItem( AC_HostName, attr )) {
                        /*
                        **      Store host name.
                        **/
                        ad->ad_HostName = ( UBYTE * )tag->ti_Data;
                        /*
                        **      Name valid?
                        **/
                        if ( ad->ad_HostName && *ad->ad_HostName ) {
                                /*
                                **      Make the name unique.
                                **/
                                sprintf( unique_name, "%s.1", ad->ad_HostName );
                                Forbid();
                                while (( mp = FindPort( unique_name )) && ext <= 99 )
                                        sprintf( unique_name, "%s.%ld", ad->ad_HostName, ++ext );
                                Permit();
                                /*
                                **      Name unique?
                                **/
                                if ( ! mp ) {
                                        /*
                                        **      Copy the name.
                                        **/
                                        if ( ad->ad_HostName = DupStr( ad, unique_name )) {
                                                /*
                                                **      Uppercase it.
                                                **/
                                                tmp = ad->ad_HostName;
                                                while ( *tmp ) {
                                                        *tmp = toupper( *tmp );
                                                        tmp++;
                                                }
                                                /*
                                                **      Create the port.
                                                **/
                                                if ( ad->ad_Port = CreateMsgPort()) {
                                                        /*
                                                        **      Initialize and add the port.
                                                        **/
                                                        ad->ad_Port->mp_Node.ln_Name = ad->ad_HostName;
                                                        ad->ad_Port->mp_Node.ln_Pri  = 0;
                                                        AddPort( ad->ad_Port );
                                                } else if ( ecode )
                                                        *ecode = RXERR_OUT_OF_MEMORY;
                                        } else if ( ecode )
                                                *ecode = RXERR_OUT_OF_MEMORY;
                                } else if ( ecode )
                                        *ecode = RXERR_PORT_ALREADY_EXISTS;
                        } else if ( ecode )
                                *ecode = RXERR_NO_PORT_NAME;
                } else if ( ecode )
                        *ecode = RXERR_NO_PORT_NAME;

                /*
                **      Do we have a port now?
                **/
                if ( ad->ad_Port ) {
                        /*
                        **      Find the commandlist.
                        **/
                        if ( tag = FindTagItem( AC_CommandList, attr )) {
                                if ( ad->ad_Commands = ( REXXCOMMAND * )tag->ti_Data ) {
                                        /*
                                        **      Setup the AmigaDOS parser.
                                        **/
                                        if ( ad->ad_DOSParser = ( struct RDArgs * )AllocDosObject( DOS_RDARGS, NULL )) {
                                                ad->ad_DOSParser->RDA_Flags = RDAF_NOPROMPT;
                                                /*
                                                **      Obtain file extention.
                                                **/
                                                if ( tag = FindTagItem( AC_FileExtention, attr ))
                                                        ad->ad_FileExt = ( UBYTE * )tag->ti_Data;
                                                else
                                                        ad->ad_FileExt = "rexx";
                                                return( rc );
                                        } else if ( ecode )
                                                *ecode = RXERR_OUT_OF_MEMORY;
                                } else if ( ecode )
                                        *ecode = RXERR_NO_COMMAND_LIST;
                        } else if ( ecode )
                                *ecode = RXERR_NO_COMMAND_LIST;
                }
#ifdef POOLS
                } else if ( ecode )
                        *ecode = RXERR_OUT_OF_MEMORY;
#endif
                /*
                **      Bliep error...bliep....
                **/
                CoerceMethod( cl, ( Object * )rc, OM_DISPOSE );
        }
        return( 0L );
}

/*
**      OM_DISPOSE.
**/
STATIC ULONG ARexxDispose( Class *cl, Object *obj, Msg msg )
{
        AD                      *ad = ( AD * )INST_DATA( cl, obj );
        struct RexxMsg          *rxm;

        /*
        **      Do we have a port?
        **/
        if ( ad->ad_Port ) {
                /*
                **      Remove the port from the public list
                **/
                RemPort( ad->ad_Port );
                /*
                **      Wait for and handle all
                **      messages still pending.
                **/
                if ( ad->ad_PendCnt ) {
                        while ( ad->ad_PendCnt ) {
                                /*
                                **      Wait for a message.
                                **/
                                WaitPort( ad->ad_Port );
                                /*
                                **      Get messages.
                                **/
                                while ( rxm = ( struct RexxMsg * )GetMsg( ad->ad_Port )) {
                                        /*
                                        **      Replyed message?
                                        **/
                                        if ( rxm->rm_Node.mn_Node.ln_Type == NT_REPLYMSG ) {
                                                /*
                                                **      Free the message and decrease the
                                                **      pending counter.
                                                **/
                                                FreeRexxCommand( rxm );
                                                ad->ad_PendCnt--;
                                        } else
                                                /*
                                                **      Tell'm where getting out of here.
                                                **/
                                                ReplyRexxCommand( rxm, -20, ( LONG )"Host object closing down", NULL );
                                }
                        }
                } else {
                        /*
                        **      In case there are no messages pending we
                        **      still need to reply all that is waiting at
                        **      the port.
                        **/
                        while ( rxm = ( struct RexxMsg * )GetMsg( ad->ad_Port ))
                                ReplyRexxCommand( rxm, -20, ( LONG )"Host object closing down", NULL );
                }

                /*
                **      Delete the port.
                **/
                DeleteMsgPort( ad->ad_Port );
        }

        /*
        **      Delete the AmigaDOS parser.
        **/
        if ( ad->ad_DOSParser )
                FreeDosObject( DOS_RDARGS, ad->ad_DOSParser );

        /*
        **      Delete the port name.
        **/
        if ( ad->ad_HostName )
                FreeMemory( ad->ad_HostName );

#ifdef POOLS
        /*
        **      Free the pool.
        **/
        LibDeletePool( ad->ad_MemPool );
#endif

        /*
        **      Let the superclass do the rest.
        **/
        return( DoSuperMethodA( cl, obj, msg ));
}

/*
**      ACM_EXECUTE.
**/
STATIC ULONG ARexxExecute( Class *cl, Object *obj, struct acmExecute *acme )
{
        AD                              *ad = ( AD * )INST_DATA( cl, obj );
        REXXCOMMAND                     *rco;
        REXXARGS                        *ra;
        struct RexxMsg                  *rxm;
        UBYTE                           *args, *tmp, *result = NULL, *com;
        UWORD                            numargs = 0, len = 0;
        ULONG                            r = 20, r2;

        /*
        **      Allocate a private copy of the command string.
        **/
        if ( com = ( UBYTE * )AllocMemory( strlen( acme->acme_CommandString ) + 2 )) {
                /*
                **      Make a copy terminated with a newline.
                **/
                strcpy( com, acme->acme_CommandString );
                strcat( com, "\n" );
                /*
                **      Find the length of the command
                **      and the start of the arguments.
                **/
                args = com;
                while ( isspace( *args ))   args++;
                tmp = args;
                while ( ! isspace( *args )) { len++; args++; }
                /*
                **      Look up the command.
                **/
                if ( rco = FindRXCommand( ad, tmp, len )) {
                        /*
                        **      Allocate a REXXARGS structure.
                        **/
                        if ( ra = ( REXXARGS * )AllocMemory( sizeof( REXXARGS ))) {
                                /*
                                **      Args expected?
                                **/
                                if ( rco->rc_ArgTemplate ) {
                                        /*
                                        **      Count the expected number of arguments.
                                        **/
                                        tmp = rco->rc_ArgTemplate;
                                        while ( *tmp != '\n' ) {
                                                if ( *tmp++ != ',' ) numargs++;
                                        }
                                        numargs++;
                                        /*
                                        **      Allocate space to parse the args.
                                        **/
                                        if ( ra->ra_ArgList = ( ULONG * )AllocMemory( numargs * sizeof( ULONG ))) {
                                                /*
                                                **      Setup the parser.
                                                **/
                                                ad->ad_DOSParser->RDA_Source.CS_Buffer = args;
                                                ad->ad_DOSParser->RDA_Source.CS_Length = strlen( args );
                                                ad->ad_DOSParser->RDA_Source.CS_CurChr = 0;
                                                ad->ad_DOSParser->RDA_DAList           = NULL;
                                                ad->ad_DOSParser->RDA_Buffer           = NULL;
                                                /*
                                                **      Parse the args.
                                                **/
                                                if ( ReadArgs( rco->rc_ArgTemplate, ra->ra_ArgList, ad->ad_DOSParser )) {
                                                        /*
                                                        **      Run command.
                                                        **/
                                                        ( rco->rc_Func )( ra, NULL );
                                                        /*
                                                        **      Store results.
                                                        **/
                                                        r      = ra->ra_RC;
                                                        r2     = ra->ra_RC2;
                                                        result = ra->ra_Result;
                                                        FreeArgs( ad->ad_DOSParser );
                                                } else {
                                                        r  = 10;
                                                        r2 = IoErr();
                                                }
                                                /*
                                                **      Deallocate arg list.
                                                **/
                                                FreeMemory( ra->ra_ArgList );
                                        } else
                                                r2 = ERROR_NO_FREE_STORE;
                                } else {
                                        /*
                                        **      Run command.
                                        **/
                                        ( rco->rc_Func )( ra, NULL );
                                        /*
                                        **      Store results.
                                        **/
                                        r      = ra->ra_RC;
                                        r2     = ra->ra_RC2;
                                        result = ra->ra_Result;
                                }
                                /*
                                **      Deallocate the REXXARGS structure.
                                **/
                                FreeMemory( ra );
                        } else
                                r2 = ERROR_NO_FREE_STORE;
                        /*
                        **      When not passed to the rexx server
                        **      we must close the given IO handle
                        **      ourselves.
                        **/
                        if ( acme->acme_IO ) Close( acme->acme_IO );
                } else {
                        /*
                        **      We do not send the new-line to
                        **      the ARexx server.
                        **/
                        com[ strlen( com ) - 1 ] = '\0';
                        /*
                        **      Unknown commands are shipped
                        **      off to the REXX server.
                        **/
                        if ( rxm = CreateRexxCommand( ad, com, acme->acme_IO )) {
                                if ( ! CommandToRexx( ad, rxm ))
                                        r2 = ERROR_NOT_IMPLEMENTED;
                                else
                                        r = r2 = 0L;
                        } else
                                r2 = ERROR_NO_FREE_STORE;
                }
                /*
                **      Deallocate the command copy.
                **/
                FreeMemory( com );
        } else
                r2 = ERROR_NO_FREE_STORE;

        /*
        **      Put the results into their
        **      storage spaces.
        **/
        if ( acme->acme_RC     ) *( acme->acme_RC     ) = r;
        if ( acme->acme_RC2    ) *( acme->acme_RC2    ) = r2;
        if ( acme->acme_Result ) *( acme->acme_Result ) = result;

        return( 1L );
}

/*
**      OM_GET.
**/
STATIC ULONG ARexxGet( Class *cl, Object *obj, struct opGet *opg )
{
        AD                      *ad = ( AD * )INST_DATA( cl, obj );
        ULONG                   rc = 1L;

        /*
        **      What do they want?
        **/
        switch ( opg->opg_AttrID ) {

                case    AC_HostName:
                        *( opg->opg_Storage ) = ( ULONG )ad->ad_HostName;
                        break;

                case    AC_RexxPortMask:
                        *( opg->opg_Storage ) |= ( 1L << ad->ad_Port->mp_SigBit );
                        break;

                default:
                        rc = DoSuperMethodA( cl, obj, ( Msg )opg );
                        break;
        }
        return( rc );
}

/*
**      ACM_HANDLE_EVENT.
**/
STATIC ULONG ARexxHandleEvent( Class *cl, Object *obj, Msg msg )
{
        struct RexxMsg          *rxm, *org;
        AD                      *ad = ( AD * )INST_DATA( cl, obj );
        ULONG                    rc = 1L;

        /*
        **      Get the messages from the port.
        **/
        while ( rxm = ( struct RexxMsg * )GetMsg( ad->ad_Port )) {
                /*
                **      A Rexx command?
                **/
                if (( rxm->rm_Action & RXCODEMASK ) != RXCOMM )
                        ReplyMsg(( struct Message * )rxm );
                else if ( rxm->rm_Node.mn_Node.ln_Type == NT_REPLYMSG ) {
                        /*
                        **      Reply'd message. See if it was started
                        **      as a script.
                        **/
                        if ( org = ( struct RexxMsg * )rxm->rm_Args[ 15 ] ) {
                                if ( rxm->rm_Result1 )
                                        ReplyRexxCommand( org, 20, ERROR_NOT_IMPLEMENTED, NULL );
                                else
                                        ReplyRexxCommand( org, 0, 0, ( UBYTE * )rxm->rm_Result2 );
                        }
                        /*
                        **      Free the message and decrease the
                        **      pending counter.
                        **/
                        FreeRexxCommand( rxm );
                        ad->ad_PendCnt--;
                } else if ( ARG0( rxm ))
                        /*
                        **      Execute message.
                        **/
                        DoRXCommand( ad, rxm );
                else
                        ReplyMsg(( struct Message * )rxm );
        }
}

/*
**      Class dispatcher.
**/
STATIC SAVEDS ASM ULONG ARexxDispatch( REG(A0) Class *cl, REG(A2) Object *obj, REG(A1) Msg msg )
{
        ULONG                   rc = 0L;

        /*
        **      Evaluate the method.
        **/
        switch ( msg->MethodID ) {

                case    OM_NEW:
                        rc = ARexxNew( cl, obj, ( struct opSet * )msg );
                        break;

                case    OM_GET:
                        rc = ARexxGet( cl, obj, ( struct opGet * )msg );
                        break;

                case    OM_DISPOSE:
                        rc = ARexxDispose( cl, obj, msg );
                        break;

                case    ACM_HANDLE_EVENT:
                        rc = ARexxHandleEvent( cl, obj, msg );
                        break;

                case    ACM_EXECUTE:
                        rc = ARexxExecute( cl, obj, ( struct acmExecute * )msg );
                        break;

                default:
                        rc = DoSuperMethodA( cl, obj, msg );
                        break;
        }
        return ( rc );
}
