SlideShare uma empresa Scribd logo
1 de 34
REXX Programming
   For TSO:

Advanced Concepts




                           Dan O’Dea
                    September 2, 2004



                                    1
Table of Contents
Introduction . . . . . . . . . . . . . . . . .                                  3
  Goals of the course. . . . . . . . . . . . .                                  3

More on syntax                                                                  4
  Comments . . . . . . . . . . . . . . . . . .                                  4
  Handling Hexadecimals. . . . . . . . . . . .                                  4

More on If/Then                                                                 5
  Using words to indicate success or failure .                                  5
  Boolean operators. . . . . . . . . . . . . .                                  7

Passing Parameters                                                              8
  Using keyword parameters . . . . . . . . . .                                  8

Parsing                                                                         9
  Using   columns.   .   .   .   .   .   .   .   .   .   .   .   .   .   .   . 9
  Using   literals   .   .   .   .   .   .   .   .   .   .   .   .   .   .   . 10
  PARSE   VALUE. .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   . 10
  PARSE   SOURCE .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   . 10

Trace Output Line Codes                                                        12

OutTrap Restrictions                                                           12

Arithmetic and verifying expressions                                           13

Addressing Environments                                                        14

Stem Variables                                                                 15

System Information                             16
  SYSVAR . . . . . . . . . . . . . . . . . . . 16
  MVSVAR . . . . . . . . . . . . . . . . . . . 18

Other Performance Concerns                                                     19

Putting it all together:              real-world examples                      20


Appendix A, Page 26:          a few simple edit macros
Appendix B, Page 28:          solutions to exercises




                                                                                    2
Introduction

   The first part of this course provided you with basic learning needed to code REXX
execs. Some topics were glossed over, or skipped entirely. REXX performance was not
discussed. Finally, some REXX features can be used to make your program more
readable, such as word tests (something like the COBOL 88 level variable).

   This document corrects those “oversights.” While the first document often used
simplified examples, this document uses real-world examples wherever possible.


                              Goals of the Course

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

   •   Do arithmetic on hexadecimal values.

   •   Use nouns in IF statements rather than comparisons.

   •   Handle both positional and keyword parameters.

   •   Parse anything into anything.

   •   Understand trace output.

   •   Verify arithmetic expressions.

   •   Use multi-dimensional stem variables (matrixes).

   •   Code more efficient REXX execs.




                                                                                        3
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. When using
          like this, use one pair of delimiters.    */

   Longer comments are more efficient because the REXX interpreter opens comment
processing each time it encounters a /*. By coding only one set around multiple
comment lines, the interpreter does less work. This is especially important in loops.

   Make a box comment above a loop to explain everything the loop does. If the
comments are in the loop, they are scanned each pass through the loop. To avoid
unnecessary loop processing, don’t comment within the loop.


                                Hexadecimal Values

   You may recall that hexadecimal numbers may be used. Here is a piece of code
showing how interpreting hex characters is used to determine what LPAR you’re running
on. How it works:

   First, convert the 4 bytes of system storage area at location x’10’ to hex. Convert that
to decimal for math, then add the offset to the storage location of the system name (in this
case, hex 154 / decimal 340). Convert the result back to hex. Then get the 8 bytes of
storage at the resulting address and strip the spaces from it.

/*------------*
 * What LPAR? *
 *------------*/
CVT = C2X(STORAGE(10,4))
CVT_154 = D2X(X2D(CVT) + X2D(154))
SID = STRIP(STORAGE(CVT_154,8))




                                                                                          4
Conditional Operators

   As we know, the IF statement follows a standard format:

   If expression
      Then one instruction
      Else one instruction

DO WHILE and DO UNTIL statements are considered IF statements.

    Internally, any IF statement operates by testing the outcome of a binary choice (yes or
no). We’ve seen the results of IF comparisons can be displayed as “1” (true) or “0”
(false). We can use this feature to our advantage.

    In REXX, almost any statement can contain a variable to be resolved. Consider the
following:

Dan = 0
Say “Enter name”
Pull Name
If Name = “Dan” Then Dan = 1
If Dan
  Then Say “Hi, Mr. O’Dea!”
  Else Say “Hi,” Name“.”

   REXX resolves the expression “If Dan” as either “If 0” or “If 1” depending on the
value in the variable Dan. “Dan” is called a noun switch. It’s COBOL equivalent is the
88 level.

   There are two things to keep in mind when using this method:

   1. You must be careful to set the switch to only zero or one. If the IF statement
      finds any other value, your exec terminates with a syntax error.
   2. You cannot negate the switch. To test for a negative condition, it’s a good idea to
      code the statement positively, and use the NOP command to skip the THEN
      condition. For example,

   KSDS = 0
   If <index> Then KSDS = 1
   If KSDS
     Then NOP         /* Do nothing for KSDS */
     Else Say “ESDS” /* Do ESDS stuff */




                                                                                          5
Conditional Operators Noun Switch Example

   Here is part of the code from the stack example above. First, set the switch to 0
(off/false). Then, check if the dataset has an index component. If the index component
exists, turn the switch on. You can then check the switch in the IF.

KSDS = 0
CKIX = INDSN“.INDEX”
If SysDSN(CKIX) = “OK” Then KSDS = 1
  ::
If KSDS
  Then Do
     Queue "       DATA    (NAME("NewDSN".DATA)) -"
     Queue "       INDEX (NAME("NewDSN".INDEX))"
  End
  Else Queue "        DATA    (NAME("NewDSN".DATA))"

   Here’s another piece of code used to scan a LISTC ENT() ALL output for the index
CI size:

If EndOfData
  Then NOP
  Else Do
    Do Until LCA.I = "ATTRIBUTES"; I = I + 1; End
    I = I + 1
    ICIS = Strip(Right(Word(LCA.I,4),6),"L","-")
  End

Notes on the above:

   1. The “Do Until” scans the output from the previous CISIZE field (the one in the
      data component section) until the index attributes section to skip over stuff we
      don’t want. The index “I” must point to the next line after “ATTRIBUTES” to
      get the CISZ.
   2. The last line gets the fourth word of the output line, takes the last 6 characters of
      that word, then removes leading dashes from it to give the CI size of the index.


                                     Exercise #1

   Modify the exec you wrote in Exercise #28 of the first class to use a noun switch
instead of expression tests when deciding what to do with the dataset.



                                                                                              6
Passing Information to the Exec

   The ARG statement parses values passed to the program based on spaces or commas.
ARG is thus a positional operation selector. Here’s one way to code the ARG to enable
you to process passed parameters as keywords.

       /* REXX Keyword Parms, LC accepts up to three
           parms. More than that are ignored. */
       ARG PARM.1 PARM.2 PARM.3 .
       Lall = “”; LGDG = “”
       Do I = 1 to 3
         Parse Var Parm.I Opt 4 .
         Select
            When Opt = “” Then NOP
            When Opt = “ENT” Then Do
              Parse Var Parm.I . “(” LCADSN
              LCADSN = Strip(LCADSN,“T”,“)”)
            End
            When Opt = “ALL” Then LAll = “ALL”
            When Opt = “GDG” Then LGDG = “GDG”
            Otherwise Say Parm.I “is invalid.”
         End
       End
       LCADSN = $FixDSN(LCADSN)
       “LISTC ENT(“LCADSN”)” LAll LGDG

   This is designed to perform a LISTC ENT() on without having to code ALL, GDG, or
other argument in any order. All these calls give the same result:

       LC   ENT(‘ISDODEA.TEST.DATA’) ALL GDG
       LC   GDG ENT(‘ISDODEA.TEST.DATA’) ALL
       LC   ALL GDG ENT(‘ISDODEA.TEST.DATA’)
       LC   ALL ENT(‘ISDODEA.TEST.KSDS’) GDG


                                    Exercise #2

   Modify the exec you wrote in Exercise #16 from the first course to accept the unit and
amount values as keywords. Only accept two arguments. If one argument is bad, display
the syntax and exit.




                                                                                        7
Separating Data: Parsing

   In the introduction we talked about parsing data by word or literal. You can also parse
data in other ways. Some of them are very useful.

  You can parse by column number. It can get complicated because column numbers in
PARSE are not intuitive, but for some things it performs very well.

   The PARSE command is very fast. It is faster than SUBSTR or the LEFT and RIGHT
functions. In loops running many times, PARSE using columns can save you some CPU.

   When specifying column delimiters, the number to the left of the variable name is the
start column, while the number on the right is the end column, minus one. As you can
see in the example, sometimes this can be misleading:

       %SPLITIT ABCDEFGHIJKLMNOP

       PARSE ARG 1 VAR1 5 VAR2 9 VAR3 13

   In this example, the variables are stored as:

•VAR1 = ABCD;
•VAR2 = EFGH;
•VAR3 = IJKL


   When using both column numbers everywhere, it can be worse:

       %SPLITIT ABCDEFGHIJKLMNOP

       PARSE ARG 1 VAR1 5 7 VAR2 9 11 VAR3 13

   In this example, the variables are stored as:

•VAR1 = ABCD;
•VAR2 = GH;
•VAR3 = KL


   Please don’t give up on this just because it looks complicated. You’ll see why below.




                                                                                           8
