You are on page 1of 178

1

REXX Programming
For TSO and CMS

Dan O’Dea
December 30, 2004

1
2

Table of Contents

Introduction . . . . . . 3 Error handling 40


Goals of the course. . 4 Interactive debugger . . 40
Uses of REXX; what is Trace options . . . . . . 41
a REXX program?. . . 5 EXECUTIL. . . . . . . . . 42
CLIST vs. REXX . . . . 6 Error traps . . . . . . . 43
How does TSO know the Examples. . . . . . . . . 46
difference?. . . . . 7
What is REXX?. . . . . 8 Arithmetic 49
Arithmetic operations . . 49
Getting Started 10 Precision . . . . . . . . 50
Allocating REXX files. 10
Connecting your execs Control Structures 51
to your session. . . 13 SELECT instruction. . . . 51
Looping . . . . . . . . . 53
Get on with it! 14
General syntax . . . . 14 Talking to the environment 57
REXX keywords. . . . . 17 What environment? . . . . 59
Variables and labels . 18 Edit macros . . . . . . . 61
Literals . . . . . . . 20
SAY instruction. . . . 22 Modular programming in REXX 62
REXXTRY exec . . . . . 22 Functions and subroutines 62
Some built-in functions . 63
Real REXX coding 23 User-written functions 67
Variables. . . . . . . 23 Internal. . . . . . . . 68
Concatenation. . . . . 25 External. . . . . . . . 71
Conditional operators. 27
DO groups. . . . . . . 28 Compound (stem) variables 74
Approximations . . . . 29
Boolean operators. . . 30 TSO Interactions 77
Evaluating expressions 30 Messages and OUTTRAP. . . 77
Passing data (ARG) . . 32 The stack . . . . . . . . 79
NEWSTACK and DELSTACK . . 81
Data handling 35 TSO and the stack . . . . 83
PARSE instruction. . . 35
User interactions. . . 38 File I/O 86
PARSE EXTERNAL . . . . 38 Reading . . . . . . . . . 87
Writing . . . . . . . . . 88
Updating. . . . . . . . . 89

References, Page 90
Appendix A, page 92: examples of REXX code
Appendix B, page 96: answers to questions
Appendix C, Page 106: solutions to exercises
Appendix D: complete list of REXX built-in functions (with
separate page numbers and TOC).

2
3

Introduction

There are two basic types of programming languages: compiled and interpreted. The
first kind, such as COBOL or Assembler, must pass through two steps. The first step
converts text (your source code) into object code, something the machine can understand.
The second step converts the object code into an executable module. Only then can you
see if your program runs.

TSO CLISTs and the original BASIC language are interpreted. An interpreted
language runs the source code directly, one line at a time.

You can do a lot with CLISTs, but they have some annoying quirks. There are also
some things you just can’t do in a CLIST. On the other hand, COBOL works with
machine and data files and doesn’t have “user friendly” interfaces. Assembler, while
extremely powerful, is somewhat obtuse.

What we programmers like to see is a language that’s user-friendly, so we can quickly


write workable programs. It should be easy to read, so nearly anyone can write a tool or
debug an existing one. It must be fully functional, so no additional programming is
needed. Finally, it must be portable so you can run it anywhere. There is such a
language; it’s called REXX.

To program in REXX in zOS, you need three things.

 You should be somewhat familiar with TSO commands.


 You should be somewhat familiar with the ISPF editor.
 You should have some experience with a symbolic language.

And that’s it.

3
4

Goals of the Course

When you complete the course, you will be able to do the following.

 Describe the advantages of REXX.

 Explain what REXX is good for.

 Tailor your TSO environment.

 Simplify the use of TSO commands.

 Write programs to help avoid keying errors.

 Test programs you are developing.

 Explain how REXX operates.

 Understand how the TSO stack works and how to use it.

 Write safe and proper REXX programs.

4
5

The Uses of REXX

 Simplify TSO life by coding automation programs.

 Eliminate many errors by cutting back on typing.

 Create new commands to do things with fewer keystrokes.

 Simplify data entry.

 Create complete applications. In some cases the application might be better done
in COBOL or assembler, but in most cases the REXX version is efficient enough
to leave as-is.

 Automate programmer tasks.

 Build a wall of safety around commands by allowing confirmation of choices.

 Learn a personal programming language.

 Streamline program development.

What is a REXX program?

 A REXX program is a command procedure.

 It resides in a PDS whose name ends in EXEC (or CLIST).

 It contains TSO commands and/or REXX instructions.

 You execute it interactively at your terminal.

 It can be executed in the background through JCL.

 It can invoke Dialog Manager services.

 It can be executed in the ISPF Editor as an edit macro.

5
6

Differences between REXX and CLISTs

CLISTs: REXX execs:

1. Bad for data strings. 1. Good string handling.

2. Lots of &&&&&. 2. English-like syntax.

3. Obscure at times. 3. Clear and concise.

4. Good control structures. 4. Excellent control structures.

5. Bad at mathematics. 5. Good at mathematics.

6. Unique to TSO. 6. Common. REXX can be found


on PCs and on VM machines as
well as on mainframes and the
Internet. There is also an object-
oriented REXX.

6
7

How Does TSO Know Which is Which?

A CLIST starts in no special way, because it’s just a string of commands. A REXX
exec must start with the word REXX somewhere on the first line. That tells the
command interpreter how to run the commands. Here’s an example of two ways to do
the same thing.

CLIST

PROC 1 DSN
ALLOCATE FILE(A) DA(‘&DSN’)

REXX

/* REXX program */
Arg DSN
“ALLOCATE FILE(A) DA(‘”DSN”’)”

Trivia

CLIST is an acronym for Command LIST.

REXX is an acronym for Restructured EXtended eXecutor.

7
8

What is REXX like?

REXX combines features of many languages.

 User variables.

 Literals, i.e. in-line text.

 Simple punctuation.

 REXX can be coded in upper and lower case, your choice. See the second
course on programming style for more on this option.

 There is no data typing. All variables can be numeric or character depending


on the data content.

 There is no need to declare or pre-define variables.

 REXX has full-featured conditionals (e.g. IF statements).

 It can accept input at time of execution.

 It can manipulate words within sentences (parsing).

 Full-featured string handling (similar to COBOL’s String and Unstring, but


easier).

 Complete interactive debugging facility.

 Complete set of mathematical functions.

 Uses decimals for accuracy.

 Any number of digits.

 Can approximate instead of insisting on exact equality.

8
9

What is REXX like?

 REXX talks to TSO, ISPF, ISPF Editor, and Dialog Manager.

 It prepares, formats, and gives commands to TSO to execute.

 REXX’s many powerful built-in functions allow for fewer instructions to create
certain data structures.

 REXX has no I/O command; instead it uses TSO’s EXECIO command.

 Very flexible loop structures.

 It is built to use TSO’s features.

 You can code compound variables (arrays) and matrixes (like COBOL
“OCCURS” clauses) in REXX.

Questions

What language is this program written in?

/* REXX Cobol Program Compiler */


COBOL PROG1 SOURCE EXEC

What language is this program written in?

/* Cobol Program Compiler */


COBOL PROG1 SOURCE EXEC

What two significant language features does REXX not have?

How does file I/O happen in a REXX exec?

9
10

Getting Started: Allocating a REXX Exec Library

You can allocate a single dataset to hold one REXX exec. That is fine for a one-time
run of the exec. No DD concatenation is needed to run the command, and compression is
never needed. That being said, running a single REXX exec is clumsy. I recommend
creating a PDS.

Some REXX PDS datasets use RECFM=FB, LRECL=80, allocated to the DD name
SYSEXEC. Some data centers choose to connect their REXX datasets to the DD name
SYSPROC. Concatenated datasets must have the same LRECL. If the PDS is to be
connected to SYSPROC, it must be defined as RECFM=VB, LRECL=255, the same as a
CLIST PDS, even if it is named **.EXEC. I recommend the latter use, as datasets
allocated to SYSPROC are accessed before datasets allocated to SYSEXEC.

Let’s create a PDS for your REXX execs and connect it to your session. Once you’ve
done that, running a REXX program is as simple as typing its name on the command line.
Please follow the SYSPROC convention, and define the PDS with RECFM=VB,
LRECL=255.

Installation note: if you’ve concatenated or allocated your REXX PDS to SYSEXEC,


and you can’t execute your execs, place this command in your logon script:

EXECUTIL SEARCHDD(YES)

The command activates the DD name SYSEXEC.

10
11

Creating and Connecting a REXX PDS: 1 of 3

ISPF 3.2

Menu RefList Utilities Help


-------------------------------------------------------------------------------
Data Set Utility
Option ===> A

A Allocate new data set C Catalog data set


R Rename entire data set U Uncatalog data set
D Delete entire data set S Short data set information
blank Data set information V VSAM Utilities

ISPF Library:
Project . .
Group . . .
Type . . . .

Other Partitioned, Sequential or VSAM Data Set:


Data Set Name . . . my.rexx.exec
Volume Serial . . . (If not cataloged, required for option "C")

Data Set Password . . (If password protected)

 Option A or M for Allocate.


 Key in the name to allocate (here, MY.REXX.EXEC).
 Hit ENTER.

11
12

Creating and Connecting a REXX PDS: 2 of 3

ISPF 3.2

Menu RefList Utilities Help


--------------------------------------------------------------------------
Allocate New Data Set
Command ===>

Data Set Name . . . : ISDODEA.MY.REXX.EXEC

Management class . . . (Blank for default management class)


Storage class . . . . (Blank for default storage class)
Volume serial . . . . (Blank for system default volume) **
Device type . . . . . (Generic unit or device address) **
Data class . . . . . . (Blank for default data class)
Space units . . . . . tracks (BLKS, TRKS, CYLS, KB, MB, BYTES
or RECORDS)
Average record unit (M, K, or U)
Primary quantity . . 5 (In above units)
Secondary quantity 1 (In above units)
Directory blocks . . 10 (Zero for sequential data set) *
Record format . . . . FB
Record length . . . . 255
Block size . . . . .
Data set name type : PDS (LIBRARY, HFS, PDS, or blank) *
(YY/MM/DD, YYYY/MM/DD
Expiration date . . . YY.DDD, YYYY.DDD in Julian form
Enter "/" to select option DDDD for retention period in days
Allocate Multiple Volumes or blank)

( * Specifying LIBRARY may override zero directory block)

( ** Only one of these fields may be specified)

Fill in as shown:

 Five tracks are enough for this class. My own current REXX PDS is only two
cylinders.
 Ten directory blocks is forty members or so.
 RECFM = VB, LRECL = 255.

12
13

Creating and Connecting a REXX PDS: 3 of 3

Go into ISPF Edit on the file you just created, choose a member name, and edit the
member. Type the three lines of code below.
File Edit Edit_Settings Menu Utilities Compilers Test Help
-------------------------------------------------------------------------------
EDIT ISDODEA.MY.REXX.EXEC(REXXPGM) Columns 00001 00072
Command ===> Scroll ===> CSR
****** ***************************** Top of Data ******************************
000001 /* REXX program */
000002 SAY "Hello world!"
000003 EXIT
****** **************************** Bottom of Data ****************************

Now save the program. Congratulations, you’ve written your first REXX exec!

Connecting the PDS to Your Session

In your startup CLIST, you should add an allocation line for this file. If you do that,
you can run programs as their member names rather than typing the entire PDS and
member name.

If you’d rather concatenate your library as part of your first TSO command, you can
do so. In your first TSO command, or in READY mode, type the following two
commands, hitting ENTER after each one:

ALLOC F(A1) DA(your.REXX.PDS) SHR REUSE


CONCAT F(SYSPROC A1)

From this point, your REXX PDS is in the search list for commands and programs to
run under TSO. Use SYSPROC as the DD name if you planned for that.

13
14

Get on with it!

You may be itching to get programming for real. So, off we go.

General REXX syntax: Sample Program 1 of 2

Here is a sample REXX program:

1. /* Sample REXX program */


2. A = 1
3. B = 2
4. C = A + B
5. Say “The answer is” C
6. Exit

REXX does not have line numbers per se. The REXX interpreter keeps track of line
numbers, which may be displayed when an error message occurs. When editing a REXX
exec, your edit profile should be “NUMBER OFF”.

Remember, a REXX exec must have the word REXX somewhere on Line 1. It
doesn’t have to be first, although that is a typical convention.

Comments in REXX begin with a “/*”, and must end with a “*/”. REXX does not
“close” the comment until it finds a “*/”. For example, this is a long comment line:

/* REXX long comment: the “C = A + B” won’t be


executed because it’s considered part of the
comment:
C = A + B */

Line 2 puts a value of “1” into A. What does Line 4 do?

Line 5 displays

The answer is 3

on the terminal.

Line 6 ends the program.

14
15

General REXX syntax: Sample Program 2 of 2

If EXIT is on the last line, you can leave it out.

EXIT can give a caller a return code. For example,

EXIT 10

passes a 10 back to the calling program.

You can test this with the WHEN TSO command, or as a variable.

 In a CLIST, the variable &LASTCC contains the return code.

 In a REXX exec, the variable RC contains the return code.

Questions

What does a REXX exec always start with?

What word or instruction tells REXX to display something on the terminal?

What does this sample program do?

/* REXX */
X = 10
Y = 20
Z = Y - X
Say “The answer is” Z

15
16

More on REXX Syntax

Comments are started by “/*” and ended with “*/”. They can go anywhere in the
program. For example, this is a wing comment:

Say “Hello” /* instruction displays “Hello” */

This is a box comment. Note each line is not closed. Only the first and last lines have
slashes; the first slash starts the comment, and the last finishes it.

/* multiple-line comments are more efficient


than many single-line comments. So, when
making comments like this, use one pair of
comment delimiters. */

REXX execs have one statement per line. You can continue a statement on a second
line by ending the first part with a comma:

Say,
“Next?”

You can also have multiple statements on a line by separating each statement with a
semicolon:

Say “Hello”; Say “Aloha”; Say “Ciao”

You can end a line with a semicolon, but it’s not necessary.

You can mix upper and lower case. When the REXX interpreter gets a line it forces
the commands, variable names, etc. into upper case. For example, the following are all
the same variable:

DataSetName
datasetNAME
DATASETNAME

However, text enclosed in quotes remains intact. For example:

Say “AARGH” produces “AARGH”


Say “Aargh” produces “Aargh”

16
17

SAY “AaRgh” produces “AaRgh”

17
18

REXX Keywords

Keywords: SAY, DO, IF, EXIT

Literals: “Moe”, ‘Larry’, Curley, 1234, ‘9876’

Variables: A, XYZ, DSN, MyFileName

Assignments: A = B

Command: ALLOCATE, DELETE, LOGOFF

Keywords are REXX verbs. They tell REXX to do something.

A literal is itself. It doesn’t stand for anything. Unassigned variables are considered
literals. For example this program produces two lines of output, shown below.

/* REXX */
Say Curley
Curley = 2
Say Curley

READY
<rexx name>
Curley
2
READY

18
19

REXX Variables

Variables contain information. They stand for another thing. For example, in this
statement

A + B

A and B are variables containing numbers (you can’t add “A” and “B”).

Anything starting with a word REXX does not understand is taken to be a TSO
command and given to TSO.

Assignments are understood by REXX and are not sent to TSO.

SAY is understood by REXX, so REXX carries out the instruction.

DELETE is not understood by REXX and is sent to TSO for processing.

To send something directly to TSO, put quotes around it and put it first on the line (or
after an IF). For example, these REXX statements do the same thing:

“ALLOC F(A) DA(MY.JCL) SHR REUSE”


‘ALLOC F(A) DA(MY.JCL) SHR REUSE’

The type of quotes does not matter, as long as the start and end quote characters
match. As a general rule, I use double quotes because fully-qualified dataset names use
single quotes. At times the type of quotes does matter. We’ll cover those instances as we
continue.

REXX Labels

Labels name a subroutine, user-written function, target of a transfer-of-control


instruction, or error trap. The label must be the first thing on a line, followed by a colon.
The label can be on a line by itself.

COMPUTE: C = A + B

COMPUTE:
C = A + B

19
20

Questions

Do these use the correct syntax? If not, what’s wrong with them?

1 - Say “Eat”; Say “At”; Say “Joe’s.”

2 – Say “Hey”;
Say;
“Man”

3 - Compute; C = Y - A

True or false: you can “go to” a label.

True or false: the EXIT statement must always be the very last line in the program.

Will REXX try to execute this statement?

BARK “Hello”

Exercise #1

Write an exec to execute the following two TSO commands:

TIME
SEND ‘Sample REXX ‘ U(*)

Include the comment,

This is a sample REXX program for class

Execute the REXX statement below, but use a continuation character and two lines.

Say TIME() DATE()

20
21

Taking Literals Literally

Although literals can be used without quotes, you should use quotes (unless it’s a
number) to avoid confusion.

Single or double quotes are equally good, as long as you use them consistently. For
example, see the results of the following statements.

Say “Oh no!” displays Oh no!


Say ‘Oh no!’ displays Oh no!
Say ‘Oh no!” displays Oh no!” plus everything that follows up to
the next single quote, or produces an error if more than 250 characters lie
between single quotes.

If you forget quotes, it usually works anyway. REXX checks to see if it’s a variable.
If something has been assigned to it, that’s what you get. If there is nothing assigned to
it, REXX assumes you meant a literal.

REXX does not try to evaluate literals.

If the literal is first on a line, REXX sends it to TSO. For example,

“LISTCAT”

The null string is represented by “” or ‘’.

The maximum size of a literal is 250 characters.

Numeric literals may have quotes, unlike COBOL. These are all valid:

A = “1”
B = ‘2’
C = A + B + “3” “C” contains 6.

If there’s an “E” in the number, it’s scientific notation. For example, 2E+4 is 2 times
10 to the fourth power, or 20,000.

Literals can be hex strings. Hex characters must be in quotes, with an “X” after the
last quote. For example,

Say ‘F1F2F3’X

produces 123 as its output.

21
22

A Pitfall

Anytime you put an “X” after a string, REXX takes it as a hex string. Look at this
example.

X = 5 + 6
Say “The answer is “X /* Not this, error */
Say “The answer is“ X /* This is correct */

Literal Questions

What does this program do?

/* REXX TRY ME */
Say Greeting

What does this program do?

/* REXX TRY ME TOO */


Greeting = “Go Away!”
Say Greeting

What does this program do (I don’t expect you to know)? Try it at a terminal.

/* REXX TRY ME THREE */


Greeting = “Go Away!”
Say Greeting
Drop Greeting
Say Greeting

Exercise #2

Write an exec to display these three lines:

3 + 1 is 4
‘3 + 1 is 4’
O’Brien

22
23

Answers and questions about SAY

The SAY instruction sends data to the terminal. SAY can do math and display the
answer.

What does this display on the terminal?

Say 5 - 3

What does this display on the terminal?

SAY “5 – 3”

What does this display on the terminal?

SAY “5” – 3

23
24

The Variable

Variables:

 Contain information.

 Stand for that information, which may vary.

 Is not the information itself.

 Has a name you choose.

 Name must start with a letter.

 Name can be up to 250 characters long, with no blanks in the name.

 Mixed upper- and lower-case letters are OK.

 No hyphens, parentheses, quotes, equal signs, or apostrophes are allowed. However,


underscores are OK for readability.

 A period in the name indicates a compound variable (advanced topic to be discussed


later in the course).

Variables are given values through:

 Assignment (e. g. A = 5).

 PULL (input from terminal).

 ARG (data entered at run time).

 PARSE (string manipulation).

 EXECIO (file access).

 LISTDSI and other TSO commands and functions.

A variable never given a value is considered a literal.

Do not use REXX keywords as variables! It is legal, but a bad idea.

24
25

Questions about Variables

Which variable names are invalid, and why?

1. 1_time_only
2. ProGraMmer_NamE
3. Last_Name”
4. input-data
5. Say

What does this display on the terminal?

/* REXX exec */
MESSAGE = MESSAGE
SAY MESSAGE
EXIT

Exercise #3

Write an exec to:

 Assign the number 10 to a variable;


 Assign the number 20 to a variable;
 In one instruction, display the total of the two.

25
26

Concatenation

To display or connect variables you can just put them next to each other. If they are
separated by any number of spaces, they are connected by a single space. For example:

Say “Hi” “Fred”


- or -
Greeting = “Hi”
Person = “Fred”
Say Greeting Person

Either results in the display Hi Fred.

If you don’t specify spaces in between the values, you get no space between the
results. However, you do get an extra quote.

Say “Hi”“Fred” displays Hi”Fred

The double quote appears in the middle because REXX interprets a doubled quote as
meaning a single quote.

You cannot concatenate variables directly. For example this doesn’t work as you think
it might.

Salary = “10000”
Person = “Barney”
Say SalaryPerson

SalaryPerson is a separate variable from Salary and Person.

How do we separate variables but be able to concatenate them together?

26
27

More on Concatenation

The concatenation operator is two vertical bars: ||. Here are some examples of
concatenation, both with and without the bars:

1.Var1 = “Up”
2.Var2 = “Away”
3.Var3 = “per”
4.Var4 = “son”
5.Say Var1 Var1 “and” Var2
6.Say Var1 Var1“ and ”Var2
7.Say Var1 Var1 “ and ” Var2
8.Say Var2 Var3 Var4
9.Say Var2 Var3||Var4
10. Say Var2 Var1||Var3 Var4

The results of the Say statements are:

5.Up Up and Away


6.Up Up and Away
7.Up Up and Away (note the extra spaces)
8.Away per son
9.Away person
10. Away Upper son

The concatenation operator is needed only when concatenating two variables. There
is no need to use it when concatenating literals with variables.

Questions about Concatenating Variables

When this prints, how many spaces are between Elle and Woody?

Say “Elle” || “Woody”

When this prints, how many spaces are between Elle and Woody?

Say “Elle” “Woody”

27
28

Conditional Operators

As in all programming languages, REXX has an IF statement. The statement follows a


standard format.

If expression
Then one instruction
Else one instruction

The statement can also be put on one line.

The REXX verb NOP (No OPeration) tells REXX to do nothing. Here is an example
of a complete IF statement:

If Marital_Status = “MARRIED”
Then NOP
Else Say “Say, I was wondering....”

A complete list of comparison operators is found in Appendix D. However, here are a


few examples.

> Greater than


< Less than
= Numerically equivalent, e. g. 001 = 1.
Strings are equivalent when padded with
blanks (e. g. “ New York” = “New York ”).
== Equal in every way. For example:
001 == 001 True
001 == 1 False
“ New York” == “New York ” False
<> Not equal

You must use the symbols. For example, you cannot code

If A greater than B

28
29

Putting Several Instructions after the IF:


The DO – END group

If you want more than one instruction after the IF statement, use a DO … END group.
All of the instructions in the DO group are performed one after another. Every DO must
be matched by an END. It’s a good idea to indent so the program is readable. Here are a
couple of examples.

If A = B Then Do
A = B – 1
Say “A was equal to B, but now it’s not.”
End

If A = B
Then Do
Say “A is equal to B.”
Say “B is equal to A.”
End
Else Say “A is not equal to B.”

