home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 18 REXX / 18-REXX.zip / testrexx.zip / testrexx.c next >
Text File  |  1994-04-07  |  38KB  |  999 lines

  1. /* TESTREXX.C
  2. *
  3. * This is a template for a DLL written to add REXX External Functions to the REXX Interpreter (ie,
  4. * which any REXX program can then call just as if they were standard "built-in" REXX commands).
  5. * Here's how it works:
  6. *      The REXX interpreter is that portion of OS/2 that loads and executes REXX scripts (ie, programs).
  7. * This DLL registers, with the REXX interpreter, ascii strings that are the "names" of the
  8. * REXX-callable functions within it. (The DLL was compiled with these functions declared external
  9. * so that the REXX interpreter can resolve the actual addresses of the functions). The REXX
  10. * interpreter works as a "middle man" between the REXX script and this DLL. When a REXX script
  11. * makes a call to one of our function "names", the REXX interpreter grabs any args supplied by the
  12. * REXX script, packs them into an array of RXSTRING structures, and calls our appropriate DLL
  13. * function, passing on those args from the script.
  14. *     What's a RXSTRING structure? It's this thing that REXX uses to store a variable's value.
  15. * Remember that all variables in REXX are expressed as null-terminated strings, including numeric
  16. * variables. For example, if a variable has the value 129, REXX internally stores it as the
  17. * null-terminated string of "129". (Another way of looking at it in C is a UCHAR array containing
  18. * {0x31, 0x32, 0x39, 0x00} ). So, a RXSTRING has 2 fields. It has a pointer to the null-terminated
  19. * string that represents the variable's value. It has another field that is the length of that string in
  20. * bytes. That's how REXX variables are stored. So, sometimes our DLL needs to convert args
  21. * passed by the script to ULONG, LONG, USHORT, SHORT, UCHAR, or CHAR binary values (if those
  22. * args are supposed to be numeric values).
  23. *     REXX always passes 5 args to our REXX functions, with one of those args being a pointer to
  24. * that aforementioned array of RXSTRING structures, which itself contains any args supplied by the
  25. * actual REXX script. Another one of those 5 args is a ULONG that tells how many RXSTRINGs are
  26. * in that array of script-supplied args (ie, how many args the script passed us). Another one of the 5
  27. * args is a pointer to a RXSTRING structure into which our function is supposed to return a string to
  28. * the REXX script. That's how we implement a "return value" to the script, which of course,  the REXX
  29. * interpreter assigns to the script's RC variable. Since all variables in REXX are expressed as
  30. * null-terminated strings, including numeric variables, anything we return to a script must also be
  31. * expressed as a null-terminated string. For example, if we want to return a 0 for RC to a REXX script,
  32. * we'll stuff the string "0" into that return RXSTRING structure, and set its length to 1.
  33. *     If we want to return an "null" string (ie, "") to a REXX script, we set the return RXSTRING's
  34. * length to 0.    In that case, we don't even need to initialize the RXSTRING's strptr field. Returning
  35. * a null string is a handy way of indicating to the REXX script that a function failed to complete its
  36. * task. This is just a suggestion. That's often what I return for an error condition, even from
  37. * functions that would otherwise return non-nulled strings. It's easy to do.
  38. *    The actual return value of a DLL function is for the REXX interpreter itself. This should be 0
  39. * if we detected no syntax errors with the args supplied by the REXX script. It will be non-zero if we
  40. * want to tell the REXX interpreter to perhaps raise the ERROR or FAILURE flags. Remember that
  41. * this return has nothing to do with the return RXSTRING, the latter being for the REXX script.
  42. *
  43. * Essentially, this DLL is a template for developing your own DLLs. It has 20 functions in it, called
  44. * "TestDummy1", "TestDummy2", "TestDummy3", etc. These dummy functions do very little of
  45. * any importance. At best, they demonstrate simple concepts. You can simply replace the code for
  46. * one of these dummy functions with the code of one of your own functions. Then, do a string
  47. * search on this source, exchanging the name of that dummy function wherever it appears in the
  48. * source, to your desired name. Start with TestDummy1 for your first function, then TestDummy2 for
  49. * your second function. etc. Also, edit the file TESTREXX.DEF, and change the old TestDummy name
  50. * to your new function name. I recommend that you preface all of your DLL names with a certain string
  51. * of characters. For example, here I chose to begin all function names with the 4 characters "Test".
  52. * This helps you to identify calls to your DLL when you write a REXX script. Also, if you pick a unique
  53. * preface, then it helps to avoid name collisions with other REXX scripts. (ie, You don't want to have
  54. * identical function names with other DLL function libraries).
  55. *      Compile the DLL and test it with a REXX script. The REXX script should begin with the
  56. * following two lines:
  57. *
  58. * CALL RxFuncAdd 'TestLoadFuncs', 'TESTREXX', 'TestLoadFuncs'
  59. * CALL TestLoadFuncs
  60. *
  61. *      When you finally have all of the functions that you desire added to the DLL, (and tested/debugged),
  62. * delete all of the references to TestDummy functions.    ie, Delete the RexxFunctionHandler
  63. * declarations for them. Delete the references in FncTable (ie, delete any lines containing the
  64. * TestDummy name). And delete any TestDummy functions in the source. DON'T DELETE TestLoadFuncs
  65. * or TestDropFuncs -- these aren't dummy functions!
  66. *      You'll probably want to change the name of the DLL itself, from TESTREXX, to something else.
  67. * Edit the files TESTREXX.C, TESTREXX.MAK, TESTREXX.DEF, and any test scripts that you write,
  68. * changing any instance of TESTREXX to your new desired name.
  69. *      Also, you may wish to change the preface of TestLoadFuncs and TestDropFuncs from "Test" to
  70. * whatever preface you used for your other functions. Remember to change all references in this
  71. * source, your test script, and also TESTREXX.DEF.
  72. *      Finally, if none of your functions happen to use some of the support functions in this DLL, such as
  73. * AddRexxVar, then you can delete those support functions.
  74. *      Now, recompile your DLL into a finished product. Not too bad, eh?
  75. *
  76. *  NOTES: The DLL should be compiled with the multi-threaded C libs for multi-threaded versions of
  77. * strlen, strcpy, etc. The C/Set2 makefile specifies this.
  78. *    Furthermore, if you use static (ie, global) variables whose values are changed by any DLL
  79. * function (ie, the variable doesn't keep one value throughout its lifetime), then you should change
  80. * the TESTREXX.DEF file. Change the line:
  81. *
  82. * DATA SINGLE SHARED
  83. *
  84. * to the line
  85. *
  86. * DATA MULTIPLE NONSHARED
  87. *
  88. * This ensures that each REXX script gets its own copy of this DLL's data section. In my test version,
  89. * none of my global variables ever change value, so there is no need to have separate data sections
  90. * for each REXX script that uses this DLL. You can avoid needing multiple copies of data by only using
  91. * local variables in your DLL functions. Note that the return RXSTRING passed to a function always
  92. * points to a 256 byte buffer. I frequently use that as a convenient temp buffer or variables, up to the
  93. * point that I need to format the return string.
  94. *    This DLL doesn't make any calls to OS/2 functions. That doesn't mean that you couldn't call
  95. * DosOpen() for example.
  96. */
  97.  
  98.  
  99. /* Include files */
  100. #define  INCL_DOSFILEMGR
  101. #define  INCL_DOSMEMMGR
  102. #define  INCL_REXXSAA
  103. #define  INCL_RXSHV
  104. #define  _DLL
  105. #define  _MT
  106. #include <os2.h>
  107. #include <rexxsaa.h>
  108. #include <string.h>
  109.  
  110.  
  111.  
  112. /*********************************************************************
  113.  * Function Returns (to the REXX interpreter)
  114.  *********************************************************************/
  115. #define  INVALID_ROUTINE 40        /* Raise Rexx error */
  116. #define  VALID_ROUTINE      0           /* Successful completion */
  117.  
  118.  
  119.  
  120. /*********************************************************************
  121.  * Declare all exported functions as REXX functions (ie, a REXX app can call them)
  122.  *********************************************************************/
  123. RexxFunctionHandler TestDummy1;
  124. RexxFunctionHandler TestDummy2;
  125. RexxFunctionHandler TestDummy3;
  126. RexxFunctionHandler TestDummy4;
  127. RexxFunctionHandler TestDummy5;
  128. RexxFunctionHandler TestDummy6;
  129. RexxFunctionHandler TestDummy7;
  130. RexxFunctionHandler TestDummy8;
  131. RexxFunctionHandler TestDummy8;
  132. RexxFunctionHandler TestDummy10;
  133. RexxFunctionHandler TestDummy11;
  134. RexxFunctionHandler TestDummy12;
  135. RexxFunctionHandler TestDummy13;
  136. RexxFunctionHandler TestDummy14;
  137. RexxFunctionHandler TestDummy15;
  138. RexxFunctionHandler TestDummy16;
  139. RexxFunctionHandler TestDummy17;
  140. RexxFunctionHandler TestDummy18;
  141. RexxFunctionHandler TestDummy19;
  142. RexxFunctionHandler TestDummy20;
  143. RexxFunctionHandler TestLoadFuncs;
  144. RexxFunctionHandler TestDropFuncs;
  145.  
  146.  
  147.  
  148. /* NOTE: In the module definition (.def) file, I specify DATA to be SINGLE SHARED rather than MULTIPLE NONSHARED. This
  149.     is because, since I only have globals that are read-only, I don't need a separate copy of these for each process that
  150.     uses this DLL */
  151.  
  152.  
  153.  
  154. /******************************************************
  155.  * This is an array containing the names of all of the REXX callable
  156.  * functions in this DLL. This is used when we initially register each
  157.  * function with the REXX interpreter, and deregister upon closedown.
  158.  ******************************************************/
  159.  
  160. CHAR FncTable[] = { "TestLoadFuncs\0\
  161. TestDropFuncs\0\
  162. TestDummy1\0\
  163. TestDummy2\0\
  164. TestDummy3\0\
  165. TestDummy4\0\
  166. TestDummy5\0\
  167. TestDummy6\0\
  168. TestDummy7\0\
  169. TestDummy8\0\
  170. TestDummy9\0\
  171. TestDummy10\0\
  172. TestDummy11\0\
  173. TestDummy12\0\
  174. TestDummy13\0\
  175. TestDummy14\0\
  176. TestDummy15\0\
  177. TestDummy16\0\
  178. TestDummy17\0\
  179. TestDummy18\0\
  180. TestDummy19\0\
  181. TestDummy20\0\
  182. \0" };
  183.  
  184.  
  185.  
  186.  
  187. /**************************** TestLoadFuncs() *****************************
  188. * Syntax:     call TestLoadFuncs
  189. *
  190. * Params:   none
  191. *
  192. * RC Return: If success, a null string
  193. *
  194. * Purpose:  Registers all of the REXX functions in this DLL with the REXX Interpreter, so
  195. *           that a REXX program can call them. This function relieves a REXX script from
  196. *           having to individually RXFUNCADD for every function in this DLL that the REXX
  197. *           script wishes to call.
  198. *************************************************************************/
  199.  
  200. ULONG TestLoadFuncs(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
  201. {
  202.     UCHAR * ptr;
  203.  
  204.     /* Set RC="0" */
  205.     retstr->strlength = 0;
  206.  
  207.     /* Get first function name */
  208.     ptr = &FncTable[0];
  209.  
  210.     /* Register each one of the REXX functions in this DLL. Now, the REXX interpreter will let a REXX
  211.     program call them. */
  212.     while (*ptr)
  213.     {
  214.       RexxRegisterFunctionDll(ptr, "TESTREXX", ptr);
  215.       ptr = ptr+strlen(ptr)+1;
  216.     }
  217.  
  218.     /* Tell REXX that calling template was correct */
  219.     return VALID_ROUTINE;
  220. }
  221.  
  222.  
  223.  
  224.  
  225. /***************************** TestDropFuncs() ******************************
  226. * Syntax:     call TestDropFuncs
  227. *
  228. * Params:    none
  229. *
  230. * RC Return: If success, a null string
  231. *
  232. * Purpose:    Deregisters all of the REXX functions in this DLL.
  233. *************************************************************************/
  234.  
  235. ULONG TestDropFuncs(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
  236. {
  237.     UCHAR * ptr;
  238.  
  239.     /* Set RC="" */
  240.     retstr->strlength = 0;
  241.  
  242.     /* Get first function name */
  243.     ptr = &FncTable[0];
  244.  
  245.     /* Register each one of the REXX functions in this DLL. Now, the REXX interpreter will let a REXX
  246.     program call them. */
  247.     while (*ptr)
  248.     {
  249.       RexxDeregisterFunction(ptr);
  250.       ptr = ptr+strlen(ptr)+1;
  251.     }
  252.  
  253.     /* Tell REXX that calling template was correct */
  254.     return VALID_ROUTINE;
  255. }
  256.  
  257.  
  258.  
  259.  
  260. /* ************************** SetRexxVar() *******************************
  261.  * Called by various DLL routines to set the value of a REXX variable (and create it if it
  262.  * doesn't yet exist). This is just a support function. It is passed two strings; the name
  263.  * of the REXX variable (must be in upper case), and the value of that variable (expressed
  264.  * as a string). This returns 0 for success, 1 for error.
  265.  ******************************************************************** */
  266.  
  267. ULONG SetRexxVar(UCHAR *name, UCHAR *value)
  268. {
  269.     LONG rc;
  270.     SHVBLOCK block;
  271.  
  272.     /* The shvcode is set to RXSHV_SET, which means we're setting the variable, using its
  273.     name as-is. This means the name must be a valid REXX symbol, in upper case. */
  274.     block.shvcode = RXSHV_SET;
  275.  
  276.     /* Set the variable value */
  277.     MAKERXSTRING(block.shvvalue, value, strlen(value));
  278.  
  279.     /* Set the variable name */
  280.     block.shvname.strptr = name;
  281.     block.shvname.strlength = strlen(name);
  282.  
  283.     /* The shvnamelen and shvvaluelen duplicate the lengths of the variable name and its value
  284.     string for simple variable-setting operations such as what we are doing here. */
  285.     block.shvnamelen = block.shvname.strlength;
  286.     block.shvvaluelen = block.shvvalue.strlength;
  287.  
  288.     /* The shvnext ptr is 0 because we only are doing one request (ie, one variable). */
  289.     block.shvnext = 0;
  290.  
  291.     /* NOTE: The shvret byte is not set. It's an output value. */
  292.  
  293.     /* Call the REXX variable pool to set/create this REXX variable. We use an AND operation to
  294.     turn off the low-order bit in the return code, because that bit indicates "new variable
  295.     created", and we do not care about that. Other return values indicate an error. */
  296.     if( ((rc=RexxVariablePool(&block)) & 0xFFFFFFFE) ) return(1);
  297.  
  298.     return(0);
  299. }
  300.  
  301.  
  302.  
  303.  
  304. /* ************************** GetRexxVar() *******************************
  305.  * Called by various DLL routines to fetch the value of a REXX variable (and create it if it
  306.  * doesn't yet exist). This is just a support function. It is passed 3 args; a ptr to the name
  307.  * of the REXX variable (must be in upper case), a ptr to the buffer where that variable's
  308.  * value (expressed as a string) is returned, and the length of that buffer. This returns
  309.  * 0 for success, 1 for error.
  310.  ******************************************************************** */
  311.  
  312. ULONG GetRexxVar(UCHAR *name, UCHAR *buffer, ULONG buffersize)
  313. {
  314.     LONG rc;
  315.     SHVBLOCK block;
  316.  
  317.     /* The shvcode is set to RXSHV_FETCH, which means we're fetching the variable's value, using its
  318.     name as-is. This means the name must be a valid REXX symbol, in upper case. */
  319.     block.shvcode = RXSHV_FETCH;
  320.  
  321.     /* Set the buffer where REXX returns the variable value */
  322.     block.shvvalue.strptr = buffer;
  323.     block.shvvalue.strlength = buffersize;
  324.  
  325.     /* Set the variable name */
  326.     block.shvname.strptr = name;
  327.     block.shvname.strlength = strlen(name);
  328.  
  329.     /* The shvnamelen and shvvaluelen duplicate the lengths of the variable name and its value
  330.     string for simple variable-fetching operations such as what we are doing here. */
  331.     block.shvnamelen = block.shvname.strlength;
  332.     block.shvvaluelen = block.shvvalue.strlength;
  333.  
  334.     /* The shvnext ptr is 0 because we only are doing one request (ie, one variable). */
  335.     block.shvnext = 0;
  336.  
  337.     /* NOTE: The shvret byte is not set. It's an output value. */
  338.  
  339.     /* Call the REXX variable pool to fetch/create this REXX variable. We use an AND operation to
  340.     turn off the low-order bit in the return code, because that bit indicates "new variable
  341.     created", and we do not care about that. Other return values indicate an error. Of course,
  342.     if a variable doesn't yet exist, when created, it's value is its name. */
  343.     if( ((rc=RexxVariablePool(&block)) & 0xFFFFFFFE) ) return(1);
  344.  
  345.     /* REXX doesn't null-terminate the value in the buffer, so for the sake of C string compatibility
  346.     we'll null it out ourselves. */
  347.     *(block.shvvalue.strptr+block.shvvalue.strlength) = 0;
  348.  
  349.     return(0);
  350. }
  351.  
  352.  
  353.  
  354.  
  355. /****************************** TestDummy1() ******************************
  356. * Syntax:    something = TestDummy1(something)
  357. *
  358. * Params:  A rexx value. Remember, all REXX values are expressed as null-terminated strings.
  359. *
  360. * RC Return: If success, it simply returns that same value that the script passed.
  361. *
  362. * Purpose:  Returns the same value that the script passes. Completely worthless.
  363. *************************************************************************/
  364.  
  365. ULONG TestDummy1(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
  366. {
  367.      /* Check that the REXX script has passed in at least 1 arg, and it's valid (ie, not a null string).
  368.      If not, tell REXX to raise FAILURE */
  369.      if ( !numargs || !RXVALIDSTRING(args[0]) ) return INVALID_ROUTINE;
  370.  
  371.      /* OK, simply copy that one arg to the return RXSTRING */
  372.      strcpy(retstr->strptr, args[0].strptr);
  373.      retstr->strlength = args[0].strlength;
  374.  
  375.      /* Tell REXX that calling template was correct */
  376.      return VALID_ROUTINE;
  377. }
  378.  
  379.  
  380.  
  381.  
  382. /****************************** TestDummy2() ******************************
  383. * Syntax:    somenum = TestDummy2(somenum)
  384. *
  385. * Params:  A rexx numeric value. Remember, all REXX values are expressed as null-terminated
  386. *         strings; even numeric values.
  387. *
  388. * RC Return: If success, it simply returns that same value that the script passed.
  389. *            If an error, returns a null string.
  390. *
  391. * Purpose:  Checks that the passed value contains only numeric digits. Returns the same value
  392. *          if so, or a null string if not so.
  393. *************************************************************************/
  394.  
  395. ULONG TestDummy2(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
  396. {
  397.      CHAR * ptr;
  398.  
  399.      /* Check that the REXX script has passed in at least 1 arg, and it's valid (ie, not a null string).
  400.      If not, tell REXX to raise FAILURE */
  401.      if ( !numargs || !RXVALIDSTRING(args[0]) ) return INVALID_ROUTINE;
  402.  
  403.      /* Assume that we'll return a null string */
  404.      retstr->strlength = 0;
  405.  
  406.      /* Check all of the digits. Make sure that they're numeric */
  407.      ptr = args[0].strptr;
  408.      while (*ptr=='+' || *ptr=='-') ptr++;
  409.      while (*ptr)
  410.      {
  411.      if ( !isdigit(*(ptr)++) ) return VALID_ROUTINE;  /* oops! Not all numeric digits */
  412.      }
  413.  
  414.      /* OK, simply copy that one arg to the return RXSTRING */
  415.      strcpy(retstr->strptr, args[0].strptr);
  416.      retstr->strlength = args[0].strlength;
  417.  
  418.      /* Tell REXX that calling template was correct */
  419.      return VALID_ROUTINE;
  420. }
  421.  
  422.  
  423.  
  424.  
  425. /****************************** TestDummy3() ******************************
  426. * Syntax:    sum = TestDummy3(num1, num2)
  427. *
  428. * Params:  2 rexx numeric values.
  429. *
  430. * RC Return: If success, it returns the sum of the 2 values that the script passed.
  431. *            If an error, returns a null string.
  432. *
  433. * Purpose:  Checks that the 2 passed values contain only numeric digits. If so, the values
  434. *           are added, and the sum is returned.    If not numeric values, a null string is returned.
  435. *************************************************************************/
  436.  
  437. ULONG TestDummy3(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
  438. {
  439.      CHAR * ptr;
  440.      LONG val1, val2;
  441.  
  442.      /* Check that the REXX script has passed in at least 2 args, and they're valid (ie, not a null strings).
  443.      If not, tell REXX to raise FAILURE */
  444.      if ( numargs < 2 || !RXVALIDSTRING(args[0]) || !RXVALIDSTRING(args[1]) ) return INVALID_ROUTINE;
  445.  
  446.      /* Assume that we'll return a null string */
  447.      retstr->strlength = 0;
  448.  
  449.      /* Check all of the digits in the first arg. Make sure that they're numeric */
  450.      ptr = args[0].strptr;
  451.      while (*ptr=='+' || *ptr=='-') ptr++;
  452.      while (*ptr)
  453.      {
  454.      if ( !isdigit(*(ptr)++) ) return VALID_ROUTINE;  /* oops! Not all numeric digits */
  455.      }
  456.  
  457.      /* Check all of the digits in the second arg. Make sure that they're numeric */
  458.      ptr = args[1].strptr;
  459.      while (*ptr=='+' || *ptr=='-') ptr++;
  460.      while (*ptr)
  461.      {
  462.      if ( !isdigit(*(ptr)++) ) return VALID_ROUTINE;  /* oops! Not all numeric digits */
  463.      }
  464.  
  465.      /* OK, now in order to add these values, we've got to convert those ascii strings into 32-bit values */
  466.      val1 = atol(args[0].strptr);
  467.      val2 = atol(args[1].strptr);
  468.  
  469.      /* Add them */
  470.      val1 += val2;
  471.  
  472.      /* OK, now we have to express the 32-bit sum as an ascii string when we stuff it into the return RXSTRING.
  473.      Remember that REXX internally stores all values as ascii strings. */
  474.      _ltoa(val1, retstr->strptr, 10);
  475.      retstr->strlength = strlen(retstr->strptr);
  476.  
  477.      /* Tell REXX that calling template was correct */
  478.      return VALID_ROUTINE;
  479. }
  480.  
  481.  
  482.  
  483.  
  484.  
  485. /****************************** TestDummy4() ******************************
  486. * Syntax:    str = TestDummy4(str1, str2)
  487. *
  488. * Params:  2 rexx values.
  489. *
  490. * RC Return: If success, it returns str2 appended to str1.
  491. *            If an error, returns a null string.
  492. *
  493. * Purpose:  Returns str2 appended to str1.
  494. *************************************************************************/
  495.  
  496. ULONG TestDummy4(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
  497. {
  498.      ULONG len;
  499.      UCHAR * mem;
  500.  
  501.      /* Check that the REXX script has passed in at least 2 args, and they're valid (ie, not a null strings).
  502.      If not, tell REXX to raise FAILURE */
  503.      if ( numargs < 2 || !RXVALIDSTRING(args[0]) || !RXVALIDSTRING(args[1]) ) return INVALID_ROUTINE;
  504.  
  505.      /* We have to check whether the combined length of the 2 args doesn't exceed 255 characters, plus 1 for
  506.      a terminating null byte. Why? Because that's how big the return RXSTRING's strptr is. So what happens
  507.      if we need a bigger buffer? Well, we can allocate a new buffer with with DosAllocMem(), and put the pointer
  508.      in the return RXSTRING's strptr. REXX will return that memory on our behalf.
  509.      */
  510.      len = args[0].strlength + args[1].strlength + 1;
  511.      if (len > retstr->strlength)
  512.      {
  513.      if (DosAllocMem((PPVOID)&mem, len, PAG_COMMIT|PAG_WRITE|PAG_READ))
  514.      {
  515.            /* Return a null string */
  516.            retstr->strlength = 0;
  517.            return VALID_ROUTINE; /* oops. No memory. Return null string */
  518.      }
  519.      retstr->strptr=mem;
  520.      }
  521.  
  522.      /* Copy str1 to the return RXSTRING. Then append str2. Then set the return RXSTRING's length */
  523.      strcpy(retstr->strptr, args[0].strptr);
  524.      strcat(retstr->strptr, args[1].strptr);
  525.      retstr->strlength = strlen(retstr->strptr);
  526.  
  527.      /* Tell REXX that calling template was correct */
  528.      return VALID_ROUTINE;
  529. }
  530.  
  531.  
  532.  
  533.  
  534. /****************************** TestDummy5() ******************************
  535. * Syntax:    sum = TestDummy5(num1, num2)
  536. *
  537. * Params:  2 rexx numeric values.
  538. *
  539. * RC Return: If success, it returns the sum of the 2 values that the script passed. Also,
  540. *            it sets the REXX variable TESTSUM to that same value.
  541. *            If an error, returns a null string.
  542. *
  543. * Purpose:  Checks that the 2 passed values contain only numeric digits. If so, the values
  544. *           are added, and the sum is returned, as well as the REXX variable TESTSUM
  545. *           is set to that value.  If not numeric values, a null string is returned.
  546. *             This is the same thing as TestDummy3, except that it demonstrates how
  547. *           to use SetRexxVar() to directly create and set a variable in a REXX script.
  548. *************************************************************************/
  549.  
  550. ULONG TestDummy5(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
  551. {
  552.      CHAR * ptr;
  553.      LONG val1, val2;
  554.  
  555.      /* Check that the REXX script has passed in at least 2 args, and they're valid (ie, not a null strings).
  556.      If not, tell REXX to raise FAILURE */
  557.      if ( numargs < 2 || !RXVALIDSTRING(args[0]) || !RXVALIDSTRING(args[1]) ) return INVALID_ROUTINE;
  558.  
  559.      /* Assume that we'll return a null string */
  560.      retstr->strlength = 0;
  561.  
  562.      /* Check all of the digits in the first arg. Make sure that they're numeric */
  563.      ptr = args[0].strptr;
  564.      while (*ptr=='+' || *ptr=='-') ptr++;
  565.      while (*ptr)
  566.      {
  567.      if ( !isdigit(*(ptr)++) ) return VALID_ROUTINE;  /* oops! Not all numeric digits */
  568.      }
  569.  
  570.      /* Check all of the digits in the second arg. Make sure that they're numeric */
  571.      ptr = args[1].strptr;
  572.      while (*ptr=='+' || *ptr=='-') ptr++;
  573.      while (*ptr)
  574.      {
  575.      if ( !isdigit(*(ptr)++) ) return VALID_ROUTINE;  /* oops! Not all numeric digits */
  576.      }
  577.  
  578.      /* OK, now in order to add these values, we've got to convert those ascii strings into 32-bit values */
  579.      val1 = atol(args[0].strptr);
  580.      val2 = atol(args[1].strptr);
  581.  
  582.      /* Add them */
  583.      val1 += val2;
  584.  
  585.      /* OK, now we have to express the 32-bit sum as an ascii string when we stuff it into the return RXSTRING.
  586.      Remember that REXX internally stores all values as ascii strings. */
  587.      _ltoa(val1, retstr->strptr, 10);
  588.  
  589.      /* Let's set the REXX variable, TESTSUM, to that same value. If an error, get out of here, leaving the return
  590.      RXSTRING's strlength=0 (ie, a null string) */
  591.      if ( SetRexxVar("TESTSUM", retstr->strptr) ) return VALID_ROUTINE;
  592.  
  593.      /* OK, now that everything went well, let's set the length of the return RXSTRING. (ie, so that we're no
  594.      longer returning it as 0 length, which is a null string regardless of the contents of strptr) */
  595.      retstr->strlength = strlen(retstr->strptr);
  596.  
  597.      /* Tell REXX that calling template was correct */
  598.      return VALID_ROUTINE;
  599. }
  600.  
  601.  
  602.  
  603.  
  604. /****************************** TestDummy6() ******************************
  605. * Syntax:    sum = TestDummy6(num1, num2)
  606. *
  607. * Params:  2 rexx numeric values.
  608. *
  609. * RC Return: If success, it returns the sum of the 2 values that the script passed. Also,
  610. *            it sets the REXX stem variable TESTRESULT.0 to that same value, and the
  611. *            REXX stem variable TESTRESULT.1 to the multiplication of the 2 values.
  612. *            If an error, returns a null string.
  613. *
  614. * Purpose:  Checks that the 2 passed values contain only numeric digits. If so, the values
  615. *           are added, and the sum is returned, as well as the REXX variable TESTRESULT.0
  616. *           is set to that value, and TESTRESULT.1 set to the multiplication of the two
  617. *           values.  If not numeric values, a null string is returned.
  618. *             This is the same thing as TestDummy5, except that it demonstrates how
  619. *           to use SetRexxVar() to directly create and set stem variables in a REXX script.
  620. *           Stem variables simply have a "." in the name, and a number after.
  621. *************************************************************************/
  622.  
  623. ULONG TestDummy6(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
  624. {
  625.      CHAR * ptr;
  626.      LONG val1, val2, sum;
  627.  
  628.      /* Check that the REXX script has passed in at least 2 args, and they're valid (ie, not a null strings).
  629.      If not, tell REXX to raise FAILURE */
  630.      if ( numargs < 2 || !RXVALIDSTRING(args[0]) || !RXVALIDSTRING(args[1]) ) return INVALID_ROUTINE;
  631.  
  632.      /* Assume that we'll return a null string */
  633.      retstr->strlength = 0;
  634.  
  635.      /* Check all of the digits in the first arg. Make sure that they're numeric */
  636.      ptr = args[0].strptr;
  637.      while (*ptr=='+' || *ptr=='-') ptr++;
  638.      while (*ptr)
  639.      {
  640.      if ( !isdigit(*(ptr)++) ) return VALID_ROUTINE;  /* oops! Not all numeric digits */
  641.      }
  642.  
  643.      /* Check all of the digits in the second arg. Make sure that they're numeric */
  644.      ptr = args[1].strptr;
  645.      while (*ptr=='+' || *ptr=='-') ptr++;
  646.      while (*ptr)
  647.      {
  648.      if ( !isdigit(*(ptr)++) ) return VALID_ROUTINE;  /* oops! Not all numeric digits */
  649.      }
  650.  
  651.      /* OK, now in order to add these values, we've got to convert those ascii strings into 32-bit values */
  652.      val1 = atol(args[0].strptr);
  653.      val2 = atol(args[1].strptr);
  654.  
  655.      /* Multiply them */
  656.      sum = val1 * val2;
  657.  
  658.      /* Let's set the REXX variable, TESTRESULT.1 to that result value. If an error, get out of here, leaving the return
  659.      RXSTRING's strlength=0 (ie, a null string).  We have to express the 32-bit result as an ascii string when we
  660.      set TESTRESULT.1's value. Remember that REXX internally stores all values as ascii strings. I need a temp
  661.      buffer to store the ascii string. Hey, why not use the return RXSTRING's buffer? Yeah.
  662.      */
  663.      _ltoa(sum, retstr->strptr, 10);
  664.      if ( SetRexxVar("TESTRESULT.1", retstr->strptr) ) return VALID_ROUTINE;
  665.  
  666.      /* Add them */
  667.      val1 += val2;
  668.  
  669.      /* OK, now we have to express the 32-bit sum as an ascii string when we stuff it into the return RXSTRING.
  670.      Remember that REXX internally stores all values as ascii strings. */
  671.      _ltoa(val1, retstr->strptr, 10);
  672.  
  673.      /* Let's set the REXX variable, TESTRESULT.0, to that same value. If an error, get out of here, leaving the return
  674.      RXSTRING's strlength=0 (ie, a null string) */
  675.      if ( SetRexxVar("TESTRESULT.0", retstr->strptr) ) return VALID_ROUTINE;
  676.  
  677.      /* OK, now that everything went well, let's set the length of the return RXSTRING. (ie, so that we're no
  678.      longer returning it as 0 length, which is a null string regardless of the contents of strptr) */
  679.      retstr->strlength = strlen(retstr->strptr);
  680.  
  681.      /* Tell REXX that calling template was correct */
  682.      return VALID_ROUTINE;
  683. }
  684.  
  685.  
  686.  
  687.  
  688. /****************************** TestDummy7() ******************************
  689. * Syntax:    err = TestDummy7(str1, str2, flag)
  690. *
  691. * Params:  2 rexx strings to compare.
  692. *          A flag. If 's' then this means to do a case-sensitive compare of str1 and str2.
  693. *          If 'i' then this means to do a case-insensitive compare of str1 and str2. If the
  694. *          flag arg is not supplied, or is a null-string, then assume case-sensitive.
  695. *
  696. * RC Return: If str1 < str2, then a value less than 0 is returned.
  697. *            If str1 = str2, then a value of 0 is returned.
  698. *            If str1 > str2, then a value greater than 0 is returned.
  699. *
  700. * Purpose:  Compares str2 to str1 and results a result as above.
  701. *************************************************************************/
  702.  
  703. ULONG TestDummy7(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
  704. {
  705.      LONG result;
  706.  
  707.      /* Check that the REXX script has passed in at least 2 args, and they're valid (ie, not a null strings).
  708.      If not, tell REXX to raise FAILURE. NOTE: We allow the author of the REXX script to specify the flag
  709.      arg, or omit it. */
  710.      if ( numargs < 2 || !RXVALIDSTRING(args[0]) || !RXVALIDSTRING(args[1]) ) return INVALID_ROUTINE;
  711.  
  712.      /* Did he specify the flag arg? (ie, is there a third arg, and is it a valid RXSTRING)? If not, just
  713.      proceed with a case-sensitive compare. */
  714.      if ( numargs > 2 && RXVALIDSTRING(args[2]) )
  715.      {
  716.      /* OK, let's examine that flag arg. If it's 'i', then we do a case-insensitive string compare.
  717.          Otherwise, assume case-sensitive compare. Note that we could specifically check for both
  718.          'i' and 's', and if neither, return INVALID_ROUTINE to let the REXX script know that there's a
  719.          problem with it's args. */
  720.      if (*args[2].strptr == 'i')
  721.      {
  722.           /* Compare str1 to str2 and grab the return value from strcmpi */
  723.           result = (LONG)strcmpi(args[0].strptr, args[1].strptr);
  724.           goto return_me;
  725.      }
  726.      }
  727.  
  728.      /* Compare str1 to str2 and grab the return value from strcmp */
  729.      result = (LONG)strcmp(args[0].strptr, args[1].strptr);
  730.  
  731. return_me:
  732.      /* Now, let's return that 32-bit result from strcmp. Remember, we got to convert to an ascii string */
  733.      _ltoa(result, retstr->strptr, 10);
  734.      retstr->strlength = strlen(retstr->strptr);
  735.  
  736.      /* Tell REXX that calling template was correct */
  737.      return VALID_ROUTINE;
  738. }
  739.  
  740.  
  741.  
  742.  
  743. /******************************** TestDummy8() *******************************
  744. * Syntax:    val = TestDummy8(anything)
  745. *
  746. * Params:  any value
  747. *
  748. * RC Return: If successful, returns the value of the REXX variable TESTVAL.
  749. *            If an error, returns a null string.
  750. *
  751. * Purpose:  Fetchs the current value of the REXX script's variable named TESTVAL, and
  752. *           returns its value. This demonstrates how to fetch the value of some variable in
  753. *           a REXX script.
  754. *************************************************************************/
  755.  
  756. ULONG TestDummy8(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
  757. {
  758.      /* Fetch the value of TESTVAL. Use the return RXSTRING buffer to hold the value. And that's a good
  759.      place for it, because we're going to return that value. */
  760.      if ( GetRexxVar("TESTVAL", retstr->strptr, retstr->strlength) )
  761.      {
  762.       /* Return a null string */
  763.       retstr->strlength = 0;
  764.       return VALID_ROUTINE;
  765.      }
  766.  
  767.      /* Set the length for return */
  768.      retstr->strlength = strlen(retstr->strptr);
  769.  
  770.      return VALID_ROUTINE;
  771. }
  772.  
  773.  
  774.  
  775.  
  776. /******************************** TestDummy9() *******************************
  777. * Syntax:    err = TestDummy9(anything)
  778. *
  779. * Params:  any value
  780. *
  781. * RC Return: Returns a null string.
  782. *
  783. * Purpose:  none
  784. *************************************************************************/
  785.  
  786. ULONG TestDummy9(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
  787. {
  788.      retstr->strlength=0;
  789.      return VALID_ROUTINE;
  790. }
  791.  
  792.  
  793.  
  794. /******************************** TestDummy10() *******************************
  795. * Syntax:    err = TestDummy10(anything)
  796. *
  797. * Params:  any value
  798. *
  799. * RC Return: Returns a null string.
  800. *
  801. * Purpose:  none
  802. *************************************************************************/
  803.  
  804. ULONG TestDummy10(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
  805. {
  806.      retstr->strlength=0;
  807.      return VALID_ROUTINE;
  808. }
  809.  
  810.  
  811.  
  812.  
  813. /******************************** TestDummy11() *******************************
  814. * Syntax:    err = TestDummy11(anything)
  815. *
  816. * Params:  any value
  817. *
  818. * RC Return: Returns a null string.
  819. *
  820. * Purpose:  none
  821. *************************************************************************/
  822.  
  823. ULONG TestDummy11(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
  824. {
  825.      retstr->strlength=0;
  826.      return VALID_ROUTINE;
  827. }
  828.  
  829.  
  830.  
  831.  
  832. /******************************** TestDummy12() *******************************
  833. * Syntax:    err = TestDummy12(anything)
  834. *
  835. * Params:  any value
  836. *
  837. * RC Return: Returns a null string.
  838. *
  839. * Purpose:  none
  840. *************************************************************************/
  841.  
  842. ULONG TestDummy12(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
  843. {
  844.      retstr->strlength=0;
  845.      return VALID_ROUTINE;
  846. }
  847.  
  848.  
  849.  
  850.  
  851. /******************************** TestDummy13() *******************************
  852. * Syntax:    err = TestDummy13(anything)
  853. *
  854. * Params:  any value
  855. *
  856. * RC Return: Returns a null string.
  857. *
  858. * Purpose:  none
  859. *************************************************************************/
  860.  
  861. ULONG TestDummy13(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
  862. {
  863.      retstr->strlength=0;
  864.      return VALID_ROUTINE;
  865. }
  866.  
  867.  
  868.  
  869.  
  870. /******************************** TestDummy14() *******************************
  871. * Syntax:    err = TestDummy14(anything)
  872. *
  873. * Params:  any value
  874. *
  875. * RC Return: Returns a null string.
  876. *
  877. * Purpose:  none
  878. *************************************************************************/
  879.  
  880. ULONG TestDummy14(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
  881. {
  882.      retstr->strlength=0;
  883.      return VALID_ROUTINE;
  884. }
  885.  
  886.  
  887.  
  888.  
  889. /******************************** TestDummy15() *******************************
  890. * Syntax:    err = TestDummy15(anything)
  891. *
  892. * Params:  any value
  893. *
  894. * RC Return: Returns a null string.
  895. *
  896. * Purpose:  none
  897. *************************************************************************/
  898.  
  899. ULONG TestDummy15(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
  900. {
  901.      retstr->strlength=0;
  902.      return VALID_ROUTINE;
  903. }
  904.  
  905.  
  906.  
  907.  
  908. /******************************** TestDummy16() *******************************
  909. * Syntax:    err = TestDummy16(anything)
  910. *
  911. * Params:  any value
  912. *
  913. * RC Return: Returns a null string.
  914. *
  915. * Purpose:  none
  916. *************************************************************************/
  917.  
  918. ULONG TestDummy16(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
  919. {
  920.      retstr->strlength=0;
  921.      return VALID_ROUTINE;
  922. }
  923.  
  924.  
  925.  
  926.  
  927. /******************************** TestDummy17() *******************************
  928. * Syntax:    err = TestDummy17(anything)
  929. *
  930. * Params:  any value
  931. *
  932. * RC Return: Returns a null string.
  933. *
  934. * Purpose:  none
  935. *************************************************************************/
  936.  
  937. ULONG TestDummy17(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
  938. {
  939.      retstr->strlength=0;
  940.      return VALID_ROUTINE;
  941. }
  942.  
  943.  
  944.  
  945.  
  946. /******************************** TestDummy18() *******************************
  947. * Syntax:    err = TestDummy18(anything)
  948. *
  949. * Params:  any value
  950. *
  951. * RC Return: Returns a null string.
  952. *
  953. * Purpose:  none
  954. *************************************************************************/
  955.  
  956. ULONG TestDummy18(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
  957. {
  958.      retstr->strlength=0;
  959.      return VALID_ROUTINE;
  960. }
  961.  
  962.  
  963.  
  964.  
  965. /******************************** TestDummy19() *******************************
  966. * Syntax:    err = TestDummy19(anything)
  967. *
  968. * Params:  any value
  969. *
  970. * RC Return: Returns a null string.
  971. *
  972. * Purpose:  none
  973. *************************************************************************/
  974.  
  975. ULONG TestDummy19(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
  976. {
  977.      retstr->strlength=0;
  978.      return VALID_ROUTINE;
  979. }
  980.  
  981.  
  982.  
  983.  
  984. /******************************** TestDummy20() *******************************
  985. * Syntax:    err = TestDummy20(anything)
  986. *
  987. * Params:  any value
  988. *
  989. * RC Return: Returns a null string.
  990. *
  991. * Purpose:  none
  992. *************************************************************************/
  993.  
  994. ULONG TestDummy20(CHAR *name, ULONG numargs, RXSTRING args[], CHAR *queuename, RXSTRING *retstr)
  995. {
  996.      retstr->strlength=0;
  997.      return VALID_ROUTINE;
  998. }
  999.