The Subcommand Interface to EXECs and CLISTs

Introduction

The SUBCOM feature provides an interface between the SAS/C library and the command languages of CMS and TSO (REXX or EXEC2 and CLIST). It can also be used as an interface to REXX for programs running under the OpenEdition shell. The interface allows a program to accept input (traditionally subcommands) from a running EXEC or CLIST or to begin executing a new EXEC or CLIST. The SUBCOM interface is implemented through a set of nine functions. This discussion of the SUBCOM feature provides an introduction and overview of the SUBCOM interface followed by descriptions and examples of each of the functions involved.

This chapter is intended for systems programmers and assumes an understanding of either the CMS or TSO command language as well as the concepts of EXEC processing.

Subcommand Processing in C Programs

Subcommand functions enable a program to identify itself to the operating system as a subcommand processor. The ability to handle subcommands provides program flexibility and extends program capabilities. The SUBCOM facility provides a program with a mechanism for taking input from CMS or TSO command languages and provides the programmer with an additional full-scale language to perform program tasks. For example, a simple subcommand application allows a CLIST or EXEC to execute a number of similar commands repeatedly. A more complicated application defines complex subcommands as sequences of simpler commands, packaged as a CLIST or EXEC.

All SUBCOM interfaces are portable between CMS, TSO, and OpenEdition, so a program written for one environment can be ported to another. Although the exact internal processing (or effect of SUBCOM functions) is system-dependent, these differences are transparent to the user, so a program's operation on different systems is still predictable. Even SUBCOM programs with substantial differences in required CMS and TSO behavior can usually be written with the system dependencies isolated to smaller parts of the program.

Steps in Subcommand Processing

Under TSO, CMS and the OpenEdition shell, programs that support a subcommand environment can be described as doing their processing in a basic loop consisting of the following steps:
  1. Get the next subcommand (from the terminal or from an EXEC or CLIST).
  2. Identify the subcommand name.
  3. Process the subcommand, possibly issuing one or more messages.
  4. Set the subcommand's return code for access by an EXEC or CLIST.
  5. Go to step 1.
Associated with each of these steps are the following SUBCOM functions:
  1. execget, which obtains a subcommand from an appropriate source
  2. execid, which extracts the subcommand name and may perform additional system-dependent processing, for example, recognizing system-defined use of special characters in the subcommand
  3. execmsg, which sends diagnostic messages, the exact form of which can be controlled by the user
  4. execrc, which sets the return code from the completed subcommand and may perform additional system-dependent processing.
In addition, an execinit function is used to initialize the subcommand environment (and assign a name to it), and execend is used to terminate the environment.

The set of library functions that implement the SUBCOM interface can be summarized as follows: execinit establishes and assigns a name to a SUBCOM environment.

execcall
invokes a new CLIST or EXEC, from which input can be read later.
execend
cancels the program's SUBCOM environment.
execget
gets a line of input from an EXEC/CLIST or from the terminal.
execid
parses a string into a subcommand name and operands, using system-dependent conventions.
execinit
establishes and assigns a name to a SUBCOM environment.
execmsg
sends a message to the user, in a system-dependent way, allowing the use of system facilities for message suppression or abbreviation.
execmsi
tests the environment, in a system-dependent way, to determine which portions of messages the user wants to print.
execrc
sets the return code of the most recent subcommand.
execshv
provides a way to fetch, set, or drop REXX, EXEC2 or CLIST variable values.
If these functions are used to create a subcommand environment, a typical SUBCOM program has the following basic form:
 execinit(...);                    /* Set up environment.       */
 for(;;) {
  execget(...);                    /* Get a subcommand.         */
     cmd = execid(...);            /* Identify subcommand.      */
     if (strcmp(cmd,"END")==0)
        break;
     if (strcmp(cmd,"EXEC")==0)
        execcall(...);             /* Implement exec.           */
 
     else
        process(...);              /* Process the subcommand.   */

     execrc(...);                  /* Set the return code.      */
 }
 execend();                         /* Terminate the environment.*/
 

Overview of the SUBCOM Environment

As noted in the Introduction, a program using the SUBCOM interface can be completely portable between CMS, TSO and OpenEdition environments. Topics of general interest concerning these implementations are discussed in this section. Using SUBCOM in an interactive and noninteractive environment also is discussed. (Topics specific to the implementation of each SAS/C function are presented later in each detailed function description.)

SUBCOM and CMS

Under CMS, you traditionally establish a subcommand environment by invoking the CMS SUBCOM service. You then write an assembler routine to provide the appropriate function parameters and calling sequence. The SAS/C compiler simplifies this process by providing a set of functions that enable a program to become a subcommand processor. More detailed coverage of all aspects of the CMS SUBCOM service can be found in the appropriate IBM documentation for your system.

To use the SUBCOM facility under CMS, a program must tell CMS its name and the address to which CMS should pass the commands. The next step is to give the program the commands. Under CMS the user typically uses an EXEC to pass the commands. Because commands in an EXEC are sent by default to CMS, however, an alternate destination can be specified via the address statement (in REXX) or the &SUBCOMMAND statement (in EXEC2). After the program has handled the command, it returns to CMS so that CMS can get another command for the program to handle. Therefore, subcommand processors are being invoked continually and returning to CMS until they are told to stop or are interrupted.

CMS also has a facility for letting the user program call the EXEC processor directly. In this case, the EXEC processor sends commands by default to the program instead of CMS. To invoke an EXEC for itself, the program again must first declare itself via SUBCOM so that it can get commands. In this case, it then calls CMS to tell it the name of a file that the EXEC processor will execute. The EXEC processor executes the file and sends the commands to the program.

To facilitate this processing, the SUBCOM interface uses the CMS SUBCOM facility that enables a subcommand entry point. To invoke an EXEC, the interface calls the EXEC processor with the filename and filetype of the EXEC to be executed.

SUBCOM and TSO

Under TSO, CLISTs are executed through a two-stage process. First, the EXEC command is called to read and compile the CLIST. The resulting data are accessible through a control block called the TSO stack. Later, when a program, or TSO itself, needs to obtain input and is willing to accept CLIST input, it calls the PUTGET service routine. If a CLIST is present on the stack, TSO passes control to the CLIST. The CLIST executes until it generates a subcommand line. At this point, TSO returns control to PUTGET, which passes the subcommand line back to its caller.

The SUBCOM implementation for TSO is closely tied to traditional CLIST processing. The execget function reads the CLIST input by calling the PUTGET routine. The execcall function, which invokes a new CLIST, does so by calling the EXEC command.

Accessing REXX under TSO

The SAS/C Library SUBCOM features supports TSO REXX in addition to the CLIST language. The REXX support does not require that existing CLIST applications be modified. A single application can generally be used to communicate with both CLISTs and REXX EXECs without concern for which command language is used. There are a few cases where a program's behavior processing a REXX EXEC may differ from the behavior processing a similar CLIST, as described in the function descriptions.

