As a programming language, REXX contains a large set of built-in functions. Using these functions makes it easier to write programs in REXX. However, in specialized situations, you may find that your program needs a function that REXX does not supply. For example, there is no built-in square root function. If you need a function that REXX does not provide, you can write specialized functions in other languages and group them together under a name recognized by REXX as referring to a function package. In this way you can extend the capabilities of your REXX program by calling these functions. Writing function packages in the C language for REXX programs becomes an efficient way to enhance REXX applications.
Function packages are based on the programming concept of grouping sets
of instructions (routines), designed to perform some specific task,
outside of the mainline program. The REXX language allows calls to
routines internal to the program and external to the program. Internal
calls cause a branch to a routine identified by a statement label
within the program. External calls are made to routines that reside in
files outside both the user's program and the interpreter. Grouping
similar external routines together into a function package is the focus
of this chapter. For example, function packages typically are sets of
related functions, such as sin
, cos
, and other trigonometric
functions. Functions in a function package can share common code and
data, or each function can be independent of the others.
The REXX interpreter recognizes three function package names. RXSYSFN is supplied with REXX and contains functions that interface with CP and CMS. The other two function packages are called RXUSERFN and RXLOCFN and can be written by any REXX user. Function packages written using the C language can use either of the names RXLOCFN or RXUSERFN.
To find and execute an external function, REXX goes through a specific
search order. For example, if a REXX statement such as the following
refers to a name that is not a label or the name of a built-in
function, REXX searches for an external function with the name csqrt
:
root = csqrt(100)REXX searches for an external function by prefixing the function name with RX (RXCSQRT, in our example) and invoking it as a CMS command. If such a program is found, REXX invokes it with the argument list. However, if no such command can be found, REXX then searches for the function in either RXUSERFN or RXLOCFN.
Once called, a function in a function package can access its parameter list, get or set REXX variable values, and return a value to its caller just as any internal function can.
The next section covers the distinction between functions and subroutines, function packages as CMS nucleus extensions, and the parameter lists used between REXX and the function package routines.
csqrt
would be called as x = csqrt(4)
. To
use csqrt
as a subroutine, the call would be call csqrt(4)
.
The use of Adlen pairs is related to the way REXX handles variable
values. REXX keeps all its variable values, even numeric values, as
character strings. Each variable value is kept internally as a pointer
to a character string coupled with an int
containing the length
of
the string. For example, given the following REXX statement, x
is
a string of length 3 with the value 100:
x = 100REXX external functions must accept their parameter lists in this format and return values in this format to REXX. This is why function parameter lists are passed to the function in the form of an array of such Adlen pairs.
For more information about REXX interfaces to external functions, refer to the appropriate IBM documentation for your VM system.
cmsrxfn
creates a function package by defining a set of C
functions that can be called directly from REXX. rxeval
returns a
function result to REXX. rxresult
returns a function result to
REXX. cmsshv
provides a way of assigning values to new and
existing variables shared with REXX or of dropping REXX variables or
stems. (REXX stems allow collections of variables to be handled.) The
following three macros are provided with this function:
execset
execfetch
fetches a REXX variable.
execdrop
drops a REXX variable.
cmsstack
cmspush
cmsqueue
main
function that uses cmsrxfn
to define all the
functions in the function package. You do not need to have the
main
function return a value to CMS because cmsrxfn
sets these
return values.
rxeval
or rxresult
function to set the value of the REXX RESULT variable. The following
differences between functions and subroutines should be considered in
your program:
cmsshv
and cmsstack
functions and the execset
,
execfetch
, execdrop
, cmspush
, and cmsqueue
macros as
needed in the function package.
cmsrxfn
, REXXMAIN provides
the interface that supports the use of C programs as REXX function
packages.
Together, REXXMAIN and cmsrxfn
provide
all of the support necessary to
main
function as the argument array argv
. (Note that REXX
function packages are slightly unusual because they are always invoked
by REXX instead of from the CMS command line.)
The C program then calls the cmsrxfn
function, passing it the
argv
array and an array of pointers to C functions. The C functions
pointed to by this array are made available to REXX as external
functions. cmsrxfn
thereafter handles the transfer of control and
data between REXX and C. If a special situation occurs, such as an
ABEND under CMS, cmsrxfn
returns to its caller, which can then do
such cleanup as required.
cmsrxfn
that REXX enters on the second and subsequent calls to the
function package.
A nucleus extension entry point is created for each C function called
by REXX. The name is the name of the C function prefixed with RX
.
All of these entry points identify the same location within
cmsrxfn
. When REXX calls the function directly, this entry point is
responsible for restoring the C environment and transferring control
to the C function.
Between calls to the function package (after a function returns to REXX), the C environment is saved. Thus, open files remain open, memory stays allocated, and external variables retain their values.
For more information on the SYSTEM and SERVICE command attributes and nucleus extensions, refer to the CMS command and macro reference appropriate for your release.
<cmsexec.h>
header file, is defined as follows:
struct REXX_PLIST { char *ad; int len; };The variable
ad
points to an argument string, and len
provides
the length. Together these fields are used by REXX to map the
elements in a REXX argument array (Adlen pairs). Omitted function
arguments are denoted by a NULL
value in the ad
element. To see
how these arguments are used with the REXX plist, refer to
cmsrxfn .
cmsrxfn
, cmsrxfn
is re-entered. cmsrxfn
then
args
and subflag
) where
args
is a pointer to an array of Adlen pairs, and subflag
is an integer that is nonzero when the function is called as a
subroutine
fncv
array.
cmsrxfn
checks the return value. If
the return value is 0
, it checks to see if rxeval
or
rxresult
was called within the same module in which cmsrxfn
was called. If either result function was called within the same
module in which cmsrxfn
was called, cmsrxfn
puts the value passed to rxeval
or rxresult
(via a special control block called an EVALBLOK) in word 6 of the REXX
plist and then returns to REXX. The current C environment is then
saved for the next call. (Note that if both rxresult
and
rxeval
are called, cmsrxfn
uses the value from the last call
made.)
Caution: 370 mode
Do not issue CMS commands between main
and the call to
cmsrxfn
in 370 mode. The parameter list that is created by
REXXMAIN and passed to main
as the argument array argv
is destroyed before it can be passed to cmsrxfn
.
#include <cmsexec.h> int cmsrxfn(int argc, const char *argv[], int fncc, REXX_FNC fncv[]);
cmsrxfn
defines a set of C functions that can be called
directly from the System Product Interpreter (REXX). fncc
specifies the number of functions that can be called in this manner,
and fncv
is a pointer to an array of function pointers. Each
function pointer in the array points to a function of type REXX_FNC
that can be called by REXX. The argc
and argv
parameters are
the command-line parameters passed to the main
function in the C
function package. These parameters should not be altered before
passing them to cmsrxfn
.
cmsrxfn
is such that it does not return under usual circumstances.
Therefore, cmsrxfn
does not return the normal successful return
code of 0
. If cmsrxfn
cannot allocate enough storage for control
blocks or cannot install the module as a nucleus extension, it returns
-1. If the module is terminated by an ABEND under CMS,
cmsrxfn
returns 1
. If the module is terminated by a NUCXDROP command,
cmsrxfn
returns 2
.
RXLOCFN LOAD FUNC1This parameter list is passed to the
main
function via argc
and
argv
. These parameters should be passed directly to cmsrxfn
without modification.
REXX calls a function in a function package with a parameter list in
the form of an array of pairs of character pointers and integers, each
pair describing a parameter. (Refer to The SAS/C Library Interface and the REXX Extended Plist ).
All parameters,
including numbers, are in character format. The array is terminated
with a char *
, int
pair where the value of the char *
is REXX_LAST_AD (defined in <cmsexec.h>
), and the value of the
int
is REXX_LAST_LEN.
The result value, assigned to the REXX variable RESULT, also should be in character format.
If a C function called as a REXX function (identified as such in the
array pointed to by fncv
) returns a nonzero value via the RETURN
statement, REXX ignores any RESULT value returned by the function. To
set the REXX variable RC, use cmsshv
. To assign a value to the
REXX variable RESULT or to return a value from a C function called
as a REXX function, use rxresult
or rxeval
.
cmsrxfn
causes the
program to be installed as a REXX function package.
#include <cmsexec.h> #include <stdlib.h> #include <string.h> #include <stdio.h> static int list(); /* Declare REXX external function.*/ REXX_FNC rxfncs[] = {& list} ; void main(int argc, char *argv[] ) { int rc; /* Call cmsrxfn, passing the argc, argv parameters, the number */ /* of functions to define (1), and an array of pointers to the */ /* function(s). */ rc = cmsrxfn(argc, argv, 1, rxfncs); printf("cmsrxfn completed with return code %d\n",rc); } /* This REXX function types a note indicating whether it was */ /* called as a function or as a subroutine (via a REXX call */ /* statement ). It then lists the arguments it was called with */ /* and sets the value of the REXX variable RESULT to the number */ /* of arguments. */ static list(args, subflag) struct REXX_PLIST args[]; int subflag; { int n; /* number of arguments passed */ char result_buffer[3]; /* buffer for REXX RESULT string */ if (subflag) /* Were we called as a subroutine or a function? */ puts("Called as a subroutine."); else puts("Called as a function."); /* Count arguments and print. REXX will provide up to ten */ /* argument strings. */ for (n = 0; args[n].len != REXX_LAST_LEN; n++) if (args[n].ad != NULL) printf("Argument %d: \"%.*s\"\n", n, args[n].len, args[n].ad); else printf("Argument %d: (Omitted)\n", n); if (n == 0) puts("No arguments passed."); /* Convert 'n' to string and set REXX RESULT variable. */ sprintf(result_buffer, "%d", n); rxresult(result_buffer); return 0; }
#include <cmsexec.h> int cmsshv(int code, char *vn, int vnl, char *vb, int vbl, int *vl);
cmsshv
can be called in any situation where REXX or
EXEC2 is used, as well as in conjunction with function packages.
cmsshv
performs the following tasks:
char
code
determines what action is performed by cmsshv
and how the remaining parameters are used. <cmsexec.h>
defines the
following values that may be used as the value of code
:
SHV_SET_DIRECT
vn
, must be uppercase. If the name is a REXX
stem, the characters following the period can be in mixed case.
SHV_SET_SYM
code
.
SHV_FETCH_DIRECT
vb
. The variable name must be uppercase. If the name is
a REXX stem, the characters following the period can be in mixed
case.
SHV_FETCH_SYM
code
.
SHV_FETCH_PRIV
ARG
, which fetches the argument string that is parsed by the REXX
statement PARSE ARG
SOURCE
, which fetches the source string that is parsed by the REXX
statement PARSE SOURCE
VERSION
, which fetches the version string that is parsed by the REXX
statement PARSE VERSION.
SHV_FETCH_NEXT
cmsshv
with any other value for
code
.
SHV_DROP_DIRECT
SHV_DROP_SYM
SHV_DROP_DIRECT
except that the name can be in
mixed case.
vn
, vnl
, vb
,
vbl
, and vl
are controlled by code
. The values of these
arguments are summarized in the following table.
Table 8.1 cmsshv Argument Values
+------------------+-------------+------------+------------+-------------+-------------+ | SHV_SET_DIRECT | addresses | length of | addressed | length of | ignored | | SHV_SET_SYM | the name | the | a buffer | the value; | | | | of the | variable | containing | if 0, then | | | | variable | name; if | the value | the value | | | | | 0, then the| to be | is assumed | | | | | name is | assigned | to be null- | | | | | assumed to | | terminated | | | | | be null- | | | | | | | terminated | | | | |------------------|-------------|------------|------------|-------------|-------------| | SHV_FETCH_DIRECT | addresses | length of | addresses | length of | addresses | | SHV_FETCH_SYM | the name | the | a buffer | the buffer | an int | | SHV_FETCH_PRIV | of the | variable | to which | | where the | | | variable | name; if | the value | | actual | | | | 0, then the| of the | | length of | | | | name is | variable | | the value | | | | assumed to | is copied | | will be | | | | be null- | | | stored. | | | | terminate | | | If NULL, | | | | | | | then the | | | | | | | value will | | | | | | | be null- | | | | | | | terminated | |------------------|-------------|------------|------------|-------------|-------------| | SHV_FETCH_NEXT | addresses a | length of | addresses | length of | addresses | | | buffer where| the | a buffer | the buffers | an int | | | the name | variable | to which | | where the | | | is copied. | name buffer| the vaule | | actual | | | The name | | of the | | length of | | | returned | | variable | | the value | | | is null- | | is copied | | will be | | | terminated | | | | stored. | | | | | | | If NULL. | | | | | | | then the | | | | | | | value will | | | | | | | be null- | | | | | | | terminated | |------------------|-------------|------------|------------|-------------|-------------| | SHV_DROP_DIRECT | addresses | length of | ignored | ignored | ignored | | SHV_DROP_SYM | the name of | the | | | | | | the | variable | | | | | | variable | name; if 0 | | | | | | |then the | | | | | | | name is | | | | | | | assumed to | | | | | | | be null- | | | | | | | terminated | | | | +------------------+-------------+------------+------------+-------------+-------------+
cmsshv
returns a negative value if the operation could not
be performed and a nonnegative value if it was performed.
<cmsexec.h>
defines the negative return values from cmsshv
as
follows:
cmsshv
failed to create a correct EXECCOMM parameter
list.
cmsshv
are any one or more of the
following. When one or more of these conditions are true, they are
logically OR'd to indicate that the condition occurred.
vb
was too short to contain the entire
value of the variable. If vl
addresses an int
, the actual
length of the value is stored in *vl
. For SHV_FETCH_NEXT, this
value can be returned if the buffer addressed by vn
is too short
to contain the entire name.
code
is SHV_FETCH_DIRECT
and EXEC2 is active.
code
is not one of the
values defined in <cmsexec.h>
.
cmsshv
uses
the direct interface to REXX and EXEC2 variables (known as EXECCOMM)
as documented in VM/SP System Product Interpreter Reference, IBM
publication No. SC24-5239. Refer to this publication for a detailed
explanation about this interface.
<cmsexec.h>
also
defines three macros for use with cmsshv
. The macros
execset
, execfetch
, and execdrop
assign, retrieve, and drop REXX
variables, respectively. Using these macros can, in some situations,
simplify the use of cmsshv
considerably. Each macro is shown
here, followed by an example.
/* macro */ execset(char *vn, char *vb); rc = execset("REXXVAR","THIS_VALUE"); /* macro */ execfetch(char *vn, char *vb, int vbl); char valbuf[50]; rc=execfetch("RVAR",valbuf,sizeof(valbuf)); /* macro */ execdrop(char *vn); rc = execdrop("REXXVAR");
#include <cmsexec.h> #include <lcio.h> #include <stdio.h> main() { int rc; int len; char namebuf[20]; char valbuf[200]; rc = cmsshv(SHV_FETCH_NEXT,namebuf,20,valbuf,200,& len); while (rc >= && !(rc & SHVLVAR)) { if (rc SHVTRUNC) { puts("Either name or value truncated."); printf("Actual value length is %d\n",len); } printf("The variable name is: %s\n",namebuf); printf("The variable value is: %.*s\n",len,valbuf); rc=cmsshv(SHV_FETCH_NEXT,namebuf,20,valbuf,200,& len); } }
#include <cmsexec.h> int cmsstack(int order, const char *str, int len);
cmsstack
inserts the character array addressed by
str
of length len
onto the CMS program stack in either
last-in-first-out (LIFO) or first-in-first-out (FIFO) order depending
on the value of the order
argument. (cmsstack
can be used in
any CMS application, not just with function packages.)
<cmsexec.h>
defines two values for order
: STK_LIFO and
STK_FIFO. If len
is 0
, then the character array addressed by
str
is assumed to be null-terminated.
cmsstack
returns 0
if the string was inserted or a nonzero value
if the string was not inserted.
len
(or if len
is 0
the maximum
length of the string addressed by str
) is 255.
<cmsexec.h>
also defines two
macros based on cmsstack
. The definitions are shown
here, followed by an example:
/* cmspush */ #define cmspush(s) cmsstack(STK_LIFO, s, 0) rc = cmspush("This string is stacked LIFO"); /* cmsqueue */ #define cmsqueue(s) cmsstack(STK_FIFO, s, 0) rc = cmsqueue("This string is stacked FIFO");
#include <cmsexec.h> /* Stack the parameter on the program stack in FIFO order. */ int main(int argc, char *argv[]) { int rc; if (argc != 2) exit(8); rc = cmsstack(STK_FIFO,argv[1],0); exit(rc == 0 ? 0 : 8); }
#include <cmsexec.h> int rxeval(const char *ptr, unsigned int len);
rxeval
assigns len
bytes, starting at the location
addressed by ptr
, to the REXX variable RESULT.
rxeval
is similar to the rxresult
function except that the
value assigned to the REXX variable RESULT can contain embedded NULL
characters.
rxeval
returns 0
if the value was
properly assigned or some nonzero value if the assignment fails.
rxeval
can be used only in conjunction with the
cmsrxfn
function. If the return value from a function called from
REXX is not 0
, then the value assigned by rxeval
is ignored.
If both rxresult
and rxeval
are used in the same function, the
last value assigned by either function is the value assigned to
RESULT.
#include <cmsexec.h> char hexdata[4]; int rc; /* Note that hexdata can contain any value */ rc = rxeval(hexdata,sizeof(hexdata));
#include <cmsexec.h> int rxresult(const char *str);
rxresult
assigns the string addressed by str
to
the REXX variable RESULT. (To set the REXX variable RC, use the
cmsshv
function.)
rxresult
returns 0
if the value was
properly assigned or some nonzero value if the assignment fails.
rxresult
can be used only in conjunction with the
cmsrxfn
function. If the return value from a function called from
REXX is not 0
, then the value assigned by rxresult
is ignored.
If both rxresult
and rxeval
are used in the same function, the
last value assigned by either function is the value assigned to
RESULT.
rxresult
is included in the result
function of
the sample program that follows these function descriptions.
csqrt
, csin
, and ccos
.
The routines in the package can be called either as functions or as
subroutines from REXX.
A copy of this example program is provided with the compiler and library. See your SAS Software Representative for C compiler products for more information.
#include <cmsexec.h> #include <stdio.h> #include <stdlib.h> #include <math.h> #include <string.h> #include <options.h> /* Because this program cannot be invoked directly from the */ /* command line, run-time options should be specified via the */ /* '_options' variable. For example, int _options = _DEBUG; */ static int csin(), ccos(), csqrt(); static double todouble(); static void result(); /* Define the values of 'fncv' and 'fncc'. */ REXX_FNC funlist[] = {csin,ccos,csqrt} ; #define NFUNCS sizeof(funlist)/sizeof(REXX_FNC) void main(int argc, char *argv[]); { int rc; rc = cmsrxfn(argc,argv,NFUNCS,funlist); /* A positive return code from cmsrxfn() indicates that either */ /* a NUCXDROP RXLOCFN was entered or an ABEND occurred under */ /* CMS. A negative return code indicates that initialization */ /* did not complete. */ if (rc < 0) puts("RXLOCFN did not initialize."); } /* Compute trigonometric sine. Example: x = csin(y) */ static csin(struct REXX_PLIST args[]); { register double r; /* Ensure that there is exactly one argument and that it is */ /* 15 or fewer characters long. (Other validation is probably */ /* useful, but it has been omitted here.) */ if (args->ad == REXX_LAST_AD || args->len > 15 || args[1].ad != REXX_LAST_AD) return 1; /* Perform other parameter validation as necessary. */ r = todouble(args->ad,args->len); /* Convert to double. */ r = sin(r); /* Get the sine. */ result(r); /* Set REXX 'result' variable. */ return 0; /* Tell REXX it worked. */ } /* Compute trigonometric cosine. Example: x= ccos(y) */ static ccos(struct REXX_PLIST args[]); { register double r; if (args->ad == REXX_LAST_AD || args->len > 15 || args[1].ad != REXX_LAST_AD) return 1; r = todouble(args->ad,args->len); r = cos(r); result(r); return 0; } /* Compute square root. Example: x = csqrt(y) */ static csqrt(struct REXX_PLIST args[]); { register double r; if (args->ad == REXX_LAST_AD || args->len > 15 || args[1].ad != REXX_LAST_AD) return 1; r = todouble(args->ad,args->len); if (r < 0.0) return 1; r = sqrt(r); result(r); return 0; } /* Convert REXX parameter from Adlen to double. */ static double todouble(char *str, int len); { char buff[16]; /* Convert string to double. */ double d; /* Copy to a temporary buffer and add a null terminator. */ memcpy(buff,str,len); buff[len] = '\0'; d = strtod(buff,0); return d; /* Return converted argument. */ } /* Convert function result to char and set REXX result variable. */ static void result(double r); { /* Need enough room to handle leading 0, sign, decimal point, */ /* and exponent. */ char buff[15]; /* This is similar to REXX's NUMERIC DIGITS 9 format. */ sprintf(buff,"%.9G",r); rxresult(buff); }
argv
array used by cmsrxfn
is not a normal C argv
array. It can be inspected but not modified.
cmsrxfn
expects functions to return normally via a RETURN
statement. Therefore, exit
and longjmp
should not be used.
Calls to these functions are trapped and cause an ABEND. Similarly,
the REXXMAIN entry point also expects the main function to return by a
RETURN
statement. To comply with this condition, do not use
exit
to terminate a REXX function package.
RENT
compiler
option. If the function package is not compiled with RENT
, a
diagnostic message is issued when the package is called, and it is not
loaded.
cmsrxfn
, rxresult
, rxeval
) should be
used only in the main load module. Using one of these functions in a
dynamically loaded module will result in problems when the module is
linked.
LOAD RXLOCFN (RLDSAVE RESET REXXMAIN GENMOD RXLOCFN (FROM first_CSECTInspect the LOAD map produced by the LOAD command to determine the name of the first CSECT. The RLDSAVE option in the LOAD command causes the resulting MODULE to be relocatable. The RESET REXXMAIN option causes REXXMAIN to be the entry point for the MODULE. The FROM option forces the GENMOD command to save the MODULE, beginning with the first CSECT. Always use the name of the first CSECT in the object code as the FROM parameter. (The LOAD MAP file lists the names of the CSECTs in your program.)
If you have more than one compilation in your function package that initializes external variables, you need to use COOL to preprocess the TEXT files. You may also need to use COOL to remove pseudoregisters from the TEXT files. In this case, the following commands can be used to create the function package MODULE. (This assumes that the function package is created from several compilations named RXLOCP1, RXLOCP2, and so on.)
COOL RXLOCP1 RXLOCP2 ... LOAD COOL (RLDSAVE RESET REXXMAIN GENMOD RXLOCFN (FROM @EXTERN#Do not use the GENMOD option of the COOL EXEC to produce the MODULE file. The COOL EXEC will not produce a relocatable MODULE. Instead, use a separate LOAD command to load the COOL370 TEXT file with the correct options. Note that, in this case, the first CSECT in the COOL370 TEXT file is @EXTERN#, which is used as the parameter to the FROM option in the GENMOD command.
Because you cannot specify the =debug
run-time option via the
command line, set the _DEBUG
flag in the _options
variable to
start the debugger (see the previous example). When the function
package is called the first time, the debugger initializes normally
and prompts for a command. Use the debugger as you would in any other
C program. When you finish debugging, recompile the function package
without the _DEBUG
flag set. (For more information about the
_options
variable, refer to the "Library Options"
section of Chapter 9, "Run-Time Argument Processing," in SAS/C Compiler and Library User's Guide, Fourth Edition ).
If the function package is active but no debugger commands are in effect or no debugger breakpoints are set, you can force the debugger to issue a prompt by entering IC on the CMS command line. The next time an EXEC calls a C function in the package, the debugger will issue a prompt.
Copyright (c) 1998 SAS Institute Inc. Cary, NC, USA. All rights reserved.