home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 18 REXX
/
18-REXX.zip
/
testrexx.zip
/
testrexx.c
next >
Wrap
Text File
|
1994-04-07
|
38KB
|
999 lines
/* TESTREXX.C
*
* This is a template for a DLL written to add REXX External Functions to the REXX Interpreter (ie,
* which any REXX program can then call just as if they were standard "built-in" REXX commands).
* Here's how it works:
* The REXX interpreter is that portion of OS/2 that loads and executes REXX scripts (ie, programs).
* This DLL registers, with the REXX interpreter, ascii strings that are the "names" of the
* REXX-callable functions within it. (The DLL was compiled with these functions declared external
* so that the REXX interpreter can resolve the actual addresses of the functions). The REXX
* interpreter works as a "middle man" between the REXX script and this DLL. When a REXX script
* makes a call to one of our function "names", the REXX interpreter grabs any args supplied by the
* REXX script, packs them into an array of RXSTRING structures, and calls our appropriate DLL
* function, passing on those args from the script.
* What's a RXSTRING structure? It's this thing that REXX uses to store a variable's value.
* Remember that all variables in REXX are expressed as null-terminated strings, including numeric
* variables. For example, if a variable has the value 129, REXX internally stores it as the
* null-terminated string of "129". (Another way of looking at it in C is a UCHAR array containing
* {0x31, 0x32, 0x39, 0x00} ). So, a RXSTRING has 2 fields. It has a pointer to the null-terminated
* string that represents the variable's value. It has another field that is the length of that string in
* bytes. That's how REXX variables are stored. So, sometimes our DLL needs to convert args
* passed by the script to ULONG, LONG, USHORT, SHORT, UCHAR, or CHAR binary values (if those
* args are supposed to be numeric values).
* REXX always passes 5 args to our REXX functions, with one of those args being a pointer to
* that aforementioned array of RXSTRING structures, which itself contains any args supplied by the
* actual REXX script. Another one of those 5 args is a ULONG that tells how many RXSTRINGs are
* in that array of script-supplied args (ie, how many args the script passed us). Another one of the 5
* args is a pointer to a RXSTRING structure into which our function is supposed to return a string to
* the REXX script. That's how we implement a "return value" to the script, which of course, the REXX
* interpreter assigns to the script's RC variable. Since all variables in REXX are expressed as
* null-terminated strings, including numeric variables, anything we return to a script must also be
* expressed as a null-terminated string. For example, if we want to return a 0 for RC to a REXX script,
* we'll stuff the string "0" into that return RXSTRING structure, and set its length to 1.
* If we want to return an "null" string (ie, "") to a REXX script, we set the return RXSTRING's
* length to 0. In that case, we don't even need to initialize the RXSTRING's strptr field. Returning
* a null string is a handy way of indicating to the REXX script that a function failed to complete its
* task. This is just a suggestion. That's often what I return for an error condition, even from
* functions that would otherwise return non-nulled strings. It's easy to do.
* The actual return value of a DLL function is for the REXX interpreter itself. This should be 0
* if we detected no syntax errors with the args supplied by the REXX script. It will be non-zero if we
* want to tell the REXX interpreter to perhaps raise the ERROR or FAILURE flags. Remember that
* this return has nothing to do with the return RXSTRING, the latter being for the REXX script.
*
* Essentially, this DLL is a template for developing your own DLLs. It has 20 functions in it, called
* "TestDummy1", "TestDummy2", "TestDummy3", etc. These dummy functions do very little of
* any importance. At best, they demonstrate simple concepts. You can simply replace the code for
* one of these dummy functions with the code of one of your own functions. Then, do a string
* search on this source, exchanging the name of that dummy function wherever it appears in the
* source, to your desired name. Start with TestDummy1 for your first function, then TestDummy2 for
* your second function. etc. Also, edit the file TESTREXX.DEF, and change the old TestDummy name
* to your new function name. I recommend that you preface all of your DLL names with a certain string
* of characters. For example, here I chose to begin all function names with the 4 characters "Test".
* This helps you to identify calls to your DLL when you write a REXX script. Also, if you pick a unique
* preface, then it helps to avoid name collisions with other REXX scripts. (ie, You don't want to have
* identical function names with other DLL function libraries).
* Compile the DLL and test it with a REXX script. The REXX script should begin with the
* following two lines:
*
* CALL RxFuncAdd 'TestLoadFuncs', 'TESTREXX', 'TestLoadFuncs'
* CALL TestLoadFuncs
*
* When you finally have all of the functions that you desire added to the DLL, (and tested/debugged),
* delete all of the references to TestDummy functions. ie, Delete the RexxFunctionHandler
* declarations for them. Delete the references in FncTable (ie, delete any lines containing the
* TestDummy name). And delete any TestDummy functions in the source. DON'T DELETE TestLoadFuncs
* or TestDropFuncs -- these aren't dummy functions!
* You'll probably want to change the name of the DLL itself, from TESTREXX, to something else.
* Edit the files TESTREXX.C, TESTREXX.MAK, TESTREXX.DEF, and any test scripts that you write,
* changing any instance of TESTREXX to your new desired name.
* Also, you may wish to change the preface of TestLoadFuncs and TestDropFuncs from "Test" to
* whatever preface you used for your other functions. Remember to change all references in this
* source, your test script, and also TESTREXX.DEF.
* Finally, if none of your functions happen to use some of the support functions in this DLL, such as
* AddRexxVar, then you can delete those support functions.
* Now, recompile your DLL into a finished product. Not too bad, eh?
*
* NOTES: The DLL should be compiled with the multi-threaded C libs for multi-threaded versions of
* strlen, strcpy, etc. The C/Set2 makefile specifies this.
* Furthermore, if you use static (ie, global) variables whose values are changed by any DLL
* function (ie, the variable doesn't keep one value throughout its lifetime), then you should change
* the TESTREXX.DEF file. Change the line:
*
* DATA SINGLE SHARED
*
* to the line
*
* DATA MULTIPLE NONSHARED
*
* This ensures that each REXX script gets its own copy of this DLL's data section. In my test version,
* none of my global variables ever change value, so there is no need to have separate data sections
* for each REXX script that uses this DLL. You can avoid needing multiple copies of data by only using
* local variables in your DLL functions. Note that the return RXSTRING passed to a function always
* points to a 256 byte buffer. I frequently use that as a convenient temp buffer or variables, up to the
* point that I need to format the return string.
* This DLL doesn't make any calls to OS/2 functions. That doesn't mean that you couldn't call
* DosOpen() for example.
*/
/* Include files */
#define INCL_DOSFILEMGR
#define INCL_DOSMEMMGR
#define INCL_REXXSAA
#define INCL_RXSHV
#define _DLL
#define _MT
#include <os2.h>
#include <rexxsaa.h>
#include <string.h>
/*********************************************************************
* Function Returns (to the REXX interpreter)
*********************************************************************/
#define INVALID_ROUTINE 40 /* Raise Rexx error */
#define VALID_ROUTINE 0 /* Successful completion */
/*********************************************************************
* Declare all exported functions as REXX functions (ie, a REXX app can call them)
*********************************************************************/
RexxFunctionHandler TestDummy1;
RexxFunctionHandler TestDummy2;
RexxFunctionHandler TestDummy3;
RexxFunctionHandler TestDummy4;
RexxFunctionHandler TestDummy5;
RexxFunctionHandler TestDummy6;
RexxFunctionHandler TestDummy7;
RexxFunctionHandler TestDummy8;
RexxFunctionHandler TestDummy8;
RexxFunctionHandler TestDummy10;
RexxFunctionHandler TestDummy11;
RexxFunctionHandler TestDummy12;
RexxFunctionHandler TestDummy13;
RexxFunctionHandler TestDummy14;
RexxFunctionHandler TestDummy15;
RexxFunctionHandler TestDummy16;
RexxFunctionHandler TestDummy17;
RexxFunctionHandler TestDummy18;
RexxFunctionHandler TestDummy19;
RexxFunctionHandler TestDummy20;
RexxFunctionHandler TestLoadFuncs;
RexxFunctionHandler TestDropFuncs;
/* NOTE: In the module definition (.def) file, I specify DATA to be SINGLE SHARED rather than MULTIPLE NONSHARED. This
is because, since I only have globals that are read-only, I don't need a separate copy of these for each process that
uses this DLL */
/******************************************************
* This is an array containing the names of all of the REXX callable
* functions in this DLL. This is used when we initially register each
* function with the REXX interpreter, and deregister upon closedown.
******************************************************/
CHAR FncTable[] = { "TestLoadFuncs\0\
TestDropFuncs\0\
TestDummy1\0\
TestDummy2\0\
TestDummy3\0\
TestDummy4\0\
TestDummy5\0\
TestDummy6\0\
TestDummy7\0\
TestDummy8\0\
TestDummy9\0\
TestDummy10\0\
TestDummy11\0\
TestDummy12\0\
TestDummy13\0\
TestDummy14\0\
TestDummy15\0\
TestDummy16\0\
TestDummy17\0\
TestDummy18\0\
TestDummy19\0\
TestDummy20\0\
\0" };
/**************************** TestLoadFuncs() *****************************
* Syntax: call TestLoadFuncs
*
* Params: none
*
* RC Return: If success, a null string
*
* Purpose: Registers all of the REXX functions in this DLL with the REXX Interpreter, so
* that a REXX program can call them. This function relieves a REXX script from
* having to individually RXFUNCADD for every function in this DLL that the REXX
* script wishes to call.
*************************************************************************/
ULONG TestLoadFuncs(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
{
UCHAR * ptr;
/* Set RC="0" */
retstr->strlength = 0;
/* Get first function name */
ptr = &FncTable[0];
/* Register each one of the REXX functions in this DLL. Now, the REXX interpreter will let a REXX
program call them. */
while (*ptr)
{
RexxRegisterFunctionDll(ptr, "TESTREXX", ptr);
ptr = ptr+strlen(ptr)+1;
}
/* Tell REXX that calling template was correct */
return VALID_ROUTINE;
}
/***************************** TestDropFuncs() ******************************
* Syntax: call TestDropFuncs
*
* Params: none
*
* RC Return: If success, a null string
*
* Purpose: Deregisters all of the REXX functions in this DLL.
*************************************************************************/
ULONG TestDropFuncs(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
{
UCHAR * ptr;
/* Set RC="" */
retstr->strlength = 0;
/* Get first function name */
ptr = &FncTable[0];
/* Register each one of the REXX functions in this DLL. Now, the REXX interpreter will let a REXX
program call them. */
while (*ptr)
{
RexxDeregisterFunction(ptr);
ptr = ptr+strlen(ptr)+1;
}
/* Tell REXX that calling template was correct */
return VALID_ROUTINE;
}
/* ************************** SetRexxVar() *******************************
* Called by various DLL routines to set the value of a REXX variable (and create it if it
* doesn't yet exist). This is just a support function. It is passed two strings; the name
* of the REXX variable (must be in upper case), and the value of that variable (expressed
* as a string). This returns 0 for success, 1 for error.
******************************************************************** */
ULONG SetRexxVar(UCHAR *name, UCHAR *value)
{
LONG rc;
SHVBLOCK block;
/* The shvcode is set to RXSHV_SET, which means we're setting the variable, using its
name as-is. This means the name must be a valid REXX symbol, in upper case. */
block.shvcode = RXSHV_SET;
/* Set the variable value */
MAKERXSTRING(block.shvvalue, value, strlen(value));
/* Set the variable name */
block.shvname.strptr = name;
block.shvname.strlength = strlen(name);
/* The shvnamelen and shvvaluelen duplicate the lengths of the variable name and its value
string for simple variable-setting operations such as what we are doing here. */
block.shvnamelen = block.shvname.strlength;
block.shvvaluelen = block.shvvalue.strlength;
/* The shvnext ptr is 0 because we only are doing one request (ie, one variable). */
block.shvnext = 0;
/* NOTE: The shvret byte is not set. It's an output value. */
/* Call the REXX variable pool to set/create this REXX variable. We use an AND operation to
turn off the low-order bit in the return code, because that bit indicates "new variable
created", and we do not care about that. Other return values indicate an error. */
if( ((rc=RexxVariablePool(&block)) & 0xFFFFFFFE) ) return(1);
return(0);
}
/* ************************** GetRexxVar() *******************************
* Called by various DLL routines to fetch the value of a REXX variable (and create it if it
* doesn't yet exist). This is just a support function. It is passed 3 args; a ptr to the name
* of the REXX variable (must be in upper case), a ptr to the buffer where that variable's
* value (expressed as a string) is returned, and the length of that buffer. This returns
* 0 for success, 1 for error.
******************************************************************** */
ULONG GetRexxVar(UCHAR *name, UCHAR *buffer, ULONG buffersize)
{
LONG rc;
SHVBLOCK block;
/* The shvcode is set to RXSHV_FETCH, which means we're fetching the variable's value, using its
name as-is. This means the name must be a valid REXX symbol, in upper case. */
block.shvcode = RXSHV_FETCH;
/* Set the buffer where REXX returns the variable value */
block.shvvalue.strptr = buffer;
block.shvvalue.strlength = buffersize;
/* Set the variable name */
block.shvname.strptr = name;
block.shvname.strlength = strlen(name);
/* The shvnamelen and shvvaluelen duplicate the lengths of the variable name and its value
string for simple variable-fetching operations such as what we are doing here. */
block.shvnamelen = block.shvname.strlength;
block.shvvaluelen = block.shvvalue.strlength;
/* The shvnext ptr is 0 because we only are doing one request (ie, one variable). */
block.shvnext = 0;
/* NOTE: The shvret byte is not set. It's an output value. */
/* Call the REXX variable pool to fetch/create this REXX variable. We use an AND operation to
turn off the low-order bit in the return code, because that bit indicates "new variable
created", and we do not care about that. Other return values indicate an error. Of course,
if a variable doesn't yet exist, when created, it's value is its name. */
if( ((rc=RexxVariablePool(&block)) & 0xFFFFFFFE) ) return(1);
/* REXX doesn't null-terminate the value in the buffer, so for the sake of C string compatibility
we'll null it out ourselves. */
*(block.shvvalue.strptr+block.shvvalue.strlength) = 0;
return(0);
}
/****************************** TestDummy1() ******************************
* Syntax: something = TestDummy1(something)
*
* Params: A rexx value. Remember, all REXX values are expressed as null-terminated strings.
*
* RC Return: If success, it simply returns that same value that the script passed.
*
* Purpose: Returns the same value that the script passes. Completely worthless.
*************************************************************************/
ULONG TestDummy1(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
{
/* Check that the REXX script has passed in at least 1 arg, and it's valid (ie, not a null string).
If not, tell REXX to raise FAILURE */
if ( !numargs || !RXVALIDSTRING(args[0]) ) return INVALID_ROUTINE;
/* OK, simply copy that one arg to the return RXSTRING */
strcpy(retstr->strptr, args[0].strptr);
retstr->strlength = args[0].strlength;
/* Tell REXX that calling template was correct */
return VALID_ROUTINE;
}
/****************************** TestDummy2() ******************************
* Syntax: somenum = TestDummy2(somenum)
*
* Params: A rexx numeric value. Remember, all REXX values are expressed as null-terminated
* strings; even numeric values.
*
* RC Return: If success, it simply returns that same value that the script passed.
* If an error, returns a null string.
*
* Purpose: Checks that the passed value contains only numeric digits. Returns the same value
* if so, or a null string if not so.
*************************************************************************/
ULONG TestDummy2(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
{
CHAR * ptr;
/* Check that the REXX script has passed in at least 1 arg, and it's valid (ie, not a null string).
If not, tell REXX to raise FAILURE */
if ( !numargs || !RXVALIDSTRING(args[0]) ) return INVALID_ROUTINE;
/* Assume that we'll return a null string */
retstr->strlength = 0;
/* Check all of the digits. Make sure that they're numeric */
ptr = args[0].strptr;
while (*ptr=='+' || *ptr=='-') ptr++;
while (*ptr)
{
if ( !isdigit(*(ptr)++) ) return VALID_ROUTINE; /* oops! Not all numeric digits */
}
/* OK, simply copy that one arg to the return RXSTRING */
strcpy(retstr->strptr, args[0].strptr);
retstr->strlength = args[0].strlength;
/* Tell REXX that calling template was correct */
return VALID_ROUTINE;
}
/****************************** TestDummy3() ******************************
* Syntax: sum = TestDummy3(num1, num2)
*
* Params: 2 rexx numeric values.
*
* RC Return: If success, it returns the sum of the 2 values that the script passed.
* If an error, returns a null string.
*
* Purpose: Checks that the 2 passed values contain only numeric digits. If so, the values
* are added, and the sum is returned. If not numeric values, a null string is returned.
*************************************************************************/
ULONG TestDummy3(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
{
CHAR * ptr;
LONG val1, val2;
/* Check that the REXX script has passed in at least 2 args, and they're valid (ie, not a null strings).
If not, tell REXX to raise FAILURE */
if ( numargs < 2 || !RXVALIDSTRING(args[0]) || !RXVALIDSTRING(args[1]) ) return INVALID_ROUTINE;
/* Assume that we'll return a null string */
retstr->strlength = 0;
/* Check all of the digits in the first arg. Make sure that they're numeric */
ptr = args[0].strptr;
while (*ptr=='+' || *ptr=='-') ptr++;
while (*ptr)
{
if ( !isdigit(*(ptr)++) ) return VALID_ROUTINE; /* oops! Not all numeric digits */
}
/* Check all of the digits in the second arg. Make sure that they're numeric */
ptr = args[1].strptr;
while (*ptr=='+' || *ptr=='-') ptr++;
while (*ptr)
{
if ( !isdigit(*(ptr)++) ) return VALID_ROUTINE; /* oops! Not all numeric digits */
}
/* OK, now in order to add these values, we've got to convert those ascii strings into 32-bit values */
val1 = atol(args[0].strptr);
val2 = atol(args[1].strptr);
/* Add them */
val1 += val2;
/* OK, now we have to express the 32-bit sum as an ascii string when we stuff it into the return RXSTRING.
Remember that REXX internally stores all values as ascii strings. */
_ltoa(val1, retstr->strptr, 10);
retstr->strlength = strlen(retstr->strptr);
/* Tell REXX that calling template was correct */
return VALID_ROUTINE;
}
/****************************** TestDummy4() ******************************
* Syntax: str = TestDummy4(str1, str2)
*
* Params: 2 rexx values.
*
* RC Return: If success, it returns str2 appended to str1.
* If an error, returns a null string.
*
* Purpose: Returns str2 appended to str1.
*************************************************************************/
ULONG TestDummy4(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
{
ULONG len;
UCHAR * mem;
/* Check that the REXX script has passed in at least 2 args, and they're valid (ie, not a null strings).
If not, tell REXX to raise FAILURE */
if ( numargs < 2 || !RXVALIDSTRING(args[0]) || !RXVALIDSTRING(args[1]) ) return INVALID_ROUTINE;
/* We have to check whether the combined length of the 2 args doesn't exceed 255 characters, plus 1 for
a terminating null byte. Why? Because that's how big the return RXSTRING's strptr is. So what happens
if we need a bigger buffer? Well, we can allocate a new buffer with with DosAllocMem(), and put the pointer
in the return RXSTRING's strptr. REXX will return that memory on our behalf.
*/
len = args[0].strlength + args[1].strlength + 1;
if (len > retstr->strlength)
{
if (DosAllocMem((PPVOID)&mem, len, PAG_COMMIT|PAG_WRITE|PAG_READ))
{
/* Return a null string */
retstr->strlength = 0;
return VALID_ROUTINE; /* oops. No memory. Return null string */
}
retstr->strptr=mem;
}
/* Copy str1 to the return RXSTRING. Then append str2. Then set the return RXSTRING's length */
strcpy(retstr->strptr, args[0].strptr);
strcat(retstr->strptr, args[1].strptr);
retstr->strlength = strlen(retstr->strptr);
/* Tell REXX that calling template was correct */
return VALID_ROUTINE;
}
/****************************** TestDummy5() ******************************
* Syntax: sum = TestDummy5(num1, num2)
*
* Params: 2 rexx numeric values.
*
* RC Return: If success, it returns the sum of the 2 values that the script passed. Also,
* it sets the REXX variable TESTSUM to that same value.
* If an error, returns a null string.
*
* Purpose: Checks that the 2 passed values contain only numeric digits. If so, the values
* are added, and the sum is returned, as well as the REXX variable TESTSUM
* is set to that value. If not numeric values, a null string is returned.
* This is the same thing as TestDummy3, except that it demonstrates how
* to use SetRexxVar() to directly create and set a variable in a REXX script.
*************************************************************************/
ULONG TestDummy5(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
{
CHAR * ptr;
LONG val1, val2;
/* Check that the REXX script has passed in at least 2 args, and they're valid (ie, not a null strings).
If not, tell REXX to raise FAILURE */
if ( numargs < 2 || !RXVALIDSTRING(args[0]) || !RXVALIDSTRING(args[1]) ) return INVALID_ROUTINE;
/* Assume that we'll return a null string */
retstr->strlength = 0;
/* Check all of the digits in the first arg. Make sure that they're numeric */
ptr = args[0].strptr;
while (*ptr=='+' || *ptr=='-') ptr++;
while (*ptr)
{
if ( !isdigit(*(ptr)++) ) return VALID_ROUTINE; /* oops! Not all numeric digits */
}
/* Check all of the digits in the second arg. Make sure that they're numeric */
ptr = args[1].strptr;
while (*ptr=='+' || *ptr=='-') ptr++;
while (*ptr)
{
if ( !isdigit(*(ptr)++) ) return VALID_ROUTINE; /* oops! Not all numeric digits */
}
/* OK, now in order to add these values, we've got to convert those ascii strings into 32-bit values */
val1 = atol(args[0].strptr);
val2 = atol(args[1].strptr);
/* Add them */
val1 += val2;
/* OK, now we have to express the 32-bit sum as an ascii string when we stuff it into the return RXSTRING.
Remember that REXX internally stores all values as ascii strings. */
_ltoa(val1, retstr->strptr, 10);
/* Let's set the REXX variable, TESTSUM, to that same value. If an error, get out of here, leaving the return
RXSTRING's strlength=0 (ie, a null string) */
if ( SetRexxVar("TESTSUM", retstr->strptr) ) return VALID_ROUTINE;
/* OK, now that everything went well, let's set the length of the return RXSTRING. (ie, so that we're no
longer returning it as 0 length, which is a null string regardless of the contents of strptr) */
retstr->strlength = strlen(retstr->strptr);
/* Tell REXX that calling template was correct */
return VALID_ROUTINE;
}
/****************************** TestDummy6() ******************************
* Syntax: sum = TestDummy6(num1, num2)
*
* Params: 2 rexx numeric values.
*
* RC Return: If success, it returns the sum of the 2 values that the script passed. Also,
* it sets the REXX stem variable TESTRESULT.0 to that same value, and the
* REXX stem variable TESTRESULT.1 to the multiplication of the 2 values.
* If an error, returns a null string.
*
* Purpose: Checks that the 2 passed values contain only numeric digits. If so, the values
* are added, and the sum is returned, as well as the REXX variable TESTRESULT.0
* is set to that value, and TESTRESULT.1 set to the multiplication of the two
* values. If not numeric values, a null string is returned.
* This is the same thing as TestDummy5, except that it demonstrates how
* to use SetRexxVar() to directly create and set stem variables in a REXX script.
* Stem variables simply have a "." in the name, and a number after.
*************************************************************************/
ULONG TestDummy6(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
{
CHAR * ptr;
LONG val1, val2, sum;
/* Check that the REXX script has passed in at least 2 args, and they're valid (ie, not a null strings).
If not, tell REXX to raise FAILURE */
if ( numargs < 2 || !RXVALIDSTRING(args[0]) || !RXVALIDSTRING(args[1]) ) return INVALID_ROUTINE;
/* Assume that we'll return a null string */
retstr->strlength = 0;
/* Check all of the digits in the first arg. Make sure that they're numeric */
ptr = args[0].strptr;
while (*ptr=='+' || *ptr=='-') ptr++;
while (*ptr)
{
if ( !isdigit(*(ptr)++) ) return VALID_ROUTINE; /* oops! Not all numeric digits */
}
/* Check all of the digits in the second arg. Make sure that they're numeric */
ptr = args[1].strptr;
while (*ptr=='+' || *ptr=='-') ptr++;
while (*ptr)
{
if ( !isdigit(*(ptr)++) ) return VALID_ROUTINE; /* oops! Not all numeric digits */
}
/* OK, now in order to add these values, we've got to convert those ascii strings into 32-bit values */
val1 = atol(args[0].strptr);
val2 = atol(args[1].strptr);
/* Multiply them */
sum = val1 * val2;
/* Let's set the REXX variable, TESTRESULT.1 to that result value. If an error, get out of here, leaving the return
RXSTRING's strlength=0 (ie, a null string). We have to express the 32-bit result as an ascii string when we
set TESTRESULT.1's value. Remember that REXX internally stores all values as ascii strings. I need a temp
buffer to store the ascii string. Hey, why not use the return RXSTRING's buffer? Yeah.
*/
_ltoa(sum, retstr->strptr, 10);
if ( SetRexxVar("TESTRESULT.1", retstr->strptr) ) return VALID_ROUTINE;
/* Add them */
val1 += val2;
/* OK, now we have to express the 32-bit sum as an ascii string when we stuff it into the return RXSTRING.
Remember that REXX internally stores all values as ascii strings. */
_ltoa(val1, retstr->strptr, 10);
/* Let's set the REXX variable, TESTRESULT.0, to that same value. If an error, get out of here, leaving the return
RXSTRING's strlength=0 (ie, a null string) */
if ( SetRexxVar("TESTRESULT.0", retstr->strptr) ) return VALID_ROUTINE;
/* OK, now that everything went well, let's set the length of the return RXSTRING. (ie, so that we're no
longer returning it as 0 length, which is a null string regardless of the contents of strptr) */
retstr->strlength = strlen(retstr->strptr);
/* Tell REXX that calling template was correct */
return VALID_ROUTINE;
}
/****************************** TestDummy7() ******************************
* Syntax: err = TestDummy7(str1, str2, flag)
*
* Params: 2 rexx strings to compare.
* A flag. If 's' then this means to do a case-sensitive compare of str1 and str2.
* If 'i' then this means to do a case-insensitive compare of str1 and str2. If the
* flag arg is not supplied, or is a null-string, then assume case-sensitive.
*
* RC Return: If str1 < str2, then a value less than 0 is returned.
* If str1 = str2, then a value of 0 is returned.
* If str1 > str2, then a value greater than 0 is returned.
*
* Purpose: Compares str2 to str1 and results a result as above.
*************************************************************************/
ULONG TestDummy7(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
{
LONG result;
/* Check that the REXX script has passed in at least 2 args, and they're valid (ie, not a null strings).
If not, tell REXX to raise FAILURE. NOTE: We allow the author of the REXX script to specify the flag
arg, or omit it. */
if ( numargs < 2 || !RXVALIDSTRING(args[0]) || !RXVALIDSTRING(args[1]) ) return INVALID_ROUTINE;
/* Did he specify the flag arg? (ie, is there a third arg, and is it a valid RXSTRING)? If not, just
proceed with a case-sensitive compare. */
if ( numargs > 2 && RXVALIDSTRING(args[2]) )
{
/* OK, let's examine that flag arg. If it's 'i', then we do a case-insensitive string compare.
Otherwise, assume case-sensitive compare. Note that we could specifically check for both
'i' and 's', and if neither, return INVALID_ROUTINE to let the REXX script know that there's a
problem with it's args. */
if (*args[2].strptr == 'i')
{
/* Compare str1 to str2 and grab the return value from strcmpi */
result = (LONG)strcmpi(args[0].strptr, args[1].strptr);
goto return_me;
}
}
/* Compare str1 to str2 and grab the return value from strcmp */
result = (LONG)strcmp(args[0].strptr, args[1].strptr);
return_me:
/* Now, let's return that 32-bit result from strcmp. Remember, we got to convert to an ascii string */
_ltoa(result, retstr->strptr, 10);
retstr->strlength = strlen(retstr->strptr);
/* Tell REXX that calling template was correct */
return VALID_ROUTINE;
}
/******************************** TestDummy8() *******************************
* Syntax: val = TestDummy8(anything)
*
* Params: any value
*
* RC Return: If successful, returns the value of the REXX variable TESTVAL.
* If an error, returns a null string.
*
* Purpose: Fetchs the current value of the REXX script's variable named TESTVAL, and
* returns its value. This demonstrates how to fetch the value of some variable in
* a REXX script.
*************************************************************************/
ULONG TestDummy8(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
{
/* Fetch the value of TESTVAL. Use the return RXSTRING buffer to hold the value. And that's a good
place for it, because we're going to return that value. */
if ( GetRexxVar("TESTVAL", retstr->strptr, retstr->strlength) )
{
/* Return a null string */
retstr->strlength = 0;
return VALID_ROUTINE;
}
/* Set the length for return */
retstr->strlength = strlen(retstr->strptr);
return VALID_ROUTINE;
}
/******************************** TestDummy9() *******************************
* Syntax: err = TestDummy9(anything)
*
* Params: any value
*
* RC Return: Returns a null string.
*
* Purpose: none
*************************************************************************/
ULONG TestDummy9(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
{
retstr->strlength=0;
return VALID_ROUTINE;
}
/******************************** TestDummy10() *******************************
* Syntax: err = TestDummy10(anything)
*
* Params: any value
*
* RC Return: Returns a null string.
*
* Purpose: none
*************************************************************************/
ULONG TestDummy10(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
{
retstr->strlength=0;
return VALID_ROUTINE;
}
/******************************** TestDummy11() *******************************
* Syntax: err = TestDummy11(anything)
*
* Params: any value
*
* RC Return: Returns a null string.
*
* Purpose: none
*************************************************************************/
ULONG TestDummy11(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
{
retstr->strlength=0;
return VALID_ROUTINE;
}
/******************************** TestDummy12() *******************************
* Syntax: err = TestDummy12(anything)
*
* Params: any value
*
* RC Return: Returns a null string.
*
* Purpose: none
*************************************************************************/
ULONG TestDummy12(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
{
retstr->strlength=0;
return VALID_ROUTINE;
}
/******************************** TestDummy13() *******************************
* Syntax: err = TestDummy13(anything)
*
* Params: any value
*
* RC Return: Returns a null string.
*
* Purpose: none
*************************************************************************/
ULONG TestDummy13(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
{
retstr->strlength=0;
return VALID_ROUTINE;
}
/******************************** TestDummy14() *******************************
* Syntax: err = TestDummy14(anything)
*
* Params: any value
*
* RC Return: Returns a null string.
*
* Purpose: none
*************************************************************************/
ULONG TestDummy14(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
{
retstr->strlength=0;
return VALID_ROUTINE;
}
/******************************** TestDummy15() *******************************
* Syntax: err = TestDummy15(anything)
*
* Params: any value
*
* RC Return: Returns a null string.
*
* Purpose: none
*************************************************************************/
ULONG TestDummy15(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
{
retstr->strlength=0;
return VALID_ROUTINE;
}
/******************************** TestDummy16() *******************************
* Syntax: err = TestDummy16(anything)
*
* Params: any value
*
* RC Return: Returns a null string.
*
* Purpose: none
*************************************************************************/
ULONG TestDummy16(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
{
retstr->strlength=0;
return VALID_ROUTINE;
}
/******************************** TestDummy17() *******************************
* Syntax: err = TestDummy17(anything)
*
* Params: any value
*
* RC Return: Returns a null string.
*
* Purpose: none
*************************************************************************/
ULONG TestDummy17(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
{
retstr->strlength=0;
return VALID_ROUTINE;
}
/******************************** TestDummy18() *******************************
* Syntax: err = TestDummy18(anything)
*
* Params: any value
*
* RC Return: Returns a null string.
*
* Purpose: none
*************************************************************************/
ULONG TestDummy18(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
{
retstr->strlength=0;
return VALID_ROUTINE;
}
/******************************** TestDummy19() *******************************
* Syntax: err = TestDummy19(anything)
*
* Params: any value
*
* RC Return: Returns a null string.
*
* Purpose: none
*************************************************************************/
ULONG TestDummy19(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
{
retstr->strlength=0;
return VALID_ROUTINE;
}
/******************************** TestDummy20() *******************************
* Syntax: err = TestDummy20(anything)
*
* Params: any value
*
* RC Return: Returns a null string.
*
* Purpose: none
*************************************************************************/
ULONG TestDummy20(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
{
retstr->strlength=0;
return VALID_ROUTINE;
}