When using the SUBCOM interface to communicate with a REXX EXEC in TSO, the EXEC must specifically address (via the ADDRESS command) the SAS/C application's environment to send it subcommands. The default environment for EXECs invoked from SAS/C is still TSO. The name to be addressed is the value of the first argument to execinit.

Note that a REXX EXEC that addresses a SUBCOM application cannot be called using the system function. If this is attempted, any subcommand addressed to the SUBCOM application will fail with a return code of -10.

The TSO SUBCOM implementation for calling a REXX EXEC is somewhat different from the CLIST implementation. In this case, execinit defines a host-command environment routine to process subcommands addressed to the application. When execget is called, control is transferred from the SUBCOM application to REXX. When the REXX EXEC generates a subcommand for the application, REXX calls the Host Command Environment routine, which passes the subcommand back to execget.

Attention Handling

If a SUBCOM application has a SIGINT signal handler or uses the debugger, an attention while a REXX EXEC is running is specially handled. The user is prompted to enter either IC or a regular REXX immediate command. If IC is entered, an attention is presented to the debugger or the SIGINT handler as if REXX were not active. Otherwise, the REXX immediate command is passed to REXX. This allows attention handling to be shared between REXX (which ordinarily will not let an application handle attentions itself) and the SAS/C application.

SUBCOM and OpenEdition

Under the OpenEdition shell, REXX EXECs are stored as executable files in the hierarchical file system, and can be invoked by any of the exec family of functions. However, when a REXX EXEC is invoked in this fashion, it runs in a separate address space from its caller, and only the standard REXX environments (such as the MVS and SYSCALL environments) are available.

The SAS/C SUBCOM implementation for OpenEdition uses the oeattach function to invoke REXX in the same address space as its caller. This allows the library to establish the additional SUBCOM environment used for communication with the calling program. The IBM support routine BPXWRBLD is used to establish a REXX environment that is OpenEdition aware, so that REXX input and output will be directed to the hierarchical file system rather than to DDnames.

The SAS/C REXX interface under the shell runs as a separate process from the calling program to prevent interference between the program and EXEC processing. When the REXX interface is created by a call to execinit, the program's environment variables and file descriptors are copied to the REXX interface process. This copy of the environment is not affected by any changes requested by the program, including the use of setenv or close. REXX input and output are always directed to the REXX process' file descriptors 0 and 1, even if these descriptors have been reopened by the program after the call to execinit.

Note that a REXX script invoked by the system function under OpenEdition cannot address the invoker's SUBCOM environment. Any attempt to do so will fail with return code -3 (command not found).

SUBCOM in Interactive and Noninteractive Environments

A program operating in a subcommand environment can execute in either an interactive or noninteractive manner. Either type of execution can be used by programs that accept input from CLISTs or EXECs, subject to operating system restrictions.

A program with interactive style gets subcommands from the terminal. This style program, even if it is invoked from an EXEC or CLIST, begins by reading from the terminal and accepts EXEC or CLIST input only as the result of terminal input (such as an EXEC command from the user). Commands in an EXEC or CLIST following a call to an interactive program can be executed only after that program terminates.

A program with noninteractive style receives subcommands from an EXEC or CLIST. If this style program is invoked from an EXEC or CLIST, it gets its input from that EXEC or CLIST and takes input from the terminal only as a result of commands from the EXEC or CLIST or after that EXEC or CLIST has terminated.

For example, an EXEC or CLIST could contain the following commands:

 CPGM1   (options)
 SUBCMD2 ( options)
 
If the program CPGM1 is interactive, the SUBCMD2 input line is not read by CPGM1 or processed at all until CPGM1 terminates. If CPGM1 is noninteractive, however, the SUBCMD2 line is read by CPGM1 the first time it calls execget to get a line of input.

Under CMS, most processors are defined as interactive. In versions of CMS after VM/SP 5, we do not recommend noninteractive programs.

Under TSO, most processors are defined as noninteractive. An interactive program must create a new version of the TSO stack to avoid reading input from the CLIST that invoked it.

Under the OpenEdition shell, only the interactive mode of SUBCOM is supported.

Function Descriptions

Descriptions of each subcommand interface function follow. Each description includes a synopsis, description, discussions of return values and portability issues, and an example. Also, errors, cautions, diagnostics, implementation details, and usage notes are included if appropriate. A comprehensive example incorporating all the SUBCOM functions is presented and discussed following the function descriptions. This chapter concludes with a summary of guidelines and considerations for using the SUBCOM interface.

execcall -- Identify a Macro to Be Executed

SYNOPSIS

 #include <exec.h>

 int execcall(const char *cmd);
 

DESCRIPTION

The execcall function invokes an EXEC or CLIST. The character string pointed to by cmd is an EXEC command. The exact format of the command is system-dependent. cmd can include a specification of the file to execute and also operands or options.

The library assumes that both "EXEC cmdname operands" and "cmdname operands" are valid arguments to execcall, but the exact details are system-dependent. (Under OpenEdition, the command string should not begin with "EXEC" unless the intent is to invoke a REXX script named EXEC.)

RETURN VALUE

The execcall function returns 0 if the call is successful or nonzero if the call is not successful. A nonzero return code indicates that the EXEC or CLIST could not be executed.

Under TSO, the return code is that of the EXEC command or a negative return code whose meaning is the same as that from the system function. However, if execcall is used to call a TSO REXX EXEC that terminates without passing any subcommands to the C program, the return code is that set by the EXEC's RETURN or EXIT statement. This cannot always be distinguished from an EXEC command failure.

Under the OpenEdition shell, as under TSO, the return code from execcall may reflect either a failure of the REXX interface (IRXJCL) or a return code set by the called EXEC.

CAUTIONS

The execcall function is valid only for programs that have defined an environment name through execinit.

Once execcall is used successfully, future execget calls are expected to read from the EXEC or CLIST. execget returns "*ENDEXEC rc " the first time it is called after a CLIST or EXEC invoked by execcall has completed, where rc is the return code from the CLIST or EXEC. Subsequent calls to execget result in terminal reads.

IMPLEMENTATION

For a CMS EXEC or a TSO CLIST, execcall does not cause any of the statements of the EXEC or CLIST to be executed. execcall finds the file to be executed and completes the parameter list to be processed.

Under CMS, the macro to be invoked has a filename of execname and a filetype of envname, where envname was specified by the call to execinit. If the macro cannot be found, an error code is returned. Otherwise, execcall saves the fileid but does not invoke the macro. The next call to execget invokes the EXEC processor with the EXEC parameter list and a FILEBLOK indicating the filename (from execcall) and filetype (from execinit) of the macro.

Under TSO, execcall uses the ATTACH macro to call the EXEC command. Any CLIST arguments are processed at this time, and prompts are generated for any omitted arguments. No other CLIST processing occurs until execget is called. For a TSO REXX EXEC, execcall executes the statements of the EXEC up to the first subcommand addressed to the program's environment before control is returned to the program.