DO groups are quite flexible, may be nested, and can be run multiple times. We’ll talk
more about DO groups later in the course.

29
30

Inexact Comparisons

1000.0001 is pretty close to 1000. In normal mathematics, the two are not equal.
With the REXX instruction NUMERIC FUZZ, they can be equal. The syntax of the
FUZZ instruction is:

Numeric Fuzz 1 /* Ignore one digit */

9 significant digits is the default. REXX normally looks at all significant digits.
FUZZ tells REXX to ignore the least significant digit or digits. Here are some examples:

Numeric Digits 5 /* set to five sig. digits */


Numeric Fuzz 1
If 12361 = 12362 Then Say ‘Equal’
/* FUZZ says ignore least significant digit */

If 02361 = 02362 Then Say ‘Not Equal’


/* there aren’t 5 significant digits */

Numeric Digits 4 /* set to four sig. digits */


Numeric Fuzz 1
If 02361 = 02362 Then Say ‘Equal’
/* FUZZ says ignore least significant digit */

Exercise #4

Write an exec to:

 Store the number 15 as a constant;


 Store the number 19 as a constant;
 Write the instructions to compare the numbers and find them equal.

30
31

Boolean Operators

Boolean operators allow more than one compare on one statement. The standard
operators are:

& is AND, both must be true.

| is OR, either or both are true

&& is Exclusive OR, one or the other is true, but not both.

If Me = “Hungry” & Food = “On Table”


Then Say “I’m on a see-food diet.”

If Today = “Saturday” | Today = “Sunday”


Then Say “Honey, where are my golf clubs?”

If What = “Cake” && How = “Eat it”


Then Say “Can’t have your cake and eat it too”

Evaluation of Expressions

A comparison is an expression too. If the comparison is true the value is a 1. If the


comparison is false it becomes a 0. For example,

Say “1” = “2”

displays “0” because the comparison is false.

Be careful!

A = B = 2

checks to see if B = 2. If it is, it produces a “1”, so A is set to 1. If it’s not, A is set to 0.


The value in B is not affected.

31
32

A Specific Example

Here’s something you wouldn’t do, but it shows how REXX works.

“ABC” = “CDE”

REXX processes it as:

 “ABC” is not a REXX keyword.


 It’s not an assignment.
 Not a TSO command because of the “=”.
 Taken as a comparison. Is “ABC” = “CDE”?
 It isn’t, so the value becomes “0”.
 The line thus becomes 0. REXX doesn’t understand “0”, so it gives it to TSO.
 0 is not a valid TSO command. Running this produces a TSO error message
IKJ56621I INVALID COMMAND NAME SYNTAX

32
33

Passing Information to the Exec

What happens when you execute a program like this?

/* REXX MYP exec */


ARG LASTN FIRSTN SCHOOL
SAY FIRSTN LASTN “at” SCHOOL

MYP Conda Anna Yale

When you execute a program, you can pass it information on the command line after
the name of the program. Any information on the command line is passed to the
program. REXX picks this information up with the ARG command. Thus, the above
program produces:

Anna Conda at Yale

Spaces separate arguments. Do not use commas between arguments. When calling
subroutines or functions you would use commas.

If the information needs quotes (e. g. a dataset name), code them.

Exercise #5

Write an exec to execute the LISTCAT ENTRY(dsn) command. Pass the DSN using
ARG. Make sure it works on your own datasets and datasets not belonging to you.

33
34

More Variables than Items

Extra variables contain nulls. For example, the variable SCHOOL contains nothing.

MYP Conda Anna

/* REXX MYP exec */


ARG LASTN FIRSTN SCHOOL
SAY FIRSTN LASTN “at” SCHOOL

Anna Conda at

More Items than Variables

Extra items are all sent to the last variable. For example, the variable SCHOOL would
contain both the “La” and the “Salle”.

MYP Conda Anna La Salle

/* REXX MYP exec */


ARG LASTN FIRSTN SCHOOL
SAY FIRSTN LASTN “at” SCHOOL

Anna Conda at La Salle

More Items than Variables, Redux

You can choose to drop extra items. By placing a period at the end of the ARG
statement, the extra items are ignored. Using the same arguments, then, the results of this
would be “Anna Conda La”. The “Salle” goes away, never to return.

MYP Conda Anna La Salle

/* REXX MYP exec */


ARG LASTN FIRSTN SCHOOL .
SAY FIRSTN LASTN “at” SCHOOL

Anna Conda at La

34
35

Exercise #6

Write an exec to accept three pieces of information and display them in reverse order
of entry. If more than three are entered, display an error message.

Exercise #7

Write an exec to examine information passed to it. Perform the following checks.

 If “Al” is entered, display “Must be a Liberal”.


 If “Rush” is entered, display “Gotta be a Conservative”.
 If both “Al” and “Rush” typed in, display “Look out!”.
 If neither given, display an error message.

35
36

Separating Data: Parsing

The general form of the REXX Parse instruction is:

PARSE (UPPER) origin template

UPPER converts to upper case and is optional.

Possible origins are ARG, VAR, PULL, SOURCE, VALUE, and EXTERNAL. The
pairings of origin and location are listed here.

 ARG: command line when program is executed (PARSE ARG).


 a variable (PARSE VAR).
 the TSO stack (PARSE PULL).
 information TSO maintains on the program’s execution (PARSE SOURCE).
 a literal (PARSE VALUE). The keyword WITH tells where the literal ends.
 the terminal (PARSE EXTERNAL).

The template is a mask or filter through which data must pass before being distributed
to the variables. The template may be:

 Just the variables.


 Columns (where columns are the delimiters).
 Literals (literals are the delimiters).

All forms of PARSE place the information into variables. They all work the same
way, the only difference being the origin of the data. All the variables in the template are
changed.

PARSE is sometimes used in short forms:

 ARG is the same as PARSE UPPER ARG;


 PULL is the same as PARSE UPPER PULL.

36
37

Literals in a Template

All the templates we’ve seen so far were just a list of variables. You can tell REXX
how to distribute information into variables. That gives you more control over how
information is passed to the exec. Consider this:

%RUNIT HOW ARE YOU TODAY (HOT ISN’T IT?

PARSE ARG ONE TWO THREE “(” REST

In this example, the variables are stored as:

 ONE = HOW;
 TWO = ARE;
 THREE = “YOU TODAY”
 REST = “HOT ISN’T IT?”

Anything before the literal is stored into the leading variable(s). Anything after the
literal goes into the trailing variable(s).

You can use a variable containing a delimiter. Consider this:

%RUNIT THERE ARE TWO AND 1 / 2 DOZEN EGGS.

SLSH = “/”
PARSE ARG ONE TWO THREE (SLSH) REST

To use a variable, enclose it in parentheses. In this example the variables become

 ONE = THERE;
 TWO = ARE;
 THREE = “TWO AND 1”
 REST = “2 DOZEN EGGS.”

37
38

Question

If you execute the exec with the command shown, what are the results?

%RUNME JOHN 1313-1/2 DEADEND ROAD

/* REXX Runme */
Arg YOUR_NAME ADDRESS “/” JUNK
Say “Your name is” YOUR_NAME
Say “You live at” ADDRESS

Exercise #8

Write an exec to accept two arguments: the day of the week and the weather. If the
day is Friday and the weather is rainy, display “Head for office!”. If the day is Friday and
the weather is not rainy, display “Go Rock Cats!”.

38
39

Interacting with a User

User interactions happen at the terminal. We already know how to send messages to
the terminal; now it’s time to learn how to get data from the terminal. The REXX verb to
do that is PULL

PULL is short for PARSE UPPER PULL. Information comes from the terminal or
from the TSO stack. For now we’ll just concentrate on the terminal and revisit the TSO
stack later.

Please, do a SAY first. Otherwise the keyboard unlocks and the poor user won’t know
what to do. For example:

Say ‘Enter your name, please:’


PULL NAME
Say “Thank you,” NAME

Be careful. If there is something in the TSO stack, PULL takes that and you won’t get
what was typed at the terminal. There is a way to get around that, shown below. Don’t
worry; we’ll get more into the stack later. For now just be aware of it.

/* Something may be in the stack now;


NEWSTACK saves the old stuff in the stack */
“NEWSTACK”
Say ‘Enter your name, please:’
PULL NAME
Say “Thank you,” NAME
/* Delete new stack (restores old stack) */
“DELSTACK”

Avoiding the Stack Problem: PARSE EXTERNAL

The PARSE EXTERNAL command gets the line directly from the terminal. That
means you don’t have to worry about what’s in the stack. The syntax is:

Parse External variable

As with other PARSE commands, you can use multiple variables. The command line
is parsed by spaces, with extra data in the last variable and extra variables set to null.

39
40

Question

You want the user to enter three, and only three, items of information separated by
spaces. What does your PULL look like?

Exercise #9

Write an exec to ask for three, and only three, words and display them in reversed
order.

Exercise #10

Write a front end exec for the DELETE command. Have it ask, “Are you sure?”. If
the dataset name is “junk.data”, don’t ask.

Exercise #11

Write an exec to compute the percent increase of one number over another. Ask for
the two amounts from the terminal. The formula is:

% increase = 100 * ((new – old) / old)

Imagine your old salary was $34,000 and your new salary is $80,000. You would
calculate your percent increase as:

100 * ((80000 – 34000) / 34000)

Exercise #12

Ask for the weather and the day of the week from the terminal. If it’s raining, ask how
many inches are expected.

If it’s Friday and sunny or cloudy, display “Head for the golf course!”

If it’s Saturday and less than .5 inches, display “Fishing pole, please!”

40
41

Error handling in REXX

Interactive Debugger

Before we get further into REXX code, you should know REXX has an interactive
debugger. It must be invoked for each execution, so you have to turn it on every time.
The TSO command EXECUTIL TS turns on interactive debugging, as does the REXX
statement TRACE ?.

During interactive debugging you can interrupt using the PA1 or ATTN keys, and then
use one of the immediate keys:

 TS stops the exec, putting it into interactive debug;


 HI stops the exec and returns control to TSO;
 HT cancels any displays to the terminal;
 RT resumes any displays to the terminal;
 TE stops all tracing. If the Exec is running with some kind of tracing (not interactive
debug), this turns that off.

If you just press ENTER after PA1 or ATTN, the exec continues.

You can trace the execution of your exec. There are many options to trace; these are
described on the next page.

You can code most trace options (including EXECUTIL statements) inside a REXX
exec. You’d probably remove them when the exec has been released to the general
public.

Main programs and their subroutines do not share tracing settings.

Other Tracing Notes

Both the “!” and “?” trace options toggle. In other words, the first time they appear,
that switch is turned on. The second time, it is turned off. For example:

TRACE !
‘DELETE MY.CNTL.PDS’ Does not delete the file
TRACE !
‘DELETE MY.CNTL.PDS’ Deletes the file

You can specify the abbreviation shown above, or the full word (e. g. TRACE
NORMAL).

41
42

Tracing Options

Least amount of things traced

TRACE ! Nothing traced, don’t execute any commands.

TRACE O Nothing traced.

TRACE N Normal, TSO/CMS commands failing or erroring out


are traced.

TRACE F Failure, non-existent or abending TSO/CMS commands

TRACE E Error, non-working TSO/CMS commands

TRACE !C Trace commands but don’t execute them

TRACE C Commands, TSO/CMS commands

TRACE L Labels only

TRACE A All (labels, commands, REXX verbs)

TRACE S Scan labels, commands, and REXX verbs

TRACE !R Results for labels, commands, REXX verbs, or any


time a variable is changed, but don’t execute any
commands

TRACE R Results for labels, commands, REXX verbs, or any


time a variable is changed

TRACE ?R Results for labels, commands, REXX verbs, or any


time a variable is changed with interactive debug

TRACE !I Intermediate, labels, commands, REXX verbs, or any


time a variable is changed, showing intermediate
results (e. g. C = (4+3) * 2), and don’t execute
commands.

TRACE I Intermediate, labels, commands, REXX verbs, or any


time a variable is changed, showing intermediate
results (e. g. C = (4+3) * 2)

Most amount of things traced

42
43

Using EXECUTIL

“EXECUTIL TS” turns interactive debugging on within the exec. There is no special
reason for doing this in an exec, “TRACE ?” works.

“EXECUTIL TE” turns interactive debugging off within the exec. There is no special
reason for doing this in an exec, “TRACE OFF” works.

“EXECUTIL HT” stops the display to the terminal.

“EXECUTIL RT” restarts the display to the terminal.

Interactive Debugging

During interactive debugging, your exec stops. You can do these things:

 Execute one instruction at a time, using the ENTER key;


 Display or change variables;
 Turn tracing on or off;
 Re-execute the most recent instruction by typing the “=” key. You might want to do
that if you changed a variable and want to see if it works now.
 Type and run any instruction you could put into your exec:
o Display variables: Say AH
o Change variables: AH = 9
o Send commands to the environment :
 “EXECUTIL TE”
 “STATUS CICTMDRI”
 TRACE ?
 List an actual line of the program as you wrote it
 SAY SOURCELINE(number)

Questions

Your program is running, apparently not doing anything, and won’t stop. What do you
type in to see what it’s doing?

You’re in interactive trace, single-stepping through the program, and want to quit.
What do you type in to get the program to complete without further tracing?

43
44

Can you regain control of a runaway program by typing “EXECUTIL TS”?

44
45

Error Trapping

REXX allows five types of error trapping routines. There are two types of error trap.
One quits the program, the other continues running from the point of the error. We’ll take
a look at both.

Some errors should halt the program. The program should inform the user of the error
before shutting down.

Error traps are generally set up at the beginning of the program. Conditions occurring
after that are intercepted wherever they happen. Control jumps to the error handling
routine. Usually the routine contains an EXIT. Here is the syntax:

Signal on condition
:
:
condition:
/* handle the condition */
Exit

The other type of error traps are subroutines. Control returns to the instruction
immediately after the one causing the error. You may not return anything. The RESULT
variable is not set. Here is the syntax.

Call on condition
:
:
condition:
/* handle the condition */
Return

You can name the routine if you want to, as in this example.

Call on condition,
NAME routine_name
:
:
routine_name:
/* handle the condition */
Return

45
46

What Can You Trap?

There are five things you can trap in REXX.

Syntax error: your REXX instruction cannot be interpreted. If you don’t have a
syntax trap, REXX displays the line in error and an error message, then stops.

Error: traps failures of known commands to the environment. That happens when
your TSO, ISPF, or macro command failed. If you have no “FAILURE” trap, ERROR
traps that as well. If you don’t have an error trap, REXX displays the failed command
and continues. Warning: some commands give return codes greater than zero when they
work correctly. Those trigger the ERROR trap.

Failure: the command does not exist (RC = –3). The command’s abend code is
converted to a decimal number in RC. A system abend gives a negative number; a user
abend gives a positive number. To convert to “normal” abend codes, use this command:

Say D2X(ABS(RC))

If you don’t have a FAILURE trap, REXX displays the failed command and continues
with the exec.

NOVALUE: remember, it is legal to use undefined variables; REXX considers them


literals. However, you may want to catch that. This is useful in debugging, but after the
program works there is no need to leave it in. If you don’t set up a NOVALUE trap,
undefined variables are seen as literals.

PA1/ATTN: capture the user hitting the ATTN or PA1 keys. If you don’t set a HALT
trap, hitting PA1 or ATTN, then typing HI, ends the exec. Don’t prevent the user from
stopping execution!

On the next page are some examples of each trap.

46
47

Trapping Examples

Signal on Syntax
Call on Error
Signal on Failure
Call on NoValue
Call on Halt
:
:
Syntax:
Say “Syntax error”
Exit

Error:
Say “Command failed”
Return

Failure:
Say “Oops!”
Say “Error code is” D2X(ABS(RC))
Exit

NoValue:
Say “Please define your variables”
Return

Halt:
Say “Want to stop now (Y/N)?”
Pull REPLY
If REPLY = “Y” Then Exit
Return

To turn off the error trap, use OFF instead of ON:

Signal off Syntax


Signal off Error
Signal off Failure
Signal off NoValue
Signal off Halt

47
48

What Can You Do in a Condition Trap?

Apart from notifying users there has been an error, you can do several things in a
REXX condition trap.

 The special REXX variable SIGL contains the line number of the statement sent to
the error trap.

 The function SOURCELINE(line number) gives the actual program statement.

 The function ERRORTEXT(RC) gives the message REXX puts out for a syntax error.

 The function CONDITION(“D”) displays the string in error with a NOVALUE trap
(i.e. the variable in error).

The Infamous “Go To”

As you may have guessed, SIGNAL can send to a label, even without an error
condition. This is the equivalent of the GOTO. If you do use SIGNAL as a GOTO, it
should go to an exit point.

Do not use SIGNAL to exit loop structures or subroutines as it destroys loop or


subroutine structure. There are better ways to exit subroutines.

48
49

Examples of Condition Traps

/* REXX sample showing condition traps */


Call on ERROR
Signal on SYNTAX
Signal on FAILURE

“DELETE NO.SUCH.DATASET”
“WHAT COMMAND IS THIS “
SALARY-INCREASE = 10
Say ERROR_MESSAGE

Exit

ERROR:
Say “Command to TSO did not work on line” SIGL
Say “Command was” SOURCELINE(SIGL)
Return

SYNTAX:
Say “Syntax error on line” SIGL
Say “Instruction was” SOURCELINE(SIGL)
Say “REXX error message:” ERRORTEXT(RC)
Say “Problem is in:” CONDITION(“D”)
Exit

FAILURE:
Say “Command to TSO did not work on line” SIGL
Say “Command was” SOURCELINE(SIGL)
Say “Abend Code was” RC
If RC = -3 Then Say “Command not found.”
Exit

49
50

Questions

Should you leave error traps in execs going into production?

How does the NOVALUE trap help enforce good programming practice?

ERRORTEXT(RC) is used in which error trap?

Exercise #13

Write an exec asking for a dataset name. It then issues the TSO command LISTD on
it. Set up an error trap to intercept the command not working.

In the error trap, display the line of the program in error, the string producing the error,
and the error code from TSO. Ask for the dataset name again, re-execute the command,
and exit.

50
51

Arithmetic Operations in REXX

Math is done on assignment statements. Pretty much everywhere REXX sees an


assignment statement, except in quotes, math is done on it. REXX uses mathematical
operators as in other languages, plus % and //.

Logical evaluations of statements are not math. For example, SAY 1 = 2 is the same
as SAY 0.

Operators

+ Add, as in 1 + 1 = 2.

- Subtract, as in 1 - 1 = 0.

* Multiply, as in 2 * 3 = 6.

/ Divide, as in 4 / 2 = 2.

% Integer divide, as in 5 % 2 = 2.

// Remainder, as in 5 // 2 = 1.

** Raise to a power (whole numbers only), as in 3 ** 2 = 9. Negative


exponents work, but fractional exponents are error conditions.

A “-” (minus sign) just before a number indicates a negative (e. g. –1).

A “+” (plus sign) just before a number indicates a positive number (e. g. +1).

Use parentheses to group numbers, e. g. (5 + 2) * 3 = 21.

51
52

Arithmetic Precision

REXX can do math to almost any precision, and very quickly. You can run out of
memory using too great a precision. The default precision is 9 digits. Try the following
on your own machine:

/* REXX precision testing */


Say 2 / 3
/* displays .666666667, 9 digits */
Numeric Digits 100
Say 2 / 3 /* displays: .
66666666666666666666666666666666666666666666666
66666666666666666666666666666666666666666666666
66666666666666666666666667 */

While I’ve never tried a huge number, NUMERIC DIGITS 10000 has worked for me
(it’s kind of fun to see all those sixes).

A while back we talked about FUZZ as a way to approximate values. For example,
NUMERIC FUZZ 1 ignores the least significant digit.

TRACE I shows all intermediate results of mathematical operations.

NUMERIC FORM SCIENTIFIC gives 1.23E+13

NUMERIC FORM ENGINEERING gives 12.3E+12

Exercise #14

Write an exec asking for two numbers, and then multiply them. Set an error trap to
intercept any error such as invalid numbers.

Exercise #15

Write an exec asking for an LRECL and a BLKSIZE, then determine if the BLKSIZE
is an even multiple of the LRECL. Tell the user if it is or is not.

52
53

More Advanced REXX Control Structures

So far, we’ve seen three REXX control structures: the IF statement, the SIGNAL
instruction, and the DO group. The DO group we’ve seen so far is executed once. You
can execute the same DO group over and over based on conditions, much like in an IF
statement. There is also a special form of the DO group called SELECT. We’ll talk
about SELECT first, and then the other DO group types.

The SELECT statement lets you specify a number of conditions, only one of which is
executed, based on a single condition check. SELECT replaces a string of IF … THEN
… GOTO statements.

The basic syntax of SELECT is:

Select
When expression Then instruction
Otherwise instruction
End

You can have as many WHEN expressions as you need to complete your selection.
Each expression is checked in order. When an expression is found to be true, that
instruction is executed and the program continues after the END. If none of the
expressions are true, the OTHERWISE instruction is executed.

The instruction may be another SELECT, a DO group, or any valid REXX instruction.
Here is a concrete example:

Select
When DAY = 1 Then Say “Sunday”
When DAY = 2 Then Say “Monday”
When DAY = 3 Then Say “Tuesday”
When DAY = 4 Then Say “Wednesday”
When DAY = 5 Then Say “Thursday”
When DAY = 6 Then Say “Friday”
When DAY = 7 Then Say “Saturday”
Otherwise Say “Unknown day of the week.”
End

53
54

Another SELECT Example

Day_Off = “No”
Select
When DAY = 1 Then Say “Sunday”
When DAY = 2 Then Say “Monday”
When DAY = 3 Then Say “Tuesday”
When DAY = 4 Then Say “Wednesday”
When DAY = 5 Then Say “Thursday”
When DAY = 6 Then Do
Say “Friday!”
Day_Off = “YES”
End
When DAY = 7 Then Do
Say “Saturday”
Day_Off = “YES”
End
Otherwise Say “Unknown day of the week.”
End

Exercise #16

Write a metric conversion exec. The exec should accept two arguments: UNIT and
QUANTITY.

UNIT can have one of these possible values: LITER, QUART, MILE, or
KILOMETER.

It returns the equivalent of Quantity in the other measurement system based on this
table.

1 liter = 1.057 quarts


1 quart = 0.946 liters
1 mile = 1.6 kilometers
1 kilometer = 0.625 miles

54
55

Looping (on purpose) within REXX

The basic syntax of the DO group is:

Do
instructions
End

This runs the group of instructions once. There are several modifiers you can use with
the DO group to make the group run more than once, or even not at all.

If you want to run the loop a specific number of times, code a number. For example,
this DO group runs 3 times:

Do 3
Say “Hello”
End

A variable can be used instead of a number if you need to change the number of times
the loop is run for each execution.

Do variable_name
Say “And so on...”
End

If you want to run the loop at least once, until some condition is true, use the UNTIL
clause. The UNTIL is checked after the loop has been executed.

Do Until TIME() = “17:00:00”


Say “WORK”
End

If you want to run the loop as long as some condition is true, use the WHILE clause.
The condition is checked before the loop begins, so the loop may not execute even once.

Do While TIME() < “17:00:01”


Say “WORK”
End

55
56

More DO Loops

You can step through a variable for a DO loop. This is useful for going through a
table:

Do variable = <variable or number> To <variable or number>


Say “Hello”
End

By default, the loop steps through the variable by 1. To step by another increment, use
a “BY” clause and the increment desired. Here are three examples.

Do I = 1 to 10
Say “Number of times through loop:” I
End

EndLoop = 10; Y = 1
Do Z = 1 to EndLoop By 2
Say “Loop counter is:” Z
Say “Number of times through loop:” Y
Y = Y + 1
End

Say ‘Countdown:’
Do J = 10 to 1 By -1
Say J
End

56
57

DO Loops: FOREVER, LEAVE and ITERATE

You can make a loop run forever. That may sound strange, but there are situations
where you want to do just that. Of course, there has to be a way to duck out neatly;
REXX has one. Here’s an example.

Do Forever
If TIME() > “12:00:00” Then Leave
Say “Is it lunch time yet?”
End

LEAVE gets you out of the loop.

In any loop you may encounter a condition such that, while you don’t want to leave
the loop, you also don’t want to execute the rest of the instructions in it. REXX has a
way to do that. The ITERATE instruction tells REXX to “never mind the rest of the loop,
go back to the top, increment, and continue from there.” Here’s an example for those not
liking the number 13.

Do I = 1 to 15
If I = 13 Then Iterate
Say “I is” I
End

The result of the above loop is this.

I is 1
I is 2
I is 3
I is 4
I is 5
I is 6
I is 7
I is 8
I is 9
I is 10
I is 11
I is 12
I is 14
I is 15

57
58

Questions

What is the ending value of I in this example? Run it to find out.

Do I = 1 To 4
Say I
End
Say “Ending value of I is” I

What is the result of running this after 12:00 noon? Run it to find out (but please
don’t do so until 11:59!).

Do Until TIME() > “12:00:00”


Say “Arf”
End

Exercise #17

Write an exec asking the user to enter a series of numbers, never the same one twice in
a row. Limit the list to 10 numbers.

58
59

Talking to the Environment;


Or, “REXX speaks!”

REXX understands its own keywords. Anything else is passed to the environment.
The environment is a command processor, whether it’s TSO, MVS, ISPF, or the ISPF
Editor. The environment processes commands sent to it by REXX, setting the return
code (RC) based on the results of the command. RC will be one of:

 Usually 0 if the command worked;


 Usually not 0 if the command failed;
 A –3 if the command was not found.

REXX environment interaction looks something like this:

Line of program
|
Does REXX understand it?
| |
| |
Yes No
| |
REXX does it |__Pass to TSO
ISPEXEC
ISREDIT

Who can REXX talk to?

If you run your exec from READY mode, you can pass commands to TSO.

If you run your exec from within ISPF, you can pass commands to TSO, ISPEXEC,
ISPF, and the ISPF Dialog Manager.

If you run your exec from within the ISPF Editor, you can pass commands to TSO,
ISPEXEC, ISPF, ISREDIT, and the ISPF Dialog Manager.

Putting quotes around the command tells REXX not to try to understand it.

A = B /* understood */
“A = B” /* not understood */
YELP “Bark” /* not understood */
YELP = “Bark” /* understood */

59
60

Questions

Will this line of instruction be passed to the environment?

Pull var1 var2 var3

Will this line of instruction be passed to the environment?

Articulate “Please enter name”

If this command works, what will be in RC?

“DELETE ABC.DATA”

Can you talk to ISPF from any EXEC?

60
61

How does REXX know an instruction is for the environment?

 If it is an assignment, like A = 3, REXX does the assignment.


 If it is a REXX keyword, like Say “Aah”, REXX does it.
 If it is a comparison operator, like “ABC” = “DEF”, REXX returns a 1 (if true) or
a 0 (if false).
 If the instruction is in quotes, like “DELETE ...”, REXX gives it to the
environment.
 If the instruction is unknown, like “Whisper ‘Hello’” , REXX gives it to the
environment.

REXX tries to figure out what kind of command the statement is. It then determines
who should execute the statement. All unknown commands are passed to the
environment. Using quotes tells REXX to send the statement directly to the environment.

How does REXX know what environment?

That is a good question. In READY mode there is only one environment: TSO.
When the exec is running in ISPF there are several environments to choose from. If you
don’t specify the environment, the command is sent to TSO. The question is, how do you
specify the environment for the command?

The ADDRESS keyword tells REXX what environment the command should be sent
to. ADDRESS has two, similar formats.

 ADDRESS environment “one environment command” sends a single


command to an environment. If the next statement is an environment command, it is
sent to TSO unless preceded by another ADDRESS.

 ADDRESS environment sets the environment such that each subsequent


command goes to the environment given on the ADDRESS statement until another
ADDRESS statement is encountered.

That might be a little confusing, so there are two examples on the next page.

61
62

How does REXX know what environment? continued

Temporarily change the environment, affecting a single command:

ADDRESS ISPEXEC “DISPLAY PANEL(MEDMENU)”


“DELETE ABC.DATA” /* Sent to TSO */

Change the environment until it’s changed back, affecting many commands:

ADDRESS ISPEXEC
“DISPLAY PANEL(MEDMENU)” /* Sent to ISPF */
“VPUT (DOG CAT) SHARED” /* Sent to ISPF */
ADDRESS TSO
“DELETE ABC.DATA” /* Sent to TSO */

You cannot address ISPF from READY mode because the ISPF environment isn’t
available. In REXX you can find out whether an environment is available using the
SUBCOM command. A return code of 0 means that environment is available.

“SUBCOM ISPEXEC”
If RC = 0
Then NOP /* In ISPF, so continue */
Else Do /* Try again, using ISPF */
“ISPSTART COMMAND(this command)”
Exit
End

Note the EXIT in the ELSE loop. This is needed because otherwise the exec will
continue to execute. Another way to tell if ISPF is active is this code.

If SysVar(SYSISPF) <> "ACTIVE" Then Do


“ISPSTART COMMAND(this command)”
Exit
End

62
63

Edit Macros in REXX

Coding edit macros is a separate topic in itself. This topic covers the differences in
writing REXX versus CLIST edit macros.

An edit macro executes as a subcommand of the ISPF editor. It must be a member of


a dataset concatenated to SYSPROC or SYSEXEC. It must start:

ADDRESS ISREDIT “MACRO”

If you have parameters to pass to the macro, they follow the MACRO keyword and
are enclosed in parentheses, separated by commas if necessary. For example, with two
arguments:

ADDRESS ISREDIT “MACRO (parm1,parm2)”

To pass commands to the editor, use the environment ISREDIT. Below is a very
simple edit macro to convert all lower case characters to upper case:

/* REXX macro converts lower to upper case */


Address ISREDIT
“MACRO”
“C P’<’ P’>’ ALL”
Exit

Question

Is the environment ISPEXEC always available?

Exercise #18

Write an ISPF edit macro to save and execute the exec you’re currently working on. It
must start with the usual comment, and then have ADDRESS ISREDIT MACRO (see
above). The commands to pass to the ISPF editor are:

(CURMBR) = MEMBER
SAVE

The command to be passed to TSO is:

63
64

EXEC your_exec_library(curmbr) EXEC

64
65

Modular Programming in REXX

Functions and Subroutines

REXX has a large library of built-in functions. They simplify programming, acting
arithmetically on data and manipulating strings. The result of the function is substituted
for the function call. For example,

Say REVERSE(“ABCD”)

displays “DCBA”.

A function may be called like a subroutine. When done in this way, the result of the
function is placed in a special variable called RESULT. For example,

Call REVERSE “ABCD”


Say RESULT

displays “DCBA”.

Commas are used to separate data items passed to functions.

Some functions have defaults, and need nothing in the parentheses:

Say TIME()

A complete list of functions can be found in the attachment to this class, and in Book
Manager and other IBM documentation.

A Pitfall

If you leave a space between the function name and its data items you don’t get the
function, you get concatenation.

Say Length (“ABCDEF”)

The above REXX statement produces this output.

Length ABCDEF

65
66

A Few Good Functions

We’ve seen a few functions already, such as TIME() and PROMPT(). All functions
must have the parentheses even when nothing is in them. Here are a few useful ones:

SYSVAR(system_variable): collect the value of a system variable.

Say SYSVAR(“SYSPREF”) dataset name prefix


Say SYSVAR(“SYSUID”) your user ID

LENGTH(string): get the length of a character string.

Say Length(“Kitty”) displays 5

LEFT(string,length): get the left-most length characters of a string. A related function


is RIGHT, which gets the rightmost characters.

Say Left(“Kitty”,3) displays Kit


Say Right(“Kitty”,3) displays tty

POS(string1,string2,start_pos): returns the location of string 1 in string2, or a 0 if


string1 is not found in string2. The search begins at start_pos. If start_pos is not
provided the search begins at the first character.

If POS(“’”,dsn) = 1 then Say “Fully Qualified”


Say POS(“rop”,”Quadrophenia”) displays 5.
Say POS(“rop”,”Quadrophenia”,6) displays 0.

SUBSTR(string,start-pos,length,pad): returns the indicated portion of string starting at


start_pos for length characters. If the original string is shorter than the length requested,
pad the new string with the pad character.

Say SUBSTR(“REXX is Fun!”,9,3) displays Fun


Say SUBSTR(“REXX is Fun!”,9,5,”!”) displays Fun!!

66
67

A Few Good Functions continued

JUSTIFY(string,length,pad): creates a new string from string of length characters. If a


pad character is not supplied, the pad character is spaces.

Say JUSTIFY(“Go Get It”,15,”!”)


displays Go!!!!Get!!!!It

STRIP(string,option,character): removes leading and/or trailing character from string


based on option. If the character is not supplied, spaces are removed. Options:

 B – remove both leading and trailing characters (default).


 T – remove trailing characters.
 L – remove leading characters.

Say STRIP(“ Who Are You? ”) displays Who Are You?


Say STRIP(“’FULL.NAME.DSN’”,,”’”)
displays FULL.NAME.DSN
Say STRIP(“’FULL.NAME.DSN’”,L,”’”)
displays FULL.NAME.DSN’

WORD(string,n): displays the nth word from string.

Say WORD(“A Scotsman on a horse”,3) displays on

WORDPOS(phrase,string,start_word): searches for phrase in string beginning at


start_word. If start_word is not provided, the search begins at the first word.

Say WORDPOS(“Hi”,“Hi ho, Hi ho, it’s off to work we


go”,2) displays 3

USERID(): returns the user ID of the person running the exec.

TRACE([trace_value]): returns the current trace setting. If trace_value is supplied, the


current value is returned and the trace setting is changed to trace_value.

67
68

A Few Good Functions continued

DATATYPE(string[,type]):

If only string is specified, DATATYPE returns “NUM” if the string is numeric or


“CHAR” if not numeric.

If type is specified, DATATYPE returns a “1” if the string is that type or “0” if it does
not match the type.

Say DATATYPE(“A12”) returns CHAR


Say DATATYPE(“123”) returns NUM
Say DATATYPE(“123”,N) returns “1”

Valid types are:

 A – alphabetic
 B – bits
 L – lower case
 M – mixed case
 N – number
 S – symbol (valid symbol, e. g. $, @)
 U – upper case
 W – whole number
 X – hexadecimal number

DATE([option]):

If option is omitted the format of the date is DD MMM YYYY. Valid types are:

 C – number of days so far this century


 D – number of days so far this year
 E – European date format (dd/mm/yy)
 J – Julian date (yyddd)
 O – ordered date, sortable (yy/mm/dd)
 U – USA date (mm/dd/yy)
 W – weekday (e. g. Friday)

68
69

One More Useful Function

To list dataset information like LRECL or RECFM, use the REXX function LISTDSI.
LISTDSI sets system variables from data returned to it by the system. It also sets a return
code based on whether the file was there or if the system information is usable. For a
complete list of codes see Appendix D.

CODE = LISTDSI(“’ENDN1.A06TIC08.SPROCLIB’”)
CODE = LISTDSI(“MY.COBOL.PDS”)

A CODE of “0” means the dataset exists. A CODE of 4 means the directory
information of a PDS is invalid. A 16 is returned if no information is available (e. g. file
not found).

LISTDSI may also be used with a DD name by providing the parameter “FILE”.

“ALLOC F(INF) DA(ABC.DATA) SHR REUSE”


CODE = LISTDSI(INF “FILE”)

LISTDSI may also be used to get directory information.

CODE = LISTDSI(“ABC.CNTL” “DIRECTORY”)

Questions

True or false: there must be a space after the name of the function and before the
parenthesis when you use the function.

When you call a built-in function with CALL, where is the answer given?

Exercise #19

Write an exec to accept two numbers from the terminal. Verify they are valid
numbers. Subtract one from the other, drop the sign, and display the answer.

Exercise #20

Write an exec see if a dataset called WORKSHOP.TEMP exists. If so, delete it. If
not, allocate a new one like your EXEC library. The TSO command is:

69
70

ALLOC DSN(WORKSHOP.TEMP) LIKE(your.exec)

70
71

User-written Functions and Subroutines

A function and a subroutine are essentially the same. The difference is in how they
are invoked. Functions are used in place; subroutines are CALLed and put the result into
the RESULT variable.

It is strongly suggested you write any functions and/or subroutines to be usable as


both (it’s not very hard). To do that:

 Avoid sharing variables;


 Give back an answer on the RETURN statement.

The benefit is the ability to use the function internally or externally with minor
change. It also makes the program more modular. That in turn makes the program easier
to debug.

A function/subroutine can be either internal (within the same program) or external (in
a separate file). Internal and external functions are called the same way.

REXX looks for internal functions first. If you use quotes, REXX looks externally.
For example:

Say “MYSUBR”(123)

You can write traditional subroutines if you want. A traditional subroutine

 Cannot be used as a function


 Is CALLed
 Doesn’t use ARG or RETURN value
 If internal, shares variables
 If external, can exchange information using ISPF variables, or can use the stack (but
are not modular when doing so).

71
72

User-written Internal Functions / Subroutines

User-written functions and subroutines:

 Are contained within the program (usually at the end).


 Usually have an EXIT before them.
 Label defines the function name.
 Data picked up through arguments, just like the main program.
 Separate arguments with commas, so both kinds of call work.
 You must place the answer on a RETURN statement.
 Main program gets the answer in the variable RESULT.
 Variables are shared by default with main program (but it is suggested you don’t
share).
 Use PROCEDURE after the label to prevent sharing.
 If you want to share variables, use EXPOSE.
 Internal functions/subroutines inherit settings from the main program (i. e.
ADDRESS, NUMERIC DIGITS, FUZZ).
 If internal functions/subroutine changes those settings, they are restored at the
RETURN so the main program doesn’t get confused.

SIGL contains the line number of the CALL.

Note the use of commas in the example below and those on the next page.

Call ADDEMUP 1,2 /* invoke with CALL */


Say RESULT /* display answer */
Exit /* don’t fall into routine */

ADDEMUP: PROCEDURE /* don’t share vars */


Arg NUM1,NUM2
ANSWER = NUM1 + NUM2
Return ANSWER /* give answer back on RETURN */

72
73

User-written Internal Functions / Subroutines:


More Examples

Call ADDEMUP 1,2 /* invoke with CALL */


Say RESULT /* display answer */
Say NUM1 /* shared and usable */
Exit /* don’t fall into routine */

ADDEMUP: /* share vars */


Arg NUM1,NUM2
ANSWER = NUM1 + NUM2
Return ANSWER /* give answer back on RETURN */

Say ADDEMUP(1,2) /* invoke as function */


Exit /* don’t fall into routine */

ADDEMUP: PROCEDURE /* don’t share vars */


Arg NUM1,NUM2
ANSWER = NUM1 + NUM2
Return ANSWER /* give answer back on RETURN */

Call ADDEMUP 1,2 /* invoke with CALL */


Say RESULT /* display answer */
Say NUM2 /* shared and usable */
Exit /* don’t fall into routine */

ADDEMUP: /* share certain vars */


PROCEDURE EXPOSE NUM2
Arg NUM1,NUM2
ANSWER = NUM1 + NUM2
Return ANSWER /* give answer back on RETURN */

73
74

Questions

True or false: in REXX, user-written functions / subroutines may be used as either


functions or subroutines.

True or false: if the code is used as a function, you execute it this way:
function_name(data).

True or false: if the code is used as a subroutine, you execute it this way: Call
function_name data and the answer comes back in RESULT.

Exercise #21

Write an exec with an internal function / subroutine to concatenate, without spaces,


two data items passed to it. Invoke it as a function and as a subroutine. Check
automatically within the exec to verify the calls produce the same result.

74
75

User-written External Functions / Subroutines

External functions and subroutines have these characteristics.

 They are a separate file from main program.


 They can be in the same PDS, or a PDS concatenated to SYSPROC or SYSEXEC.
 Information is picked up through ARG.
 It ends with RETURN, not EXIT.
 There is no label.
 There is no PROCEDURE statement.
 All variables are hidden from main program and are not shareable.
 Settings of ADDRESS and NUMERIC are not inherited.
 If you need to exchange variables, use ISPF variable pools.
 Information is passed back on the RETURN statement. If invoked with CALL, the
answer is in RESULT; if invoked as a function, the answer replaces the function call.

Generally, it’s cleaner to call a user-written function as you would any standard
function. If you do code it as a call, you must quote the function name, and pass the
parameters outside of the parenthesis. For example:

/* REXX */
DSN1 = “SR1.CNTL”; DSN2 = “’@TSSQ04.SR1.EXEC’”

Call “$FixDSN” DSN1 /* call as subroutine */


Say “The fully-qualified DSN is” RESULT

/* call external procedure as function */


Say “Fully-qualified DSN is” $FixDSN(DSN2)
Exit

/* REXX Fix DSN external proc */


Arg DSN
USERID = Sysvar(SYSUID)
If Left(DSN,1) = "'"
Then DSN = Strip(DSN,,"'"
Else DSN = USERID"."DSN
Return DSN

75
76

Search Order for Functions / Subroutines

This is a simplified version of how REXX and TSO search for functions and
subroutines.

 If function / subroutine name is NOT in quotes,


search for:

1.internal (i.e. a label);


2.built-in (e.g. LENGTH);
3.external (separate program).

 If function / subroutine IS in quotes, search


for:

1.built-in (e.g. LENGTH);


2.external (separate program).

It is advisable to do the following:

 Hide variables in internal functions / subroutines;


 If ARG uses commas, use commas in call

A final note: typically SYSEXEC is searched before SYSPROC. This is not always
true, it is installation-dependent.

Calling another Program

As you can call a function / subroutine, you can call a separate CLIST or REXX from
within an exec. REXX programs pick up data from their ARGs; CLISTs pick up data
from their PROCs. Any program can directly return only one thing back to the caller: a
return code. The caller picks up this return in the variable RC. Variables are not shared
with the caller, and you cannot pick up CLIST GLOBAL arguments. CLISTs or REXX
execs are called in one of two ways:

“EXEC exec_name ‘arguments’ EXEC”

“%exec_name arguments”

76
77

Exercises

You must try Exercises #22 and #25. If you choose, you may do only one of either
Exercise #23 or #24.

Exercise #22

Redo the percent increase exec from Exercise #11 as an external subroutine. Name it
PERCENT. Have it accept two parameters, the old number and the new number. Have it
return the percent increase on the RETURN statement. Make sure both args are numeric.
If either one or both are not valid, display a message and return a “0”.

Exercise #23

Using your solution to #21, write your concatenate subroutine as an external function /
subroutine (leave the internal one where it is). Call the external function / subroutine in
such a way as to make sure you invoke the external one and not the internal one. Prove
you made the right call.

Exercise #24

Create a metric conversion external subroutine called METRIC (see Exercise #16).
Also write a main exec calling this function.

Exercise #25

Create an external subroutine called SQRT to compute the square root of a number
passed to it. It accepts one number only. Validate the data item for being a number. If it
is negative, or not numeric, display an appropriate message and return a “0”. Set a loop
limit of 50 (close is good enough for the cigar).

Start with a guess of ½ the number. The loop consists of this:

Make new guess = (guess + (number / guess)) / 2


Make guess = new guess

Return the new guess to the caller.

77
78

Compound (Stem) Variables

Compound variables are a convenient way of grouping information. If you’ve worked


with arrays in BASIC or Fortran, or indexed tables in COBOL, the compound variable is
just like them.

A compound variable is also called a stem variable, and consists of the following.

 A stem name that remains unchanged for all references.


 A period to separate the stem from the extension.
 An extension, equivalent to the index number for the array. Each different name or
symbol you put here indicates a different variable.

Here’s an example:

WEEKDAY.1 = “Sunday”
WEEKDAY.2 = “Monday”
:
WEEKDAY.7 = “Saturday”

These look a lot like WEEKDAY_1, WEEKDAY_2, and so on. The importance of the
period, and these being stem variables, is simply this: you can substitute a variable for
the number:

A = 1
WEEKDAY.A = “Sunday”
Say WEEKDAY.1 /* produces Sunday */
Say WEEKDAY.A /* produces Sunday */

You can easily vary the variable with a DO group, and step through a long list of data:

/* Assume the above assignment statements,


this produces the days of the week. */
Do A = 1 to 7
Say WEEKDAY.A
End

78
79

Stem Variables: A “Real-world” Example

/* REXX to load and display the space report.


Note some code is skipped to concentrate on
the basics of stem variables */

RPTDATE = Date("U")
/*-- Loop through the DSN list. If a report
does not exist, create a new one. */
Do I = FirstDSN To SPCRPT.0
VSAMDSN = Word(SPCRPT.I,1)
OUTDSN = "'DBIG."VSAMDSN".SPCHIST'"
::
"ALLOC DA("OUTDSN") F(INFILE) SHR REUSE"
"EXECIO * DISKR INFILE (STEM SPCOUT. FINIS"
If SPCOUT.0 > 57 Then SPCOUT.0 = 57

/* The first five lines are headers. Make a


blank line at Line 6 for current info. */
Do J = 1 To SPCOUT.0
If J <= 5
Then IX = J
Else IX = J + 1
SPCOUT1.IX = SPCOUT.J
End
SPCOUT1.6 = RPTDATE Substr(SPCRPT.I,32,102)
SPCOUT1.0 = SPCOUT.0 + 1
:: /* Write the records */

End

79
80

Stem Variables: Changing the Stem

Changing the stem changes every element in the array. Then, if you change a single
element, all other elements are equal to the stem… even elements that haven’t been
created yet!

WEEKDAY. = “UNKNOWN” /* nothing after “.” */


WEEKDAY.1 = “Sunday”
Say WEEKDAY.1 /* produces “Sunday” */
Say WEEKDAY.2 /* produces “UNKNOWN” */

If you set the default in this way, the *.0 variable also has the default value. To use
*.0 to tell you the number of valid variables, you must reset it on your own.

Exercise #26

Write an exec with a loop setting NUMBER.1 through NUMBER.10 to the numbers
101 through 110. Then loop through the array, displaying the contents of NUMBER.1
through NUMBER.11, to see what happens.

80
81

TSO Interactions

Command Messages and OUTTRAP

TSO displays messages from most of its commands. To suppress messages use this
statement.

DUMMY = MSG(“OFF”)

Of course, MSG(“ON”) resumes displaying these messages.

You can trap most (but not all) of these messages. They are not displayed, but are still
accessible if your REXX wants to use them. To trap displayed TSO messages, use the
OUTTRAP function.

DUMMY = OUTTRAP(“LINE.”,”*”)

You can use any variable instead of LINE. Because this variable is a stem variable,
you must include the period.

The “*” says to capture all lines. This is the default. For the “*” you can substitute a
number to capture that many lines.

DUMMY = OUTTRAP(“LINE.”,100)

captures 100 lines.

Using LINE. as our stem variable, the following is true.

1. LINE.0 is the number of lines captured


2. LINE.1 is the first line captured
3. LINE.2 is the second line captured

and so on. To suppress messages and not capture them either, this statement works.

DUMMY = OUTTRAP(“LINE.”,0)

To turn off line trapping, use this statement.

DUMMY = OUTTRAP(“OFF”)

81
82

Command Messages and OUTTRAP

Here is an example of using OUTTRAP to display trapped output lines:

DUMMY = OUTTRAP(“LINE.”)
“LISTC”
Say “This many lines were trapped:” LINE.0
Do I = 1 to LINE.0
Say “Line of output was:” LINE.I
End
DUMMY = OUTTRAP(“OFF”)

This example runs the CRJ command against all members of a PDS. The function
STRIP removes spaces from around the member names.

/* Rexx Run CRJ against entire PDS */


Arg CRJDSN .
CRJDSN = $FixDSN(CRJDSN) /* Fully qualify */
foo = OutTrap(MemList.) /* Trap output */
"LISTDS '"CRJDSN"' MEM" /* Get member list */
foo = OutTrap(Off) /* No more trapping */

/*-- Member list starts on Line 7 --*/


Do I = 7 to MemList.0
Member = Strip(MemList.I)
Say "Processing job" Member
Say
"%CRJ '"CRJDSN"("Member")'"
End

Exercise #27

Write an exec to capture the display from TSO commands. Display the lines of output
one at a time.

 Display “Please enter a command”.


 Accept the command into a variable.
 Turn on OUTTRAP.
 Run the command, then turn off OUTTRAP.
 Display the message, “Here comes the command’s output:”.
 Loop through the array, displaying each line of the command’s output.

82
83

Using the TSO Stack

The TSO stack is a buffer-like storage area managed by TSO. It holds data keyed at
the keyboard in the terminal stack, and data sent to or from programs in the program
stack. The entire stack is shared by subroutines and other called programs.

Each stack is temporary, owned by the running program, and deleted when the
program ends. Certain TSO commands use the stack. You can put information into the
stack for later use or for use by another program running concurrently.

Who Puts Things in the Stack?

 Keying data at a terminal when keyboard unlocked;


 EXECIO read (first in, first out (FIFO));
 QUEUE instruction (FIFO) ;
 PUSH instruction (last in, first out (LIFO)).

Who Takes Things From the Stack?

 PULL;
 EXECIO write;
 PARSE EXTERNAL;
 TSO commands with PROMPTs.

How Does the Stack Work?

The TSO stack is in two parts: the console stack (terminal input buffer) and the
program stack containing program data. The data moves from the end of the stack to the
beginning, in the direction shown below:

Terminal  (end) Console  Program (beginning)


Stack Stack

Data moves one line at a time. Each line in the stack is either one line entered at the
terminal or one REXX instruction being executed. A line may be null, have one word, or
several.

83
84

How the Stack Works, continued

Terminal input enters the stack at the end (at left in the diagram). FIFO commands
(e.g. QUEUE, EXECIO READ) put data into the stack at the end of the program stack:

Terminal  (end) Console  Program (beginning)


Stack Stack

/\FIFO /\LIFO
commands commands
here here

LIFO commands (e.g. PUSH, EXECIO WRITE) put commands at the start of the
program stack.

The QUEUED() function tells you how many lines are in the stack.

Data is taken from the very start of the stack by PULL, EXECIO WRITE, and several
others.

If the program stack is empty, data comes from the terminal input buffer.

PARSE EXTERNAL takes data from the beginning of the terminal input buffer.
Because that is before the program buffer starts, you can use the command to avoid
getting things from the program buffer.

If the terminal input buffer is empty, the keyboard unlocks and TSO waits for you to
type something.

If you might have something in the program stack to be saved, or as good


programming practice in a new EXEC, issue the NEWSTACK command. NEWSTACK
creates a new program stack and saves the old one. All subsequent commands go to this
new stack until the DELSTACK command is issued.

When your program ends, and all programs executing have ended, control goes back
to TSO (or to the editor if the command was a macro). Anything left over in the stack is
taken as a command by TSO.

84
85

Questions

What will this line of a program do: SAY QUEUED()

There is nothing in either stack. Your program says PULL. What happens?

Your exec does 10 PUSHes and 9 PULLs. What happens?

Your exec does 9 PUSHes and 10 PULLs. What happens?

Why use NEWSTACK?

NEWSTACK creates a new program stack. You should leave the stack in the
condition it was just before your program needed it. That is essential when your program
calls or is called by another program also using the stack. Issue NEWSTACK at the
beginning of any exec using the stack.

When you use NEWSTACK, be sure to use DELSTACK when you’re done.
Otherwise the new stack remains after the exec ends (although the stack will be emptied
when your exec ends). Remember, if anything is left over in the stack TSO tries to run it.

This does not affect the terminal input buffer (console stack), which should be empty
if you are using the program stack.

Here is an example of using NEWSTACK and DELSTACK:

“NEWSTACK”
Queue “ABC.DATA”
“DELETE”
“DELSTACK”

85
86

How Many Stacks Are There?

There is always at least one stack, plus any you’ve created with NEWSTACK. REXX
has a function to tell you how many stacks there are. To determine how many you
created, subtract 1 from the total number of stacks. To delete all your new stacks:

“QSTACK” /* queries the stack manager */


Stacks = RC /* Total number of stacks */
My_Stacks = Stacks – 1 /* These are mine */
Say “There are” Stacks “ stacks.”
Do My-Stacks
“DELSTACK” /* delete all my stacks */
End

Push “First Record”


Push “Second Record”

“NEWSTACK” /* saves stack records,


makes a new stack */
Say “Please enter your name”
Pull NAME
Say “Thanks,” NAME
“DELSTACK” /* deletes current stack,
restores old stack */

Pull STACK_ELEMENT
Say STACK_ELEMENT /* displays Second Record */

Pull STACK_ELEMENT
Say STACK_ELEMENT /* displays First Record */

86
87

Executing a TSO Command Using the Stack

Below is an example of using a TSO command (SUBMIT) by feeding it records


through the stack. This particular code is part of a larger exec used to rename datasets
from one user ID to another.

OMSG = MSG("ON") /* Turn on messaging for this... */


Queue "//MYIDJOB JOB ACCOUNT1,'VSAM MOVE',”
Queue "// MSGCLASS=H,PRTY=4,NOTIFY="UID
Queue "//STEP01 EXEC PGM=IDCAMS"
Queue "//SYSIN DD *"
Queue " DELETE" NewDSN "PURGE"
Queue " IF LASTCC LE 8 THEN SET MAXCC=0"
Queue " DEFINE CLUSTER(NAME("NewDSN") -"
Queue " MODEL("OldDSN")) -"
If KSDS
Then Do
Queue " DATA (NAME("NewDSN".DATA)) -"
Queue " INDEX (NAME("NewDSN".INDEX))"
End
Else Queue " DATA (NAME("NewDSN".DATA))"
Queue "//SYSPRINT DD SYSOUT=*"
Queue "//************************************************/”
Queue "//STEP02 EXEC PGM=IDCAMS,COND=(0,NE)"
Queue "//INFILE DD DSN="OldDSN",DISP=SHR,"
Queue "// AMP=('BUFND=180')"
Queue "//OUTFILE DD DSN="NewDSN",DISP=SHR,"
Queue "// AMP=('BUFND=180')"
Queue "//SYSIN DD *"
Queue " REPRO INFILE(INFILE) OUTFILE(OUTFILE)"
Queue "//SYSPRINT DD SYSOUT=*"
Queue "ZZ"
"SUBMIT * END(ZZ)"

Notes:

 The variables UID, NewDSN, OldDSN, and KSDS were defined and set before
this set of code.
 The SUBMIT command takes everything from the first QUEUE command
through the queued ZZ.
 The “If KSDS” statement is explained below with the IF command details.

87
88

Executing a TSO Command Using the Stack

Here is an example of both passing data and NOT passing data to a program.

/* REXX sample to pass data to a program */


Queue “Rocky”
Queue “Bullwinkle”
%otherpgm /* Displays Received Rocky and
Received Bullwinkle */
Queue “Rocky”
Queue “Bullwinkle” /* Requeue lines */

“NEWSTACK”
%otherpgm /* Accepts two lines of input from
user at terminal and displays them */
“DELSTACK”
%otherpgm /* Displays Received Rocky and
Received Bullwinkle */

- - - - - - - - - - - - - - - - - - - - -

/* REXX OTHERPGM */
Pull data_line
Say “Received” data_line
Pull data_line
Say “Received” data_line

88
89

TSO Prompting

TSO prompting uses the same stack as PUSH, PULL, and QUEUE. Prompting in
TSO defaults to ON. Prompting in an Exec defaults to OFF. You can turn prompting on
if your TSO profile allows it (PROFILE PROMPT).

To turn prompting on and save the previous setting:

“PROFILE PROMPT”
SAVE_PROMPT = PROMPT(‘ON’)

To restore the previous setting:

DUMMY = PROMPT(SAVE_PROMPT)

To find out the current setting:

Say PROMPT()

Question

When and why should you use NEWSTACK?

Exercise #28

Write a REXX to accept a dataset name from the terminal. Verify the DSN is less than
45 bytes long. Verify it exists. Place the DSN into the stack so the LISTD command can
get at it. Suggestion: use QUEUE.

Turn on prompting. Execute the LISTD command but omit the dataset name so TSO
prompts for it. Since the DSN is in the stack, LISTD should get it from there.

89
90

File I/O in REXX: EXECIO

EXECIO is a function of TSO, not REXX. Use EXECIO to read from or write to
QSAM files. You can read a single record, read the entire file at once into a stem
variable, or use the program stack.

RC holds the return code from EXECIO. RC can have the following values:

 0 - successful action
 1 – truncated data written
 2 – if end of file
 20 – a severe error was encountered

You should always close a file when finished. The FREE command does not close the
file. Use the FINIS option of the EXECIO command to close files.

General Form of the EXECIO command

Since EXECIO is a TSO function, the entire command needs to be in quotes, options
and all. You must allocate the file first, whether NEW or MOD (if writing) or SHR (if
reading). The format of the command is:

“EXECIO how_many operation ddname seq (options”

The various fields are:

 operation:
o DISKR: read
o DISKW: write
o DISKRU: read for update
 Seq: sequence number (record number (not used on
a write)). You can’t go backwards.
 (options:
o none: use the stack
o STEM: use an array
o FINIS: close the file

The file can be read from or written to a record at a time or all at once.

90
91

Reading from a File

Remember this from the stem example above?

/*-- These two lines read the previous space


*-- report into another stem variable. */
"ALLOC DA("OUTDSN") F(INFILE) SHR REUSE"
"EXECIO * DISKR INFILE (STEM SPCOUT. FINIS"

The “*” tells EXECIO to retrieve all records. The “(FINIS” tells EXECIO to close the
file when the last record has been read.

This next example shows three different concepts:

 The stack is used to hold the one record retrieved by the EXECIO call.

 The RANDOM function generates a random number between the two named limits,
inclusive.

 The “PARSE PULL” instruction gets the one record from storage.

/* REXX QUIP program */


“NEWSTACK”
"ALLOC F(QUIP) DA('DBIG.EXEC(QUIPS)') SHR REU"
QNUM = RANDOM(1,773)

/*-- This line gets the one record named by the


*-- QNUM result above. For example, if the
*-- number generated was 327, the 327th record is
*-- retrieved. */
"EXECIO 1 DISKR QUIP" QNUM "(FINIS"
PARSE PULL QUIP
SAY QUIP
"FREE F(QUIP)"
“DELSTACK”

91
92

Writing to a File

You can write to a file allocated as SHR, OLD, NEW, or MOD, but using SHR is not a
good idea. Use MOD if you want to keep the data already in the file.

When reading a file, we used an “*” to indicate all records. When writing to a file,
EXECIO has no idea when you’ve run out of records; only you do. Therefore you
always have to specify how many records to write. You can do this with a number, as we
did in previous examples, or with a variable. When you do so you cannot enclose the
variable within the quotes:

“EXECIO” variable “DISKW ddname (options”

This example makes a copy of a TSO dataset.

/* REXX Copy A File */


Arg INDSN OUTDSN
INDSN = $FixDSN(INDSN)
OUTDSN = $FixDSN(OUTDSN)
"ALLOC F(INFILE) DA('"INDSN"') SHR REUSE"
"EXECIO * DISKR INFILE (STEM INREC. FINIS"
"ALLOC F(OUTFILE) DA('"OUTDSN"') NEW”,
“LIKE(“INDSN”)”
"EXECIO * DISKW OUTFILE (STEM INREC. FINIS"
“FREE F(INFILE OUTFILE)”

Freeing Allocated Files

Don’t forget to FREE your files when you’re done with them. It’s standard REXX
TSO syntax.

“FREE F(dd1)”

92
93

Updating a File

Let’s update a record (say, record 10) in a file. Note this uses the TSO stack; we’ll
revisit this example when we talk about the stack in the advanced course.

“ALLOCATE F(INFILE) DA(TEST.DATA) OLD REUSE”


“NEWSTACK”
“EXECIO 1 DISKRU INFILE 10”
Pull RECORD /* Get record from stack */
RECORD = “new data” /* change record */
Push RECORD /* Put record into stack */
“EXECIO 1 DISKW INFILE” /* write record */
“DELSTACK” /* drop the stack */
“EXECIO 0 DISKW INFILE (FINIS”
“FREE F(INFILE)”

Exercise #29

Choose a file you currently have available. Read it into an array, and then display
every other line starting with Line 1.

Exercise #30

Choose a file you currently have available. Read it into an array one record at a time.
Count each line as you read. Keep track of the longest record length. At the end of the
program, display the record count and the length of the longest record.

Exercise #31

Modify the OUTTRAP discussion example above to store the captured lines in the
dataset TEMP.CAPTURE.DATA.

o Use SYSDSN to see if the dataset exists. The syntax is SYSDSN(dsn_var) or


SYSDSN(“DSN”).
o If the dataset does not exist, allocate it using this command: “ALLOC
DA(TEMP.CAPTURE.DATA) F(CAPTURE) NEW SPACE(1 1) TRACKS
LRECL(80) RECFM(F B)”.
o OUTTRAP loads an array. Use that array to write the lines of the command display.
o The WRITE command should close the file when it’s done writing.

93
94

References

Here is a list of references I’ve found helpful. Most are on the Web.

Mike Cowlishaw, the creator of REXX, wrote a book titled The REXX language:
ISBN 0-13-780735-X Prentice-Hall, 1985
ISBN 0-13-780651-5 2nd edition, 1990

IBM’s REXX page: http://www-306.ibm.com/software/awdtools/rexx/language/.

REXX list server: http://www2.marist.edu/htbin/wlvindex?tso-rexx.

REXX Language Association home page: http://www.rexxla.org.

REXX FAQ page: http://www.mindspring.com/%7Edave_martin/RexxFAQ.html. The


character between “dave” and “martin” is an underscore, not a blank.

Mark Zelden’s home page. Mr. Zelden has created a large number of useful REXX
commands. The ones posted on this site are available for download.
http://home.flash.net/%7Emzelden/mvsutil.html

Doug Nadel’s home page. Mr. Nadel has been an ISPF developer at IBM for more than
fifteen years. This site has several useful utilities, although not all of them are written in
REXX. http://www.sillysot.com/mvs/

Lionel B. Dyck’s home page. Mr. Dyck has been a systems’ programmer since 1972.
This page has a few really useful things. http://www.lbdsoftware.com/

Two pages from Neil Hancock. Mr. Hancock has been a programmer for many years,
formerly for Met Office, and recently for Macro4, in the UK. The first page is a set of
FAQs on REXX; the second is a REXX style guide.
http://www.uberfish.freeserve.co.uk/Computers/rexxfaq.html.
http://www.uberfish.freeserve.co.uk/Computers/RexxStyleGuide.html.

94
95

This page intentionally left blank.

95
96

Appendix A: More Examples

Accept a DSN from the user. Check if it’s fully qualified (has quotes) and remove the
quotes. Assume the DSN belongs to the user if no quotes are found. The end result is a
fully-qualified name with no quotes.

USERID = Sysvar(SYSUID)
If Left(dsn,1) = "'"
Then dsn = Strip(dsn,,"'") /* fully qualified */
Else dsn = USERID"."dsn /* NOT fully qualified */
Return dsn

Accept a DSN from the user. Create a second dataset from the first, except change the
third qualifier to today’s date. The variable assumed here is INDSN.

Parse Upper Var INDSN SDQ1 "." SDQ2 "." SDQ3 "." SDQ4
UQ3 = “J”DATE(“J”)
OUTDSN = SDQ1”.”SDQ2"."UQ3”.”SDQ4

Determine if a dataset is not available. The dataset is not quoted, so a kludge is


needed to pass the DSN (the POS function tells whether a quote is present). The
“NORECALL” option tells LISTDSI not to recall a migrated dataset.

IF POS("'",DSN1) = 0 THEN DSN1 = "'"DSN1"'"


EXIST1 = LISTDSI(DSN1 NORECALL)
IF EXIST1 > 0 THEN SAY "DATASET 1 NOT AVAILABLE"

Get the dataset name of the most recent generation of a GDG.

Arg GDGBASE
If Left(GDGBASE,1) = "'" Then Strip(GDGBASE,,"'")
foo = OutTrap(CMDDATA.)
"LISTC ENT('"GDGBASE"') ALL GDG"
foo = OutTrap(Off)
LINE = CMDDATA.0
CURGEN = CMDDATA.LINE
OFFSET = Pos("--",CURGEN) + 2
CURGEN = Strip(Substr(CURGEN,OFFSET,45))
If SYSDSN("'"CURGEN"'") <> OK Then CURGEN = "BAD"
Return CURGEN

96
97

Appendix A: More Examples

Show me my RACF access to a particular dataset (the RI exec).

x = outtrap(RI.,'*')
"LD DA('"RACFDS"') GENERIC ALL"
If Word(RI.1,1) = "ICH35003I" Then Do /* No profile */
ZEDSMSG = "NO PROFILE FOUND"
ZEDLMSG = "Dataset" RACFDS "is not protected by RACF."
Address ISPEXEC "SETMSG MSG(ISRZ001)"
Exit
End
Do I = 1 To RI.0
Parse Var RI.1 . . . PROFILE
Parse Var RI.5 . . UACC .
Parse Var RI.17 MYACC .
End
If RI.0 = 1 Then Do
MYACC = "NONE"
UACC = "????"
PROFILE = "????"
End
MYACC = "My access ===>" MYACC
UACC = "Universal access ===>" UACC
PROFILE = "RACF profile name ===>" PROFILE

zedlmsg = MYACC copies(" ",76 - Length(MYACC)),


UACC copies(" ",76 - Length(UACC)),
PROFILE copies(" ",76 - Length(PROFILE))
Address ISPEXEC "SETMSG MSG(ISRZ001)"

Display information about who I am (the WHOAMI exec).

If SysVar(SYSENV) = "FORE" Then "CLRSCRN"


SAY "You’re logged on to" MVSVar(SYSNAME) "as",
SysVar(SYSUID)"."
SAY
SAY "Your current prefix is" SysVar(SYSPREF)"."
SAY
SAY "Here is the RACF information for your user ID."
SAY
"LU"

97
98

Appendix A: More Examples

Get the space used by a VSAM file.

SPCTYPE = "TRACKS"; SPCDATA = 0; SPCINDEX = 0


A = OutTrap(cmddata.)
"LISTCAT ENTRIES('"DSN"') ALL”
A = OutTrap(off)
COUNT = 1
Do Until cmddata.COUNT = "EXTENTS:"
COUNT = COUNT + 1
End
Do Until Word(cmddata.COUNT,1) = "INDEX”
Parse Var cmddata.COUNT LOWCCHH LOWRBA SPACE JUNK
If Left(SPACE,3) = "TRA" Then Do
SPCWork = Strip(Right(SPACE,6),L,"-")
SPCDATA = SPCDATA + SPCWORK
End
COUNT = COUNT + 1
End
Do Until cmddata.COUNT = "EXTENTS:"
COUNT = COUNT + 1
End
Do Until COUNT = cmddata.0
Parse Var cmddata.COUNT LOWCCHH LOWRBA SPACE JUNK
If Left(SPACE,3) = "TRA" Then Do
SPCWork = Strip(Right(SPACE,6),L,"-")
SPCINDEX = SPCINDEX + SPCWORK
End
COUNT = COUNT + 1
End
Say "VSAM space information for" DSN":"
Say " Data space in tracks is" SPCDATA"."
Say " Index space in tracks is" SPCINDEX"."

98
99

Appendix A: More Examples

"CLRSCRN" /* Information about my TSO session */


SAY "You are logged on to" MVSVar(SYSNAME) "as",
SysVar(SYSUID) "using logon proc" SysVar(SYSPROC)"."
SAY "The SYSPLEX ID is" MVSVar(SYSPLEX)"."
SAY "Operating system info: " MVSVar(SYSOPSYS)"."
SAY "Current level of TSO is" SysVar(SYSTSOE)"."
SAY "Current level of DFP is" MVSVar(SYSDFP)"."
If SysVar(SYSHSM) = "" Then Say "HSM not available."
Else Say "Current level of HSM is" SysVar(SYSHSM)"."
SAY "DFsms is" MVSVar(SYSSMS)"."
If SysVar(SYSLRACF) = "" Then Say "RACF is not installed.”
Else Say "Current level of RACF is" SysVar(SYSLRACF)"."
SAY "JES: " SysVar(SYSJES)", node name" SysVar(SYSNODE)"."
CPUT = Strip(SysVar(SYSCPU)); SRVU = Strip(SysVar(SYSSRV))
SAY "Your session has used" CPUT "CPU seconds and",
SRVU "service units so far."
TL = Strip(SysVar(SYSLTERM)); TW = Strip(SysVar(SYSWTERM))
SAY "Your LTERM displays" TL "lines of" TW "bytes each."
SAY "Your terminal ID is" TERMID()"."
If SysVar(SYSISPF) = "ACTIVE" Then Do
Address ISPEXEC "VGET (ZENVIR)"
ISPFVER = Left(Word(ZENVIR,2),3)
Say "You’re running under ISPF version" ISPFVER"."
End
Else Say "You’re running in READY mode (i. e. TSO."
SAY "Prompting is" Prompt()", Messaging is" MSG()"."
SAY "Your current TSO Virtual Storage Usage is: "
Numeric Digits 10
ASCB = C2d(Storage(224,4))
LDA = C2d(Storage(D2x(ASCB + 48),4))
LDALIMIT = C2d(storage(D2x(LDA + 208),4))
LDALIMIT = Right(LDALIMIT/1024,9)
LDALOAL = C2d(storage(D2x(LDA + 232),4))
LDALOAL = Right(LDALOAL/1024,9)
LDAELIM = C2d(storage(D2x(LDA + 216),4))
LDAELIM = Right(LDAELIM/1024,9)
LDAELOAL = C2d(storage(D2x(LDA + 240),4))
LDAELOAL = Right(LDAELOAL/1024,9)
LDAREGRQ = C2d(storage(D2x(LDA + 204),4)) / 1024
AVAIL = Right(LDALIMIT-LDALOAL,9)
EAVAIL = Right(LDAELIM-LDAELOAL,9)
Say " Region requested:" LDAREGRQ"K"
Say "Below 16M:" LDALIMIT"K" LDALOAL"K" AVAIL"K"
Say "Above 16M:" LDAELIM"K" LDAELOAL"K" EAVAIL"K"

99
100

Appendix B: Answers to Questions

Questions from Page 9:

What language is this program written in? REXX


/* REXX Cobol Program Compiler */
COBOL PROG1 SOURCE EXEC

What language is this program written in? CLIST


/* Cobol Program Compiler */
COBOL PROG1 SOURCE EXEC

What two significant language features does REXX not have? Data Typing and
File I/O

How does file I/O happen in a REXX exec? The TSO function EXECIO

Questions from Page 15:

What does a REXX program always start with? A comment line with the word
REXX in it.

What instruction tells REXX to display something on the terminal? SAY

What does this sample program do?


/* REXX Example */
X = 10
Y = 20
Z = Y – X
Say "The Answer Is” Z

100
101

Displays “The Answer is 10” on the terminal.

101
102

Questions from Page 19:

Are these correct syntax? If not, what’s wrong with them?

1 - Say “Eat”; Say “At”; Say “Joe’s.”


This is correct.

2 – Say “Hey”;
Say;
“Man”
This is incorrect. The semicolon after the second “Say” must be a comma.
If this is run, the result is “HEY” on one line, a blank line, then either the
TSO command “MAN” runs (if it exists) or an error message is displayed.

3 - Compute; C = Y - A
This is incorrect. The semicolon must be a colon.

True or false: you can “go to” a label. True. SIGNAL is the REXX equivalent
of GOTO.

True or false: the EXIT statement must always be the very last line in the program.
False. The EXIT statement is optional if it is the last line.

Will REXX try to execute this statement?

BARK “Hello”

No, it is passed to TSO.

102
103

Questions from Page 21:

What does this program do?

/* REXX Try Me */
Say Greeting

Displays “GREETING” on the terminal.

What does this program do?

/* REXX Try Me Too */


GREETING = “Go Away!”
Say Greeting

Displays “Go Away!” on the terminal.

What does this program do (I don’t expect you to know)? Try it!

/* REXX Try Me Three */


GREETING = “Go Away!”
Say Greeting
Drop GREETING
Say GREETING

Displays “Go Away!” on the terminal, and then displays “GREETING” on the
terminal. The DROP command removes the named entry from the variable
table, so it’s as if GREETING had never been assigned before.

103
104

Questions from Page 24:

Which variable names are invalid, and why?

1. 1_time_only can’t start with a number


2. ProGramMer_NamE valid
3. progame” can’t use quotes
4. input-data no hyphens allowed
5. SAY valid, but don’t do it

What does this program do?

/* REXX */
MESSAGE = MESSAGE
Say MESSAGE
EXIT
Displays “MESSAGE” on the terminal.

Questions from Page 26:

When this prints, will there be a space between Elle and Woody? No.

Say “Elle” || “Woody”

When this prints, how many spaces will be between Elle and Woody? One.

Say “Elle” “Woody”

104
105

Question from Page 37:

If you execute the exec with the command shown, what are the results?

/* REXX Runme */
Arg YOUR_NAME ADDRESS “/” JUNK
Say “Your name is” YOUR_NAME
Say “You live at” ADDRESS

%RUNME JOHN 1313-1/2 DEADEND ROAD

Your name is John


You live at 1313-1

Question from Page 39:

You want the user to enter three, and only three, items of information separated by
spaces. What does your PULL look like?

Pull V1 V2 V3 .

105
106

Questions from Page 43:

Your program is running, apparently not doing anything, and won’t stop. What do you
type in to see what it’s doing? First, hit ATTN or PA1. Then, type TS to enter
interactive debug and hit ENTER.

You’re in interactive trace, single-stepping through the program, and want to quit.
What do you type in to get the program to complete without further tracing? Type one
of TRACE ?N, TRACE O, or SET EXECTRACE OFF.

Can you regain control of a runaway program by typing “EXECUTIL TS”? Only if
you hit ATTN or PA1 first.

Questions from Page 48:

Should you leave error traps in execs going into production? Yes.

Does the NOVALUE trap help enforce good programming practice? Yes, to catch
undefined variable usage versus literals.

ERRORTEXT(RC) is used in which error trap? Syntax errors.

106
107

Questions from Page 56:

What is the ending value of I in this example? Run it to find out. 5.

Do I = 1 To 4
Say I
End
Say “Ending value of I is” I

What is the result of running this after 12:00 noon? Run it to find out.

Do Until TIME() > “12:00:00”


Say “Arf”
End

Nothing happens. Since the time is greater than noon, the loop is not
entered.

Questions from Page 58:

Will this line of instruction be passed to the environment? No.


Pull var1 var2 var3

Will this line of instruction be passed to the environment? Yes.


Articulate “Please enter name”

If this command works, what will be in RC? 0.


“DELETE ABC.DATA”

Question from Page 61:

107
108

Can you talk to ISPF from any EXEC? Yes, if a valid ISPF environment is
active.

108
109

Questions from Page 66:

True or false: there must be a space after the name of the function and before the
parenthesis when you use the function. False.

When you call a built-in function with CALL, where is the answer given? In the
special variable RESULT.

Questions from Page 70:

True or false: in REXX, user-written functions / subroutines may be used as either


functions or subroutines. True.

True or false: if the code is used as a function, you execute it this way:
function_name(data). True.

True or false: if the code is used as a subroutine, you execute it this way: Call
function_name data and the answer comes back in RESULT. True.

109
110

Questions from Page 81:

What will this line of a program do: SAY QUEUED() Display how many lines
are in the program stack.

There is nothing in either stack. Your program says PULL. What happens?
Keyboard unlocks, waiting for your input.

Your exec does 10 PUSHes, and 9 PULLs. What happens? The last line in the
stack waits for another PULL, write, or TSO command looking for input. If
the exec ends before that happens, the last line in the stack is passed to
TSO to be executed.

Your exec does 9 PUSHes, and 10 PULLs. What happens? Keyboard unlocks,
waiting for your input.

Question from Page 85:

When and why should you use NEWSTACK? Use NEWSTACK when you want
to preserve stack contents, or as good practice when you interact with the
user via PULL. You must do this if there is a risk of a left-over command or
line of data from a previous command using the stack.

110
111

This page intentionally left blank.

111
112

Appendix C: Solutions to Exercises

Solutions to exercises may also be found in the HIGPLEX dataset


‘DBIG.REXX.CLASS.EXEC’.

Exercise 1, page 19

Write an exec to execute the following two TSO commands:

TIME
SEND ‘Sample REXX ‘ U(*)

Include the comment,

This is a sample REXX program for class

Execute the REXX statement below, but use a continuation character and two lines:

Say TIME() DATE()

/* This is a sample REXX program for class */


"TIME"
"SEND 'Sample REXX ' U(*)"
Say TIME(),
DATE()

112
113

Exercise 2, page 21

Write an exec to display these three lines:

3 + 1 is 4
‘3 + 1 is 4’
O’Brien

/* REXX # 2 */
Say "3 + 1 is 4"
Say "'3 + 1 is 4'”
Say "O'Brien”

Exercise 3, page 24

Write an exec to:

 Assign the number 10 to a variable;


 Assign the number 20 to a variable;
 In one instruction, display the total of the two.

/* REXX */
A = 10
B = 20
Say "A + B is" A + B

113
114

Exercise 4, page 29

Write an exec to:

 Store the number 15 as a constant;


 Store the number 19 as a constant;
 Compare the numbers and find them equal.

/* Rexx FUZZ example */


A = 15
B = 19
Numeric Digits 2
Numeric Fuzz 1
If A = B
Then Say "A is equal to B"
Else Say "A is not equal to B"

Exercise 5, page 32

Write an exec to execute the LISTCAT ENTRY(dsn) command. Pass the DSN using
ARG. Make sure it works on your own datasets and datasets not belonging to you.

/* REXX */
Arg DSN
"LISTC ENT("DSN")"

114
115

Exercise 6, page 34

Write an exec to accept three pieces of information and display them in reverse order
of entry. If more than three are entered, display an error message.

/* REXX rejecting additional arguments */


Arg VAR1 VAR2 VAR3 JUNK
If JUNK = ""
Then NOP
Else Say "Too many args given."
Say VAR3 VAR2 VAR1

Exercise 7, Page 34

Write an exec to examine information passed to it. Perform the following checks:

 If “Al” is entered, display “Must be a Liberal”


 If “RUSH” is entered, display “Gotta be a Conservative”
 If both “Al” and “RUSH” typed in, display “Look out!”
 If neither given, display an error message

/* REXX Agenda */
Arg NAME1 NAME2
If NAME1||NAME2 = "ALRUSH",
| NAME1||NAME2 = "RUSHAL"
Then Say "Look out!"
Else Do
If NAME1 = "AL" | NAME2 = "AL"
Then Say "Must be a liberal."
If NAME1 = "RUSH" | NAME2 = "RUSH"
Then Say "Must be a conservative."
If NAME1 <> "RUSH" & NAME2 <> "RUSH",
& NAME1 <> "AL" & NAME2 <> "AL"
Then Say "Oh, a moderate."
End

115
116

Exercise 8, Page 37

Write an exec to accept two args: the day of the week and the weather. If the day is
Friday and the weather is rainy display “Head for office!”. If the day is Friday and the
weather is not rainy display “Head for golf course!”.

/* REXX rainy and golf? */


Arg WEEKDAY WEATHER
If WEEKDAY = "FRIDAY"
Then If WEATHER = "RAINY"
Then Say "Head for office."
Else Say "Head for golf course!"
Else NOP /* Do nothing if day not Friday */

Exercise 9, Page 39

Write an exec to ask for three, and only three, words, and display them in reversed
order.

/* REXX interact with user */


Say "Enter three words.”
Pull VAR1 VAR2 VAR3 .
Say VAR3 VAR2 VAR1

Exercise 10, Page 39

Write a front end exec for the DELETE command. Have it ask, “Are you sure?”. If
the dataset name is “junk.data” don’t ask.

/* REXX DELETE front end */


Arg DSN
If DSN <> "JUNK.DATA" Then Do
Say "Are you sure (enter Y/N)?"
Pull ANS
If ANS = "N" Then Exit /* Don’t delete! */
"DELETE" DSN

116
117

Exercise 11, Page 39

Write an exec to compute the percent increase of one number over another. Ask for
the two amounts from the terminal. The formula is:

% increase = 100 * ((new – old) / old)

/* REXX percent increase */


Say "Enter two numbers. The percent increase of"
Say "the second from the first is displayed."
Pull OLD NEW
Say "Percent increase is" 100 * ((new - old) / old)

Exercise #12, Page 39

Ask for the weather and the day of the week from the terminal. If it’s raining, ask how
many inches are expected.

If it’s Friday and sunny or cloudy, display “Head for the golf course!”

If it’s Saturday and less than .5 inches, display “Fishing pole, please!”

/* REXX input from user */


Say "What day of the week is this?"
Pull WEEKDAY
Say "What's the weather out?”
Pull WEATHER
RAIN = 0 /* Default if not rainy */
If WEATHER = "RAINY" Then Do
Say "How much rain (in inches)?"
Pull RAIN
End
If WEEKDAY = "FRIDAY" & WEATHER <> "RAINY"
Then Say "Head for golf course!”
If WEEKDAY = "SATURDAY" & RAIN < .5
Then Say "Fishing pole please!"

117
118

Exercise 13, Page 48

Write an exec asking for a dataset name. It then issues the TSO command LISTD.
Set up an error trap to intercept the command not working.

In the error trap display the line of the program in error, the string producing the error,
and the error code from TSO. Ask for the dataset name again, re-execute the command,
and exit.

/* REXX error handling example */


Call On Error

Say "Front end for LISTD. Enter a dataset name:"


Pull DSN
"LISTD" DSN
Say
Say "Exec terminated."
Exit

ERROR:
Say "Command to TSO did not work on line" SIGL
Say "Command was" SOURCELINE(SIGL)
Say "Abend Code was" RC
Say
Say "The DSN may be wrong. Try again:"
Pull DSN
"LISTD" DSN
Return

118
119

Exercise 14, Page 50

Write an exec asking for two numbers, and then multiply them. Set an error trap to
intercept any error such as invalid numbers.

/* REXX error handling example #2 */


Signal On Error
Signal On Syntax

Say "Enter two numbers to be multiplied."


Pull NUM1 NUM2
Say
Say NUM1 "multiplied by" NUM2 "is" NUM1 * NUM2 "."
Exit

ERROR:
SYNTAX:
Say "Command to TSO did not work on line" SIGL
Say "Error; command was" SOURCELINE(SIGL)
Say "Abend Code was" RC
Exit

Exercise 15, Page 50

Write an exec asking for an LRECL and a BLKSIZE. Determines if the BLKSIZE is
an even multiple of the LRECL. Tell the user if it is or is not.

/* REXX arithmetic example */


Say "Enter an LRECL here:"
Pull LRECL
Say "Enter the chosen BLKSIZE here:"
Pull BLKSIZE
If BLKSIZE//LRECL = 0
Then Say "BLKSIZE is evenly divisible by LRECL"
Else Say "BLKSIZE NOT evenly divisible by LRECL"

119
120

Exercise 16, Page 52

Write a metric conversion exec. The exec should accept two arguments: UNIT and
QUANTITY. UNIT can have one of these possible values: LITER, QUART, MILE, or
KILOMETER.

It returns the equivalent of Quantity in the other measurement system based on this
table:

1 liter = 1.057 quarts


1 quart = 0.946 liters
1 mile = 1.6 kilometers
1 kilometer = 0.625 miles

/* REXX Metric conversion */


Say "Metric conversion program."
Say
Say "Enter a number followed by a unit of measure.
Say “The unit of measure must be one of LITER,”
Say “QUART, MILE, or KILOMETER."
Pull AMT UNIT

Select
When UNIT = "LITER"
Then Say AMT UNIT "is" AMT*1.057 "quart."
When UNIT = "QUART"
Then Say AMT UNIT "is" AMT*.946 "liter."
When UNIT = "MILE"
Then Say AMT UNIT "is" AMT*1.6 "kilometer."
When UNIT = "KILOMETER"
Then Say AMT UNIT "is" AMT*.625 "mile."
Otherwise Say "Invalid unit entered."
End

120
121

Exercise 17, Page 56

Write an exec asking the user to enter a series of numbers, never the same one twice in
a row. Limit the list to 10 numbers.

/* REXX Looping problem */


Say "Enter up to 10 numbers. You can’t enter the”
Say "same number twice in a row or the number is”
Say "ignored. To quit entering numbers, enter a”
Say “null or an 'X'.”
Say
HOLDNUM = "HOLDNUM" /* Init, just in case */
Do I = 1 to 10
Say "Enter number" I":"
Pull NUM
If NUM = "X" | NUM = "" Then Leave
If NUM = HOLDNUM Then Iterate
HOLDNUM = NUM
End

Exercise 18, Page 61

Write an ISPF edit macro to save and execute the exec you’re currently working on. It
must start with the usual comment, then ADDRESS ISREDIT MACRO. The commands
to be passed to the ISPF editor are:

(CURMBR) = MEMBER
SAVE

The command to be passed to TSO is:

EXEC your_exec_library(curmbr) EXEC

/* REXX edit macro, save current exec and run it */


Address ISREDIT
"MACRO"
"(CURMBR) = MEMBER"
"SAVE"
"EXEC 'COMMON.REXX.CLASS("CURMBR")' EXEC"
Exit

121
122

Exercise 19, page 66

Write an exec to accept 2 numbers from the terminal. Verify they are valid numbers.
Subtract one from the other, drop the sign and display the answer.

/* REXX Function Call */


Say "Enter two numbers to be subtracted. The”
Say “result is the absolute value of the”
Say “difference."
Pull V1 V2
If DataType(V1) <> "NUM" | DataType(V2) <> "NUM"
Then Do
Say "At least one value not numeric."
Exit
End
Say "Difference is" ABS(V1 - V2)

Exercise 20, Page 66

Write an exec see if a dataset called WORKSHOP.TEMP exists. If so, delete it. If
not, allocate a new one like your EXEC library. The TSO command is:

ALLOC DSN(WORKSHOP.TEMP) LIKE(your.exec)

/* REXX WORKSHOP.TEMP */
LDRC = LISTDSI("WORKSHOP.TEMP")
If LDRC = 0
Then "DELETE WORKSHOP.TEMP"
Else "ALLOC DSN(WORKSHOP.TEMP) LIKE(EXEC)"

122
123

Exercise 21, Page 70

Write an exec with an internal function / subroutine to concatenate, without spaces,


two data items passed to it. Invoke it as a function and as a subroutine. Check
automatically within the exec to verify the calls produce the same result.

/* REXX subroutine call: internal */


Say "Enter two data items to concatenate together.”
Say "Separate the items by a space."
Pull VAR1 VAR2

Call CONC VAR1,VAR2


SUBRES = RESULT /* Call as subroutine */
SUBFUN = CONC(VAR1,VAR2) /* Call as function */
NOSUBR = VAR1||VAR2 /* Concat compare */

Say "Called as subroutine: " SUBRES


Say "Called as function: " SUBFUN
Say "'Normal' concatenation: " NOSUBR

If SUBRES == SUBFUN /* Exact equality */


Then Say "Function and subroutine calls match”
Else Say "Function and subroutine do not match”
Exit

/*-----------------------------------------*
* CONCatenate any two variables into one. *
*-----------------------------------------*/
CONC:
Arg V1,V2
ANS = VAR1||VAR2
RETURN ANS

123
124

Exercise 22, Page 73

Redo the % increase exec from Exercise #11 as an external subroutine. Name it
PERCENT. Have it accept two parameters, the old number and the new number. Have it
return the % increase on the RETURN statement. Check both args to be numeric. If
either or both arguments are invalid display a message and return a “0”.

/* REXX external subroutine PCTINCR */


ARG OLD,NEW

If DataType(OLD) <> "NUM" Then Do


Say "First value not numeric. Returning 0."
Return 0
End
If DataType(NEW) <> "NUM" Then Do
Say "Second value not numeric. Returning 0."
Return 0
End

PCTX = 100 * ((NEW - OLD) / OLD)

Return PCTX

124
125

Exercise 23, Page 73

Using your solution to #21, write your concatenate subroutine as an external function /
subroutine (leave the internal one where it is). Call the external function / subroutine in a
such a way as to make sure you invoke the external one and not the internal one. Prove
you made the right call.

/* REXX subroutine call: internal */


Arg V1,V2
ANS = V1||V2
Say "Entered External Subroutine!”
RETURN ANS /* Go back to to caller */

/* REXX caller: internal vs external */


Say "Enter two data items to concatenate.”
Say "Separate the items by a space."
Pull VAR1 VAR2

Call "EX23" VAR1,VAR2

Exit

/*--------------------------------------------*
* This is the subroutine as an internal *
* call. There is a display in the external *
* call, none in this one. If the external *
* one is called, you should see the display. *
*--------------------------------------------*/
EX23:
Arg V1,V2
ANS = V1||V2
RETURN ANS /* Return the answer */

125
126

Exercise 24, Page 73

Convert the metric conversion program from Exercise #16 to an external subroutine
called METRIC, and write a main exec calling the function.

/* REXX Metric conversion returns "0" if invalid */


Arg AMT,UNIT
Select
When UNIT = "LITER" Then ANS = AMT*1.057
When UNIT = "QUART" Then ANS = AMT*.946
When UNIT = "MILE" Then ANS = AMT*1.6
When UNIT = "KILOMETER" Then ANS = AMT*.625
Otherwise ANS = 0
End
Return ANS

/* REXX Metric conversion caller */


Say "Metric conversion program. Enter a number”
Say "followed by a unit of measure. Note the unit"
Say "of measure must be one of LITER, QUART, MILE,”
Say “or KILOMETER."
Pull AMOUNT UNIT

Call "EX24" AMOUNT,UNIT

Select
When UNIT = "LITER"
Then Say AMOUNT UNIT "is" RESULT "quart."
When UNIT = "QUART"
Then Say AMOUNT UNIT "is" RESULT "liter."
When UNIT = "MILE"
Then Say AMOUNT UNIT "is" RESULT "kilometer."
When UNIT = "KILOMETER"
Then Say AMOUNT UNIT "is" RESULT "mile."
Otherwise Say "Invalid unit entered."
End

126
127

Exercise 25, Page 73

Create an external subroutine called SQRT to compute the square root of a number. It
accepts one number only. Make sure the data item is a number. If it’s not numeric (or it’s
negative) display an appropriate message and return a “0”. Set a loop limit of 50.

Start with a guess of ½ the number. The loop consists of this:

Make new guess = (guess + (number / guess)) / 2


Make guess = new guess

Return the new guess to the caller.

/* REXX square root program */


Arg BASE

/* Validate if numeric and greater than 0 */


If DataType(BASE) <> "NUM" Then Do
Say "Variable passed is not a valid number.”
Return 0
End
If BASE <= 0 Then Do
Say "Variable is 0 or negative.”
Return 0
End

/* Now determine square root. 50 times close enough */


NEWNUM = BASE / 2 /* First guess is 1/2 number */
Do 50
If NEWNUM * NEWNUM = BASE then leave
NEWNUM = (NEWNUM + (BASE / NEWNUM)) / 2
End
Return NEWNUM

/* REXX square root program CALLER */


Say "Enter a number to calculate the square root."
Pull NUM
Say "Square root of” NUM “is” EX25(NUM) “.”

127
128

Exercise 26, Page 76

Write an exec with a loop setting NUMBER.1 through NUMBER.10 to the numbers
101 through 110. Then loop through the array displaying the contents of NUMBER.1
through NUMBER.11 to see what happens.

/* REXX using stems */


Do I = 1 to 10
NUMBER.I = 100 + I
End
Do I = 1 to 11
Say "Number" I "is" NUMBER.I
End

Exercise 27, Page 78

Write an exec to capture the display from TSO commands in an array, and then
display the lines of output one at a time.

 Display “Please enter a command”


 Accept the command into a variable
 Turn on OUTTRAP, using the array Display_Line
 Run the command, then turn off OUTTRAP
 Display the message, “Here comes the command’s output:”
 Loop through the array, displaying each line of the command’s output.

/*Rexx -- capture TSO command


Say "Enter a TSO command:"
Pull CMD

DUMMY = OutTrap(Display_Line.)
Address TSO CMD
DUMMY = OutTrap(Off)

Say "Here comes the output:”


Do I = 1 to Display_Line.0
Say Display_Line.I
End

128
129

Exercise 28, Page 85

Write a REXX to accept a dataset name from the terminal. Verify the DSN is less than
45 bytes long. Verify it exists. Place the DSN into the stack so the LISTD command can
get at it. Suggestion: use QUEUE.

Turn on prompting. Execute the LISTD command but omit the dataset name so TSO
will prompt for it. Since the DSN is in the stack, LISTD should get it from there.

/* REXX use the stack to pass a DSN to LISTD. */

Say "Enter dataset name to pass to LISTD."


Pull DSN
If Length(DSN) > 44 Then Do
Say "DSN" DSN "longer than 44 bytes."
Exit
End
If SYSDSN(DSN) <> "OK" Then Do
Say DSN "does not exist."
Exit
End
Queue DSN
DUMMY = Prompt("ON")
"LISTD"

129
130

Exercise 29, Page 89

Choose a file you currently have available. Read it into an array, and then display
every other line starting with Line 1.

/* REXX Display File Contents */


"ALLOCATE F(MEMIN) DA('your.DSN') SHR REUSE"
"EXECIO * DISKR MEMIN (STEM MEMLIST. FINIS"
"FREE F(MEMIN)"

DO I = 1 TO MEMLIST.0 By 2
Say MEMLIST.I
END

130
131

Exercise 30, Page 89

Choose any available file. Read it into an array one record at a time. Count each line
as you read. Keep track of the longest record length. At the end of the program display
the record count and the length of the longest record.

/* REXX read a file a record at a time */


“ALLOCATE F(INFILE) DA(your.dsn) SHR REUSE”
“NEWSTACK”
LineNum = 0
LLine = 0
EOF = “NO”
Call READIT

MAINLINE:
Do I = 1 While EOF = “NO”
LineNum = LineNum + 1
Pull RECORD
LineLen = Length(RECORD)
If LineLen > LLine Then LLine = LineLen
Call READIT
End
Say “Number of lines read: “ LineNum
Say “Length of Longest Record: “ LLine
“DELSTACK”
“FREE F(INFILE)”
Exit

READIT:
“EXECIO 1 DISKR INFILE”
If RC <> 0 Then Do
EOF = “YES”
“EXECIO 0 DISKR INFILE (FINIS”
End
Return “”

131
132

Exercise 31, Page 90

In the OUTTRAP discussion above, we had this example:

DUMMY = OUTTRAP(“LINE.”)
/* your command here */
Do I = 1 to LINE.0 /* write each line */
Say “Line of output was:” LINE.I
End
DUMMY = OUTTRAP(“OFF”)

Modify this example to store the lines captured in the dataset


TEMP.CAPTURE.DATA:

 Use SYSDSN to see if the dataset exists. If not, allocate it using this command:
“ALLOC DA(TEMP.CAPTURE.DATA) F(CAPTURE) NEW SPACE(1 1) TRACKS
LRECL(80) RECFM(F B)”
 OUTTRAP loads an array. Use that array to write the lines of the commad display.

The write command should close the file when it’s done writing.

/* REXX Capture output from command in dataset */


Say “Enter TSO command:”
Pull CMD

DUMMY = OutTrap(Display_Line.)
Address TSO CMD
DUMMY = OutTrap(Off)

If SYSDSN(TEMP.CAPTURE.DATA) = “OK”
Then “ALLOC F(CAPTURE) DA(TEMP.CAPTURE.DATA) OLD REUSE”
Else “ALLOC F(CAPTURE) DA(TEMP.CAPTURE.DATA) NEW REUSE
SPACE(1 1) TRACKS LRECL(80) RECFM(F B)”
"EXECIO" Display_Line.0 "DISKW CAPTURE (STEM
Display_Line. FINIS”
Address ISPEXEC “BROWSE DATASET(TEMP.CAPTURE.DATA)”

132
Appendix D: REXX Functions and Instructions

Keywords are supported under both TSO and CMS unless noted. Some keywords are
used both as instructions and as functions. Keywords without a description are
environment commands (e.g. CONWAIT). Instructions begin on page 2; function
descriptions begin on Page 24.

Page numbers refer to pages in this appendix.

KEYWORD PAGE KEYWORD PAGE KEYWORD PAGE


ABBREV function 25 HI instruction 8 RETURN instruction 19
ABS function 25 HT instruction 8 REVERSE function 38
ADDRESS instruction 2 IF instruction 8 RIGHT function 39
ADDRESS function 25 INDEX function 32 RT instruction 19
ARG instruction 3 INSERT function 32 SAY instruction 19
ARG function 26 INTERPRET instruct. 10 SELECT instruction 20
BITAND function 27 ITERATE instruction 11 SIGL variable 28
BITOR function 27 JUSTIFY function 32 SIGN function 39
BITXOR function 27 LASTPOS function 33 SIGNAL instruction 21
CALL instruction 3 LEAVE instruction 11 SOURCELINE function 39
CENTER function 27 LEFT function 33 SPACE function 39
COMPARE function 28 LENGTH function 33 STORAGE function 39
CONDITION function 28 LINESIZE function 33 STRIP function 40
CONWAIT 4 LISTDSI function 33 SUBCOM 21
COPIES function 28 MAKEBUF / DROPBUF 12 SUBSTR function 40
C2D function 28 MAX function 35 SUBWORD function 40
C2X function 28 MIN function 35 SYMBOL function 40
DATATYPE function 29 MSG function 36 SYSDSN function 41
DATE function 30 MVSVAR function 36 SYSVAR function 42
DELSTR function 30 NEWSTACK / DELSTACK 13 TE instruction 21
DELWORD function 30 NOP instruction 14 TIME function 44
DESBUF 4 NUMERIC instruction 14 TRACE instruction 22
DIAGRC function 31 OTHERWISE instruct. 14 TRACE function 44
DIGITS function 31 OUTTRAP function 37 TRANSLATE function 44
DO instruction 4 OVERLAY function 37 TRUNC function 44
DROP instruction 5 PARSE instruction 15 TS 21
D2C function 31 POS function 38 UPPER instruction 21
D2X function 31 PROCEDURE instruct. 17 USERID function 45
END instruction 5 PROMPT function 38 VALUE function 45
ERRORTEXT function 31 PULL instruction 17 VERIFY function 45
EXECIO 5 PUSH instruction 17 WORD function 45
EXECUTIL 7 QBUF 18 WORDINDEX function 45
EXIT instruction 7 QELEM 18 WORDLENGTH function 45
EXPOSE instruction 7 QSTACK 18 WORDPOS function 45
EXTERNALS function 31 QUEUE instruction 19 WORDS function 46
FIND function 31 QUEUED function 38 XRANGE function 46
FORM function 32 RANDOM function 38 X2C function 46
FORMAT function 32 RC variable 23 X2D function 46
FUZZ function 32 RESULT variable 23
REXX instructions and environment commands

The ADDRESS instruction tells REXX to pass commands to a specific command


processor called an environment. Valid environments are:

TSO:
TSO (the default)
ISPEXEC (ISPF, Dialog Manager)
ISREDIT (the ISPF editor)

CMS:
CMS (the default if the file type is EXEC)
XEDIT (the default if the file type is XEDIT, i.e. XEDIT macros)
COMMAND (the CMS command processor. Note this bypasses synonyms,
requires upper case, and requires the file type EXEC for EXECs and CP for
CP commands).

ADDRESS environment changes the default environment. All non-REXX commands


are sent to this environment until the next ADDRESS instruction.

Example:
ADDRESS ISPEXEC
“DISPLAY PANEL(MENUPANL)”
“VGET (RESP) SHARED”
ADDRESS TSO
“DELETE” RESP

ADDRESS environment command sends the one command to the named environment.
Other commands are not sent to this environment.

Example:
ADDRESS ISPEXEC “DISPLAY PANEL(MENUPANL)”
“DELETE” RESP
The ARG instruction receives data from a calling program. It is a short form of PARSE
UPPER ARG. In a main program it receives data typed on the command line; in a
function or subroutine it receives data passed to it on the call.

The data may be received in one or more variables separated by a delimiter such as a
space or comma. Fields separated by spaces are considered words; commas are normally
used in function calls. Use a period (.) to drop variables.

ARG FIELD1 FIELD2 FIELD3 places the first word (all characters up to the
first blank) in FIELD1, the second word in FIELD2, and all remaining characters
(spaces and all) in FIELD3.

ARG FIELDS places all data passed into the variable FIELDS.

ARG FIELD1 FIELD2 . places the first word in FIELD1, the second word in
FIELD2, and ignores everything after the second word.

ARG FIELD1,FIELD2 places all data before the first comma into FIELD1 and
the remaining data into FIELD2.

The CALL instruction invokes a subroutine, whether external or internal. The called
routine should end with a RETURN instructor. CALL can also turn on or off an error
trap.

CALL subroutine parameters passes parameters to the subroutine, which picks them up
via the ARG instruction. The subroutine passes the result, if any, in the variable
RESULT.

Example:
CALL LENGTH “ABCD”
SAY RESULT /* displays a 4 */

CALL ON condition-name sets up an error trap (see SIGNAL). If the error trap is
entered, a RETURN at the end of the trap sends control back to the instruction after the
failed instruction.

Example:
CALL ON ERROR /* sends control to the ERROR trap */
CONWAIT is a CMS-only instruction. It waits until all output directed to the terminal
has been displayed. Often used with DESBUF to ensure no terminal output is lost while
clearing the terminal output buffer.

Example:
PUSH “CART”
“CONWAIT”
“DESBUF” /* CART is lost */

DESBUF is a CMS-only instruction. It clears the stack and both the input and output
terminal buffers. Often used with CONWAIT to ensure no terminal output is lost while
clearing the buffers.

Example:
PUSH “CART”
“CONWAIT”
“DESBUF” /* CART is lost */

The DO instruction starts a group of instructions to be performed repeatedly. The


number of times the instruction group repeats is controlled by a variable or a REXX
keyword set. Some examples:

DO I = 1 TO 10 /* Increment a variable */
SAY I
END

DO I = 10 TO 1 BY -1 /* Decrement a variable */
SAY I
END

DO FOREVER /* Loop forever, get out with LEAVE */


IF TIME() > “16:00:00” THEN LEAVE
END

DO UNTIL TIME() > “16:00:00” /* Until test true */


SAY “WORK”
END

DO WHILE TIME() < “16:00:00” /* As long as true */


SAY “WORK”
END

DO 10; SAY “WORK”; END /* Loop this many times */


DROP undefines a variable. REXX takes the string as an upper case literal.

Example:
GREETING = “HI!”
SAY GREETING /* displays HI! on the screen */
DROP GREETING
SAY GREETING /* displays GREETING on the screen */

END ends a DO or SELECT structure. If on a counting loop, END can name the
counting variable to ensure the right loop is ending.

Example:
DO I = 1 TO 10
SAY I
END I /* we know it’s ending the DO I loop */

The EXECIO TSO/CMS instruction handles dataset I/O for reading, writing, and
updating datasets in a REXX exec. In TSO you must ALLOCATE the dataset before you
can run EXECIO.

CMS examples: note filename, filetype, and filemode stand for the actual file being
read or written.

“EXECIO * CARD” reads from reader into stack

“EXECIO * CARD (STEM RECD.” reads from the reader into the variables
RECD.1 through RECD.n where “n” is the number of cards read. EXECIO
sets RECD.0 to the number of cards read.

“EXECIO “ RECD.0 ”PRINT” writes all records from RECD. to the printer.

“EXECIO “ QUEUED() ”PUNCH” writes all records from the stack to punch.

“EXECIO * DISKR filename filetype filemode” reads from the


named file into the stack

“EXECIO * DISKR filename filetype filemode (STEM RECD.”


reads from the named file into the stem variable RECD.

“EXECIO “ QUEUED() ” DISKW filename filetype filemode”


writes all records from the stack to the named file.
EXECIO CMS examples continued.

“EXECIO “ RECD.0 ” DISKW filename filetype filemode


(STEM RECD.” writes the entire array RECD. to the named file.

“EXECIO 1 DISKRU filename filetype filemode” gets one


record from the file and holds it for rewriting.

“EXECIO 1 DISKRU filename filetype filemode (VAR REC1”


gets one record from the file and holds it in the variable REC1 for rewriting.

“EXECIO 1 CP (STRING QUERY READER ALL)” passes the command


QUERY READER ALL to CP and receives the reply in the stack.

“FINIS filename filetype filemode” closes the file.

TSO examples: note ddname stands for the DDname of the dataset being read or
written.

“EXECIO * DISKR ddname (FINIS” reads from the dataset into the stack
and closes the dataset.

“EXECIO * DISKR ddname (STEM RECD. FINIS” reads from the


dataset into the array RECD. and closes the dataset.

“EXECIO “ QUEUED() “ DISKW ddname (FINIS” writes all records


from the stack to a dataset and closes it.

“EXECIO “ RECD.0 “ DISKW ddname (STEM RECD. FINIS”


writes all records from the array RECD. to a dataset and closes it.

“EXECIO 1 DISKRU ddname” gets one record from the file and holds it for
rewriting.

“EXECIO 1 DISKRU ddname (VAR REC1” gets one record from the file
and holds it in the variable REC1 for rewriting.
The EXECUTIL instruction is valid in TSO only. It may be executed inside an exec or
as a native TSO command. It controls the execution of a REXX exec. Some examples:

“EXECUTIL TS” turns on interactive debugging with TRACE R.

“EXECUTIL TE” turns off interactive debugging.

“EXECUTIL HT” stops display of all SAYs, even in other execs.

“EXECUTIL RT” resumes display of all SAYs.

“EXECUTIL HI” stops program execution.

“EXECUTIL SEARCHDD(YES)” usually executed before the first REXX exec


in a TSO session. It causes TSO to search for REXX execs in the DDname
SYSEXEC (the default is SYSPROC). Not usually found inside REXX
execs.

The EXIT instruction ends the REXX program and returns control to the calling program
or environment. May pass back a return code (numeric only) by coding it after the
instruction (EXIT 8 passes a return code of 8). The return code can be checked as
follows:

 TSO: use the WHEN TSO command to check.


 CMS: return code is displayed on the terminal.
 REXX exec: return code is found in the variable RC.
 CLIST: the return code is found in the variables &LASTCC and &MAXCC.

The EXPOSE instruction is used with the PROCEDURE instruction to allow the named
variables to be shared with the main program. In effect, EXPOSE makes the named
variables global. This instruction is usually found right after the PROCEDURE
statement; for example:

SUBPGM: PROCEDURE EXPOSE VAR1 shares VAR1 with the main program;
all other variables in the procedure SUBPGM are protected and cannot be
seen or modified by the main program.
The HI instruction stops execution of the program (HI is Halt Immediate). In TSO it
may only be executed after an ATTN interrupt. In CMS, it may be executed only when
the screen displays MORE … in the lower right.

The HT instruction stops display to the terminal (HT is Halt Terminal). In TSO it may
only be executed after an ATTN interrupt. In CMS, it may be executed only when the
screen displays MORE … in the lower right. See the RT command.

The IF instruction controls conditional execution of one or more instructions. If the


expression is true, the instrruction on the THEN keyword is processed. If the expression
is false, either nothing happens (no ELSE) or the instruction on the ELSE keyword is
executed.

The format of the IF instruction is:

IF expression
THEN instruction
ELSE instruction

The expression may contain one of these comparison operators. Numbers are
compared numerically, characters by the collating sequence. See also the
description under “equal” and “strictly equal.”

= Equal. Numbers are equal numerically, e.g. 001.0 equals 1. Characters are
considered equal ignoring leading or trailing spaces, e.g. “ DEB “ is equal to
“DEB”. Case is significant, so “DEB” does not equal “Deb”.

<> Not equal. The opposite of “=”. Other ways of writing not equal are ^=, \=,
and ¬ =.

> Greater than.

>= Greater than or equal.

¬> Not greater than. The opposite of “>”. Other ways of writing not greater than
are ^= and \=.

< Less than.

<= Less than or equal.

¬> Not less than. The opposite of “<”. Other ways of writing not less than are ^=
and \=.
IF expressions continued.

== Equal in every way, strictly equal. This is a bit comparison rather than a value
comparison.

\== Not strictly equal. Also ¬==.

>> Strictly greater than. This is a bit comparison.

>>= Strictly greater than or equal to. This is a bit comparison.

<< Strictly less than. This is a bit comparison.

<<= Strictly less than or equal. This is a bit comparison.

¬>> Strictly not greater than. This is a bit comparison.

¬<< Strictly not less than. This is a bit comparison.

The expression may also contain one or more of these logical connectors.

& AND. Both conditions must be true.


| OR. One or both conditions must be true.
&& Exclusive OR. Only one, but not both, conditions must be true.

Examples:

IF A = 1
THEN SAY “A IS EQUAL TO 1”
ELSE SAY “A IS NOT EQUAL TO 1”

IF A = 1
THEN DO
SAY “A IS EQUAL TO 1”
SAY “ISN’T IT?”
END
ELSE SAY “A IS NOT EQUAL TO 1”
IF examples continued.

IF A = 1 & B = 1
THEN SAY “A and B ARE BOTH EQUAL TO 1”
ELSE SAY “ONE OF A OR B IS NOT EQUAL TO 1”

IF A == “DATE”
THEN SAY “TRULY A DATE”
ELSE SAY “MAYBE A DATE, BUT NOT EXACTLY”

IF A ¬= “MEAT”
THEN SAY “I CAN EAT THAT”
ELSE DO
SAY “SORRY, I’M A VEGETARIAN”
EXIT
END

IF A <= 3
THEN SELECT
WHEN A = 1 THEN SAY “IT’S ON!”
WHEN A = 3 THEN SAY “IT’S OFF!”
OTHERWISE SAY “IT’S NOT ON OR OFF.”
END
ELSE IF A = 4
THEN SAY “SWITCH IS IN MIDDLE.”
ELSE SAY “INVALID VALUE FOR A.”

TRUE = 1
IF TRUE
THEN SAY “IT’S TRUE!”
ELSE SAY “DON’T LIE TO ME!”

The INTERPRET instruction causes REXX to look at data as if it is seeing it for the first
time. The data may be a REXX instruction or a command meant for the command
processor. Two examples:

INTERPRET “SAY HI!”

PART1 = “S”
PART2 = “AY HI”
PART3 = “!”
INTERPRET PART1||PART2||PART3
The ITERATE instruction sends control to the DO of a DO-END loop. It skips the
instructions between it and the END. In a nested loop, ITERATE works only in its loop.
If ITERATE names a loop’s variable, REXX iterates within that loop. Three examples:

DO I = 1 TO 20
IF I = 13 THEN ITERATE /* SKIP #13 */
SAY I
END

DO 20 /* Skips #13 in the inner loop only */


DO I = 1 TO 20
IF I = 13 THEN ITERATE
SAY I
END
END

DO HR = 1 TO 12
DO MM = 1 TO 60
IF MM = 60 THEN ITERATE HR /* next hour */
SAY HR“:”MM
END
END

The LEAVE instruction sends control to the instruction after the END of a DO-END
loop. LEAVE ends the loop it’s in. In a nested loop, LEAVE works only in its oen loop.
If LEAVE names a loop’s variable, REXX iterates within that loop. Three examples:

DO I = 1 TO 20
IF I = 13 THEN LEAVE /* PRINT 1 – 12 ONLY */
SAY I
END

DO 20 /* The process is still done 20 times */


DO I = 1 TO 20
IF I = 13 THEN LEAVE /* PRINT 1 – 12 ONLY */
SAY I
END
END

DO HR = 1 TO 12
DO MM = 1 TO 60
IF MM = 60 THEN LEAVE HR /* next hour */
SAY HR“:”MM
END
END
The MAKEBUF instruction creates a new buffer. This buffer can be used like a stack,
but does not isolate the current stack. The buffer’s number is returned in the variable RC.
Drop buffers created with DROPBUF. When you delete the buffer, all its data is lost.
Here are two examples.

“MAKEBUF”
BUFNO = RC /* save buffer number */
SAY QUEUED() /* displays “1” */
PUSH “CART” /* place CART in stack */
SAY QUEUED() /* displays “1” */
“DROPBUF” BUFNO /* release MAKEBUF buffer */
SAY QUEUED() /* displays “0” (CART went away */
SAY “ENTER NAME” /* assume Arthur */
PULL NAME
SAY “THANKS,” NAME“.” /* NAME = Arthur */

PUSH “CART” /* place CART in stack */


SAY QUEUED() /* displays “1” */
“MAKEBUF”
BUFNO = RC /* save buffer number */
SAY QUEUED() /* displays “1” */
SAY “ENTER NAME”
PULL NAME
SAY QUEUED() /* displays “0” */
SAY “THANKS,” NAME“.” /* NAME = CART */
“DROPBUF” BUFNO /* release MAKEBUF buffer */
The NEWSTACK instruction creates a new program stack. Anything in the old stack is
preserved, but unavailable. Keep track of any new stacks you create. Drop user-created
stacks with the DELSTACK command. When the last user-defined stack is gone, the
original program stack is again available. Here are two examples.

PUSH “CART”
SAY QUEUED() /* displays “1” */
“NEWSTACK” /* get a new stack */
SAY QUEUED() /* displays “0” */
SAY “ENTER NAME” /* assume Arthur */
PULL NAME
SAY QUEUED() /* displays “0” */
SAY “THANKS,” NAME“.” /* NAME = Arthur */
“DELSTACK” /* return to the old stack */
SAY QUEUED() /* displays “1” */
SAY “ENTER VEHICLE” /* assume TAXI */
PULL VEHICLE
SAY “VEHICLE WAS” VEHICLE“.” /* displays CART */

“NEWSTACK”
PUSH “CART” /* place CART in stack */
SAY QUEUED() /* displays “1” */
“DELSTACK” /* Removes stack with CART */
SAY QUEUED() /* displays “0” */
SAY “ENTER NAME” /* assume Arthur */
PULL NAME
SAY QUEUED() /* displays “0” */
SAY “THANKS,” NAME“.” /* NAME = Arthur */
SAY QUEUED() /* displays “1” */
SAY “ENTER VEHICLE” /* assume TAXI */
PULL VEHICLE
SAY “VEHICLE WAS” VEHICLE“.” /* displays TAXI */
The NOP instruction tells REXX to do nothing. Here’s an example.

IF A = B
THEN NOP
ELSE SAY “A IS NOT EQUAL TO B.”

The NUMERIC instruction sets the way REXX deals with numbers. It has three forms,
explained below with examples.

NUMERIC DIGITS number sets the precision of arithmetic operations. The system
default is 9. While there is no limit to precision, high precision has high overhead. If
the number of digits exceeds the precision, the least significant digit is rounded.

NUMERIC FORM SCIENTIFIC or ENGINEERING sets the way numbers are


shown in exponential notation.

NUMERIC FORM SCIENTIFIC /* The default */


SAY 1.0001 * 10000000000000 /* displays 1.00010000E+9 */
NUMERIC FORM ENGINEERING
SAY 1.0001 * 10000000000000 /* displays 100.010000E+7 */

NUMERIC FUZZ number controls the number of low-order digits in numeric


comparisons. This allows approximation instead of insisting on exact equality.

NUMERIC FUZZ 1 /* Ignore least significant digit */


IF 987654321 = 987654320
THEN SAY “SURPRISE!”
ELSE SAY “THIS WON’T HAPPEN.”

The OTHERWISE instruction notes the default alternative in a SELECT structure. This
path is taken if none of the SELECT conditions are true. OTHERWISE is optional;
however, an END is required at the end of the SELECT whether there is an
OTHERWISE or not.

SELECT
WHEN S = “M” THEN SAY “HI, MISTER!”
WHEN S = “F” THEN SAY “HELLO, MISS!”
OTHERWISE SAY “GREETINGS!”
END
The PARSE instruction does character string functions. The basic syntax for PARSE is:

PARSE (UPPER) origin template

The UPPER is optional. If used, it converts the characters to upper case. If it is not used,
the characters remain in the case they started in.

PARSE takes the data from the origin and processes it using the template. The net result
is that all the variables in the template are set or changed in some way.

The template is just a list of variables. Possible orgins are:

ARG the command line.

EXTERNAL the terminal, without passing through the stack.

PULL the stack.

SOURCE internal settings from the environment and program.

VALUE a literal, function result, or variable.

VAR a variable.

VERSION internal system information about the version of REXX.

On this and the next two pages are several examples.

Normal parsing is done based on spaces.

/* REXX STOOGE */
PARSE UPPER ARG VAR1 VAR2 VAR3
SAY VAR1
SAY VAR2
SAY VAR3

> STOOGE MOE LARRY CURLEY

MOE
LARRY
CURLEY
PARSE examples continued.

Using a literal string delimiter. Data is first split at the delimiting character(s). Each
side of the delimiter is split by spaces or other delimiters. The delimiter is not
considered part of the string.

/* REXX STOOGE */
PARSE UPPER ARG VAR1 VAR2 “!” VAR3
SAY VAR1 VAR2
SAY VAR3

> STOOGE HEY MOE! HELLO!

HEY MOE
HELLO!

Using column delimiters. Data is split at the columns given. The number to the left
of a variable is the starting column; the number to the right of a variable is the
ending column, minus one.

/* REXX ALPA BETTY */


PARSE UPPER ARG 1 VAR1 4 VAR2 8 VAR3 11
SAY VAR1
SAY VAR2
SAY VAR3

> ALPHA ABCDEFGHIJKLMNOPQRSTUVWXYZ

ABC
DEFG
HIJ

/* REXX ALPA BETTY */


PARSE UPPER ARG 1 VAR1 4 6 VAR2 8 5 VAR3 11
SAY VAR1
SAY VAR2
SAY VAR3

> ALPHA ABCDEFGHIJKLMNOPQRSTUVWXYZ

ABC
FG
EFGHIJK
Using a value.

PARSE VALUE “TWO WORDS ONLY” VAR1 VAR2 .


SAY VAR1
SAY VAR2

TWO
WORDS

Using a variable.

VARSTRING = “HELLO ALL YOU PEOPLE”


PARSE VAR VARSTRING VAR1 VAR2 VAR3
SAY VAR1
SAY VAR2
SAY VAR3

HELLO
ALL
YOU PEOPLE

The PROCEDURE instruction is used in a subroutine or internal function to protect the


variables from the main program’s effects.

Example: SUB1: PROCEDURE

The PULL instruction takes a line from the program stack or, if empty, from the terminal
input buffer. Short form of PARSE UPPER PULL, see PARSE for more information.

The PUSH instruction puts a line into the program stack last in, first out (LIFO) (see the
QUEUE instruction). For example,

PUSH “HAS FLEAS”


VAR1 = “MY DOG”
PUSH VAR1
PULL LINE
SAY LINE
PULL LINE
SAY LINE

The resulting output is MY DOG HAS FLEAS.


The QBUF instruction returns the number of buffers created by the MAKEBUF
instruction. The number of buffers is returned in the variable RC. For example,

“QBUF”
SAY RC /* returns “0” */
“MAKEBUF”
“QBUF”
SAY RC /* returns “1” */
“DROPBUF”
“QBUF”
SAY RC /* returns “0” */

The QELEM instruction returns the number of elements or lines are available in the
buffer created by MAKEBUF. The number of buffers is returned in the variable RC. For
example,

“MAKEBUF”
PUSH “CART”
PUSH “CAR”
“QELEM”
SAY RC /* returns “2” */
“DROPBUF”
“QELEM”
SAY RC /* returns “0” */

The QSTACK instruction is available in TSO only. It returns the number of stacks
created by the NEWSTACK instruction, plus one. The number of stacks is returned in
the variable RC. To drop all the new stacks, use RC – 1 to know how many times to run
the DELSTACK command. For example,

“QSTACK”
HOW_MANY = RC - 1
DO HOW_MANY
“DELSTACK”
END
The QUEUE instruction puts a line in the program stack. Data is put into the stack first
in, first out (FIFO) (see the PUSH instruction). For example,

PUSH “HAS FLEAS”


VAR1 = “MY DOG”
PUSH VAR1
PULL LINE
SAY LINE
PULL LINE
SAY LINE

The resulting output is MY DOG HAS FLEAS.

The RETURN instruction sends control back from a subroutine or internal function to
the instruction just after the call. Except in an error trap, can pass back a single string or
variable to the caller. For example:

SUBR:
ARG NUM1,NUM2
TOTAL = NUM1 + NUM2
RETURN TOTAL

Returns the sum of NUM1 and NUM2 back to the main program.

The RT instruction resumes display to the terminal (RT is Resume Terminal). This is the
opposite of HT. In TSO it may only be executed after an ATTN interrupt. In CMS, it
may be executed only when the screen displays MORE … in the lower right.

The SAY instruction displays a line on the terminal. The line may be either a variable, a
literal, a function call, or any combination.

Examples:

SAY “Aaah!” /* displays “Aaah!” */


A = 1
SAY A /* displays “1” */
SAY TIME() /* displays result of the TIME function */
SAY “A is” A“, time is” TIME()“.”
The SELECT instruction is REXX’s CASE structure. Provides a selection of one of
many alternatives. An END is required at the end of the SELECT. OTHERWISE
provides an optional default answer. Often OTHERWISE is some sort of DO group to
correct for the missing cases.

Example:

SELECT
WHEN DAY = 1 THEN SAY “SUNDAY”
WHEN DAY = 2 THEN SAY “MONDAY”
WHEN DAY = 3 THEN SAY “TUESDAY”
WHEN DAY = 4 THEN SAY “WEDNESDAY”
WHEN DAY = 5 THEN SAY “THURSDAY”
WHEN DAY = 6 THEN SAY “FRIDAY”
WHEN DAY = 7 THEN SAY “SATURDAY”
OTHERWISE SAY “NO SUCH DAY OF THE WEEK!”
END

The SIGNAL instruction turns on or off a condition trap named by a label. The trap can
interrupt a non-normal condition wherever it occurs after the SIGNAL. The code for the
error trap is found after the end of the main program.

Examples:

SIGNAL ON SYNTAX /* syntax error */


SIGNAL ON ERROR /* command in error */
SIGNAL ON FAILURE /* command doesn’t exist */
SIGNAL ON NOVALUE /* uninitialized variable */
SIGNAL ON HALT /* attention interrupt */
:: code
EXIT

SYNTAX:
SAY “SYNTAX ERROR!”
EXIT

NOVALUE:
SAY “UNINITIALIZED VARIABLE ON” SOURCELINE(SIGL)
RETURN

SIGNAL can also be used as a label for a GOTO operation. For example,

SIGNAL DONE
:: code
DONE: A = A + 1
The SUBCOM instruction asks TSO or CMS if a particular environment is available, and
returns the answer in RC. RC is “0” if the environment is available, “1” if not.

Examples:

“SUBCOM” “TSO”
SAY RC /* displays “0” under TSO */
“SUBCOM” “ISPEXEC”
SAY RC /* displays “1” under TSO */
“SUBCOM” “ISPEXEC”
SAY RC /* displays “0” in ISPF */
“SUBCOM” “XEDIT”
SAY RC /* displays “0” if in XEDIT */
“SUBCOM” “USA”
SAY RC /* displays “1” */

The TE instruction is valid for TSO only. It turns interactive debugging off, and may be
run only after an attention interrupt.

The TS instruction turns interactive debugging on. In TSO it may only be executed after
an ATTN interrupt. In CMS, it may be executed only when the screen displays MORE …
in the lower right.

The UPPER instruction converts the alphabetic contents of a variable to upper case.

Example:

VAR1 = “this, 2, is lower case.”


UPPER VAR1
SAY VAR1 /* produces THIS, 2, IS LOWER CASE. */
The TRACE instruction controls tracing and interactive debugging. The TRACE
instruction has the following operands, listed in order from the least things traced to the
most things traced.

TRACE ! Nothing traced, don’t execute any commands.

TRACE O Nothing traced.

TRACE N Normal, TSO/CMS commands failing or erroring out


are traced.

TRACE F Failure, non-existent or abending TSO/CMS commands

TRACE E Error, non-working TSO/CMS commands

TRACE !C Trace commands but don’t execute them

TRACE C Commands, TSO/CMS commands

TRACE L Labels only

TRACE A All (labels, commands, REXX verbs)

TRACE S Scan labels, commands, and REXX verbs

TRACE !R Results for labels, commands, REXX verbs, or any


time a variable is changed, but don’t execute any
commands

TRACE R Results for labels, commands, REXX verbs, or any


time a variable is changed

TRACE ?R Results for labels, commands, REXX verbs, or any


time a variable is changed with interactive debug

TRACE !I Intermediate, labels, commands, REXX verbs, or any


time a variable is changed, showing intermediate
results (e. g. C = (4+3) * 2), and don’t execute
commands.

TRACE I Intermediate, labels, commands, REXX verbs, or any


time a variable is changed, showing intermediate
results (e. g. C = (4+3) * 2)
REXX special variables

REXX has three reserved variables it uses for many situations. You may display,
check, or assign from them, but you should not change their contents.

The RC variable contains the return code set by an environment command. If the
command works, RC usually contains 0. There are exceptions; for example, see
MAKEBUF, QELEM, and QBUF. It also contains the number assigned to REXX syntax
errors. It is not set by REXX instructions, e.g. IF. Examples:

SIGNAL ON SYNTAX
“MAKEBUF”
SAY RC /* returns “1” or greater, buffer number */
“DELETE A.B.C”
IF RC = 0
THEN SAY “A.B.C DELETED”
ELSE SAY “DELETE FAILED”
SAY “A” – “B”
EXIT

SYNTAX:
SAY RC “IS THE REXX ERROR NUMBER.”
SAY ERRORTEXT(RC) “IS THE ERROR MESSAGE.”
SAY SIGL “IS THE LINE NUMBER IN ERROR.”
EXIT

The RESULT variable contains data passed back from a function or subroutine invoked
with the CALL instruction. It contains any data passed back on the RETURN statement.
Here is an example:

SAY RESULT /* displays RESULT, nothing returned yet */


CALL LENGTH“ABCD”
SAY RESULT /* displays 4 */
CALL ADDEMUP 1,2
SAY RESULT /* displays 3 */
Exit
ADDEMUP:
ARG NUM1,NUM2
RETURN NUM1 + NUM2

The SIGL variable contains the line number of the REXX statement causing a transfer of
control to a subroutine or condition trap. This is useful for debugging; see the example
code under RC.
REXX functions

The REXX functions listed here are supported under both TSO and CMS unless
otherwise noted. Functions may be used in one of two ways:

 Letting REXX substitute the value for the function. For example,

SAY LENGTH(“ABCD”) becomes SAY 4, which displays “4” on the terminal.

SAVE_LEN = LENGTH(“ABCD”) /* assignment */

SAY “NAME HAS” LENGTH(NAME) “LETTERS.”

SAY “DOUBLE THE NAME’S LENGTH IS” 2 * LENGTH(NAME)

NOTES:

o There must not be a space between the function name and the parentheses.
o The result of the function is not available in the variable RESULT.
o Separate parameters with commas, not spaces. For example,
LEFT(“ABCD”,3).

 Using the special variable RESULT to get the value for the function. For
example,

CALL LENGTH “ABCD”


SAY RESULT /* displays “4” */

CALL LENGTH “ABCD”


SAVE_LEN = RESULT /* assignment */

CALL LENGTH NAME


SAY “NAME CONTAINS” RESULT “CHARACTERS.”

CALL LENGTH NAME


SAY “DOUBLE THE NAME’S LENGTH IS” 2 * RESULT

NOTES:

o You must do a CALL for the answer to be in RESULT.


o Separate parameters with commas, not spaces. For example, LEFT
“ABCD”,3.
o Parentheses are not used.
ABBREV(word,abbrev,length). Tells whether abbrev is a valid abbreviation of word,
considering length characters. If the length is omitted, all characters are checked. The
function returns a “1” if it is a valid abbreviation, “0” if it is not.

Examples:

SAY ABBREV(“ALLOCATE”,“AL”,2) /* returns 1 */


SAY ABBREV(“ALLOCATE”,“AL”) /* returns 1 */
SAY ABBREV(“ALLOCATE”,“ALK”,2) /* returns 0 */
SAY ABBREV(“ALLOCATE”,“ALK”) /* returns 0 */

ABS(number). Returns the absolute value (i.e. drops the sign) of a number. Formats the
result using the current NUMERIC setting. For example:

SAY ABS(-12.3) /* displays “12.3” */

ADDRESS(). Returns the name of the current environment as follows:

TSO: returns TSO, MVS, ISPEXEC, or ISREDIT


CMS: returns CMS, COMMAND, XEDIT

Example:

SAY ADDRESS() /* might display “TSO” */


ARG(number,operand). The ARG function has four forms, detailed on this page.

ARG() returns the number of argument strings passed (commas delimit argument
strings). You can pass several to a subroutine or function, but only one to a main
program. Example:

CALL SUBR “CINDY”,“DEB” “LORI”


EXIT
SUBR:
SAY ARG() /* displays 2 */

ARG(number) returns the argument string of the number passed. Example:

CALL SUBR “CINDY”,“DEB” “LORI”


EXIT
SUBR:
SAY ARG(2) /* displays DEB LORI */

ARG(number,”EXISTS”) returns a “1” if the argument numbered number exists, a


“0” if it does not. EXISTS may be abbreviated “E”.

CALL SUBR “CINDY”,“DEB” “LORI”


EXIT
SUBR:
SAY ARG(2,“EXISTS”) /* displays “1” */
SAY ARG(5,“E”) /* displays “0” */

ARG(number,”OMITTED”) returns a “1” if the argument numbered number does


not exist, a “0” if it does. OMITTED may be abbreviated “O”.

CALL SUBR “CINDY”,“DEB” “LORI”


EXIT
SUBR:
SAY ARG(2,“OMITTED”) /* displays “0” (is there) */
SAY ARG(5,“O”) /* displays “1” (not there */
BITAND(string1,string2,pad). BITAND logically ANDs string1 and string2, with pad
used to fill the shorter string to the right. Each bit in string1 is compared to the
corresponding bit in string2. Each bit in the result is set to 1 if the matching bits in both
string1 and string2 are a 1. If both bits don’t match, the corresponding bit in the result is
set to 0. Example:

SAY BITAND(‘01’X,‘0F’X) /* displays ‘01’X */

BITOR(string1,string2,pad). BITOR logically ORs string1 and string2, with pad used
to fill the shorter string to the right. Each bit in string1 is compared to the corresponding
bit in string2. Each bit in the result is set to 1 if the matching bit in either string is a 1. If
neither bit in the strings are 1, the corresponding bit in the result is set to 0. Examples:

SAY BITOR(‘01’X,‘0F’X) /* displays ‘0F’X */


SAY BITOR(‘01’X,‘0E’X) /* displays ‘0F’X */

BITXOR(string1,string2,pad). BITXOR logically ORs string1 and string2, with pad


used to fill the shorter string to the right. Each bit in string1 is compared to the
corresponding bit in string2. Each bit in the result is set to 1 if only one, not both, of the
matching bits in both string1 and string2 are a 1. If both bits aren’t 1, or both bits are 1,
the corresponding bit in the result is set to 0. Examples:

SAY BITOR(‘01’X,‘0F’X) /* displays ‘0E’X */


SAY BITOR(‘01’X,‘0E’X) /* displays ‘0F’X */

CENTER(string,length,pad). Centers string within a larger string of length. Pad, if


present, is used to pad the string to the left and right; the default is spaces. Examples:

CENTER(“CENTER”,10) /* returns ‘ CENTER ’ */


CENTER(“CENTER”,10,“-”) /* returns ‘--CENTER--’ */

COMPARE(string1,string2,pad). Compares string1 to string2. If pad is supplied, it is


used to fill the shorter string; the default is spaces. If both strings are equal COMPARE
returns a 0. If the strings are not equal, the character position of the discompare is
returned. Examples:

COMPARE(“APPLES”,“APPLES”) /* returns 0 */
COMPARE(“APPLES”,“APPLESAUCE”) /* returns 1 */
CONDITION(type). Normally used in a condition trap, this function gives information
about the condition found. Valid types are:

 C – gives the name of the condition (ERROR, FAILURE, NOVALUE, HALT,


SYNTAX).
 D – when possible, gives the string causing the error.
 I – gives the instruction sending to the trap (SIGNAL or CALL).
 S – gives the current status of the condition (ON, OFF, or DELAY). DELAY
means the condition is currently trapped, and further trapping is disabled to
prevent recursive calling of error trap.

Examples:

SIGNAL ON ERROR
“LISTCAT NOHLQ”
EXIT

ERROR:
SAY “COMMAND FAILED!”
SAY CONDITION(“C”) /* displays “ERROR” */
SAY CONDITION(“D”) /* displays “LISTCAT NOHLQ” */
SAY CONDITION(“I”) /* displays “SIGNAL” */
EXIT

COPIES(string,how-many). Returns how-many copies of string, one after another.


Examples:

COPIES(“MORE”,3) /* returns MOREMOREMORE */


COPIES(“LESS”,0) /* returns nothing (no copies) */
Copies(“ ”,80) /* returns a line of 80 spaces */

C2D(string). Converts string to its binary form, then to a decimal number. Examples:

SAY C2D(“B”) /* displays 194 */


SAY C2D(“b”) /* displays 130 */
SAY C2D(“10”X) /* displays 16 */

C2X(string). Converts string to its hexadecimal form. Examples:

SAY C2X(“B”) /* displays C2 */


SAY C2X(“b”) /* displays 82 */
SAY C2X(“16”) /* displays F1F6 */
SAY C2X(16) /* displays F1F6 */
DATATYPE(string)

Returns NUM if string is a number, otherwise it returns CHAR.

SAY DATATYPE(1234) /* returns NUM */


SAY DATATYPE(Z234) /* returns CHAR */

DATATYPE(string,type)

Returns 1 if string has the same type as type, otherwise it returns 0. Note you may
need to strip extra spaces to get this to work properly; for example,

SAY DATATYPE(SPACE(“ABCD”,0),“U”) /* returns 1 */

Valid types:

A – alphanumeric (A – Z, a – z, 0 – 9)
B – binary digits 1 and 0
D – double-byte characters
L – lower-case characters
M – mixed-case characters
N – valid number
S – valid REXX symbol
U – upper-case letters
W – whole number
X – hexadecimal number (0 – 9, A – F)

Examples:

SAY DATATYPE(“1234”,“N”) /* returns 1 */


SAY DATATYPE(“Z234”,“N”) /* returns 0 */
SAY DATATYPE(“AbcD”,“M”) /* returns 1 */
SAY DATATYPE(“Z234”,“A”) /* returns 1 */
SAY DATATYPE(“ABCD”,“X”) /* returns 1 */
IF DATATYPE(VAR1,”N”)
THEN SAY “VAR1 IS A NUMBER.”
ELSE SAY “VAR1 IS NOT A NUMBER.”
DATE(). Returns the current date in the format 12 JULY 2002.

DATE(type). Returns the current date in the format indicated by type.

Valid types:

B – base date, number of days since January 1, year 1.


C – century, the number of days in this century.
D – days, the number of days so far this year.
E – Eurpean date, format dd/mm/yyyy.
J – Julian date, format yyyyddd.
M – the name of the current month.
O – ordered date, sortable, format yyyy/mm/dd.
S – sortable date, yyyymmdd.
U – USA format, mm/dd/yyyy.
W – the name of the current weekday.

Examples (for June 17, 2008):

SAY DATE(“B”) /* returns 733209 */


SAY DATE(“C”) /* returns 3091 */
SAY DATE(“D”) /* returns 169 */
SAY DATE(“E”) /* returns 06/17/2008 */
SAY DATE(“J”) /* returns 2008169 */
SAY DATE(“M”) /* returns June */
SAY DATE(“O”) /* returns 2008/06/17 */
SAY DATE(“S”) /* returns 20080617 */
SAY DATE(“U”) /* returns 06/17/2008 */
SAY DATE(“W”) /* returns Tuesday */

DELSTR(string,start,length). Returns the remains of a string when the characters from


start for length characters are removed.

SAY DELSTR(“GOLDENEYES”,5,2) /* returns GOLDEYES. */

DELWORD(string,start-word,how-many-words). Returns the remains of a string when


words (groups of characters surrounded by spaces) are removed from the original string.
The words deleted start at the start-word word for how-many-words characters are
removed.

SAY DELWORD(“HI HOW ARE YOU”,2,2) /* returns HI YOU */


DIAGRC(rc,command) (CMS only). Returns an explanation of the return code from
the command sent to CMS. A return code from the command processor is included in the
first eleven bytes of the reply.

SAY DIAGRC(8,”QUERY READER”)


returns 0 0 NO RDR FILES

DIGITS(). Returns the current setting of NUMERIC DIGITS.

NUMERIC DIGITS 7
SAY DIGITS() /* returns 7 */

D2C(number). Converts the decimal number to a hex number, and then returns the
character for that hexadecimal code.

SAY D2C(194) /* returns B */


SAY D2C(240) /* returns 0 */

D2X(number). Converts the decimal number into its hexadecimal equivalent.

SAY D2X(240) /* returns F0 */

ERRORTEXT(number). Returns the REXX syntax message for the passed error
number.

SAY ERRORTEXT(16) /* returns LABEL NOT FOUND */

EXTERNALS(). Returns the number of elements in the terminal input buffer (how
many lines are there). In VM/CMS, this number can be greater than zero; in TSO,
because there is no type-ahead buffer REXX returns zero..

SAY EXTERNALS() /* returns 0, usually */

FIND(string,findstring). Returns the word number of the first word of findstring in


string.

SAY FIND(“To be or not to be”,“not to be”)

Returns 4 as the NOT is the fourth word in the string.


FORM(). Returns the current setting of NUMERIC FORM.

NUMERIC FORM SCIENTIFIC


SAY FORM() /* returns ‘SCIENTIFIC’ */

FORMAT(number,before decimal,after decimal). Allows the formatting of a number.


The “before” number is the number of digits, blank-padded in front, appearing before the
decimal point. The “after” number is the number of digits, padded with zeroes, appearing
after the decimal point.

LongNum = 1 / 3
ShortNum = 1 / 2
SAY FORMAT(LongNum,3,2) /* returns ‘ 1.33’ */
SAY FORMAT(ShortNum,2,4) /* returns ‘ 0.5000’ */

FUZZ(). Returns the current setting of NUMERIC FUZZ.

NUMERIC FUZZ 2
SAY FUZZ() /* returns ‘2’ */

INDEX(string,findstring). Returns the position of the start of findstring within string.

A = “A rose by any other name would smell as sweet”


Say INDEX(A,“smell”) /* returns ‘32’ */

INSERT(string1,string2,position). Inserts string1 into string2 at the indicated position.

Say INSERT(“E”,“ABCDF”,4) /* returns ‘ABCDEF’ */

JUSTIFY(string,length,pad). Creates a new string using string of length characters.


Justifies to both margins by adding blanks between words. If pad is used, that character
will be the fill character instead of blanks, which is the default.

Say JUSTIFY(“Hello Kitty”,20)


Returns ‘Hello Kitty’

Say JUSTIFY(“Hello My Baby”,20,”?”)


Returns ‘Hello?????My????Baby’
LASTPOS(string1,string2). Returns the position of the last occurrence of string2 within
string1. Returns a zero if string2 is not found within string1.

A = “Left, Left, Left Right Left”


Say LASTPOS(A,“Left”) /* returns ‘24’ */
Say LASTPOS(A,“Center”) /* returns ‘0’ */

LEFT(string,length,pad). Creates a new string using the leftmost length characters of


string. By default, the string is padded to the right with spaces if string is shorter than
length. If pad is supplied it will be the pad character.

Say LEFT(“Hello Kitty”,2) /* returns ‘He’ */


Say LEFT(“Hello”,10,”?”) /* returns ‘Hello?????’ */

LENGTH(string). Returns the length of string in bytes.

Say LENGTH(“Hello Kitty”) /* returns ‘11’ */

LINESIZE(). Returns the length of the terminal line width, minus 1.

Say LINESIZE() /* returns ‘79’ on TSO, ‘80’ on CMS */

LISTDSI (TSO only). Returns information about the dataset in REXX variables. See
the following pages for a list of those variables and for the return codes set by LISTDSI.
It can be used in two ways.

RetCode = LISTDSI(dsn)

or

CALL LISTDSI dsn


RetCode = RESULT

There is an optional argument, DIRECTORY, which tells LISTDSI to provide


information regarding the directory of a PDS (but not of a PDS-E). The syntax is
RetCode = LISTDSI(dsn) DIRECTORY.

RetCode = LISTDSI(dsn) /* sets variables */


Say SYSVOLUME /* displays the VOLSER */
Say SYSRECFM /* displays the record format */
Say SYSLRECL /* displays the record length */
Variables set by LISTDSI are listed here. Note all dates are returned as Julian
dates using a slash (/). For example, 2006/107 is April 17, 2006.

SYSDSNAME Dataset name


SYSVOLUME VOLSER
SYSUNIT Device type of VOLSER
SYSDSORG Dataset organization
DA = Direct Access (BDAM)
PO = PDS or PDS-E
PS = sequential
VS = VSAM
SYSRECFM Record format
F = Fixed length
V = Variable length
A = contains ASA characters
B = Blocked
SYSLRECL Logical record length
SYSBLKSIZE Blocksize (VS is VSAM)
SYSKEYLEN Key length or zero
SYSALLOC Total space allocated
SYSUSED Total space used
SYSPRIMARY Primary space allocation
SYSSECONDS Secondary space allocation
SYSUNITS Type of space allocation
Cylinders
Tracks
Blocks
SYSEXTENTS Total extents used
SYSCREATE Creation date
SYSREFDATE Last reference date
SYSEXDATE Expiration date or “0”
SYSPASSWORD Password protection
NONE = no password needed
READ = read password needed
WRITE = write password needed
SYSRACFA RACF protection status
NONE = no protection
GENERIC = generic profile
DISCRETE = discrete profile
SYSUPDATED YES or NO
SYSTRKCYL # of tracks / cylinder on VOLSER
SYSBLKSTRK # of blocks / track on VOLSER
SYSADIRBLK # of directory blocks on a PDS
SYSUDIRBLK # of used directory blocks
SYSMEMBERS # of members in the PDS
SYSREASON reason code for command failure
LISTDSI return codes are listed here. The first-level system error message is
in the variable SYSMSGLVL1; the second-level system error message is in the
variable SYSMSGLVL2.

0) Success!
1) REXX had a problem analyzing the statement
2) Dynamic allocation failure
3) Cannot process this type of dataset
4) Error occurred obtaining unit name
5) Dataset is not catalogued
6) Error occurred getting dataset name
7) Error occurred getting unit type
8) Dataset is not on disk
9) Dataset is migrated and cannot be recalled
10) -
11) Unauthorized to obtain directory information
12) VSAM dataset
13) Open error
14) Device type not found in MVS
15) –
16) Command did not work
17) Some sort of abend
18) Incomplete information obtained
19) Multi-volume dataset
20) Unknown device type
21) Catalog error
22) Volume not mounted
23) I/O error attempting to get dataset information
24) Dataset not on catalogued volume
25) Dataset is migrated and is unavailable
26) Dataset is on a mass storage device
27) Cannot find VOLSER for this dataset
28) DD name specified is not valid
29) Neither dataset name nor DD name was provided

