As you probably know, when you specify *YES for the REPLACE parameter in many of the CRT* commands (such as CRTBNDRPG, CRTCLPGM, etc), as well as in some APIs to create objects such as user spaces, the replaced object isn't completely gone. Instead, it's copied into the QRPLOBJ library under a new, unique name generated by the system. The QRPLOBJ library is cleared only when you perform an IPL. So at least for a while, if you delete a program, or if you recompile a program by specifying REPLACE(*YES), you can retrieve the deleted/old program from QRPLOBJ. In this way, QRPLOBJ functions much like the Recycle Bin on a PC, and this has saved me on many occasions.
However, in my experience, it's much more likely that I'll accidentally delete a source member rather than a program, and when I do that, the source member is gone. I bet you've also had that sinking feeling when you realize that all your work writing that RPG or CL code has been wiped out because you pressed the Enter key too quickly. If only we could automatically save those deleted source members the same way the system saves our deleted programs. Well, it turns out that we can, and doing so is surprisingly simple.
The RECYCLE Program
The technique relies on creating a "recycling" program (which I've called RECYCLE and have included with this article) and specifying that program as a validity checker to the Remove Member (RMVM) command. The system runs RMVM whenever a file member is deleted, including when you select option 4=Delete from the Work with Members Using PDM display.
When you specify a program as a validity checker, the system calls that program before the command processing program (CPP) defined for the command. Most commands don't have validity checkers because it's simpler to add the validity checking code into the CPP itself.
However, you can make a validity checker do nearly anything you want—it doesn't have to just check validity! Because the RMVM command calls RECYCLE before the member is deleted, the program copies the member into a source file (also called RECYCLE) in QRPLOBJ. And there the source member will sit until the next IPL, waiting for you to suddenly realize that perhaps you shouldn't have deleted it.
A Look at the Source Code
To understand how the program works, let's take a brief stroll through the source in Figure 1.
/*T: Save deleted source members */
/*O: CRTBNDCL DBGVIEW(*SOURCE) LOG(*NO) USRPRF(*OWNER) */
DCL VAR(&QFILE) TYPE(*CHAR) LEN(20)
DCL VAR(&MBR) TYPE(*CHAR) LEN(10)
DCL VAR(&FILE) TYPE(*CHAR) LEN(10)
DCL VAR(&LIB) TYPE(*CHAR) LEN(10)
DCL VAR(&TEXT) TYPE(*CHAR) LEN(50)
DCL VAR(&FILETYPE) TYPE(*CHAR) LEN(50)
DCL VAR(&NBRCURRCD) TYPE(*DEC) LEN(10 0)
DCL VAR(&CHAR9) TYPE(*CHAR) LEN(9)
DCL VAR(&DEC9) TYPE(*DEC) LEN(9 0)
DCL VAR(&NEWMBR) TYPE(*CHAR) LEN(10)
DCL VAR(&DATETIME) TYPE(*CHAR) LEN(20)
MONMSG MSGID(CPF0000 MCH0000) EXEC(GOTO CMDLBL(ERROR))
CHGVAR VAR(&FILE) VALUE(%SST(&QFILE 1 10))
CHGVAR VAR(&LIB) VALUE(%SST(&QFILE 11 10))
/* Quit if the member is not a source file or if it's empty */
RTVMBRD FILE(&LIB/&FILE) MBR(&MBR) +
IF COND(&FILETYPE = '*DATA') THEN(GOTO +
IF COND(&NBRCURRCD = 0) THEN(GOTO CMDLBL(ENDPGM))
/* Check for the RECYCLE file in QRPLOBJ and create if necessary - */
/* use a record length of 240, which should be big enough for most */
/* different source types. */
CHKOBJ OBJ(QRPLOBJ/RECYCLE) OBJTYPE(*FILE)
MONMSG MSGID(CPF0000) EXEC(DO)
RCVMSG MSGTYPE(*EXCP) RMV(*YES)
QSYS/CRTSRCPF FILE(QRPLOBJ/RECYCLE) RCDLEN(240) +
TEXT('Saved deleted source members')
/* Build the new source member name as 'Rnnnnnnnnnn' - check that a */
/* source member with that name doesn't already exist in RECYCLE, */
/* and if it does, generate the next name and so on... */
CHGVAR VAR(&DEC9) VALUE(1)
LOOP: CHGVAR VAR(&CHAR9) VALUE(&DEC9)
CHGVAR VAR(&NEWMBR) VALUE('R' || &CHAR9)
CHKOBJ OBJ(QRPLOBJ/RECYCLE) OBJTYPE(*FILE) +
MONMSG MSGID(CPF9815) EXEC(DO)
RCVMSG MSGTYPE(*EXCP) RMV(*YES)
CHGVAR VAR(&DEC9) VALUE(&DEC9 + 1)
/* Copy the source file member into QRPLOBJ/RECYCLE */
QSYS/CPYSRCF FROMFILE(&LIB/&FILE) +
TOFILE(QRPLOBJ/RECYCLE) FROMMBR(&MBR) +
/* Change the source member text to display the original member */
CHGVAR VAR(&TEXT) VALUE(&LIB |< '/' || &FILE |> '(' +
|| &MBR |< ')' |> &DATETIME)
CHGPFM FILE(QRPLOBJ/RECYCLE) MBR(&NEWMBR) TEXT(&TEXT)
ERROR: RCVMSG MSGTYPE(*EXCP) RMV(*YES)
ENDPGM: RMVMSG CLEAR(*ALL)
Figure 1: The RECYCLE program source code
As you can see, the code in RECYCLE is simple:
- If the member that's being deleted isn't a source member or is empty, RECYCLE quits and does nothing.
- If the RECYCLE file doesn't exist in QRPLOBJ, the program will create it (with a record length of 240 bytes, which is big enough for all normal source files).
- The program generates a new, unique member name starting at R000000001, then R000000002, then R000000003, and so on.
- The program then copies the source member into the RECYCLE file under the new name.
- Finally, the RECYCLE program modifies the text for the new source member to include the details of the original source member as well as the data and time when it was deleted.
So, if I delete a source member called IMPORTANT from QRPGLESRC in library RORY, and I suddenly realize that it was indeed important, I can retrieve that source member from the RECYCLE file (in this case, it's the member called R000000004), as Figure 2 shows.
File . . . . . . RECYCLE
Library . . . . QRPLOBJ Position to . . . . .
Type options, press Enter.
2=Edit 3=Copy 4=Delete 5=Display 6=Print 7=Rename
8=Display description 9=Save 13=Change text 14=Compile 15=Create module...
Opt Member Type Text
R000000001 TXT M11077/QTXTSRC (MBR4) 20121001141836400610
R000000002 DSPF M11077/QDDSSRC (CMPSRCD2) 20121001174938318999
R000000003 CLLE RORY/QCLSRC (CL2AGRTR) 20121002121857929297
R000000004 RPGLE RORY/QRPGLESRC (IMPORTANT) 20121002160322001436
R000000005 CLLE RORY/QCLSRC (CL2AGRTR) 20121002160511164321
Parameters or command
F3=Exit F4=Prompt F5=Refresh F6=Create
F9=Retrieve F10=Command entry F23=More options F24=More keys
Figure 2: Work with Members Using PDM screen
In this example, the two members R000000003 and R000000005 are actually different versions of the same original source member (CL2AGRTR from QCLSRC in RORY) deleted at different times. So every version of every source member is saved each time it's deleted.
After you compile the RECYCLE program, you'll need to change it to USRPRF(*OWNER) and specify QSYS as the new owner to prevent authority errors. In my case, I put the RECYCLE program into QGPL, but you can place it wherever you want.
The detailed steps to create the RECYCLE objects are as follows:
- Copy the source for the RECYCLE program into QQCLSRC in QGPL.
- Compile the RECYCLE source member by using the following command:
- Change the RECYCLE *PGM to have the correct ownership:
- Change the RMVM command to use RECYCLE as its validity checker:
That's it! You're done and no longer will have that sinking feeling when you delete a source member by mistake.
Editor's note: Article updated 11/5/2012 with correction.