Note that if errors are detected by the EXEC command, the TSO stack is flushed, and a diagnostic message is printed by EXEC.

Under the OpenEdition Shell, the requested REXX EXEC is invoked using the IRXJCL service routine. Due to limitations of this service, the name of the EXEC is limited to eight characters. The EXEC must be an executable file, and must reside in one of the directories defined by the PATH environment variable. The statements of the EXEC up to the first subcommand addressed to the program's environment will be processed before control is returned to the program.

EXAMPLE

See the example for the execget function and the comprehensive SUBCOM example.

execend -- Cancel the SUBCOM Interface

SYNOPSIS

 #include <exec.h>

 int execend(void);
 

DESCRIPTION

The execend function is called to terminate subcommand processing.

RETURN VALUE

The execend function returns 0 if the call is successful or nonzero if the call is not successful. A nonzero value is returned when there is no previous successful call to execinit or if execend encounters other errors during its processing. Further use of a SUBCOM environment created by execinit is not possible after a call to execend, if execend does not complete successfully.

CAUTIONS

If any EXECs invoked by execcall are active when execend is called, the effect is system-dependent and also can depend on whether the previous call to execinit was interactive.

Under CMS, if the program invokes an EXEC via execcall and then calls execend (see the IMPLEMENTATION section) before processing the entire EXEC, execend allows the EXEC processing to complete. All remaining subcommands are accepted but ignored, and the return code for each subcommand is set to - 10 .

Under TSO, if the original execinit call is interactive, any active CLISTs started by execcall are terminated when execend is called. If the execinit call is noninteractive, however, any such CLISTs continue to execute and may generate input for the processor that called the C program.

If the execend function is called with one or more TSO REXX EXECs still active, the EXECs continue to execute, but any attempt to send a subcommand to the application that called execend results in a REXX RC of - 10 and a message. The call to execend is not considered to be complete until all active REXX EXECs have terminated. If execinit is called in noninteractive mode, any CLISTs on the TSO stack beneath the first REXX EXEC at the time of a call to execend remain active.

Under the OpenEdition shell, the behavior is the same as under CMS.

IMPLEMENTATION

Under TSO, when SUBCOM is initialized with an interactive call to execinit, execend causes all CLISTs invoked by execcall to be forcibly terminated. If execinit creates a new stack allocation, the previous allocation is restored.

Similarly, if SUBCOM is initialized as interactive, execend deletes the current REXX data stack, restoring the stack defined when execinit was called. If SUBCOM is initialized as noninteractive, the REXX data stack is not modified.

EXAMPLE

See the example for the execget function and the comprehensive SUBCOM example.

execget -- Return the Next Subcommand

SYNOPSIS

 #include <exec.h>

 char *execget(void);
 

DESCRIPTION

The execget function obtains a subcommand from a CLIST or EXEC or from the terminal if no CLIST or EXEC input is available. It returns the address of a null-terminated string containing the subcommand. The area addressed by the pointer returned from execget remains valid until the next call to execget.

If an EXEC or CLIST is active and accessible, input is obtained from that EXEC or CLIST; otherwise, the user is prompted with the name of the environment and a colon, and the next line of terminal input is returned to the program. (Before the prompt is issued, input is taken from the REXX data stack, if the stack is not empty.) If the SUBCOM interface is initialized with an interactive call to execinit, any EXECs or CLISTs active at the time are inaccessible. Additionally, under TSO, any input on the REXX data stack is inaccessible. Therefore, for such programs, a call to execget reads from the terminal unless execid or execcall has invoked a new CLIST or EXEC.

If all active EXECs or CLISTs terminate and the data stack becomes empty, execget returns a pointer to the string "*ENDEXEC rc ". The component "*ENDEXEC" is a literal character string provided as a dummy subcommand. The component "rc" is an appropriate return code. The "rc" can be omitted if no return code is defined. Subsequent calls to execget issue terminal reads, using a prompt formed from the original envname suffixed with a colon.

The "*ENDEXEC" convention was designed for the convenience of programs that want to use execget to read only from CLISTs or EXECs, while using another form of normal input (for instance, a full-screen interface). Such programs use execget only after execcall (or at start-up, if noninteractive) and switch to their alternate form of input after execget returns "*ENDEXEC".

RETURN VALUE

The execget function returns a pointer to a buffer containing the subcommand string. If no subcommand can be returned due to an unexpected system condition, execget returns NULL.

CAUTIONS

The execget function is rejected when there is no environment name defined via execinit.

Under TSO, a CLIST can use the TERMIN statement to switch control from the CLIST to the terminal. Use of TERMIN when the SUBCOM interface is initiated with a noninteractive call causes "*ENDEXEC" to be returned, even when the CLIST has not ended.

The CMS SUBCOM interface acknowledges two logical cases of SUBCOM processing. The first case is when the program is accepting subcommands from an EXEC that is invoked external to the program. The second case occurs when the program is accepting subcommands from an EXEC invoked by execcall (see the execcall description).

If the program is accepting subcommands from an EXEC invoked externally and the EXEC terminates without sending a subcommand to execget, the string "*ENDEXEC" is placed in the subcommand buffer without a return code value because the return code belongs to the environment that invoked the EXEC (usually CMS). Subsequent calls to execget read from the terminal. If the program terminates before all the subcommands in the EXEC are processed, the remaining subcommands are issued to CMS. Under most circumstances, CMS responds with an error message and a return code of - 3. (Refer also to the note on *ENDEXEC in the section CAUTIONS under execcall.)

Note that under the OpenEdition shell, input is read from the REXX process' file descriptor 0 if no EXEC is active and there is no data present on the data stack. File descriptor 0 is normally allocated to the terminal, but could be directed elsewhere if the program's standard input had been redirected when execinit was called.

Premature termination of programs that have invoked an EXEC via execcall are handled by execend. See the section CAUTIONS following the execend description.

IMPLEMENTATION

For CMS, refer to the IMPLEMENTATION discussion in the execcall description.

Under TSO, execget issues the PUTGET MODE macro to get a line of input if the current input source is a CLIST or the terminal. If the current input source is a REXX EXEC, the next input line is obtained from the host-command environment routine. In interactive mode, "*EXECEND" is returned when the current allocation of the TSO stack becomes empty. In noninteractive mode, "*EXECEND" is returned when control passes from a CLIST to the terminal, or on the first call if no CLIST is active when program execution begins.

Under OpenEdition, if no EXEC is active when execget is called, execget reads a line from the REXX process' file descriptor 0 using the IRXSTK service routine's PULLEXTR function.

EXAMPLE

This example obtains a subcommand from a CLIST or EXEC. If no command is available, SUBCOM processing is terminated. Otherwise, the command is assumed to be an EXEC command and is executed using execcall.
 #include <exec.h>
 #include <string.h>

 int rc;
 char *cmd;

 cmd = execget();

 if (cmd != 0 && memcmp(cmd,"*ENDEXEC",8))
    execend();
 
 else {
    rc = execcall(cmd);
    execrc(rc);
 }
 