MAX(number1,number2,…number20). Returns the maximum number in a string of


numbers. Note the numbers must be provided; variable substitution is not allowed.

Say MAX(5,4,3,2,4,8,2) /* returns ‘8’ */

MIN(number1,number2,…number20). Returns the maximum number in a string of


numbers. Note the numbers must be provided; variable substitution is not allowed.

Say MIN(5,4,3,2,4,8,2) /* returns ‘2’ */


MSG(), TSO only. Returns the MSG status of your TSO session. Alternatively, sets the
MSG status of your session. When MSG is on, TSO command messages are displayed;
when MSG is off, those messages are suppressed.

TM = MSG(“OFF”) /* turn MSG off */


OM = MSG(“ON”) /* saves old status and sets it ON */
Say MSG() /* returns ‘ON’ */
TM = MSG(OM) /* sets status to whatever is in OM */
Say MSG() /* returns ‘OFF’ (see Line 1 and 2) */

MVSVAR(system-variable), TSO only. Retrieves information about the MVS system,


one variable at a time. This is the list of system variables you can display.

 SYSAPPCLU: the APPC/MVS logical unit (LU) name.


 SYSDFP : the level of MVS/Data Facility Product (MVS/DFP).
 SYSMVS : the level of the base control program (BCP) component of z/OS.
 SYSNAME : the name of the system your REXX exec is running on, as
