Wednesday, December 31, 2025

Find where a command is used in programs

A friend had been tasked to determine which programs used a particular CL command. When telling me about this she mentioned a CL command I had never used: Print Command Usage, PRTCMDUSG. She had used this command to check which CLP programs used the CL command.

Never having used this command, I wanted to try it for myself. I created a scenario where I wanted to find all the programs in my library that use the CL command DSPOBJD.

I created three programs with the DSPOBJD. The first I called TESTCL1, as a modern CLLE program:

01  DLTF FILE(QTEMP/OUTFILE)
02  MONMSG MSGID(CPF2105)

03  DSPOBJD OBJ(MYLIB/*ALL) +     
04            OBJTYPE(*FILE) +
05            OUTPUT(*OUTFILE) +
06            OUTFILE(QTEMP/OUTFILE)

The second program, TESTCL2, I created as an OPM CLP program.

01  PGM

02  DLTF FILE(QTEMP/OUTFILE)
03  MONMSG MSGID(CPF2105)

04  DSPOBJD OBJ(MYLIB/*ALL) +     
05            OBJTYPE(*FILE) +
06            OUTPUT(*OUTFILE) +
07            OUTFILE(QTEMP/OUTFILE)

08  ENDPGM

After creating TEXTCL2 I made a copy of its source member and then deleted the original source member:

01  CPYSRCF FROMFILE(DEVSRC) TOFILE(DEVSRC) +
              FROMMBR(TESTCL2) TOMBR(TESTCL2A)

02  RMVM FILE(DEVSRC) MBR(TESTCL2)

Line 1: The CPYSRCF allows me to copy one source member to a new one, in this case make a copy of TESTCL2 to TESTCL2A.

Line 2: I then remove the old member from the source file.

This allows me to see if PRTCMDUSG checks the source member or the objects itself.

The third program is a RPG program. As I can call various CL commands with QCMDEXC I wanted to see if I could identify this as well:

01  **free
02  ctl-opt option(*srcstmt) ;

03  dcl-pr QCMDEXC extpgm ;
04    *n char(100) options(*varsize) const ;
05    *n packed(15 : 5) const ;
06  end-pr ;

07  dcl-s Command char(100) ;

08  Command = 'DLTF FILE(QTEMP/OUTFILE)' ;
09  monitor ;
10    QCMDEXC(Command : %len(%trimr(Command)) ) ;
11    on-error ;
12  endmon ;

13  Command = 'DSPOBJD OBJ(MYLIB/*ALL) OBJTYPE(*FILE) ' +
14            'OUTPUT(*OUTFILE) OUTFILE(QTEMP/OUTFILE)' ;
15  QCMDEXC(Command : %len(%trimr(Command)) ) ;

16  *inlr = *on ;

When I entered PRTCMDUSG and prompted the command, with F4, the following was displayed:

                        Print Command Usage (PRTCMDUSG)

Type choices, press Enter.

Command  . . . . . . . . . . . . CMD                     
  Library  . . . . . . . . . . .                 *LIBL      
                          + for more values               
                                                 *LIBL      
Program  . . . . . . . . . . . . PGM           *ALL      
  Library  . . . . . . . . . . .                 *USRLIBL  

I can enter up to five commands in the CMD parameter. And can check programs in a library or in my library list.

I wanted to look for any program that uses DSPOBJD in my library, MYLIB. Therefore, I used the following:

01  PRTCMDUSG CMD(DSPOBJD) PGM(MYLIB/*ALL)

The command generates a spool file even if it does not find any matching objects. Below is a copy of part of the spool file that was generated for the above command:

5770SS1 V7R6M0                                        Print Command Usage
Commands requested . . . . . . . . :  *LIBL/DSPOBJD
Programs requested . . . . . . . . :  MYLIB/*ALL
                                                       Command Use Detail
Program           Library           Command           Library           Command           Library
TESTCL2           MYLIB             DSPOBJD           *LIBL
Libraries searched  . . . . . . . . . . . . . :   MYLIB
CL programs using commands  . . . . . . . . . :            1
CL programs searched  . . . . . . . . . . . . :            1

It found the program TESTCL2, which shows it looks at the CLP object and not the source member. It did not find TESTCL1 or TESTRPG. After further research I found that PRTCMDUSG only searches CLP.

For a worthwhile analysis of the use of a command in a library I would need to check CLP programs, CLLE programs and modules, RPG programs, RPGLE programs and modules, and source members containing SQL too. The only way I can reasonably do this is to search the source members for the objects in the library. I would have to rely on there being no "lost" source members, i.e. a program without a matching source member.

In an earlier post I showed how I could use PDM's FNDSTRPDM to write what it found to an output file. I can modify that program to fit this scenario. All I need is:

  1. An output file
  2. Program to write to the output file
  3. A PDM custom option
  4. Program to execute FNDSTRPDM over various source files

The output "file" is a DDL table with four columns:

01  CREATE TABLE MYLIB.FINDSTRPDM_OUTPUT
02     FOR SYSTEM NAME "SRCHOUTF"
03  (SRC_LIBRARY CHAR(10),
04   SRC_FILE CHAR(10),
05   SRC_MEMBER CHAR(10),
06   SRC_TYPE CHAR(10)) ;

I created a RPG program, SRCHPGM0, to add to the output file:

01  **free
02  ctl-opt main(Main) option(*srcstmt) ;

03  dcl-proc Main ;
04    dcl-pi *n ;
05      SrcLibrary char(10) ;
06      SrcFile char(10) ;
07      SrcMbr char(10) ;
08      SrcType char(10) ;
09    end-pi ;

10    exec sql INSERT INTO SRCHOUTF
11               VALUES(:SrcLibrary,:SrcFile,:SrcMbr,:SrcType) ;

12    return ;
13  end-proc ;

Line 2: I am using a Main procedure so that this program will not use the RPG cycle, which would make it slower.

Line 3: Start of the Main procedure, which ends on line 13.

Lines 4 – 9: Passed parameters from the FNDSTRPDM command.

Lines 10 – 11: I am using a SQL Insert statement to add this as a new row to the output file.

My new PDM option is:

Option  Command
  X0    CALL PGM(MYLIB/SRCHPGM0) PARM(&L &F &N &T)

I called the new PDM option "X0" (X-zero) as I had already used "XX" in my prior example.

Four parameters are passed to the program called:

  • &L:  Library name
  • &F:  Source file name
  • &M:  Source member name
  • &T:  Source member type

When FNDSTRPDM finds the string it is searching for it calls SRCHPGM0.

The last program is a CL program, I have called SRCHPGM1, the executes the FNDSTRPDM command for the "standard" source files.

01  PGM PARM(&STRING &LIB)

02  DCL VAR(&STRING) TYPE(*CHAR) LEN(30)
03  DCL VAR(&LIB) TYPE(*CHAR) LEN(10)
04  DCL VAR(&FILE) TYPE(*CHAR) LEN(10)

05  CLRPFM FILE(SRCHOUTF)

06  CHGVAR VAR(&FILE) VALUE('QCLSRC')
07  CALLSUBR SUBR(DO_FIND)

08  CHGVAR VAR(&FILE) VALUE('QDDSSRC')
09  CALLSUBR SUBR(DO_FIND)

10  CHGVAR VAR(&FILE) VALUE('QRPGSRC')
11  CALLSUBR SUBR(DO_FIND)

12  CHGVAR VAR(&FILE) VALUE('QRPGLESRC')
13  CALLSUBR SUBR(DO_FIND)

14  CHGVAR VAR(&FILE) VALUE('DEVSRC')
15  CALLSUBR SUBR(DO_FIND)

16  SUBR SUBR(DO_FIND)
17    FNDSTRPDM STRING(&STRING) 
18                FILE(&LIB/&FILE) 
19                MBR(*ALL) +
20                OPTION(X0)
21    MONMSG MSGID(PDM0055)
22  ENDSUBR

I think the above is simple enough that it explains itself. There is only one group of lines I think deserves an explanation.

Lines 17 – 20: Within the subroutine FNDSTRPDM is executed with the following parameters:

  • Line 17: The passed search string
  • Line 18: The source file to search. The library name is passed to the program; the source files are hard coded within the program
  • Line 19: Search all members
  • line 20: When a match is found execute the PDM option "X0". As I showed above, this writes to the output file

Line 21: This MONSMG stops the program from erroring if the source file is not found in the library.

To check my library for the DSPOBJD command I call SRCHPGM1, passing to it the search string and the library to search:

01  CALL SRCHPGM1 ('DSPOBJD' 'MYLIB')

When the program completed I looked in the job log to see what happened:

CALL SRCHPGM1 ('DSPOBJD' 'MYLIB')
Member SRCHOUTF file SRCHOUTF in MYLIB cleared.
File QCLSRC in library MYLIB was not found.
Error while processing the FNDSTRPDM command.
File QDDSSRC in library MYLIB was not found.
Error while processing the FNDSTRPDM command.
File QRPGSRC in library MYLIB was not found.
Error while processing the FNDSTRPDM command.
File QRPGLESRC in library MYLIB was not found.
Error while processing the FNDSTRPDM command.
3 members match the Find string in file DEVSRC.

I do not have the "standard" source files in my library. I only use DEVSRC for all source members.

When I check the output file I see three results, which is what I expected:

SRC_LIBRARY  SRC_FILE  SRC_MEMBER  SRC_TYPE
-----------  --------  ----------  --------
MYLIB        DEVSRC    TESTCL1     CLLE
MYLIB        DEVSRC    TESTCL2A    CLP
MYLIB        DEVSRC    TESTRPG     RPGLE

What recommendation did I make to my friend? Use PRTCMDUSG to search the CLP programs she had. In the library she had no CLLE. And then use the method I described above to check the other types of programs. It is not perfect, but it would give her a good idea of where she needs to look.

 

You can learn more about the PRTCMDUSG command from the IBM website here.

 

This article was written for IBM i 7.6, and should work for some earlier releases too.

No comments:

Post a Comment

To prevent "comment spam" all comments are moderated.
Learn about this website's comments policy here.

Some people have reported that they cannot post a comment using certain computers and browsers. If this is you feel free to use the Contact Form to send me the comment and I will post it for you, please include the title of the post so I know which one to post the comment to.