Note: Also see the comprehensive SUBCOM example.

execid -- Parse a Line of Input as a Subcommand

SYNOPSIS

 #include <exec.h>

 char *execid(char **cmdbuf);
 

DESCRIPTION

The execid function parses a line of input as a subcommand, according to system-specific conventions. cmdbuf is a pointer to the address of the line of input. When execid returns, *cmdbuf is altered to address the first operand of the subcommand or the null character ending the string. The return value from execid is a pointer to the subcommand name.

The execid function can enforce syntax conventions of the host system. This is useful particularly in TSO applications. For example, under TSO, execid checks to see if the subcommand begins with the '%' character, and then treats it as an implicit EXEC command if it does. In such cases, execid can preempt processing of the subcommand. If it does, it returns a command name of "*EXEC" to indicate that processing of the subcommand has been preempted, that a new EXEC may have been invoked, and that the program should get another line of input.

If the PCF-II product is installed on TSO, execid also processes the PCF-II X command and returns "*EXEC" on completion of PCF-II processing.

Under MVS and CMS, execid uppercases the input command name in accordance with TSO and CMS conventions. Under the OpenEdition shell, the command name is not uppercased, in accordance with UNIX conventions.

RETURN VALUE

The execid function returns a pointer to the subcommand name if the call is successful or 0 if it failed.

A 0 return code from execid always means that the program should expect subsequent input to come from a CLIST or an EXEC. (The next execget that reads from the terminal always returns "*ENDEXEC".)

CAUTIONS

Whether execid is valid for a program without an environment name defined is system-dependent.

The execid function does no validation of the subcommand name. It is the responsibility of the program to decide whether to accept the subcommand.

Under TSO, if execid is not called for a subcommand, the CLIST variable &SYSSCMD does not contain the current subcommand name.

IMPLEMENTATION

The use of execid is optional. However, the environmental integration it provides may be helpful in TSO applications.

Under TSO, execid calls the IKJSCAN service routine to extract the subcommand name. If IKJSCAN indicates that the % prefix was used, execid calls the EXEC command to process an implicit CLIST request and returns "*EXEC" to indicate this.

EXAMPLE

 #include <exec.h>

 char *cmdname, *operands;
 cmdname = execid(&operands);
 

Note: Also see the comprehensive SUBCOM example.

execinit -- Create a SUBCOM Environment

SYNOPSIS

 #include <exec.h>

 int execinit(const char *envname, int interactive);
 

DESCRIPTION

The execinit function establishes a SUBCOM environment with the name specified by envname. The characteristics of envname are system-dependent. Under TSO, the value of envname is available through the standard CLIST variable &SYSPCMD or the REXX call SYSVAR(SYSPCMD). Under CMS, the value of envname is the environment addressed by an EXEC to send subcommands. Also under CMS, for EXECs invoked via execcall, the environment is used as the filetype of the EXEC.

The interactive argument is a true-or-false value (where 0 is false and nonzero is true) indicating whether the program should run as an interactive or noninteractive SUBCOM processor. Because the interactive argument to execinit can be a variable, it is possible for a program to choose its mode according to a command-line option, allowing complete flexibility. An interactive processor is one that normally accepts input from the terminal, even if the program is invoked from an EXEC or CLIST. A noninteractive processor is one that is normally called from an EXEC or CLIST and that takes input from the EXEC or CLIST that calls it. Using this terminology, most CMS processors, such as XEDIT, are interactive, while most TSO processors, such as TEST, are noninteractive.

Note that TSO REXX does not allow a program invoked from an EXEC to read input from the same EXEC. For TSO REXX applications, the only distinction between interactive and noninteractive mode is that, in interactive mode a new REXX data stack is created, while in noninteractive mode the previous data stack is shared with the application.

Similarly, noninteractive mode is not supported under the OpenEdition shell.

RETURN VALUE

The execinit function returns 0 if the call is successful or nonzero if the call is not successful. Reasons for failure are system-dependent. Possible errors include a missing or invalid envname or, under CMS, a nonzero return code from the CMS SUBCOM service.

CAUTIONS

A program cannot call execinit more than once without an intervening call to execend. Under CMS, a program that calls execinit must have been called directly from CMS. Calls from other languages, including assembler front ends, are not supported.

Noninteractive SUBCOM processing is not supported under bimodal CMS or OpenEdition.

Under TSO, when a program calls execinit with interactive set to true (nonzero), it is not possible for CLISTs invoked by that program to share GLOBAL variables with the CLIST that called the program.

If the value of interactive is true (nonzero) and the SAS/C program is invoked from an EXEC or CLIST, the remaining statements of the EXEC or CLIST are inaccessible via execget. If execget is called before the use of execcall, or after any EXECs or CLISTs called by execcall have terminated, the program gets input from the terminal. If the value of interactive is false (0) and the SAS/C program is invoked from a CMS EXEC or TSO CLIST, then calls to execget without the use of execcall read from that EXEC or CLIST.

Under TSO, if the value of interactive is true, a new allocation of the REXX data stack is created, and the previous data stack contents are inaccessible until execend is called.

IMPLEMENTATION

Under CMS, execinit invokes the CMS SUBCOM service. The name of the environment is envname, truncated to eight characters if necessary and translated to uppercase.

Under CMS, the execinit function creates a nucleus extension called L$CEXEC with the ENDCMD attribute. This is done so that premature EXEC termination can be detected (see the execget function description). If the CMS command NUCXDROP L$CEXEC is issued to delete this nucleus extension, the program continues to execute, but premature termination is not detected. If this happens, the program remains active, but control cannot be transferred from CMS back to the program. Therefore, it is not recommended that you use NUCXDROP L$CEXEC. For more information, see "Guidelines for Subcommand Processing" at the end of this chapter.

Under TSO, the value of envname is stored in the ECTPCMD field of the Environment Control Table (ECT). When the value of interactive is true, a new allocation of the TSO stack is created, if necessary, to preserve the status of a previously executing CLIST.

Under the OpenEdition shell, execinit creates a new process using the oeattach function. This process invokes the BPXWRBLD service to create an OpenEdition REXX language environment.

EXAMPLE

 #include <exec.h>
 #include <stdio.h>

 int rc;
 .
 .
 .
 if (rc = execinit("EXENV",0))
    printf("Failed to establish SUBCOM envr\n");
 

Note: See also the comprehensive SUBCOM example.

execmsg -- Send a Message to the Terminal

SYNOPSIS

 #include <exec.h>

 int execmsg(const char *id, char *msg);
 

DESCRIPTION

The execmsg function sends a message to the terminal, editing it in a system-dependent way. The character string pointed to by id is a message identifier that either can be sent or suppressed. id can be 0 to indicate the absence of the message identifier. The character string pointed to by msg is the actual message text.

RETURN VALUE

