Interfacing Rexx to other programs
This chapter describes an interface between a REXX interpreter and another program, typically written in C or another high level, compiled language. It is intended for application programmers who are implementing REXX support in their programs. It describes the interface known as the REXX SAA API.
The functionality of the interface is divided into some main areas:
Subcommand handlers
which trap and handle a command to an external environment.
External function handlers
extend the REXX language with external functions
Interpreting
REXX scripts, either from a disk file, or from memory.
Variable interface
which makes it possible to access the variables in the interpreter, and allows operations like setting, fetching and dropping variables.
System exits
which are used to hook into certain key points in the interpreter while it executes a script.
External Queue interface
which allows access to Regina's external queuing mechanism.
Macrospace functions
which are used to load and save external macros into Regina's macrospace for faster execution.
Memory Allocation functions
which provide for platform-independent memroy allocating/deallocation functions.
Callback functions
which are used to allow the API program to execute a procedure within the running script.
In the following sections each of these areas are described in detail, and a number of brief but complete examples are given at the end of the chapter.
The description is of a highly technical nature, since it is assumed that the reader will be an application programmer seeking information about the interface. Therefore, much of the content is given as prototypes and C style datatype definitions. Although this format is cryptic for non-C programmers, it will convey exact, compact, and complete information to the intended readers. Also, the problems with
ambiguity and incompleteness that often accompany a descriptive prose text are avoided.
All the C code that uses the REXX application interface, must include a special header file that contains the necessary definitions. This file is called rexxsaa.h. Where you will find this file,
will depend on you system and which compiler you use.
Also, the interface part between the application and the REXX interpreter may be implemented as a library, which you link with the application using the functions described in this chapter. The name of
this library, and its location might differ from system to system. Under Unix, this library can be implemented as a static (libregina.a) or dynamic library (libregina.[so|sl]). Under other platforms Regina is also be implemented as a static or dynamic library.
Including a header file ought to be enough; unfortunately, that is not so. Each of the domains of functionality listed above are defined in separate sections' in the rexxsaa.h header file. In order for
these to be made available, certain preprocessor symbols have to be set. For instance, you have to include the following definition:
#define INCL_RXSHV
in order to make available the definitions and datatypes concerning the variable pool interface. The various definitions that can be set are:
INCL_RXSUBCOM
Must be defined in order to get the prototypes, datatypes and symbols needed for the subcommand interface of the API.
INCL_RXFUNC
Must be defined in order to get the prototypes, datatypes and symbols needed for the external function interface of the API.
INCL_RXSYSEXIT
Must be defined in order to get the prototypes, datatypes, and symbols needed for the system exit functions
INCL_RXSHV
Must be set in order to get the prototypes, symbols and datatype definitions necessary to use the REXX variable pool.
INCL_RXQUEUE
Must be set in order to get the prototypes, symbols and datatype definitions necessary to use the REXX external queues.
INCL_RXMACRO
Must be set in order to get the prototypes, symbols and datatype definitions necessary to use the REXX macrospace interface of the API.
In this section, some data structures and datatypes relevant to the application interface to REXX are defined and described. The datatypes defined are:
RXSTRING
Holds a REXX string.
RXSYSEXIT
Holds a definition of a system exit handler. Used when starting a REXX script with RexxStart(), and when defining the system exit handlers.
The datatypes used in the SAA API are defined in rexxsaa.h. They are:
typedef char CHAR ;
typedef short SHORT ;
typedef long LONG ;
typedef char *PSZ ;
typedef CHAR *PCHAR ;
typedef SHORT *PSHORT ;
typedef LONG *PLONG ;
typedef unsigned char UCHAR ;
typedef unsigned short USHORT ;
typedef unsigned long ULONG ;
typedef USHORT *PUSHORT ;
typedef char *PCH ;
typedef unsigned char *PUCHAR ;
typedef void VOID;
typedef void *PVOID;
typedef ULONG APIRET;
typedef APIRET (APIENTRY *PFN)();
One other item needs mentioning; APIENTRY. This value is used to specify the linkage type on OS/2 and Win32 platforms. It is assumed that this value #defined by inclusion of compiler-specific header files in rexxsaa.h. Under Unix, this is #defined to nothing.
The SAA API interface uses Rexx string which are stored in the structure RXSTRING. There is also a datatype PRXSTRING, which is a pointer to RXSTRING. Their definitions are:
typedef struct {
unsigned char *strptr ; /* Pointer to string contents */
unsigned long strlength ; /* Length of string */
} RXSTRING ;
typedef RXSTRING *PRXSTRING ;
The strptr field is a pointer to an array of characters making up the contents of the Rexx string', while strlength holds the number of characters in that array.
Unfortunately, there are some inconsistencies in naming of various special kinds of strings. In REXX (TRL), a ``null string'' is a string that has zero length. On the other hand, the SAA API operates with two kinds of special strings: null strings and zero length strings. The latter is a string with zero length (equals null strings in REXX), while the former is a sort of undefined or empty string, which denotes a string without a value. The null strings of SAA API are used to denote unspecified values (e.g. a parameter left out in a subroutine call). In this chapter, when the terms null strings and zero length strings are italicized, they refer to the SAA API style meaning.
A number of macros are defined, which simplifies operations on RXSTRINGs for the programmer. In the list below, all parameters called x are of type RXSTRING.
MAKERXSTRING(x,content,length)]
The parameter content must be a pointer to char, while length is integer. The x parameter will be set to the contents and length supplied. The only operations are assignments; no new space is allocated and the contents of the string is not copied.
RXNULLSTRING(x)]
Returns true only if x is a null string.
i.e. x.strptr is NULL.
RXSTRLEN(x)]
Returns the length of the string x as an unsigned long. Zero is returned both when x is a null string or a zero length string.
RXSTRPTR(x)]
Returns a pointer to the first character in the string x, or NULL if x is a null string. If x is a zero length string, and non-NULL pointer is returned.
RXVALIDSTRING(x)]
Returns true only if x is neither a null string nor a zero length string
i.e. x must have non-empty contents.
RXZEROLENSTRING(x)]
Returns true only if x is a zero length string.
i.e. x.strptr is non-NULL, and x.strlength is zero.
These definitions are most likely to be defined as preprocessor macros, so you should never call them with parameters having any side effects. Also note that at least MAKERXSTRING() is likely to be implemented as two statements, and might not work properly if following
e.g. an if statement. Check the actual definitions in the rexxsaa.h header file before using them in a fancy context.
One definition of these might be (don't rely on this to be the case with your implementation):
#define MAKERXSTRING(x,c,l) ((x).strptr=(c),(x).strlength=(l))
#define RXNULLSTRING(x) (!(x).strptr)
#define RXSTRLEN(x) ((x).strptr ? (x).strlength : 0UL)
#define RXSTRPTR(x) ((x).strptr)
#define RXVALIDSTRING(x) ((x).strptr && (x).strlength)
#define RXZEROLENSTRING(x) ((x).strptr && !(x).strlength)
Note that these definitions of strings differ from the normal definition in C programs; where a string is an array of characters, and its length is implicitly given by a terminating ASCII NUL character. In the RXSTRING definition, a string can contain any character, including an ASCII NUL, and the length is explicitly given.
This structure is used for defining which system exit handlers are to handle which system exits. The two relevant datatypes are defined as:
typedef struct {
unsigned char *sysexit_name ;
short sysexit_code ;
} RXSYSEXIT ;
typedef RXSYSEXIT *PRXSYSEXIT ;
In this structure, sysexit_name is a pointer to the ASCII NUL terminated string containing the name of a previously registered (and currently active) system exit handler. The sysexit_code field is main function code of a system exit.
The system exits are divided into main functions and sub-functions. An exit is defined to handle a main function, and must thus handle all the sub-functions for that main function. All the functions and sub-functions are listed in the description of the EXIT structure.
This sections describes the subcommand handler interface, which enables the application to trap commands in a REXX script being executed and handle this commands itself.
A subcommand handler is a piece of code, that is called to handle a command to an external environment in REXX. It must be either a subroutine in the application that started the interpreter, or a subroutine in a dynamic link library. In any case, when the interpreter needs to execute a command to an external environment, it will call the subcommand handler, passing the command as a parameter.
Typically, an application will set up a subcommand handler before starting a REXX script. That way, it can trap and handle any command being executed during the course of the script.
Each subcommand handler handles one environment, which is referred to by a name. It seems to be undefined whether upper and lower case letters differ in the environment name, so you should assume they differ. Also, there might be an upper limit for the length of an environment name, and some letters may be illegal as part of an environment name.
Regina allows any letter in the environment name, except ASCII NUL; and sets no upper limit for the length of an environment name. However, for compatibility reasons, you should avoid uncommon letters and keep the length of the name fairly short.
The prototype of a subcommand handler function is:
APIRET APIENTRY handler(
PRXSTRING command,
USHORT flags,
PRXSTRING returnstring
) ;
After registration, this function is called whenever the application is to handle a subcommand for a given environment. The value of the parameters are:
[command]
The command string that is to be executed. This is the resulting string after the command expression has been evaluated in the REXX interpreter. It can not be empty, although it can be a zero-length-string.
[flags]
Points to an unsigned short which is to receive the status of the completion of the handler. This can be one of the following: RXSUBCOM_OK, RXSUBCOM_ERROR, or RXSUBCOM_FAILURE. The contents will be used to determine whether to raise any condition at return of the subcommand. Do not confuse it with the return value.
[returnstring]
Points to a RXSTRING which is to receive the return value from the subcommand. Passing the return value as a string makes it possible to return non-numeric return codes. As a special case, you might set returnstring.strptr to NULL, instead of specifying a return string of the ASCII representation of zero.
Note that it is not possible to return nothing in a subcommand, since this is interpreted as zero. Nor is it possible to return a numeric return code as such; you must convert it to ASCII representation before you return.
The returnstring string will provide a 256 byte array which the programmer might use if the return data is not longer that that. If that space is not sufficient, the handler can provide another area itself. In that case, the handler should not de-allocate the default area, and the new area should be allocated in a standard fashion.
This function is used to register a subcommand handler with the interface. The subcommand handler must be a procedure located within the code of the application. After registration, the REXX interpreter can execute subcommands by calling the subcommand handler with parameters describing the subcommand.
The prototype for RexxRegisterSubcomExe() is:
APIRET APIENTRY RexxRegisterSubcomExe(
PSZ EnvName,
PFN EntryPoint,
PUCHAR UserArea
) ;
All the parameters are input, and their significance are:
[EnvName]
Points to an ASCII NUL terminated character string which defines the name of the environment to be registered. This is the same name as the REXX interpreter uses with the ADDRESS clause in order to select an external environment.
[EntryPoint]
Points to the entrypoint of the subcommand handler routine for the environment to be registered. See the section on Subcommand Handlers for more information. There is an upper limit for the length of this name.
[UserArea]
Pointer to an 8 byte area of information that is to be associated with this environment. This pointer can be NULL if no such area is necessary.
The areas pointed to by EnvName and UserArea are copied to a private area in the interface, so the programmer may de-allocate or reuse the area used for these parameters after the call has returned.
The RexxRegisterSubcom() returns an unsigned long, which carries status information describing the outcome of the operation. The status will be one of the RXSUBCOM values:
[ RXSUBCOM_OK]
The subcommand handler was successfully registered.
[RXSUBCOM_DUP]
The subcommand handler was successfully registered. There already existed another subcommand handler which was registered with RexxRegisterSubcomDll(), but this will be shadowed by the newly registered handler.
[RXSUBCOM_NOTREG]
Due to some error, the handler was not registered. Probably because a handler for EnvName was already defined at a previous call to RexxRegisterSubcomExe().
[RXSUBCOM_NOEMEM]
The handler was not registered, due to lack of memory.
[RXSUBCOM_BADTYPE]
Indicates that the handler was not registered, due to one or more of the parameters having invalid values.
This function is used to set up a routine that is located in a module in a dynamic link library, as a subcommand handler. Some operating systems don't have dynamic linking, and thus cannot make use of this facility. The prototype of this function is:
APIRET APIENTRY RexxRegisterSubcomDll(
PSZ EnvName,
PSZ ModuleName,
PFN EntryPoint,
PUCHAR UserArea,
ULONG DropAuth
) ;
This function is not yet supported by Regina.
This function is used to remove a particular environment from the list of registered environments. The prototype of the function is:
APIRET APIENTRY RexxDeregisterSubcom(
PSZ EnvName,
PSZ ModuleName
) ;
Both parameters are input values:
[EnvName]
Pointer to ASCII NUL terminated string, which represents the name of the environment to be removed.
[ModuleName]
Also an ASCII NUL terminated string, which points to the name of the module containing the subcommand handler of the environment to be deleted.
The list of defined environments is searched, and if an environment matching the one named by the first parameter are found, it is deleted.
The returned value from RexxDeregisterSubcom() can be one of:
[RXSUBCOM_OK]
The subcommand handler was successfully deleted.
[RXSUBCOM_NOTREG]
The subcommand handler was not found.
[RXSUBCOM_BADTYPE]
One or more of the parameters had illegal values, and the operation was not carried through.
Most systems that do have dynamic linking have no method for reclaiming the space used by dynamically linked routines. So, even if you were able to load a dll, there are no guarantees that you will be able to unload it.
This function retrieves information about a previously registered subcommand handler. The prototype of the function is:
APIRET APIENTRY RexxQuerySubcom(
PSZ EnvName,
PSZ ModuleName,
PUSHORT Flag,
PUCHAR UserWord
) ;
The significance of the parameters are:
[EnvName]
Pointer to an ASCII NUL terminated character string, which names the subcommand handler about which information is to be returned.
[ModuleName]
Pointer to an ASCII NUL terminated character string, which names a dynamic link library. Only the named library will be searched for the subcommand handler named by EnvName. This parameter must be NULL if all subcommand handlers are to be searched.
[Flag]
Pointer to a short which is to receive the value RXSUBCOM_OK or RXSUBCOM_NOTREG. In fact, this is the same as the return value from the function.
[UserWord]
Pointer to an area of 8 bytes. The userarea of the subcommand handler is copied to the area pointed to by UserWord. This parameter might be NULL if the data of the userarea is not needed.
The returned value from RexxQuerySubcom() can be one of:
[RXSUBCOM_OK]
The subcommand handler was found, and the required information has been returned in the Flag and UserWord variables.
[RXSUBCOM_NOTREG]
The subcommand handler was not found. The Flag variable will also be set to this value, and the UserWord variable is not changed.
[RXSUBCOM_BADTYPE]
One or more of the parameters had illegal values, and the operation was not carried through.
This sections describes the external function handler interface, which extends the language by enabling external functions to be written in a language other than REXX.
An external function handler is a piece of code, that is called to handle external functions and subroutine calls in REXX. It must be either a subroutine in the application that started the interpreter, or a subroutine in a dynamic link library. In any case, when the interpreter needs to execute a function registered as an external function, it will call the external function handler, passing the function name as a parameter.
All external functions written in a language other than REXX must be registered with the interpreter before starting a REXX script.
An external function handler can handle one or more functions. The handler can determine the function actually called by examiining one of the parameters passed to the handler and act accordingly.
The prototype of a subcommand handler function is:
APIRET APIENTRY handler(
PSZ name,
ULONG argc,
PRXSTRING argv,
PSZ queuename,
PRXSTRING returnstring
) ;
After a function is registered with this function defined as the handler, this function is called whenever the application calls the function. The value of the parameters are:
[name]
The function called.
[argc]
The number of parameters passed to the function. Argv will contain argc RXSTRINGs.
[queuename]
The name of the currently define data queue.
[returnstring]
Points to a RXSTRING which is to receive the return value from the function. Passing the return value as a string makes it possible to return non-numeric return codes. As a special case, you might set returnstring.strptr to NULL, instead of specifying a return string of the ASCII representation of zero.
The returnstring string will provide a 256 byte array which the programmer might use if the return data is not longer that that. If that space is not sufficient, the handler can provide another area itself. In that case, the handler should not de-allocate the default area, and the new area should be allocated in a standard fashion. if the external function does not return a value, it should set returnstring to an empty RXSTRING. This will enable the interpreter to raise error 44; Function did not return data, if the external function is called as a function. If the external function is invoked via a CALL command, the interpreter drops the special variable RESULT.
The handler returns zero if the function completed successfully. When the handler returns a non-zero value, the interpreter will raise error 40; Invalid call to routine.
This function is used to register an external function handler with the interface. The external function handler must be a procedure located within the code of the application. After registration, the REXX interpreter can execute external functions as if they were built-ins.
The prototype for RexxRegisterFunctionExe() is:
APIRET APIENTRY RexxRegisterFunctionExe(
PSZ FuncName,
PFN EntryPoint
) ;
All the parameters are input, and their significance are:
[FuncName]
Points to an ASCII NUL terminated character string which defines the name of the external function to be registered. This is the same name as the REXX interpreter uses with a function call or via the CALL command.
[EntryPoint]
Points to the entrypoint of the external function handler routine for the function to be registered. See the section on External Function Handlers for more information.
The area pointed to by FuncName is copied to a private area in the interface, so the programmer may de-allocate or reuse the area used for this parameter after the call has returned.
The RexxRegisterFunctionExe() returns an unsigned long, which carries status information describing the outcome of the operation. The status will be one of the RXFUNC values:
[ RXFUNC_OK]
The handler was successfully registered.
[RXFUNC_DUP]
The handler was successfully registered. There already existed another external function handler which was registered with RexxRegisterFunctionExe(), but this will be shadowed by the newly registered handler.
[RXFUNC_NOEMEM]
The handler was not registered, due to lack of memory.
This function is used to set up an external function handler that is located in a module in a dynamic link library. Some operating systems don't have dynamic linking, and thus cannot make use of this facility. The prototype of this function is:
APIRET APIENTRY RexxRegisterFunctionDll(
PSZ ExternalName,
PSZ LibraryName,
PSZ InternalName
) ;
All the parameters are input, and their significance are:
[ExternalName]
Points to an ASCII NUL terminated character string which defines the name of the external function to be registered. This is the same name as the REXX interpreter uses with a function call or via the CALL command.
[LibraryName]
Points to an ASCII NUL terminated character string which defines the name of the dynamic library. This string may require a directory specification.
[InternalName]
Points to an ASCII NUL terminated character string which defines the name of the entrypoint within the dynamic library. On systems where the case of function names in dynamic libraries is relevant, this name must be specified in the same case as the function name within the dynamic library.
The areas pointed to by all parameters are copied to a private area in the interface, so the programmer may de-allocate or reuse the area used for these parameters after the call has returned.
The RexxRegisterFunctionDll() returns an unsigned long, which carries status information describing the outcome of the operation. The status will be one of the RXFUNC values:
[ RXFUNC_OK]
The handler was successfully registered.
[RXFUNC_DUP]
The handler was successfully registered. There already existed another external function handler which was registered with RexxRegisterFunctionDll(), but this will be shadowed by the newly registered handler.
[RXFUNC_NOEMEM]
The handler was not registered, due to lack of memory.
This function is used to remove a particular external function handler from the list of registered external function handlers. The prototype of the function is:
APIRET APIENTRY RexxDeregisterFunction(
PSZ FuncName
) ;
The parameter is an input value:
[FuncName]
Points to an ASCII NUL terminated character string which defines the name of the external function to be registered. This is the same name as the REXX interpreter uses with a function call or via the CALL command.
The list of defined function handlers is searched, and if an environment matching the one named by the parameter are found, it is deleted. This call is used to de-register function handlers registered with either RexxRegisterFunctionExe() or RexxRegisterFunctionDll().
The returned value from RexxDeregisterFunction() can be one of:
[RXFUNC_OK]
The handler was successfully deleted.
[RXFUNC_NOTREG]
The handler was not found.
Most systems that do have dynamic linking have no method for reclaiming the space used by dynamically linked routines. So, even if you were able to load a dll, there are no guarantees that you will be able to unload it.
This function retrieves the status of an external function handler. The prototype of the function is:
APIRET APIENTRY RexxQueryFunction(
PSZ FuncName
) ;
The significance of the parameters is:
[FuncName]
Points to an ASCII NUL terminated character string which defines the name of the external function to be registered. This is the same name as the REXX interpreter uses with a function call or via the CALL command.
The returned value from RexxQueryFunction() can be one of:
[RXFUNC_OK]
The external function handler was found.
[RXFUNC_NOTREG]
The handler was not found.
This sections describes the RexxStart() function, which allows the application to startup the interpreter and make it interpret pieces of REXX code.
This function is used to invoke the REXX interpreter in order to execute a piece of REXX code, which may be located on disk, as a pre-tokenized macro, or as ASCII source code in memory.
APIRET APIENTRY RexxStart(
LONG ArgCount,
PRXSTRING ArgList,
PSZ ProgramName,
PRXSTRING Instore,
PSZ EnvName,
LONG CallType,
PRXSYSEXIT Exits,
PUSHORT ReturnCode,
PRXSTRING Result
) ;
Of these parameters, ReturnCode and Result are output-only, while Instore is both input and output. The rest of the parameters are input-only. The significance of the parameters are:
[ArgCount]
The number of parameter strings given to the procedure. This is the number of defined REXX-strings pointed to by the ArgList parameter. The default maximum number of arguments that can be pased is 32, but this can be changed by the MAX_ARGS_TO_REXXSTART macro in rexx.h.
[ArgList]
Pointer to an array of REXX-strings, constituting the parameters to this call to REXX. The size of this array is given by the parameter ArgCount. If ArgCount is greater than one, the first and last parameters are ArgList[0] and ArgList[ArgCount-1]. If ArgCount is 0, the value of ArgList is irrelevant.
If the strptr of one of the elements in the array pointed to by ArgList is NULL, that means that this parameter is empty (i.e. unspecified, as opposed to a string of zero size).
[ProgName]
An ASCII NUL terminated string, specifying the name of the REXX script to be executed. The value of Instore will determine whether this value is interpreted as the name of a (on-disk) script, or a pre-tokenized macro. If it refers to a filename, the syntax of the contents of this parameter depends on the operating system.
[Instore]
Parameter used for storing tokenized REXX scripts. This parameter might either be NULL, else it will be a pointer to two RXSTRING structures, the first holding the ASCII version of a REXX program, the other holding the tokenized version of that program. See below for more information about how to use Instore.
[EnvName]
Pointer to ASCII NUL terminated string naming the environment which is to be the initial current environment when the script is started. If this parameter is set to NULL, the filetype is used as the initial environment name. What the filetype is, may depend on your operating system, but in general it is everything after the last period '.' in the filename.
[CallType]
A value describing whether the REXX interpreter is to be invoked in command, function or subroutine mode. Actually, this has little significance. The main difference is that in command mode, only one parameter string can be passed, and in function mode, a value must be returned. In addition, the mode chosen will affect the output of the PARSE SOURCE instruction in REXX.
Three symbolic values of integral type are defined, which can be used for this parameter: RXCOMMAND, RXFUNCTION and RXSUBROUTINE.
A value of RXRESTRICTED can be OR'ed with one of the above types to specify that Regina will run in restricted mode. This is particularly useful when Regina is used as an embedded intepreter in applications such as a database procedural language or a web-browser scripting language.
[SysExists]
A pointer to an array of exit handlers to be used. If no exit handlers are to be defined, NULL may be specified. Each element in the array defines one exit handler, and the element immediately following the last definition must have a sysexit_code set to RXENDLST.
[ReturnCode]
Pointer to a SHORT integer where the return code is stored, provided that the returned value is numeric, and within the range -(2**15) to 2**15-1. I don't know what happens to ReturnCode if either of these conditions is not satisfied. It probably becomes undefined, which means that it is totally useless since the program has to inspect the return string in order to determine whether ReturnCode is valid.
[Result]
Points to a REXX string into which the result string is written. The caller may or may not let the strptr field be supplied. If supplied (i.e. it is non-NULL), that area will be used, else a new area will be allocated. If the supplied area is used, its size is supposed to be given by the strlength field. If the size if not sufficient, a new area will be allocated, by RexxAllocateMemory(), and the caller must see to that it is properly de-allocated using RexxFreeMemory()..
Note that the ArgCount parameter need not be the same as the ARG() built-in function would return. Differences will occur if the last entries in ArgList are null strings.
The Instore parameter needs some special attention. It is used to directly or indirectly specify where to fetch the code to execute. The following algorithm is used to determine what to execute:
If Instore is NULL, then ProgName names the filename of an on-disk REXX script which it to be read and executed.
Else, if Instore is not NULL, the script is somewhere in memory, and no reading from disk is performed. If both Instore[0].strptr and Instore[1].strptr are NULL, then the script to execute is a pre-loaded macro which must have been loaded with a call to either RexxAddMacro() or RexxLoadMacroSpace(); and ProgName is the name of the macro to execute.
Else, if Instore[1].strptr is non-NULL, then Instore[1] contains the pre-tokenized image of a REXX script, and it is used for the execution.
Else, if Instore[0].strptr is non-NULL, then Instore[0]} contains the ASCII image of a REXX script, just as if the script had been read directly from the disk (i.e. including linefeeds and such). This image is passed to the interpreter, which tokenizes it, and stores the tokenized script in the Instore[1] string, and then proceeds to execute that script. Upon return, the Instore[1] will be set, and can later be used to re-execute the script within the same process, without the overhead of tokenizing.
The user is responsible for de-allocating any storage used by Instore[1]. Note that after tokenizing, the source code in Instore[0] is strictly speaking not needed anymore. It will only be consulted if the user calls the SOURCELINE() built-in function. It is not an error to use SOURCELINE() if the source is not present, but nullstrings and zero will be returned.
To tokenise a REXX script and save it for execution by a later execution by RexxStart() either in the currently running process or outside the current process, you need to call RexxStart() with the following arguments:
Parameter |
Value |
Notes |
---|---|---|
ArgCount |
1 |
|
ArgList.strlength |
3 |
|
ArgList.strptr |
//T |
|
ProgName |
|
Ignored |
Instore[0].strptr |
ASCII image of Rexx script |
|
Instrore[0].strlength |
Length of Instore[0].strptr |
|
Instrore[1].strptr |
Ignored |
This will be populated with the tokenised code. |
Instore[1].strlength |
Ignored |
This will be set to the length of Instore[1].strptr |
EnvName |
SYSTEM |
|
CallType |
RXCOMMAND |
|
SysExits |
NULL |
|
ReturnCode |
Ignored |
|
Result.strptr |
Ignored |
|
Result.strlength |
Ignored |
|
The valid return values from RexxStart() are:
[Negative]
indicates that a syntax error occurred during interpretation. In general, you can expect the error value to have the same absolute value as the REXX syntax error (but opposite signs, of course).
[Zero]
indicates that the interpreter finished executing the script without errors.
[Positive]
indicates probably that some problem occurred, that made it impossible to execute the script, e.g. a bad parameter value. However, I can't find any references in the documentation which states which values it is supposed to return.
During the course of an execution of RexxStart(), subcommand handlers and exit handlers might be called. These may call any function in the application interface, including another invocation of
RexxStart().
Often, the application programmer is interested in providing support simplifying the specification of filenames, like an environment variable search path or a default file type. The REXX interface does support a default file type: .CMD, but the user may not set this to anything else. Therefore, it is generally up to the application programmer to handle search paths, and also default file types (unless .CMD is OK).
If the initial environment name (EvnName) is NULL, then the initial environment during interpretation will be set equal to the file type of the script to execute. If the script does not have a file
type, it is probably set to some interpreter specific value.
This section describes the variable pool part of the application interface, which allows the application programmer to set, retrieve and drop variables in the REXX interpreter from the application program. It also allows access to other information.
The C preprocessor symbol INCL_RXSHV must be defined if the definitions for the variable pool interface are to be made available when rexxsaa.h is included.
First, let us define two terms, symbolic variable name and direct variable name, which are used in connection with the variable pool.
A symbolic variable name is the name of a variable, but it needs normalization and tail substitution before it names the real variable. The name foo.bar is a symbolic variable name, and it is transformed by normalization, to FOO.BAR, and then by tail substitution to FOO.42 (assuming that the current value of BAR is 42).
Normalization is the process of uppercasing all characters in the symbolic name; and tail substitution is the process of substituting each distinct simple symbol in the tail for its value.
On the other hand, a direct variable refers directly to the name of the variable. In a sense, it is a symbolic variable that has already been normalized and tail substituted. For instance, foo.bar is not a valid direct variable name, since lower case letters are not allowed in the variable stem. The direct variable FOO.42 is the same as the variable above. For simple variables, the only difference between direct and symbolic variable names is that lower case letters are allowed in symbolic names
Note that the two direct variable names FOO.bar and FOO.BAR refer to different variables, since upper and lower case letters differ in the tail. In fact, the tail of a compound direct variable may contain any character, including ASCII NUL. The stem part of a variable, and all simple variables can not contain any lower case letters.
As a remark, what would the direct variable FOO. refer to: the stem FOO. or the compound variable having stem FOO. and a nullstring as tail? Well, I suppose the former, since it is the more useful. Thus, the latter is inaccessible as a direct variable.
All requests to manipulate the REXX variable pool are controlled by a structure which is called SHVBLOCK, having the definition:
typedef struct shvnode {
struct shvnode *shvnext ; /* ptr to next in blk in chain */
RXSTRING shvname ; /* name of variable */
RXSTRING shvvalue ; /* value of variable */
ULONG shvnamelen ; /* length of shvname.strptr */
ULONG shvvaluelen ; /* length of shvvalue.strptr */
UCHAR shvcode ; /* operation code */
UCHAR shvret ; /* return code */
} SHVBLOCK ;
typedef SHVBLOCK *PSHVBLOCK ;
The fields shvnext and shvcode are purely input, while shvret is purely output. The rest of the fields might be input or output, depending on the requested operation, and the value of the fields. The significance of each field is:
[shvnext]
One call to RexxVariablePool() may sequentially process many requests. The shvnext field links one request to the next in line. The last request must have set shvnext to NULL. The requests are handled individually and thus, calling RexxVariablePool() with several requests is equivalent to making one call to RexxVariablePool() for each request.
[shvname]
Contains the name of the variable to operate on, as a RXSTRING. This field is only relevant for some requests, and its use may differ.
[shvvalue]
Contains the value of the variable to operate on as a RXSTRING. Like shvname, this might not be relevant for all types of requests.
[shvnamelen]
The length of the array that shvname.strptr points to. This field holds the maximum possible number of characters in shvname.strptr. While shvname.strlength holds the number of characters that are actually in use (i.e. defined).
[shvvaluelen]
The length of the array that shvvalue.strptr points to. Relates to shvvalue, like shvnamelen relates to shvname.
[shvcode]
The code of operation; decides what type of request to perform. A list of all the available requests is given below.
[shvret]
A return code describing the outcome of the request. This code is a bit special. The lower seven bits are flags which are set depending on whether some condition is met or not. Values above 127 are not used in this field.
There is a difference between shvnamelen and shvname.strlength. The former is the total length of the array of characters pointed to by shvname.strptr (if set). While the latter is the number of these characters that are actually in use. When a SHVBLOCK is used to return data from RexxVariablePool(), and a pre-allocated string space has been supplied, both these will be used; shvname.strlength will be set to the length of the data returned, while shvnamelen is never changed, only read to find the maximum number of characters that shvname can hold.
Even though shvnamelen is not really needed when shvname is used for input, it is wise to set it to its proper value (or at least set it to the same as shvname.strlength). The same applies for shvvalue and shvvaluelen.
The field shvcode can take one of the following symbolic values:
[RXSHV_DROPV]
The variable named by the direct variable name shvname is dropped (i.e. becomes undefined). The fields shvvalue and shvvaluelen do not matter.
[RXSHV_EXIT]
This is used to set the return value for an external function or exit handler.
[RXSHV_FETCH]
The value of the variable named by the direct variable name shvname is retrieved and stored in shvvalue. If shvvalue.strptr is NULL, the interpreter will allocate sufficient space to store the value (but it is the responsibility of the application programmer to release that space). Else, the value will be stored in the area allocated for shvvalue, and shvvaluelen is taken to be the maximum size of that area.
[RXSHV_NEXTV]
This code is used to retrieve the names and values of all variables at the current procedure level; i.e. excluding variables shadowed by PROCEDURE. The name and value of each variable are retrieved
simultaneously into shvname and shvvalue, respectively.
Successive requests for RXSHV_NEXTV will traverse the interpreter's internal data structure for storing variables, and return a new pair of variable name and value for each request. Each variable that is visible in the current scope, is returned once and only once, but the order is non-deterministic.
When all available variables in the REXX interpreter have already been retrieved, subsequent RXSHV_NEXTV requests will set the flag RXSHV_LVAR in the shvret field. There are a few restrictions. The traversal will be reset whenever the interpreter resumes execution, so an incomplete traversal can not be continued in a later external function, exit handler, or subcommand handler. Also, any set, fetch or drop operation will reset the traversal. These restrictions have been added to ensure that the variable pool is static throughout one traversal.
[RXSHV_PRIV]
Retrieves some piece of information from the interpreter, other than a variable value, based on the value of the shvname field. The value is stored in shvvalue as for a normal fetch. A list of possible names is shown below.
[RXSHV_SET]
The variable named by the direct variable name shvname is set to the value given by shvvalue.
[RXSHV_SYFET]
Like RXSHV_FETCH, except that shvname is a symbolic variable name.
[RXSHV_SYDRO]
Like RXSHV_DROPV, except that shvname is a symbolic variable name.
[RXSHV_SYSET]
Like RXSHV_SET, except that shvname is a symbolic variable name.
One type of request that needs some special attention is the RXSHV_PRIV, which retrieves a kind of meta-variable. Depending on the value of shvname, it returns a value in shvvalue describing some aspect of the interpreter. For RXSHV_PRIV the possible values for shvname are:
[PARM]
Returns the ASCII representation of the number of parameters to the currently active REXX procedure. This may not be the same value as the built-in function ARG() returns, but is the number ArgCount in RexxStart(). The two might differ if a routine was called with trailing omitted parameters.
[PARM.n]
The n must be a positive integer; and the value returned will be the n'th parameter at the current procedure level. This is not completely equivalent to the information that the built-in function ARG() returns. For parameters where ARG() would return the state omitted, the returned value is a null string, while for parameters where ARG() would return the state existing, the return value will be the parameter string (which may be a zero length string.
[QUENAME]
The name of the currently active external data queue. This feature has not yet been implemented in Regina, which always return default.
[SOURCE]
Returns the same string that is used in the PARSE SOURCE clause in REXX, at the current procedure level of interpretation.
[VERSION]
Returns the same string that is used in the PARSE VERSION clause in REXX.
The value returned by a variable pool request is a bit uncommon. A return value is computed for each request, and stored in the shvret field. This is a one-byte field, of which the most significant bit is never set. A symbolic value RXSHV_OK is defined as the value zero, and the shvret field will be equal to this name if none if the flags listed below is set. The symbolic value for these flags are:
[RXSHV_BADF]
The shvcode of this request contained a bad function code.
[RXSHV_BADN]
The shvname field contained a string that is not valid in this context. What exactly is a valid value depends on whether the operation is a private, a symbolic variable, or direct variable operation.
[RXSHV_LVAR]
Set if and only if the request was RXSHV_NETXV, and all available variables have already been retrieved by earlier requests.
[RXSHV_MEMFL]
There was not enough memory to complete this request.
[RXSHV_NEWV]
Set if and only if the referenced variable did not previously have a value. It can be returned for any set, fetch or drop operation.
[RXSHV_TRUNC]
Set if the retrieved value was truncated when it was copied into either the shvname or shvvalue fields. See below.
These flags are directly suitable for logical OR, without shifting, e.g. to check for truncation and no variables left, you can do something like:
if (req->shvret & (RXSHV_TRUNC | RXSHV_LVAR))
printf("Truncation or no vars left\n") ;
RXSHV_TRUNC can only occur when the interface is storing a retrieved value in a SHVBLOCK, and the pre-allocated space is present, but not sufficiently large. As described for RXSHV_FETCH, the interpreter will allocate enough space if shvvalue.strptr is NULL, and then RXSHV_TRUNC will never be set. Else the space supplied by shvvalue.strptr is used, and shvvaluelen is taken as the maximum length of shvvalue, and truncation will occur if the supplied space is too small.
Some implementations will consider SHV_MEMFL to be so severe as to skip the rest of the operations in a chain of requests. In order to write compatible software, you should never assume that requests
following in a chain after a request that returned SHV_MEMFL have been performed.
RXSHV_BADN is returned if the supplied shvname contains a value that is not legal in this context. For the symbolic set, fetch and drop operations, that means a symbol that is a legal variable name; both upper and lower case letters are allowed. For the direct set, fetch and drop operations, that means a variable name after normalization and tail substitution is not a legal variable name. For RXSHV_PRIV, it must be one of the values listed above.
There is a small subtlety in the above description. TRL states that when a REXX assignment assigns a value to a stem variable, all possible variables having that stem are assigned a new value (independent of whether they had an explicit value before). So, strictly speaking, if a stem is set, then a RXSHV_NETV sequence should return an (almost) infinite sequence of compound variables for that stem. Of course, that is completely useless, so you can assume that only compound variables of that stem given an explicit value after the stem was assigned a value will be returned by RXSHV_NEXTV. However, because of that subtlety, the variables returned by RXSHV_NEXTV for compound variables might not be representative for the state of the variables.
e.g. what would a sequence of RXSHV_NEXT requests return after the following REXX code ?:
foo. = 'bar'
drop foo.bar
The second statement here, might not change the returned values! After the first statement, only the stem foo. would probably have been returned, and so also if all variables were fetched after the second statement.
Due to the subtleties described at the end of the previous subsection, some notes on how Regina handles RXSHV_NEXTV requests for compound variables are in order. The following rules applies:
Both the stem variable FOO. and the compound variable having FOO. as stem and a nullstring as tail, are returned with the name of FOO.. In this situation, a sequence of RXSHV_NEXTV requests may seem to return values for the same variable twice. This is unfortunate, but it seems to be the only way. In any case, you'll have to perform the RXSHV_SYFET in order to determine which is which.
If a stem variable has not been assigned a value, its compound variables are only returned if they have been assigned an explicit value. i.e. compound variables for that stem that have either never been assigned a value, or have been dropped, will not be reported by RXSHV_NEXTV. There is nothing strange about this.
If a stem variable has been assigned a value, then its compound variables will be reported in two cases: Firstly, the compound variables having explicitly been assigned a value afterwards. Secondly, the compound variables which have been dropped afterwards, which are reported to have their initial value, and the flag RXSHV_NEWV is set in shvret.
It may sound a bit stupid that unset variables are listed when the request is to list all variables which have been set, but that is about the best I can do, if I am to stay within the standard definition and return a complete and exact status of the variable pool.
If the return code from RexxVariablePool() is less than 128, Regina is guaranteed to have tried to process all requests in the chain. If the return code is above 127, some requests may not have been
processed. Actually, the number 127 (or 128) is a bit inconvenient, since it will be an problem for later expansion of the standard. A much better approach would be to have a preprocessor symbol (say,
RXSHV_FATAL, and if the return code from the RexxVariablePool() function was larger than that, it would be a direct error code, and not a composite error code built from the shvret fields of the requests. The RXSHV_FATAL would then have to be the addition of all the atomic composite error codes.
(Warning: author mounting the soapbox.)
The right way to fix this, is to let the function RexxVariablePool() set another flag in shvret (e.g. named RXSHV_STEM) during RXSHV_NEXTV if and only if the value returned is a stem variable. That way, the application programmer would be able to differ between stem variables and compound variable with a null string tail.
To handle the other problem with compound variables and RXSHV_NEXTV, I would have liked to return a null string in shvvalue if and only if the variable is a compound variable having its initial value, and the stem of that compound variable has been assigned a value. Then, the value of the compound variable is equal to its name, and is available in the shvname field.
I'd also like to see that the shvret value contained other information concerning the variables, e.g. whether the variable was exposed at the current procedure level. Of course, Regina does not contain any of these extra, non-standard features.
(Author is dismounting the soapbox.)
When Regina is returning variables with RXSHV_NEXTV, the variables are returned in the order in which they occur in the open hashtable in the interpreter. i.e. the order in which variables belonging to different bins are returned is consistent, but the order in which variables hashed to the same bin are returned, is non-deterministic. Note that all compound variables belonging to the same stem are returned in one sequence.
This function is used to process a sequence of variable requests, and process them sequentially. The prototype of this function is:
APIRET APIENTRY ULONG RexxVariablePool(
SHVBLOCK *Request
) ;
Its only parameter is a pointer to a SHVBLOCK structure, which may be the first of the linked list. The function performs the operation specified in each block. If an error should occur, the current request is terminated, and the function moves on to the next request in the chain.
The result value is a bit peculiar. If the returned value is less than 128, it is calculated by logically OR'ing the returned shvret field of all the requests in the chain. That way, you can easily check whether any of the requests was e.g. skipped because of lack of memory. To determine which request, you have to iterate through the list.
If the result value is higher than 127, it signifies an error. If any of these values are set, you can not assume that any of the requests have been processed. The following symbolic name gives its meaning.
[RXSHV_NOAVL]
Means that the interface is not available for this request. This might occur if the interface was not able to start the interpreter, or if an operation requested a variable when the interpreter is not currently executing any script (i.e. idle and waiting for a script to execute).
The exit handlers provide a mechanism for governing important aspects of the REXX interpreter from the application: It can trap situations like the interpreter writing out text, and then handle them itself, e.g. by displaying the text in a special window. You can regard system exits as a sort of hooks.
Just like the subcommand handler, the system exit handler is a routine supplied by the application, and is called by the interpreter when certain situations occur. These situations are described in detail later. For the examples below, we will use the output from SAY as an example.
If a system exit handler is enabled for the SAY instruction, it will be called with a parameter describing the text that is to be written out. The system exit handler can choose to handle the situation (e.g. by writing the text itself), or it can ignore it and let the interpreter perform the output. The return code from the system exit tells the interpreter whether a system exit handled the situation or not.
A system exit handler must be a routine defined according to the prototype:
LONG APIENTRY my_exit_handler(
LONG ExitNumber,
LONG Subfunction,
PEXIT ParmBlock
) ;
In this prototype, the type PEXIT is a pointer to a parameter block containing all the parameters necessary to handle the situation. The actual definition of this parameter block will vary, and is described in detail in the list of each system exit.
The exits are defined in a two-level hierarchy. The ExitNumber defines the main function for a system exit, while the Subfunction defines the subfunction within that main function. e.g. for SAY, the main function will be RXSIO (the system exit for standard I/O) and the subfunction will be RXSIOSAY. The RXSIO main function has other sub-functions for handling trace output, interactive trace input, and PULL input from standard input.
The value returned from the system exit handler must be one of the following symbolic values:
[RXEXIT_HANDLED]
Signals that the system exit handler took care of the situation, and that the interpreter should not proceed to do the default action. For the SAY instruction, this means that the interpreter will not print out anything.
[RXEXIT_NOT_HANDLED]
Signals that the system exit handler did not take care of the situation, and the interpreter will proceed to perform the default action. For the SAY instruction, this means that it must print out the argument to SAY.
[RXEXIT_RAISE_ERROR]
Signals that the interpreter's default action for this situation should not be performed, but instead a SYNTAX condition should be raised. Don't get confused by the name, it is not the ERROR condition, but the SYNTAX condition is raised, using the syntax error Failure in system service (normally numbered 48).
In addition to returning information as the numeric return value, information may also be returned by setting variables in the parameter block. For instance, if the system exit is to handle interactive trace input, that is how it will supply the interpreter with the input string.
It is a good and disciplined practice to let your exit handlers start by verifying the ExitNumber and Subfunction codes, and immediately return RXEXIT_NOT_HANDLED if it does not recognize both of them. That way, your application will be upwards compatible with future interpreters which might have more sub-functions for any given main function.
The RXFNC system exit handler provides hooks for external functions. It has only one subfunction; RXFNCCAL, which allows an application program to intervene and handle any external function or subroutine.
Do not confuse this exit handler with the external function routines which allow you to define new REXX, semi-built-in functions. The exit handler is called for all invocations of external routines, and can be called for function names which you were unaware of.
The parameter ParmBlock for RXFNCCAL is defined as:
typedef struct {
typedef struct {
unsigned int rxfferr:1 ;
unsigned int rxffnfnd1 ;
unsigned int rxffsub: 1;
} rxfnc_flags ;
unsigned char *rxfnc_address ;
unsigned short rxfnc_addressl ;
unsigned char *rxfnc_que ;
unsigned short rxfnc_quel ;
unsigned short rxfnc_argc;
RXSTRING *rxfnc_argv ;
RXSTRING rxfnc_retc ;
} RXFNCCAL_PARM ;
The significance of each variable is:
[rxfnc_flags.rxfferr]
Is an output parameter that is set on return in order to inform the interpreter that the function or subroutine was incorrectly called, and thus the SYNTAX condition should be raised.
[rxfnc_flags.rxffnfnd]
Is an output parameter that tells the interpreter that the function was not found. Note the inconsistency: it is only effective if tthe exit handler returns RXEXIT_HANDLED, which looks like a logic contradiction to setting the not-found flag.
[rxfnc_flags.rxffsub]
Is an input parameter that tells the exit handler whether it was called for a function or subroutine call. If set, the call being handled is a subroutine call and returning a value is optional; else it was called for a function, and must return a value in rxfnc_retc if RXEXIT_HANDLED is to be returned.
[rxfnc_name]
Is a pointer to the name of the function or subroutine to be handled, stored as a character array. This is an input parameter, and its length is given by the rxfnc_namel parameter.
[rxfnc_namel]
Holds the length of rxfnc_name. Note that the last character is the letter ell, not the number one.
[rxfnc_que]
Points to a character array holding the name of the currently active queue. This is an input parameter. The length of this name is given by the rxfnc_quel field.
[rxfnc_que1]
Holds the length of rxfnc_que. Note that the last character is the letter ell, not the number one.
[rxfnc_argc]
Is the number of arguments passed to the function or subroutine. It defines the size of the array pointed to by the rxfnc_argv field.
[rxfnc_argv]
Points to an array holding the parameters for the routines. The size of this array is given by the rxfnc_argc field. If rxfnc_argc is zero, the value of rxfnc_argv is undefined.
[rxfnc_retc]
Holds an RXSTRING structure suitable for storing the return value of the handler. It is the responsibility of ht ehandler to allocate space for the contents of this string (i.e. the array pointed to by the rxfnc_retc.strptr).
The main function code for this exit handler is given by the symbolic name RXCMD. It is called whenever the interpreter is about to call a subcommand, i.e. a command to an external environment. It has only one subfunction: RXCMDHST.
The ParmBlock parameter for this subfunction has the following definition:
typedef struct {
typedef struct {
unsigned int rxfcfail:1 ;
unsigned int rxfcerr:1 ;
} rxcmd_flags ;
unsigned char *rxcmd_address ;
unsigned short rxcmd_addressl ;
unsigned char *rxcmd_dll ;
unsigned short rxcmd_dll_len ;
RXSTRING rxcmd_command ;
RXSTRING rxcmd_retc ;
} RXCMDHST_PARM ;
The significance of each variable is:
[rxcmd_flags.rxfcfail]
If this flag is set, the interpreter will raise a FAILURE condition at the return of the exit handler.
[rxcmd_flags.rxfcerr]
Like the previous, but the ERROR condition is raised instead.
[rxcmd_address]
Points to a character array containing the name of the environment to which the command normally would be sent.
[rxcmd_addressl]
Holds the length of rxcmd_address. Note that the last character is the letter ell, not the number one.
[rxcmd_dll]
Defines the name for the DLL which is to handle the command. I'm not sure what this entry is used for. It is not currently in use for Regina.
[rxcmd_dll_len]
Holds the length of rxcmd_dll. If this length is set to zero, the subcommand handler for this environment is not a DLL, but an EXE handler.
[rxcmd_command]
Holds the command string to be executed, including command name and parameters.
[rxcmd_retc]
Set by the exit handler to the string which is to be considered the return code from the command. It is assigned to the special variable RC at return from the exit handler. The user is responsible for allocating space for this variable. I have no clear idea what happens if rxcmd_retc.strptr is set to NULL; it might set RC to zero, to the null string, or even drop it.
It seems that this exit handler is capable of raising both the ERROR and the FAILURE conditions simultaneously. I don't know whether that is legal, or whether only the FAILURE condition is raised, since the ERROR condition is a sort of subset of FAILURE.
Note that the return fields of the parameter block are only relevant if the value RXEXIT_HANDLED was returned. This applies to the rxcmd_flags and rxcmd_retc fields of the structure.
The external data queue exit handler is used as a hook for operations manipulating the external data queue (or the stack). Unfortunately, the stack is a borderline case of what is relevant to the REXX SAA API. Operations like putting something on, retrieving a string from, obtaining the size, etc. of the stack is not part of the SAA API.
However, some of this functionality is seemingly here; but not all. For instance for the RXMSQPLL subfunction, SAA API is called by the interpreter before the interpreter calls whatever system-specific call is available for retrieving a string from the stack.
Thus the SAA API can be used by an application to provide the interpreter with a fake stack, but it is not a suitable means for the application itself to manipulate the real stack.
The RXMSG exit has four subfunctions:
[RXMSQPLL]
This is called before a line is retrieved from the stack and the application may itself provide the interpreter with an alternative line. On entry, the third parameter points to a structure having the following definition:
typedef struct {
RXSTRING rxmsq_retc;
} RXMSQPLL_PARM;
The rxmsq_retc field holds the string to be retrieved from the stack. Note that it is an output parameter, so its value on entry is undefined.
[RXMSQPSH]
This is called before the interpreter puts a line on the stack, and it may grab the line itself, and thus prevent the interpreter from putting the line on the stack. Note that this exit handles both pushing and queuing. The third parameter is:
typedef struct {
struct {
unsigned rxfmlifo: 1;
} rxmsq_flags;
RXSTRING rxmsq_value;
} RXMSQPSH_PARM;
Here the field rxmsq_value holds the string to be put on the stack. Whether the string is to be pushed or queued is determined by the boolean value rxmsq_flags.rxmlfifo, which is TRUE if the string is to be pushed.
All values are input values. What happens if you change them is not defined in the SAA API. Some implementations may let you modify the contents of rxmsq_value and return RXEXIT_NOT_HANDLED and the string push by the interpreter contains the modified string. However, you should not rely on this since it is highly incompatible. You may not de-allocate rxmsq_value.
[RXMSQSIZ]
this is called before the interpreter tries to determine the size of the stack, and it may present an alternative size to the interpreter. The third parameter is:
typedef struct {
ULONG rxmsq_size;
} RXMSQSIZ_PARM;
The field rxmsq_size can be set to the number the application wants the QUEUED() function to return. Note that this parameter is undefined on entry, so it cannot be used to retrieve the number of lines on the stack.
[RXSQNAM]
This is called before the interpreter tries to retrieve the name of the current stack, and it may present the interpreter with an alternative name. Note that this functionality is part of SAA but not TRL; it supports the Get option of the RXQUEUE() built-in function. Note that there are no other exits supporting the other options of RXQUEUE(). The third parameter for this exit is:
typedef struct {
RXSTRING rxmsq_name;
} RXMSQNAM_PARM;
As with RXSQMSIZ, the field rxmsq_name can be set to the name which the application wants to return to the interpreter as the name of the current stack. Note that this is an output-only parameter; its value on input is undefined, and in particular is not the name of the real stack.
Note that this area is troublesome. In TRL, external data queues are not defined as part of the language, while in SAA it is. Thus, TRL-compliant interpreters are likely to implement stacks in various ways that may not be compatible with the SAA.
The main code for this exit handler has the symbolic value RXSIO. There are four sub-functions:
[RXSIODTR]
Called whenever the interpreter needs to read a line from the user during interactive tracing. Note the difference between this subfunction and RXSIOTRD.
[RXSIOSAY]
Called whenever the interpreter tries to write something to standard output in a SAY instruction, even a SAY instruction without a parameter.
[RXSIOTRC]
Called whenever the interpreter tries to write out debugging information, e.g. during tracing, as a trace back, or as a syntax error message.
[RXSIOTRD]
Called whenever the interpreter need to read from the standard input stream during a PULL or PARSE PULL instruction. Note that it will not be called if there is sufficient data on the stack to satisfy the operation.
Note that these function are only called for the exact situations that are listed above. e.g. the RXSIOSAY is not called during a call to the REXX built-in function LINEOUT() that writes to the default output stream. TRL says that SAY is identical to calling LINEOUT() for the standard output stream, but SAA API still manages to see the difference between stem variables and compound variables with a ``zero-length-string'' tail. Please bear with this inconsistency.
Depending on the subfunction, the ParmBlock parameter will have four only slightly different definitions. It is kind of frustrating that the ParmBlock takes so many different datatypes, but it can
be handled easily using unions, see a later section. The definitions are:
typedef struct {
RXSTRING rxsiodtr_retc ; /* the interactive trace input */
} RXSIODTR_PARM ;
typedef struct {
RXSTRING rxsio_string ; /* the SAY line to write out */
} RXSIOSAY_PARM ;
typedef struct {
RXSTRING rxsio_string ; /* the debug line to write out */
} RXSIOTRC_PARM ;
typedef struct {
RXSTRING rxsiotrd_retc ; /* the line to read in */
} RXSIOTRD_PARM ;
In all of these, the RXSTRING structure either holds the value to be written out (for RXSIOSAY and RXSIOTRC), or the value to be used instead of reading standard input stream (for RXSIOTRD and RXSIODTR). Note that the values set by RXSIOTRD and RXSIODTR are ignored if the exit handler does not return the value RXEXIT_HANDLED.
Any end-of-line marker are stripped off the strings in this context. If the exit handler writes out the string during RXSIOSAY or RXSIOTRC, it must supply any end-of-line action itself. Similarly, the interpreter does not expect a end-of-line marker in the data returned from RXSIODTR and RXSIOTRD.
The space used to store the return data for the RXSIODTR and RXSIOTRD sub-functions, must be provided by the exit handler itself, and the space is not de-allocated by the interpreter. The space can be reused by the application at any later time. The space allocated to hold the data given by the RXSIOSAY and RXSIOTRC sub-functions, will be allocated by the interpreter, and must neither be de-allocated by the exit handler, nor used after the exit handler has terminated.
Note: Because the RXHLT exit handler is called after every REXX instruction, enabling this exit slows REXX program execution.
The main code for this exit handler has the symbolic value RXHLT. There are two sub-functions:
[RXHLTTST]
Called whenever the interpreter polls externally raised HALT conditions; ie after every REXX instruction.
The definition of the ParmBlock is:
typedef struct {
unsigned rxfhhlt : 1 ;
} RXHLTTST_PARM ;
The rxfhhlt parameter is set to the state of the HALT condition in the interpreter; either TRUE or FALSE.
[RXHLTCLR]
Called to acknowledge processing of the HALT condition when the interpreter has recognized and raised a HALT condition.
RXTER and this exit handler are a bit different from the others. RXINI provides the application programmer with a method of getting control before the execution of the script starts. Its main purpose is to enable manipulation of the variable pool in order to set up certain variables before the script starts, or set the trace mode.
It has only one subfunction, RXINIEXT, called once during each call to RexxStart(): just before the first REXX statement is interpreted. Variable manipulations performed during this exit will have effect when the script starts.
As there is no information to be communicated during this exit, the value of ParmBlock is undefined. It makes no difference whether you return RXEXIT_HANDLED or RXEXIT_NOT_HANDLED, since there is no situation to handle.
This exit resembles RXINI. Its sole subfunction is RXTEREXT, which is called once, just after the last statement of the REXX script has been interpreted. The state of all variables are intact during this call; so it can be used to retrieve the values of the variables at the exit of a script. (In fact, that is the whole purpose of this exit handler.)
Like RXINI, there is no information to be communicated during the exit, so ParamBlock is undefined in this call. And also like RXINI, it is more of a hook than an exit handler, so it does not matter whether you return RXEXIT_HANDLED or RXEXIT_NOT_HANDLED.
This System Exit is specific to Regina, so caution should be exercised if you plan on making your code portable to other Rexx interpreters.
The main code for this exit handler has the symbolic value RXENV. There are four sub-functions:
[RXGETENV]
Called whenever the BIF; VALUE() is called to obtain a value from the external environment. i.e. the call to VALUE() is of the form:
VALUE('VARNAME', ,'ENVIRONMENT').
[RXSETENV]
Called whenever the BIF; VALUE() is called to set a value in the external environment. i.e the call to VALUE() is of the form:
VALUE('VARNAME',newvalue,'ENVIRONMENT').
[RXGETCWD]
Called whenever the current working directory is needed to be obtained from the environment. The DIRECTORY() BIF respects this system exit.
[RXSETCWD]
Called whenever the current working directory is changed by a call to DIRECTORY() or CHDIR() BIFs.
The ParmBlock parameter has the following definitions for each sub-function type:
typedef struct {
RXSTRING rxenv_name ; /* the name of the external environment variable */
RXSTRING rxenv_value ; /* the returned value of the external environment variable */
} RXGETENV_PARM ;
typedef struct {
RXSTRING rxenv_name ; /* the name of the external environment variable */
RXSTRING rxenv_value ; /* the value of the external environment variable */
} RXSETENV_PARM ;
In both of these, the RXSTRING; rxenv_name structure holds the name of the environment variable as known by the external environment. Note that the values set by RXSIOTRD and RXSIODTR are ignored if the exit handler does not return the value RXEXIT_HANDLED.
The space used to store the return data for the RXSIODTR and RXSIOTRD sub-functions, must be provided by the exit handler itself, and the space is not de-allocated by the interpreter. The space can be reused by the application at any later time. The space allocated to hold the data given by the RXSIOSAY and RXSIOTRC sub-functions, will be allocated by the interpreter, and must neither be de-allocated by the exit handler, nor used after the exit handler has terminated.
The external queue interface provide a mechanism for interacting with the interpreter's external queues. This interface is nalogous to a Rexx program's use of PUSH, QUEUE, PULL, and RXQUEUE(). Note that this interface only works with the external queues, it cannot interface to the internal named queues that exists within the interpreter.
This function is used to create a new, named, external queue.
The prototype for RexxCreateQueue() is:
APIRET APIENTRY RexxCreateQueue(
PSZ Buffer,
ULONG BuffLen,
PSZ RequestedName,
ULONG *DupFlag
) ;
The following parameters are input, and their significance are:
[RequestedName]
Points to an ASCII NUL terminated character string which specifies the name of the queue to be created. See for the structure of a queue name. If the user wishes to have the interpreter create a unique queue name on the local queue server at the default port number, then this value should be set to NULL. To request an interpreter-generated queue name, on the machine fred listening on port 5858, then specify @fred:5858. Ie leave the queue name portion blank.
The following parameters are output, and their significance are:
[Buffer]
Points to an ASCII NUL terminated character string allocated by the user. The name of the queue that is created will be copied into this area.
[BuffLen]
Specifies the size of the memory area pointed to by Buffer.
[DupFlag]
Indicates if the queue name that was requested, already existed. If a queue name was specifed, and the queue already existsed, DupFlag is set to RXQUEUE_DUP, otherwise it is set to 0.
The RexxCreateQueue() returns an unsigned long, which carries status information describing the outcome of the operation. The status will be one of the RXQUEUE values:
[RXQUEUE_OK]
The queue was successfully created.
[RXQUEUE_NOEMEM]
The queue was not created, due to lack of memory.
This function is used to delete a named, external queue.
The prototype for RexxDeleteQueue() is:
APIRET APIENTRY RexxDeleteQueue(
PSZ QueueName
) ;
The only parameters is an input, and its significance is:
[QueueName]
Points to an ASCII NUL terminated character string which specifies the name of the queue to be deleted. See for the structure of a queue name.
The RexxDeleteQueue() returns an unsigned long, which carries status information describing the outcome of the operation. The status will be one of the RXQUEUE values:
[RXQUEUE_OK]
The queue was successfully deleted.
[RXQUEUE_NOTREG]
The queue name specified does not exist.
[RXQUEUE_BADQNAME]
The queue name was not specified.
This function is used to determine the number of items that are available on the named, external queue.
The prototype for RexxQueueQueue() is:
APIRET APIENTRY RexxQueryQueue(
PSZ QueueName,
ULONG *Count
) ;
One parameters is an input, and its significance is:
[QueueName]
Points to an ASCII NUL terminated character string which specifies the name of the queue to be queried. See for the structure of a queue name.
The following parameter is output, and its significance is:
[Count]
Points to an unsigned long which indicates the number of items on the specified queue.
The RexxQueryQueue() returns an unsigned long, which carries status information describing the outcome of the operation. The status will be one of the RXQUEUE values:
[RXQUEUE_OK]
The queue was successfully queried, and Count contains the number of items on the queue.
[RXQUEUE_NOTREG]
The queue name specified does not exist.
[RXQUEUE_BADQNAME]
The queue name was not specified
This function is used to determine add an item to a named, external queue.
The prototype for RexxAddQueue() is:
APIRET APIENTRY RexxAddQueue(
PSZ QueueName,
PRXSTRING EntryData,
ULONG AddFlag
) ;
All parameters are input, and their significance are:
[QueueName]
Points to an ASCII NUL terminated character string which specifies the name of the queue on which the data is to be added. See for the structure of a queue name.
[EntryData]
Points to a RXSTRING structure containing the data to be added to the queue.
[AddFlag]
Indicates how the data is to be added. Can be one of:
RXQUEUE_FIFO, to indicate that the data is to be added in a first-in-first-out order. This is equivalent to the QUEUE keyword.
RXQUEUE_LIFO, to indicate that the data is to be added in a last-in-first-out order. This is equivalent to the PUSH keyword.
The RexxAddQueue() returns an unsigned long, which carries status information describing the outcome of the operation. The status will be one of the RXQUEUE values:
[RXQUEUE_OK]
The data was successfully added to the specified queue.
[RXQUEUE_NOTREG]
The queue name specified does not exist.
[RXQUEUE_BADQNAME]
The queue name was not specified
This function is used to extract an item from the specified named, external queue. When successful, the item from the queue is returned, and that item deleted from the queue.
The prototype for RexxPullQueue() is:
APIRET APIENTRY RexxPullQueue(
PSZ QueueName,
PRXSTRING DataBuf,
PDATETIME TimeStamp,
ULONG WaitFlag
) ;
The following parameters are input, and their significance are:
[QueueName]
Points to an ASCII NUL terminated character string which specifies the name of the queue from which the data is to be extracted. See for the structure of a queue name.
[WaitFlag]
Indicates if the process should wait until there is data in the specified queue before returning. This could cause the process to block forever, if no data is due in the queue. Regina does not support this option at this stage; RXQUEUE_NOWAIT is assumed Value can be one of:
RXQUEUE_WAIT, the process is to block and wait for data if the queue is currently empty.
RXQUEUE_NOWAIT, the process does not wait for data in the queue if it is currently empty. RexxPullQueue() will return RXQUEUE_EMPTY if there is no data in the queue.
The following parameters are output, and their significance are:
[DataBuf]
Points to a RXSTRING structure into which the contents of the extracted item are placed. The memory associated with the RXSTRING strptr, should be deallocated using RexxFreememory().
[TimeStamp]
Points to a PDATETIME structure, which on return, contains the time details of when the item was added to the external queue. Regina does not support this option at this stage.
The RexxPullQueue() returns an unsigned long, which carries status information describing the outcome of the operation. The status will be one of the RXQUEUE values:
[RXQUEUE_OK]
The data was successfully added to the specified queue.
[RXQUEUE_NOTREG]
The queue name specified does not exist.
[RXQUEUE_BADQNAME]
The queue name was not specified
[RXQUEUE_EMPTY]
The queue was empty and RXQUEUE_NOWAIT was specified.
[RXQUEUE_BADWAITFLAG]
The value of the WaitFlag parameter was not RXQUEUE_WAIT or RXQUEUE_NOWAIT.
The macro space interface provide a mechanism for pre-loading external Rexx programs into the current interpreter's macro space, so that the macros can be executed faster than reading them from disk each time they are called. This interface is not available in Regina at this stage.
For several of the functions described in this chapter, the application calling them must allocate or de-allocate dynamic memory. Depending on the operating system, compiler and REXX interpreter, the method for these allocations and de-allocations vary. Because of this, Regina supplies the API function calls RexxAllocateMemory() and RexxFreeMemory(). These functions are wrappers for the appropriate compiler or operating system memory functions.
The prototype for RexxAllocateMemory() is:
PVOID APIENTRY RexxAllocateMemory(
ULONG size
) ;
The parameter is an input, and its significance is:
[size]
The number of bytes of dynamic memory requested.
RexxAllocateMemory() returns a pointer to the newly allocated block of memory, or NULL if no memory could be allocated.
The prototype for RexxFreeMemory() is:
APIRET APIENTRY RexxFreeMemory(
PVOID MemoryBlock
) ;
The parameter is an input, and its significance is:
[MemoryBlock]
A void pointer to the block of memory allocated by the interpreter, or allocated by a previous call to RexxAllocateMemory().
RexxFreeMemory() always return 0.
This section describes the RexxCallBack() function, which allows the application to execute a procedure within the running REXX program. This function is particularly useful for building a Rexx interface to those library applications that operate using a callback mechanism.
A callback mechanism is one where certain events within a particular application can be connected to a particular function, so that when a particular event occurs, the connected function is executed. Many C library applications use a callback mechanism.
This function is specific to Regina, so caution should be exercised if you plan on making your code portable to other REXX interpreters.
This function is used to invoke the REXX interpreter in order to execute a piece of REXX code, which may be located on disk, as a pre-tokenized macro, or as ASCII source code in memory.
APIRET APIENTRY RexxCallBack(
PSZ ProcedureName,
LONG ArgCount,
PRXSTRING ArgList,
PUSHORT ReturnCode,
PRXSTRING Result
) ;
Of these parameters, ReturnCode and Result are output-only. The rest of the parameters are input-only. The significance of the parameters are:
[ProcedureName]
An ASCII NUL terminated string, specifying the name of the internal procedure of the running REXX script to be executed. This internal procedure name must exist or this function will return with RX_CB_BADN.
[ArgCount]
The number of parameter strings given to the procedure. This is the number of defined REXX-strings pointed to by the ArgList parameter. The default maximum number of arguments that can be pased is 32, but this can be changed by the MAX_ARGS_TO_REXXSTART macro in rexx.h.
[ArgList]
Pointer to an array of REXX-strings, constituting the parameters to this call to REXX. The size of this array is given by the parameter ArgCount. If ArgCount is greater than one, the first and last parameters are ArgList[0] and ArgList[ArgCount-1]. If ArgCount is 0, the value of ArgList is irrelevant.
If the strptr of one of the elements in the array pointed to by ArgList is NULL, that means that this parameter is empty (i.e. unspecified, as opposed to a string of zero size).
[ReturnCode]
Pointer to a SHORT integer where the return code from the called ProcedureName is stored, provided that the returned value is numeric, and within the range -(2**15) to 2**15-1. I don't know what happens to ReturnCode if either of these conditions is not satisfied. It probably becomes undefined, which means that it is totally useless since the program has to inspect the return string in order to determine whether ReturnCode is valid.
[Result]
Points to a REXX string into which the result string from the called ProcedureName is written. The caller may or may not let the strptr field be supplied. If supplied (i.e. it is non-NULL), that area will be used, else a new area will be allocated. If the supplied area is used, its size is supposed to be given by the strlength field. If the size if not sufficient, a new area will be allocated, by RexxAllocateMemory(), and the caller must see to that it is properly de-allocated using RexxFreeMemory().
Note that the ArgCount parameter need not be the same as the ARG() built-in function would return. Differences will occur if the last entries in ArgList are null strings.
The arguments passed to the ProcedureName will be passed indivdually. i.e. the PARSE ARG command in the ProcedureName must use commas to separate the arguments.
The valid return values from RexxCallBack() are:
[Negative]
indicates that a syntax error occurred during interpretation. In general, you can expect the error value to have the same absolute value as the REXX syntax error (but opposite signs, of course).
[Zero, or RX_CB_OK]
indicates that the interpreter finished executing the procedure without errors.
[Positive]
indicates probably that some problem occurred, that made it impossible to execute the procedure, e.g. a bad parameter value. The values that can be return are:
RX_CB_BADP bad parameters
RX_CB_NOTSTARTED there is no running REXX program
RX_CB_TOOMANYP too many parameters supplied
RX_CB_BADN the ProcedureName does not exist
During the course of an execution of RexxCallBack(), subcommand handlers and exit handlers might be called. These may call any function in the application interface, including another invocation of
RexxCallBack()..
Table of Contents
Interfacing Rexx to other programs 1
1Overview of functions in SAA 1
1.1Include Files and Libraries 1
1.2Preprocessor Symbols 2
1.3Data structures and data types 2
2The Subcommand Handler Interface 6
2.1What is a Subcommand Handler 6
2.2The RexxRegisterSubcomExe() function 7
2.3The RexxRegisterSubcomDll() function 8
2.4The RexxDeregisterSubcom() function 8
2.5The RexxQuerySubcom() function 9
3The External Function Handler Interface 11
3.1What is an External Function Handler 11
3.2The RexxRegisterFunctionExe() function 12
3.3The RexxRegisterFunctionDll() function 12
3.4The RexxDeregisterFunction() function 13
3.5The RexxQueryFunction() function 14
4Executing REXX Code 15
4.1The RexxStart() function 15
5Variable Pool Interface 19
5.1Symbolic or Direct 19
5.2The SHVBLOCK structure 19
5.3Regina Notes for the Variable Pool 23
5.4The RexxVariablePool() function 24
6The System Exit Handler Interface 26
6.1The System Exit Handler 26
6.2List of System Exit Handlers 27
7The External Queue Interface 35
7.1The RexxCreateQueue() function 35
7.2The RexxDeleteQueue() function 36
7.3The RexxQueryQueue() function 36
7.4The RexxAddQueue() function 37
7.5The RexxPullQueue() function 37
8The Macro Space Interface 40
8.1The RexxAddMacro() function 40
8.2The RexxDropMacro() function 40
8.3The RexxSaveMacroSpace() function 40
8.4The RexxLoadMacroSpace() function 40
8.5The RexxQueryMacro() function 40
8.6The RexxReorderMacro() function 40
8.7The RexxClearMacroSpace() function 40
9Allocating and De-allocating Space 41
9.1The RexxAllocateMemory() function 41
9.2The RexxFreeMemory() function 41
10Calling back into running REXX Code 42
10.1The RexxCallBack() function 42