1(b). Starting the REXX interpreter
The function RexxStart invokes the interpreter to execute a REXX program.
long RexxStart(long argc,
PRXSTRING argv,
PSZ *name,
PRXSTRING instore,
PSZ envname,
long calltype,
PRXSYSEXIT exits,
PSHORT rc,
PRXSTRING result);
The parameters are:
argc: the number of arguments passed to the program
argv: an array of argc RXSTRINGs containing the arguments passed to
the program. Any omitted arguments should be passed as NULL
strings.
name: a name for the program. This will usually be the file name of
the program on disk (which will be searched for in the usual
way as described in the REXX programming reference, but will
not have any file extension added to it). However, if the
instore parameter contains the source of the program, then the
name parameter is used only for the PARSE SOURCE instruction.
instore: either NULL or a pointer to the first of two RXSTRINGs which
define an in-storage REXX program. If instore is NULL, then the
program is loaded from a disk file named by the name parameter.
Otherwise, instore[0] must be a valid RXSTRING which contains
the source to be executed and instore[1] must be a NULL string.
The source contained in instore[0] should be in the same format
as it would be if it had just been loaded from disk. Note that
REXX/imc will not, at present, return a tokenised image of the
program in instore[1].
envname: either NULL or a null-terminated string which contains the name
of the initial subcommand environment for the REXX program. The
environment name must be at most 30 characters in length. If
envname is NULL then a default will be chosen. This will be
the uppercased file extension, if this is present and suitable,
otherwise the string "UNIX".
calltype:one of the three integer values RXCOMMAND, RXSUBROUTINE
or RXFUNCTION as defined in rexxsaa.h. This determines
the invocation method as given by the instruction
"PARSE SOURCE . method .". In addition, if the calltype
is RXFUNCTION then any RETURN instruction which causes the
program to end will be required to return a result.
exits: either NULL or a pointer to the first in an array of RXSYSEXIT
structures defining the exits which will be used (see the
section on system exits for more information). The last item
in the array must have an exit code of RXENDLST. Exits will
not be used if the exits parameter is NULL.
rc: a pointer to a short which will receive the numeric form of the
result string. If the result string is a whole number in the
range -32767 to +32767 then it will be converted to an integer
and stored in rc. Otherwise, rc will be set to (short)(1<<15),
but note that this is an extension which is present in REXX/imc
only. This parameter is documented as PLONG, but it seems to be
PSHORT in most versions so for the time being it is a PSHORT in
REXX/imc.
result: a pointer to an RXSTRING which will receive the result string
returned by a RETURN or EXIT instruction from the REXX program,
or NULL to indicate that the return value should be discarded.
If the program did not return a result, then this parameter
will be set to a NULL string. If result points to an RXSTRING
then the caller of RexxStart may set it to either a NULL string
or a valid string. If result is a valid string which is long
enough to hold the result, then the result will be copied into
that string and result->strlength will be set to indicate its
length. Otherwise, REXX will allocate (using malloc) a string
to hold the result and the caller of RexxStart is responsible
for freeing this storage.
The possible return values from RexxStart are:
negative: a syntax error (or other REXX error) occurred. The absolute
value of the return value will be the same as the REXX error
number.
zero: the program ended normally.
positive: some error occurred which made it impossible to execute the
program. A return value of 3 means that the program could not
be read in (although this number may change in the future). A
return value of 1 means that the parameters were incorrect or
that the interpreter could not initialise properly.
Note that the interpreter will never return a result string to the caller
if an error occurred, and in this case the value of rc is undefined when
RexxStart returns.
RexxStart is recursive - that is, the application may call it from within
a subcommand handler, external function call or system exit even though a
previous call to RexxStart has still not finished. It is not, however,
re-entrant, so if the operating system supports threads or lightweight
processes (LWPs), the SAA API should only be called from within a single
thread.
1(c). The Subcommand Interface
A subcommand handler is a function supplied by the application which
processes commands (that is, REXX instructions which consist only of
expressions, or occurrences of "command" in instructions of the form
"ADDRESS environment command"). Each subcommand handler must be registered
under an environment name, and these names are selected by the ADDRESS
instruction in REXX.
A subcommand handler must be declared as a function with the following
prototype:
ULONG command_handler(
PRXSTRING command,
PUSHORT flags,
PRXSTRING result);
where:
command is the command string created by REXX. The character just
after the end of the string is guaranteed to be a null, but
the command may also contain other null characters.
flags is a pointer to a short integer to receive the completion status
of the command. The subcommand handler should set this to one
of RXSUBCOM_OK, RXSUBCOM_ERROR or RXSUBCOM_FAILURE in order to
indicate that the command completed successfully, completed with
ERROR status, or completed with FAILURE status respectively.
result is a pointer to an RXSTRING which will receive the return
code from the command in the form of a string (note that any
numeric return code must be converted to a string before it
can be passed to REXX). This string will be assigned to the
variable RC when the subcommand handler returns. If result
is a NULL string, then REXX will assign the string "0" to RC.
REXX will set result to a 256-byte string before calling the
subcommand handler. The handler may store the result in this
string (and set result->strlength to the length of the string),
or (if the result is longer than that) it may allocate another
using malloc. If the handler allocates a new string to hold
the result, then REXX will call free to release the storage
after the handler returns.
The only valid return value from a subcommand handler seems to be 0.
The following functions are available to register, deregister and query
subcommand handlers:
ULONG RexxRegisterSubcomExe(
PSZ envname,
RexxSubcomHandler *handler,
PUCHAR userarea);
The RexxRegisterSubcomExe function registers the subcommand handler
whose address is given by the parameter handler under the environment
name given by the parameter envname.
The userarea parameter may be NULL or it may point to a user-defined
eight byte area of memory which will be associated with the subcommand
handler. The user area information may be retrieved with the
RexxQuerySubcom function.
The possible return values from RexxRegisterSubcomExe are:
RXSUBCOM_OK The handler was registered.
RXSUBCOM_NOTREG The handler was not registered because there was
another already registered under the same name.
RXSUBCOM_NOEMEM The handler was not registered due to lack of
memory.
RXSUBCOM_BADTYPE The handler was not registered due to a parameter
error.
ULONG RexxDeregisterSubcom(PSZ envname, PSZ module);
The RexxDeregisterSubcom function deregisters the subcommand handler
which was registered under the name given by the parameter envname.
The module parameter is not used by REXX/imc.
The possible return values from RexxDeregisterSubcom are:
RXSUBCOM_OK The handler was deregistered.
RXSUBCOM_NOTREG The handler was not found.
RXSUBCOM_BADTYPE The handler was not deregistered due to a parameter
error.
ULONG RexxQuerySubcom(PSZ envname, PSZ module, PUSHORT flag, PUCHAR userarea);
The RexxQuerySubcom function queries any subcommand handler which might
have been registered under the name given by the parameter envname. The
module parameter is not used by REXX/imc. The result is both stored in
the short integer pointed to by the flag parameter and returned as the
result of the function. If the userarea parameter is supplied, it must
point to an eight-byte area which will receive the user area which was
given when the subcommand handler was registered.
The possible return values from RexxQuerySubcom are:
RXSUBCOM_OK The handler is registered.
RXSUBCOM_NOTREG The handler is not registered.
RXSUBCOM_BADTYPE The parameters were in error.
1(d). External Functions
An external function may be registered with REXX/imc using the SAA API.
After it has been registered, it may be called by a Rexx program as usual,
but only using the name under which it was registered. The names of
functions are case-sensitive and unquoted function names are always
translated into upper case, so that a function to be called with the REXX
function call foo() must be registered under the name FOO. This is a
difference from OS/2. Functions registered using the SAA interface of
REXX/imc are searched just after the built-in functions, and at the same
time as functions loaded from ".rxfn" files (see section 2). Function names
registered under the SAA interface of REXX/imc must not contain the slash
character (/). Note that function names in REXX function calls have all
characters up to and including the last slash character removed before they
are matched against function names which have been loaded from ".rxfn" files
or registered under the SAA interface, so that "/tmp/FOO" matches a
function registered under the name "FOO". [This was done so that the path
name of an auto-registering function may be specified, but will not
prevent the function from being found on subsequent calls when it has
already been registered. This behaviour might be removed in a future
release.]
An external function to be registered using the SAA interface must be a
function with the following prototype:
LONG function_handler(
PSZ name,
long argc,
PRXSTRING argv,
PSZ queuename,
PRXSTRING result);
where:
name is a null-terminated string giving the name by which REXX
called the function.
argc is the number of arguments passed to the function.
argv is an array of argc RXSTRINGs giving the arguments passed to
the function. Any omitted arguments will be passed as NULL
strings. The character just after the end of each argument
which is present is guaranteed to be a null, but the arguments
may also contain other null characters.
queuename is the null-terminated name of the current queue, which is
currently meaningless in REXX/imc and will equal the string
"SESSION".
result is a pointer to an RXSTRING which will receive the result from
the function. If the function does not return a result, then
it should set this RXSTRING to a NULL string. If the function
handler was called as a function, then the result will be used
directly (and an error will be raised if the function did not
return a result). If it was called as a subroutine, the result
(if any) will be assigned to the REXX variable RESULT.
REXX will set result to a 256-byte string before calling the
function. The function may copy the result into this string
(and set result->strlength to the length of the result), or
(if the result is longer than 256 bytes) it may allocate a new
string using malloc and set the result string to that. If the
function handler allocates a new string, REXX will use free to
release it when the function returns.
The valid return values from a function handler are:
0 The function completed successfully.
40 An error occurred. The REXX interpreter will raise error 40
(incorrect call to routine) when the function returns. Note
that if the function returns this value, it must not return a
result.
The following functions are available to register, deregister and query
external functions:
ULONG RexxRegisterFunctionExe(PSZ funcname, RexxFunctionHandler *handler);
The RexxRegisterFunctionExe function registers the function given by
the handler parameter under the name given by the funcname parameter,
which is a null-terminated string.
The possible return values from RexxRegisterFunctionExe are:
RXFUNC_OK The function was registered.
RXFUNC_DEFINED The function was not registered because it has already
been registered or loaded from a ".rxfn" file.
RXFUNC_NOMEM The function was not registered due to lack of memory.
ULONG RexxRegisterFunctionDll(PSZ funcname, PSZ dllname, PSZ entryname);
The RexxRegisterFunctionDll call registers a function stored in a shared
object file (or DLL). The name of the shared object is given in the
dllname parameter (see below). The name of the function handler within
the shared object is given in the entryname parameter, and the name by
which Rexx will call the function is given by the funcname parameter.
The dllname parameter may be either the exact path name of a file or a
relative file name which will be searched for along a path given by one
of the following: the REXXLIB environment variable; if that is not set
the REXXFUNC environment variable; or if neither is set the compile-time
default. If the name of the shared object ends with ".rxfn" then this
may be omitted from the dllname parameter.
This version of the RexxRegisterFunctionDll function differs from the
OS/2 version in that it attempts to load the function before returning
control to the caller, and it returns an indication of whether this
succeeded. The possible return values are:
RXFUNC_OK The function was registered.
RXFUNC_NOTREG The function was not registered because the shared object
could not be loaded or did not contain the required entry
point.
RXFUNC_DEFINED The function was not registered because it has already
been registered or loaded from a ".rxfn" file.
RXFUNC_NOMEM The function was not registered due to lack of memory.
ULONG RexxDeregisterFunction(PSZ funcname);
The RexxDeregisterFunction function deregisters the function which was
previously registered under the name given by the funcname parameter,
which is a null-terminated string. Note that this function is also
able to deregister functions which were loaded from ".rxfn" files.
The possible return values from RexxDeregisterFunction are:
RXFUNC_OK The function was deregistered.
RXFUNC_NOTREG The function was not found.
ULONG RexxQueryFunction(PSZ funcname);
The RexxQueryFunction function queries whether or not a function has
been registered or loaded from a ".rxfn" file under the name given by
the funcname parameter, which is a null-terminated string.
The possible return values from RexxQueryFunction are:
RXFUNC_OK The function is registered.
RXFUNC_NOTREG The function is not registered.
1(e). System Exits
The application may define functions which REXX will call instead of using
its own system interface in certain circumstances, such as whenever it needs
to write out a line as a result of the SAY instruction. The application
registers its system exit functions under names (which are similar to
environment names, although they occupy a separate name space) before
executing a REXX program. When the application starts REXX, it specifies
the system exits which it wishes to handle and associates each with a named
exit handler. Each system exit has a major function code and a subfunction
code; these are listed later in this section.
A system exit handler must be a function with the following prototype:
LONG exit_handler(long exitcode, long subcode, PEXIT parmblock);
where:
exitcode is the major function code of the exit which the handler has
been called to handle.
subcode is the subfunction code of the exit which the handler has been
called to handle.
parmblock is a pointer to a parameter block whose contents depend on
the exit being handled. The exit descriptions below give
details on the parameter block required for each exit. Some
exits may not require a parameter block at all, in which case
the parmblock parameter will be a NULL pointer.
Since each exit requires a different kind of parameter block,
the PEXIT data type has been declared as a union data type. It
is probably most convenient for each exit handler to convert
the parameter block pointer into a pointer of the required type
for the exit (which is allowed in C (I think!)).
The valid return values from a system exit handler are:
RXEXIT_HANDLED The exit handler processed the system exit as required.
RXEXIT_NOT_HANDLED The exit handler did not process the system exit, and
therefore REXX should behave as if the exit handler
had not been called.
RXEXIT_RAISE_ERROR An error occurred. REXX will raise error 48 (failure
in system service) when the handler returns this
value.
A system exit handler should begin by checking the function and subfunction
codes, and return immediately with RXEXIT_NOT_HANDLED if they correspond to
an exit which the handler was not supposed to handle.
The following functions are available to register, deregister and query
system exit handlers.
ULONG RexxRegisterExitExe(PSZ name, RexxExitHandler *handler, UCHAR *userarea);
The RexxRegisterExitExe function registers the system exit handler given
by the handler parameter under the name given by the name parameter,
which is a null-terminated string.
The userarea parameter may be NULL or it may point to a user-defined
eight byte area of memory which will be associated with the system
exit handler. The user area information may be retrieved with the
RexxQueryExit function.
The possible return values from RexxRegisterExitExe are:
RXEXIT_OK The handler was registered.
RXEXIT_NOTREG The handler was not registered because there is already a
system exit handler registered under the given name.
RXEXIT_NOEMEM The handler was not registered due to lack of memory.
RXEXIT_BADTYPE The handler was not registered due to a parameter error.
ULONG RexxDeregisterExit(PSZ name, PSZ module);
The RexxDeregisterExit function deregisters the system exit handler which
was registered under the name given by the name parameter, which is a
null-terminated string. The module parameter is not used by REXX/imc.
The possible return values from RexxDeregisterExit are:
RXEXIT_OK The handler was deregistered.
RXEXIT_NOTREG The handler was not found.
RXEXIT_BADTYPE The parameters were in error.
ULONG RexxQueryExit(PSZ name, PSZ module, PUSHORT flag, PUCHAR userarea);
The RexxQueryExit function queries any system exit handler which may have
been registered under the name given by the name parameter, which is a
null-terminated string. The module parameter is not used by REXX/imc.
The flag parameter is a pointer to a short integer which will be set to
the return value from RexxQueryExit. If the userarea parameter is not
NULL, it must point to an eight-byte area of memory which will receive
the user area which was given when the exit handler was registered.
The possible return values from RexxQueryExit are:
RXEXIT_OK The handler is registered.
RXEXIT_NOTREG The handler is not registered.
RXEXIT_BADTYPE The parameters were in error.
Once the required system exit handlers have been registered, they may
be used by passing them to RexxStart in the "exits" parameter. This
parameter is an array of structures of the form:
typedef struct {
char *sysexit_name; /* name of exit handler */
short sysexit_code; /* major function code of system exit */
} RXSYSEXIT;
Each of these structures associates a system exit handler which was
previously registered under the name given by the sysexit_name field
of the structure with the system exits whose major function codes agree
with the sysexit_code field of the structure. Note that an exit handler
must handle all the subfunctions of any particular system exit; it may do
this by immediately returning RXEXIT_NOT_HANDLED as described earlier.
The last element of the array of RXSYSEXIT structures must have sysexit_code
set to RXENDLST.
The possible system exits are as follows. Each major exit code is given
by a name of the form RXxxx, and each subfunction code is given by a name
of the form RXxxxyyy, where the RXxxx is the same as the major exit code.
RXCMD: Call a subcommand handler
RXCMDHST: This exit is called when REXX is about to carry out a command.
By default, REXX will call the appropriate subcommand handler
to execute the command, but the exit may instead choose to
process the command. The parameter block for this exit is:
typedef struct {
struct {
unsigned int rxfcfail:1; /* completed with FAILURE */
unsigned int rxfcerr:1; /* completed with ERROR */
} rxcmd_flags;
char *rxcmd_address; /* environment name */
unsigned short rxcmd_addressl;/* environment length */
char *rxcmd_dll; /* not used by REXX/imc */
unsigned short rxcmd_dll_len; /* not used by REXX/imc */
RXSTRING rxcmd_command; /* the command to be executed */
RXSTRING rxcmd_retc; /* the return string */
} RXCMDHST_PARM;
The handler may set either rxfcfail or rxfcerr to raise a
FAILURE or ERROR condition, respectively. The rxcmd_address
parameter is the null-terminated name of the environment to
which the command is being sent. The rxcmd_command is an
RXSTRING containing the command to be executed. The character
just after the end of the string is guaranteed to be a null,
but the command may also contain other null characters. The
rxcmd_retc is an RXSTRING which is to receive the return code
from the command as a string. Any numeric return code must be
translated into a string before it is returned. REXX will
provide a 256-byte buffer into which the return string may
be copied. The handler may allocate another using malloc, in
which case REXX will use free to release it after the handler
returns.
RXSIO: Perform input or output
RXSIOSAY: This exit is called when REXX is about to write a line as a
result of a SAY instruction. The exit may choose to deal with
the line as it requires; otherwise the interpreter will write
the line to the standard output stream. The parameter block
for this exit is:
typedef struct {
RXSTRING rxsio_string;
} RXSIOSAY_PARM;
The rxsio_string is the string to be written out. The
character just after the end of the string is guaranteed
to be a null, but the string may also contain other null
characters. The string will not contain an end-of-line
character unless one was present in the SAY instruction.
RXSIOTRC: This exit is called when REXX is about to write a line during
tracing. The exit may choose to deal with the line as it
requires; otherwise the interpreter will write the line to
the trace output stream (usually the standard error). The
parameter block for this exit is:
typedef struct {
RXSTRING rxsio_string;
} RXSIOTRC_PARM;
The rxsio_string is the string to be written out. The
character just after the end of the string is guaranteed
to be a null. The string will not contain an end-of-line
character.
RXSIOTRD: This exit is called when REXX is about to read a line of input
as a result of the PARSE PULL instruction when the REXX stack
is empty. The exit may choose to supply a line of its own;
otherwise the interpreter will read the line from the standard
input stream. The parameter block for this exit is:
typedef struct {
RXSTRING rxsiotrd_retc;
} RXSIOTRD_PARM;
The input line should be returned in the rxsiotrd_retc string.
It should not contain any end-of-line character which might
have been used to terminate the line of input (but it may
contain other end-of-line characters). REXX will provide a
256-byte string in which the handler may place the input line.
The handler may allocate its own using malloc, in which case
REXX will use free to release it when the handler returns.
RXSIODTR: This exit is called when REXX is about to read a line of input
at a pause during interactive tracing. The exit may choose to
supply a line of its own; otherwise the interpreter will read
the line from the standard input stream. The parameter block
for this exit is:
typedef struct {
RXSTRING rxsiodtr_retc;
} RXSIODTR_PARM;
The input line should be returned in the rxsiotrd_retc string.
It should not contain any end-of-line character which might
have been used to terminate the line of input. REXX will
provide a 256-byte string in which the handler may place the
input line. The handler may allocate its own using malloc, in
which case REXX will use free to release it when the handler
returns.
Note: the PARSE LINEIN instruction and the built-in functions LINEIN,
LINEOUT, CHARIN and CHARS do not call the RXSIO exit handler.
RXINI: Initialisation processing
RXINIEXT: This exit is called when initialisation is complete, just
before execution of the REXX program starts. The exit handler
may use this exit to perform initialisation functions such as
setting REXX variables. There is no parameter block for this
exit.
RXTER: Termination processing
RXTEREXT: This exit is called just after the REXX program has finished,
and just before termination processing. The exit handler
may use this exit to perform terminatino functions such as
retrieving REXX variables. There is no parameter block for
this exit.
1(f). The Variable Pool Interface
Variables in the REXX program may be accessed using the RexxVariablePool
function, which processes one or more request blocks arranged in a linked
list. Each request is a structure of the form:
typedef struct shvnode
{
struct shvnode *shvnext;
RXSTRING shvname;
RXSTRING shvvalue;
ULONG shvnamelen;
ULONG shvvaluelen;
UCHAR shvcode;
UCHAR shvret;
} SHVBLOCK;
where:
shvnext is the address of the next request block, or NULL if this is
the last request block.
shvcode is the request code. Valid request codes are listed below.
shvname is an RXSTRING containing a REXX variable name. It may be
an input or an output parameter according to the request
code.
shvnamelen is the length of the buffer which the shvname RXSTRING points
to.
shvvalue is an RXSTRING containing the value of a REXX variable.
It may be an input or an output parameter according to the
request code.
shvvaluelen is the length of the buffer which the shvvalue RXSTRING points
to.
shvret is a return code which will be set when the request has been
processed. It will be set to the OR of zero or more of the
following flags:
RXSHV_NEWV the named variable was uninitialised before the
request was processed
RXSHV_LVAR no more variables are available for an
RXSHV_NEXTV request.
RXSHV_TRUNC A returned variable name or value was truncated
because the supplied RXSTRING was too short to
hold the name or value.
RXSHV_BADN An invalid variable name was given.
RXSHV_MEMFL The request was not processed due to lack of
memory.
RXSHV_BADF An invalid function code was given.
The valid request codes and their meanings are:
RXSHV_SYSET: Symbolic set. The name given in the shvname field is
interpreted as a variable as if it were present in a REXX
program. It is set to the value given in the shvvalue
field.
RXSHV_SET: Direct set. The variable whose name matches that given in
the shvname field is set to the value given in the shvvalue
field.
RXSHV_SYFET: Symbolic fetch. The name given in the shvname field is
interpreted as a variable as if it were present in a REXX
program. Its value is fetched and stored in the shvvalue
RXSTRING. If the application has set this to a NULL string,
then REXX allocates one using malloc. The application
is responsible for freeing this storage. Otherwise,
shvvaluelen gives the maximum length of a string which REXX
can store in the shvvalue RXSTRING. If the value is longer
than this then it is truncated. The RXSHV_TRUNC flag will
then be set in shvret.
RXSHV_FETCH: Direct fetch. The value of the variable whose name matches
that given in the shvname field is fetched and stored in the
shvvalue RXSTRING, as in the RXSHV_SYFET request.
RXSHV_SYDRO: Symbolic drop. The name given in the shvname field is
interpreted as a variable as if it were present in a REXX
program. It is dropped (that is, it becomes undefined).
RXSHV_DROPV: Direct drop. The variable whose name matches that given
in the shvname field is dropped (that is, it becomes
undefined).
RXSHV_NEXTV: Fetch next variable. Each time this request is processed
the interpreter returns the name and value of a variable
which is currently accessible by the executing REXX program.
The interpreter keeps track of which variables have been
returned, and successive RXSHV_NEXTV requests will return
different variables (in no particular order) until all the
accessible variables have been returned. When there is no
variable left to return, the RXSHV_NEXTV request will set
the RXSHV_LVAR flag in shvret and will leave the returned
variable and value undefined. The information about which
variables have been returned is reset every time the REXX
program resumes execution, and also at every set, fetch or
drop request from the variable pool.
The name of each returned variable will be stored in the
shvname field of the request block. If the application
has set this to a NULL string then REXX allocates one using
malloc. The application is responsible for freeing this
storage. Otherwise, shvnamelen gives the maximum length
of a string which REXX can store in the shvname RXSTRING.
If the name is longer than this then it is truncated. The
RXSHV_TRUNC flag will then be set in shvret.
The value of each variable is returned in an analogous
manner in the shvvalue field (in exactly the same way as
for the RXSHV_SYFET and RXSHV_FETCH requests).
In the current release of REXX/imc, simple symbols and
stems are returned in (approximately) the order in which
they became defined. The compound symbols of any one stem
are returned consecutively in a similar order, just after
the stem (if any) is returned. Note that it is in general
not possible to tell the difference between a stem being
returned (for example, after the instruction foo.=3) and a
compound variable whose tail has length zero (for example,
after the instructions bar=''; foo.bar=3). However, if
"foo." is returned after any other variable with that
stem (including "foo." itself), it is always the case
that this occurrence of "foo." is a compound variable.
The converse is unfortunately not true. That is, if
"foo." is the first variable with that stem, it is not
necessarily a stem variable unless another copy of "foo."
appears, in which case the first one is a stem and the
second is a compound variable.
A symbolic variable name is one which is interpreted according to REXX
rules. That is, it must contain only letters, digits, dots, and a small
number of other characters that are valid in symbols and must not start
with a digit or dot; it is uppercased before use, and each simple symbol
component of a compound symbol (except the stem) is replaced by its value
before use.
A direct variable name is the exact name of a variable which appears in
the variable pool (and it is the kind of variable name that RXSHV_NEXTV
returns). It is the kind of name that a symbolic variable name turns into
after it has been processed as described in the above paragraph. That part
of the name up to the first dot must satisfy the conditions for a symbolic
name and must be in upper case. After the first dot, the name is allowed
to contain any character.
Variable pool requests are processed by the function:
ULONG RexxVariablePool(PSHVBLOCK RequestBlockList);
The parameter is a pointer to the first request block in the linked list.
The return value may be RXSHV_NOAVL, meaning that the variable pool API is
not available. In this case, none of the requests will have been processed.
The API is made available just before execution of a program begins (just
before the RXINIEXT exit is called) and may be used until just after the
execution finishes (just after the RXTEREXT exit is called).
If the return value is not RXSHV_NOAVL, then it will be the OR of all the
return codes which have been returned individually in the shvret fields of
the request blocks.
2(c). Writing a ".rxfn" file
A function written as a ".rxfn" file will be searched for and linked in
with the interpreter at the time when it is first requested. This gives an
advantage over using the SAA API in that SAA functions must be registered by
the application before they may be used by a REXX program - though this
is made easier by the inclusion of the RxFuncAdd REXX function in the
interpreter. In general, the function(s) in a ".rxfn" file may either be
SAA function handlers as described above or specially written functions
for REXX/imc. However, the latter type is the only one supported for
stand-alone functions; files containing only SAA functions must be supplied
as a library with a ".rxlib" file (see section 3). This is because it is
the ".rxlib" file which tells the interpreter that the functions must be
called using the SAA calling sequence (either that, or a REXX/imc-style
function must use the SAA interface to register all the SAA functions).
A ".rxfn" file should be compiled and then linked in the normal way for
shared objects. This differs between operating systems, but if the "Make"
program knows how to compile shared objects on your system then try making
rxmathfn.rxfn to see what flags it uses. For a stand-alone function the
name of the output file should be the name by which the function is to
be called with ".rxfn" appended, all in lower case. If this file is in
the rexx function search order (see the section on function or subroutine
invocation in the REXX/imc programming reference) then the file will be
loaded and linked in with the interprter automatically the first time that
the function is called. Future calls to the same function will not have to
load the function from a file.
The REXX/imc-style function is described in the remainder of this section.
It is not really recommended for new functions because of portability
issues, so skip to the next section if this is not of interest.
A REXX/imc function will need access to several of the interpreter's
functions, so its source must include "functions.h". It may also need
access to certain global variables, in which case it should include
"globals.h". Useful functions and global variables are listed below.
In addition, the function may call any routine from the API, in which
case it should include "rexxsaa.h" as described in section 1.
A REXX/imc function should be declared as:
int rxfunction(char *name, int argc);
(including the name rxfunction). The name parameter gives the name by which
the function was called, excluding any path name. That is, any part of the
name up to and including the last slash (/) will have been removed. This
may be used to distinguish between several function calls that have all
been implemented by the same routine (this is more common in a function
library; see the section on function libraries). The argc parameter gives
the number of arguments which were passed to the function. These will be
placed on the calculator stack in LIFO order. Any missing parameters will
have been stacked as strings of length -1.
The function will be expected to remove all its parameters from the
calculator stack and either
(i) place a result on the calculator stack and return 1, or
(ii) return 0 without placing anything on the calculator stack. This means
that the function has not returned a result.
However, the function may choose to return an error. In this case, it
need not remove all arguments from the calculator stack. A valid error
code is a negative number whose absolute value is the number of a REXX
error. When the function returns, that error will be reported.
REXX/imc contains a function declared as follows:
int funccall(RexxFunctionHandler *func,char *name,int argc);
This function enables a function that has a REXX/imc-style calling sequence
to call a function that has an SAA-style calling sequence. For example,
if function_handler is an external function designed for use with the SAA
API, then it can be saved as a REXX/imc-style ".rxfn" file by appending the
following function:
int rxfunction(char *name, int argc)
{
return funccall(function_handler,name,argc);
}
This gives the SAA function the advantage of being able to be loaded on
demand instead of having to be registered.
A single function stored in a REXX/imc file should not contain a public
symbol called rxdictionary; this is reserved for function libraries.
Useful functions, including functions to retrieve arguments and stack the
answer, are the following:
char *delete(len) int *len;
Deletes a string from the calculator stack, and returns an address where it
can be found. On return, len will be set to the length of the argument.
The string is guaranteed to be followed by two bytes of available memory,
so that the application may teminate it with a null character if necessary,
and the string may be written to. The string is not guaranteed to be
null-terminated when it is unstacked. The string is not actually moved in
memory, so it will be invalidated whenever the calculator stack is changed
(that means that it may not be used as an argument to stack()). However,
further calls to delete() will not corrupt the string.
int getint(flag) int *flag;
Deletes a string from the calculator stack, interprets it as an integer, and
returns the result. If the string is not a number which can be held in an
int variable, the function will die. If the string is not an integer and
the flag is non-zero, the function will die, otherwise the number will be
converted into the nearest integer.
void stack(string,len) char *string; int len;
Copies the given string of the given length on to the calculator stack.
void stackint(i) int i;
Stacks a string representing the integer i on the calculator stack.
int isnull()
Returns 1 if the next value to be unstacked is null (that is, it has
length -1 indicating an omitted parameter), and zero otherwise.
void die(rc) int rc;
Raise the error whose code is rc. The interpreter will then either halt
with diagnostics or signal to an appropriate label, according to the current
settings of "SIGNAL ON". This function never returns to its caller.
int num(minus,exp,zero,len) int *minus,*exp,*zero,*len;
Attempts to extract a number from the top value on the calculator stack,
returning it as a sequence of digits. The value is always left stacked. If
unsuccessful, num returns a negative number (except when the top value is
null, in which case num dies). Otherwise, num stores a sequence of digits
in the workspace (see below), and returns the offset from the start of the
workspace to the start of the sequence of digits (which always equals the
old value of eworkptr). The value eworkptr is updated to point past the end
of the sequence of digits. The length of the sequence is returned in len.
If the number is zero, then zero is set to 1, and otherwise it is set to 0.
If it is negative, then minus is set to 1, and otherwise 0. The exponent
stored in exp is such that if a decimal point were placed between the first
two digits of the sequence and the result were multiplied by ten to the
power exp, then the original number would be recovered.
void stacknum(num,len,exp,minus) char *num; int exp,len,minus;
A sequence of digits starting at address num and of length len is formatted
according to the Rexx rules for numerics and stacked. The exponent "exp" and
sign "minus" are interpreted according to the rules in "num" above.
The following functions from the variable interface should probably not
be used. Use the SAA API instead.
char *varget(name,namelen,len) char *name; int varlen; int *len;
The variable whose name is "name" and contains "namelen" characters is
searched for in the current symbol table. If the name is a compound symbol
or a stem, then the first character of the name must have bit 7 set. The
name is used as-is, that is, it is not translated to upper case, and if it
is a compound variable then no substitution occurs in the tail. A pointer
to the variable's value is returned, and "len" is set to its length. If the
variable has not been assigned a value then "len" and the result are zero
and the null pointer, respectively. The copy of the value which is returned
must not be changed in any way. It may be invalidated whenever the symbol
table is changed (and may not be used as a parameter to varset()).
void varset(name,namelen,value,len) char *name,*value; int namelen,len;
The parameters "name" and "namelen" name a variable and satisfy the same
conditions as in varget above. The parameters "value" and "len" describe
the position and length respectively of a string which is to be assigned to
the variable. The assignment always succeeds unless there is insufficient
memory available, in which case the routine dies.
Note: the variable name and the string may contain arbitrary characters, and
neither is validated. If you assign a value to a simple symbol called, e.g.
"blah" (in lower case) or "XYZ.123" (containing a dot), etc. then the
symbol will not be accessible to the Rexx program, but it may be recovered
using varget(), provided it has not been hidden or destroyed by a PROCEDURE
instruction.
Any other function from the Rexx source may be used, but they are [or
perhaps are not!] described elsewhere. Try in the source itself...
A function may use global data from Rexx. It may either include the
header file "globals.h" (which defines all the global variables of the Rexx
interpreter) or simply include declarations for the particular variables it
uses. Some useful variables are:
char *workptr; unsigned eworkptr,worklen;
The workspace, which may be used freely by a function. The space starts at
workptr, and is of maximum length worklen. The variable eworkptr may be
used to hold the length of the data currently stored in the workspace.
int precision,fuzz;
The setting of "NUMERIC DIGITS", and precision minus the setting of "NUMERIC
FUZZ", respectively. That is, "precision" holds the precision of ordinary
calculations, and "fuzz" holds the precision of comparison operations.
char numform;
Zero if "NUMERIC FORM SCIENTIFIC" is in effect, and one if "NUMERIC FORM
ENGINEERING".
int ppc;
The number of the instruction being interpreted.
int rxstacksock;
A file descriptor which is connected to the Rexx stack process via a socket.
FILE *ttyin,*ttyout;
Streams which may be used to communicate with the terminal.
A Rexx memory structure (for example the workspace) may be extended using
the macros mtest or dtest. These are called like functions, but note that
some arguments may be evaluated more than once or not at all.
mtest(memptr,alloc,length,extend)
Check that the desired length for the area, "length", is not greater than
the actual length, "alloc". If it is, then attempt to reallocate the area,
pointed to by "memptr", with "extend" more bytes than its previous length.
The parameters memptr and alloc will be updated to reflect the new position
and length of the area. The macro will die if not enough memory is
available.
dtest(memptr,alloc,length,extend)
Perform mtest on the four arguments, but return 0 if the area did not move,
and non-zero otherwise. In order to use this macro, the current function
must contain variables:
char *mtest_old;
long mtest_diff;
If the memory pointer has moved, then mtest_diff contains the difference
(the new pointer minus the old).
3. Writing Function Libraries for REXX/imc
Any of the four methods used in section 2 may be used to write a function
library, with some minor changes in certain circumstances. A function
library is a single file which implements several functions (although there
is nothing to say that a library must contain more than one function). Each
function library is accompanied by a ".rxlib" file, described below, which
lists the functions contained in the library.
If the file is a REXX program or a Unix program, then the same entry
point (the start of the program) is used for all the functions in the
library, and the program must examine the name by which it was called
in order to determine which function to carry out. In a Unix program,
this is argv[0] as described in section 2(d). In a REXX program, the
name may be found from PARSE SOURCE (in the fourth word).
If the file is a ".rxfn" file, then the functions have separate entry points
and must be listed in a dictionary within the file. [The only time when
a ".rxfn" file would not have a dictionary is when it contains a single
function whose entry point is called rxfunction; in that case the only
difference between a stand-alone function and a library is the presence of a
".rxlib" file, described below. The name by which Rexx calls the function
will be the name listed in the ".rxlib" file.] A ".rxfn" dictionary is a
public symbol whose name is rxdictionary and whose type is an array of
dictionary elements, thus:
typedef struct _dictionary {
char *name;
int (*function)();
} dictionary;
dictionary rxdictionary[];
Each name field must be initialised to the null-terminated name of a
function, as called by a REXX program. The names are case-sensitive and
should usually be in upper case. Each function field must be initialised
to the entry point of the function, which will have either the calling
sequence described in section 2(c) or the SAA calling sequence (whichever
choice is made, all functions should have the same calling sequence). The
last element of the dictionary array must have both fields set to NULL.
When a ".rxfn" file is opened, all the functions in the dictionary are
registered automatically and will not need to be loaded again from the file
on subsequent references.
Once the functions have been collected together in one file as described
above, the library should be placed in a directory which REXX/imc will
search for function libraries (see the section on function or subroutine
invocation in the REXX/imc programming reference). REXX/imc also needs
to be made aware of the library's contents. This is done by writing a
text file whose name is the same as that of the library (but without the
file extension such as ".rxfn" or ".rexx") with ".rxlib" appended. The
text file contains the names of the functions which may be found in the
library, separated by blanks, newlines or tab characters. The names are
case-sensitive. REXX/imc will only load a function library if the required
function name is found within this text file. The name of the function
library itself is irrelevant; REXX/imc will read all ".rxlib" files found in
the appropriate directories and store the names found inside.
If the function library is a set of functions using the SAA interface
then the first token in the ".rxlib" file must be "rxsaa:". If this is
missing then the functions will be called using the wrong protocol and the
interpreter will crash.
The set of mathematical functions, rxmathfn.*, is an example of a function
library. It is supplied in two forms: a REXX program and a ".rxfn" file.
Either will work.
The rxmathfn.rexx function library demonstrates a kludge in the library
interface: if the ".rxlib" file contains the token "rxmathfn:" then the REXX
interpreter will skip the usual procedure of resetting NUMERIC DIGITS to 9
whenever it starts an external function. The setting is still preserved,
and restored when the function returns, but if the ".rxlib" file contains
the token "rxmathfn:" then the function library can inherit the value. This
token is only meaningful when the function library is a REXX program. It is
ignored for the other forms of library.