The execmsg function returns 0 if the call is successful or nonzero if the call fails. Reasons for failure are system-dependent. For example, under TSO a message cannot be sent if it is longer than 256 characters, including the identifier. execmsg cannot fail under CMS, although depending on the current EMSG setting, the message may not be sent.

CAUTIONS

Under CMS, the message ID must be ten characters long. If it is longer than ten characters, it is truncated on the right. If it is less than ten characters, it is padded on the left with asterisks (*). This means that a message ID of length 0 has different effects than if id is NULL, although the TSO effects are the same.

Under CMS, the maximum message length, for the ID and text, is 130 characters. If it is longer, the message text is truncated.

The execmsg function edits the message according to the user's EMSG setting. No message is sent in the following situations:

Under TSO, messages sent using execmsg can be trapped by the CLIST command output trapping facility (the symbolic variables &SYSOUTTRAP and &SYSOUTLINEnnnn). Terminal output sent in other ways (such as using the standard SAS/C I/O facilities) is not trapped.

Under OpenEdition, messages sent using execmsg are sent to file descriptor 1 of the REXX interface process, which is normally the terminal. OpenEdition does not support suppression of message IDs.

IMPLEMENTATION

The message is sent under TSO using PUTLINE INFOR (or PUTLINE DATA if id is 0). Under CMS, the message is edited via DIAG X'5C' and sent to the terminal by TYPLIN or LINEWRT. Under OpenEdition, the message is sent to the REXX process' file descriptor 1 using the IRXSAY service routine.

The execmsg function can be used by programs that have not defined an environment name with execinit under CMS or TSO; however, prior use of execinit is required under OpenEdition.

EXAMPLE

 #include <exec.h>

 static char *id="EXEC-MSG";
 execmsg(id,"File not found");
 

Note: See also the comprehensive SUBCOM example.

execmsi -- Return System Message Preference

SYNOPSIS

 #include <exec.h>

 int execmsi(void);
 

DESCRIPTION

The execmsi function tests the user's preferences for printing system messages, specifically, whether message IDs should be printed or suppressed. Under TSO, execmsi indicates whether PROFILE MSGID or PROFILE NOMSGID is in effect. Under CMS, execmsi tests whether the CP EMSG setting is EMSG ON, EMSG TEXT, EMSG CODE, or EMSG OFF.

Under OpenEdition, execmsi always returns MSI_MSGON, indicating that message IDs should be printed.

RETURN VALUE

The execmsi function returns an integer value indicating the user's message processing preference. Symbolic names for these return values are defined in the header file <exec.h>, as follows: MSI_MSGON specifies to print message and message id (TSO PROFILE MSGID or CMS SET EMSG ON or OpenEdition). MSI_MSGTEXT specifies to print message text only (TSO PROFILE NOMSGID or CMS SET EMSG TEXT). MSI_MSGCODE specifies to print message ID only (CMS SET EMSG OFF). MSI_MSGOFF specifies not to print messages (CMS SET EMSG OFF). MSI_NOINFO specifies that the information is unavailable (MVS batch).

EXAMPLE

This example formats a message to stderr based on the return code from execmsi:
 #include <stdio.h>
 #include <string.h>
 #include <exec.h>

 void msgfmt(int msgid, char *msgtext)
 {
    int rc;
    rc = execmsi();
    switch (rc)
    {
       case MSI_MSGON:       /* id + text  */
       default:
          fprintf(stderr, "%d -- %s\n", msgid, msgtext);
          break;
       case MSI_MSGTEXT:     /* text only  */
          fprintf(stderr, "%s\n", msgtext);
          break;
 
       case MSI_MSGCODE:     /* id only    */
          fprintf(stderr, "%d\n", msgid);
          break;
       case MSI_MSGOFF;      /* no message */
          break;
    }
    return;
 }
 

execrc -- Set Return Code of Most Recent Subcommand

SYNOPSIS

 #include <exec.h>

 int execrc(int rc);
 

DESCRIPTION

The execrc function is used to set the return code of the most recently executed subcommand. The return code is passed back to any executing EXEC or CLIST and is accessible via the REXX RC variable, the EXEC2 &RC variable, or the CLIST &LASTCC variable. In all environments, a negative return code is treated as a more serious error than a positive code.

RETURN VALUE

The execrc function returns the maximum return code value that is specified so far. That is, the return value is the maximum of the current execrc argument and any previous argument to execrc. Under TSO and OpenEdition, the return code is replaced by its absolute value before the maximum is computed.

execrc returns a negative value if it is unable to successfully store the requested return code.

CAUTIONS

If execrc is called more than once for the same subcommand, the last value set is used, but the execrc return value may reflect the previous value.

If execrc is not called for each subcommand, the old value of rc is retained.

If execrc is not called at all for a subcommand, the return code for the previous subcommand is set, or 0 is used if there is no previous value.

IMPLEMENTATION

Under TSO, if rc is negative, the IKJSTCK service routine is called to flush the stack. Then, the absolute value of rc is stored in the ECTRCDF field, which the CLIST processor uses as the value of the &LASTCC variable.

However, a call to execrc with a negative argument does not flush a REXX EXEC because this functionality is not present in TSO REXX. In this case, the EXEC receives rc unchanged, not its absolute value, as for a CLIST. If execrc is called with a negative argument and one or more CLISTs are active, the CLISTs are still flushed until a REXX EXEC or the terminal is at the top of the stack, at which point flushing stops.

Under CMS, the rc value is the R15 value returned when control is returned to CMS, presumably in search of a new subcommand.

EXAMPLE

See the example for the execget function and the comprehensive SUBCOM example.

execshv -- Fetch, Set, or Drop REXX, EXEC2, or CLIST Variable Values

SYNOPSIS

 #include <exec.h>

 int execshv(int code, char *vn, int vnl, char *vb,
             int vbl, int *vl);
 

DESCRIPTION

Under TSO or CMS, the execshv function can be called in any situation where an EXEC or CLIST is active. Under OpenEdition, execshv can be used only when an EXEC invoked by execcall is active.

execshv performs the following tasks:

The value of code determines what action execshv performs and how the remaining parameters are used. <exec.h> defines the following values that can be used as the value of code:
SHV_DROP
drops the named REXX or CLIST variable if it exists. The name is translated to uppercase before being used. For TSO CLIST or REXX variables, the variable name cannot truly be dropped; instead, it is set to a zero-length string.
SHV_FETCH
fetches the value of a REXX, EXEC2, or CLIST variable to a buffer, vb. The variable name is translated to uppercase before being used.
SHV_FIRST
initializes the SHV_NEXT fetch next sequence and retrieves the first in the list of all names and values of all REXX or CLIST variables as known to the REXX or CLIST interpreter. The particular variable fetched is unpredictable.
SHV_NEXT
fetches the next in the list of names and values of all REXX or CLIST variables as known to the REXX or CLIST interpreter. The order in which variable names and values are fetched is unpredictable. The fetch loop can be reset to start again by calling execshv with SHV_FIRST or any other value for code.
SHV_SET
sets a REXX, EXEC2, or CLIST variable to a value. The case of the variable name, vn, is ignored (no distinction is made between uppercase and lowercase characters).
The values of the remaining arguments vn, vnl, vb, vbl, and vl are controlled by code. The values of these arguments are summarized in Table 7.1.