PARSE using Literals

  When parsing using a literal, you can use any literal. Consider this:

         LINE1 = “ DEFINE CLUSTER(NAME(YOUR.DSN) –”
         PARSE VAR LINE1 JUNK “NAME(” DSN “)” .

After the PARSE the variable DSN contains “YOUR.DSN”. However, you can skip the
LINE1 assignment statement by using PARSE VALUE and the literal itself.



                                  PARSE VALUE

   PARSE VALUE needs the keyword WITH to tell where the template begins. This
can be used with functions as well as variables.

         PARSE VALUE “This is a sample” With A B C D
         PARSE VALUE TIME() With HRS “:” MIN “:” SEC



                                 PARSE SOURCE

  PARSE SOURCE asks TSO for information about the way the exec was run.

         PARSE SOURCE OP_SYSTEM HOW_CALLED EXEC_NAME,
            DD_NAME, DATASET_NAME AS_CALLED,
            DEFAULT_ADDRESS, NAME_OF_ADDRESS_SPACE

where:

         •OP_SYSTEM is TSO
         •HOW_CALLED is either COMMAND, SUBROUTINE, or FUNCTION
         •EXEC_NAME is the name of the exec in upper case
         •DD_NAME the exec was found in SYSEXEC or SYSPROC
         •DATASET_NAME is the DSN the exec was in when called implicitly
         •AS_CALLED is the name the exec was invoked by. May be in lower case
         when called implicitly
         •DEFAULT_ADDRESS is the initial address environment. This is usually TSO,
         MVS, ISPEXEC (i. e. ISPF), or ISREDIT (the ISPF Editor)
         •NAME_OF_ADDRESS_SPACE is one of MVS, TSO, or ISPF.




                                                                                  9
Parsing continued

   Here are several examples of using PARSE rather than a built-in functions or many
assignments.

   Take the third word from the variable INVAR and call it WORD3. The PARSE
instructions are much faster, and if you don’t need the previous two words use the “.”.

   1.   WORD3   = WORD(INVAR,3)
   2.   WORD3   = SUBWORD(INVAR,1,3)
   3.   PARSE   VAR INVAR JUNK1 JUNK2 WORD3 .
   4.   PARSE   VAR INVAR . . WORD3 .


   Take the left five bytes from the variable AMBI and call it LEFTY. LEFT is faster
then SUBSTR. Again, the PARSE instruction are much faster than either.

   1. LEFTY = Left(AMBI,5)
   2. LEFTY = SUBSTR(AMBI,1,5)
   3. PARSE VAR AMBI 1 LEFTY 6 .



   Take the right five bytes from the variable AMBI and call it RT. Note there is an
extra step for the SUBSTR example, and no PARSE example. Parse could be used if you
knew, when you coded, how long AMBI would be. The single function call is faster.

   1. RT = Right(AMBI,5)
   2. ST = Length(AMBI) – 4; RT = SUBSTR(AMBI,ST,5)


   Take the middle five bytes from the variable AMBI, where the middle is known
before coding. The PARSE instruction is faster.

   1. MID = SUBSTR(AMBI,5,5)
   2. PARSE VAR AMBI . 5 MID 10 .


  Set A, B, C, and D to null. The PARSE instruction is faster. You can also use this
method to set all variables to zero.

   1. A = “”; B = “”; C = “”; D = “”
   2. PARSE VALUE “” WITH A B C D
   3. PARSE VALUE “0 0 0 0” WITH A B C D




                                                                                          10
Exercise #3

  Write an exec to parse the date into month, day, and year. Use Parse Value.
Redisplay the date in the following formats:

       MM.DD.YY (USA), DD.MM.YY (European), YY.DD.MM, and
       YY.MM.DD (ordered).

The command to get the USA-style date (MM/DD/YY) is DATE(”U”).




                                                                                11
Trace Output Line Codes

   When running a program in trace mode, the REXX interpreter prefixes each line of
trace output with a three character symbol. These symbols tell you what is happening at
each stage of REXX’s handling of the statement.

   The three-character codes displayed during tracing are:

•*-*     original program line
•+++     trace message
•>>>     result of an expression during TRACE R
•>.>     value assigned to a placeholder (period)
          during parsing
•>C>     resolved name of a compound variable
•>F>     result of a function call
•>L>     a literal
•>O>     result of an operation on two terms
•>P>     result of a prefix operation
•>V>     contents of a variable


                              OutTrap Restrictions

   Not all output from TSO commands can be trapped by REXX. Whether you can trap
output depends on how the command sends its output to the screen. Outtrap cannot
capture lines produced by the following.

•TPUT
•WTO macro
•Messages issued by REXX (IRX*)
•Trace output messages

   An example of a command you cannot capture the output for is CONCAT. Try it.




                                                                                      12
Arithmetic Operations in REXX

    As you know, there are certain characters you can use in any arithmetic statement, and
some you can’t. In the first half of the class we saw the basic arithmetic operators.
Parentheses can be used, as well as spaces to separate terms, periods for decimals, and the
letters A – F for hexadecimal numbers. Here is a clever way to make sure your
expression (EXPR) contains only valid characters:

       If Verify(EXPR,'1234567890ABCDEF+-*/% ()') = 0

   The VERIFY function checks to see if all the characters in the first string are
contained in the second string.



                          An Unusual Type of DO loop

   On a DO from X to Y loop, the FOR clause enforces a specific count:

       Do I = 1.234 To 9943.2323 by .3203 For 22
         Say “I is” I
       End

   The FOR tells REXX to run this loop 22 times no matter what else happens.




                                                                                        13
Talking to the Environment

   For each ADDRESS command, REXX opens a command processor link. To improve
performance, then, it is good practice to use as few ADDRESS commands as possible.
For example, the following code is a subroutine to format and submit a piece of JCL.

Arg JCLSKEL
Address ISPEXEC
DO0JOBNM = $JobName()
"FTOPEN TEMP"
"VGET (ZTEMPF) SHARED"
"FTINCL" JCLSKEL
"FTCLOSE"
ZEDSMSG = DO0JOBNM "SUBMITTED"
ZEDLMSG = "Batch job” DO0JOBNM “submitted.”
"SETMSG MSG(ISRZ000)"
Address TSO "SUBMIT DATASET('"ZTEMPF"')"
Return

   This is the same code, with the ADDRESS commands as part of the code.

Arg JCLSKEL
DO0JOBNM = $JobName()
Address ISPEXEC "FTOPEN TEMP"
Address ISPEXEC "VGET (ZTEMPF) SHARED"
Address ISPEXEC "FTINCL" JCLSKEL
Address ISPEXEC "FTCLOSE"
ZEDSMSG = "JOB SUBMITTED"
ZEDLMSG = "Batch job” DO0JOBNM “was submitted.”
Address ISPEXEC "SETMSG MSG(ISRZ000)"
Address TSO "SUBMIT DATASET('"ZTEMPF"')"
Return


   While both produce the same results, the first one uses less CPU and runs slightly
faster because there’s no address switching except for the SUBMIT command passed to
TSO. For short execs the difference is so small it’s almost meaningless, but for many
consecutive calls, or calls within a loop, the difference is measurable.




                                                                                    14
More on Stem Variables

   So far we’ve only seen stem variables with a single index that’s always a number.
Stem variables are more flexible than that.

    For example, although a stem variable has a stem and an index, a single entry of a
stem variable is just like any other, non-stem variable. We can set any single entry of a
stem variable to be a stem variable using a second index. Here is a piece of code to
initializes a chessboard, a two-dimensional array of eight ranks and eight files.

       Do R = 1 To 8
         Do F = 1 To 8
           ChessBoard.R.F = "."
         End
       End


   The stem index does not have to be a number. REXX understands the use of a letter
rather than a number. For example, try this.

       /* REXX */
       Do Forever
         Say “What is your name (END to stop)?”
         Pull NAME
         If NAME = END Then Leave
         Say “Thanks,” NAME “, how old are you?”
         Pull HOW_OLD
         If DataType(HOW_OLD) <> “NUM”
           Then Say “Not valid age, rejected.”
           Else AGE.NAME = HOW_OLD
       End

       Do Forever
         Say “Whose age would you like”,
           “(END to stop)?”
         Pull NAME
         If NAME = “END” Then Leave
         If SYMBOL(‘AGE.NAME’) <> “VAR”
           Then Say “Name not in memory.”
           Else Say NAME “is” AGE.NAME “years old.”
       End



                                                                                            15
System Variables

  REXX provides two sets of system variables. The first set is SYSVAR.

1. SYSPREF: the prefix assigned to not-fully-qualified dataset names.
2. SYSPROC: the logon procedure for the current session.
3. SYSUID: the user ID of the person logged on.
4. SYSLTERM and SYSWTERM: the length and width of the current terminal screen.
    In batch, SYSLTERM returns 0 and SYSWTERM returns 132.
5. SYSENV: returns FORE or BACK.
6. SYSICMD: returns the name of the running, implicitly called exec. If the call was
    explicit, SYSICMD returns null.
7. SYSISPF: returns ACTIVE or NOT ACTIVE.
8. SYSNEST: if the current program was called from another, SYSNEST returns YES;
    otherwise it returns NO.
9. SYSPCMD: the most recently processed TSO-E command processor. The initial
    value of SYSPCMD may be EXEC (if the EXEC command was used) or EDIT (if the
    EXEC subcommand of EDIT was used).
10. SYSSCMD: the most recently processed TSO-E subcommand processor. The initial
    value of SYSPCMD may be null (if the EXEC command was used) or EXEC (if the
    EXEC subcommand of EDIT was used).
11. SYSCPU: the number of CPU seconds used this session.
12. SYSSRV: the number of service units used this session.
13. SYSHSM: the status of DFHSM. Returns AVAILABLE or null.
14. SYSJES: the name and level of JES.
15. SYSLRACF: the level of RACF. If RACF is not installed, this is null.
16. SYSRACF: returns AVAILABLE, NOT AVAILABLE, or NOT INSTALLED.
17. SYSNODE: the JES node name. This returns either the JES node name, the string
    -INACTIVE-, or the string -DOWNLEVEL- if the subsystem is neither JES2 SP4.3
    or later, nor JES3 SP5.1.1 or later.
18. SYSTERMID: the terminal ID, or null if batch.
19. SYSTSOE: the level of TSO installed. For OS/390 Version 2, Release 4 and later,
    SYSTSOE returns 2060.
20. SYSDTERM: If double-byte character set (DBCS) is enabled, returns YES.
21. SYSKTERM: if Katakana character set is enabled, returns YES.
22. SYSPLANG and SYSSLANG: returns the 3-byte primary and secondary language
    settings.
23. SOLDISP and UNSDISP: show solicited (operator replies) or unsolicited (operator
    messages) messages on the user’s terminal. YES or NO.
24. SOLNUM and UNSNUM: the size of the message table.
25. MFTIME: show a time stamp with each message. YES or NO.
26. MFOSNM: show originating system name with each message. YES or NO.
27. MFJOB: display the originating job name. YES or NO.
28. MFSNMJBX: did the user request to NOT show the originating job and system
    names of the message. YES means to NOT show the names; NO means to show
    them.


                                                                                  16
System Variables: SYSVAR notes

1. SYSVAR can only be used in TSO/E environments (i. e. not VM, OS/2, etc.).

2.   SYSPROC has three different responses, depending on where it was called:
     o Foreground: returns the logon procedure name.
     o Batch: returns INIT because the job has an initiator.
     o 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."




                                                                                 17
System Variables: MVSVAR

    The second set of system variables are the MVS variables.

•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.



                                    Exercise #4

   Write an exec to print a calendar month, given the name of the month and the name of
the first day of the month. For example, CALPRT JANUARY MONDAY.


                                    Exercise #5

   Write an exec to fill a 3X3 array with “#” by having the user enter a row name (letter)
and column number. If the row and column are “equal”, fill the slot with a “$” instead.
Print the array at the end. The array should look like this:

     1   2    3
A    $   #    #
B    #   $    #
C    #   #    $




                                                                                        18
Other Performance Concerns

   Here are some other performance tips. They are not in any particular order.
Remember, you get more bang for your buck in a loop than in a single instruction. Don’t
sacrifice readability for speed unless you absolutely need to.

o Shorter variable names are faster. Use abbreviated names if they’re clear enough.
  For example, Social_Security_Number is valid, but SSN is just as clear and more
  efficient.

o Drop large stem arrays when you’re done with them.

o When known ahead of time, use literals or constants rather than variables.

o Don’t use external subroutines in loops. If a subroutine is necessary, code it in the
  exec.

o Code subroutines as close to the call as possible (without sacrificing readability). Use
  SIGNAL to get around the code, if necessary.

o When grouping subroutines, place the most often used routine first.

o   Consider a subroutine when a large number of statements are needed for something
    that is often NOT executed.

o When calling subroutines or functions don’t specify the defaults, and pass literals
  rather than variables when possible.

o Avoid using functions within functions. For example, a) is more efficient than b)
  despite using more lines of code:
       a)PT = right(strip(cmddata),3)
       b)PL = strip(cmddata); PT = right(PL,3)