specified in the SYSNAME statement in SYS1.PARMLIB member
IEASYSxx.
 SYSOPSYS : the z/OS name, version, release, modification level, and FMID.
 SYSSECLAB: the security label (SECLABEL) name of the TSO/E session.
 SYSSMFID : identification of the system on which System Management
Facilities (SMF) is active.
 SYSSMS : indicator whether DFSMS/MVS is available to your REXX exec.
 SYSCLONE : MVS system symbol representing its system name.
 SYSPLEX : the MVS sysplex name as found in the COUPLExx or LOADxx
member of SYS1.PARMLIB.
 SYMDEF : symbolic variables of your MVS system.

When using SYMDEF, you must supply both the word SYMDEF and the symbolic
variable name. Variables must be defined in the SYS1.PARMLIB member IEASYMxx.
OUTTRAP, TSO only. Captures the output from most TSO commands into a stem
variable you specify. The first entry of the stem variable, VAR.0, contains the number of
lines of output. You can also specify the number of lines to trap, from none (suppress all
messages) to all lines.

NOTE: some programs, such as the CONCAT command, produce output that cannot
be captured by OUTTRAP.

OUTTRAP syntax is one of the following.

TMP = OUTTRAP(LCDATA.) /* captures output lines from */


“LISTC” /* the LISTC in the stem variable LCDATA. */

