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.
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.
execget
, which obtains a subcommand from an appropriate source
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
execmsg
, which sends diagnostic messages, the exact form of which
can be controlled by the user
execrc
, which sets the return code from the completed subcommand
and may perform additional system-dependent processing.
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
execend
execget
execid
execinit
execmsg
execmsi
execrc
execshv
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.*/
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.
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.
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
.
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).
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.
#include <exec.h> int execcall(const char *cmd);
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
.)
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.
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.
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.
execget
function and the comprehensive SUBCOM
example.
#include <exec.h> int execend(void);
execend
function is called to terminate subcommand
processing.
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.
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.
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.
execget
function and the comprehensive SUBCOM example.
#include <exec.h> char *execget(void);
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"
.
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
.
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.
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.
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.
#include <exec.h> char *execid(char **cmdbuf);
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.
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"
.)
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.
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.
#include <exec.h> char *cmdname, *operands; cmdname = execid(&operands);
Note: Also see the comprehensive SUBCOM example.
#include <exec.h> int execinit(const char *envname, int interactive);
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.
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.
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.
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.
#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.
#include <exec.h> int execmsg(const char *id, char *msg);
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.
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.
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:
id
is NULL
msg
is NULL
.
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.
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.
#include <exec.h> static char *id="EXEC-MSG"; execmsg(id,"File not found");
Note: See also the comprehensive SUBCOM example.
#include <exec.h> int execmsi(void);
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.
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).
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; }
#include <exec.h> int execrc(int rc);
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.
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.
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.
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.
execget
function and the comprehensive
SUBCOM example.
#include <exec.h> int execshv(int code, char *vn, int vnl, char *vb, int vbl, int *vl);
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:
char
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
SHV_FETCH
vb
. The variable name is translated to
uppercase before being used.
SHV_FIRST
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
execshv
with SHV_FIRST
or any other value for code
.
SHV_SET
vn
, is ignored (no distinction is made between
uppercase and lowercase characters).
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.| | | | | | | | | | | | | | | | | | +-------------+-------------+------------+------------+------------+-------------+
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
SHV_NO_MEM
SHV_LIBERR
execshv
internal library error.
SHV_INVALID_VAR
SHV_INVALID_FUNC
SHV_SET
,
SHV_FETCH
,
SHV_DROP
, SHV_FIRST
, or SHV_NEXT
.
SHV_NOT_SUPPORTED
SHV_SIGNAL
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
SHV_NOT_FOUND
SHV_LAST_VAR
SHV_NEXT
loop have been fetched,
as in an end-of-file return. The returned variable name and value are
unpredictable.
SHV_TRUNC_VAL
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
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.
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.
<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));
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)
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. */ }
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)
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 " RCThe 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
/* 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)
"*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.