I looked on the internet, to find something that would allow me to make the same change to all members of a PDS, and there seemed to be several options, IPOUPDTE, CPPUPDTE, PDSUPDTE, written in assembler many years ago.
I had written an equivalent program in C, but I no longer have the source. I found it as quick to write a solution using Rexx and an ISPF edit, than to find a solution on the internet. The solution is much more flexible, and can do so much more.
The processing has two stages
- Code to iterate over each member of the PDS, and invoke ISPF EDIT on each member
- An edit macro to make the changes.
Code to iterate over each member
ISPF 3.4 DSLIST displays data sets
DSLIST - Data Sets Matching COLIN.J*
Command ===>
Command - Enter "/" to select action
-------------------------------------------
AA COLIN.JCL
COLIN.JCL.DCOLLECT.OUTPUT
You can enter local commands at the front of each line. The commands can be ISPF special (D for Delete, C for Catalog, R for Rename), or TSO commands, where a TSO command can be a Rexx exec in the ISPEXEC concatenation. (You can use the TSO ISRDDN command to display the data sets allocated to your session)
I have a Rexx exec called AA.
Access the parameters passed to the rexx
/* rexx */
ADDRESS ISPEXEC "VGET (ZDLDSN) SHARED"
ADDRESS ISPEXEC "VGET (ZDLCMD) SHARED"
ADDRESS ISPEXEC "VGET (ZDLLCMD) SHARED"
say "ZDLDSN " ZDLDSN
say "ZDLCMD " ZDLCMD
say "ZDLLCMD " ZDLLCMD
With the line command
Command - Enter "/" to select action
--------------------------------------
AA 99 COLIN.JCL
The Rexx produces
ZDLDSN COLIN.JCL
ZDLCMD AA 99
ZDLLCMD AA 99 'COLIN.JCL'
- ZDLDSN is the data set name
- ZDLCMD is the line command and any data
- ZDLLCMD is the (Long) combined command and the data set
With the line command
aa 99 / Zyx
The dataset name is substituted for /, and the output is
ZDLDSN COLIN.JCL
ZDLCMD AA 99 / Z
ZDLLCMD AA 99 'COLIN.JCL' Zyx
This means you can pass parameters to your Rexx.
Process every member
The data set name is in variable ZDLDSN. When you use it, you should quote it because your userid may/may not have prefix on, which puts your userid on the front of every data set you use. Without quotes, it could not find dataset COLIN.COLIN.JCL
- The LMINIT command returns a handle in the DATAID(handle) variable, to refer to the data set. My handle is called data1.
- The LMOPEN command opens the dataset associated with the handle.
- The LMMLIST command iterates through the list, starting with a blank member name which indicates start with the first.
- The EDIT command invokes ISPF edit on the member, and passes the name of an ISPF EDIT macro to use. In my case the macro is called FOREACH.
Address ispexec "LMINIT DATAID(data1) dataset('"ZDLDSN"')"
if rc <> 0 then
do /* report the errors */
say ZERRSM ZERRLM
return 8
end
Address ispexec 'LMOPEN DATAID('data1') OPTION(INPUT)'
member = ' '
lmrc = 0
/*********************************************************************/
/* Loop through all members in the PDS, issuing the EDIT service for */
/* each. */
/*********************************************************************/
Do i = 1 by 1
Address ispexec 'LMMLIST DATAID('data1') OPTION(LIST),
MEMBER(MEMBER) STATS(NO)'
If rc = 8 then leave /* not found */
If rc <> 0 Then
do
say ZERRSM ZERRLM
leave
end
else
do
Address ispexec 'EDIT DATAID('data1') MEMBER('member')
MACRO(FOREACH)'
end
End
/*********************************************************************/
/* Free the member list and close the dataid for the PDS. */
/*********************************************************************/
Address ispexec 'LMMLIST DATAID('data1') OPTION(FREE)'
Address ispexec 'LMCLOSE DATAID('data1')'
Exit 0
From the information passed to the Rexx exec, you could pass the edit macro as a parameter, such as
AA / MYMAC
I was just lazy and hard coded the macro name.
An edit macro to make the changes
My basic macro just reports the member name, and the size of the file
ADDRESS ISPEXEC 'ISREDIT MACRO'
"ISREDIT (last) = LINENUM .ZLAST"
"ISREDIT (member ) = MEMBER"
"ISREDIT (curr,orig,concat)= DATASET"
say "foreach " member last curr
"ISREDIT END"
- The LINENUM command returns the line number of the specified line. You can create your own line labels. ISPF provides .ZFIRST and .ZLAST .ZCSR (the line where the cursor is currently.
- The MEMBER command returns the member name
- The DATASET command returns the current name of the dataset (and other information)
You can now do conditional processing, if the data set name starts with… then change…; if the member name starts with… then change…
You can use standard ISPF commands for example
/* */
/* trace e */
ADDRESS ISPEXEC 'ISREDIT MACRO'
"ISREDIT X ALL "
"ISREDIT FIND '//' ALL"
"ISREDIT CHANGE 'IBMUSER' 'COLIN' 1 10 ALL NX"
return
"ISREDIT END"
Without the return statement in the code, the code makes the changes and exits, it invisibly edits all of the members. With the return statement this gives you the opportunity to review the changes and to use end or cancel to leave the edit session.
Whoops – what happened there
To test the macros I use ‘view’ of the member, to edit it, but not save any changes. I then executed the macro.
I was trying to change a VOLSER to a symbol.
/* */
ADDRESS ISPEXEC 'ISREDIT MACRO'
"ISREDIT (member ) = MEMBER"
say member
"ISREDIT SCAN OFF"
if substr(member,1,6) <> "IEASYM" then
do
"ISREDIT CHANGE 'A3PRD1' '&&SYSP1.' ALL "
"ISREDIT CHANGE 'A3PRD2' '&&SYSP2.' ALL "
"ISREDIT CHANGE 'A3PRD3' '&&SYSP3.' ALL "
"ISREDIT CHANGE 'A3PRD4' '&&SYSP4.' ALL "
"ISREDIT CHANGE 'A3PRD5' '&&SYSP5.' ALL "
"ISREDIT CHANGE 'A3RES2' '&&SYSR2.' ALL "
"ISREDIT CHANGE 'A3SYS1' '&&SYSS1.' ALL "
"ISREDIT CHANGE 'A3CFG1' '&&SYSC1.' ALL "
end
return
"ISREDIT END"f
I wanted to change A3PRD1 to the symbol &SYSP1. I had use use &&, and SCAN OFF. Without these, ISPF treats &SYSP1 as an ISPF symbol, cannot find it, so replaces it with a null.