REXX Complete Reference
REXX Complete Reference
IBMMAINFRAMES.com
About REXX
What is Rexx?
Rexx is a procedural programming language that allows programs and algorithms to be written in a clear and structured way. It is easy to use by experts and casual users alike. Rexx has been designed to make easy the manipulation of the kinds of symbolic objects that people normally deal with such as words and numbers. Although Rexx has the capability to issue commands to its host environment and to call programs and functions written in other languages, Rexx is also designed to be independent of its supporting system software when such commands are kept to a minimum.
Rexx provides powerful character and arithmetical abilities in a simple framework. It can be used to write simple programs with a minimum of overhead, but it can also be used to write robust large programs. It can be used for many of the programs for which BASIC would otherwise be used, and its layout may look somewhat similar to that of a structured BASIC program. Note, however, that Rexx is not BASIC!
Many applications are programmable by means of macros. Unfortunately, in the Unix world, almost every application has a different macro language. Since Rexx is essentially a character manipulation language, it could provide the macro language for all these applications, providing an easy-to-use and consistent interface across all applications. The best examples of such systems are on CMS (IBM's mainframe operating system which was the birthplace of Rexx) and on the Amiga. However, IBM's OS/2 is catching up, and now that Rexx is freely available on Unix it cannot be long before applications start to appear which have Rexx as their macro language. Two products already exist. They are the Workstation Group's Uni-XEDIT and Mark Hessling's THE (a link to which is displayed on my Rexx title page).
Rexx treats any instruction that it doesn't immediately recognise as an expression which it evaluates and passes to the host environment as a command. A simple XEDIT macro in Rexx looks like this.
/* this XEDIT macro centres the line containing the cursor. */ width = 72 /* Width within which to centre the line */ "extract /cursor /curline" /* location of cursor and current line # */ if cursor.3=-1 /* if cursor is not on a line... */ then "emsg Cursor not in a data line" /* then give an error message */ else do restore=curline.2-cursor.1 /* how far cursor is from current */ ":"||cursor.3 /* make cursor line current */
IBMMAINFRAMES.com
Rexx can be used as an "application glue" language, in a manner similar to that in which shell scripts are often used. Since Rexx is able to pass arbitrary command strings for execution by its environment, it can be used to execute Unix programs as well as providing the control language necessary for testing things such as parameters and return codes and acting accordingly.
Rexx is often executed by an interpreter, and this permits rapid program development. This productivity advantage makes the language very suitable for modelling applications and products - in other words, for prototype development. Rexx is also fairly easy to debug. Once a design has been shown to work satisfactorily, it can be easily recoded in another language if that is required for performance or other reasons. The design of Rexx is such that the same language can effectively and efficiently be used for many different applications that would otherwise require the learning of several languages.
REXX Instructions
Keyword SAY, PARSE, IFThenElse, DO Assignment Variable assignment statements, e.g., VAR1 = 20 Label Used with subroutines and functions, e.g., MYLABEL: Null comment lines (/*..*/) and blank lines Commands dependent on the environment, TSO, MVS, ISPF, USS
Always begin a REXX routine with a comment line containing the word REXX. Example: /* REXX this is a REXX routine */ SAY 'Enter your name' PULL name IF name = '' THEN SAY 'No name entered' ELSE SAY 'Hello' name EXIT
IBMMAINFRAMES.com
IBMMAINFRAMES.com
A constant, which is a number that is expressed as: An integer (12) A decimal (12.5) A floating point number (1.25E2) A signed number (-12) A string constant (' 12') or ('C1'x)
A string, which is one or more words that may or may not be enclosed in quotation marks, such as: THIS VALUE IS A STRING. 'This value is a literal string.'
The value from another variable, such as: variable1 = variable2 In the above example, variable1 changes to the value of variable2, but variable2 remains the same.
An expression, which is something that needs to be calculated, such as: variable2 = 12 + 12 - .6 /* variable2 becomes 23.4 */
Before a variable is assigned a value, its value is its own name translated to uppercase characters. For example: var1 = orange If orange has not been assigned a value then orange has the value ORANGE and var1 then has the value ORANGE.
IBMMAINFRAMES.com
Compound variables
Compound variables can be used in REXX to store groups of related data in such a way that the data can be easily retrieved. Compound variables create a one-dimensional array or list of variables in REXX. For example, a list of employee names can be stored in an array called employee and retrieved by number. EMPLOYEE (1) Adams, Joe (2) Crandall, Amy (3) Devon, David (4) Garrison, Donna In some computer languages, you access an element in the array by the number of the element, such as, employee(1), which retrieves "Adams, Joe". In REXX, you use compound variables. A compound variable contains at least one period and at least two other characters. It cannot start with a digit or a period, and if there is only one period in the compound symbol, it cannot be the last character. abc.d is a compound variable. It is comprised of a stem, abc., followed by simple variables called the tail. The derived name of a compound symbol is the stem of the symbol, in uppercase, followed by the tail, in which all simple symbols have been replaced with their values. Examples: /* rexx d = 5 /* assigns '5' to the variable D e = 6 /* assigns '6' to the variable E abc.d = "Hello" /* "Hello" to abc.5 abc.e = "there" /* "there" to abc.6 SAY abc.d abc.6 abc.f /* displays Hello there ABC.F /* rexx a=3 /* assigns '3' z=4 /* c='Fred' /* a.z='Fred' /* a.fred=5 /* a.c='Bill' /* c.c=a.fred /* y.a.z='Annie' /* to the variable A '4' to Z 'Fred' to C 'Fred' to A.4 '5' to A.FRED 'Bill' to A.Fred '5' to C.Fred 'Annie' to Y.3.4 */ */ */ */ */ */ */ */ */ */ */ */ */ */ */
say a z c a.a a.z a.c c.a a.fred y.a.4 /* displays the string: */ /* "3 4 Fred A.3 Fred Bill C.3 5 Annie" */
IBMMAINFRAMES.com
You can use a DO loop to initialize a group of compound variables and set up an array. DO i = 1 TO 4 SAY 'Enter an employee name.' PARSE PULL employee.i END If you entered the same names used in the earlier example of an array, you would have a group of compound variables as follows: employee.1 = 'Adams, Joe' employee.2 = 'Crandall, Amy' employee.3 = 'Devon, David' employee.4 = 'Garrison, Donna' When the names are in the group of compound variables, you can easily access a name by its number, or by a variable that represents its number. name = 3 SAY employee.name SAY employee.2 /* Displays 'Devon, David' */ /* Displays 'Crandall, Amy' */
IBMMAINFRAMES.com
Stems
A stem is a symbol that contains just one period, which is the last character. It cannot start with a digit or a period. Examples: JOHN. B. ARRAY. EMPLOYEE. By default, the value of a stem is the string consisting of the characters of its symbol (that is, translated to uppercase). If the symbol has been assigned a value, it names a variable and its value is the value of that variable. Example: /* rexx */ a = stem.1 b = stem.any stem.3 = "assigned" stem.4 = assigned say a stem.2 b stem.3 stem.4 /* displays STEM.1 STEM.2 STEM.ANY assigned ASSIGNED
*/
When a stem is used as the target of an assignment then all possible compound variables whose names begin with that stem receive the new value regardless of whether they previously had a value or not. In the following example, all possible compound variables whose names begin abc. are assigned a value of "Anyone" by the first statement, and then some of the compound variables are reassigned. abc. = "Anyone" d = 5 abc.d = "Hello!" abc.6 = "home?" SAY abc.d abc.f abc.6 abc.7 abc.8 abc.anything /* displays Hello! Anyone home? Anyone Anyone Anyone
*/
You can use stems with the EXECIO command when reading from and writing to a data set. This will be covered in a later section. Stems can also be used with the OUTTRAP TSO/E external function when trapping command output. See the TSO/E REXX User's Guide for more information. There is also an example at the end of this document.
IBMMAINFRAMES.com
IF/THEN/ELSE
At least one instruction should follow the THEN and ELSE clauses. When either clause has no instructions, it is good programming practice to include NOP (no operation) next to the clause. IF expression THEN Instruction ELSE NOP If you have more than one instruction for a condition, begin the set of instructions with a DO and end them with an END. IF weather = rainy THEN SAY "Find a good book." ELSE DO SAY "Would you like to play tennis or golf?" PULL answer END Without the enclosing DO and END, the language processor assumes only one instruction for the ELSE clause. When nesting IF/THEN/ELSE instructions within other IF/THEN/ELSE instructions it is important to match each IF with an ELSE and each DO with an END otherwise the results can be unpredictable. Example: weather = "fine" tenniscourt = "occupied" IF weather = "fine" THEN DO SAY "What a lovely day!" IF tenniscourt = "free" THEN SAY "Shall we play tennis?" ELSE NOP END ELSE SAY "Shall we take our raincoats?" And the result of executing this is: What a lovely day!
IBMMAINFRAMES.com
Without the DO/END/ELSE NOP the code looks like this: weather = "fine" tenniscourt = "occupied" IF weather = "fine" THEN SAY "What a lovely day!" IF tenniscourt = "free" THEN SAY "Shall we play tennis?" ELSE SAY "Shall we take our raincoats?" And the result of executing this is: What a lovely day! Shall we take our raincoats? Moral of the story is to be sure to use DO and END to group instructions in an IFTHENELSE set and when nesting IFTHENELSE include an ELSE NOP where appropriate.
IBMMAINFRAMES.com
Looping
Looping through your code is controlled by the use of the DO and END instruction. We saw a simple DOEND in our earlier discussion of the IFTHENELSE sequence. This DOEND sequence caused the instructions contained within to be executed only one time. Using DO with a repetitor expression we can cause the set of instructions to be executed any number of times. Examples are: DO 5 SAY "Hello!" END OR number = 5 DO number SAY "Hello!" END This will display Hello! 5 times. Note: number must evaluate to a whole number otherwise you will receive an error. The most common repetitive DO loop is of the form: DO X = Start TO Fini BY Incr Start, Fini, and Incr must evaluate to numbers or an error will occur. If BY Incr is not specified it defaults to +1. Two other common DO loop forms are: and DO WHILE expression DO UNTIL expression
In a DO WHILE loop the expression is evaluated before the group of instructions is executed. In a DO UNTIL the expression is evaluated after the group of instructions is executed. In other words, a DO UNTIL will always execute its group of instructions at least one time whereas a DO WHILE may not execute at all. The last DO loop to be discussed is: DO FOREVER This is a continuous loop, as you would surmise. To exit this loop construction you must use either EXIT, which will end the routine all together, or the LEAVE instruction. LEAVE may be used in all DO loops to end the loop prematurely. The LEAVE instruction affects the innermost loop in a set of nested loops unless the control variable for a loop is specified as part of the instruction.
IBMMAINFRAMES.com
Example 1: count = 0 DO FOREVER SAY "HERE WE GO LOOP-DE-LOOP" count = count + 1 IF count > 5 THEN LEAVE END The output from this execution looks like this: HERE HERE HERE HERE HERE HERE WE WE WE WE WE WE GO GO GO GO GO GO LOOP-DE-LOOP LOOP-DE-LOOP LOOP-DE-LOOP LOOP-DE-LOOP LOOP-DE-LOOP LOOP-DE-LOOP
Example 2: DO I = 1 TO 5 SAY "HERE WE GO LOOP-DE-LOOP" DO J = 1 TO 2 SAY "LOOP-DE-LOOP" IF I > 3 THEN LEAVE J ELSE NOP END END The execution output looks like: HERE WE GO LOOP-DE-LOOP LOOP-DE-LOOP LOOP-DE-LOOP HERE WE GO LOOP-DE-LOOP LOOP-DE-LOOP LOOP-DE-LOOP HERE WE GO LOOP-DE-LOOP LOOP-DE-LOOP LOOP-DE-LOOP HERE WE GO LOOP-DE-LOOP LOOP-DE-LOOP HERE WE GO LOOP-DE-LOOP LOOP-DE-LOOP
IBMMAINFRAMES.com
Example 3: DO I = 1 TO 5 SAY "HERE WE GO LOOP-DE-LOOP" DO J = 1 TO 2 SAY "LOOP-DE-LOOP" IF I > 3 THEN LEAVE I ELSE NOP END END The execution output looks like: HERE WE GO LOOP-DE-LOOP LOOP-DE-LOOP LOOP-DE-LOOP HERE WE GO LOOP-DE-LOOP LOOP-DE-LOOP LOOP-DE-LOOP HERE WE GO LOOP-DE-LOOP LOOP-DE-LOOP LOOP-DE-LOOP HERE WE GO LOOP-DE-LOOP LOOP-DE-LOOP
IBMMAINFRAMES.com
Parsing
Parsing is the act of separating data into one or more variables. There are several forms of the PARSE instruction but the most commonly used are: PARSE PULL PARSE ARG PARSE VAR Each of these may also include UPPER to force all character information to uppercase before putting it into the variables; e.g., PARSE UPPER VAR. The instructions PULL and ARG may substitute for PARSE UPPER PULL and PARSE UPPER ARG respectively. ARG is used to pass arguments to another exec, a subroutine or a function. Parsing separates the data by comparing the data to a template or pattern of variable names. Separators in a template can be a blank (most common), string, variable, or number that represents a column position. The simplest, and most common, template is a group of variable names separated by blanks. Each variable name gets one word of data in sequence except for the last variable which gets the remainder of the data. Example: /* REXX */ ptst = "A B C D E" PARSE VAR ptst v1 v2 v3 SAY "V1=" v1 /* v1 CONTAINS WORD 1 (A) SAY "V2=" v2 /* v2 CONTAINS WORD 2 (B) SAY "V3=" v3 /* v3 CONTAINS ALL REMAINING WORDS EXIT When executed the result is: V1= A V2= B V3= C D E
*/ */ */
IBMMAINFRAMES.com
Another template can use a string, sometimes called a delimiter, to separate data as long as the data contains the string as well. /* REXX - PARSE with a delimiter */ ptst = "AA BB:CC DD EE" PARSE VAR ptst v1":" v2 SAY "V1=" v1 SAY "V2=" v2 SAY "V3=" v3 EXIT When executed the result is: V1= AA BB V2= CC DD EE V3= V3 A string or delimiter may also be a variable in the event you dont know what the separator should be in advance. In this case the delimiter name must be enclosed in parentheses. /* REXX - PARSE with a delimiter as a variable */ ptst = "AA BB:CC:DD EE" delim = ":" PARSE VAR ptst v1 (delim) v2 (delim) v3 SAY "V1=" v1 SAY "V2=" v2 SAY "V3=" v3 EXIT When executed the result is: V1= AA BB V2= CC V3= DD EE
IBMMAINFRAMES.com
Numbers can be used in the template to indicate at which column to separate data. An unsigned integer indicates an absolute column position and a signed integer indicates a relative column position. Absolute column position - the unsigned integer in the template separates the data according to absolute position. The first segment begins at column 1 and continues up to but does not include the column number specified. Subsequent segments begin at the column number specified. /* REXX - PARSE using absolute column positioning */ ptst = "AAAABBCCCCCDDDDDDEEE" PARSE VAR SAY "V1=" SAY "V2=" SAY "V3=" EXIT ptst v1 5 v2 7 v3 v1 /* v1 CONTAINS ONLY COLUMNS 1-4 v2 /* v2 CONTAINS ONLY COLUMNS 5-6 v3 /* v3 CONTAINS ALL FROM COLUMN 7 TO THE END */ */ */
When executed the result is: V1= AAAA V2= BB V3= CCCCCDDDDDDEEE Relative column positions a signed integer in the template separates data according to the relative column position. The signed integer specifies the length of the data assigned to the variable preceding it in the template. Negative numbers are allowed, just be sure of what you are trying to do. /* REXX - PARSE using relative column positioning */ ptst = "AAAABBCCCCCDDDDDDEEE" PARSE VAR SAY "V1=" SAY "V2=" SAY "V3=" EXIT ptst v1 +4 v2 +2 v3 v1 /* v1 CONTAINS ONLY COLUMNS 1-4 v2 /* v2 CONTAINS ONLY COLUMNS 5-6 v3 /* v3 CONTAINS ALL FROM COLUMN 7 TO THE END */ */ */
When executed the result is: V1= AAAA V2= BB V3= CCCCCDDDDDDEEE
IBMMAINFRAMES.com
Examples: /* REXX - PARSE using a period in the parse template */ ptst = "AA BB CC DD EE" SAY "PERIOD AS PLACEHOLDER" PARSE VAR ptst v1 v2 . . v3 SAY "V1=" v1 /* v1 CONTAINS WORD 1 (AA) */ SAY "V2=" v2 /* v2 CONTAINS WORD 2 (BB) */ SAY "V3=" v3 /* v3 CONTAINS WORD 5 (EE) */ DROP v1 DROP v2 DROP v3 SAY SAY "PERIOD AS A COLLECTOR OF UNWANTED INFORMATION" PARSE VAR ptst v1 . v2 . SAY "V1=" v1 /* v1 CONTAINS WORD 1 (AA) */ SAY "V2=" v2 /* v2 CONTAINS WORD 2 (CC) */ EXIT When executed the result is: PERIOD AS PLACEHOLDER V1= AA V2= BB V3= EE PERIOD AS A COLLECTOR OF UNWANTED INFORMATION V1= AA V2= CC
/* REXX - parse example with periods parse source . . . . . . . ENV . . if ENV = 'OMVS' then do /* are (instructions) end if ENV = 'TSO/E' then do /* are (instructions) end if ENV = 'ISPF' then do /* are (instructions) end if ENV = 'MVS' then do /* are (instructions) end
as placeholders */ we running under UNIX ? we running under TSO/E? we running under ISPF? we running under MVS? */ */ */ */
IBMMAINFRAMES.com
The search order for functions is: internal routines take precedence, then built-in functions, and finally external functions.
Calling a subroutine:
To call a subroutine, use the CALL instruction. The subroutine call is an entire instruction: CALL subroutine_name argument1, argument2, argument3,.... A subroutine does not have to return a value, but when it does, it sends back the value with the RETURN instruction. RETURN value The calling exec receives the value in the REXX special variable named RESULT. SAY 'The answer is' RESULT
Calling a function:
A function call is part of an instruction, for example, and assignment instruction: var1 = function(argument1, argument2, argument3,.....) A function must return a value. The value replaces the function call, so that in the previous example, var1 = value. SAY 'The answer is' var1
IBMMAINFRAMES.com
IBMMAINFRAMES.com
IBMMAINFRAMES.com
IBMMAINFRAMES.com
Writing a Subroutine
The instruction that invokes the subroutine is the CALL instruction. The CALL instruction may be used several times in an exec to invoke the same subroutine. When the subroutine ends, it can return control to the instruction that directly follows the subroutine call. The instruction that returns control is the RETURN instruction. instruction(s) CALL sub1 Instruction(s) EXIT sub1: instruction(s) RETURN
Subroutines may be internal and designated by a label as in the previous example, or they may be external and designated by the data set member name that contains the subroutine. The following illustrates an external subroutine. REXX.EXEC(MAIN) instruction(s) CALL sub2 Instruction(s) . . EXIT
User written external subroutines must reside in an execution library that is part of the implicit search order, for example SYSEXEC or SYSPROC, or be in the same PDS.
IBMMAINFRAMES.com
Writing a Function
An exec invokes a user-written function the same way it invokes a built-in function -- by the function name immediately followed by parentheses with no blanks in between. The parentheses can contain up to 20 arguments or no arguments at all. function(argument1, argument2,...) or function() A function requires a returned value because the function call generally appears in an expression. x = function(argument1, argument2,...) When the function ends, it uses the RETURN instruction to send back a value. instruction(s) x=func1(arg1,arg2) instruction(s) EXIT Func1: ARG var1, var2 instruction(s) RETURN value The previous example illustrated an internal function. The following illustrates an external function. REXX.EXEC(MAIN) instruction(s) x=func2(arg1,arg2) instruction(s) EXIT
IBMMAINFRAMES.com
Here is an example of an internal function that adds three numbers. Note the commas (,) between the function's arguments which are the variables "number1", "number2", and "number3". /* REXX - internal "ADD3" function example */ v1 = add3(10,20,30) v2 = add3(5,100,25) SAY "V1=" v1 SAY "V2=" v2 EXIT /* be sure to include for internal function calls */ Add3: ARG number1, number2, number3 answer = number1 + number2 + number3 RETURN answer When executed the result is: V1= 60 V2= 130
NOTE: Because internal functions and subroutines generally appear after the main part
of the exec, when you have an internal function or subroutine, it is important to end the main part of the exec with the EXIT instruction.
User written external functions must reside in an execution library that is part of the implicit search order, for example SYSEXEC or SYSPROC, or be in the same PDS. The following illustrates ADD3 written as an external function. REXX.EXEC(MYPGM) /* REXX - external function call example */ v1 = add3(10,20,30) v2 = add3(5,100,25) SAY "V1=" v1 SAY "V2=" v2 EXIT REXX.EXEC(ADD3) /* REXX - ADD3 function to add 3 numbers */ ARG number1, number2, number3 answer = number1 + number2 + number3 RETURN answer
IBMMAINFRAMES.com
Because of the data stack's unique characteristics, you can use the data stack specifically to: Store a large number of data items for a single exec's use. Pass a large number of arguments or an unknown number of arguments between a routine (subroutine or function) and the main exec. Pass responses to an interactive command that can run after the exec ends. Note: Data left in the stack when the exec ends will be treated as TSO commands and will try to execute as such. Store data items from an input data set, which were read by the EXECIO command.
IBMMAINFRAMES.com
The following examples illustrate the difference when QUEUE and PUSH are used to place data in the data stack. PARSE PULL is used to retrieve information from the stack. QUEUE example: /* REXX */ DO A = 1 TO 3 QUEUE "A=" A /* PUT DATA IN THE STACK (FIFO) */ END SAY "NUMBER OF ELEMENTS IN THE STACK IS:" QUEUED() IF QUEUED() > 0 THEN DO QUEUED() /* DISPLAY ALL THE ITEMS IN THE STACK */ PARSE PULL V1 SAY V1 END EXIT When executed the results are: NUMBER OF ELEMENTS IN THE STACK IS: 3 A= 1 A= 2 A= 3 PUSH example: /* REXX */ DO A = 1 TO 3 PUSH "A=" A /* PUT DATA IN THE STACK (LIFO) */ END SAY "NUMBER OF ELEMENTS IN THE STACK IS: " QUEUED() IF QUEUED() > 0 THEN DO QUEUED() /* DISPLAY ALL THE ITEMS IN THE STACK */ PARSE PULL V1 SAY V1 END EXIT When executed the results are: NUMBER OF ELEMENTS IN THE STACK IS: A= 3 A= 2 A= 1 3
Other data stack functions that will not be covered here include: MAKEBUF - Create a buffer in the data stack DROPBUF- Drop a buffer in the data stack QBUF - Find the number of buffers in a data stack QELEM - Find the number of elements in a buffer NEWSTACK - Create a new data stack DELSTACK - Delete the most recently created data stack QSTACK - Find the number of stacks that exist
IBMMAINFRAMES.com
Other trace output is as follows: *-* +++ >>> >.> a source line from the program a trace message from REXX an expression result (TRACE R) a value assigned to a place holder
When executed the result is: 5 *-* IF x + 1 > 5 * y >V> "9" >L> "1" >O> "10" >L> "5" >V> "2" >O> "10" >O> "0" 7 *-* ELSE *-* NOP /* No operation on the ELSE path */
IBMMAINFRAMES.com
First you see the line number (5 *-*) followed by the expression. Then the expression is broken down by operation as follows: >V> >L> >O> >L> >V> >O> >O> "9" "1" "10" "5" "2" "10" "0" (value of variable x) (value of literal 1) (result of operation x + 1) (value of literal 5) (value of variable y) (result of operation 5 * y) (result of final operation 10 > 10 is false)
Tracing Results: To trace only the final result of an expression, use the TRACE R (TRACE Results) form of the TRACE instruction. All expressions that follow the instruction are analyzed and the results are displayed as: >>> Final result of an expression If you changed the TRACE instruction operand in the previous example from an I to an R, you would see the following results. 5 *-* IF x + 1 > 5 * y >>> "0" 7 *-* ELSE *-* NOP /* No operation on the ELSE path */ With interactive debugging, you also have the ability to single or multi-step though instructions. The language processor will pause between instructions to await debug input. While paused you can enter any commands or REXX statements or re-execute the last instruction. To enable interactive debug prefix the TRACE action with a ? (for example TRACE ?R). Using the previous example with TRACE ?R you would see the following: 5 *-* IF x + 1 > 5 * y >>> "0" +++ Interactive trace.
After hitting ENTER, you would step though to the next result (if any) and the language processor would pause awaiting input. 7 *-* ELSE *-* NOP /* No operation on the ELSE path */
While paused you can continue tracing by entering a null line, type one or more additional instructions to be processed before the next instruction is traced, enter an equal sign (=) to reexecute the last instruction, or change trace options. For example, TRACE N (TRACE Normal) would turn back the default tracing options (trace failures only), TRACE O (TRACE Off) would turn off all tracing, and TRACE ? would turn off interactive tracing (but leave the current tracing options in effect).
IBMMAINFRAMES.com
Environments
The system under which REXX programs run includes at least one host command environment for processing commands. An environment is selected by default on entry to a REXX program. In TSO/E REXX, the environment for processing host commands is known as the host command environment. You can change the environment by using the ADDRESS instruction. You can find out the name of the current environment by using the ADDRESS built-in function. TSO/E provides several host command environments for a TSO/E address space (TSO/E and ISPF) and for non-TSO/E address spaces. For example, in REXX processing, a host command can be: A TSO/E command processor, such as ALLOCATE, FREE, or EXEC A TSO/E REXX command, such as NEWSTACK or QBUF A program that you link to or attach An MVS system or subsystem command that you invoke during an extended MCS console session An ISPF command or service
When you invoke a REXX exec in the TSO/E address space, the default initial host command environment is TSO (ADDRESS TSO). When you run a REXX exec in a non-TSO/E address space, the default initial host command environment is MVS (ADDRESS MVS). The CONSOLE host command environment (ADDRESS CONSOLE) is available only to REXX execs that run in the TSO/E address space. Use the CONSOLE environment to invoke MVS system and subsystem commands during an extended MCS console session. To use the CONSOLE environment, you must have CONSOLE command authority. The ISPEXEC and ISREDIT host command environments are available only to REXX execs that run in ISPF (ADDRESS ISPEXEC and ADDRESS ISREDIT). Use these environments to invoke ISPF commands and services, and ISPF edit macros. To use ISREDIT, you must be in an edit (or view) session. The LINK, LINKMVS, and LINKPGM host command environments are available to any address space and let you link to unauthorized programs on the same task level (ADDRESS LINK, ADDRESS LINKMVS, and ADDRESS LINKPGM). The ATTACH, ATTCHMVS, and ATTCHPGM host command environments are also available to any address space and let you attach unauthorized programs on a different task level (ADDRESS ATTACH, ADDRESS ATTCHMVS, and ADDRESS ATTCHPGM).
IBMMAINFRAMES.com
To change the host command environment, use the ADDRESS instruction followed by the name of an environment. The ADDRESS instruction has two forms: one affects all commands issued after the instruction, and one affects only a single command. All commands When an ADDRESS instruction includes only the name of the host command environment, all commands issued afterward within that exec are processed as that environment's commands. ADDRESS ISPEXEC /* Change environment to ISPF */ "EDIT DATASET("dsname")" . instruction(s) . "SELECT PANEL(TECH001)" . instruction(s) . . ADDRESS TSO /* Change environment back to TSO "FREE FILE(WORK)" Single Command When an ADDRESS instruction includes both the name of the host command environment and a command, only that command is affected. After the command is issued, the former host command environment becomes active again. /* Issue one command from the ISPF environment */ ADDRESS ISPEXEC "EDIT DATASET("dsname")" /* Return to the default TSO host command environment */ "ALLOC DA("dsname") F(SYSEXEC) SHR REUSE" ADDRESS Example 1: /* rexx */ SAY 'Do you know your PF keys?' PULL answer IF answer = 'NO' | answer = 'N' THEN ADDRESS ispexec "SELECT PGM(ISPOPT) PARM(ISPOPT3)" ELSE SAY 'O.K. Never mind.' ADDRESS Example 2: /* REXX */ text = 'PROGRAM' pgname 'ABENDED. PLEASE ACKNOWLEDGE BY REPLYING!' ltext = d2c(length(text),2) wtor = ltext || text ADDRESS LINKPGM "OPSWTOR wtor" Exit 0
*/
IBMMAINFRAMES.com
Commands
To send a command to the currently addressed host command environment, use a clause of the form: expression; The expression is evaluated and submitted to the host command environment. The environment then processes the command and eventually returns control to the language processor, after setting a return code. The language processor places this return code in the REXX special variable RC. The return code can then be tested for conditional processing: "ALLOC FILE(INPUT) DA(PROGA.INPUT) SHR" IF RC \= 0 THEN DO SAY 'CAN''T ALLOCATE INPUT FILE' EXIT 16 /* exit and set RC=16 */ END ELSE DO . instructions(s) . END To differentiate commands from other types of instructions, enclose the command within single or double quotation marks. Any part of the expression not to be evaluated should be enclosed in quotation marks. Here is an example of submitting a command to the TSO/E host command environment mydata = "PROGA.OUTFILE" "FREE DATASET("mydata")" This would result in the string FREE DATASET(PROGA.OUTFILE) being submitted to TSO/E. Many TSO/E commands, for example EXEC and ALLOCATE, use single quotation marks within the command. For this reason, it is recommended that, as a matter of course, you enclose TSO/E commands with double quotation marks. "ALLOC DA('USERID.MYREXX.EXEC') F(SYSEXEC) SHR REUSE" "EXEC 'USERID.MYREXX.EXEC(ADD3)' '25 78 33' EXEC" When a variable represents a fully-qualified data set name, the name must be enclosed in two sets of quotation marks to ensure that one set of quotation marks remains as part of the value. name = "'SYS3.LINKLIB'" "LISTDS" name "STATUS" Another way to ensure that quotation marks appear around a fully-qualified data set name when it appears as a variable is to include them as follows: name = SYS3.LINKLIB "LISTDS '"name"' STATUS"
IBMMAINFRAMES.com
Before you can use the EXECIO command to read from or write to a data set, the data set must meet the following requirements. An I/O data set must be: Either sequential or a single member of a PDS. Previously allocated with the appropriate attributes for its specific purpose.
Allocation can be made by using the TSO ALLOCATE command for REXX execs that execute in a TSO/E address space, or in the case of batch REXX execution, the allocations can be made with JCL. If you use EXECIO to read information from a data set and to the data stack, the information can be stored in FIFO or LIFO order on the data stack. FIFO is the default. If you use EXECIO to read information from a data set and to a list of variables, the first data set line is stored in variable1, the second data set line is stored in variable2, and so on. Data read into a list of variables can be accessed randomly. After the information is in the data stack or in a list of variables, the exec can test it, copy it to another data set, or update it before returning it to the original data set.
Specify the number of lines to read: To open a data set without reading any records, put a zero immediately following the EXECIO command and specify the OPEN operand. "EXECIO 0 DISKR mydd (OPEN"
To read a specific number of lines, put the number immediately following the EXECIO command. "EXECIO 25 ..." To read the entire data set, put an asterisk immediately following the EXECIO command. "EXECIO * ..."
IBMMAINFRAMES.com
When all the information is on the data stack, either queue a null line to indicate the end of the information, or if there are null lines throughout the data, assign the built-in QUEUED() function to a variable to indicate the number of items on the stack. Depending on the purpose you have for the input data set, use either the DISKR or DISKRU operand of EXECIO to read the data set. DISKR - Reading Only - To initiate I/O from a data set that you want to read only, use the DISKR operand with the FINIS option. The FINIS option closes the data set after the information is read. Closing the data set allows other execs to access the data set and the ddname. "EXECIO * DISKR ... (FINIS"
Note: Do not use the FINIS option if you want the next EXECIO statement in your exec to continue reading at the line immediately following the last line read. DISKRU - Reading and Updating - To initiate I/O to a data set that you want to both read and update, use the DISKRU operand without the FINIS option. You can update only the last line that was read therefore, you usually read and update a data set one line at a time, or go immediately to the single line that needs updating. The data set remains open while you update the line and return the line with a corresponding EXECIO DISKW command. "EXECIO 1 DISKRU ..."
In order to access a data set for I/O, it must first be allocated to a ddname. The ddname need not exist previously. You can allocate before the exec runs, or you can allocate from within the exec with the ALLOCATE command. "ALLOC DA(input.data) F(mydd) SHR REUSE" "EXECIO * DISKR mydd (FINIS" You can specify a starting line number for reading a data set other than at the beginning. For example, to read all lines to the data stack starting at line 100, add the following line number operand. "EXECIO * DISKR myindd 100 (FINIS"
To read just 5 lines to the data stack starting at line 100, write the following: "EXECIO 5 DISKR myindd 100 (FINIS"
To open a data set at line 100 without reading lines 1 through 99 to the data stack, write the following: "EXECIO 0 DISKR myindd 100 (OPEN"
IBMMAINFRAMES.com
To read the information to either a list of compound variables that can be indexed, or a list of variables appended with numbers use the STEM option. Specifying STEM with a variable name ensures that a list of variables (not the data stack) receives the information. "EXECIO * DISKR myindd (STEM newvar."
In this example, the list of compound variables has the stem newvar. and lines of information or records from the data set are placed in variables newvar.1, newvar.2, newvar.3, and so forth. The number of items in the list of compound variables is placed in the special variable newvar.0. This makes it extremely easy to create loops that process input data. Example: "ALLOC DA(old.data) F(indd) SHR REUSE" "EXECIO * DISKR indd (STEM newvar." DO i = 1 to newvar.0 SAY newvar.i END To avoid confusion as to whether a residual stem variable value is meaningful, you should clear the entire stem variable prior to entering the EXECIO command. To clear all stem variables, you can either: Use the DROP instruction which sets all stem variables to their uninitialized state: DROP newvar. Set all stem variables to nulls: newvar. = ''
To empty a data set, issue this command to open the data set and position the file position pointer before the first record. You then issue EXECIO 0 DISKW myoutdd ... (FINIS to write an end-of-file mark and close the data set. This deletes all records in data set MYOUTDD. You can also empty a data set by issuing EXECIO with both the OPEN and FINIS operands like this: "EXECIO 0 DISKW myoutdd ... (OPEN FINIS"
IBMMAINFRAMES.com
To write a specific number of lines, put the number immediately following the EXECIO command. "EXECIO 25 DISKW ..."
To write the entire data stack or until a null line is found, put an asterisk immediately following the EXECIO command. "EXECIO * DISKW ..."
When you specify *, the EXECIO command will continue to pull items off the data stack until it finds a null line. If the stack becomes empty before a null line is found, EXECIO will prompt the terminal for input until the user enters a null line. Therefore, if you do not want to have terminal I/O, queue a null line at the bottom of the stack to indicate the end of the information. QUEUE '' If the data contains null lines and the data stack is not shared, you can use the built-in QUEUED() function to determine the number of lines to write in either of the two fashions below: n = QUEUED() "EXECIO" n "DISKW outdd (FINIS" outdd (FINIS"
or
Just as with reading a data set, before you can write to a data set it must first be allocated to a ddname. You can allocate from within the exec with the ALLOCATE command as shown in the following example, or you can allocate before the exec runs. "ALLOC DA(output.data) F(myoutdd) OLD REUSE" "EXECIO * DISKW myoutdd ..." To write the information from compound variables or a list of variables beginning with the name specified after the STEM keyword. The variables, instead of the data stack, holds the information to be written. "EXECIO * DISKW myoutdd (STEM newvar."
In this example, the variables would have the stem newvar. and lines of information from the compound variables would go to the data set. Each variable is labeled newvar.1, newvar.2, newvar.3, and so forth. The variable newvar.0 is not used when writing from compound variables. When * is specified with a stem, the EXECIO command stops writing information to the data set when it finds a null value or an uninitialized compound variable. The EXECIO command can also specify the number of lines to write from a list of compound variables. "EXECIO 5 DISKW myoutdd (STEM newvar."
In this example, the EXECIO command writes 5 items from the newvar variables including uninitialized compound variables, if any.
IBMMAINFRAMES.com
EXECIO Examples
/* REXX - copy a sequential data set */ "ALLOC DA('userid.input') F(datain) SHR REUSE" "ALLOC DA('userid.output') F(dataout) LIKE('userid.input') NEW" "EXECIO * DISKR datain (FINIS" QUEUE '' /* Add a null line to indicate the end of the information */ "EXECIO * DISKW dataout (FINIS" "FREE F(datain dataout)"
/* REXX */ /*************************************************************/ /* This exec will read an input file of data set names and */ /* create IDCAMS "DELETE NOSCRATCH" and "DEFINE NONVSAM" */ /* control cards to indirectly catalog sysres data sets. */ /*************************************************************/ arg catname if catname = '' then catname = 'ICFCAT.MASTER' /* <=== catalog name */ /******************************************/ /* allocate input and output files */ /******************************************/ "ALLOC F(INPUT) DA('MY.PDS.CNTL(INPUT)') SHR REUSE" "ALLOC F(OUTPUT) DA('MY.PDS.CNTL(OUTPUT)') SHR REUSE" /******************************************/ /* read input file into stem variables */ /******************************************/ "EXECIO * DISKR INPUT (STEM INREC. FINIS" /******************************************/ /* process input data */ /******************************************/ do i = 1 to inrec.0 inrec.i = word(inrec.i,1) /* remove leading and trailing blanks queue ' DEL ('||inrec.i||') -' queue ' NSCR CAT('||catname||')' queue ' /* */ ' queue ' DEF NVSAM(NAME('||inrec.i||') -' queue ' VOL(******) DEVT(0000)) -' queue ' CAT('||catname||')' queue ' /*********************************************/ ' end /* do i */ queue '' /* null line to signal end of data stack */ /******************************************/ /* write output file and exit */ /******************************************/ "EXECIO * DISKW OUTPUT (FINIS" "FREE F(INPUT OUTPUT)" exit 0
*/
IBMMAINFRAMES.com
/* REXX */ /******************************************/ /* Scan file for PARM string */ /* */ /* INPUT DDNAME - SCANIN */ /* OUTPUT DDNAME - SCANOUT */ /******************************************/ parse arg SEARCH /* preserve arg case */ if SEARCH = '' then do /* no srch parm */ say ' ******************************' say ' * NO SCAN PARM SUPPLIED *' say ' ******************************' exit 12 end /* if search */ /******************************************/ /* allocate input and output files */ /******************************************/ "ALLOC FI(SCANIN) DA('SYS3.SYSLOG.G0521V00') SHR REUSE" "ALLOC FI(SCANOUT) DA(*) REUSE" /******************************************/ /* Initialize counters */ /******************************************/ FOUND = 0 /* records found counter */ RECNUM = 1 /* current record number */ /******************************************/ /* Process input file */ /******************************************/ do forever "EXECIO 1 DISKR SCANIN "RECNUM /* read 1 record */ if RC <> 0 then leave /* no more records, exit loop */ RECNUM = RECNUM+1 /* bump up record count by 1 */ parse pull INREC /* pull record from data stack */ if pos(SEARCH,INREC) <> 0 then do push INREC /* write record to data stack */ FOUND = FOUND+1 /* bump up found counter by 1 */ "EXECIO 1 DISKW SCANOUT" /* write 1 record */ end /* if pos(search,inrec) */ end /* do forever */ /******************************************/ /* close output files */ /******************************************/ "EXECIO 0 DISKR SCANIN (FINIS" "EXECIO 0 DISKW SCANOUT (FINIS" /******************************************/ /* write totals and exit */ /******************************************/ RECNUM = right(RECNUM-1,7,'0') /* 7 digit number w/leading zeros */ FOUND = right(FOUND,7,'0') /* 7 digit number w/leading zeros */ say RECNUM' RECORDS WERE READ FROM THE INPUT FILE.' say FOUND' RECORDS WERE FOUND WITH "'||SEARCH||'".' if FOUND = 0 then exit 4 /* if no matches found, end with RC=4 */ else exit 0
IBMMAINFRAMES.com
REXX routines may be executed explicitly by entering EX on the member line in a PDS enhanced member list (ISPF option 3.4), at the TSO READY prompt, under the ISPF Command Shell (option 6), or by entering EXEC followed by the data set name and member to be executed: EXEC userid.REXX.EXEC(MYEXEC)' 'arg1 arg2' EXEC The "EXEC" operand may be omitted as long as the REXX exec begins with a comment line containing the word REXX , otherwise it will be interpreted as a CLIST. EXEC userid.REXX.EXEC(MYEXEC)' 'arg1 arg2' REXX routines may be executed implicitly if the data set containing the exec is allocated to a system file (SYSPROC or SYSEXEC). When both system files are available, SYSEXEC is searched before SYSPROC. As with explicit execution, REXX routines in SYSPROC must begin with a comment line containing the word REXX, otherwise it will be interpreted as a CLIST. REXX routines in SYSEXEC do not have this restriction, however it is always a good idea to have the comment line with the word REXX as the first line in an exec. You may also specify alternate libraries for implicit execution with the ALTLIB command. The ALTLIB command gives you more flexibility in specifying exec libraries for implicit execution. With ALTLIB, you can easily activate and deactivate exec libraries for implicit execution as the need arises. To implicitly execute your REXX exec you enter the command at the READY prompt, from the COMMAND option of ISPF, or on the command line of any ISPF screen as long as the member name is preceded by "TSO %command" (without the quotes). The percent sign (%) is optional, if there is not a TSO command with the same name. However, when you use this form, called the extended implicit form, TSO/E searches only the ALTLIB or SYSPROC libraries for the name, thus reducing the amount of search time. TSO %MYEXEC arg1 arg2 Since SYSPROC and SYSEXEC are usually allocated to your session via the LOGON PROC, and you may not have update access to those data sets, your best options for executing your execs is to use the explicit method outlined above or use ALTLIB to define an APPLICATION data set that contains your execs allowing you to use the implicit execution method. This will also keep your private execs from interfering with other execs that may have the same name.
IBMMAINFRAMES.com
The ALTLIB command lets you specify alternative libraries to contain implicitly executable execs. You can specify alternative libraries on the user, application, and system levels. The user level includes exec libraries previously allocated to the file SYSUEXEC or SYSUPROC. During implicit execution, these libraries are searched first. The application level includes exec libraries specified on the ALTLIB command by data set or file name. During implicit execution, these libraries are searched after user libraries. The system level includes exec libraries previously allocated to file SYSEXEC or SYSPROC. During implicit execution, these libraries are searched after user or application libraries.
Data sets concatenated to each of the levels can have differing characteristics (logical record length and record format), but the data sets within the same level must have the same characteristics. The ALTLIB command offers several functions, which you specify using the following operands: ACTIVATE Allows implicit execution of execs in a library or libraries on the specified level(s), in the order specified.
DEACTIVATE Excludes the specified level from the search order. DISPLAY RESET Displays the current order in which exec libraries are searched for implicit execution. Resets searching to the system level only (execs allocated to SYSEXEC or SYSPROC).
Here is an example of the ALTLIB command allocating a APPLICATION exec library: ALTLIB ACT APPLICATION(EXEC) DSNAME('userid.REXX.EXEC') If you are in split-screen mode in ISPF and you issue the ALTLIB command from a one-screen session, the changes affect only that screen session. The ALTLIB search order is not valid across split screens.
IBMMAINFRAMES.com
Running an exec in the background is the same as running a CLIST in the background. The program IKJEFT01 sets up a TSO/E environment from which you can invoke execs and CLISTs and issue TSO/E commands. For example, to run an exec named MYEXEC contained in a partitioned data set USERID.REXX.EXEC, submit the following JCL: //USERIDA JOB 'ACCOUNT,DEPT,BLDG','PROGRAMMER NAME', // CLASS=A,MSGCLASS=X,MSGLEVEL=(1,1) //* //TMP EXEC PGM=IKJEFT01,REGION=4M //SYSEXEC DD DISP=SHR,DSN=USERID.REXX.EXEC //SYSTSPRT DD SYSOUT=* //SYSTSIN DD * %MYEXEC arg1 arg2 /* // The EXEC statement defines the program as IKJEFT01. The REXX routine (and any called routines) must be in a PDS defined to the SYSEXEC or SYSPROC system file. You can assign one or more PDSs to SYSEXEC or SYSPROC. The SYSTSPRT DD allows you to print output to a SYSOUT class or a specified data set. In the input stream, after the SYSTSIN DD, you can issue TSO/E commands and invoke execs and CLISTs. Any additional files needed to run the exec can be defined with the TSO ALLOCATE command within the exec or in the SYSTSIN input stream (prior to executing the exec), or by adding additional JCL DD statements. Another way to run REXX execs in batch is to use the IRXJCL program. Running an exec in batch using IRXJCL is similar in many ways to running an exec in the TSO/E background using IKJEFT01, however, there are differences. One major difference is that the exec running in batch via IRXJCL cannot use TSO/E services, such as TSO/E commands (like ALLOCATE) and most of the TSO/E external functions. //USERIDA JOB 'ACCOUNT,DEPT,BLDG','PROGRAMMER NAME', // CLASS=A,MSGCLASS=X,MSGLEVEL=(1,1) //* //* RUN REXX EXEC IN BATCH //* //* PARM VALUE IS THE CLIST AND ARGUMENTS TO BE EXECUTED //* SYSEXEC DD POINTS TO THE EXEC PDS //* //S1 EXEC PGM=IRXJCL,PARM='MYEXEC arg1 arg2' //SYSTSIN DD DUMMY //SYSTSPRT DD SYSOUT=* //SYSEXEC DD DISP=SHR,DSN=USERID.REXX.EXEC Since execs running in batch via IRXJCL cannot use TSO/E services, any additional files needed to run the exec must be defined by adding JCL DD statements.
IBMMAINFRAMES.com
IBMMAINFRAMES.com
Edit macros can be either CLISTs or REXX execs written in the CLIST or REXX command language, or program macros written in a programming language (such as FORTRAN, PL/I, or COBOL). Our focus here will only be on REXX. Since REXX is such a powerful language, REXX edit macros have virtually endless possibilities. In order to use an edit macro, it must reside in partitioned data set that is part of the implicit search order (see the previous section). REXX edit macros are made up of REXX statements that fall into one of the following categories: Edit macro commands REXX command procedure statements and comments ISPF and PDF dialog service requests TSO commands
All statements are initially processed by the TSO command processor, which scans them and does symbolic variable substitution. It is important to recognize the different kinds of CLIST and REXX statements listed because: They are processed by different components of the system. They have different syntax rules and error handling. Their descriptions are in different manuals.
Since edit macros need to use ISPF services, they must run under the ISPEXEC or ISREDIT host command environment (ADDRESS ISPEXEC or ADDRESS ISREDIT). ISREDIT passes commands directly to the PDF editor and ISPEXEC is needed for using other ISPF services within an edit macro.
IBMMAINFRAMES.com
Any statement in an edit macro that begins with ISREDIT is assumed to be an edit macro command or assignment statement: ADDRESS ISPEXEC "ISREDIT FIND 'PGM='" "ISREDIT BOUNDS = 1,50" /* edit macro command */ /* edit macro assignment statement */
If you have several edit macro commands within a REXX EXEC, you can change the host command environment default to the PDF editor with the ADDRESS ISREDIT instruction. ADDRESS ISREDIT "FIND 'PGM='" "BOUNDS = 1,50" /* edit macro command */ /* edit macro assignment statement */
All edit macros must have an ISREDIT MACRO statement as the first edit command. /* REXX - this is an edit macro */ ADDRESS ISPEXEC "ISREDIT MACRO" "ISREDIT CHANGE JANUARY FEBRUARY ALL" The MACRO command is also used to pass parameters to an edit macro. Parameters are enclosed within parenthesis after the MACRO command. Multiple parameters are separated by a blank or comma. Example: /* REXX - MYMAC edit macro */ ADDRESS ISPEXEC "ISREDIT MACRO (PARM1,PARM2,REST)" If you executed this edit macro with parameters like this: Command ===> MYMAC FIRST SECOND THIRD FOURTH PARM1 would be set to FIRST, PARM2 to SECOND, and REST would be assigned the value THIRD FORTH. Here is a simple edit macro called "INCL". It can be used to exclude all lines in the editor except for a particular sting. The string to include is passed as a parameter to the macro. To use it you would type the following on the command line: Command ===> INCL DSN= /* REXX */ ADDRESS ISPEXEC "ISREDIT MACRO (parm1)" "ISREDIT EXCLUDE ALL" "ISREDIT FIND ALL" parm1 /* REXX */ ADDRESS ISREDIT "MACRO (parm1)" "EXCLUDE ALL" "FIND ALL" parm1
OR
IBMMAINFRAMES.com
You can use most primary commands in an edit macro if you precede it with ISREDIT. Some commands have additional operands permitted in a macro that cannot be used interactively. You cannot issue line commands directly from an edit macro. For example, you cannot use the M (move) line command within an edit macro. However, you can perform most of the functions provided by line commands by using a combination of primary commands, edit macro commands and edit assignment statements. Edit macros use edit assignment statements to communicate between macros and the editor. An assignment statement consists of two parts, values and keyphrases, which are separated by an equal sign. Data is always transferred from the right-hand side of the equal sign in an assignment statement to the left side. Therefore, if the keyphrase is on the right, data known to the editor is put into REXX variables on the left. In the following example, yyy would be a keyphrase, and xxx would be the value. ADDRESS ISPEXEC "ISREDIT xxx = yyy" Edit assignment statements can be used to: Set or query edit parameters, for example CAPS, NULLS, and NUMBER Pass values back and forth between the editor and the macro Manipulate data (insert, delete, or replace lines)
Here are some examples: ADDRESS ISPEXEC "ISREDIT CAPS = OFF" /* set CAPS off */ "ISREDIT (capmode) = CAPS" /* puts value of CAPS into var capmode */ "ISREDIT CAPS = "capmode /* set CAPS to whatever is in capmode var */ In the first statement, CAPS are set to a value of OFF. The second statements queries the current value of CAPS and places it into variable capmode. The third statement sets the value of CAPS to whatever is in the capmode variable. If these instructions were executed in this order, the last statement would be equivalent to the first one. ADDRESS ISPEXEC "ISREDIT (lb,rb) = BOUNDS" "ISREDIT BOUNDS = (lb,rb)" In the first statement, the current left and right boundaries are stored into the variables lb and rb. In the second statement, the values from the variables lb and rb are used to change the current boundaries. ADDRESS ISREDIT "(linedat) = LINE "curline "LINE" curline "= (linedat)" In the first statement, the data from line number "curline" (a variable used in the exec) is stored into variable linedat. In the second statement, the data from variable linedat is used to change the contents of the editor at line number "curline".
IBMMAINFRAMES.com
This edit macro, called "DELX" will delete everything but a specified string. The search can optionally be limited to specific columns. Example: DELX 'DISP=(NEW' /* REXX - DELX edit macro */ Address ISREDIT /* ISREDIT host command environment */ "MACRO (parm col1 col2)" /* MACRO parms */ "(lastln) = LINENUM .ZLAST" /* find out how many lines */ "SEEK "parm col1 col2" FIRST" /* any thing found? */ If rc = 0 then do /* we found something */ "EXCLUDE ALL" /* exclude all lines */ "FIND "parm col1 col2" ALL" /* UN-exclude lines */ "SEEK "parm col1 col2" ALL" /* get counts for later */ "DELETE ALL X" /* delete excluded lines */ "(count,lines) = SEEK_COUNTS" /* save counts for later */ count = Format(count) /* remove leading zeros */ lines = Format(lines) /* remove leading zeros */ deleted = lastln-lines /* how many lines were deleted */ Upper parm /* change parm to upper case for msg */ zedsmsg = deleted' LINES DELETED' /* short msg */ zedlmsg = count 'OCCURRENCES OF "'parm'" WERE KEPT', 'ON 'lines 'LINES - 'deleted 'LINES WERE DELETED.' "RESET" /* re-display excluded lines */ Address ISPEXEC "SETMSG MSG(ISRZ000)" /* msg - no alarm */ Exit 0 /* exit and set rc to 0 */ End Else do /* string not found */ Upper parm /* change parm to upper case for msg */ zedsmsg = 'STRING NOT FOUND' zedlmsg = 'THE STRING "'parm'" WAS NOT FOUND IN THE FILE.' "RESET" /* re-display excluded lines */ Address ISPEXEC "SETMSG MSG(ISRZ001)" /* msg with alarm */ Exit 12 /* exit and set rc to 12 */ End
IBMMAINFRAMES.com
IBMMAINFRAMES.com
CLIST
/* ANY COMMENT WRITE WRITENR &ZUSER WRITE IS AUTHORIZED PROC n &VAR SET X = &SUBSTR(3:8,&A) &STR() &STR(&X) &X&Y PRFX&MIDVAR.SUFFIX CONTROL CONLIST SYMLIST LIST CONTROL END(ENDO) ISPEXEC ispf service WHEN (&A = &B) &NOP (as statement) + and - as continuation characters SET A = &B &C SET A = &B.&C SET A = &B.C READDVAL OPENFILE, GETFILE, PUTFILE, CLOSEFILE (a TSO command) AND, && OR GE, GT, EQ, NE, LE, LT
REXX
/* ANY COMMENT */ SAY SAY ZUSER "IS AUTHORIZED" ARG or PARSE ARG VAR X = SUBSTR(A,3,6) or PARSE VAR X 3 A 9 "" or '' X X || Y "PRFX"MIDVAR"SUFFIX" TRACE RESULTS (no equivalent) ADDRESS ISPEXEC "ispf service" WHEN A = B THEN NOP , (comma) A = B C A = B || C A = B"C" PULL "EXECIO * ..." with PUSH, PULL "a TSO command" & | >=, >, =, =, <= < (=, /=, \=, <>, >< are all valid ways of coding "not equal")
The following keywords have identical or nearly identical meanings in both CLIST and REXX: IF, THEN, ELSE, DO, END, SELECT, WHEN, and OTHERWISE
IBMMAINFRAMES.com
REXX- GUIDELINES
Is REXX better than ?
Short answer: Yes. No. Maybe. Does it matter? Long answer: This question wastes a lot of bandwidth in comp.lang.rexx and other newsgroups. Every language has its good points and its bad points. Some people love REXX, some people hate it. Use a language that suits your needs.
Why does my OS/2 REXX program run more quickly the second time?
When you run a REXX CMD file for the first time, a tokenized version will be stored on disk using the OS/2 extended file attributes. (You can see how big the tokenized version is by using the /N option on the DIR command.) If a tokenized version exists AND the file has not been modified, CMD.EXE will use the tokenized version instead of parsing the source. Note that there is a 64K limit on the size of an extended attribute entry, so very large REXX programs do not benefit from this automatic tokenization.
IBMMAINFRAMES.com
The STREAM function can also be used to open files, query their sizes and seek into the file. Consult your REXX documentation for specific instructions for your interpreter.
IBMMAINFRAMES.com
Use the PROCEDURE instruction to keep variables local to a procedure, using EXPOSE to explicitly expose any "global" variables. The only catch is that you have to make sure you expose the variables inside every procedure. One way to define and use global variables is to use a stem called "Globals." and define all your procedures like this:
Foo: procedure expose Globals
Then at the top of you program initialize the Globals stem and assign appropriate values to your global variables
Globals. = '' Globals.!NeedToSave = 0 Globals.!TmpDir = "D:\TMP"
The tail names in this example are all prefixed with '!', though you could also use an underscore ('_'). This is just a convention used to avoid this kind of problem:
Globals.TmpDir = "D:\TMP" call Foo
IBMMAINFRAMES.com
say Globals.TmpDir exit Foo: procedure expose Globals. tmpdir = "foo" Globals.TmpDir = tmpdir return
It's a subtle bug that has to do with how REXX interprets stem tails.
IBMMAINFRAMES.com