The CMS REXX SAS/C® Interface

Introduction

This chapter covers the interface between the CMS REXX (Restructured Extended Executor) language and the SAS/C Library. The interface provided by the library enables you to extend REXX with function packages written in C language. Topics covered include an overview of REXX and function packages, a discussion of how the library interfaces with REXX, and a detailed discussion of the C functions involved. The chapter concludes with a comprehensive example and guidelines. This chapter is intended for applications and systems programmers who work in a CMS environment. A basic understanding of CMS and REXX is assumed.

REXX Concepts and Background

CMS provides an interpretive command and macro processor called the System Product Interpreter to interpret REXX programs. REXX is a high-level, general-purpose programming language. REXX is often used for writing EXECs (command procedures) or XEDIT macros. However, REXX is versatile enough for a wide range of additional programming applications.

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.

Extending REXX with Function Packages

Function packages add flexibility to the REXX language. For example, external functions can access REXX variables and return values to the REXX EXEC. In addition to extending the REXX language, function packages also can boost the performance of a REXX program. Because function packages are usually written in assembler or in a compiled language such as C, more complicated or arithmetically intensive functions can be executed in machine code rather than interpreted word-by-word and line-by-line as for REXX. Also, function packages are loaded from disk only once, thereby avoiding the overhead of reloading each time a function is called.

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.

Functions and subroutines

Within a REXX program, routines in a function package can be used either as functions or as subroutines. Because the C language does not have subroutines or procedures, this means partitioning functions into two types that REXX recognizes as functions or as subroutines. The distinction is that functions must return a result; subroutines need not return a result. A subroutine is called by the REXX CALL instruction. A function is called with a function call. For example, a function named csqrt would be called as x = csqrt(4). To use csqrt as a subroutine, the call would be call csqrt(4).

REXX function packages as nucleus extensions

A REXX function package is an efficient use of code because it is not subject to repetitive reloading. This is because it resides in storage as a nucleus extension. The first time REXX invokes the package, the package copies itself into storage outside of the CMS user program area and identifies itself as a nucleus extension. Once it has been loaded and identified, the function package remains loaded until LOGOFF or until CMS is re-IPLed. Nucleus extensions come before MODULE files in the command search order, so the function package MODULE file is not reloaded as long as the nucleus extension is active. The function package also identifies each separate function as a nucleus extension entry point, thereby enabling REXX to call the function directly (after the first call) without going through the function package search again.

Function parameter lists and variable values

A special parameter list called an Extended Plist is used by REXX for function and subroutine calls. All external routines are invoked using this six-word plist. Word 5 of this plist points to a list of arguments for the function being invoked. These arguments are in the form of address/length pairs known as Adlen pairs. Adlen pairs also are used in the C function that builds and uses the REXX Extended Plist.

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 = 100
 
REXX 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.

How the Library Interfaces with REXX

The library takes advantage of all aspects of the REXX function package interface. This section explains how the library provides the functions that enable you to create function packages in C.

The SAS/C Library REXX Interface Functions

The library provides a set of functions that help you create and use REXX function packages. These functions and their purposes are as follows: 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
sets a REXX variable. execfetch fetches a REXX variable. execdrop drops a REXX variable.
cmsstack
inserts a string into the CMS program stack (the system-provided data queue that can be shared by REXX and your program). The following two macros provide stack access as well:
cmspush
stack (LIFO) access.
cmsqueue
queue (FIFO) access.
You use these functions in your program as follows:

The SAS/C Library Function Package Support

In addition to these functions, the library defines a special C program entry point, REXXMAIN. Together with 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 When a C program is entered via REXXMAIN, the REXXMAIN code copies the program into storage and identifies it as a nucleus extension by calling the CMS command NUCXLOAD for itself. REXXMAIN then transfers control to the normal C initialization routine, passing a specially formatted parameter list. The C environment is created normally, and the parameter list created by REXXMAIN is passed to the 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.

C Function Packages and Nucleus Extensions

During the execution of a REXX function package written using the library interface, several nucleus extension entry points are created. Two have the same name as the function package. The first of these nucleus extension entry points has the SYSTEM attribute and owns the storage occupied by the package code. The entry point identifies the location where the package should be entered if the C environment has been destroyed by an ABEND and must be reinitialized. This support ensures that the C program remains in storage even if an ABEND occurs under CMS. Thus, the function package does not need to be reloaded from disk. The C environment, even though destroyed by the ABEND, is reinitialized automatically the next time the function package is called. The second nucleus extension entry point has the SERVICE attribute. Its entry point identifies the location within 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.

The SAS/C Library Interface and the REXX Extended Plist

When a C function is called by REXX, the first parameter is always a pointer to an array of Adlen pairs. Each Adlen pair describes an argument to the function. The REXX_PLIST structure can reference members of this array. This structure, contained in the <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 .

When REXX Calls a C Function Package

When REXX calls one of the functions in a C function package, REXX creates the six-word plist and invokes the function as a command. Because the nucleus extension entry point indicates a location inside cmsrxfn, cmsrxfn is re-entered. cmsrxfn then When the function returns, 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.

Function Descriptions

Descriptions of each REXX SAS/C interface function follow. Each description includes a synopsis, a description, discussions of return values and portability issues, and an example. Also, errors, cautions, diagnostics, implementation details, and usage notes are included where appropriate. An additional example and discussion of a function package written in C follows the detailed function descriptions. The function package documentation concludes with additional guidelines on using REXX with the library interface.

cmsrxfn -- Create a REXX Function Package

