Interfacing to ARexx Notes for DevCon '89 by William Hawes I. Introduction ARexx is a high-level language specifically designed to act as a "macro" or scripting language for other applications. By adding an interface to ARexx, any application can be made fully programmable using REXX-language macros. In the next few pages we'll examine the issues involved in designing and implementing such an interface. ARexx can interact with external programs in several ways. The "command interface" is used to communicate with an external program running as a separate task on the Amiga, and provides both a means of sharing data and a method of controlling an applications program. Commands are issued by passing messages between public message ports, forming a command "dialogue" similar to the interaction of a program with Intuition. The application receiving commands from ARexx is termed a "host application" and its public message port is called the "host address." ARexx can also interact with external applications by means of function libraries and function hosts. Function libraries provide a mechanism for calling external code as part of an ARexx program's task context. The linkages for such calls are established dynamically at run time rather than when t linked, so each function library must include an entry point to match its internal names with the function name being called. Function hosts are external tasks that manage a public message port for communicating with ARexx or other programs. ARexx imposes no constraints on the internal operations of a function host, except to require that message packets be returned with an appropriate code. For example, a function host could be used as a gateway into a network to provide a remote procedure call facility. Both function hosts and function libraries are managed by a prioritized search list. ARexx programs are launched and managed by the REXX Server, a separate task that acts as the hub for communications between ARexx and external entities. The REXX server opens and manages a public message port named ``REXX'' and provides a number of support services. Note that the server is itself a ``host application'' whose function it is to launch ARexx programs and maintain global resources. The ARexx interpreter is structured as an Amiga shared library and includes entry points specifically designed to help implement an interface to ARexx. Functions are available to create and delete message packets, argument strings, and other resources. Software developers are urged to use these library routines whenever possible, as this will minimize the code required for the interface and assure compatibility with future releases of the software. II. Overview of Operations II.A. The Command Dialogue. The basic sequence of events in the command interface begins when the host sends a command invocation message to the ARexx resident process. This is usually in response to a direct input from the user, such as a command that was not recognized as one of the primitives supported by the host. When the REXX server receives the invocation message, it spawns a new DOS process the run the macro program. The command line is parsed to extract the command token (the first word), and the interpreter searches for a file that matches the command name. Once a macro program file has been found, it is executed by the interpreter and (usually) results in one or more commands being issued back to the host application's public port. The macro program waits while each command is processed by the host, and takes appropriate actions if the return code indicates that an error occurred. Eventually the macro program finishes and returns the invocation message packet back to the host. Error handling is an important consideration in the interface design. Macro programs must receive meaningful return codes so that processing actions can be altered when errors occur. Normally, the host application should not return a message packet until the command has been processed and its error status is known. Errors in user commands might normally be reported on the screen, but errors in ARexx commands must be reported by setting the result field in the message packet. II.B. Data Structures. Most developers will need to work with only two of the data structures used by ARexx: the RexxMsg and RexxArg structures. The RexxMsg structure is an extension of an EXEC Message structure and is the message packet used for all communications with external applications. A RexxMsg will usually contain one of more argument strings, which are maintained as RexxArg structures. The RexxArg structure is passed as an offset pointer that can be treated like an ordinary string pointer. 1. Message Packets. The structure of the RexxMsg used by ARexx is diagrammed in Figure 2 below. Functions are provided in the ARexx Systems Library to create, initialize, and delete these message packets. Each packet sent from ARexx to an external program is marked with a special pointer in its name field. This can be used to distinguish the message packets from those belonging to other programs, in case a message port is being shared. Message packets are created using the CreateRexxMsg() function, and can be released using the DeleteRexxMsg() function. Note that the message packets passed by ARexx to a host application (as a command, for instance) are identical to the packets the host would use to invoke an ARexx program. This commonality of design means that only one set of functions is needed to create and delete message packets, and that external programs can use the same routines that the interpreter uses to handle the packets. STRUCTURE RexxMsg,MN_SIZE APTR rm_TaskBlock ; global pointer APTR rm_LibBase ; library pointer LONG rm_Action ; command code LONG rm_Result1 ; primary result LONG rm_Result2 ; secondary result STRUCT rm_Args,16*4 ; argument array (ARG0-ARG15) APTR rm_PassPort ; forwarding port APTR rm_CommAddr ; host address APTR rm_FileExt ; file extension LONG rm_Stdin ; input stream LONG rm_Stdout ; output stream LONG rm_avail ; reserved LABEL rm_SIZEOF ; 128 bytes Figure 1. The RexxMsg Structure 2. Argstrings. All ARexx strings are maintained as RexxArg structures, as diagrammed in Figure 2 below. Note that this actually a variable-length structure allocated for each specific string length. RexxArg structures are usually referenced by an offset pointer called an "argstring", which points to the start of the string buffer area of the structure. The string in the structure is always given a trailing null byte, so that external programs can treat argstrings like a pointer to a null-terminated string. Additional data about the string --- for example, its length --- are available at negative offsets from the argstring pointer. STRUCTURE RexxArg,0 LONG ra_Size ; allocated length UWORD ra_Length ; length of string UBYTE ra_Flags ; attribute flags UBYTE ra_Hash ; hash code STRUCT ra_Buff,8 ; buffer (argstring points here) Figure 2. The RexxArg Structure Library functions are available to create and delete argstrings, and to convert an integer value into argstring format. The function CreateArgstring(string,length) allocates a structure and copies the specified string into it, and returns an argstring pointer to the structure. DeleteArgstring(argstr) will release an argstring when it is no longer needed. II.C. Development Tools There are several example files to assist the construction of an ARexx interface. The file "fancydemo.c" is a very simple ARexx host that opens a comamnd winndow to launch programs, and then receives and processes commands from the macro program. "Minrexx" is a collection of routines for handling all aspects of the ARexx command interface, and includes code for defining and processing a table of commands. The header files "storage.h" and "rxslib.h" contain all of the data definitions required to work with ARexx, so interested developers should review these files. The binding routines for the ARexx systems library (rexxsyslib.library) are available in the file "rexxglue.o". III. Receiving ARexx Commands. The minimal command interface between ARexx and an applications program requires only a public message port and a routine to process the commands received. For most host applications this will require little extra machinery, as the program will probably already have several message ports for key and menu events, timer messages, and so on. The specific choice of which commands to support is always left up to the applications designer, as ARexx imposes no restrictions on the structure of the commands that can be issued. III.A. Processing the Command An ARexx macro program issues a command to a host by sending a message packet to the host's public port. The structure of these message packets is shown in Figure 1. The rm_Action field will be set to the constant RXCOMM, and the ARG0 parameter slot will contain the command as an argstring pointer. Parameter slots ARG1-ARG15 are not used for command messages. Although the internal processing of a command is entirely up to the applications designer, for the sake of discussion we'll assume that a table-driven dispatcher is used to decode and process the commands. As each message is received, the command string (in slot ARG0) is first parsed to extract the command token. The command token is then compared to a series of strings in a table of known commands until a match is found. The matching table entry then supplies the address of the internal function to call to process the commands, plus any argument processing information that may be required. III.B. Result Fields When the host program finishes processing the command, it must set the primary result field rm_Result1 to zero if no errors occurred or to an error value otherwise. The value placed in this the field will be assigned to the ARexx special variable RC in the macro program, so the error values should be chosen to indicate the type and severity of the error. Return codes should generally be chosen to follow the model of an error severity level, with small integers representing relatively harmless conditions and larger values indicating progressively more severe errors. This will allow a characteristic failure level to be established within a macro program, so that insignificant errors can be ignored. The choice of the specific return code values is left to the applications designer, as ARexx imposes no constraint on the error values chosen. However, Figure 3 provides some general guidelines for error levels. Error Level Connotation ----------- ----------- 1 - 9 Warnings or Minor Errors 10-19 Moderate Errors 20 + Serious Errors Figure 3. Recommended Error Severity Levels The secondary result field rm_Result2 must be set to zero unless a result string (as described below) is being returned. The packet can then be returned to the sender using the EXEC function ReplyMsg(). Except for setting the result fields rm_Result1 and rm_Result2, the host application should not alter the message packet. III.C. Result Strings. In some cases a macro program may request a result string rather than just a return code. It does this using the OPTIONS RESULTS instruction, which sets the RXFB_RESULT modifier bit in the command code. If possible, the host application should then provide a result string as an argstring pointer in the secondary result field rm_Result2. A result string should only be returned if explicitly requested and if no errors occurred during the call (rm_Result1 set to zero.) Failure to observe these rules will result either in memory loss or in corruption of the system free-memory list. III.D. Unknown Commands. What should the application do if it receives an unknown command? There are two basic courses of action here. The simpler (and less interesting, of course) is to reply the command with an error indicating "unknown command" and then return to normal processing. In this mode of operation the application accepts only its own internal primitives, and all macro programs are expected to issue just these commands. The more interesting course of action is to reissue the command back to the REXX server, just as though it had originated internally. This allows macro commands to invoke other macro commands, so that a full hierarchy of command procedures can be built. The first invocation message should be saved in an internal queue so that it can be replied once the reissued command has finished. IV. Launching ARexx Programs ARexx programs are launched by sending a message packet to the REXX server process. The client application creates and initializes a message packet that describes the program to be run, and the REXX server then creates a new ARexx task to execute the program. After sending the invocation message the client can return to its normal activities --- presumably an event wait loop --- to await ARexx commands and other input events. IV.A. Message Packets. ARexx uses the same RexxMsg structure for launching programs and issuing commands, as shown in Figure 1. This structure provides fields for passing arguments and for specifying overrides to various internal defaults. The structure is cleared when allocated using CreateRexxMsg(), and the client-supplied fields are never altered by ARexx. Message packets can be reused after being returned, and generally only one is required. The function CreateRexxMsg() allows the caller to specify a replyport, a host address string, and a file extension string with the allocation. The other required fields can be filled in using the returned structure pointer. The RexxMsg structure includes several fields that can be used to override various defaults when a program is invoked. These extension fields can be filled in selectively, and only the non-zero values will override the corresponding default. ARexx never modifies the extension area. Host applications should supply values for the file extension and host address fields of the message packet. The file extension affects which program files will match a given command name, and allows macro programs specific to the host to be given distinctive names. The host address must refer to a public message port, and will usually indicate the host's own port. Any appropriate (but usually short) strings can be chosen for these values. Often, the name of the applications program itself can be used as its host address and file extension. The following fields should be initialized in the invocation message: IV.A.1. Command (Action) Code. The rm_Action field of the message packet determines the mode of invocation for the program, and is set to either RXCOMM or RXFUNC for command or function mode, respectively. Although ARexx programs may be invoked as either a command or as a function, the command mode of invocation is more frequently used, as it requires setting only a few fields in the message packet. Several modifier flags can be ORed into the command code to select special options; these are described later on. IV.A.2. Argument Strings. The command string must be supplied as an argstring pointer and is installed in the ARG0 slot of the message packet. Strings can be conveniently packaged into argstrings using the CreateArgstring(strptr,length) library function, which takes a string pointer and a length as its arguments. Argstrings point to a null-terminated string and may be treated like an ordinary string pointer in most cases. In principle, a host application could build the argstrings directly, but since the strings must remain unchanged for the duration of an ARexx program, the host might need to maintain many such structures. The argstring pointer returned by CreateArgstring() is installed in the appropriate parameter slot of the message packet: ARG0 for the command string or function name, and ARG1-ARG15 for argument strings. Argstrings can be recycled after a packet has returned by calling the DeleteArgstring() function. IV.A.3. Host Address. The rm_CommAddr field provides a default initial host address for the program, which would otherwise be set to "REXX". The host address is the name of the message port to which commands will be directed, and is supplied as a pointer to a null-terminated string. Since most macro programs will want to send commands back to the program that launched them, the host applications should provide a value for rm_CommAddr. Applications that support multiple instances of user data will usually create a separate hostport for each instance; the name of this port would then be supplied as the host address for any commands issued from that instance. IV.A.4. File Extension. The rm_FileExt field provides a default file extension for the ARexx program, which would otherwise use the string ".rexx". The file extension is a convenient method for distinguishing the names of the macro programs specific to that application. It is supplied as a pointer to a null-terminated string. IV.A.5. Input and Output Streams. The default input and output streams for an ARexx program are inherited from the host application's process structure, if the host is a process rather than just a task. One or both of these streams may be overridden by supplying an appropriate value in the rm_Stdin or rm_Stdout fields; effectively these behave like redirected input/output for a DOS command. The values supplied must be valid DOS filehandles, and must not be closed while the program is executing. The streams are installed directly into the program's process structure, replacing the prior values. In the event that an ARexx program is invoked by an EXEC task, rather than by an DOS process, the rm_Stdin and rm_Stdout streams are the only way that the launched program can be given default I/O streams. The output stream is also used as the default tracing stream for the program. If interactive tracing is to be used in a program, the output stream should refer to a console device, since it will be used for input as well. IV.B. Sending the Packet. Once the required fields have been filled in, the host application can send the packet to the "REXX" public port using the EXEC function PutMsg(). The address of the "REXX" port can be obtained by a call to the FindPort() function, but this address should not be cached internally, since the REXX server could be closed at any time. To be absolutely safe, the calls to FindPort() and PutMsg() should be bracketed by calls to the EXEC routines Forbid() and Permit(). This will exclude the slight possibility that the message port could close in the few microseconds before the message packet is actually sent to the port address. After sending the packet the host can return to its normal processing, since the macro program will execute as a separate task. In most cases it will be advisable to ``lock-out'' further user commands while the ARexx program is running, to preserve the integrity of any shared data structures that may be accessed externally. IV.C. Command Modifier Flags There are several flags that can be set in the command code (rm_Action) field of an invocation. IV.C.1. RXFB_TOKEN In the command mode of invocation the host supplies a command string consisting of a name token followed by an argument string. ARexx parses the string to extract the command name, which is usually the name of a program file. The default action is to use the remainder of the command string as the (single) argument string for the program. This may be overridden by requesting \hp{command tokenization}, which is done by setting the |RXFB\_TOKEN| modifier flag in the action code of the message packet. In this case the entire command string will be parsed, and thus the program may have many argument strings. IV.C.2. RXFB_STRING A command string is usually interpreted as a program name followed by an argument string. ARexx allows the entire command string to be declared as a "string file" by setting the RXFB_STRING modifier flag of the action code. When this flag is set, the command is accepted as a complete ARexx program and no parsing at all is applied to the command. IV.C.3. RXFB_RESULT Command invocations do not usually request a result string, but can do so by setting the RXFB_RESULT modifier flag. In this case the result of an EXIT or RETURN instruction in the program will be returned as a string when the invocation message is replied. Note that the host application must be prepared to recycle the result string once it is no longer needed. IV.C.4. RXFB_NOIO By default ARexx will inherit the input and output streams from the client process. This action can be suppressed by setting the RXFB_NOIO flag bit in the action code. In most cases this will not be required, but would be necessary if the client could not ensure that the input and output streams would remain valid for the course of the macro program's execution. IV.D. Processing the Reply Message Every ARexx invocation message sent to the REXX port will eventually return to the replyport declared by the client. These reply messages can be processed very simply, as in most cases the client will just need to is report the results and release the message structure. IV.D.1. Interpreting the Result Fields. When an ARexx program finishes it installs values in the rm_Result1 and rm_Result2 fields of the invocation message, and then returns the message to the client. The interpretation of the result fields depends partly on the mode of invocation, as the result fields will contain either error codes or possibly a result string. If the primary result field rm_Result1 is zero, the program executed normally and the secondary field rm_Result2 will contain either zero or a pointer to a result string, assuming that one was requested (and available.) If the primary result is non-zero, it represents either an error severity level or else the return code from a command invocation. The two cases can be distinguished by examining the secondary result. If the secondary field is also non-zero, an error occurred and the secondary field is an ARexx error code. If the secondary result is zero, then the primary result is the return code passed by an EXIT or RETURN instruction in the program. The application program can use this return code either as an error indication or to initiate some particular processing action. Errors occurring in macro programs should usually be reported to the user. Explanatory messages are available for all ARexx error codes, and can be obtained by calling the ARexx Systems Library function |ErrorMsg()|. Result strings are always returned as an argstring and become the property (that is, responsibility) of the host. When the string is no longer needed, it can be released using the DeleteArgstring() function. IV.D.2. Releasing the Message Packet. Since the invocation message was originally created by the host application, it must be released after processing the errors or result string. Any argument strings must be released first using either DeleteArgstring or by calling the function ClearRexxMsg(), which recycles all of the arguments at once. The message packet can then be released using DeleteRexxMsg(). Most applications will want to maintain a count of how many ARexx invocations are outstanding, so such a count should be decremented as each reply message is released. V. REXX Variables Interface The standard ARexx command interface allows only a single result string to be passed back to a macro program, but in some cases it would be more convenient to allow for multiple result returns. For example, a database application might need to transfer an entire record description, or perhaps the values for all of the fields in a record. The REXX Variables Interface (RVI) provides such a capability. It consists of a set of functions that allow an ARexx host to manipulate a macro program's symbol table, so that the host can retrieve values for existing variables, install new values, or create completely new variables. Since any number of variables can be processed in this way, the RVI functions are very convenient for transfering information to and from a macro program. There are three functions available: CheckRexxMsg() verifies that a command message really came from ARexx, GetRexxVar() retrieves the value of a variable, and SetRexxVar() installs a new value (possibly creating the variable in the process.) The RVI functions can be called only while the ARexx host holds a pending command message issued by an ARexx program. While the command message is outstanding, the macro program is waiting for the reply and the program's symbol table is in a consistent state. All calls to the interface functions must be completed before the command message is replied. In a typical application, a macro program issues a command to the host to perform some specific action, and may pass the name of a stem variable as an argument. The host performs the command and then writes data to the appropriate variables before replying to the message. When the macro program resumes operation, it can check the values of the variables set by the host. V.A. Variable Names. Variable names are specified by a pointer to a null-terminated string and must follow the REXX language symbol conventions. Alphabetic characters must be in uppercase. The variable name is treated as a literal string, and no substitution for compound symbols is performed. The host application should document the variables affected by each command so that the macro programmer knows where to find the transferred information, and to avoid conflicts with variable names used internally. If a large number of values must be passed, it is most convenient to define them as compound variables and let the macro program pass the stem name as an argument. V.B. Calling Conventions. The RVI functions are linkable functions callable from either C or assembly language. The C language entry points have an underscore prepended to the function name to match the external name generated by the compiler. For the C language calls the following declarations apply: struct RexxMsg *message; char *variable; char *value; long length; long boolean; To use the RVI functions, you will need to include the file "rexxvars.o" when linking your application code. It is not necessary to define the ARexx library base (RexxSysBase), as the ARexx library is opened on the fly when required. V.C. C Language Calling Convention. CheckRexxMsg() Usage: boolean = CheckRexxMsg(message); This function verifies that the message pointer is a valid RexxMsg and that it came from an ARexx macro program. The validation test is more stringent than that performed by the ARexx library function IsRexx(). The latter verifies that the message is tagged as a RexxMsg structure, but not that it necessarily came from an ARexx macro program. Each macro program installs a pointer to its global data structure in the command message, and this pointer is necessary to gain access to the symbol table. The return from the function will be non-zero (TRUE) if the message is valid, and 0 (FALSE) otherwise. GetRexxVar() Usage: error = GetRexxVar(message,variable,&value); This function retrieves the current value for the specified variable name. It first validates the message using CheckRexxMsg() and then, if the message pointer is valid, retrieves the value string and passes it in the supplied return pointer. The return pointer is actually an argstring (an offset pointer to a RexxArg structure), but can be treated as a pointer to a null-terminated string. The value must not be disturbed by the host. The function return will be zero if the value was successfully retrieved and non-zero otherwise. An error code of 10 indicates an invalid message. SetRexxVar() Usage: error = SetRexxVar(message,variable,value,length); This function installs a value in the specified variable. It validates the message pointer using CheckRexxMsg() and then installs the value, creating a symbol table entry if required. The value is supplied as a pointer to a data area along with the total length; the data may contain arbitrary values and need not be null-terminated. The function return will be zero if the call was successful and non-zero otherwise. The possible error codes are given in the table below. Error Code Reason for Failure 3 Insufficient storage 9 String too long 10 Invalid message Figure 5. SetRexxVar Error Codes V.D. Assembly Language Calling Convention. The assembly language entry points are identical to their similarly-named C counterparts, but of course don't have an underscore prepended. The register usages are as follows: boolean = CheckRexxMsg(message) D0 A0 error, value = GetRexxVar(message,variable) D0 A1 A0 A1 error = SetRexxVar(message,variable,value,length) D0 A0 A1 D0 D1 Note that the value returned by GetRexxVar() is an argstring (pointer), and the value passed to SetRexxVar() is just a pointer to a data area. V.E. Code Examples and Test Program. A sample C program to exercise the RVI functions is included. VarTestC.c opens a public port named "VarTest" and waits for messages to arrive from ARexx. Each message is validated with a call to CheckRexMsg(), after which the value for A.1 is retrieved using GetRexxVar(). The program then installs the value "A-OK" in the variable STATUS and replies the message. VarTestC exits when it receives a "CLOSE" command. The ARexx program TestRVI.rexx will demonstrate the VarTestC host. First issue a "run Vartest" command and then a "rx TestRVI". The TestRVI program executes with tracing on so that you can see the results of each statement. VI. Advanced Topics In this section we'll look at some more advanced techniques. Not every application will need to use the material here, but it will help in understanding the full capabilities of ARexx. VI.A. Function Libraries External function libraries provide a general mechanism for user-defined extensions to ARexx. Function libraries can be written by users or applications developers, and any number of such libraries can be made available to ARexx. For example, a library could be used to extend the string manipulation or mathematical capabilities of the language by defining new functions. Such a library could be entirely self-contained or might call other system libraries to perform specific operations. All function libraries share a common structure, as the design follows that of the standard EXEC shared library. There are three required entry points --- Open, Close, and Expunge --- plus an entry reserved for future uses. In addition, the library must have an ARexx-specific "query" entry point, which serves to match the function name passed by ARexx with the intended routine. Typically, the query entry consists of a table of function names and code to search for the specified one. Functions libraries should be designed to be fully reentrant, since any number of ARexx programs may be running at any time. If this is not feasible due to other design constraints, then at least the query function should be made reentrant, and the library should include a lockout mechanism to prevent multiple calls to the library routines. VI.A.1. Calling Convention The query entry will be called from the ARexx program's process context with a pointer to a RexxMsg structure in register A0 and the library base in A6. The structure of the message packet is as shown in Figure 1, but note that the message packet is merely used to carry the function arguments; it is not queued at a message port and does not need to be unlinked. The name of the function to be called is carried in the ARG0 parameter slot (rm_Args[0]); it is passed as an argstring and can be treated as an ordinary string pointer. The query function must search for this function name and, if the name cannot be found, must return an error code of 1 ("Program not found") in register D0. The library will then be closed and the search continued in the next function library. It is important that the fields within the message packet not be modified, as the message packet will be passed along to the next library until the function is found. VI.A.2. Parameter Conversion If the requested function name is found, the query function may need to transform the parameters passed by ARexx into the form expected by the function. Whether the parameter strings need to be converted depends on how they are to be used. In some cases it may be sufficient just to forward a pointer to the message packet to the called function, while in other cases the query function may need to load parameters into registers or to perform conversion operations. The parameters in ARG1-ARG15 (rm_Args[1]-rm_Args[15]) are always passed as argstrings, and may be treated like a pointer to a null-terminated string. Numeric quantities are passed as strings of ASCII characters and will need to be converted to integer or floating-point format if arithmetic calculations are to be performed. The ARexx Systems Library includes a limited set of functions to do parameter conversions. The actual parameter count is contained in the low-order byte of the rm_Action field in the message packet. The count does not include the function name itself (in ARG0), but does include arguments specified as "defaults"; such arguments will have a NULL pointer in the corresponding parameter slot. VI.A.3. Returned Values The library function must return both an error code and a value string. The error code is returned in register DO, and should be 0 if no errors occurred. The value string must be returned as an argstring pointer in register A0, provided that no errors occurred during the call (D0 = 0). The mechanisms for creating the proper return values can be made part of the query function, so that all functions in the library share a common return path. VI.B. Function Hosts Function hosts are similar to function libraries, but have a slightly different interface. A function host runs as a separate task and must open a public message port. Instead of being called directly from the ARexx program' context, the program issues a message to the function host's public port. Thus, the function host is also very similar to a command host, and in fact the same public message port may be used for both commands and function calls. The REXX server itself is an example of this; it is both a command and a function host. VI.C. Communicating with the REXX Server The REXX server provides a number of support services that may be of use to the applications developer. The server can add or remove entries to the Library List and Clip List, and can open or close a global tracing console. All communications with the REXX server are handled by passing message packets, which were previously diagrammed in Figure 1. The packet has a command field that describes the action to be performed and parameter fields that are specific to the command. Message packets are processed as they are received, and are then either returned to the sender or passed along to another process, in the case of a program invocation. The packet includes two result fields that are used to return error codes or result strings. The parameter fields of the message packet may contain either (long) integer values or pointers to argument strings. String arguments are assumed to be argstring pointers unless otherwise specified. The resident process uses the standard DOS command-level conventions for the primary return code installed in rm_Result1. Minor or warning errors are indicated by a value of 5, and more serious errors are returned as values of 10 or 20. The secondary result field rm_Result2 will either be zero or an ARexx error code if applicable. Note that RXCOMM and RXFUNC messages are returned directly by the invoked macro program, rather than by the resident process. VI.C.1. Command (Action) Codes The command codes that are currently implemented in the REXX server are described below. Commands are listed by their mnemonic codes, followed by the allowed modifier flags. The final command code value is always the logical OR of the base command code value and all of the modifier flags selected. The command code is installed in the rm_Action field of the message packet. a. RXADDCON [RXFB_NONRET] This command specifies an entry to be added to the Clip List. Parameter slot ARG0 points to the name string, slot ARG1 points to the value string, and slot ARG2 contains the length of the value string. The name and value arguments do not need to be argstrings, but can be just pointers to storage areas. The name should be a null-terminated string, but the value can contain arbitrary data including nulls. b. RXADDFH [RXFB_NONRET] This command specifies a function host to be added to the Library List. Parameter slot ARG0 points to the (null-terminated) host name string, and slot ARG1 holds the search priority for the node. The search priority should be an integer between 100 and -100 inclusive; the remaining priority ranges are reserved for future extensions. If a node already exists with the same name, the packet is returned with a warning level error code. Note that no test is made at this time as to whether the host port exists. c. RXADDLIB [RXFB_NONRET] This command specifies an entry to be added to the Library List. Parameter slot ARG0 points to a null-terminated name string referring either to a function library or a function host. Slot ARG1 is the priority for the node and should be an integer between 100 and -100 inclusive; the remaining priority ranges are reserved for future extensions. Slot ARG2 contains the entry point offset and slot ARG3 is the library version number. If a node already exists with the same name, the message is returned with a warning level error code. Otherwise, a new entry is added and the library or host becomes available to ARexx programs. Note that no test is made at this time as to whether the library exists and can be opened. d. RXCOMM [RXFB_TOKEN] [RXFB_STRING] [RXFB_RESULT] [RXFB_NOIO] Specifies a command-mode invocation of an ARexx program. Parameter slot ARG0 must contain an argstring pointer to the command string. The RXFB_TOKEN flag specifies that the command line is to be tokenized before being passed to the invoked program. The RXFB_STRING flag bit indicates that the command string is a "string file." Command invocations do not normally return result strings, but the RXFB_RESULT flag can be set if the caller is prepared to handle the cleanup associated with a returned string. The RXFB_NOIO modifier suppresses the inheritance of the host's input and output streams. e. RXFUNC [RXFB_RESULT] [RXFB_STRING] [RXFB_NOIO] argcount This command specifies a function invocation. Parameter slot ARG0 contains a pointer to the function name string, and slots ARG1-ARG15 point to the argument strings, all of which must be passed as argstrings. The lower byte of the command code is the argument count; this count excludes the function name string itself. Function calls normally set the RXFB_RESULT flag, but this is not mandatory. The RXFB_STRING modifier indicates that the function name string is actually a "string file." The RXFB_NOIO modifier suppresses the inheritance of the host's input and output streams. f. RXREMCON [RXFB_NONRET] This command requests that an entry be removed from the Clip List. Parameter slot ARG0 points to the null-terminated name to be removed. The Clip List is searched for a node matching the supplied name, and if a match is found the list node is removed and recycled. If no match is found the message is returned with a warning error code. g. RXREMLIB [RXFB_NONRET] This command removes a Library List entry. Parameter slot ARG0 points to the null-terminated string specifying the library to be removed. The Library List is searched for a node matching the library name, and if a match is found the node is removed and released. If no match is found the packet is returned with a warning error code. The library node will not be removed if the library is currently being used by an ARexx program. h. RXTCCLS [RXFB_NONRET] This code requests that the global tracing console be closed. The console window will be closed immediately unless one or more ARexx programs are waiting for input from the console. In this event, the window will be closed as soon as the active programs are no longer using it. i. RXTCOPN [RXFB_NONRET] This command requests that the global tracing console be opened. Once the console is open, all active ARexx programs will divert their tracing output to this console. Tracing input (for interactive debugging) will also be diverted to the new console. Only one console can be opened; subsequent RXTCOPN requests will be returned with a warning error message. VI.C.2. Modifier Flags Command codes may include modifier flags to select various processing options. Modifier flags are specific to certain commands, and are ignored otherwise. a. RXFB_NOIO This modifier is used with the RXCOMM and RXFUNC command codes to suppress the automatic inheritance of the client's input and output streams. b. RXFB_NONRET Specifies that the message packet is to be recycled by the REXX server rather than being returned to the sender. This implies that the sender doesn't care about whether the requested action succeeded, since the returned packet provides the only means of acknowledgement. Message packets are released using the library function DeleteRexxMsg(). c. RXFB_RESULT This modifer is valid with the RXCOMM and RXFUNC commands, and requests that the called program return a result string. If the program executes an EXIT (or RETURN) instruction with an expression, the expression result is returned to the caller as an argstring. It is then the caller's responsibility to release the argstring when it is no longer needed; this can be done using the library function DeleteArgstring(). d. RXFB_STRING This modifier is valid with the RXCOMM and RXFUNC command codes. It indicates that the command or function argument (in slot ARG0) is a "string file" rather than a file name. e. RXFB_TOKEN This flag is used with the RXCOMM code to request that the command string be completely tokenized before being passed to the invoked program. Programs invoked as commands normally have only a single argument string. The tokenization process uses "white space" to separate the tokens, except within quoted strings. Quoted strings can use either single or double quotes, and the end of the command string (a null character) is considered as an implicit closing quote.