CALL OUTTRAP “LINES.” /* same as the first example */


“LISTC”

TMP = OUTTRAP(LCDATA.,10) /* capture only the first 10 */


“LISTC” /* lines of the LISTC output in LCDATA. */

TMP = OUTTRAP(LCDATA.,0) /* do not capture or display */


“LISTC” /* any lines of the LISTC output. */

TMP = OUTTRAP(“OFF”) /* turns off output trapping */

Here is an example of reading the lines of output, with certain assumptions.

TMP = OUTTRAP(LCDATA.)
“LISTC ENT(MY.JCL.PDS)”
TMP = OUTTRAP(“OFF”)
SAY “There are” LCDATA.0 “lines of LISTC output.”
Say LCDATA.1 /* produces ‘0NONVSAM MYID.MY.JCL.PDS’ */
Say LCDATA.2 /* produces ‘ IN-CAT LOCAL.CATALOG’ */
Do I = 1 to LCDATA.0
Say LCDATA.I
End /* DO group displays all lines from the LISTC */

NewString = OVERLAY(string1,string2,start-position). Replaces characters in string2


with string1, starting with the character position at start-position. This command is very
useful when formatting output lines.

Say OVERLAY(“.”,“2007/102”,5) /* displays 2007.102 */


Say OVERLAY(“DO”,“CAT”,1) /* displays DOT */
VAR1 = Overlay(“.”,VAR1,10)
POS(string1,string2,start-position). Returns the position of string1 in string2, starting
with the character position at start-position. It returns a zero if the string is not found.

