Published on System iNetwork (http://systeminetwork.com)
Where's the ILE *PGM Creation Source Name?
By cbushong
Created Jun 5 2008 - 16:42

By:
Bob Cozzi [1]

Where Is the ILE *PGM Creation Source File Name?

In a recent Tuesday Tips video newsletter (www.systeminetwork.com/TuesdayTips [2]), I provided a quick-and-easy way for programmers to insert the creation source file name into ILE *PGM objects. Unlike legacy RPG III *PGM objects, this information is only stored in the *MODULE, not in the resulting *PGM. So it becomes bothersome when using in-house and even some older versions of third-party tools. The CHGPGMSRC CL command I featured in that issue of Tuesday Tips seemed to be well received, but then someone wrote in and asked me this question:

(and I paraphrase) Bob, is there a way to automate the process of assigning the source file, library, and member name to the *PGM object? For example, can I pull this information from the *PGM's first *MODULE object and assign it?

This a great question because I've actually wanted this capability since they shipped ILE. Here's the basic scenario: If you run the DSPOBJD command for a given ILE program and then selection option 8 (Service Information), a display similar to the following appears:

                      Display Object Description - Service                      

 Object . . . . . . . . . . . . . . . :   CHGPGMINF                             
   Library  . . . . . . . . . . . . . :     RPGCODER                            
 Library ASP device . . . . . . . . . :   *SYSBAS                               
 Library ASP group  . . . . . . . . . :   *SYSBAS                               
 Type . . . . . . . . . . . . . . . . :   *PGM                                  
                                                                                
 Source file  . . . . . . . . . . . . :                                
   Library  . . . . . . . . . . . . . :     
 Member . . . . . . . . . . . . . . . :                                
 Attribute  . . . . . . . . . . . . . :   RPGLE                                 
 User-defined attribute . . . . . . . :                                         
 Freed  . . . . . . . . . . . . . . . :   NO                                    
 Size . . . . . . . . . . . . . . . . :   208896                                
 Creation date/time . . . . . . . . . :   06/01/08  05:34:38                    
 Source file date/time  . . . . . . . :                                         
 System level . . . . . . . . . . . . :   V5R4M0                                
 Compiler . . . . . . . . . . . . . . :   CRTPGM     V5R4M0                     

Notice the source file, library, and member name items are empty. This is because of the fundamental design of *PGM objects under ILE. They are made up of one or more *MODULE objects. In order to produce a *MODULE, a source member is compiled, and that compiled source member produces a *MODULE object that is bound into a *PGM object by the CRTPGM command or "bind phase" of the compiler.

The CHGPGMSRC command I mentioned earlier allows you to insert something (anything actually) into the source file, library, and member name area of an existing ILE *PGM object. So you could issue the following command:

CHGPGMSRC  PGM(AP0150) SRCFILE(VEGGIE/PICKLES) SRCMBR(PEPPERS)

And end up having something that looks like this:

                      Display Object Description - Service                      

 Object . . . . . . . . . . . . . . . :   CHGPGMINF                             
   Library  . . . . . . . . . . . . . :     RPGCODER                            
 Library ASP device . . . . . . . . . :   *SYSBAS                               
 Library ASP group  . . . . . . . . . :   *SYSBAS                               
 Type . . . . . . . . . . . . . . . . :   *PGM                                  
                                                                                
 Source file  . . . . . . . . . . . . :   PICKLES                             
   Library  . . . . . . . . . . . . . :     VEGGIE
 Member . . . . . . . . . . . . . . . :   PEPPERS
 Attribute  . . . . . . . . . . . . . :   RPGLE                                 
 User-defined attribute . . . . . . . :                                         
 Freed  . . . . . . . . . . . . . . . :   NO                                    
 Size . . . . . . . . . . . . . . . . :   208896                                
 Creation date/time . . . . . . . . . :   06/01/08  05:34:38                    
 Source file date/time  . . . . . . . :                                         
 System level . . . . . . . . . . . . :   V5R4M0                                
 Compiler . . . . . . . . . . . . . . :   CRTPGM     V5R4M0                     

Which probably isn't as good a result as you might like.

This could make the viewer's request seem much more urgent. So I've spend a few minutes throwing together a new RPG IV program and CL command that does what you really want -- you give it an ILE *PGM name, and it looks at the *MODULE objects contained in that program, and if it finds one that has the same name as that of the *PGM, it extracts its source file, library, and member name and then changes the *PGM's creation information to match.

I've written a simple CL command definition source member to go with it, called Change Program Information (CHGPGMINF). The source code for that command follows:

CHGPGMINF:  CMD        PROMPT('Change Program Information')         
            /*         Command processing program is: CHGPGMINF  */ 
            /*  Updates a program's Creation Source File & Mbr   */ 
            /*  with that of the *MODULE in the *PGM with the    */ 
            /*  same name as the *PGM.                           */ 
                                                                    
            PARM       KWD(PGM) TYPE(QUAL2) MIN(1) PROMPT('Program +
                         name')                                     
QUAL2:      QUAL       TYPE(*NAME) EXPR(*YES)                       
            QUAL       TYPE(*NAME) DFT(*LIBL) SPCVAL((*LIBL) +      
                         (*CURLIB)) EXPR(*YES) PROMPT('Library')    

To compile this command, just use PDM option 14 (CRTCMD). No additional parameters or options are needed.

RPG TnT: 101 Dynamic Tips ‘n Techniques with RPG IV by Bob Cozzi is available now.
[3] The latest book from author Bob Cozzi is 300 pages, containing 101 example RPG IV Tips and Techniques for everyday programming tasks, from date calculations, to regular express searches, to using APIs. Cozzi wrote down every cool technique he's found over the years, updated them, and consolidated them into this compact book that is a great desktop companion -- and it includes full example source code. Order your copy today. [4]

Of course, we also need the RPG IV source code that will perform this task for us. Rather than go over it line by line, I'm going to comment on specific areas in a generalized sense and leave the analysis to you. Here's to source code:

     H BNDDIR('QC2LE')
     H DFTACTGRP(*NO) OPTION(*SRCSTMT : *NODEBUGIO)

      /INCLUDE QSYSINC/QRPGLESRC,qbnlpgmi
      /INCLUDE QSYSINC/QRPGLESRC,qusgen
      /INCLUDE QSYSINC/QRPGLESRC,qusec

     D apiError        DS                  LikeDS(QUSEC) Inz

     D szUserSpace     DS                  Qualified
     D  name                         10A   Inz('MODLIST')
     D  library                      10A   Inz('QTEMP')

     D nPos            S             10I 0

     D apiFmt          C                   Const('PGML0100')
     
     D QUALOBJ         DS                  Qualified
     D  object                       10A
     D  obj                          10A   overlay(object)
     D  objName                      10A   overlay(object)
     D  name                         10A   overlay(object)
     D  library                      10A
     D  lib                          10A   overlay(library)
     D  libname                      10A   overlay(library)
     D  objlib                       10A   overlay(library)

     D module_T        DS                  Qualified
     D  pgm                                LikeDS(qualObj)
     D  module                       10A
     D  modname                      10A   Overlay(module)
     D  name                         10A   Overlay(module)
     D  library                      10A
     D  modLib                       10A   Overlay(library)
     D  srcfile                            LikeDS(qualObj)
     D  srcMbr                       10A
     D  seuType                      10A

     D module          DS                  LikeDS(module_T)

	  **  Retrieve User Space Data
     D QusRtvUS        PR                  extPgm('QUSRTVUS')
     D  szUserSpace                  20A   Const
     D  nStart                       10I 0 Const
     D  nLength                      10I 0 Const
     D  szRtnData                 65535A   Options(*VARSIZE)
     D  api_error                          LikeDS(QUSEC)
     D                                     OPTIONS(*VARSIZE:*NOPASS)

	  **  List the Modules in an ILE *PGM
     D ListModules     PR                  extPgm('QBNLPGMI')
     D  userSpace                    20A   Const
     D  Format                        8A   Const
     D  pgmName                      20A   Const
     D  apiError                           LikeDS(QUSEC)
     D                                     OPTIONS(*VARSIZE)

	  **  Change Object Description
     D QLICOBJD        PR                  EXTPGM('QLICOBJD')
     D  rtnLib                       10A
     D  object                             Const LikeDS(QualObj)
     D  objType                      10A   Const
     D  chgData                    4096A   Const OPTIONS(*VARSIZE)
     D  apiErrorDS                   16A   OPTIONS(*VARSIZE)


     D GetNextModule   PR            10I 0
     D  module                             LikeDS(Module_T)
     D  ref                          10I 0

     D ChgPgmSrc       PR
     D  pgm                                LikeDS(QUALOBJ) Const
     D  srcFile                            LikeDS(QUALOBJ) Const
     D  srcMbr                       10A   Const OPTIONS(*NOPASS)

     D chgpgmInf       PR                  EXTPGM('CHGPGMINF')
     D  pgm                                LikeDS(qualobj)

     D ChgpgmInf       PI
     D  pgm                                LikeDS(qualobj)


     C                   eval      *INLR = *ON

      /FREE
           ListModules(szUserSpace : apiFmt : pgm: apiError);

           dow GetNextModule(module : nPos ) > 0;
             if module.pgm.Name = pgm.name and
                   module.pgm.Library = pgm.library;
               chgpgmsrc(pgm : module.srcfile : module.srcMbr);
             endif;
           enddo;
      /END-FREE

     P GetNextModule   B
     D GetNextModule   PI            10I 0
     D  module                             LikeDS(Module_T)
     D  nRef                         10I 0

     D nRtvLen         S             10I 0
     D nStart          S             10I 0

     D genHdr          DS                  LikeDS(QUSH0100) Inz
     D moduleInfo      DS                  LikeDS(QBNL0100)

      /FREE
           QusRTVUS(szUserSpace : 1 : %size(genHdr) : genHdr);

           if (nRef <= 0);
             nRef = 1;
           endif;

           if (nRef > genHdr.QUSNBRLE);
             return 0;
           endif;

          //  Each entry is located at start-of-list, plus length of entry,
          //  (QUSSEE) multipled by the nRefPos (entry number).
           nStart = (genHdr.QUSOLD+1) + ((nRef-1) * genHdr.QUSSEE);
           nRtvLen = %size(moduleInfo);
           QusRTVUS(szUserSpace : nStart : nRtvLen : moduleInfo);
            module.Pgm.Name = moduleInfo.QBNPGMN00;
            module.Pgm.Library  = moduleInfo.QBNPGMLN;
            module.Name = moduleInfo.QBNBMN;
            module.library  = moduleInfo.QBNBMLN;
            module.srcfile.name = moduleInfo.QBNSFILN;
            module.srcfile.library = moduleInfo.QBNSFLN;
            module.srcMbr = moduleInfo.QBNSFILM;
            module.seuType  = moduleInfo.QBNMA;

           nRef += 1;

           return nRef;
      /end-free

     P GetNextModule   E


     P ChgPgmSrc       B
     D ChgPgmSrc       PI
     D  pgm                                LikeDS(QUALOBJ) Const
     D  srcFile                            LikeDS(QUALOBJ) Const
     D  srcMbr                       10A   Const OPTIONS(*NOPASS)

     D  OIR_srcInfo_T  DS                  Qualified
     D   srcFile                     10A
     D   srcLib                      10A
     D   srcMbr                      10A

     D keyInfo_T       DS                  Qualified Inz
     D  keyID                        10I 0
     D  length                       10I 0
     D   data                              LikeDS(OIR_srcInfo_T)

     D objInfo_T       DS                  Qualified
     D  keyCount                     10I 0
     D  keyData                            LikeDS(keyInfo_T) Inz(*LIKEDS)

     D rtnLib          S             10A

     D myObjInfo       DS                  LikeDS(objInfo_T) Inz

     C                   eval      *INLR = *ON
      /free
            myObjInfo.keyCount = 1;
            myObjInfo.keyData.length = %size(oir_srcInfo_T);
            myObjInfo.keyData.keyID = 1; // Change source lib/file/mbr

            myObjInfo.keyData.data.srcFile = srcfile.name;
            myObjInfo.keyData.data.srcLib  = srcfile.lib;
            if (%parms < 3 or srcmbr = '*PGM' or srcmbr = ' ');
                myObjInfo.keyData.data.srcMbr  = pgm.name;
            else;
                myObjInfo.keyData.data.srcMbr  = srcmbr;
            endif;

           qlicobjd(rtnLib : pgm :'*PGM': myObjInfo : apiError);
           return;

      /end-free
     P chgPgmSrc       E  

To use this source member, you really need the "System Openness Includes" or QSYSINC library installed. If you don't have it installed (it is free from IBM and already on your installation media), you may be able to locate the necessary include members on the web or simply enter them yourself. But if you don't have QSYSINC installed, you really need to change your installation procedures so that this option is included next time you update the operation system. The three lines of code that depend on QSYSINC are the three /INCLUDE statements, as follows:

      /INCLUDE QSYSINC/QRPGLESRC,qbnlpgmi
      /INCLUDE QSYSINC/QRPGLESRC,qusgen
      /INCLUDE QSYSINC/QRPGLESRC,qusec

These include members copy in several data structures that are used as Data Structure Templates. The data returned from a couple of API calls is stored in this structures. Those APIs are

  1. QBNLPGMI - List Modules that make up a *PGM
  2. QUSRTVUS - Retrieve User Space Data
  3. QLICOBJD - Change Object Description

The QBNLPGMI API produces a list of module names and their corresponding creation information for a given *PGM. This information includes the source file, library, and member names along with things such as SEU type, creation date, compilers and version of compilers used, and so on.

Call QBNLPGMI to generate the list of *MODULE objects and their information. This information is stored in a user space. I've published information on creating a user space in January 2008, and you will need the Creation User Space (CRTUSRSPC) CL command featured in that issue or one of the similar commands from TAATOOLS or RPG xTools. A user space named MODLIST in the QTEMP library must be created before running the CRTPGMINF command. (Wouldn't it be great if the APIs were smart enough to just create the user spaces if they didn't exist? Is that asking too much?)

The QUSRTVUS API allows you to retrieve data from a user space. It's similar to using the RTVDTAARA command, but it works with user spaces. This API is used to extract each entry on the list generated by QBNLPGMI. Each entry is the description of the *MODULE object and is stored in a data structure based on the QBNL0100 data structure from QSYSINC. I named my data structure MODULEINFO on the following statement:

     D moduleInfo      DS                  LikeDS(QBNL0100)

Each module entry on the list produced by QBNLPGMI is returned by the GetNextModule subprocedure. This subprocedure simply iterates through the list of modules and returns the "next" one to the caller. I created a more concise data structure to return just the module information I'm interested in. That data structure is based on MODULE_T. It also accepts, modifies, and returns a reference handle that is used to determine which entry index is being requested. This process continues in the DOW loop until the name of the module returned is the same as that of the program.

Once the correct module information is returned, the third API, QLICOBJD, is called. This API allows you to change what's called the "object information repository," or OIR, for the program. The name of the source file, library, and member that were used to create the internal *MODULE is passed to this API, which changes the program's description.

At this point, the ILE *PGM has the name of the source file, library, and member used to create it stored in its description. Using DSPOBJD on the program and selecting option 8 reveals the following:

                      Display Object Description - Service                      

 Object . . . . . . . . . . . . . . . :   CHGPGMINF                             
   Library  . . . . . . . . . . . . . :     RPGCODER                            
 Library ASP device . . . . . . . . . :   *SYSBAS                               
 Library ASP group  . . . . . . . . . :   *SYSBAS                               
 Type . . . . . . . . . . . . . . . . :   *PGM                                  
                                                                                
 Source file  . . . . . . . . . . . . :   QRPGLESRC                             
   Library  . . . . . . . . . . . . . :     RPGCODER
 Member . . . . . . . . . . . . . . . :   CHGPGMINF
 Attribute  . . . . . . . . . . . . . :   RPGLE                                 
 User-defined attribute . . . . . . . :                                         
 Freed  . . . . . . . . . . . . . . . :   NO                                    
 Size . . . . . . . . . . . . . . . . :   208896                                
 Creation date/time . . . . . . . . . :   06/01/08  05:34:38                    
 Source file date/time  . . . . . . . :                                         
 System level . . . . . . . . . . . . :   V5R4M0                                
 Compiler . . . . . . . . . . . . . . :   CRTPGM     V5R4M0                     

Note that the source file name is now display as desired.

 

Bob Cozzi is currently touring the U.S. with his one-day RPG World Academy [5] seminar on RPG Application Modernization entitled Subprocedures Modules, and Service Programs. Visit www.RPGWorld.com [6] for details, dates, and locations.

Copyright © Penton Media

Source URL: http://systeminetwork.com/article/wheres-ile-pgm-creation-source-name

Links:
[1] http://systeminetwork.com/author/bob-cozzi
[2] http://www.systeminetwork.com/TuesdayTips
[3] http://www.amazon.com/dp/1583041214?tag=rw0c-20&camp=14573&creative=327641&linkCode=as1&creativeASIN=1583041214&adid=0FDDKRAMASMW6SZB3462&
[4] http://www.amazon.com/dp/1583041214?tag=rw0c-20&camp=14573&creative=327641&linkCode=as1&creativeASIN=1583041214&adid=0FDDKRAMASMW6SZB3462&
[5] http://www.rpgworld.com/academy
[6] http://www.RPGWorld.com