Jump to content

REXX-ercising Your Applications - Part 2: Difference between revisions

From EDM2
mNo edit summary
Ak120 (talk | contribs)
 
(5 intermediate revisions by 2 users not shown)
Line 1: Line 1:
Written by [[Gordon Zeglinski]]
[[File:edm1i7-2.png|right]]
''Written by [[Gordon Zeglinski]]''


==Introduction==
==Introduction==
 
In [[REXX-ercising Your Applications - Part 1|part 1]], we seen how to extend REXX by creating external functions and how to execute REXX programs from within our applications. '''RexxStart()''' wasn't thoroughly explored in part 1, so in this issue we will examine two more permutations of '''RexxStart()'''. Also, in this issue, we will look at using external command handlers and using the REXX macrospace.
In part 1, we seen how to extend REXX by creating external functions and how to execute REXX programs from within our applications. RexxStart() wasn't thoroughly explored in part 1, so in this issue we will examine two more permutations of RexxStart(). Also, in this issue, we will look at using external command handlers and using the REXX macrospace.


Enough preamble, let's get to it!
Enough preamble, let's get to it!


==More on RexxStart()==
==More on RexxStart()==
Last time, we set the '''INSTORE''' parameter to '''NULL''', to execute REXX procedures from files. By permuting the '''INSTORE''' parameter, we can execute REXX procedure from memory or the macrospace.


Last time, we set the INSTORE parameter to NULL, to execute REXX procedures from files. By permuting the INSTORE parameter, we can execute REXX procedure from memory or the macrospace.
If you remember '''REXXSAMP.CPP''' from part 1, the variable '''Prog[]''' in the '''main()''' function, declares an in-memory version of this program. '''Prog[]''' is then assigned to the '''RXSTRING INSTORE[0]'''.
 
<code>
If you remember REXXSAMP.CPP from part 1, the variable Prog[] in the main() function, declares an in-memory version of this program. Prog[] is then assigned to the RXSTRING INSTORE[0].
 
<small><nowiki>
  #define INCL_RXFUNC      /* external function values */
  #define INCL_RXFUNC      /* external function values */
  #define INCL_VIO
  #define INCL_VIO
Line 22: Line 20:
  ULONG _System SysCls(UCHAR *name, ULONG numargs, RXSTRING args[],
  ULONG _System SysCls(UCHAR *name, ULONG numargs, RXSTRING args[],
                     PSZ *queuename, RXSTRING *retstr);
                     PSZ *queuename, RXSTRING *retstr);
   
   
  ULONG _System SysCls(UCHAR *name, ULONG numargs, RXSTRING args[],
  ULONG _System SysCls(UCHAR *name, ULONG numargs, RXSTRING args[],
Line 29: Line 26:
   BYTE bCell[2];
   BYTE bCell[2];
   
   
  // If arguments, return non-zero to indicate error
// If arguments, return non-zero to indicate error
   if (numargs)
   if (numargs)
     return 1;
     return 1;
Line 42: Line 39:
   retstr->strlength=1;
   retstr->strlength=1;
   strcpy(retstr->strptr,"0");
   strcpy(retstr->strptr,"0");
   
   
   return 0;
   return 0;
Line 75: Line 71:
         "say \"Screen cleared by sample external REXX function\"\r\n\x1A";
         "say \"Screen cleared by sample external REXX function\"\r\n\x1A";
   
   
   RexxRegisterFunctionExe((PSZ)"EDM_SysCls",(PFN)&SysCls);
   RexxRegisterFunctionExe((PSZ)"EDM_SysCls",(PFN) & amp.SysCls);
   
   
   INSTORE[0].strptr=Prog;
   INSTORE[0].strptr=Prog;
Line 84: Line 78:
   INSTORE[1].strlength=0;
   INSTORE[1].strlength=0;
   
   
   cout<<"Sample EDM/2 Rexx Demonstration Program Part 1"<<endl<<"by Gordon Zeglinski"<<endl;
   cout<<"Sample EDM/2 Rexx Demonstration Program Part 1"<<
      endl<<"by Gordon Zeglinski"<<endl;
   cout<<"Executing REXX programs via INSTORE parameter"<<endl;
   cout<<"Executing REXX programs via INSTORE parameter"<<endl;
   cout<<endl<<"INSTORE[1].strptr\t"<<(void*)INSTORE[1].strptr;
   cout<<endl<<"INSTORE[1].strptr\t"<<(void*)INSTORE[1].strptr;
   cout<<endl<<"INSTORE[1].strlength\t"<<
   cout<<endl<<"INSTORE[1].strlength\t"<<INSTORE[1].strlength<<endl;
      INSTORE[1].strlength<<endl;
   
   
   if(!strlen(Input))
   if(!strlen(Input))
Line 99: Line 88:
   cout<<"Executing Sample Program "<<endl;
   cout<<"Executing Sample Program "<<endl;
   cout<<"-----------------"<<endl;
   cout<<"-----------------"<<endl;
   
   
   retstr.strptr=new char [1024];
   retstr.strptr=new char [1024];
   retstr.strlength=1024;
   retstr.strlength=1024;
   
   
   return_code = RexxStart(0,             // No arguments
   return_code = RexxStart(0,       // No arguments
                         argv,           // dummy entry
                         argv,       // dummy entry
                         Input,           // File name
                         Input,     // File name
                         INSTORE,         // InStore
                         INSTORE,   // InStore
                         "CMD",           // use the "CMD" command processor
                         "CMD",     // use the "CMD" command processor
                         RXCOMMAND,       // execute as a command
                         RXCOMMAND, // execute as a command
                         NULL,           // No exit handlers
                         NULL,       // No exit handlers
                         &rc,       // return code from REXX routine
                         & amp.rc,     // return code from REXX routine
                         &retstr);   // return string from REXX routine
                         & amp.retstr); // return string from REXX routine
   
   
   delete [] retstr.strptr;
   delete [] retstr.strptr;
   
   
   cout<<"After Execution";
   cout<<"After Execution";
   cout<<endl<<"INSTORE[1].strptr\t"<<
   cout<<endl<<"INSTORE[1].strptr\t"<<(void*)INSTORE[1].strptr;
      (void*)INSTORE[1].strptr;
   cout<<endl<<"INSTORE[1].strlength\t"<<INSTORE[1].strlength<<endl;
   cout<<endl<<"INSTORE[1].strlength\t"<<
      INSTORE[1].strlength<<endl;
   
   
  return 0;
  return 0;
  }
  }
</nowiki></small>
</code>
''Figure 1. Using '''INSTORE''' to execute REXX procedures in memory''


<font size="2"> Figure 1. Using INSTORE to execute REXX procedures in memory </font>
The above program should have the same output as '''REXXSAMP.CMD'''. We haven't done anything too revolutionary yet, but it's a good start. Next we'll see how to set up '''INSTORE''' to execute macros.
 
<code>
The above program should have the same output as REXXSAMP.CMD. We haven't done anything too revolutionary yet, but it's a good start. Next we'll see how to set up INSTORE to execute macros.
 
<nowiki>
  #define INCL_RXFUNC      /* external function values */
  #define INCL_RXFUNC      /* external function values */
  #define INCL_RXMACRO
  #define INCL_RXMACRO
Line 142: Line 124:
  ULONG _System SysCls(UCHAR *name, ULONG numargs, RXSTRING args[],
  ULONG _System SysCls(UCHAR *name, ULONG numargs, RXSTRING args[],
                     PSZ *queuename, RXSTRING *retstr);
                     PSZ *queuename, RXSTRING *retstr);
   
   
  ULONG _System SysCls(UCHAR *name, ULONG numargs, RXSTRING args[],
  ULONG _System SysCls(UCHAR *name, ULONG numargs, RXSTRING args[],
Line 162: Line 143:
   retstr->strlength=1;
   retstr->strlength=1;
   strcpy(retstr->strptr,"0");
   strcpy(retstr->strptr,"0");
   
   
   return 0;
   return 0;
  }
  }
   
   
  int main(){
  int main(){
Line 173: Line 152:
   RXSTRING  argv[2];      /* program argument string    */
   RXSTRING  argv[2];      /* program argument string    */
   RXSTRING  retstr;      /* program return value      */
   RXSTRING  retstr;      /* program return value      */
   SHORT     rc;          /* converted return code      */
   SHORT     rc;          /* converted return code      */
   RXSTRING  INSTORE[2];
   RXSTRING  INSTORE[2];
   char NumArg;
   char NumArg;
   
   
   RexxRegisterFunctionExe((PSZ)"EDM_SysCls",(PFN) & SysCls);
   RexxRegisterFunctionExe((PSZ)"EDM_SysCls",(PFN) &SysCls);
   
   
   INSTORE[0].strptr=NULL;
   INSTORE[0].strptr=NULL;
Line 184: Line 163:
   INSTORE[1].strlength=0;
   INSTORE[1].strlength=0;
   
   
   cout<<"Sample EDM/2 Rexx Demonstration Program 2"<<endl<<"by Gordon Zeglinski"<<endl;
   cout<<"Sample EDM/2 Rexx Demonstration Program 2"<<
   cout<<"Type rexxsamp.cmd <ENTER> to execute supplied smaple"<<endl;
      endl<<"by Gordon Zeglinski"<<endl;
   cout<<"Type rexxsamp.cmd <ENTER>
      to execute supplied smaple"<<endl;
   cin>>Input;
   cin>>Input;
   
   
Line 210: Line 185:
   cout <<"Executing Macro"<<endl;
   cout <<"Executing Macro"<<endl;
   
   
   return_code = RexxStart(0,             // No arguments
   return_code = RexxStart(0,         // No arguments
                         argv,           // dummy entry
                         argv,         // dummy entry
                         "MACRO1",       // File name
                         "MACRO1",     // File name
                         INSTORE,         // NULL InStore
                         INSTORE,     // NULL InStore
                         "CMD",           // use the "CMD" command processor
                         "CMD",       // use the "CMD" command processor
                         RXCOMMAND,       // execute as a command
                         RXCOMMAND,   // execute as a command
                         NULL,           // No exit handlers
                         NULL,         // No exit handlers
                         & rc,         // return code from REXX routine
                         &rc,         // return code from REXX routine
                         & retstr);   // return string from REXX routine
                         &retstr);     // return string from REXX routine
   
   
   delete [] retstr.strptr;
   delete [] retstr.strptr;
Line 224: Line 199:
  return 0;
  return 0;
  }
  }
</nowiki>
</code>
 
''Figure 2. Using INSTORE to Execute Macros''
<font size="2">Figure 2. Using INSTORE to Execute Macros</font>
 
Now we have explore two new ways to use RexxStart(). In part 1 and part 2 of this article, we have now seen how to use RexxStart() to:


Now we have explore two new ways to use '''RexxStart()'''. In part 1 and part 2 of this article, we have now seen how to use '''RexxStart()''' to:
* execute REXX programs from a file
* execute REXX programs from a file
* execute REXX programs from memory
* execute REXX programs from memory
* execute REXX programs from the macrospace
* execute REXX programs from the macrospace
 
So what is this '''macrospace''' thing and what does '''RexxAddMacro()''' function do?
So what is this macrospace thing and what does RexxAddMacro() function do?


For the answer to these and other exciting questions, keep reading...
For the answer to these and other exciting questions, keep reading...


==The REXX Macrospace==
==The REXX Macrospace==
The macrospace is a storage area in memory where REXX can keep functions for rapid execution. The application can add, remove, query, and change the search order of macros using the functions:
The macrospace is a storage area in memory where REXX can keep functions for rapid execution. The application can add, remove, query, and change the search order of macros using the functions:
* (ULONG)RexxAddMacro(PSZ FunctionName,PSZ SourceFile,ULONG SearchOrder)
* (ULONG)RexxAddMacro(PSZ FunctionName,PSZ SourceFile,ULONG SearchOrder)
* (ULONG)RexxDropMacro(PSZ FunctionName)
* (ULONG)RexxDropMacro(PSZ FunctionName)
Line 247: Line 217:
* (ULONG)RexxReorderMacro(PSZ FunctionName,ULONG Position)
* (ULONG)RexxReorderMacro(PSZ FunctionName,ULONG Position)


For RexxAddMacro(), the following parameters are specified:
For '''RexxAddMacro()''', the following parameters are specified:
 
;FunctionName: This is the name of the function that is to be used in the RexxStart function call
; '''FunctionName'''
;SourceFile: This is the name of the file in which the macro's source code is stored.
: This is the name of the function that is to be used in the RexxStart function call
;SearchOrder: Either '''RXMACRO_SEARCH_BEFORE''' (search before external functions or source files) or '''RX_MACRO_SEARCH_AFTER''' (search after external functions or source files)
; '''SourceFile'''
: This is the name of the file in which the macro's source code is stored.
; '''SearchOrder'''
: Either RXMACRO_SEARCH_BEFORE (search before external functions or source files) or RX_MACRO_SEARCH_AFTER (search after external functions or source files)


Return Value:
Return Value:
;RXMACRO_OK:no error
;RXMACRO_NO_STORAGE:macrospace is full
;RXMACRO_SOURCE_NOT_FOUND:'''SourceFile''' is invalid
;RXMACRO_INVALID_POSITION:'''SearchOrder''' is invalid


For '''RexxDropMacro()''', the following parameters are specified:
RXMACRO_OK                no error
;FunctionName: This is the name of the function that was used in the '''RexxAddMacro()''' function call
RXMACRO_NO_STORAGE        macrospace is full
RXMACRO_SOURCE_NOT_FOUND  SourceFile is invalid
RXMACRO_INVALID_POSITION  SearchOrder is invalid
 
For RexxDropMacro(), the following parameters are specified:
 
; '''FunctionName'''
: This is the name of the function that was used in the RexxAddMacro() function call


Return Value:
Return Value:
;RXMACRO_OK:no error
;RXMACRO_NOT_FOUND:macro name is not registered


For '''RexxQueryMacro()''', the following parameters are specified:
RXMACRO_OK          no error
;FunctionName: This is the name of the function whose existence is being queried
RXMACRO_NOT_FOUND  macro name is not registered
;Position: If the macro is found, its search order is returned here.
 
For RexxQueryMacro(), the following parameters are specified:
 
; '''FunctionName'''
: This is the name of the function whose existence is being queried
; '''Position'''
: If the macro is found, its search order is returned here.


Return Value:
Return Value:
 
;RXMACRO_OK:no error
;RXMACRO_NOT_FOUND:macro name is not registered
RXMACRO_OK         no error
RXMACRO_NOT_FOUND   macro name is not registered


For RexxReorderMacro(), the following parameters are specified:
For RexxReorderMacro(), the following parameters are specified:
 
;FunctionName: This is the name of the function that is to be used in the '''RexxStart()''' function call
; '''FunctionName'''
;SearchOrder: either '''RXMACRO_SEARCH_BEFORE''' or '''RX_MACRO_SEARCH_AFTER'''
: This is the name of the function that is to be used in the RexxStart() function call
; '''SearchOrder'''
: either RXMACRO_SEARCH_BEFORE or RX_MACRO_SEARCH_AFTER


Return Value:
Return Value:
 
;RXMACRO_OK:no error
;RXMACRO_NOT_FOUND:macro name is not registered
RXMACRO_OK                 no error
;RXMACRO_INVALID_POSITION:'''SearchOrder''' is invalid
RXMACRO_NOT_FOUND         macro name is not registered
RXMACRO_INVALID_POSITION   SearchOrder is invalid


===Manipulating the MACRO Storage Area===
===Manipulating the MACRO Storage Area===
In addition to manipulating the functions within the storage area, REXX also allows us to manipulate the storage area itself. The storage area can be saved to disk, loaded from disk and erased using the functions:
In addition to manipulating the functions within the storage area, REXX also allows us to manipulate the storage area itself. The storage area can be saved to disk, loaded from disk and erased using the functions:
* (ULONG)RexxSaveMacroSpace(ULONG Num,PSZ* List,PSZ FileName)
* (ULONG)RexxSaveMacroSpace(ULONG Num,PSZ* List,PSZ FileName)
* (ULONG)RexxLoadMacroSpace(ULONG Num,PSZ* List,PSZ FileName)
* (ULONG)RexxLoadMacroSpace(ULONG Num,PSZ* List,PSZ FileName)
* (ULONG)RexxClearMacroSpace(VOID)
* (ULONG)RexxClearMacroSpace(VOID)


For RexxSaveMacroSpace(), the following parameters are specified:
For '''RexxSaveMacroSpace()''', the following parameters are specified:
 
;Num: The number of function names in List (if Num is 0, then save the whole macrospace)
; '''Num'''
;List: List of function names to be saved (if Num is 0, then this can be NULL)
: The number of function names in List (if Num is 0, then save the whole macrospace)
;FileName: The name of the file in which the macrospace will be saved.
; '''List'''
: List of function names to be saved (if Num is 0, then this can be NULL)
; '''FileName'''
: The name of the file in which the macrospace will be saved.


Return Value:
Return Value:
 
;RXMACRO_OK:no error
;RXMACRO_NOT_FOUND:a name in List is not registered
RXMACRO_OK                   no error
;RXMACRO_EXTENSION_REQUIRED:'''FileName''' must have an extension
RXMACRO_NOT_FOUND           a name in List is not registered
;RXMACRO_FILE_ERROR:An error occurred while accessing the file given by '''FileName'''
RXMACRO_EXTENSION_REQUIRED   FileName must have an extension
The parameters for '''RexxLoadMacroSpace()''' are the same as those for '''RexxSaveMacroSpace()'''
RXMACRO_FILE_ERROR           An error occurred while accessing
                              the file given by FileName
 
The parameters for RexxLoadMacroSpace() are the same as those for RexxSaveMacroSpace()


Return Value:
Return Value:
 
;RXMACRO_OK:no error
;RXMACRO_NO_STORAGE:macrospace ran out while loading in the saved file
RXMACRO_OK               no error
;RXMACRO_NOT_FOUND:a name in List is not in the saved file
RXMACRO_NO_STORAGE       macrospace ran out while loading in
;RXMACRO_ALREADY_EXISTS:a name in List is already in the macrospace
                          the saved file
;RXMACRO_FILE_ERROR:an error occurred while accessing the file given by '''FileName'''
RXMACRO_NOT_FOUND         a name in List is not in the saved file
;RXMACRO_SIGNATURE_ERROR:the file is not a valid saved macrospace file or it is corrupted.
RXMACRO_ALREADY_EXISTS   a name in List is already in the macrospace
The use of these three functions are illustrated in the file '''rexx23.cpp'''. The macrospace saved files are not portable across different versions of the REXX interpreter. In this case, the original source files would have to be read in using '''RexxAddMacro()'''.
RXMACRO_FILE_ERROR       an error occurred while accessing the
                          file given by FileName
RXMACRO_SIGNATURE_ERROR   the file is not a valid saved macrospace
                          file or it is corrupted.
 
The use of these three functions are illustrated in the file rexx23.cpp. The macrospace saved files are not portable across different versions of the REXX interpreter. In this case, the original source files would have to be read in using RexxAddMacro().


==External Subcommand Handlers==
==External Subcommand Handlers==
Unlike the macrospace functions, which is more for the user than the application developer, the external subcommand handler functions are used by the developer to extend the REXX language. The following functions are provided to the developer:
Unlike the macrospace functions, which is more for the user than the application developer, the external subcommand handler functions are used by the developer to extend the REXX language. The following functions are provided to the developer:
* (ULONG)RexxRegisterSubcomDll(PSZ Name,PSZ ModuleName,PSZ FunctionName,PUCHAR Data,ULONG Drop)
* (ULONG)RexxRegisterSubcomDll(PSZ Name,PSZ ModuleName,PSZ FunctionName,PUCHAR Data,ULONG Drop)
* (ULONG)RexxRegisterSubcomExe(PSZ Name,PFN FunctionAdr,PUCHAR Data)
* (ULONG)RexxRegisterSubcomExe(PSZ Name,PFN FunctionAdr,PUCHAR Data)
Line 354: Line 286:
* (ULONG)RexxQuerySubcom(PSZ Name,PSZ ModuleName,PUSHORT Flag,PUCHAR Data)
* (ULONG)RexxQuerySubcom(PSZ Name,PSZ ModuleName,PUSHORT Flag,PUCHAR Data)


For RexxRegisterSubcomDll(), the following parameters are specified:
For '''RexxRegisterSubcomDll()''', the following parameters are specified:
 
;Name: environment name
; '''Name'''
;ModuleName: name of the DLL
: environment name
;FunctionName: name of the function in the DLL
; '''ModuleName'''
;Data: 8 bytes of user data. (Can be '''NULL''')
: name of the DLL
;Drop: either '''RXSUBCOM_DROPPABLE''' (any process can deregister the handler) or '''RXSUBCOM_NONDROP''' (only the current process can deregister the handler)
; '''FunctionName'''
: name of the function in the DLL
; '''Data'''
: 8 bytes of user data. (Can be NULL)
; '''Drop'''
: either RXSUBCOM_DROPPABLE (any process can deregister the handler) or RXSUBCOM_NONDROP (only the current process can deregister the handler)


Return Value:
Return Value:
;RXSUBCOM_OK:no error
;RXSUBCOM_DUP:the environment name has already been defined (to address this handler, its library name must be specified)
;RXSUBCOM_NOEMEM:Not enough memory
;RXSUBCOM_BADTYPE:'''Drop''' is invalid


For '''RexxRegisterSubcomExe()''', the following parameters are specified:
RXSUBCOM_OK        no error
;Name: environment name
RXSUBCOM_DUP      the environment name has already been defined
;FunctionAdr: the address of the function
                    (to address this handler, its library name
;Data: 8 bytes of user data. (Can be NULL)
                    must be specified)
RXSUBCOM_NOEMEM    Not enough memory
RXSUBCOM_BADTYPE  Drop is invalid
 
For RexxRegisterSubcomExe(), the following parameters are specified:
 
; '''Name'''
: environment name
; '''FunctionAdr'''
: the address of the function
; '''Data'''
: 8 bytes of user data. (Can be NULL)


Return Value:
Return Value:
 
;RXSUBCOM_OK:no error
;RXSUBCOM_DUP:the environment name has already been defined
RXSUBCOM_OK       no error
;RXSUBCOM_NOTREG:not registered because of duplicate name
RXSUBCOM_DUP     the environment name has already been defined
;RXSUBCOM_NOEMEM:not enough memory
RXSUBCOM_NOTREG   not registered because of duplicate name
RXSUBCOM_NOEMEM   not enough memory


For RexxDeregisterSubcom(), the following parameters are specified:
For RexxDeregisterSubcom(), the following parameters are specified:
 
;Name: environment name
; '''Name'''
;ModuleName: name of the DLL in which the handler function resides
: environment name
; '''ModuleName'''
: name of the DLL in which the handler function resides


Return Value:
Return Value:
;RXSUBCOM_OK:no error
;RXSUBCOM_NOTREG:the environment name has not been registered
;RXSUBCOM_NOCANDROP:drop permission is not granted
;RXSUBCOM_BADTYPE:'''Subcom''' is invalid


For '''RexxQuerySubcom()''', the following parameters are specified:
RXSUBCOM_OK          no error
;Name: environment name
RXSUBCOM_NOTREG      the environment name has not been registered
;ModuleName: name of the DLL in which the handler function resides
RXSUBCOM_NOCANDROP  drop permission is not granted
;Flag: either '''RXSUBCOM_OK''' or '''RXSUBCOM_NOTREG''' depending upon whether '''Name''' is registered or not.
RXSUBCOM_BADTYPE    Subcom is invalid
;Data: The address to an 8 byte location in memory to receive the user data
 
For RexxQuerySubcom(), the following parameters are specified:
 
; '''Name'''
: environment name
; '''ModuleName'''
: name of the DLL in which the handler function resides
; '''Flag'''
: either RXSUBCOM_OK or RXSUBCOM_NOTREG depending upon whether Name is registered or not.
; '''Data'''
: The address to an 8 byte location in memory to receive the user data


Return Value:
Return Value:
 
;RXSUBCOM_OK:no error
;RXSUBCOM_NOTREG:the environment name has not been registered
RXSUBCOM_OK       no error
;RXSUBCOM_BADTYPE
RXSUBCOM_NOTREG   the environment name has not been registered
RXSUBCOM_BADTYPE


===Creating External Subcommand Handler Functions===
===Creating External Subcommand Handler Functions===
External command handlers must use the following format:
External command handlers must use the following format:
  ULONG _System command_handler(PRXSTRING Command,PUSHORT Flag, PRXSTRING Retstr)
  ULONG _System command_handler(PRXSTRING Command,PUSHORT Flag, PRXSTRING Retstr)


The arguments have the following meaning:
The arguments have the following meaning:
;Command: a null-terminated '''RXSTRING''' containing the issued command
;Flag: the subroutine uses this flag to return the completion status of the command. It must be either RXSUBCOM_OK, RXSUBCOM_ERROR, or RXSUBCOM_FAILURE
;Retstr: Address of an '''RXSTRING''' for the return code. '''Retstr''' is a character string return code that will be assigned to the REXX special variable RC when the subcommand handler returns. By default, Retstr points to a 256-byte '''RXSTRING'''. If necessary, a longer RXSTRING can allocated with DosAllocMem(). If the subcommand handler sets Retval to an empty '''RXSTRING''', REXX will assign the string "0" to RC.


; '''Command'''
Flag Interpretation
: a null-terminated RXSTRING containing the issued command
;RXSUBCOM_OK: Indicates that the command was successfully executed.
; '''Flag'''
;RXSUBCOM_ERROR: Indicates that the command is recognized by the handler but a syntax error occurred (incorrect parameters for instance). The REXX interpreter will raise this condition if SIGNAL ON ERROR or CALL ON ERROR traps have been created. If TRACE ERRORS has been issued, REXX will trace the command when the subcommand handler returns.
: the subroutine uses this flag to return the completion status of the command. It must be either RXSUBCOM_OK, RXSUBCOM_ERROR, or RXSUBCOM_FAILURE
;RXSUBCOM_FAILURE: Indicates that a subcommand failure has occurred. Usually this is used if Command is unrecognized. The REXX interpreter will raise this condition if '''SIGNAL ON FAILURE''' or '''CALL ON FAILURE''' traps have been created. If TRACE FAILURES has been issued, REXX will trace the command when the subcommand handler returns.
; '''Retstr'''
: Address of an RXSTRING for the return code. Retstr is a character string return code that will be assigned to the REXX special variable RC when the subcommand handler returns. By default, Retstr points to a 256-byte RXSTRING. If necessary, a longer RXSTRING can allocated with DosAllocMem(). If the subcommand handler sets Retval to an empty RXSTRING, REXX will assign the string "0" to RC.
 
===Flag Interpretation===
 
; '''RXSUBCOM_OK'''
: Indicates that the command was successfully executed.
; '''RXSUBCOM_ERROR'''
: Indicates that the command is recognized by the handler but a syntax error occurred (incorrect parameters for instance). The REXX interpreter will raise this condition if SIGNAL ON ERROR or CALL ON ERROR traps have been created. If TRACE ERRORS has been issued, REXX will trace the command when the subcommand handler returns.
; '''RXSUBCOM_FAILURE'''
: Incidactes that a subcommand failure has occurred. Usually this is used if Command is unrecognized. The REXX interpreter will raise this condition if SIGNAL ON FAILURE or CALL ON FAILURE traps have been created. If TRACE FAILURES has been issued, REXX will trace the command when the subcommand handler returns.
 
If the command has parameters, it is up to the subcommand handler to parse the command for the arguments. The following simple subcommand handler processes the command "CLS".
If the command has parameters, it is up to the subcommand handler to parse the command for the arguments. The following simple subcommand handler processes the command "CLS".
 
<code>
  ULONG _System EDM_Handler(PRXSTRING Command,PUSHORT Flags,PRXSTRING retstr){
  ULONG _System EDM_Handler(PRXSTRING Command,PUSHORT Flags,PRXSTRING
retstr){
     BYTE bCell[2];
     BYTE bCell[2];
   
   
Line 477: Line 366:
     return 1;
     return 1;
  }
  }
</code>


==Putting It All Together==
==Putting It All Together==
 
The file '''rexx24.cpp''' contains a sample program demonstrating the use of external subcommand handlers, external functions, and executing in memory REXX programs. It is rather long, and very similar to the other sample REXX programs that are included in this article, so it won't be included here. However, let's look at two code snippets that illustrate new points.
The file rexx24.cpp contains a sample program demonstrating the use of external subcommand handlers, external functions, and executing in memory REXX programs. It is rather long, and very similar to the other sample REXX programs that are included in this article, so it won't be included here. However, let's look at two code snippets that illustrate new points.


The external subcommand handler is registered by the function call:
The external subcommand handler is registered by the function call:
 
  RexxRegisterSubcomExe((PSZ)"EDM",(PFN)&EDM_Handler,NULL);
The external subcommand handler we have seen in the previous section is registered under the name "EDM". To use this handler, our '''RexxStart()''' must be modified to specify the "EDM" environment. The following section of code illustrates this:
  RexxRegisterSubcomExe((PSZ)"EDM",(PFN)& EDM_Handler,NULL);
<code>
 
  return_code = RexxStart(0,           // No arguments
The external subcommand handler we have seen in the previous section is registered under the name "EDM". To use this handler, our RexxStart() must be modified to specify the "EDM" environment. The following section of code illustrates this:
                         argv,       // dummy entry
 
                         "Sample",   // procedure name
   
                         INSTORE,     // InStore
  return_code = RexxStart(0,             // No arguments
                         "EDM",       // use the "EDM" command processor
                         argv,           // dummy entry
                         RXCOMMAND,   // execute as a command
                         "Sample",       // procedure name
                         NULL,       // No exit handlers
                         INSTORE,         // InStore
                         &rc,        // return code from REXX routine
                         "EDM",           // use the "EDM" command processor
                         &retstr);    // return string from REXX routine
                         RXCOMMAND,       // execute as a command
</code>
                         NULL,           // No exit handlers
                         & rc,        // return code from REXX routine
                         & retstr);    // return string from REXX routine


===Functions, Macros, and Commands===
===Functions, Macros, and Commands===
We have now examined a good portion of the features REXX provides developers. A few questions now arise like: what's the difference between commands and functions, and where do macros fit into this?
We have now examined a good portion of the features REXX provides developers. A few questions now arise like: what's the difference between commands and functions, and where do macros fit into this?


Let's start by looking at the difference between functions and commands. Functions require that the user (the person using your application/library) to use a bulky syntax. For example, to call a function that clears the screen, let's call it "CLS", the user would have to use the either of the following syntaxes.
Let's start by looking at the difference between functions and commands. Functions require that the user (the person using your application/library) to use a bulky syntax. For example, to call a function that clears the screen, let's call it "CLS", the user would have to use the either of the following syntaxes.
  call CLS
  call CLS
or
or
 
  dummy=CLS()
  <small>dummy=CLS()</small>
In the first case, '''CLS()''' returns a result in the special REXX variable '''RESULT'''. In the second case, the variable dummy is assigned the return value from CLS(). Both expressions are rather bulky considering that '''CLS()''' takes no arguments, and really doesn't have any return values other than '''TRUE''' or '''FALSE'''. If developing a REXX programming environment, '''CLS''' would be a prime candidate for command status, requiring only the following statement to execute:
 
  CLS
In the first case, CLS() returns a result in the special REXX variable RESULT. In the second case, the variable dummy is assigned the return value from CLS(). Both expressions are rather bulky considering that CLS() takes no arguments, and really doesn't have any return values other than TRUE or FALSE If developing a REXX programming environment, CLS would be a prime candidate for command status, requiring only the following statement to execute:
The return value for the command would be placed in the special REXX variable '''RC'''. As previously mentioned, subcommand handler must parse their own commands. So if arguments are needed, it is less work for the programmer to use functions.
 
  <small>CLS</small>
 
The return value for the command would be placed in the special REXX variable RC. As previously mentioned, subcommand handler must parse their own commands. So if arguments are needed, it is less work for the programmer to use functions.


As mentioned before, macros really aren't there for the programmer, they are for the user. REXX provides several functions to allow the programmer to manipulate the macrospace. Macros are written in REXX, while external functions and subcommand handlers are written in C/C++, or some other compiled language.
As mentioned before, macros really aren't there for the programmer, they are for the user. REXX provides several functions to allow the programmer to manipulate the macrospace. Macros are written in REXX, while external functions and subcommand handlers are written in C/C++, or some other compiled language.


==Summary==
==Summary==
This concludes our look at Rexx/2 for this issue. Building upon the REXX information in part 1, we have covered new uses for RexxStart(), external subcommand handlers, and the REXX macrospace interface. We have not looked at exit handlers or the various REXX interfaces for interactive debugging.
This concludes our look at Rexx/2 for this issue. Building upon the REXX information in part 1, we have covered new uses for RexxStart(), external subcommand handlers, and the REXX macrospace interface. We have not looked at exit handlers or the various REXX interfaces for interactive debugging.


As usual, question and comments are welcome.
As usual, question and comments are welcome.


[[Category:Scripting Articles]]
[[Category:REXX Articles]]

Latest revision as of 17:46, 27 April 2024

Written by Gordon Zeglinski

Introduction

In part 1, we seen how to extend REXX by creating external functions and how to execute REXX programs from within our applications. RexxStart() wasn't thoroughly explored in part 1, so in this issue we will examine two more permutations of RexxStart(). Also, in this issue, we will look at using external command handlers and using the REXX macrospace.

Enough preamble, let's get to it!

More on RexxStart()

Last time, we set the INSTORE parameter to NULL, to execute REXX procedures from files. By permuting the INSTORE parameter, we can execute REXX procedure from memory or the macrospace.

If you remember REXXSAMP.CPP from part 1, the variable Prog[] in the main() function, declares an in-memory version of this program. Prog[] is then assigned to the RXSTRING INSTORE[0].

#define INCL_RXFUNC       /* external function values */
#define INCL_VIO
#include <rexxsaa.h>
#include <iostream.h>
#include <string.h>

ULONG _System SysCls(UCHAR *name, ULONG numargs, RXSTRING args[],
                    PSZ *queuename, RXSTRING *retstr);

ULONG _System SysCls(UCHAR *name, ULONG numargs, RXSTRING args[],
                    PSZ *queuename, RXSTRING *retstr)
{
  BYTE bCell[2];

// If arguments, return non-zero to indicate error
  if (numargs)
    return 1;

  bCell[0] = 0x20;                     /* Space Character            */
  bCell[1] = 0x07;                     /* Default Attrib             */
  VioScrollDn( 0, 0, (USHORT)0xFFFF, (USHORT)0XFFFF,
                     (USHORT)0xFFFF, bCell, (HVIO) 0);/* CLS         */
  VioSetCurPos(0, 0, (HVIO) 0);        /*                Pos cursor  */

//return "0" to the caller
  retstr->strlength=1;
  strcpy(retstr->strptr,"0");

  return 0;
}

int main(){
  char Input[200];
  LONG      return_code;  /* interpreter return code    */
  RXSTRING  argv[1];      /* program argument string    */
  RXSTRING  retstr;       /* program return value       */
  SHORT     rc;           /* converted return code      */
  RXSTRING  INSTORE[2];
  char      Prog[]=
       "/* RexxSamp.cmd   */\r\n"
       "/* Sample EDM/2 REXX program by Gordon Zeglinski */\r\n"
       "\r\n"
       "Call RxFuncAdd \'SysSleep\', \'REXXUTIL\', \'SysSleep\'\r\n"
       "\r\n"
       "say \"Sample EDM/2 REXX program started\"\r\n"
       "\r\n"
       "/*execute a cmd based command */\r\n"
       "\'dir /w\'\r\n"
       "\r\n"
       "/* wait 5 seconds */\r\n"
       "Call SysSleep 5\r\n"
       "\r\n"
       "/*clear screen using sample function */\r\n"
       "call EDM_SysCls\r\n"
       "\r\n"
       "say\r\n"
       "say\r\n"
       "say \"Screen cleared by sample external REXX function\"\r\n\x1A";

  RexxRegisterFunctionExe((PSZ)"EDM_SysCls",(PFN)&SysCls);

  INSTORE[0].strptr=Prog;
  INSTORE[0].strlength=strlen(Prog);
  INSTORE[1].strptr=NULL;
  INSTORE[1].strlength=0;

  cout<<"Sample EDM/2 Rexx Demonstration Program Part 1"<<endl<<"by Gordon Zeglinski"<<endl;
  cout<<"Executing REXX programs via INSTORE parameter"<<endl;
  cout<<endl<<"INSTORE[1].strptr\t"<<(void*)INSTORE[1].strptr;
  cout<<endl<<"INSTORE[1].strlength\t"<<INSTORE[1].strlength<<endl;

  if(!strlen(Input))
     strcpy(Input,"RexxSamp.cmd");

  cout<<"Executing Sample Program "<<endl;
  cout<<"-----------------"<<endl;

  retstr.strptr=new char [1024];
  retstr.strlength=1024;

  return_code = RexxStart(0,        // No arguments
                        argv,       // dummy entry
                        Input,      // File name
                        INSTORE,    // InStore
                        "CMD",      // use the "CMD" command processor
                        RXCOMMAND,  // execute as a command
                        NULL,       // No exit handlers
                        &rc,        // return code from REXX routine
                        &retstr);   // return string from REXX routine

  delete [] retstr.strptr;

  cout<<"After Execution";
  cout<<endl<<"INSTORE[1].strptr\t"<<(void*)INSTORE[1].strptr;
  cout<<endl<<"INSTORE[1].strlength\t"<<INSTORE[1].strlength<<endl;

return 0;
}

Figure 1. Using INSTORE to execute REXX procedures in memory

The above program should have the same output as REXXSAMP.CMD. We haven't done anything too revolutionary yet, but it's a good start. Next we'll see how to set up INSTORE to execute macros.

#define INCL_RXFUNC       /* external function values */
#define INCL_RXMACRO
#define INCL_VIO
#include <rexxsaa.h>
#include <iostream.h>
#include <string.h>

ULONG _System SysCls(UCHAR *name, ULONG numargs, RXSTRING args[],
                    PSZ *queuename, RXSTRING *retstr);

ULONG _System SysCls(UCHAR *name, ULONG numargs, RXSTRING args[],
                    PSZ *queuename, RXSTRING *retstr)
{
  BYTE bCell[2];

// If arguments, return non-zero to indicate error
  if (numargs)
    return 1;

  bCell[0] = 0x20;                     /* Space Character            */
  bCell[1] = 0x07;                     /* Default Attrib             */
  VioScrollDn( 0, 0, (USHORT)0xFFFF, (USHORT)0XFFFF,
                     (USHORT)0xFFFF, bCell, (HVIO) 0);/* CLS         */
  VioSetCurPos(0, 0, (HVIO) 0);        /*                Pos cursor  */

//return "0" to the caller
  retstr->strlength=1;
  strcpy(retstr->strptr,"0");

  return 0;
}

int main(){
  char Input[200];
  LONG      return_code;  /* interpreter return code    */
  RXSTRING  argv[2];      /* program argument string    */
  RXSTRING  retstr;       /* program return value       */
  SHORT     rc;           /* converted return code      */
  RXSTRING  INSTORE[2];
  char NumArg;

  RexxRegisterFunctionExe((PSZ)"EDM_SysCls",(PFN) &SysCls);

  INSTORE[0].strptr=NULL;
  INSTORE[0].strlength=0;
  INSTORE[1].strptr=NULL;
  INSTORE[1].strlength=0;

  cout<<"Sample EDM/2 Rexx Demonstration Program 2"<<endl<<"by Gordon Zeglinski"<<endl;
  cout<<"Type rexxsamp.cmd <ENTER> to execute supplied smaple"<<endl;
  cin>>Input;

  if(!strlen(Input))
     strcpy(Input,"RexxSamp.cmd");

  cout<<"Executing Sample Program "<<Input<<endl;
  cout<<"-----------------"<<endl;

  retstr.strptr=new char [1024];
  retstr.strlength=1024;

  argv[0].strptr="";
  argv[0].strlength=0;

  cout <<"Storing Macro"<<endl;

  return_code =RexxAddMacro("MACRO1", Input, RXMACRO_SEARCH_BEFORE);

  cout <<"Executing Macro"<<endl;

  return_code = RexxStart(0,          // No arguments
                        argv,         // dummy entry
                        "MACRO1",     // File name
                        INSTORE,      // NULL InStore
                        "CMD",        // use the "CMD" command processor
                        RXCOMMAND,    // execute as a command
                        NULL,         // No exit handlers
                        &rc,          // return code from REXX routine
                        &retstr);     // return string from REXX routine

  delete [] retstr.strptr;

return 0;
}

Figure 2. Using INSTORE to Execute Macros

Now we have explore two new ways to use RexxStart(). In part 1 and part 2 of this article, we have now seen how to use RexxStart() to:

  • execute REXX programs from a file
  • execute REXX programs from memory
  • execute REXX programs from the macrospace

So what is this macrospace thing and what does RexxAddMacro() function do?

For the answer to these and other exciting questions, keep reading...

The REXX Macrospace

The macrospace is a storage area in memory where REXX can keep functions for rapid execution. The application can add, remove, query, and change the search order of macros using the functions:

  • (ULONG)RexxAddMacro(PSZ FunctionName,PSZ SourceFile,ULONG SearchOrder)
  • (ULONG)RexxDropMacro(PSZ FunctionName)
  • (ULONG)RexxQueryMacro(PSZ FunctionName,PUSHORT Position)
  • (ULONG)RexxReorderMacro(PSZ FunctionName,ULONG Position)

For RexxAddMacro(), the following parameters are specified:

FunctionName
This is the name of the function that is to be used in the RexxStart function call
SourceFile
This is the name of the file in which the macro's source code is stored.
SearchOrder
Either RXMACRO_SEARCH_BEFORE (search before external functions or source files) or RX_MACRO_SEARCH_AFTER (search after external functions or source files)

Return Value:

RXMACRO_OK
no error
RXMACRO_NO_STORAGE
macrospace is full
RXMACRO_SOURCE_NOT_FOUND
SourceFile is invalid
RXMACRO_INVALID_POSITION
SearchOrder is invalid

For RexxDropMacro(), the following parameters are specified:

FunctionName
This is the name of the function that was used in the RexxAddMacro() function call

Return Value:

RXMACRO_OK
no error
RXMACRO_NOT_FOUND
macro name is not registered

For RexxQueryMacro(), the following parameters are specified:

FunctionName
This is the name of the function whose existence is being queried
Position
If the macro is found, its search order is returned here.

Return Value:

RXMACRO_OK
no error
RXMACRO_NOT_FOUND
macro name is not registered

For RexxReorderMacro(), the following parameters are specified:

FunctionName
This is the name of the function that is to be used in the RexxStart() function call
SearchOrder
either RXMACRO_SEARCH_BEFORE or RX_MACRO_SEARCH_AFTER

Return Value:

RXMACRO_OK
no error
RXMACRO_NOT_FOUND
macro name is not registered
RXMACRO_INVALID_POSITION
SearchOrder is invalid

Manipulating the MACRO Storage Area

In addition to manipulating the functions within the storage area, REXX also allows us to manipulate the storage area itself. The storage area can be saved to disk, loaded from disk and erased using the functions:

  • (ULONG)RexxSaveMacroSpace(ULONG Num,PSZ* List,PSZ FileName)
  • (ULONG)RexxLoadMacroSpace(ULONG Num,PSZ* List,PSZ FileName)
  • (ULONG)RexxClearMacroSpace(VOID)

For RexxSaveMacroSpace(), the following parameters are specified:

Num
The number of function names in List (if Num is 0, then save the whole macrospace)
List
List of function names to be saved (if Num is 0, then this can be NULL)
FileName
The name of the file in which the macrospace will be saved.

Return Value:

RXMACRO_OK
no error
RXMACRO_NOT_FOUND
a name in List is not registered
RXMACRO_EXTENSION_REQUIRED
FileName must have an extension
RXMACRO_FILE_ERROR
An error occurred while accessing the file given by FileName

The parameters for RexxLoadMacroSpace() are the same as those for RexxSaveMacroSpace()

Return Value:

RXMACRO_OK
no error
RXMACRO_NO_STORAGE
macrospace ran out while loading in the saved file
RXMACRO_NOT_FOUND
a name in List is not in the saved file
RXMACRO_ALREADY_EXISTS
a name in List is already in the macrospace
RXMACRO_FILE_ERROR
an error occurred while accessing the file given by FileName
RXMACRO_SIGNATURE_ERROR
the file is not a valid saved macrospace file or it is corrupted.

The use of these three functions are illustrated in the file rexx23.cpp. The macrospace saved files are not portable across different versions of the REXX interpreter. In this case, the original source files would have to be read in using RexxAddMacro().

External Subcommand Handlers

Unlike the macrospace functions, which is more for the user than the application developer, the external subcommand handler functions are used by the developer to extend the REXX language. The following functions are provided to the developer:

  • (ULONG)RexxRegisterSubcomDll(PSZ Name,PSZ ModuleName,PSZ FunctionName,PUCHAR Data,ULONG Drop)
  • (ULONG)RexxRegisterSubcomExe(PSZ Name,PFN FunctionAdr,PUCHAR Data)
  • (ULONG)RexxDeregisterSubcom(PSZ Name,PSZ ModuleName)
  • (ULONG)RexxQuerySubcom(PSZ Name,PSZ ModuleName,PUSHORT Flag,PUCHAR Data)

For RexxRegisterSubcomDll(), the following parameters are specified:

Name
environment name
ModuleName
name of the DLL
FunctionName
name of the function in the DLL
Data
8 bytes of user data. (Can be NULL)
Drop
either RXSUBCOM_DROPPABLE (any process can deregister the handler) or RXSUBCOM_NONDROP (only the current process can deregister the handler)

Return Value:

RXSUBCOM_OK
no error
RXSUBCOM_DUP
the environment name has already been defined (to address this handler, its library name must be specified)
RXSUBCOM_NOEMEM
Not enough memory
RXSUBCOM_BADTYPE
Drop is invalid

For RexxRegisterSubcomExe(), the following parameters are specified:

Name
environment name
FunctionAdr
the address of the function
Data
8 bytes of user data. (Can be NULL)

Return Value:

RXSUBCOM_OK
no error
RXSUBCOM_DUP
the environment name has already been defined
RXSUBCOM_NOTREG
not registered because of duplicate name
RXSUBCOM_NOEMEM
not enough memory

For RexxDeregisterSubcom(), the following parameters are specified:

Name
environment name
ModuleName
name of the DLL in which the handler function resides

Return Value:

RXSUBCOM_OK
no error
RXSUBCOM_NOTREG
the environment name has not been registered
RXSUBCOM_NOCANDROP
drop permission is not granted
RXSUBCOM_BADTYPE
Subcom is invalid

For RexxQuerySubcom(), the following parameters are specified:

Name
environment name
ModuleName
name of the DLL in which the handler function resides
Flag
either RXSUBCOM_OK or RXSUBCOM_NOTREG depending upon whether Name is registered or not.
Data
The address to an 8 byte location in memory to receive the user data

Return Value:

RXSUBCOM_OK
no error
RXSUBCOM_NOTREG
the environment name has not been registered
RXSUBCOM_BADTYPE

Creating External Subcommand Handler Functions

External command handlers must use the following format:

ULONG _System command_handler(PRXSTRING Command,PUSHORT Flag, PRXSTRING Retstr)

The arguments have the following meaning:

Command
a null-terminated RXSTRING containing the issued command
Flag
the subroutine uses this flag to return the completion status of the command. It must be either RXSUBCOM_OK, RXSUBCOM_ERROR, or RXSUBCOM_FAILURE
Retstr
Address of an RXSTRING for the return code. Retstr is a character string return code that will be assigned to the REXX special variable RC when the subcommand handler returns. By default, Retstr points to a 256-byte RXSTRING. If necessary, a longer RXSTRING can allocated with DosAllocMem(). If the subcommand handler sets Retval to an empty RXSTRING, REXX will assign the string "0" to RC.

Flag Interpretation

RXSUBCOM_OK
Indicates that the command was successfully executed.
RXSUBCOM_ERROR
Indicates that the command is recognized by the handler but a syntax error occurred (incorrect parameters for instance). The REXX interpreter will raise this condition if SIGNAL ON ERROR or CALL ON ERROR traps have been created. If TRACE ERRORS has been issued, REXX will trace the command when the subcommand handler returns.
RXSUBCOM_FAILURE
Indicates that a subcommand failure has occurred. Usually this is used if Command is unrecognized. The REXX interpreter will raise this condition if SIGNAL ON FAILURE or CALL ON FAILURE traps have been created. If TRACE FAILURES has been issued, REXX will trace the command when the subcommand handler returns.

If the command has parameters, it is up to the subcommand handler to parse the command for the arguments. The following simple subcommand handler processes the command "CLS".

ULONG _System EDM_Handler(PRXSTRING Command,PUSHORT Flags,PRXSTRING retstr){
   BYTE bCell[2];

   if(!strcmp("CLS",Command->strptr ) ){
      bCell[0] = 0x20;
      bCell[1] = 0x07;
      VioScrollDn( 0, 0, (USHORT)0xFFFF, (USHORT)0XFFFF,
                     (USHORT)0xFFFF, bCell, (HVIO) 0);
      VioSetCurPos(0, 0, (HVIO) 0);

      *Flags=RXSUBCOM_OK;
      retstr->strlength=1;
      strcpy(retstr->strptr,"0");
      return 0;
   }
   *Flags=RXSUBCOM_FAILURE;
    retstr->strlength=1;
    strcpy(retstr->strptr,"1");
    return 1;
}

Putting It All Together

The file rexx24.cpp contains a sample program demonstrating the use of external subcommand handlers, external functions, and executing in memory REXX programs. It is rather long, and very similar to the other sample REXX programs that are included in this article, so it won't be included here. However, let's look at two code snippets that illustrate new points.

The external subcommand handler is registered by the function call:

RexxRegisterSubcomExe((PSZ)"EDM",(PFN)&EDM_Handler,NULL);

The external subcommand handler we have seen in the previous section is registered under the name "EDM". To use this handler, our RexxStart() must be modified to specify the "EDM" environment. The following section of code illustrates this:

return_code = RexxStart(0,           // No arguments
                        argv,        // dummy entry
                        "Sample",    // procedure name
                        INSTORE,     // InStore
                        "EDM",       // use the "EDM" command processor
                        RXCOMMAND,   // execute as a command
                        NULL,        // No exit handlers
                        &rc,         // return code from REXX routine
                        &retstr);    // return string from REXX routine

Functions, Macros, and Commands

We have now examined a good portion of the features REXX provides developers. A few questions now arise like: what's the difference between commands and functions, and where do macros fit into this?

Let's start by looking at the difference between functions and commands. Functions require that the user (the person using your application/library) to use a bulky syntax. For example, to call a function that clears the screen, let's call it "CLS", the user would have to use the either of the following syntaxes.

call CLS

or

dummy=CLS()

In the first case, CLS() returns a result in the special REXX variable RESULT. In the second case, the variable dummy is assigned the return value from CLS(). Both expressions are rather bulky considering that CLS() takes no arguments, and really doesn't have any return values other than TRUE or FALSE. If developing a REXX programming environment, CLS would be a prime candidate for command status, requiring only the following statement to execute:

CLS

The return value for the command would be placed in the special REXX variable RC. As previously mentioned, subcommand handler must parse their own commands. So if arguments are needed, it is less work for the programmer to use functions.

As mentioned before, macros really aren't there for the programmer, they are for the user. REXX provides several functions to allow the programmer to manipulate the macrospace. Macros are written in REXX, while external functions and subcommand handlers are written in C/C++, or some other compiled language.

Summary

This concludes our look at Rexx/2 for this issue. Building upon the REXX information in part 1, we have covered new uses for RexxStart(), external subcommand handlers, and the REXX macrospace interface. We have not looked at exit handlers or the various REXX interfaces for interactive debugging.

As usual, question and comments are welcome.