Say POS(“BUT”,“DOG AND BUTTERFLY”,1) /* displays 9 */


Say POS(“BUT”,“DOG AND BUTTERFLY”,10) /* displays 0 */

PROMPT(), TSO only. Returns the PROMPT status of your TSO session.
Alternatively, sets the PROMPT status of your session. When PROMPT is on, TSO
commands prompt the user for input if it is missing. When PROMPT is off, those
messages are suppressed.

TP = PROMPT(“OFF”) /* turn MSG off */


OP = PROMPT(“ON”) /* save old status, set it ON */
Say PROMPT() /* returns ‘ON’ */
TP = MSG(OP) /* set status to whatever is in OP */
Say MSG() /* returns ‘OFF’ (see Line 1 & 2 */

QUEUED(). Returns the number of lines in the stack.

Say QUEUED() /* assuming none, displays ‘0’ */


PUSH “New Line” /* put a new line in the stack. */
Say QUEUED() /* displays ‘1’ */

RANDOM(min,max,seed). Returns a random number between min and max. Seed is an


optional parameter to change the derivation of random numbers from the default. See the
examples below. Note the same number is produced by the last two statements because
seed is supplied.

Say RANDOM(1,10) /* returns ‘5’ */


Say RANDOM(1,10) /* returns ‘8’ */
Say RANDOM(1,10,120345) /* returns ‘2’ */
Say RANDOM(1,10,120345) /* returns ‘2’ */

