REXX-ercising Your Applications - Part 2: Difference between revisions
Created page with "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 applicati..." |
|||
(7 intermediate revisions by 3 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. | |||
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]. | |||
#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 (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) & | |||
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"<< | |||
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; | ||
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, | return_code = RexxStart(0, // No arguments | ||
argv, | argv, // dummy entry | ||
Input, | Input, // File name | ||
INSTORE, | INSTORE, // InStore | ||
"CMD", | "CMD", // use the "CMD" command processor | ||
RXCOMMAND, | RXCOMMAND, // execute as a command | ||
NULL, | NULL, // No exit handlers | ||
&rc, // return code from REXX routine | |||
& | &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; | ||
cout<<endl<<"INSTORE[1].strlength\t"<<INSTORE[1].strlength<<endl; | |||
cout<<endl<<"INSTORE[1].strlength\t"<< | |||
return 0; | return 0; | ||
} | } | ||
</code> | |||
''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. | |||
<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. | |||
#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 | 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; | ||
cout<<"Type rexxsamp.cmd <ENTER> | |||
cin>>Input; | cin>>Input; | ||
Line 210: | Line 185: | ||
cout <<"Executing Macro"<<endl; | cout <<"Executing Macro"<<endl; | ||
return_code = RexxStart(0, | return_code = RexxStart(0, // No arguments | ||
argv, | argv, // dummy entry | ||
"MACRO1", | "MACRO1", // File name | ||
INSTORE, | INSTORE, // NULL InStore | ||
"CMD", | "CMD", // use the "CMD" command processor | ||
RXCOMMAND, | RXCOMMAND, // execute as a command | ||
NULL, | NULL, // No exit handlers | ||
& rc, | &rc, // return code from REXX routine | ||
& retstr); | &retstr); // return string from REXX routine | ||
delete [] retstr.strptr; | delete [] retstr.strptr; | ||
Line 224: | Line 199: | ||
return 0; | return 0; | ||
} | } | ||
</code> | |||
''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 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? | |||
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: | |||
;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 | ||
; '''SourceFile''' | ;RXMACRO_NO_STORAGE:macrospace is full | ||
;RXMACRO_SOURCE_NOT_FOUND:'''SourceFile''' is invalid | |||
; '''SearchOrder''' | ;RXMACRO_INVALID_POSITION:'''SearchOrder''' is invalid | ||
For '''RexxDropMacro()''', the following parameters are specified: | |||
: This is the name of the function that was used in the RexxAddMacro() function call | ;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: | |||
;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. | |||
For RexxQueryMacro(), the following parameters are specified: | |||
; | |||
: This is the name of the function whose existence is being queried | |||
; | |||
: 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 | |||
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 | |||
; | ;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 | |||
; ''' | |||
Return Value: | 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=== | ===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) | |||
; | ;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 of function names to be saved (if Num is 0, then this can be NULL) | |||
; | |||
: 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_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()''' | |||
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_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()'''. | |||
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 | |||
; | ;ModuleName: name of the DLL | ||
: environment name | ;FunctionName: name of the function in the DLL | ||
; | ;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) | ||
; | |||
: name of the function in the DLL | |||
; | |||
: 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 (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) | |||
For RexxRegisterSubcomExe(), the following parameters are specified: | |||
; | |||
: environment name | |||
; | |||
: the address of the function | |||
; | |||
: 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_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 | |||
; | ;ModuleName: name of the DLL in which the handler function resides | ||
: environment name | |||
; | |||
: 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: | |||
;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 | |||
For RexxQuerySubcom(), the following parameters are specified: | |||
; | |||
: environment name | |||
; | |||
: name of the DLL in which the handler function resides | |||
; ''' | |||
; | |||
: 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_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. | |||
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. | |||
; | |||
: Indicates that the command was successfully executed. | |||
; | |||
: 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. | |||
; | |||
: | |||
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 | |||
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 | |||
"EDM", // use the "EDM" command processor | |||
argv, | RXCOMMAND, // execute as a command | ||
"Sample", | NULL, // No exit handlers | ||
INSTORE, | &rc, // return code from REXX routine | ||
"EDM", | &retstr); // return string from REXX routine | ||
RXCOMMAND, | </code> | ||
NULL, | |||
& 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() | |||
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. | ||
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: | [[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.