o Avoid DATE and TIME functions in loops and subroutines if possible.

o Instructions are about twice as fast as built-in functions.

o Instructions are about 100 times as fast as external commands.




                                                                                          19
Putting It All Together

  You can combine functions to manage records. Here are some examples.

   Suppose you want to scan records in JCL to remove a now-defunct symbolic
parameter. For this example, the symbolic parameter to remove is CVOL. We don’t care
what the value given is, we just want to remove it. In EDIT you can do a FIND on
CVOL, then delete characters up to the next comma. Here’s a way to do it automatically
in REXX.

   1.MCVLOC = Pos("MODCVOL",SYMB)
   2.LHLen = MCVLOC - 1
   3.RHLen = 71 - MCVLOC
   4.LHalf = Substr(Source.SOURCEI,1,LHLen)
   5.RHalf = Substr(Source.SOURCEI,MCVLOC,RHLen))
   6.Parse Var RHalf MODCVOL "," RRHalf
   7. If Right(Strip(Rhalf),1) = ","
   8.   Then SYMB = LHalf","RRHalf
   9.   Else SYMB = LHalf||RRHalf
   10.FLen = Length(SYMB)
   11.If Right(SYMB,2) = ",,"
   12.  Then SYMB = Substr(SYMB,1,FLen-1)


   1. Get the location of CVOL on the line.
   2. The length of the line up to CVOL is the location of CVOL minus 1
   3. The length of the line from CVOL to the end is 71 minus the location of CVOL
       because column 72 is the continuation column.
   4. The left “half” of the line starts at Byte 1 and continues for the length as found in
       Step 2.
   5. Start the right “half” of the line from CVOL through the end.
   6. Break up the right half into the part containing CVOL and the rest by looking for
       the comma after the CVOL parameter.
   7. If the last non-blank character in the right half is a comma:
   8. Yes, it was, add the comma when concatenating the halves together;
   9. No comma, concatenate without one.
   10. Just in case CVOL was the last thing on the line, get the length of the entire line.
   11. If CVOL was the last thing on the line, the added comma for the concatenation
       was wrong. Remove it by taking all the characters up to, but not including, the
       comma (see 12 also).




                                                                                          20
Combinations of Functions continued

   Suppose you want neater JCL. Your idea is to force the EXEC or DD to always make
room for an 8-byte step name or DD name, followed by one space, followed by either
“DD” or “EXEC.” All other arguments follow the “DD” or “EXEC” after one space.

   1.  NLine = "//"Copies(" ",70)
   2. Select
   3.    When WordPos("EXEC",Source.SOURCEI) ¬= 0 Then Do
   4.      Parse Var Source.SOURCEI SNX EXECS PROCS
   5.      NLine = Overlay(SNX,NLine,1)
   6.      NLine = Overlay("EXEC",NLine,12)
   7.      NLine = Overlay(PROCS,NLine,17)
   8. End
   9.    When WordPos("DD",Source.SOURCEI) ¬= 0 Then Do
   10.     Parse Var Source.SOURCEI DDNX DDS DDARGS
   11.     NLine = Overlay(DDNX,NLine,1)
   12.     NLine = Overlay("DD",NLine,12)
   13.     NLine = Overlay(DDARGS,NLine,15)
   14. End
   15.End
   16.Source.SourceI = NLine

   1.  Create a default JCL line of “//” followed by 70 blanks.
   2.  Start a Select group.
   3.  If this is an EXEC statement (“EXEC” surrounded by blanks), do steps 4 – 7.
   4.  Parse the line into “//<stepname>”, the word EXEC, and anything following
       EXEC.
   5. Put the “//<stepname>” on the new line beginning in Column 1.
   6. Put the word “EXEC” on the line in position 12. This leaves room for the “//”
       plus an 8-character step name.
   7. Put the remainder of the EXEC line one space after the word EXEC.
   8. End the EXEC group.
   9. If this is a DD statement (“DD” surrounded by blanks), do steps 10 – 13.
   10. Parse the LINE variable into the “//<DD name>”, the word DD, and anything
       following DD. “DD name” can be all blanks.
   11. Put the “//<DD name>” on the new line beginning in Column 1.
   12. Put the word “DD” on the line in position 12. This leaves room for the “//” plus
       an 8-character DD name.
   13. Put the remainder of the DD line one space after the word DD.
   14. End the DD group.
   15. End the Select group.
   16. Set the JCL line to its new, formatted version.




                                                                                          21
Combinations of Functions continued

    At times, people doing data security need to scan their datasets to see what is and what
is not protected, and what profiles are in WARNING mode (i.e. the profile exists but is
not observed). Here is some code to do this check against any dataset level. Please note
the code is split onto two pages, with the explanation on a third page.

1. /* REXX Checks datasets under LEVEL for RACF. */
2.  Arg LEVEL
3.  GOODPROF = 0; WARNPROF = 0; NOPROF = 0; TotalDSN = 0
4.  foo = outtrap(LCL.)
5.  "LISTC LEVEL("LEVEL")"
6.  foo = outtrap(Off)
7. OutLine.1 = "The profile name, its UACC, and warning mode”,
    “is displayed."
8. OutLine.2 = "'????' is displayed if you don't have access”,
    “to list the profile.”
9. OutLine.3 = "A warning message is displayed if there is no”,
    “profile."
10.OutLine.4 = “”
11./* Skip 5 through 13 for final tallies, 14 is header. */
12.OutLine.14 = Copies(" ",79)
13.OutLine.14 = Overlay("DSN",OutLine.14,1)
14.OutLine.14 = Overlay("PROFILE",OutLine.14,36)
15.OutLine.14 = Overlay("UACC",OutLine.14,64)
16.OutLine.14 = Overlay("WARNING?",OutLine.14,72)
17.OutIX = 14
18.Do L = 1 to LCL.0
19. Select
20.     When Word(LCL.L,1) = "NONVSAM" Then RADS = Word(LCL.L,3)
21.     When Word(LCL.L,1) = "CLUSTER" Then RADS = Word(LCL.L,3)
22.     When Word(LCL.L,1) = "AIX”      Then RADS = Word(LCL.L,3)
23.     Otherwise Iterate
24. End
25.   foo = outtrap(RI.,'*')
26.   "LD DA('"RADS"') GENERIC ALL"
27.   If Word(RI.1,1) = "ICH35003I" Then Do    /* No profile */
28.     NOPROF = NOPROF + 1
29.     OutIX = OutIX + 1
30.     OutLine.OutIX = “Dataset" RADS "not protected by RACF."
31.     Iterate L
32. End
33.   Do I = 1 To RI.0
34.     Parse Var RI.1 . . . PROFILE .
35.     Parse Var RI.5 . . UACC WARN .
36. End
37.   If RI.0 = 1 Then Do
38.     PROFILE = "????"; WARN     = "????"; UACC    = "????"
39. End


                                                                                         22
RIB exec Page 2

   This section of the exec determines the profile type and if it is in warning. Finally, the
exec creates an output report.

40.   Select
41.     When WARN = "NO" Then Do
42.       W = "HARDCHK"; GOODPROF = GOODPROF + 1
43.     End
44.     When WARN = "YES" Then Do
45.       W = "WARNING"; WARNPROF = WARNPROF + 1
46.     End
47.     Otherwise W = "UNKNOWN"
48. End
49. OutIX = OutIX + 1
50. OutLine.OutIX = Copies(" ",79)
51.   OutLine.OutIX = Overlay(RACFDS,OutLine.OutIX,1)
52. OutLine.OutIX = Overlay(PROFILE,OutLine.OutIX,36)
53.   OutLine.OutIX = Overlay(UACC,OutLine.OutIX,64)
54.   OutLine.OutIX = Overlay(W,OutLine.OutIX,72)
55.End L /* Do group from Line 19 */
56.UNKNOWN = TotalDSN - GOODPROF - WARNPROF - NOPROF
57.PROFILES = GOODPROF + WARNPROF
58. OutLine.5 = "RACF report for dataset level" LEVEL":"
59. OutLine.6 = ""
60. OutLine.7 = "TOTAL DATASETS:          ",
61.   Right("     "TotalDSN,6)
62. OutLine.8 = "DATASETS WITH PROFILES: ",
63.   Right("     "PROFILES,6)
64. OutLine.9 = "DATASETS IN WARNING:     ",
65.   Right("     "WARNPROF,6)
66. OutLine.10 = "PROTECTED DATASETS:     ",
67.   Right("     "GOODPROF,6)
68. OutLine.11 = "UNPROTECTED DATASETS:   ",
69. Right("       "NOPROF,6)
70. OutLine.12 = "INSUFFICIENT AUTHORITY: ",
71.   Right("     "UNKNOWN,6)
72. OutLine.0 = OutIX
73."ATTRIB FB80 RECFM(F B) LRECL(80)"
74.OutDSN = LEVEL".DATA"
75. "DELETE" OUTDSN
76. "ALLOC DA("OUTDSN") F(OUTFILE) NEW SPACE(5 5) TRACKS”,
77.   “REUSE USING(FB80) RELEASE"
78."EXECIO " OutLine.0 " DISKW OUTFILE (STEM OutLine. FINIS"
79. Say
80.Say "Output written to" OutDSN"."
81.“FREE F(OUTFILE) ATTR(FB80)”
82.Exit




                                                                                          23
RIB exec Page 3

   A brief explanation of some of the code follows.

Line 3 initializes all counter fields for the output totals.

Lines 4 – 6 capture LISTC LEVEL data to get the datasets to check.

Lines 7 – 16 set the headers. Note several lines are left blank at this time. This makes
     room for the summary lines, whose values cannot be determined until the exec
     ends. The OVERLAY function lets me align the headers to specific columns.
     Finally, Line 17 points the output stem index to the first blank line after the dataset
     report header line.

The select group at Line 19 keeps non-VSAM and VSAM dataset names, and skips all
     other lines from the LISTC.

Line 26 does the RACF command LISTDSD to get the profile information.

Lines 27 – 32 writes a note if there is no profile to list.

Lines 33 – 36 pull the useful information from the profile.

Lines 37 – 39 writes a note if you don’t have the authority to list the profile.

The select group at Line 40 notes whether the profile is in WARNING or not.

Lines 49 – 54 format the report line for this dataset.

Lines 56 – 72 format the summary section. This section uses the blank lines we left back
     on lines 7 – 16.

The remaining lines allocate the output dataset and write the report stem to the output
dataset.




                                                                                           24
This Page Intentionally Left Blank




                                     25
Appendix A: A Few Simple Edit Macros

  These are a few, simple edit macros to show how many macros are just a string of edit
