REXX-ercising Your Applications - Part 2
Автор: Gordon Zeglinski
Дата: June 1994
Источник: EDM/2
Contents
- 1 Introduction
- 2 More on RexxStart()
- 3 The REXX Macrospace
- 3.1 Manipulating the MACRO Storage Area
- 4 External Subcommand Handlers
- 4.1 Creating External Subcommand Handler Functions
- 4.2 Flag Interpretation
- 5 Putting It All Together
- 5.1 Functions, Macros, and Commands
- 6 Summary
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) & amp.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 & amp.rc, // return code from REXX routine & amp.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
- 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".
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.