Table 7.1 execshv Argument Values

+-------------+-------------+------------+------------+------------+-------------+
| SHV_SET     | addresses   | length of  | addresses  | length of  |    ignored  |
|             | the name    | the        | a buffer   | the value. |             |
|             | of the      | variable   | containing | If 0, then |             |
|             | variable    | name. If   | the value  | the vlaue  |             |
|             |             | 0, then    | to be      | is assumed |             |
|             |             | the name   | assigned   | to be num- |             |
|             |             | is assumed |            | terminated |             |
|             |             | to be      |            |            |             |
|             |             | null-ter   |            |            |             |
|             |             | minated.   |            |            |             |
|             |             |            |            |            |             |
|             |             |            |            |            |             |
|             |             |            |            |            |             |
|-------------|-------------|------------|------------|------------|-------------|
| SHV_FETCH   | addresses   | length of  | addresses  | length of  | addresses   |
|             | the name of | of the     | a buffer   | the buffer | an int      |
|             | the variable| variable   | to which   |            | where the   |
|             |             | name. If   | the vlaue  |            | actual      |
|             |             | 0, then    | of the     |            | length of   |
|             |             | the name   | variable   |            | the value   |
|             |             | is assumed | is copied  |            | will be     |
|             |             | to be null-|            |            | stored.  If |
|             |             | terminated |            |            | null, then  |
|             |             |            |            |            | the value   |
|             |             |            |            |            | will be     |
|             |             |            |            |            | null-ter    |
|             |             |            |            |            | minated.    |
|             |             |            |            |            |             |
|             |             |            |            |            |             |
|             |             |            |            |            |             |
|-------------|-------------|------------|------------|------------|-------------|
| SHV_FIRST   | addresses   | length of  | addressed  | length of  | addresses   |
|             | a buffer    | the        | a buffer   | the buffer | an int      |
| SHV_NEXT    | where the   | variable   | to which   | buffer     | where the   |
|             | name is     | name       | the value  |            | actual      |
|             | copied. The | buffer     | of the     |            | length of   |
|             | name        |            | variable   |            | the value   |
|             | returned    |            | is copied  |            | will be     |
|             | is null-    |            |            |            | stored.     |
|             | terminated. |            |            |            | If null,    |
|             |             |            |            |            | then the    |
|             |             |            |            |            | value will  |
|             |             |            |            |            | null-ter    |
|             |             |            |            |            | minated     |
|             |             |            |            |            |             |
|             |             |            |            |            |             |
|             |             |            |            |            |             |
|-------------|-------------|------------|------------|------------|-------------|
| SHV_DROP    | addressee   | length of  | ignored    | ignored    | ignored     |
|             | the name    | the name.  |            |            |             |
|             | of the      | If 0, the  |            |            |             |
|             | variable    | name is    |            |            |             |
|             |             | assumed    |            |            |             |
|             |             | to be null-|            |            |             |
|             |             | terminated.|            |            |             |
|             |             |            |            |            |             |
|             |             |            |            |            |             |
+-------------+-------------+------------+------------+------------+-------------+

RETURN VALUE

The execshv function returns a negative value if the operation cannot be performed and a nonnegative value if it is performed. <exec.h> defines the negative values from execshv as follows:
SHV_NO_SUBCOM
specifies that under CMS, neither REXX, EXEC2, nor any subcommand environment is active. Under TSO, no CLIST or REXX EXEC is active. Under OpenEdition, no SUBCOM environment is active or no REXX exec is active.
SHV_NO_MEM
specifies that there is not enough memory available to complete the operation.
SHV_LIBERR
specifies an execshv internal library error.

SHV_INVALID_VAR
specifies that the variable name or value is invalid. The name does not adhere to environmental restrictions (for example, the name contains invalid characters, the length is less than 0, the length is greater than 250 for REXX or 252 for TSO CLIST, and so on) or the value is invalid (for example, longer than 32K bytes in a TSO CLIST environment).
SHV_INVALID_FUNC
specifies that the function code is not one of SHV_SET, SHV_FETCH, SHV_DROP, SHV_FIRST, or SHV_NEXT.
SHV_NOT_SUPPORTED
specifies that the current environment does not support or have the TSO CLIST variable interface module IKJCT441. This module is supplied with TSO/E Version 1 Release 2.1 or higher systems.
SHV_SIGNAL
specifies that the REXX interface process was terminated by an OpenEdition signal.
Nonnegative return values from execshv are any one or more of the following. When one or more of these conditions are true, a logical OR is performed to indicate that the condition occurred.
SHV_SUCCESS
specifies that the operation completed successfully.
SHV_NOT_FOUND
specifies that the variable name was not found.
SHV_LAST_VAR
is returned after all variables from a SHV_NEXT loop have been fetched, as in an end-of-file return. The returned variable name and value are unpredictable.
SHV_TRUNC_VAL
specifies that for SHV_FETCH, SHV_FIRST, or SHV_NEXT, the buffer addressed by vb is 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.
SHV_TRUNC_VAR
specifies that for SHV_FIRST or SHV_NEXT, this value may be returned if the buffer addressed by vn is too short to contain the entire variable name.

IMPLEMENTATION

Under CMS, execshv uses the direct interface to REXX and EXEC2 variables provided by EXECCOMM. Under TSO, execshv uses the IKJCT441 interface to CLIST and REXX variables. Under OpenEdition, execshv uses the IRXEXCOM service.

USAGE NOTES

The <exec.h> function also defines five macros for use with execshv: shvset, shvfetch, shvdrop, shvfirst, and shvnext. These macros SET, FETCH, DROP, FETCH FIRST in SEQUENCE, and FETCH NEXT in SEQUENCE, respectively, by expanding to the full form of the execshv function call to process REXX/EXEC2 or CLIST variables. Using these macros can, in some situations, simplify the use of execshv considerably. The definition of each macro is shown here, followed by an example of its use:
    /* shvset macro */
 #define shvset(vn, vb) execshv(SHV_SET, vn, 0, vb, 0, 0)

 rc = shvset("XXXVAR","THIS_VALUE");

    /* shvfetch macro */
 #define shvfetch(vn, vb, vbl)
     execshv(SHV_FETCH, vn, 0, vb, vbl, 0)

 char valbuf[50];
 rc=shvfetch("RVAR",valbuf,sizeof(valbuf));

    /* shvdrop macro */
 #define shvdrop(vn) execshv(SHV_DROP, vn, 0, NULL, 0, 0)

 rc = shvdrop("XXXVAR");

    /* shvfirst macro */
 #define shvfirst(vn, vnl, vb, vbl)
     execshv(SHV_FIRST, vn, vnl, vb, vbl, 0)

    /*shvnest macro */
 #define shvnext(vn, vnl, vb, vbl)
     execshv(SHV_NEXT, vn, vnl, vb, vbl, 0)

 char vname[50], valbuf[255];
 rc = shvfirst(vname,vnl,valbuf,sizeof(valbuf));
 rc = shvnext (vname,vnl,valbuf,sizeof(valbuf));
 