commands you might use frequently. Each is explained in some detail.


                                 Initial Edit Macro

   When starting any edit session, ISPF can run a macro on the dataset before turning
control over to you. This macro is called an initial edit macro. You can specify an initial
macro on the edit screen. If you want the macro to run every time, though, it’s just as
easy to tell ISPF what it is. This is done in two easy steps.

  STEP 1: Code the macro. Here is an example. The code is COURIER; comments are
made in Arial and underscored.

Address ISREDIT
"MACRO"
"PROFILE UNLOCK" - unlock the edit profile of the current dataset.
"BOUNDS"          - Reset BOUNDS to the full LRECL.
"NULLS ON"        - Lets me insert (ignores spaces on end of lines).
"HILITE ON"       - Turns on highlighting
CURMBR = ""
"(CURMBR) = MEMBER" Checks if editing a PDS. If so, turn on STATS.
If CURMBR <> "" Then "STATS ON"
"RESET"           - Clear all messages.
Exit


    STEP 2: set the initial macro. You must use the VPUT Dialog Manager service to do
this. So you don’t have to remember (or know) how to do that, run this command:

SETZIMAC <name of your macro>

    Your CLIST or REXX macro must be in the SYSPROC or SYSEXEC concatenation.
If your macro is a program, it must be in the STEPLIB concatenation.

   Note: some ISPF dialogs run in their own application ID. Should you enter a dialog
and your edit macro doesn’t work, try running SETZIMAC again.




                                                                                         26
More Simple Edit Macros

   These two edit macros change all upper-case characters to lower-case, or vice-versa.
Note they’re just a single edit command. This could just as easily be set to a function
key.

       Address ISREDIT
       "MACRO"
       "C P'>' P'<' ALL"
       Exit

       Address ISREDIT
       "MACRO"
       "C P'<' P'>' ALL"
       Exit


   This macro excludes all lines, then does a find on a passed string. The purpose is to
see only what you want to see. Note the passed parameter may include quotes, column
numbers, and other FIND operands (except ALL, of course). For sample calls, see below
the macro.

       Address ISREDIT
       "MACRO (FPARM)"
       "EXCLUDE ALL"
       "FIND ALL "FPARM
       Exit 0

   Sample calls:

XFS ISDODEA                      shows all occurrences of this TSO user ID.
XFS WORD EXEC                    shows all EXEC lines in a JCL dataset.
XFS WORD DD 13 71                shows all DD statements not aligned at column 12.




                                                                                          27
Appendix B: Solutions to Exercises

                                Exercise 1, page 6

    Modify the exec you wrote in Exercise #28 to use a noun switch instead of expression
tests when deciding what to do with the dataset.

/* REXX passes args to LISTD with program stack. */
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 DSNExists = 1
  Else DSNExists = 0
If DSNExists
  Then Do
    Queue DSN
    DUMMY = Prompt("ON")
    "LISTD"
  End
  Else Say DSN "does not exist."




                                                                                      28
Exercise 2, Page 7

   Modify the exec you wrote in Exercise #16 from the first course to accept the unit and
amount values as keywords. Only accept two arguments. If one argument is bad, display
the syntax and exit.


/* REXX Metric Converter */
Arg Parm.1 Parm.2
Do I = 1 to 2
  Select
    When Left(Parm.I,1) = "U"
      Then Parse Var Parm.I Junk "(" UNIT ")"
    When DataType(Parm.I) = "NUM" Then AMT = Parm.I
    Otherwise Do
      Say "Invalid parm entered: " Parm.I"."
      Say "SYNTAX : CNVT UNIT(...) and a number."
      Say "UNIT must be one of liter, quart, mile, or",
         "kilometer."
      Exit
    End
  End
End

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."
End




                                                                                       29
Exercise 3, Page 11

    Write an exec to parse the date into month, day, and year. Display them in the
following formats:

       MM/DD/YY, DD/MM/YY, YY/DD/MM, and YY/MM/DD.

The command to get the USA-style date (MM/DD/YY) is DATE(”U”).


/* REXX Date formatter */
Parse Value Date("U") With Month "/" Day "/" Year
"CLRSCRN"
Say "Today's date is: " Date("U")", or"
Say "USA:             " Month"."Day"."Year";"
Say "European:        " Day"."Month"."Year";"
Say "Year, then day: " Year"."Day"."Month";"
Say "Ordered:         " Year"."Month"."Day"."




                                                                                     30
Exercise 4, Page 19

   Write an exec to print a calendar month, given the name of the month and the name of
the first day of the month. Assume the first day of the week is Sunday. For example,
CALPRT JANUARY MONDAY should start the calendar on Monday, January 1. Use
the CENTER function to display the month name.

/* REXX print a calendar month. */
Arg Month Day

/*-------------------------------*
 * Init month table with blanks. *
 *-------------------------------*/
Do I = 1 to 6
  Do J = 1 to 7
    Week.I.J = " "
  End J
End I

/*------------------------------------------*
 * Init fields:                             *
 * WeekDay is the day of the week;          *
 * DayNumber is the day of the month;       *
 *------------------------------------------*/
WeekDay = 1; DayNumber = 1

/*---------------------------------*
 * Set the first day of the month. *
 *---------------------------------*/
Select
  When Day = "SUNDAY"    Then WeekDay                  =   1
  When Day = "MONDAY"    Then WeekDay                  =   2
  When Day = "TUESDAY"   Then WeekDay                  =   3
  When Day = "WEDNESDAY" Then WeekDay                  =   4
  When Day = "THURSDAY" Then WeekDay                   =   5
  When Day = "FRIDAY"    Then WeekDay                  =   6
  When Day = "SATURDAY" Then WeekDay                   =   7
  Otherwise Do
    Say "Invalid day" Day"."
    Exit
  End
End


                              Continued on the next page




                                                                                     31
Exercise 4, Page 19, answer continued
/*---------------------------------------*
 * Load the table. Start at the current *
 * weekday, then reset it after Week 1. *
 * Skip out when at end of month.        *
 *---------------------------------------*/
Do WeekNumber = 1 to 6
  Do WeekDay = WeekDay to 7
    If Length(DayNumber) = 1
      Then DayWord = " "DayNumber
      Else DayWord = DayNumber
    Week.WeekNumber.WeekDay = DayWord
    DayNumber = DayNumber + 1
    If DayNumber > DayLimit Then Signal EndOfMonth
  End
  WeekDay = 1
End

EndOfMonth:
"CLRSCRN"
Say
Say Center(Month,21)
Say " S M T W T F S"
Do WeekNumber = 1 to 6
  Say " "Week.WeekNumber.1 Week.WeekNumber.2,
    Week.WeekNumber.3 Week.WeekNumber.4 Week.WeekNumber.5,
    Week.WeekNumber.6 Week.WeekNumber.7
End




                                                             32
Exercise 5, Page 19

   Write an exec to fill a 3X3 array with “#” by having the user enter a row name (letter)
and column number. If the row and column are “equal”, fill the slot with a “$” instead.
Print the array at the end. The array should look like this:

    1    2   3
A   $    #   #
B   #    $   #
C   #    #   $

   HINT: you will need the TRANSLATE function to do this exec cleanly. Guidance is
provided if desired.


/* REXX advanced course, exercise 6 */
Do I = 1 to 3
  Select
    When I = 1 Then Row = "A"
    When I = 2 Then Row = "B"
    When I = 3 Then Row = "C"
  End
  Do Col = 1 to 3
    Array.Row.Col = " "
  End Col
End I

/*------------------------------------*
 * SLOTS is the number of full slots; *
 * ArrayFull says the array is full. *
 *------------------------------------*/
Slots = 0; ArrayFull = 0



                               Continued on the next page




                                                                                        33
Exercise 5, Page 19 continued

/*---------------------------*
 * Loop until array is full. *
 *---------------------------*/
Do Until ArrayFull
  Say "Enter a row (A, B, or C) and a column (1, 2, or 3)."
  Pull Row Col
  Say

  If Pos(Row,"ABC") = 0 Then Do
    Say "Row" Row "invalid, try again."
    Iterate
  End

  If Pos(Col,"123") = 0 Then Do
    Say "Column" Col "invalid, try again."
    Iterate
  End

  If Array.Row.Col ¬= " " Then Do
    Say Row "and" Col "already used, try again."
    Iterate
  End

  RowNum = Translate(Row,"123","ABC")
  If RowNum = Col
    Then Array.Row.Col = "$"
    Else Array.Row.Col = "#"
  Slots = Slots + 1
  If Slots = 9 Then ArrayFull = 1
End

/*--------------------*
 * Display the array. *
 *--------------------*/
Row = "A"
Say "A" Array.Row.1 Array.Row.2 Array.Row.3
Row = "B"
Say "B" Array.Row.1 Array.Row.2 Array.Row.3
Row = "C"
Say "C" Array.Row.1 Array.Row.2 Array.Row.3




                                                          34

Mais conteúdo relacionado

Mais procurados

Parallel Sysplex Implement2
Parallel Sysplex Implement2Parallel Sysplex Implement2
Parallel Sysplex Implement2
ggddggddggdd
 
Vsam interview questions and answers.
Vsam interview questions and answers.Vsam interview questions and answers.
Vsam interview questions and answers.
Sweta Singh
 
Jcl tutor
Jcl tutorJcl tutor
Jcl tutor
shivas
 
Mainframe JCL Part - 1
Mainframe JCL Part - 1Mainframe JCL Part - 1
Mainframe JCL Part - 1
janaki ram
 

Mais procurados (20)

TSO Productivity
TSO ProductivityTSO Productivity
TSO Productivity
 
JCL FOR FRESHERS
JCL FOR FRESHERSJCL FOR FRESHERS
JCL FOR FRESHERS
 
Parallel Sysplex Implement2
Parallel Sysplex Implement2Parallel Sysplex Implement2
Parallel Sysplex Implement2
 
Mainframe interview
Mainframe interviewMainframe interview
Mainframe interview
 
Upgrade to IBM z/OS V2.5 Planning
Upgrade to IBM z/OS V2.5 PlanningUpgrade to IBM z/OS V2.5 Planning
Upgrade to IBM z/OS V2.5 Planning
 
DB2 for z/OS Architecture in Nutshell
DB2 for z/OS Architecture in NutshellDB2 for z/OS Architecture in Nutshell
DB2 for z/OS Architecture in Nutshell
 
Basic concept of jcl
Basic concept of jclBasic concept of jcl
Basic concept of jcl
 
Z OS IBM Utilities
Z OS IBM UtilitiesZ OS IBM Utilities
Z OS IBM Utilities
 
Tso and ispf
Tso and ispfTso and ispf
Tso and ispf
 
Vsam interview questions and answers.
Vsam interview questions and answers.Vsam interview questions and answers.
Vsam interview questions and answers.
 
Job Control Language
Job Control LanguageJob Control Language
Job Control Language
 
Jcl tutor
Jcl tutorJcl tutor
Jcl tutor
 
