It’s no secret that the Integrated Language Environment helps us all write more modular applications. Modules and service programs make it easy to construct programs from several small, easily maintained, reusable chunks of code. We can even create programs composed of several different languages.
Although development using modular chunks is easy, there is a bit of trouble in paradise. You sometimes need to know which application programs use particular modules and service programs. This is especially true when you modify modules and service programs, because you must rebind them to your application programs for the changes to take effect. In the past, there has been no convenient way to determine the modules and service programs your applications use. But now, you can have the Anzilepgm utility do the research and present the information to you.
Overview
Let’s take a quick look at the utility’s components. Command Anzilepgm (Figure 1) lets you specify the library whose programs you want to cross-reference. You can also specify one of three special values for the library-*ALLUSR to analyze all user libraries, *CURLIB to analyze the job’s current library, or *USRLIBL to analyze the libraries in the user portion of the job’s library list. This command is so simple that it needs no further explanation. SQL source member AnzCrtDb contains SQL statements that create the cross-reference files Anzilepgm uses. This member is of type Text.
Figure 1. Command ANZILEPGM
/* ======================================================== */ /* Command: ANZILEPGM - Analyze ILE programs */ /* ======================================================== */ ANZILEPGM: CMD PROMPT('Analyze ILE programs' ) PARM KWD(LIB ) + TYPE(*NAME) + LEN(10) + SPCVAL((*ALLUSR) + (*CURLIB) + (*USRLIBL)) + MIN(1) + PROMPT('Library to analyze')
ILE program Anzilepgm consists of two modules, CL module Anzilepgm1 and RPG module Anzilepgm2. Anzilepgm1 serves as the driver for the utility, and Anzilepgm2 builds the cross-reference database.
The Cross-Reference Database
To create its database, Anzilepgm uses the RUNSQLSTM (Run SQL Statement) command to execute the SQL statements in source member AnzCrtDb (Figure 2). These statements produce two cross-reference files- AnzILEMod, which contains information about modules, and AnzILESvc, which contains information about service programs. The CREATE TABLE statements actually create the physical files, while the LABEL ON statements add text descriptions for the fields.
Figure 2 – Creating the cross-reference database
CREATE TABLE QTEMP/ANZILEMOD (PGMNAME CHAR(10), LIBNAME CHAR(10), MODNAME CHAR(10), MODLIB CHAR(10), MODSRC CHAR(10), MODSRCLIB CHAR(10), MODSRCMBR CHAR(10), MODATR CHAR(10), MODCRT CHAR(13), MODSRCUPD CHAR(13)); LABEL ON QTEMP/ANZILEMOD (PGMNAME IS 'Prog Name', LIBNAME IS 'Libr Name', MODNAME IS 'Module Name', MODLIB IS 'Module Libr', MODSRC IS 'Mod Src File' MODSRCLIB IS 'Mod Src Libr', MODSRCMBR IS 'Mod Src Mbr', MODATR IS 'Mod Attrib', MODCRT IS 'Mod Created', MODSRCUPD IS 'Src Changed'); CREATE TABLE QTEMP/ANZILESVC (PGMNAME CHAR(10), LIBNAME CHAR(10), SVCNAME CHAR(10), SVCLIB CHAR(10), SVCSIG CHAR(16)); LABEL ON QTEMP/ANZILESVC (PGMNAME IS 'Program Name', LIBNAME IS 'Library Name', SVCNAME IS 'Srv Pgm Name', SVCLIB IS 'Srv Pgm Libr', SVCSIG IS 'Srv Pgm Signature');
Because Anzilepgm creates the files in library QTEMP, the information is no longer available after your job ends. You could keep the information in permanent files, but we don’t advise it. The temporary files always reflect the state of your objects at the time you analyze them, but permanent files run the risk of not being synchronized with your objects should you make changes without refreshing the database.
After the utility loads these files, you can query their contents however you’d like. You can simply print a report showing the information, search for information about how a particular object is used, or even use the information as input to a utility to compile programs.
The CL Driver
Anzilepgm1 functions much like an OPM CL program that acts as a driver for an OPM RPG program. That is, Anzilepgm1 acts as the entry point, setting overrides and other environmental conditions necessary for the RPG program. But unlike an OPM environment, in which the CL driver and RPG program are two separate programs, Anzilepgm1 is a module bound with RPG module Anzilepgm2 to form a single program. Anzilepgm1 (Figure 3) first executes the DSPOBJD (Display Object Description) command to create a file containing a list of the program and service program objects to be analyzed. This file serves as input to module Anzilepgm2.
Figure 3 – CL module Anzilepgm1
/* ========================================================= */ /* Module: ANZILEPGM1 - Analyze ILE programs - Main driver */ /* ========================================================= */ PGM PARM( &Library ) DCL &Library *CHAR 10 DCL &MsgID *CHAR 7 DCL &MsgF *CHAR 10 DCL &MsgFLib *CHAR 10 DCL &Msgdta *CHAR 100 MONMSG (CPF0000 MCH0000) EXEC(GOTO Error) DSPOBJD &Library/*ALL + OBJTYPE(*PGM *SRVPGM) + OUTPUT(*OUTFILE) + OUTFILE(QTEMP/QADSPOBJ) DLTF QTEMP/ANZILEMOD MONMSG CPF0000 DLTF QTEMP/ANZILESVC MONMSG CPF0000 RUNSQLSTM SRCFILE(ANZILEPGM) + SRCMBR(ANZCRTDB) OVRDBF QADSPOBJ QTEMP/QADSPOBJ OVRDBF ANZILEMOD QTEMP/ANZILEMOD OVRDBF ANZILESVC QTEMP/ANZILESVC CALLPRC ANZILEPGM2 DLTOVR *ALL RETURN Error: RCVMSG MSGTYPE(*LAST) + MSGDTA(&MsgDta) + MSGID(&MsgID) + MSGF(&MsgF) + MSGFLIB(&MsgFLib) MONMSG (CPF0000 MCH0000) SNDPGMMSG MSGID(&MsgID) + MSGF(&MsgFLib/&MsgF) + MSGDTA(&MsgDta) + MSGTYPE(*ESCAPE) MONMSG (CPF0000 MCH0000) ENDPGM
Next, Anzilepgm1 creates the cross-reference files by issuing the RUNSQLSTM command for source member AnzCrtDB. Finally, after issuing file overrides, the CL module invokes main procedure Anzilepgm2 with the CALLPRC (Call Procedure) command.
The RPG Processor
RPG module Anzilepgm2 (Figure 4) does the real work of Anzilepgm. This module uses several procedures and APIs to gather the cross-reference information. The main procedure reads a record from the file produced by the DSPOBJD command. When the object is a service program or an ILE program, the procedure invokes procedure BldModLst and BldSvcLst to build a list of modules and service programs the object uses.
Figure 4 – RPG module Anzilepgm2
// =============================================================== // Module: ANZILEPGM2 - Analyze ILE programs - Build database // =============================================================== FQadspobj IPE E Disk FAnzilemod O E Disk Prefix(Mod_) F Rename(Anzilemod:Modrec) FAnzilesvc O E Disk Prefix(Svc_) F Rename(Anzilesvc:Svcrec) // ------------------------------------------- Procedure prototypes D Bldmodlst PR D 20 Value D 8 Value D Bldsvclst PR D 20 Value D 8 Value D Crtusrspc PR D 20 Value D Getlstinf PR D 20 Value D 10U 0 D 10U 0 D 10U 0 D Pgmisile PR N D 20 Value // ------------------------------------------------- API prototypes D Qbnlpgmi PR Extpgm('QBNLPGMI') D Apiusrspc 20 Const D Apilstfmt 8 Const D Qpgmname 20 Const D Apierr 16 D Qbnlspgm PR Extpgm('QBNLSPGM') D Apiusrspc 20 Const D Apilstfmt 8 Const D Qpgmname 20 Const D Apierr 16 D Qclrpgmi PR Extpgm('QCLRPGMI') D Pgmi0100 161 D Pgmilen 10U 0 Const D Pgmifmt 8 Const D Qpgmname 20 Const D Apierr 16 D Quscrtus PR Extpgm('QUSCRTUS') D Uspcname 20 Const D Uspcextatr 10 Const D Uspcsiz 10U 0 Const D Uspcinzval 1 Const D Uspcpubaut 10 Const D Uspctext 50 Const D Uspcrpl 10 Const D Apierr 16 D Qgetlstmod PR Extpgm('QUSRTVUS') D Apiusrspc 20 Const D Apilstpos 10U 0 Const D Apisizent 10U 0 Const D Apirtnvar 508 D Qgetlstspg PR Extpgm('QUSRTVUS') D Apiusrspc 20 Const D Apilstpos 10U 0 Const D Apisizent 10U 0 Const D Apirtnvar 56 D Qgetlstinf PR Extpgm('QUSRTVUS') D Apiusrspc 20 Const D Apilstpos 10U 0 Const D Apisizent 10U 0 Const D Apirtnvar 16 // ----------------------------------------------- Global variables D Modfmt E DS Extname(Anzilemod) D Based(Modfmtptr) D Prefix(Mod_) D Svcfmt E DS Extname(Anzilesvc) D Based(Svcfmtptr) D Prefix(Svc_) D Modfmtptr S * Inz(*NULL) D Svcfmtptr S * Inz(*NULL) // --------------------------------------------------------------- // - Main procedure // --------------------------------------------------------------- /free If Odobtp = '*SRVPGM' Or Pgmisile(Odobnm + Odlbnm); Bldmodlst(Odobnm + Odlbnm:Odobtp); Bldsvclst(Odobnm + Odlbnm:Odobtp); Endif; /end-free // --------------------------------------------------------------- // Procedure: BldModLst - Populate database with modules used // --------------------------------------------------------------- P Bldmodlst B D PI D Qpgmname 20 Value D Objtype 8 Value D Apiusrspc C 'PGML0100 QTEMP ' D Apierr S 16 D Apilstpos S 10U 0 D Apinbrent S 10U 0 D Apirtnvar S 508 D Apisizent S 10U 0 D X S 10U 0 /free Crtusrspc(Apiusrspc); // Create User Space To Hold Module List If Objtype = '*SRVPGM'; // Build Module List Qbnlspgm(Apiusrspc:'SPGL0100':Qpgmname:Apierr); Else; Qbnlpgmi(Apiusrspc:'PGML0100':Qpgmname:Apierr); Endif; Getlstinf(Apiusrspc:Apilstpos:Apinbrent:Apisizent); // Get List Hdr If Apinbrent < 1; // Process List Return; Endif; Modfmtptr = %ADDR(Apirtnvar); For X = 1 To Apinbrent; Qgetlstmod(Apiusrspc:Apilstpos:Apisizent:Apirtnvar); Write Modrec; Apilstpos = Apilstpos + Apisizent; Endfor; Return; /end-free P Bldmodlst E // --------------------------------------------------------------- // Procedure: BldSvcLst - Populate database with service // --------------------------------------------------------------- P Bldsvclst B D PI D Qpgmname 20 Value D Objtype 8 Value D Apiusrspc C 'PGML0200 QTEMP ' D Apierr S 16 D Apilstfmt S 8 D Apilstpos S 10U 0 D Apinbrent S 10U 0 D Apirtnvar S 56 D Apisizent S 10U 0 D X S 10U 0 /free Crtusrspc(Apiusrspc); // Create User Space For Service Program List If Objtype = '*SRVPGM'; // Build Service Program List Qbnlspgm(Apiusrspc:'SPGL0200':Qpgmname:Apierr); Else; Qbnlpgmi(Apiusrspc:'PGML0200':Qpgmname:Apierr); Endif; Getlstinf(Apiusrspc:Apilstpos:Apinbrent:Apisizent); // Get List Hdr If Apinbrent < 1; // Process List Return; Endif; Svcfmtptr = %ADDR(Apirtnvar); For X = 1 To Apinbrent; Qgetlstspg(Apiusrspc:Apilstpos:Apisizent:Apirtnvar); If %SUBST(Apirtnvar:31:10) <> 'QSYS '; Write Svcrec; Endif; Apilstpos = Apilstpos + Apisizent; Endfor; Return; /end-free P Bldsvclst E // --------------------------------------------------------------- // Procedure: CrtUsrSpc - Creates a user space // --------------------------------------------------------------- P Crtusrspc B D PI D Uspcname 20 Value D Apierr S 16 /free Quscrtus(Uspcname: 'ANZILEPGM':4096:X'00':'*ALL':*BLANKS:'*YES':Apierr); Return; /end-free P Crtusrspc E // --------------------------------------------------------------- // Procedure: GetLstInf - Retrieves generic header list format // --------------------------------------------------------------- P Getlstinf B D PI D Uspcname 20 Value D Lstpos 10U 0 D Lstnbrent 10U 0 D Lstsizent 10U 0 D Uspcrtnvar DS D Uspclstpos 10U 0 D 10U 0 D Uspcnbrent 10U 0 D Uspcsizent 10U 0 /free Qgetlstinf(Uspcname:125:16:Uspcrtnvar); // Get list position Lstpos = Uspclstpos + 1; Lstnbrent = Uspcnbrent; Lstsizent = Uspcsizent; Return; /end-free P Getlstinf E // --------------------------------------------------------------- // Procedure: PgmIsILE - Returns indicator for program type // *ON = ILE program // *OFF = Not ILE program // --------------------------------------------------------------- P Pgmisile B D PI N D Qpgmname 20 Value D Apierr S 16 D Pgmi0100 DS D Pgmipgmtyp 161 161 /free Qclrpgmi(Pgmi0100:%SIZE(Pgmi0100):'PGMI0100':Qpgmname:Apierr); Return (Pgmipgmtyp = 'B'); // *ON=Ile Pgm, *OFF=Not Ile Pgm /end-free P Pgmisile E
BldModLst populates the cross-reference database with modules used by the object. The procedure uses APIs QBNLPGMI to retrieve information for modules and QBNLSPGM to retrieve information for service programs. Procedure BldSvcLst populates the cross-reference database with service programs used by the object. This procedure’s workings are, for all practical purposes, identical to those of BldModLst. Because these procedures are so similar, we discuss only BldModLst.
BldModLst first creates a user space to contain the module information retrieved by the APIs. Next, it retrieves the information by invoking the appropriate API. The procedure then extracts (from the generic header portion of the user space) the entry size and the number of entries in the module list. Finally, the procedure extracts the information from the user space and writes it to the cross-reference database.
Conclusion
The next time you modify a module or service program, you can use Anzilepgm to find all application programs that reference it. Anzilepgm makes the job of rebinding modified modules and service programs to your applications easier, faster, and more reliable.
Installing Anzilepgm
You should create source physical file ANZILEPGM to contain all source from the Anzilepgm utility. If you opt to use your own source file, be sure to change references to source file ANZILEPGM in the utility and installation process below.
RUNSQLSTM SRCFILE(YourLib/ANZILEPGM) + SRCMBR(ANZCRTDB) DSPOBJD YourLib/*ALL + OBJTYPE(*PGM *SRVPGM) + OUTPUT(*OUTFILE) + OUTFILE(QTEMP/QADSPOBJ) CRTCMD CMD(YourLib/ANZILEPGM) + PGM(ANZILEPGM) + SRCFILE(YourLib/Anzilepgm) CRTCLMOD MODULE(ANZILEPGM1) + SRCFILE(YourLib/Anzilepgm) CRTRPGMOD MODULE(ANZILEPGM2) + SRCFILE(YourLib/Anzilepgm) CRTPGM PGM(ANZILEPGM) + MODULE(ANZILEPGM1 ANZILEPGM2) + ACTGRP(QILE)