Rexxprogrammingii 1276412725466 Phpapp01
Rexxprogrammingii 1276412725466 Phpapp01
Table of Contents
Introduction . . . . . . . . . . . . . . . . . Goals of the course. . . . . . . . . . . . . More on syntax Comments . . . . . . . . . . . . . . . . . . Handling Hexadecimals. . . . . . . . . . . . More on If/Then Using words to indicate success or failure . Boolean operators. . . . . . . . . . . . . . Passing Parameters Using keyword parameters . . . . . . . . . . Parsing Using Using PARSE PARSE columns. literals VALUE. . SOURCE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 3 4 4 4 5 5 7 8 8
9 . 9 . 10 . 10 . 10 12 12 13 14 15
Trace Output Line Codes OutTrap Restrictions Arithmetic and verifying expressions Addressing Environments Stem Variables
System Information 16 SYSVAR . . . . . . . . . . . . . . . . . . . 16 MVSVAR . . . . . . . . . . . . . . . . . . . 18 Other Performance Concerns Putting it all together: Appendix A, Page 26: Appendix B, Page 28: real-world examples 19 20
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.
Say 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, dont 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 youre running on. How it works: First, convert the 4 bytes of system storage area at location x10 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))
Conditional Operators
As we know, the IF statement follows a standard format:
Dan = 0 Say Enter name Pull Name If Name = Dan Then Dan = 1 If Dan Then Say Hi, Mr. ODea! 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. Its 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, its 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 */
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))"
Heres 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 dont 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.
/* 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 LC LC LC
ENT(ISDODEA.TEST.DATA) ALL GDG GDG ENT(ISDODEA.TEST.DATA) ALL ALL GDG ENT(ISDODEA.TEST.DATA) 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.
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.
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 dont need the previous two words use the .. 1. 2. 3. 4. WORD3 WORD3 PARSE PARSE = WORD(INVAR,3) = SUBWORD(INVAR,1,3) VAR INVAR JUNK1 JUNK2 WORD3 . 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
*-* +++ >>> >.> >C> >F> >L> >O> >P> >V>
original program line trace message result of an expression during TRACE R value assigned to a placeholder (period) during parsing resolved name of a compound variable result of a function call a literal result of an operation on two terms result of a prefix operation 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
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.
13
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 theres no address switching except for the SUBMIT command passed to TSO. For short execs the difference is so small its almost meaningless, but for many consecutive calls, or calls within a loop, the difference is measurable.
14
/* 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. 2. 3. 4. 5. 6. 7. 8. 9. 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 and SYSWTERM: the length and width of the current terminal screen. In batch, SYSLTERM returns 0 and SYSWTERM returns 132. SYSENV: returns FORE or BACK. 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: if the current program was called from another, SYSNEST returns YES; otherwise it returns NO. 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). 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). 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: 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. SYSTERMID: the terminal ID, or null if batch. SYSTSOE: the level of TSO installed. For OS/390 Version 2, Release 4 and later, SYSTSOE returns 2060. SYSDTERM: If double-byte character set (DBCS) is enabled, returns YES. SYSKTERM: if Katakana character set is enabled, returns YES. SYSPLANG and SYSSLANG: returns the 3-byte primary and secondary language settings. SOLDISP and UNSDISP: show solicited (operator replies) or unsolicited (operator messages) messages on the users terminal. YES or NO. SOLNUM and UNSNUM: the size of the 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 to NOT show the originating job and system names of the message. YES means to NOT show the names; NO means to show them.
10.
18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28.
16
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
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: A B C 1 $ # # 2 # $ # 3 # # $
18
o Shorter variable names are faster. Use abbreviated names if theyre clear enough.
For example, Social_Security_Number is valid, but SSN is just as clear and more efficient.
o Drop large stem arrays when youre done with them. o When known ahead of time, use literals or constants rather than variables. o Dont 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 dont 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
MCVLOC = Pos("MODCVOL",SYMB) LHLen = MCVLOC - 1 RHLen = 71 - MCVLOC LHalf = Substr(Source.SOURCEI,1,LHLen) RHalf = Substr(Source.SOURCEI,MCVLOC,RHLen)) Parse Var RHalf MODCVOL "," RRHalf If Right(Strip(Rhalf),1) = "," Then SYMB = LHalf","RRHalf Else SYMB = LHalf||RRHalf FLen = Length(SYMB) If Right(SYMB,2) = ",," Then SYMB = Substr(SYMB,1,FLen-1)
Get the location of CVOL on the line. The length of the line up to CVOL is the location of CVOL minus 1 The length of the line from CVOL to the end is 71 minus the location of CVOL because column 72 is the continuation column. The left half of the line starts at Byte 1 and continues for the length as found in Step 2. Start the right half of the line from CVOL through the end. Break up the right half into the part containing CVOL and the rest by looking for the comma after the CVOL parameter. If the last non-blank character in the right half is a comma: Yes, it was, add the comma when concatenating the halves together; No comma, concatenate without one. Just in case CVOL was the last thing on the line, get the length of the entire line. 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
22
23
24
25
STEP 2: set the initial macro. You must use the VPUT Dialog Manager service to do this. So you dont 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 doesnt work, try running SETZIMAC again.
26
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:
shows all occurrences of this TSO user ID. shows all EXEC lines in a JCL dataset. shows all DD statements not aligned at column 12.
27
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" When UNIT = "QUART" Then Say AMT UNIT "is" When UNIT = "MILE" Then Say AMT UNIT "is" When UNIT = "KILOMETER" Then Say AMT UNIT "is" 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 When Day = "MONDAY" Then WeekDay When Day = "TUESDAY" Then WeekDay When Day = "WEDNESDAY" Then WeekDay When Day = "THURSDAY" Then WeekDay When Day = "FRIDAY" Then WeekDay When Day = "SATURDAY" Then WeekDay Otherwise Do Say "Invalid day" Day"." Exit End End
= = = = = = =
1 2 3 4 5 6 7
31
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: A B C 1 $ # # 2 # $ # 3 # # $
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
33
34