Z4R: Intro to Storage and DFSMS for z/OS
Z4R: Intro to Storage and DFSMS for z/OSZ4R: Intro to Storage and DFSMS for z/OS
Z4R: Intro to Storage and DFSMS for z/OS
 
Upgrade to IBM z/OS V2.5 technical actions
Upgrade to IBM z/OS V2.5 technical actionsUpgrade to IBM z/OS V2.5 technical actions
Upgrade to IBM z/OS V2.5 technical actions
 
DB2 for z/OS and DASD-based Disaster Recovery - Blowing away the myths
DB2 for z/OS and DASD-based Disaster Recovery - Blowing away the mythsDB2 for z/OS and DASD-based Disaster Recovery - Blowing away the myths
DB2 for z/OS and DASD-based Disaster Recovery - Blowing away the myths
 
DB2 for z/OS Real Storage Monitoring, Control and Planning
DB2 for z/OS Real Storage Monitoring, Control and PlanningDB2 for z/OS Real Storage Monitoring, Control and Planning
DB2 for z/OS Real Storage Monitoring, Control and Planning
 
Mainframe JCL Part - 1
Mainframe JCL Part - 1Mainframe JCL Part - 1
Mainframe JCL Part - 1
 
IBM Utilities
IBM UtilitiesIBM Utilities
IBM Utilities
 
CA 7-final-ppt
CA 7-final-pptCA 7-final-ppt
CA 7-final-ppt
 
Presentation db2 connections to db2 for z os
Presentation   db2 connections to db2 for z osPresentation   db2 connections to db2 for z os
Presentation db2 connections to db2 for z os
 

Destaque (9)

White Paper, System Z Dataset Naming Standards
White Paper, System Z Dataset Naming StandardsWhite Paper, System Z Dataset Naming Standards
White Paper, System Z Dataset Naming Standards
 
Rexx summary
Rexx summaryRexx summary
Rexx summary
 
Neha Gupta
Neha GuptaNeha Gupta
Neha Gupta
 
Basic IMS For Applications
Basic IMS For ApplicationsBasic IMS For Applications
Basic IMS For Applications
 
IMSDB - DBRC
IMSDB - DBRCIMSDB - DBRC
IMSDB - DBRC
 
Some ISPF Tricks
Some ISPF TricksSome ISPF Tricks
Some ISPF Tricks
 
White Paper, The Basics Of Data Security
White Paper, The Basics Of Data SecurityWhite Paper, The Basics Of Data Security
White Paper, The Basics Of Data Security
 
VSAM Tuning
VSAM TuningVSAM Tuning
VSAM Tuning
 
UVP, Mine
UVP, MineUVP, Mine
UVP, Mine
 

Semelhante a Advanced REXX Programming Techniques

Ruby -the wheel Technology
Ruby -the wheel TechnologyRuby -the wheel Technology
Ruby -the wheel Technology
ppparthpatel123
 

Semelhante a Advanced REXX Programming Techniques (20)

Perl intro
Perl introPerl intro
Perl intro
 
Ruby -the wheel Technology
Ruby -the wheel TechnologyRuby -the wheel Technology
Ruby -the wheel Technology
 
PerlIntro
PerlIntroPerlIntro
PerlIntro
 
PerlIntro
PerlIntroPerlIntro
PerlIntro
 
C compiler(final)
C compiler(final)C compiler(final)
C compiler(final)
 