REVERSE(string). Reverses the character order in string.

Say REVERSE(DOG) /* displays ‘GOD’ */


RIGHT(string,length,pad). Creates a new string using the rightmost length characters of
string. By default, the string is padded to the left with spaces if string is shorter than
length. If pad is supplied it will be the pad character.

Say RIGHT(“Hello Kitty”,2) /* returns ‘ty’ */


Say RIGHT(“Hello Kitty”,5,”?”) /* returns ‘???ty’ */

SIGN(number). Returns:

 1 if the number is positive.


 0 if the number is zero.
 -1 if the number is negative.

Say SIGN(-9) /* returns ‘-1’ */

SOURCELINE(linenumber). Returns the original program statement with line number


linenumber.

/* REXX PROGRAM FOR SOURCELINE */


ARG VAR1
Say SOURCELINE(2) /* returns ‘ARG VAR1’ */

SPACE(string,how-many,pad). Inserts how-many spaces between words in string. If


how-many is zero, all spaces are stripped. Pad is optional; if specified, it is inserted
instead of spaces.

Say SPACE(“THE FINAL FRONTIER”,3)


Displays ‘THE FINAL FRONTIER’

Say SPACE(“THE FINAL FRONTIER”,3,“!”)


Displays ‘THE!!!FINAL!!!FRONTIER’

Say SPACE(“THE FINAL FRONTIER”,0)


Displays ‘THEFINALFRONTIER’

STORAGE(address,length,new-data). Displays the main storage of length bytes at


address. New-data is optional; if specified, STORAGE inserts it into memory. BE
VERY CAREFUL WITH THIS! It is wise to only display storage unless you know
exactly what you are doing.

Say STORAGE(000000,8)
Displays the first 8 bytes of storage.
NewString = STRIP(string,option,character). Removes character from string based on
option. By default, character is a space. Available options are:

 B removes both trailing and leading characters (the default).


 T removes only trailing characters.
 L removes only leading characters.

Say STRIP(“ ‘LOTS OF BLANKS’ ”)


Displays “‘LOTS OF BLANKS’”

Say STRIP(“ LOTS OF BLANKS ”,“T”)


Displays “‘LOTS OF BLANKS ’”

Say STRIP(‘MYUSERID.MY.CNTL’,,“’”)
Displays “MYUSERID.MY.CNTL”

As you can see from that last example, STRIP is useful for removing leading and
trailing quotes from fully-qualified dataset names.

NewString = SUBSTR(string,start,length,pad). Returns a part of string starting at start


for a length of length bytes. The pad character is used if length is longer than the
resulting substring.

Say SUBSTR(“BOOKS”,1,3) /* Displays “BOO” */


Say SUBSTR(“BOOKS”,5,4,“S”) /* Displays “SSSS” */

NewString = SUBWORD(string,start,how-many-words). Returns a part of string


starting at start that has how-many-words words.

Say SUBWORD(“BOOKS ARE MY FRIENDS”,2,2)


Displays “ARE MY”

SYMBOL(name). Tells you if name is a variable, a literal, or an illegal symbol. Returns

 VAR if name is an assigned variable


 LIT if name is an assigned variable
 BAD if name is an assigned variable

Say SYMBOL(BOOKS) /* Displays ‘LIT’ */


BOOKS = “Blink”
Say SYMBOL(BOOKS) /* Displays ‘VAR’ */
Say SYMBOL(.=) /* Displays ‘BAD’ */
SYSDSN(dataset-name) , TSO only. Tells you if a dataset exists and its current status.
Possible returns include:

OK
MEMBER SPECIFIED BUT DATASET NOT PDS
MEMBER NOT FOUND
DATASET NOT FOUND
ERROR PROCESSING REQUESTED DATASET
PROTECTED DATASET
VOLUME NOT ON SYSTEM
UNAVAILABLE DATASET
INVALID DATASET NAME
MISSING DATASET NAME

A simple example of how SYSDSN might be used.

/* REXX Quick JCL submit */


Arg MEMBER
JCLPDS = “MY.JCL.CNTL(”MEMBER“)”
PDSQ = SYSDSN(JCLPDS)
Select
When PDSQ = “OK” Then “SUBMIT” JCLPDS
When Left(PDSQ,6) = “MEMBER” Then Say PDSQ
Otherwise,
Say “Some problem with” JCLPDS“. Error is” PDSQ“.”
End
SYSVAR(system-variable), TSO only. Retrieves information about the system. This is a
list of the variables you can discover.

 SYSPREF : the prefix assigned to not-fully-qualified dataset names.


 SYSPROC : the logon procedure for the current session.
 SYSUID : the user ID of the person logged on.
 SYSLTERM: the length of the terminal screen. In batch, returns 0.
 SYSWTERM: the width of the terminal screen. In batch, returns 132.
 SYSENV : returns FORE (foreground) or BACK (background).
 SYSICMD : returns the name of the running, implicitly called exec. If the call
was explicit, SYSICMD returns null.
 SYSISPF : returns ACTIVE or NOT ACTIVE.
 SYSNEST : returns YES if the current program was called from another, NO if
not.
 SYSPCMD : the most recently used TSO command processor. The value of
SYSPCMD is EXEC (the EXEC command) or EDIT (the EXEC
subcommand of EDIT).
 SYSSCMD : the most recently used TSO subcommand processor. The value may
be null (if not a subcommand) or EXEC (if it was).
 SYSCPU : the number of CPU seconds used this session.
 SYSSRV : the number of service units used this session.
 SYSHSM : the status of DFHSM. Returns AVAILABLE or null.
 SYSJES : the name and level of JES.
 SYSLRACF: the level of RACF. If RACF is not installed, this is null.
 SYSRACF : returns AVAILABLE, NOT AVAILABLE, or NOT INSTALLED.
 SYSNODE : either the JES node name, “-INACTIVE-”, or “-DOWNLEVEL-” if
the subsystem is not at least either JES2 SP4.3 or JES3 SP5.1.1.
 SYSTERMID: the terminal ID, or null if batch.
 SYSTSOE : the level of TSO.
 SYSDTERM: If double-byte character set (DBCS) is enabled, returns YES.
 SYSKTERM: if Katakana character set is enabled, returns YES.
 SYSPLANG: returns the 3-byte primary language setting.
 SYSSLANG: returns the 3-byte secondary language settings.
 SOLDISP : show solicited messages (operator replies). Value is YES or NO.
 UNSDISP : show unsolicited messages (operator replies). Value is YES or NO.
 SOLNUM and UNSNUM: the size of the TSO message table.
 MFTIME : show a time stamp with each message. YES or NO.
 MFOSNM : show originating system name with each message. YES or NO.
 MFJOB : display the originating job name. YES or NO.
 MFSNMJBX: did the user request NOT to show the originating job and system
names of a message. YES means DO NOT show the names; NO means to show
them.
System Variables: SYSVAR notes

1. SYSVAR can only be used in TSO/E environments (i.e. not VM, Linux, etc.).

2. SYSPROC has three different responses, depending on where it was called:


 Foreground: returns the logon procedure name.
 Batch: returns INIT because the job has an initiator.
 Started Task: returns the name of the started task.

3. SYSPCMD and SYSSCMD are connected. For example, if you’re running the TEST
TSO command (debugging a program), SYSSCMD might return EQUATE (the last
TEST command run) while SYSPCMD would return TEST.

4. MFSNMJBX is meant to override MFJOB and MFOSNM. It should not be


consistent with them.

5. Some CLIST control variables do not apply to REXX. REXX has “replacements” for
these variables.
 SYSDATE ===> DATE(“U”)
 SYSJDATE ===> DATE(“J”)
 SYSSDATE ===> DATE(“O”)
 SYSSTIME ===> SUBSTR(TIME(),1,5)
 SYSTIME ===> TIME()

Examples:

 Am I running in the foreground?


If SYSVAR(SYSENV) = “FORE”

 Am I in ISPF?
If SYSVAR(SYSISPF) = “NOT ACTIVE” Then Exit

 Who is using the command?


SYSVAR(SYSUID)

 List your JES node name.


SYSVAR(SYSNODE)

 How large is my display screen?


TL = Strip(SysVar(SYSLTERM))
TW = Strip(SysVar(SYSWTERM))
"Your LTERM displays" TL "lines of" TW "bytes each."
TIME(option). Returns the current time in a specific format based on the option below.

 TIME() : hh:mm:ss
 TIME(“E”): first time, starts the elapsed time counter
 TIME(“E”): second time, displays the number of seconds since the first
“E”.
 TIME(“H”): hours since midnight
 TIME(“M”): minutes since midnight
 TIME(“R”): Resets elapsed time to zero.
 TIME(“S”): Seconds since midnight

TRACE(). Returns the current TRACE setting.

TR = TRACE(“O”) /* save setting and turn TRACE off */


DO FOREVER /* large loop */
<instructions>
END
TR = TRACE(TR) /* set trace back to what it was */
SAY TRACE() /* displays current trace setting */

NewString = TRANSLATE(string,output table,input table). Converts any occurrence


of character 1 in the input table to character 1 of the output character, and so on.

Example:

SAY TRANSLATE(“ABCDEF”,“ZYXWV”,“ABCDE”)
displays ZYXWVF

NewNum = TRUNC(number,decimal places). Returns the number with the given


number of decimal places. Fills with spaces or zeroes as needed.

Examples:

SAY TRUNC(12.654,2) /* displays 12.65 */


SAY TRUNC(12.654,4) /* displays 12.6540 */
SAY TRUNC(12.654,0) /* displays 12 */
USERID(). Returns the ID you are logged on with.

SAY USERID /* displays ISDODEA */

VALUE(). Returns the contents of a symbol after resolving it as a variable.

PROGLANG = “COBOL”
COBOL = “English-like”
Say VALUE(“PROGLANG”) /* displays ‘COBOL’ */
Say VALUE(PROGLANG) /* displays ‘English-like’ */

VERIFY(string1,string2). Makes sure string1 is made up of the characters in string2.


For example, this is a good way of verifying an arithmetic expression.

IF VERIFY(input,'1234567890E+-*/% ().') = 0
THEN /* ‘input’ is a valid arithmetic expression */
ELSE SAY input “has invalid arithmetic characters.”

WORD(string,n). Returns the “n”th word in string.

Say WORD(“BOOKS ARE FUN”,3) /* Displays “FUN” */

WORDINDEX(string,n). Returns the character position of the “n”th word in string.

Say WORDINDEX(“BOOKS ARE FUN”,2) /* Displays “7” */

WORDLENGTH(string,n). Returns the length of the “n”th word in string.

Say WORDLENGTH(“BOOKS ARE FUN”,2) /* Displays “3” */

WORDPOS(string1,string2,starting-word). Searches for string1 in string2, starting


with starting-word and returns the number of words before string2.

Say WORDPOS(“VERY”,“BOOKS ARE VERY FUN”)


Displays “2”
Say WORDPOS(“ARE”,“BOOKS ARE VERY FUN”,3)
Displays “0”
WORDS(string). Returns the number of words in string.

Say WORDS(“BOOKS ARE VERY FUN”) /* displays “4” */


LINE = “HOW ARE YOU?”
Say WORDS(LINE) /* displays “3” */

XRANGE(start,end). Returns the hexadecimal characters between start and end,


inclusive.

Examples:

Say XRANGE(“A”,“F”) /* displays “ABCDEF” */


Say XRANGE(“F1”X,“F5”X) /* displays “12345” */
IF XRANGE(“00”X,“09”X) = “00010203040506070809”X
The IF statement would evaluate as TRUE.

X2C(hex-string). Returns the characters of the hex string.

Say X2C(“F1”) /* displays “1” */


Say X2C(“F1F1F8”) /* displays “118” */

X2D(hex-string). Returns the decimal equivalent of the hex string.

Say X2D(“1F”) /* displays “17” */


Say X2C(“11C”) /* displays “284” */

You might also like