/* Dispatch() -- dispatcher for the "rxexample.library" function library

 * This function is called as the "query" entry point from the rxexample
 * library.  The arguments are contained in a RexxMsg structure, which is
 * used as a parameter block and doesn't need to be unlinked from a port.
 * Dispatch() searches a table to see whether the requested function is
 * contained in this library and calls it if it is found.  The "query"
 * function should never alter the message packet, as it must be passed
 * along to other libraries until the function is found.

 * Arguments:
 * Up to 15 arguments may be passed in the rm_Args[] array of the parameter
 * block.  Arguments are ALWAYS passed as argstrings and can generally be
 * treated like string pointers in a 'C' program.  The called function may
 * need to convert the strings to numeric values if arithmetic operations
 * are to be performed.  A NULL value in the argument slot means that the
 * argument was omitted from the function call, as in MyFunc(1,,3).

 * The total number of arguments (including the defaulted ones) is available
 * in the low-order byte of the action code field rm_Action.
 * Note that REXX supports function calls with varying numbers of arguments,
 * and that the called function can always determine how many were actually
 * passed.

 * Error reporting:
 * The function must return an integer error code and a result string if no
 * errors were detected.  Errors are considered to be ARexx internal error
 * codes, so the function should make use of these values as appropriate.
 * A code of 0 is interpreted to mean that everything worked.

 * Result strings:
 * The result string must be returned as an argstring, a pointer to the
 * string buffer of an RexxArg structure.  Argstrings can be created by a
 * call to the ARexx Systems library function CreateArgstring().
 * N.B. Never allocate a result string if the error code is non-zero!
 */

#include "rexx/storage.h"
#include "rexx/rxslib.h"

/* The retblock structure provides storage for returned values          */
struct retblock {
   LONG     Type;                      /* type of return                */
   union {
      LONG  IntVal;                    /* integer value                 */
      APTR  AddrVal;                   /* 4-byte address                */
      UBYTE CharVal[256];              /* null-terminated string        */
      } values;
   };

#define RBINT    1                     /* integer return                */
#define RBADDR   2                     /* 4-byte address return         */
#define RBSTRING 3                     /* string return                 */

LONG     MyFunc1();
LONG     MyFunc2();
LONG     MyFunc3();
LONG     flookup(STRPTR);

/* ARexx Systems library functions  */
STRPTR   CreateArgstring(STRPTR,LONG);
STRPTR   CVi2arg(LONG);
LONG     CVa2i(STRPTR,LONG *);

struct RexxLib   *RexxSysBase;         /* Filled in by the library      */
struct RxExample *RexxExmpBase;        /* initialization code ...       */

/* The tables of function names and function pointers.  This could be done
 * more elegantly by defining a function definiton structure, perhaps
 * including an argument conversion template.
 */

#define NUMFUNC 3
STATIC STRPTR fname[NUMFUNC] =
   {"MyFunc1",
    "MyFunc2",
    "MyFunc3"};

STATIC LONG (*funcptr[NUMFUNC])(struct RexxMsg *,struct retblock *) =
   {&MyFunc1,
    &MyFunc2,
    &MyFunc3};

LONG Dispatch(rmptr,resptr)
struct RexxMsg *rmptr;                 /* message packet                */
STRPTR         *resptr;                /* return pointer                */
{
   LONG        findex;
   LONG        retcode;
   STRPTR      retval=NULL;
   struct retblock   block1;

   findex = flookup(rmptr->rm_Args[0]);

   if (findex >= 0) {
      retcode = (*(funcptr[findex]))(rmptr,&block1);

      /* Convert the return value to an argstring                       */
      if (retcode == 0) switch (block1.Type) {           /* all OK      */
         case RBINT:
            retval = CVi2arg(block1.values.IntVal);
            break;

         case RBADDR:
            retval = CreateArgstring((STRPTR) &block1.values.AddrVal,4L);
            break;

         case RBSTRING:
            retval = CreateArgstring(&block1.values.CharVal[0],
                              strlen(&block1.values.CharVal[0]));
            break;
         }
      }
   else retcode = 1L;                  /* function not found            */

   /* Set the return values                                             */
   *resptr = retval;                   /* value string                  */
   return(retcode);
}

/* Look for the function name in the table                              */

LONG flookup(name)
STRPTR name;
{
   LONG        i;
   LONG        index = -1L;

   for (i=0; i < NUMFUNC; i++) {
      if (strcmp(name,fname[i]) == 0) {
         index = i;
         break;
         }
      }

   return(index);                      /* function index or -1          */
}

/* This function returns its argument as an integer.  It will return a
 * conversion error if called with a non-numeric argument.
 */

STATIC LONG MyFunc1(rmptr,rbptr)
struct RexxMsg  *rmptr;
struct retblock *rbptr;
{
   LONG     arg,digits;

   /* Convert the first argument and check for errors                   */
   arg = CVa2i(rmptr->rm_Args[1],&digits);

   /* Fill in the return block                                          */
   rbptr->Type = RBINT;
   rbptr->values.IntVal = arg;

   /* Check whether the entire string was converted ...                 */
   return(digits == strlen(rmptr->rm_Args[1]) ? 0L : 47L);
}

/* This function returns the address of the function library            */

STATIC LONG MyFunc2(rmptr,rbptr)
struct RexxMsg *rmptr;
struct retblock *rbptr;
{
   rbptr->Type = RBADDR;
   rbptr->values.AddrVal = (APTR) RexxExmpBase;
   return(0L);
}

/* This function returns a string value                                 */

STATIC LONG MyFunc3(rmptr,rbptr)
struct RexxMsg  *rmptr;
struct retblock *rbptr;
{
   rbptr->Type = RBSTRING;
   strcpy(&rbptr->values.CharVal[0],"The Answer");
   return(0L);
}
