REXX-ercising Your Applications - Part 2

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]. ''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. ''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: So what is this macrospace thing and what does RexxAddMacro function do?
 * execute REXX programs from a file
 * execute REXX programs from memory
 * execute REXX programs from the macrospace

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: The parameters for RexxLoadMacroSpace are the same as those for RexxSaveMacroSpace
 * 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

Return Value: 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_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.

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 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".
 * 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.

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:

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.