SYNOPSIS

 #include <cmsexec.h>
 int cmsrxfn(int argc, const char *argv[], int fncc,
             REXX_FNC fncv[]);
 

DESCRIPTION

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.

RETURN VALUE

The nature of 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.

CAUTIONS

REXX typically calls a function package with a parameter list of the following form:
 RXLOCFN LOAD FUNC1
 
This 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.

IMPLEMENTATION

cmsrxfn causes the program to be installed as a REXX function package.

EXAMPLE

 #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;
 }
 

cmsshv -- Fetch, Set, or Drop REXX or EXEC2 Variable Values

SYNOPSIS

 #include <cmsexec.h>
 int cmsshv(int code, char *vn, int vnl, char *vb,
            int vbl, int *vl);
 

DESCRIPTION

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: The value of 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
sets a REXX or EXEC2 variable name to a value. The variable name, vn, must be uppercase. If the name is a REXX stem, the characters following the period can be in mixed case.
SHV_SET_SYM
sets a REXX variable name to a value. The name can be in mixed case. EXEC2 does not support this value for code.
SHV_FETCH_DIRECT
fetches the value of a REXX or EXEC2 variable to a buffer, 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
fetches the value of a REXX variable to a buffer. The name can be in mixed case. EXEC2 does not support this value for code.
SHV_FETCH_PRIV
fetches REXX private information to a buffer. The variables recognized are
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
fetches the names and values of all REXX variables as known to the REXX interpreter. The order in which the variable names and values are fetched is unpredictable. The fetch loop can be reset to start again by calling cmsshv with any other value for code.
SHV_DROP_DIRECT
drops the named REXX variable, if it exists. The name must be in uppercase or, if it is a stem, all characters preceding the period must be in uppercase. If the name is a stem, then all variables beginning with that stem are dropped.

SHV_DROP_SYM
behaves the same as SHV_DROP_DIRECT except that the name can be in mixed case.
The values of the remaining arguments 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 |            |             |             |
 +------------------+-------------+------------+------------+-------------+-------------+
 

RETURN VALUE

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:
SHVNOEXECCOMM
neither EXEC2 nor REXX is active.
SHVNOMEM
there is not enough memory available to complete the operation.
SHVLIBERR
cmsshv failed to create a correct EXECCOMM parameter list.
Nonnegative return values from 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.
SHVSUCCESS
the operation completed successfully.
SHVNEWV
the variable name did not exist.
SHVLVAR
for SHV_FETCH_NEXT only, this is the last variable to be transferred.
SHVTRUNC
for SHV_FETCH_DIRECT and SHV_FETCH_SYM, the buffer addressed by 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.
SHVBADN
the name of the variable is invalid.
SHVBADV
the value of the variable is too long. This value can be returned only when the value of code is SHV_FETCH_DIRECT and EXEC2 is active.
SHVBADF
the value of code is not one of the values defined in <cmsexec.h>.

IMPLEMENTATION

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.

USAGE NOTES

<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");
 

EXAMPLE

 #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);
    }
 }
 

cmsstack -- Insert a String into the CMS Program Stack

SYNOPSIS

 #include <cmsexec.h>

 int cmsstack(int order, const char *str, int len);
 

DESCRIPTION

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.

RETURN VALUE

cmsstack returns 0 if the string was inserted or a nonzero value if the string was not inserted.

CAUTION

The maximum value of len (or if len is 0 the maximum length of the string addressed by str) is 255.

USAGE NOTES

<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");
 

EXAMPLE

 #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);
 }
 

rxeval -- Return a Result Value to REXX

SYNOPSIS

 #include <cmsexec.h>

 int rxeval(const char *ptr, unsigned int len);
 

DESCRIPTION

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.

RETURN VALUE

rxeval returns 0 if the value was properly assigned or some nonzero value if the assignment fails.

CAUTIONS

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.

EXAMPLE

 #include <cmsexec.h>
 char hexdata[4];
 int rc;

    /* Note that hexdata can contain any value */
 rc = rxeval(hexdata,sizeof(hexdata));
 

rxresult -- Return a Result Value to REXX

SYNOPSIS

 #include <cmsexec.h>

 int rxresult(const char *str);
 

DESCRIPTION

rxresult assigns the string addressed by str to the REXX variable RESULT. (To set the REXX variable RC, use the cmsshv function.)

RETURN VALUE

rxresult returns 0 if the value was properly assigned or some nonzero value if the assignment fails.

CAUTIONS

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.

EXAMPLE

An example of the use of rxresult is included in the result function of the sample program that follows these function descriptions.

An Example of a REXX Function Package

This example shows a REXX function package containing three trigonometric functions: 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);
 }
 

Additional Guidelines and Related Topics

This section covers topics related to the REXX interface of the SAS/C Library. Included are additional guidelines for developing and using C function packages, linking function packages, and using the SAS/C Source Level Debugger with function packages.

Developing C Function Packages

The following notes should be considered when developing function packages:

Linking REXX Function Packages

Refer to Chapter 6, "Compiling, Linking, and Executing Programs under CMS," in SAS/C Compiler and Library User's Guide, Fourth Edition for general information about how to link C programs. If you do not need to use COOL to preprocess the function package TEXT files, use the following CMS commands to create the function package MODULE. Assume that the function package is named RXLOCFN:
 LOAD RXLOCFN (RLDSAVE RESET REXXMAIN
 GENMOD RXLOCFN (FROM first_CSECT
 
Inspect 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.

Using the SAS/C Debugger and the IC Command

Function packages written in the C language can be debugged using the debugger just like any other C program.

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.