Algorithm and Programming (Introduction of dev pascal, data type, value, and ...
Algorithm and Programming (Introduction of dev pascal, data type, value, and ...Algorithm and Programming (Introduction of dev pascal, data type, value, and ...
Algorithm and Programming (Introduction of dev pascal, data type, value, and ...
 
Meta Object Protocols
Meta Object ProtocolsMeta Object Protocols
Meta Object Protocols
 
Reversing & Malware Analysis Training Part 4 - Assembly Programming Basics
Reversing & Malware Analysis Training Part 4 - Assembly Programming BasicsReversing & Malware Analysis Training Part 4 - Assembly Programming Basics
Reversing & Malware Analysis Training Part 4 - Assembly Programming Basics
 
Oct.22nd.Presentation.Final
Oct.22nd.Presentation.FinalOct.22nd.Presentation.Final
Oct.22nd.Presentation.Final
 
Javascript
JavascriptJavascript
Javascript
 
C Tutorials
C TutorialsC Tutorials
C Tutorials
 
pm1
pm1pm1
pm1
 
Java generics final
Java generics finalJava generics final
Java generics final
 
Advanced MySQL Query Optimizations
Advanced MySQL Query OptimizationsAdvanced MySQL Query Optimizations
Advanced MySQL Query Optimizations
 
[ASM]Lab6
[ASM]Lab6[ASM]Lab6
[ASM]Lab6
 
C tutorial
C tutorialC tutorial
C tutorial
 
C tutorial
C tutorialC tutorial
C tutorial
 
C tutorial
C tutorialC tutorial
C tutorial
 
Python
PythonPython
Python
 
Parsers Combinators in Scala, Ilya @lambdamix Kliuchnikov
Parsers Combinators in Scala, Ilya @lambdamix KliuchnikovParsers Combinators in Scala, Ilya @lambdamix Kliuchnikov
Parsers Combinators in Scala, Ilya @lambdamix Kliuchnikov
 

Advanced REXX Programming Techniques

  • 1. REXX Programming For TSO: Advanced Concepts Dan O’Dea September 2, 2004 1
  • 2. Table of Contents Introduction . . . . . . . . . . . . . . . . . 3 Goals of the course. . . . . . . . . . . . . 3 More on syntax 4 Comments . . . . . . . . . . . . . . . . . . 4 Handling Hexadecimals. . . . . . . . . . . . 4 More on If/Then 5 Using words to indicate success or failure . 5 Boolean operators. . . . . . . . . . . . . . 7 Passing Parameters 8 Using keyword parameters . . . . . . . . . . 8 Parsing 9 Using columns. . . . . . . . . . . . . . . . 9 Using literals . . . . . . . . . . . . . . . 10 PARSE VALUE. . . . . . . . . . . . . . . . . 10 PARSE SOURCE . . . . . . . . . . . . . . . . 10 Trace Output Line Codes 12 OutTrap Restrictions 12 Arithmetic and verifying expressions 13 Addressing Environments 14 Stem Variables 15 System Information 16 SYSVAR . . . . . . . . . . . . . . . . . . . 16 MVSVAR . . . . . . . . . . . . . . . . . . . 18 Other Performance Concerns 19 Putting it all together: real-world examples 20 Appendix A, Page 26: a few simple edit macros Appendix B, Page 28: solutions to exercises 2
  • 3. Introduction The first part of this course provided you with basic learning needed to code REXX execs. Some topics were glossed over, or skipped entirely. REXX performance was not discussed. Finally, some REXX features can be used to make your program more readable, such as word tests (something like the COBOL 88 level variable). This document corrects those “oversights.” While the first document often used simplified examples, this document uses real-world examples wherever possible. Goals of the Course When you complete the course you will be able to do the following. • Do arithmetic on hexadecimal values. • Use nouns in IF statements rather than comparisons. • Handle both positional and keyword parameters. • Parse anything into anything. • Understand trace output. • Verify arithmetic expressions. • Use multi-dimensional stem variables (matrixes). • Code more efficient REXX execs. 3
  • 4. 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. When using like this, use one pair of delimiters. */ Longer comments are more efficient because the REXX interpreter opens comment processing each time it encounters a /*. By coding only one set around multiple comment lines, the interpreter does less work. This is especially important in loops. Make a box comment above a loop to explain everything the loop does. If the comments are in the loop, they are scanned each pass through the loop. To avoid unnecessary loop processing, don’t comment within the loop. Hexadecimal Values You may recall that hexadecimal numbers may be used. Here is a piece of code showing how interpreting hex characters is used to determine what LPAR you’re running on. How it works: First, convert the 4 bytes of system storage area at location x’10’ to hex. Convert that to decimal for math, then add the offset to the storage location of the system name (in this case, hex 154 / decimal 340). Convert the result back to hex. Then get the 8 bytes of storage at the resulting address and strip the spaces from it. /*------------* * What LPAR? * *------------*/ CVT = C2X(STORAGE(10,4)) CVT_154 = D2X(X2D(CVT) + X2D(154)) SID = STRIP(STORAGE(CVT_154,8)) 4
  • 5. Conditional Operators As we know, the IF statement follows a standard format: If expression Then one instruction Else one instruction DO WHILE and DO UNTIL statements are considered IF statements. Internally, any IF statement operates by testing the outcome of a binary choice (yes or no). We’ve seen the results of IF comparisons can be displayed as “1” (true) or “0” (false). We can use this feature to our advantage. In REXX, almost any statement can contain a variable to be resolved. Consider the following: Dan = 0 Say “Enter name” Pull Name If Name = “Dan” Then Dan = 1 If Dan Then Say “Hi, Mr. O’Dea!” Else Say “Hi,” Name“.” REXX resolves the expression “If Dan” as either “If 0” or “If 1” depending on the value in the variable Dan. “Dan” is called a noun switch. It’s COBOL equivalent is the 88 level. There are two things to keep in mind when using this method: 1. You must be careful to set the switch to only zero or one. If the IF statement finds any other value, your exec terminates with a syntax error. 2. You cannot negate the switch. To test for a negative condition, it’s a good idea to code the statement positively, and use the NOP command to skip the THEN condition. For example, KSDS = 0 If <index> Then KSDS = 1 If KSDS Then NOP /* Do nothing for KSDS */ Else Say “ESDS” /* Do ESDS stuff */ 5
  • 6. Conditional Operators Noun Switch Example Here is part of the code from the stack example above. First, set the switch to 0 (off/false). Then, check if the dataset has an index component. If the index component exists, turn the switch on. You can then check the switch in the IF. KSDS = 0 CKIX = INDSN“.INDEX” If SysDSN(CKIX) = “OK” Then KSDS = 1 :: If KSDS Then Do Queue " DATA (NAME("NewDSN".DATA)) -" Queue " INDEX (NAME("NewDSN".INDEX))" End Else Queue " DATA (NAME("NewDSN".DATA))" Here’s another piece of code used to scan a LISTC ENT() ALL output for the index CI size: If EndOfData Then NOP Else Do Do Until LCA.I = "ATTRIBUTES"; I = I + 1; End I = I + 1 ICIS = Strip(Right(Word(LCA.I,4),6),"L","-") End Notes on the above: 1. The “Do Until” scans the output from the previous CISIZE field (the one in the data component section) until the index attributes section to skip over stuff we don’t want. The index “I” must point to the next line after “ATTRIBUTES” to get the CISZ. 2. The last line gets the fourth word of the output line, takes the last 6 characters of that word, then removes leading dashes from it to give the CI size of the index. Exercise #1 Modify the exec you wrote in Exercise #28 of the first class to use a noun switch instead of expression tests when deciding what to do with the dataset. 6
  • 7. Passing Information to the Exec The ARG statement parses values passed to the program based on spaces or commas. ARG is thus a positional operation selector. Here’s one way to code the ARG to enable you to process passed parameters as keywords. /* REXX Keyword Parms, LC accepts up to three parms. More than that are ignored. */ ARG PARM.1 PARM.2 PARM.3 . Lall = “”; LGDG = “” Do I = 1 to 3 Parse Var Parm.I Opt 4 . Select When Opt = “” Then NOP When Opt = “ENT” Then Do Parse Var Parm.I . “(” LCADSN LCADSN = Strip(LCADSN,“T”,“)”) End When Opt = “ALL” Then LAll = “ALL” When Opt = “GDG” Then LGDG = “GDG” Otherwise Say Parm.I “is invalid.” End End LCADSN = $FixDSN(LCADSN) “LISTC ENT(“LCADSN”)” LAll LGDG This is designed to perform a LISTC ENT() on without having to code ALL, GDG, or other argument in any order. All these calls give the same result: LC ENT(‘ISDODEA.TEST.DATA’) ALL GDG LC GDG ENT(‘ISDODEA.TEST.DATA’) ALL LC ALL GDG ENT(‘ISDODEA.TEST.DATA’) LC ALL ENT(‘ISDODEA.TEST.KSDS’) GDG Exercise #2 Modify the exec you wrote in Exercise #16 from the first course to accept the unit and amount values as keywords. Only accept two arguments. If one argument is bad, display the syntax and exit. 7
  • 8. Separating Data: Parsing In the introduction we talked about parsing data by word or literal. You can also parse data in other ways. Some of them are very useful. You can parse by column number. It can get complicated because column numbers in PARSE are not intuitive, but for some things it performs very well. The PARSE command is very fast. It is faster than SUBSTR or the LEFT and RIGHT functions. In loops running many times, PARSE using columns can save you some CPU. When specifying column delimiters, the number to the left of the variable name is the start column, while the number on the right is the end column, minus one. As you can see in the example, sometimes this can be misleading: %SPLITIT ABCDEFGHIJKLMNOP PARSE ARG 1 VAR1 5 VAR2 9 VAR3 13 In this example, the variables are stored as: •VAR1 = ABCD; •VAR2 = EFGH; •VAR3 = IJKL When using both column numbers everywhere, it can be worse: %SPLITIT ABCDEFGHIJKLMNOP PARSE ARG 1 VAR1 5 7 VAR2 9 11 VAR3 13 In this example, the variables are stored as: •VAR1 = ABCD; •VAR2 = GH; •VAR3 = KL Please don’t give up on this just because it looks complicated. You’ll see why below. 8
  • 9. PARSE using Literals When parsing using a literal, you can use any literal. Consider this: LINE1 = “ DEFINE CLUSTER(NAME(YOUR.DSN) –” PARSE VAR LINE1 JUNK “NAME(” DSN “)” . After the PARSE the variable DSN contains “YOUR.DSN”. However, you can skip the LINE1 assignment statement by using PARSE VALUE and the literal itself. PARSE VALUE PARSE VALUE needs the keyword WITH to tell where the template begins. This can be used with functions as well as variables. PARSE VALUE “This is a sample” With A B C D PARSE VALUE TIME() With HRS “:” MIN “:” SEC PARSE SOURCE PARSE SOURCE asks TSO for information about the way the exec was run. PARSE SOURCE OP_SYSTEM HOW_CALLED EXEC_NAME, DD_NAME, DATASET_NAME AS_CALLED, DEFAULT_ADDRESS, NAME_OF_ADDRESS_SPACE where: •OP_SYSTEM is TSO •HOW_CALLED is either COMMAND, SUBROUTINE, or FUNCTION •EXEC_NAME is the name of the exec in upper case •DD_NAME the exec was found in SYSEXEC or SYSPROC •DATASET_NAME is the DSN the exec was in when called implicitly •AS_CALLED is the name the exec was invoked by. May be in lower case when called implicitly •DEFAULT_ADDRESS is the initial address environment. This is usually TSO, MVS, ISPEXEC (i. e. ISPF), or ISREDIT (the ISPF Editor) •NAME_OF_ADDRESS_SPACE is one of MVS, TSO, or ISPF. 9
  • 10. Parsing continued Here are several examples of using PARSE rather than a built-in functions or many assignments. Take the third word from the variable INVAR and call it WORD3. The PARSE instructions are much faster, and if you don’t need the previous two words use the “.”. 1. WORD3 = WORD(INVAR,3) 2. WORD3 = SUBWORD(INVAR,1,3) 3. PARSE VAR INVAR JUNK1 JUNK2 WORD3 . 4. PARSE VAR INVAR . . WORD3 . Take the left five bytes from the variable AMBI and call it LEFTY. LEFT is faster then SUBSTR. Again, the PARSE instruction are much faster than either. 1. LEFTY = Left(AMBI,5) 2. LEFTY = SUBSTR(AMBI,1,5) 3. PARSE VAR AMBI 1 LEFTY 6 . Take the right five bytes from the variable AMBI and call it RT. Note there is an extra step for the SUBSTR example, and no PARSE example. Parse could be used if you knew, when you coded, how long AMBI would be. The single function call is faster. 1. RT = Right(AMBI,5) 2. ST = Length(AMBI) – 4; RT = SUBSTR(AMBI,ST,5) Take the middle five bytes from the variable AMBI, where the middle is known before coding. The PARSE instruction is faster. 1. MID = SUBSTR(AMBI,5,5) 2. PARSE VAR AMBI . 5 MID 10 . Set A, B, C, and D to null. The PARSE instruction is faster. You can also use this method to set all variables to zero. 1. A = “”; B = “”; C = “”; D = “” 2. PARSE VALUE “” WITH A B C D 3. PARSE VALUE “0 0 0 0” WITH A B C D 10
  • 11. Exercise #3 Write an exec to parse the date into month, day, and year. Use Parse Value. Redisplay the date in the following formats: MM.DD.YY (USA), DD.MM.YY (European), YY.DD.MM, and YY.MM.DD (ordered). The command to get the USA-style date (MM/DD/YY) is DATE(”U”). 11
  • 12. Trace Output Line Codes When running a program in trace mode, the REXX interpreter prefixes each line of trace output with a three character symbol. These symbols tell you what is happening at each stage of REXX’s handling of the statement. The three-character codes displayed during tracing are: •*-* original program line •+++ trace message •>>> result of an expression during TRACE R •>.> value assigned to a placeholder (period) during parsing •>C> resolved name of a compound variable •>F> result of a function call •>L> a literal •>O> result of an operation on two terms •>P> result of a prefix operation •>V> contents of a variable OutTrap Restrictions Not all output from TSO commands can be trapped by REXX. Whether you can trap output depends on how the command sends its output to the screen. Outtrap cannot capture lines produced by the following. •TPUT •WTO macro •Messages issued by REXX (IRX*) •Trace output messages An example of a command you cannot capture the output for is CONCAT. Try it. 12
  • 13. Arithmetic Operations in REXX As you know, there are certain characters you can use in any arithmetic statement, and some you can’t. In the first half of the class we saw the basic arithmetic operators. Parentheses can be used, as well as spaces to separate terms, periods for decimals, and the letters A – F for hexadecimal numbers. Here is a clever way to make sure your expression (EXPR) contains only valid characters: If Verify(EXPR,'1234567890ABCDEF+-*/% ()') = 0 The VERIFY function checks to see if all the characters in the first string are contained in the second string. An Unusual Type of DO loop On a DO from X to Y loop, the FOR clause enforces a specific count: Do I = 1.234 To 9943.2323 by .3203 For 22 Say “I is” I End The FOR tells REXX to run this loop 22 times no matter what else happens. 13
  • 14. Talking to the Environment For each ADDRESS command, REXX opens a command processor link. To improve performance, then, it is good practice to use as few ADDRESS commands as possible. For example, the following code is a subroutine to format and submit a piece of JCL. Arg JCLSKEL Address ISPEXEC DO0JOBNM = $JobName() "FTOPEN TEMP" "VGET (ZTEMPF) SHARED" "FTINCL" JCLSKEL "FTCLOSE" ZEDSMSG = DO0JOBNM "SUBMITTED" ZEDLMSG = "Batch job” DO0JOBNM “submitted.” "SETMSG MSG(ISRZ000)" Address TSO "SUBMIT DATASET('"ZTEMPF"')" Return This is the same code, with the ADDRESS commands as part of the code. Arg JCLSKEL DO0JOBNM = $JobName() Address ISPEXEC "FTOPEN TEMP" Address ISPEXEC "VGET (ZTEMPF) SHARED" Address ISPEXEC "FTINCL" JCLSKEL Address ISPEXEC "FTCLOSE" ZEDSMSG = "JOB SUBMITTED" ZEDLMSG = "Batch job” DO0JOBNM “was submitted.” Address ISPEXEC "SETMSG MSG(ISRZ000)" Address TSO "SUBMIT DATASET('"ZTEMPF"')" Return While both produce the same results, the first one uses less CPU and runs slightly faster because there’s no address switching except for the SUBMIT command passed to TSO. For short execs the difference is so small it’s almost meaningless, but for many consecutive calls, or calls within a loop, the difference is measurable. 14
  • 15. More on Stem Variables So far we’ve only seen stem variables with a single index that’s always a number. Stem variables are more flexible than that. For example, although a stem variable has a stem and an index, a single entry of a stem variable is just like any other, non-stem variable. We can set any single entry of a stem variable to be a stem variable using a second index. Here is a piece of code to initializes a chessboard, a two-dimensional array of eight ranks and eight files. Do R = 1 To 8 Do F = 1 To 8 ChessBoard.R.F = "." End End The stem index does not have to be a number. REXX understands the use of a letter rather than a number. For example, try this. /* REXX */ Do Forever Say “What is your name (END to stop)?” Pull NAME If NAME = END Then Leave Say “Thanks,” NAME “, how old are you?” Pull HOW_OLD If DataType(HOW_OLD) <> “NUM” Then Say “Not valid age, rejected.” Else AGE.NAME = HOW_OLD End Do Forever Say “Whose age would you like”, “(END to stop)?” Pull NAME If NAME = “END” Then Leave If SYMBOL(‘AGE.NAME’) <> “VAR” Then Say “Name not in memory.” Else Say NAME “is” AGE.NAME “years old.” End 15
  • 16. System Variables REXX provides two sets of system variables. The first set is SYSVAR. 1. SYSPREF: the prefix assigned to not-fully-qualified dataset names. 2. SYSPROC: the logon procedure for the current session. 3. SYSUID: the user ID of the person logged on. 4. SYSLTERM and SYSWTERM: the length and width of the current terminal screen. In batch, SYSLTERM returns 0 and SYSWTERM returns 132. 5. SYSENV: returns FORE or BACK. 6. SYSICMD: returns the name of the running, implicitly called exec. If the call was explicit, SYSICMD returns null. 7. SYSISPF: returns ACTIVE or NOT ACTIVE. 8. SYSNEST: if the current program was called from another, SYSNEST returns YES; otherwise it returns NO. 9. SYSPCMD: the most recently processed TSO-E command processor. The initial value of SYSPCMD may be EXEC (if the EXEC command was used) or EDIT (if the EXEC subcommand of EDIT was used). 10. SYSSCMD: the most recently processed TSO-E subcommand processor. The initial value of SYSPCMD may be null (if the EXEC command was used) or EXEC (if the EXEC subcommand of EDIT was used). 11. SYSCPU: the number of CPU seconds used this session. 12. SYSSRV: the number of service units used this session. 13. SYSHSM: the status of DFHSM. Returns AVAILABLE or null. 14. SYSJES: the name and level of JES. 15. SYSLRACF: the level of RACF. If RACF is not installed, this is null. 16. SYSRACF: returns AVAILABLE, NOT AVAILABLE, or NOT INSTALLED. 17. SYSNODE: the JES node name. This returns either the JES node name, the string -INACTIVE-, or the string -DOWNLEVEL- if the subsystem is neither JES2 SP4.3 or later, nor JES3 SP5.1.1 or later. 18. SYSTERMID: the terminal ID, or null if batch. 19. SYSTSOE: the level of TSO installed. For OS/390 Version 2, Release 4 and later, SYSTSOE returns 2060. 20. SYSDTERM: If double-byte character set (DBCS) is enabled, returns YES. 21. SYSKTERM: if Katakana character set is enabled, returns YES. 22. SYSPLANG and SYSSLANG: returns the 3-byte primary and secondary language settings. 23. SOLDISP and UNSDISP: show solicited (operator replies) or unsolicited (operator messages) messages on the user’s terminal. YES or NO. 24. SOLNUM and UNSNUM: the size of the message table. 25. MFTIME: show a time stamp with each message. YES or NO. 26. MFOSNM: show originating system name with each message. YES or NO. 27. MFJOB: display the originating job name. YES or NO. 28. MFSNMJBX: did the user request to NOT show the originating job and system names of the message. YES means to NOT show the names; NO means to show them. 16
  • 17. System Variables: SYSVAR notes 1. SYSVAR can only be used in TSO/E environments (i. e. not VM, OS/2, etc.). 2. SYSPROC has three different responses, depending on where it was called: o Foreground: returns the logon procedure name. o Batch: returns INIT because the job has an initiator. o 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." 17
  • 18. System Variables: MVSVAR The second set of system variables are the MVS variables. •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. Exercise #4 Write an exec to print a calendar month, given the name of the month and the name of the first day of the month. For example, CALPRT JANUARY MONDAY. Exercise #5 Write an exec to fill a 3X3 array with “#” by having the user enter a row name (letter) and column number. If the row and column are “equal”, fill the slot with a “$” instead. Print the array at the end. The array should look like this: 1 2 3 A $ # # B # $ # C # # $ 18
  • 19. Other Performance Concerns Here are some other performance tips. They are not in any particular order. Remember, you get more bang for your buck in a loop than in a single instruction. Don’t sacrifice readability for speed unless you absolutely need to. o Shorter variable names are faster. Use abbreviated names if they’re clear enough. For example, Social_Security_Number is valid, but SSN is just as clear and more efficient. o Drop large stem arrays when you’re done with them. o When known ahead of time, use literals or constants rather than variables. o Don’t use external subroutines in loops. If a subroutine is necessary, code it in the exec. o Code subroutines as close to the call as possible (without sacrificing readability). Use SIGNAL to get around the code, if necessary. o When grouping subroutines, place the most often used routine first. o Consider a subroutine when a large number of statements are needed for something that is often NOT executed. o When calling subroutines or functions don’t specify the defaults, and pass literals rather than variables when possible. o Avoid using functions within functions. For example, a) is more efficient than b) despite using more lines of code: a)PT = right(strip(cmddata),3) b)PL = strip(cmddata); PT = right(PL,3) o Avoid DATE and TIME functions in loops and subroutines if possible. o Instructions are about twice as fast as built-in functions. o Instructions are about 100 times as fast as external commands. 19
  • 20. Putting It All Together You can combine functions to manage records. Here are some examples. Suppose you want to scan records in JCL to remove a now-defunct symbolic parameter. For this example, the symbolic parameter to remove is CVOL. We don’t care what the value given is, we just want to remove it. In EDIT you can do a FIND on CVOL, then delete characters up to the next comma. Here’s a way to do it automatically in REXX. 1.MCVLOC = Pos("MODCVOL",SYMB) 2.LHLen = MCVLOC - 1 3.RHLen = 71 - MCVLOC 4.LHalf = Substr(Source.SOURCEI,1,LHLen) 5.RHalf = Substr(Source.SOURCEI,MCVLOC,RHLen)) 6.Parse Var RHalf MODCVOL "," RRHalf 7. If Right(Strip(Rhalf),1) = "," 8. Then SYMB = LHalf","RRHalf 9. Else SYMB = LHalf||RRHalf 10.FLen = Length(SYMB) 11.If Right(SYMB,2) = ",," 12. Then SYMB = Substr(SYMB,1,FLen-1) 1. Get the location of CVOL on the line. 2. The length of the line up to CVOL is the location of CVOL minus 1 3. The length of the line from CVOL to the end is 71 minus the location of CVOL because column 72 is the continuation column. 4. The left “half” of the line starts at Byte 1 and continues for the length as found in Step 2. 5. Start the right “half” of the line from CVOL through the end. 6. Break up the right half into the part containing CVOL and the rest by looking for the comma after the CVOL parameter. 7. If the last non-blank character in the right half is a comma: 8. Yes, it was, add the comma when concatenating the halves together; 9. No comma, concatenate without one. 10. Just in case CVOL was the last thing on the line, get the length of the entire line. 11. If CVOL was the last thing on the line, the added comma for the concatenation was wrong. Remove it by taking all the characters up to, but not including, the comma (see 12 also). 20
  • 21. Combinations of Functions continued Suppose you want neater JCL. Your idea is to force the EXEC or DD to always make room for an 8-byte step name or DD name, followed by one space, followed by either “DD” or “EXEC.” All other arguments follow the “DD” or “EXEC” after one space. 1. NLine = "//"Copies(" ",70) 2. Select 3. When WordPos("EXEC",Source.SOURCEI) ¬= 0 Then Do 4. Parse Var Source.SOURCEI SNX EXECS PROCS 5. NLine = Overlay(SNX,NLine,1) 6. NLine = Overlay("EXEC",NLine,12) 7. NLine = Overlay(PROCS,NLine,17) 8. End 9. When WordPos("DD",Source.SOURCEI) ¬= 0 Then Do 10. Parse Var Source.SOURCEI DDNX DDS DDARGS 11. NLine = Overlay(DDNX,NLine,1) 12. NLine = Overlay("DD",NLine,12) 13. NLine = Overlay(DDARGS,NLine,15) 14. End 15.End 16.Source.SourceI = NLine 1. Create a default JCL line of “//” followed by 70 blanks. 2. Start a Select group. 3. If this is an EXEC statement (“EXEC” surrounded by blanks), do steps 4 – 7. 4. Parse the line into “//<stepname>”, the word EXEC, and anything following EXEC. 5. Put the “//<stepname>” on the new line beginning in Column 1. 6. Put the word “EXEC” on the line in position 12. This leaves room for the “//” plus an 8-character step name. 7. Put the remainder of the EXEC line one space after the word EXEC. 8. End the EXEC group. 9. If this is a DD statement (“DD” surrounded by blanks), do steps 10 – 13. 10. Parse the LINE variable into the “//<DD name>”, the word DD, and anything following DD. “DD name” can be all blanks. 11. Put the “//<DD name>” on the new line beginning in Column 1. 12. Put the word “DD” on the line in position 12. This leaves room for the “//” plus an 8-character DD name. 13. Put the remainder of the DD line one space after the word DD. 14. End the DD group. 15. End the Select group. 16. Set the JCL line to its new, formatted version. 21
  • 22. Combinations of Functions continued At times, people doing data security need to scan their datasets to see what is and what is not protected, and what profiles are in WARNING mode (i.e. the profile exists but is not observed). Here is some code to do this check against any dataset level. Please note the code is split onto two pages, with the explanation on a third page. 1. /* REXX Checks datasets under LEVEL for RACF. */ 2. Arg LEVEL 3. GOODPROF = 0; WARNPROF = 0; NOPROF = 0; TotalDSN = 0 4. foo = outtrap(LCL.) 5. "LISTC LEVEL("LEVEL")" 6. foo = outtrap(Off) 7. OutLine.1 = "The profile name, its UACC, and warning mode”, “is displayed." 8. OutLine.2 = "'????' is displayed if you don't have access”, “to list the profile.” 9. OutLine.3 = "A warning message is displayed if there is no”, “profile." 10.OutLine.4 = “” 11./* Skip 5 through 13 for final tallies, 14 is header. */ 12.OutLine.14 = Copies(" ",79) 13.OutLine.14 = Overlay("DSN",OutLine.14,1) 14.OutLine.14 = Overlay("PROFILE",OutLine.14,36) 15.OutLine.14 = Overlay("UACC",OutLine.14,64) 16.OutLine.14 = Overlay("WARNING?",OutLine.14,72) 17.OutIX = 14 18.Do L = 1 to LCL.0 19. Select 20. When Word(LCL.L,1) = "NONVSAM" Then RADS = Word(LCL.L,3) 21. When Word(LCL.L,1) = "CLUSTER" Then RADS = Word(LCL.L,3) 22. When Word(LCL.L,1) = "AIX” Then RADS = Word(LCL.L,3) 23. Otherwise Iterate 24. End 25. foo = outtrap(RI.,'*') 26. "LD DA('"RADS"') GENERIC ALL" 27. If Word(RI.1,1) = "ICH35003I" Then Do /* No profile */ 28. NOPROF = NOPROF + 1 29. OutIX = OutIX + 1 30. OutLine.OutIX = “Dataset" RADS "not protected by RACF." 31. Iterate L 32. End 33. Do I = 1 To RI.0 34. Parse Var RI.1 . . . PROFILE . 35. Parse Var RI.5 . . UACC WARN . 36. End 37. If RI.0 = 1 Then Do 38. PROFILE = "????"; WARN = "????"; UACC = "????" 39. End 22
  • 23. RIB exec Page 2 This section of the exec determines the profile type and if it is in warning. Finally, the exec creates an output report. 40. Select 41. When WARN = "NO" Then Do 42. W = "HARDCHK"; GOODPROF = GOODPROF + 1 43. End 44. When WARN = "YES" Then Do 45. W = "WARNING"; WARNPROF = WARNPROF + 1 46. End 47. Otherwise W = "UNKNOWN" 48. End 49. OutIX = OutIX + 1 50. OutLine.OutIX = Copies(" ",79) 51. OutLine.OutIX = Overlay(RACFDS,OutLine.OutIX,1) 52. OutLine.OutIX = Overlay(PROFILE,OutLine.OutIX,36) 53. OutLine.OutIX = Overlay(UACC,OutLine.OutIX,64) 54. OutLine.OutIX = Overlay(W,OutLine.OutIX,72) 55.End L /* Do group from Line 19 */ 56.UNKNOWN = TotalDSN - GOODPROF - WARNPROF - NOPROF 57.PROFILES = GOODPROF + WARNPROF 58. OutLine.5 = "RACF report for dataset level" LEVEL":" 59. OutLine.6 = "" 60. OutLine.7 = "TOTAL DATASETS: ", 61. Right(" "TotalDSN,6) 62. OutLine.8 = "DATASETS WITH PROFILES: ", 63. Right(" "PROFILES,6) 64. OutLine.9 = "DATASETS IN WARNING: ", 65. Right(" "WARNPROF,6) 66. OutLine.10 = "PROTECTED DATASETS: ", 67. Right(" "GOODPROF,6) 68. OutLine.11 = "UNPROTECTED DATASETS: ", 69. Right(" "NOPROF,6) 70. OutLine.12 = "INSUFFICIENT AUTHORITY: ", 71. Right(" "UNKNOWN,6) 72. OutLine.0 = OutIX 73."ATTRIB FB80 RECFM(F B) LRECL(80)" 74.OutDSN = LEVEL".DATA" 75. "DELETE" OUTDSN 76. "ALLOC DA("OUTDSN") F(OUTFILE) NEW SPACE(5 5) TRACKS”, 77. “REUSE USING(FB80) RELEASE" 78."EXECIO " OutLine.0 " DISKW OUTFILE (STEM OutLine. FINIS" 79. Say 80.Say "Output written to" OutDSN"." 81.“FREE F(OUTFILE) ATTR(FB80)” 82.Exit 23
  • 24. RIB exec Page 3 A brief explanation of some of the code follows. Line 3 initializes all counter fields for the output totals. Lines 4 – 6 capture LISTC LEVEL data to get the datasets to check. Lines 7 – 16 set the headers. Note several lines are left blank at this time. This makes room for the summary lines, whose values cannot be determined until the exec ends. The OVERLAY function lets me align the headers to specific columns. Finally, Line 17 points the output stem index to the first blank line after the dataset report header line. The select group at Line 19 keeps non-VSAM and VSAM dataset names, and skips all other lines from the LISTC. Line 26 does the RACF command LISTDSD to get the profile information. Lines 27 – 32 writes a note if there is no profile to list. Lines 33 – 36 pull the useful information from the profile. Lines 37 – 39 writes a note if you don’t have the authority to list the profile. The select group at Line 40 notes whether the profile is in WARNING or not. Lines 49 – 54 format the report line for this dataset. Lines 56 – 72 format the summary section. This section uses the blank lines we left back on lines 7 – 16. The remaining lines allocate the output dataset and write the report stem to the output dataset. 24
  • 25. This Page Intentionally Left Blank 25
  • 26. Appendix A: A Few Simple Edit Macros These are a few, simple edit macros to show how many macros are just a string of edit commands you might use frequently. Each is explained in some detail. Initial Edit Macro When starting any edit session, ISPF can run a macro on the dataset before turning control over to you. This macro is called an initial edit macro. You can specify an initial macro on the edit screen. If you want the macro to run every time, though, it’s just as easy to tell ISPF what it is. This is done in two easy steps. STEP 1: Code the macro. Here is an example. The code is COURIER; comments are made in Arial and underscored. Address ISREDIT "MACRO" "PROFILE UNLOCK" - unlock the edit profile of the current dataset. "BOUNDS" - Reset BOUNDS to the full LRECL. "NULLS ON" - Lets me insert (ignores spaces on end of lines). "HILITE ON" - Turns on highlighting CURMBR = "" "(CURMBR) = MEMBER" Checks if editing a PDS. If so, turn on STATS. If CURMBR <> "" Then "STATS ON" "RESET" - Clear all messages. Exit STEP 2: set the initial macro. You must use the VPUT Dialog Manager service to do this. So you don’t have to remember (or know) how to do that, run this command: SETZIMAC <name of your macro> Your CLIST or REXX macro must be in the SYSPROC or SYSEXEC concatenation. If your macro is a program, it must be in the STEPLIB concatenation. Note: some ISPF dialogs run in their own application ID. Should you enter a dialog and your edit macro doesn’t work, try running SETZIMAC again. 26
  • 27. More Simple Edit Macros These two edit macros change all upper-case characters to lower-case, or vice-versa. Note they’re just a single edit command. This could just as easily be set to a function key. Address ISREDIT "MACRO" "C P'>' P'<' ALL" Exit Address ISREDIT "MACRO" "C P'<' P'>' ALL" Exit This macro excludes all lines, then does a find on a passed string. The purpose is to see only what you want to see. Note the passed parameter may include quotes, column numbers, and other FIND operands (except ALL, of course). For sample calls, see below the macro. Address ISREDIT "MACRO (FPARM)" "EXCLUDE ALL" "FIND ALL "FPARM Exit 0 Sample calls: XFS ISDODEA shows all occurrences of this TSO user ID. XFS WORD EXEC shows all EXEC lines in a JCL dataset. XFS WORD DD 13 71 shows all DD statements not aligned at column 12. 27
  • 28. Appendix B: Solutions to Exercises Exercise 1, page 6 Modify the exec you wrote in Exercise #28 to use a noun switch instead of expression tests when deciding what to do with the dataset. /* REXX passes args to LISTD with program stack. */ 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 DSNExists = 1 Else DSNExists = 0 If DSNExists Then Do Queue DSN DUMMY = Prompt("ON") "LISTD" End Else Say DSN "does not exist." 28
  • 29. Exercise 2, Page 7 Modify the exec you wrote in Exercise #16 from the first course to accept the unit and amount values as keywords. Only accept two arguments. If one argument is bad, display the syntax and exit. /* REXX Metric Converter */ Arg Parm.1 Parm.2 Do I = 1 to 2 Select When Left(Parm.I,1) = "U" Then Parse Var Parm.I Junk "(" UNIT ")" When DataType(Parm.I) = "NUM" Then AMT = Parm.I Otherwise Do Say "Invalid parm entered: " Parm.I"." Say "SYNTAX : CNVT UNIT(...) and a number." Say "UNIT must be one of liter, quart, mile, or", "kilometer." Exit End End End 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." End 29
  • 30. Exercise 3, Page 11 Write an exec to parse the date into month, day, and year. Display them in the following formats: MM/DD/YY, DD/MM/YY, YY/DD/MM, and YY/MM/DD. The command to get the USA-style date (MM/DD/YY) is DATE(”U”). /* REXX Date formatter */ Parse Value Date("U") With Month "/" Day "/" Year "CLRSCRN" Say "Today's date is: " Date("U")", or" Say "USA: " Month"."Day"."Year";" Say "European: " Day"."Month"."Year";" Say "Year, then day: " Year"."Day"."Month";" Say "Ordered: " Year"."Month"."Day"." 30
  • 31. Exercise 4, Page 19 Write an exec to print a calendar month, given the name of the month and the name of the first day of the month. Assume the first day of the week is Sunday. For example, CALPRT JANUARY MONDAY should start the calendar on Monday, January 1. Use the CENTER function to display the month name. /* REXX print a calendar month. */ Arg Month Day /*-------------------------------* * Init month table with blanks. * *-------------------------------*/ Do I = 1 to 6 Do J = 1 to 7 Week.I.J = " " End J End I /*------------------------------------------* * Init fields: * * WeekDay is the day of the week; * * DayNumber is the day of the month; * *------------------------------------------*/ WeekDay = 1; DayNumber = 1 /*---------------------------------* * Set the first day of the month. * *---------------------------------*/ Select When Day = "SUNDAY" Then WeekDay = 1 When Day = "MONDAY" Then WeekDay = 2 When Day = "TUESDAY" Then WeekDay = 3 When Day = "WEDNESDAY" Then WeekDay = 4 When Day = "THURSDAY" Then WeekDay = 5 When Day = "FRIDAY" Then WeekDay = 6 When Day = "SATURDAY" Then WeekDay = 7 Otherwise Do Say "Invalid day" Day"." Exit End End Continued on the next page 31
  • 32. Exercise 4, Page 19, answer continued /*---------------------------------------* * Load the table. Start at the current * * weekday, then reset it after Week 1. * * Skip out when at end of month. * *---------------------------------------*/ Do WeekNumber = 1 to 6 Do WeekDay = WeekDay to 7 If Length(DayNumber) = 1 Then DayWord = " "DayNumber Else DayWord = DayNumber Week.WeekNumber.WeekDay = DayWord DayNumber = DayNumber + 1 If DayNumber > DayLimit Then Signal EndOfMonth End WeekDay = 1 End EndOfMonth: "CLRSCRN" Say Say Center(Month,21) Say " S M T W T F S" Do WeekNumber = 1 to 6 Say " "Week.WeekNumber.1 Week.WeekNumber.2, Week.WeekNumber.3 Week.WeekNumber.4 Week.WeekNumber.5, Week.WeekNumber.6 Week.WeekNumber.7 End 32
  • 33. Exercise 5, Page 19 Write an exec to fill a 3X3 array with “#” by having the user enter a row name (letter) and column number. If the row and column are “equal”, fill the slot with a “$” instead. Print the array at the end. The array should look like this: 1 2 3 A $ # # B # $ # C # # $ HINT: you will need the TRANSLATE function to do this exec cleanly. Guidance is provided if desired. /* REXX advanced course, exercise 6 */ Do I = 1 to 3 Select When I = 1 Then Row = "A" When I = 2 Then Row = "B" When I = 3 Then Row = "C" End Do Col = 1 to 3 Array.Row.Col = " " End Col End I /*------------------------------------* * SLOTS is the number of full slots; * * ArrayFull says the array is full. * *------------------------------------*/ Slots = 0; ArrayFull = 0 Continued on the next page 33
  • 34. Exercise 5, Page 19 continued /*---------------------------* * Loop until array is full. * *---------------------------*/ Do Until ArrayFull Say "Enter a row (A, B, or C) and a column (1, 2, or 3)." Pull Row Col Say If Pos(Row,"ABC") = 0 Then Do Say "Row" Row "invalid, try again." Iterate End If Pos(Col,"123") = 0 Then Do Say "Column" Col "invalid, try again." Iterate End If Array.Row.Col ¬= " " Then Do Say Row "and" Col "already used, try again." Iterate End RowNum = Translate(Row,"123","ABC") If RowNum = Col Then Array.Row.Col = "$" Else Array.Row.Col = "#" Slots = Slots + 1 If Slots = 9 Then ArrayFull = 1 End /*--------------------* * Display the array. * *--------------------*/ Row = "A" Say "A" Array.Row.1 Array.Row.2 Array.Row.3 Row = "B" Say "B" Array.Row.1 Array.Row.2 Array.Row.3 Row = "C" Say "C" Array.Row.1 Array.Row.2 Array.Row.3 34