EXAMPLE

The following program, named shutstc.c, demonstrates how to list all current REXX, EXEC2 or CLIST variables accessible to a program.
 #include <exec.h>
 #include <lcio.h>
 #include <stdio.h>

 main()
 {
    int rc;
    int len;
    char namebuf[20];
    char valbuf[200];

    rc = execshv(SHV_FIRST,namebuf,20,valbuf,200,&len);
 
    while (rc >= 0 && !(rc & SHV_LAST_VAR)) {
       if (rc & SHV_TRUNC_VAR) {
          puts("Variable name truncated.");
       }
       if (rc & SHV_TRUNC_VAL) {
          puts("Variable 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 = execshv(SHV_NEXT,namebuf,20,valbuf,200,&len);
    }
 }
 
The following EXEC, which should be named shutst.exec, will declare several variables. The shutstc.c can be called to print these variables to the terminal.
 Arnie = "cute"
 Becca = "beautiful"
       .
       .
       .
 'shutstc'
 exit(0)
 

Examples of SUBCOM Processing

These examples demonstrate the SUBCOM interface to CMS and TSO. In the first example, three subcommands are accepted: ECHO repeats operands, SETRC sets a return code, and EXEC invokes a TSO CLIST or CMS EXEC. The program can be executed either interactively or noninteractively depending on the value of the interact option in execinit.

A copy of this example is provided with the compiler and library. See your SAS Software Representative for SAS/C compiler products for more information.

 #include <exec.h>
 #include <ctype.h>
 #include <lcstring.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <errno.h>

 main(int argc, char **argv)
 {
       /* If 0, next input expected from EXEC.  If nonzero, next input */
       /* expected from terminal.                                      */
    int interact;

    char *input, *cmdname, *operands;
    int maxrc = 0;
    int thisrc;
    char msgbuff[120];
    int result;

    interact = (argc > 1 && tolower(*argv[1]) == 'i');

       /* If first argument starts with 'i', use interactive mode.     */
    result = execinit("EXAMPLE", interact);
    if (result  != 0) exit(EXIT_FAILURE);
       /* Check for failure of execinit */

    for(;;) {
       operands = input = execget();

          /* Obtain next input line. */
       if (!input)
          break;

       cmdname = execid(&operands);

          /* Obtain command name.  If error in execid, expect          */
          /* CLIST/EXEC input next.                                    */
       if (!cmdname) {
          interact = 1;
          continue;
       }

       strupr(cmdname);                   /* Upper case command name   */
 
       if (!*cmdname)                     /* Check for null input.     */
          continue;

          /* If execid did an EXEC for us, note and continue.          */
       else if (strcmp(cmdname, "*EXEC") == 0) {
          interact = 0;
          continue;
       }

          /* If we just switched from EXEC to the terminal, note this. */
       else if (strcmp(cmdname, "*ENDEXEC") == 0) {
          interact = 1;
          thisrc = atoi(operands);

             /* Extract EXEC return code. Remember it might be missing.*/
          if (operands)
             sprintf(msgbuff, "EXEC return code %d", thisrc);
          else
             strcpy(msgbuff, "EXEC return code unavailable");

             /* Inform user of return code. */
          result = execmsg("EXAM001I", msgbuff);
          if (result != 0 && errno == EINTR) break;
             /* Check for unexpected signal */
       }

          /* Terminate if END received. */
       else if (strcmp(cmdname, "END") == 0)
          break;

       else if (strcmp(cmdname, "EXEC") == 0) {

             /* Call EXEC for EXEC subcommand and expect input from it.*/
          thisrc = execcall(input);
          interact = 0;
          if (thisrc != 0) {
             sprintf(msgbuff,"EXEC failed with return code %d",thisrc);
             result = execmsg("EXAM005E",msgbuff);
             if (result != 0 && errno == EINTR) break;
                /* just quit after OpenEdition signal */
          }
       }

          /* If command is ECHO, repeat its operands.                  */
       else if (strcmp(cmdname, "ECHO") == 0) {
          result = execmsg(0, operands);
          if (result != 0 && errno == EINTR) break;
          thisrc = 0;
       }

          /* If command is SETRC, set return code as requested.     */
       else if (strcmp(cmdname, "SETRC") == 0) {
          char *number_end;
          thisrc = strtol(operands, &number_end, 10);
          if (!number_end) {
             sprintf(msgbuff, "Invalid return code: %s",operands);
             result = execmsg("EXAM002E", msgbuff);
             if (result != 0 && errno == EINTR) break;
             thisrc = 12;
          }
          else {
             sprintf(msgbuff, "Return code set to %d", thisrc);
             execmsg("EXAM003I", msgbuff);
             if (result != 0 && errno == EINTR) break;
          }
       }

          /* If unknown command name, try to EXEC it.               */
       else {
          errno = 0;                  /* Make sure errno is clear   */
          thisrc = execcall(input);
          if (thisrc != 0 && errno == EINTR) break;
          interact = 0;
       }
       maxrc = execrc(thisrc);        /* Inform EXEC of return code.*/
       if (maxrc < 0 && errno == EINTR) break;
    }
    sprintf(msgbuff, "Maximum return code was %d", maxrc);
    execmsg("EXAM004I", msgbuff);     /* Announce max return code.  */
    execend();                        /* Cancel SUBCOM connection.  */
    return maxrc;                     /* Return max return code.    */
 }
 

CLIST Example for SUBCOM Processing

The following CLIST illustrates the use of subcommand processing under TSO. The CLIST provides subcommands to the SAS/C SUBCMD sample.
 CONTROL NOCAPS
 WRITE SUBCOM EXAMPLE: starting.

    /* Issue the echo subcommand a few times. */
 ECHO This is the first subcommand.
 ECHO Your userid is &SYSUID..
 SETRC 0     /* Try the SETRC subcommand. */
 WRITE SUBCOM EXAMPLE: return code is now &LASTCC
 SETRC 8     /* Set the return code to 8 */
 WRITE SUBCOM EXAMPLE: return code is now &LASTCC
 SETRC 0     /* and back to 0 */
 WRITE

    /* Experiment with 'execmsg' by changing current       */
    /* message handling.  The PCF-II X facility is used to */
    /* direct PROFILE requests to TSO.                     */
 X PROFILE MSGID    /* Request message ID display.         */
 WRITE PROFILE MSGID (complete message)
 SETRC 0
 WRITE
 X PROFILE NOMSGID  /* Request message ID suppression.     */
 WRITE PROFILE NOMSGID (just the text)
 SETRC 0
 WRITE
 CONTROL NOMSG
 WRITE CONTROL NOMSG (no message)
 SETRC 0
 WRITE
 CONTROL MSG
 WRITE SET SYSOUTTRAP = 5 (trap messages within CLIST)
 SET SYSOUTTRAP = 5
 SETRC 0
 SET I = 1
 WRITE Output produced by SETRC subcommand:
 DO WHILE &I <= &SYSOUTLINE    /* Write output of SETRC subcommand. */
    SET MSG = &STR(&&SYSOUTLINE&I)
    WRITE &MSG
    SET I = &I + 1
 END
 SET SYSOUTTRAP = 0
 WRITE

    /* Tell the example program to invoke another CLIST,   */
    /* this one called SUBSUB.  When it finishes, display  */
    /* its return code.  SUBSUB returns the sum of its     */
    /* arguments as its return code.                       */
 %SUBSUB 12 2
 WRITE SUBCOM EXAMPLE: subsub returned &LASTCC.
 END

    /* Invoked by SUBCOM CLIST */
 PROC 2 VAL1 VAL2
 CONTROL NOCAPS
 WRITE SUBSUB EXAMPLE: Received arguments &VAL1 and &VAL2
 EXIT CODE(&VAL1+&VAL2)
 

TSO REXX Example for SUBCOM Processing

The following REXX EXEC illustrates the use of subcommand processing with TSO REXX. The EXEC provides input to the SAS/C SUBCMD example.
 address example                /* REXX SUBCOM example                 */

    /* Issue the echo subcommand a few times.                          */
 "echo This is the first subcommand."
 "echo Your userid is " sysvar("SYSUID")
 "setrc 0"                                 /* try the SETRC subcommand */
 say "SUBCOMR EXAMPLE: return code is now " RC
 "setrc 8"                                 /* Set the return code to 8 */
 say "SUBCOMR EXAMPLE: return code is now " RC
 "setrc 0"                                 /* and back to 0            */
 say " "

   /* Put an echo subcommand on the stack for execution after the EXEC */
   /* completes.                                                       */
 queue 'echo This line is written after the EXEC is completed.'
 
   /* Experiment with "execmsg" by changing current message handling.  */
 address tso "profile msgid"
 say "PROFILE MSGID (complete message)"
 "setrc 0"
 say " "
 address tso "profile nomsgid"
 say "PROFILE NOMSGID (just the text)"
 "setrc 0"
 say " "
 save = msg("OFF")
 say "MSG(OFF) (no message)"
 "setrc 0"
 say " "
 save = msg(save)

    /* Tell the example program to invoke a CLIST named SUBSUB. When   */
    /* it finishes, display its return code. SUBSUB returns the sum    */
    /* of its arguments as its return code.                            */
 "%subsub 12 2"
 say "SUBCOMR EXAMPLE: subsub returned " RC

    /* Now invoke a REXX EXEC named SUBSUBR, which performs the same   */
    /* function as the SUBSUB CLIST. Note that the negative return     */
    /* code produced by this call to subsubr will be flagged as an     */
    /* error by REXX.                                                  */
 "%subsubr 5 -23"
 say "SUBCOMR EXAMPLE: subsubr returned " RC
 
The following is the SUBSUBR EXEC invoked by the previous example.
    /* REXX EXEC invoked by SUBCOMR EXEC                               */
 parse arg val1 val2 .
 say "SUBSUBR EXAMPLE: Received arguments " VAL1 " and " VAL2
 exit val1+val2

 

CMS EXEC Example for SUBCOM Processing

The following example illustrates the use of the SAS/C SUBCOM facility under CMS. Subcommands are provided to the SUBCOM SAS/C program. By default, the subcommands are sent to the environment with the same name as the macro filetype, in this case "EXAMPLE".
    /*  REXX */
 say 'SUBCOM EXAMPLE: starting.'
    /* Issue the echo subcommand a few times.                          */
 'echo This is the first subcommand.'
 'echo Your userid is' userid()'.'
 'setrc 0'                           /* Try the setrc subcommand.      */
 say 'SUBCOM EXAMPLE: return code is now' rc
                                     /* REXX variable 'rc'             */
 'setrc 8'                           /* Set the return code to 8.      */
 say 'SUBCOM EXAMPLE: return code is now' rc
                                     /* REXX variable 'rc'             */
 'setrc 0'                           /* And back to 0.                 */
 say ''

    /* Experiment with 'execmsg' by changing the current EMSG setting. */
    /* The subcommand 'setrc 0' will cause a message (or part of a     */
    /* message or nothing) to be sent. Note that the REXX 'address'    */
    /* statement is used to change the subcommand environment to CMS.  */
 address  COMMAND 'CP SET EMSG ON'        /* Display both ID and text. */
 say 'SET EMSG ON (complete message)'
 'setrc 0'
 say ''
 address COMMAND 'CP SET EMSG CODE'       /* Display just the ID.      */
 say 'SET EMSG CODE (just the id)'
 'setrc 0'
 say ''
 address COMMAND 'CP SET EMSG TEXT'       /* Display just the text.    */
 say 'SET EMSG TEXT (just the text)'
 'setrc 0'
 say ''
 address COMMAND 'CP SET EMSG OFF'        /* Don't display anything.   */
 say 'SET EMSG OFF (no message)'
 'setrc 0'
 say ''
 address COMMAND 'CP SET EMSG ON'         /* Back to normal.           */

    /* Finally, tell SUBCOM C to invoke another macro, this one called */
    /* 'SUBSUB'. When it finishes, display the return code. SUBSUB     */
    /* returns the number of arguments it got as the return code.      */
 'exec subsub arg1 arg2'
 say 'SUBCOM EXAMPLE: subsub was passed' rc 'arguments.'
 trace ?r
 'end'
 exit 0
    /* invoked by SUBCOM EXAMPLE                                       */
 arg args
 say 'SUBSUB EXAMPLE: Got' words(args) 'arguments.'
 return words(args)
 

Guidelines for Subcommand Processing

Under CMS, programs receive an "*ENDEXEC" string indicating that an EXEC invoked externally has completed, and the program has been reentered during CMS end-of-command processing via the L$CEXEC nucleus extension (see the execinit description earlier in this chapter). Noninteractive execution of SUBCOM applications cannot be supported in bimodal CMS because, by the time the the library's end-of-command nucleus extension is entered, CMS has already purged program modules from memory.

Under the OpenEdition shell, the SAS/C REXX interface runs as a separate process from the calling program to prevent interference between the program and EXEC processing. Since the REXX interface is in a separate process, it is possible that the interface could be terminated by a signal at any time. When this occurs, on the next call to a SUBCOM function, errno is set to EINTR. Because the REXX interface has been terminated, the program cannot thereafter use any SUBCOM functions other than execend. Once execend has been called, it is possible to call execinit to create a new SUBCOM environment.


Copyright (c) 1998 SAS Institute Inc. Cary, NC, USA. All rights reserved.