Backup a bit…
About a million years ago, actually 1976, I started in the computer industry as an operator. My job interview was with the managing director of the ‘computer department’ because it wasn’t ‘Information Technology’ yet! One thing that I have always remembered from that interview is him saying that I shouldn’t worry about making mistakes because it can always be fixed. I think he probably said it as much to reassure my novice self as anything else, but it always stuck with me.
And for the most part, he was right. But I have to qualify the general statement by saying that fixing it is a lot easier if you’ve got a backup of everything!
This comes to mind because a backup saved my bacon a few days ago.
I’ve been working on a little side project for a good while now, just a fun little thing I’ve been playing around with and while it’s not a lot of code as such, it is pretty involved. Now I still work quite a bit on a good old green screen in ISPF where I tend to use the 3.4 data set list option because I can easily switch from browse of a member to edit and back without having to switch ISPF screens.
The one thing I have never liked about the data set list function and the resultant member list display for a data set though, is that you can easily enter a D (for delete!) against either a data set or member. While it does prompt you to confirm the action (unless you’ve turned it off) it is pretty easy to be not paying attention, press enter on the confirm screen and it’s gone.
At this point you go ‘Oh F***k’ or words to that effect.
I know this because that’s exactly what happened to me the other day. Not paying attention, something popped up, I hit enter and it was only when the word ‘deleted’ appeared beside the member name that I realized I’d screwed up!
Now I’ve been working on this project for a while. In fact I last touched it a few years ago, got to a ‘sticky’ point where the coding was neither fun nor easy and put it aside until recently when I decided to pick it up again.
In all that time I kept thinking I really should start taking backups of the work. Fortunately for me, that’s exactly what I had done the previous day. I spent some time to write some jobs to back everything up so when I screwed up, thankfully I had a backup that I was able to restore the missing member from.
Had this happened a day earlier and it would have been a whole different story.
An after effect of this is that I wrote my own little data set list function that does NOT allow me to do a delete either against the data set or a data set member. Again, this was something I’ve often thought about, just never gotten around to until this happened and I got ‘motivated’ as it were.
My ISPF 3.4 alternative can be down loaded from ===> here. <===
Unzip the file and upload the exec and panel to your ISPF REXX and ISPPLIB panel library using a text transfer to convert back to EBCDIC. Edit the exec to specify the name of the panel library or remove the LIBDEFs for the panel library if it is already allocated to your ISPF ISPPLIB DD.
Then just run the exec, @34. It takes two parameters:
- A mask to specify the datasets to list, just like the real 3.4
- One or more exclude strings. Any data set name in the result list that matches one of these strings will not be show.
Line commands against data sets are:
- B or S – Browse
- E – Edit
- Z – Compress
Line commands against a member in a member list are:
- S – Whatever what entered against the data set, E or B.
- E – Edit
- B – Browse
- SUB – Submit the member
- = Repeat the previous command
It’s very basic. A lot of the error messages are just issued as say statements. If you use it, feel free to modify it to your needs.
This is the exec source:
/* rexx */ /* Custom ISPF 3.4 function */ /* pfx is dataset prefix to list */ /* excl is one or more exclude masks */ /* David E Ellis April 2020 */ parse arg pfx excl if pfx = '' then pfx = sysvar(syspref) address ISPEXEC /* "CONTROL ERRORS RETURN"*/ "LIBDEF ISPPLIB DATASET ID('DAVE.ISPPLIB')" "TBCREATE @34 NAMES(DSNAME DSTYPE) NOWRITE" if rc <> 0 then do say "TBCREATE RC="rc signal exit end rc=Build_list(pfx) if rc <> 0 then signal exit do forever 'TBTOP @34' 'TBDISPL @34 PANEL(@34)' , 'AUTOSEL(NO)' , 'ROWID(ROW)' if rc > 7 then leave 'TBQUERY @34 POSITION(row)' if row = 0 then row=1 do ztdsels if sel <> '' then do upper sel select when sel='S' then do rc=doit('B') /* default */ end when sel='B' then do rc=doit('B') end when sel='E' then do rc=doit('E') end when sel='Z' then do rc=compress(dsname,dstype) end otherwise do zerrsm = 'Invalid option' zerrlm = 'Invalid option for this line' zerralrm = 'NO' 'SETMSG MSG(ISRZ002)' end sel='' end end /* if sel <> '' */ if ztdsels = 1 then iterate 'TBDISPL @34' end /* do ztdsels */ end /*----------------------------------------------------*/ exit: "CONTROL ERRORS RETURN" "TBEND @34" "LIBDEF ISPPLIB" return 0 /*----------------------------------------------------*/ Build_list: parse arg pfx myrc=0 "LMDINIT LISTID(DSID) LEVEL("pfx")" if rc <> 0 then do say "LMDINIT RC="rc myrc=4 signal Build_list_exit end DSN = '' do forever "LMDLIST LISTID("dsid") OPTION(LIST) DATASET(DSN)" , "STATS(YES)" if rc = 4 then do say 'Nothing found for 'pfx myrc=4 leave end else if rc = 8 then leave /* end of list */ else if rc <> 0 then do say "LMDLIST RC="rc myrc=4 leave end exclrc=0 /* RC=0 means do NOT exclude */ if excl<>'' then do exclrc=CheckExcl(dsn,excl) end if exclrc=0 then do dsname=dsn dstype=zdldsntp "TBADD @34" end end Build_list_exit: "LMDLIST LISTID("dsid") OPTION(FREE)" "LMDFREE LISTID("dsid")" return myrc /*----------------------------------------------------*/ doit: myrc=0 parse arg option if strip(dstype) = "" then do /* unknow org, let ispf handle */ option" DATASET('"dsname"')" signal doit_exit end else myrc=mlist(option) doit_exit: return myrc /*----------------------------------------------------*/ mlist: myrc=0 parse arg mlistopt /* mlistopt just controls default action if S entered for mbr */ "LMINIT DATAID(MLID) DATASET('"dsname"') ENQ(SHR)" if rc<>0 then do say "LMINIT Rc="rc say ZERRLM myrc=4 signal mlist_exit end "LMOPEN DATAID("mlid") OPTION(INPUT)" if rc<>0 then do say "LMOPEN Rc="rc myrc=4 signal mlist_exit end mbr="" do forever prevcmd="" "LMMDISP DATAID("mlid") OPTION (DISPLAY) COMMANDS(ANY)" , "TOP("mbr") FIELD(9)" if rc=0 then nop else if rc=4 then do say " Empty dataset" leave end else if rc=8 then leave else do say "LMMDISP DISPLAY RC="rc say ZERRLM leave end mbr=strip(zlmember) rc=procmbr(mbr,zllcmd) do forever "LMMDISP DATAID("mlid") OPTION(GET)" getrc=rc if getrc=0 then nop else if getrc=8 then leave else do say "LMMDISP GET RC="getrc say ZERRLM leave end mbr=strip(zlmember) rc=procmbr(mbr,zllcmd) end "LMMDISP DATAID("mlid") OPTION(FREE)" end mlist_exit: "CONTROL ERRORS RETURN" "LMCLOSE DATAID("mlid")" "LMFREE DATAID("mlid")" return myrc /*----------------------------------------------------*/ procmbr: Procedure Expose mlistopt dsname prevcmd myrc=0 parse arg mbr,cmd if cmd = "S" then do prevcmd=mlistopt myrc=procmbr(mbr,mlistopt) end else if cmd = "B" then do prevcmd="B" "BROWSE DATASET('"dsname"("mbr")')" end else if cmd = "E" then do prevcmd="E" "EDIT DATASET('"dsname"("mbr")')" end else if cmd = "SUB" then do prevcmd="SUB" address TSO "SUBMIT ('"dsname"("mbr")')" end else if cmd = "=" then do myrc=procmbr(mbr,prevcmd) end else do prevcmd=cmd say "Invalid command "cmd end return 0 /*----------------------------------------------------*/ CheckExcl: Procedure /* If any of the string in excl are in the dsn return rc=4 */ /* else returns rc=0 (excl strigs NOT in dsn) */ parse arg dsn , excl upper dsn upper excl do i = 1 to words(excl) if pos(word(excl,i),dsn) > 0 then return 4 end return 0 /*----------------------------------------------------*/ compress: Procedure parse arg dsname, dstype if dstype <> 'PDS' then do say 'Invalid dataset type' signal compress_exit end "LMINIT DATAID(LMIID) DATASET('"dsname"') ENQ(EXCLU)" if rc <> 0 then do say "LMINIT RC="rc say ZERRLM signal compress_exit end "LMCOMP DATAID("lmiid")" if rc=0 then do say "Compressed" end else do say "LMCOMP RC="rc say ZERRLM end "LMFREE DATAID("lmiid")" compress_exit: return 0
And this is the panel definition:
)PANEL KEYLIST(ISRSPBC,ISR) )ATTR @ TYPE(OUTPUT) INTENS(LOW) COLOR(YELLOW) )BODY EXPAND(//) %--/-/-- Dataset List --/-/-- %Command ==> _ZCMD / / +Scroll ===>_ZAMT+ +DSLIST - Data Sets Matching@Z + )MODEL _Z+ @Z + )INIT .ZVARS = '(PFX SEL DSNAME)' &zamt=csr &zcmd='' &sel='' .HELP=ISR00003 )REINIT IF (.MSG = ' ') &SEL = ' ' REFRESH (SEL) )PROC IF (&ZTDSELS ^= 0000) VER (&SEL, LIST, B, b, E , e , S , s, Z, z) )END