AS/400 Links Partner with Rochester Institute Contact Us
Training Catalog Free Downloads Privacy / Usage


FREE AS/400, iSeries, and System i / iSeries Downloads
HOME >> FREE AS/400, iSeries, and System i / iSeries Downloads >> Easy RPG Library List Control



Easy RPG Library List Control
Author: Bradley V. Stone


Slowly but surely, today's RPG programming is moving out of the familiar, comfortable environment that most of us have known. You may still maintain and create new green-screen RPG applications, but you also are probably moving RPG programs into different environments, including those that deal with the World Wide Web.

Writing RPG programs to produce dynamic content on the Web is slowly becoming just as important as writing subfile and report programs. But, the environment in which these programs run is much different, especially when it comes to controlling the library list.

If you have done any eRPG programming, you most likely have found out that controlling the library list is not as simple as setting up job descriptions. Web server jobs have their own environment that must constantly change and adapt to each application that is requested. One request may allow a user to sign on to your Web-based order entry application, while the next request processed by the same job may display an internal list of purchase orders on your Intranet.

Whether you're using the eRPG Software Development Kit
, CGIDEV2 from IBM, or your own home-grown CGI application functions, manipulating the library list is something you need to do "on the fly" at run time with your applications to access and process the correct data.

You can manipulate the library list by calling a CL program, which then calls the xxxLIBLE command to add or remove libraries from the library list. A cleaner, more portable, and easier way is to use an ILE service program that includes all the library list functions you need. Encapsulating library list processes into easy to use functions also makes them available in any application or environment.

In the following sections, I describe the F.LIBL service program I created and have used in my applications for a few years. A version of these functions is included in the eRPG SDK, but they also would be a welcome addition to CGIDEV2 or any Web toolkit you are using. These functions have evolved over time and have been added to my list of "must have" functions for any development environment.

The #addLibLE Subprocedure
The Add Library List Entry (#addLibLE) subprocedure is a wrapper to the Add Library List Entry (ADDLIBLE) command. This subprocedure uses the QCMDEXC API to call the ADDLIBLE command to add a library to the library list.

The source for this subprocedure is shown in Figure 1.

 *//////////////////////////////////////////////////////////////*
 * #addLibLE - Add Library List Entry                           *
 *                                                              *
 * Notes: This function is used to add a specific library into  *
 * a specific position in the library list.                     *
 *                                                              *
 * Lib (Input) - Library to add to the library list             *
 * Pos (Optional) - Position to place the library               *
 *   *FIRST   - Place the library at the top of the             *
 *              library list.                                   *
 *   *LAST    - Place the library at the bottom of the library  *
 *              list.                                           *
 *   *AFTER   - Place the library after the library referenced  *
 *              in the RefLib paramter.                         *
 *   *BEFORE  - Place the library before the library referenced *
 *              in the RefLib paramter.                         *
 *   *REPLACE - Replace the library referenced in the           *
 *              RefLib paramter.                                *
 * RefLib (Optional) - The name of the library to reference     *
 *                     when *AFTER, *BEFORE or #REPLACE is      *
 *                     specified in the Pos paramter.           *
 *//////////////////////////////////////////////////////////////*
P #addLibLE       B                   EXPORT
 *--------------------------------------------------------------*
D #addLibLE       PI
D  Lib                          10    Const
D  Pos                           8    Const Options(*NOPASS)
D  RefLib                       10    Const Options(*NOPASS)
 *--------------------------------------------------------------*
C                   eval      QCmdCmd = 'ADDLIBLE LIB(' +
C                                       %trim(Lib) +
C                                       ')'
 *
C                   if        (%parms > 2) and
C                             ((Pos = '*BEFORE') or
C                              (Pos = '*AFTER') or
C                              (Pos = '*REPLACE'))
C                   eval      QCmdCmd = %trim(QCmdCmd) + ' ' +
C                                       'POSITION(' +
C                                       %trim(Pos) + ' ' +
C                                       %trim(RefLib) +
C                                       ') '
C                   else
 *
C                   if        (%parms > 1)
C                   eval      QCmdCmd = %trim(QCmdCmd) + ' ' +
C                                       'POSITION(' +
C                                       %trim(Pos) + ')'
C                   endif
 *
C                   endif
 *
C                   CALL      'QCMDEXC'                            99
C                   PARM                    QCmdCmd
C                   PARM                    QCmdLength
 *--------------------------------------------------------------*
P #addLibLE       E

Figure 1 contains the source for the #addLibLE subprocedure

The #addLibLE subprocedure accepts one required parameter and two optional parameters.

The first required parameter is the name of the library you want to add to the user portion of the library list. The second parameter is optional. It lets you specify a position in the library list you would like to add the library. The default is *FIRST, or to place the library at the top of the library list. The other options are *LAST, *AFTER, *BEFORE, or *REPLACE.

If the second parameter contains the value *AFTER, *BEFORE, or *REPLACE, the third parameter is required. This third parameter contains the name of the library to reference when placing the library specified in the first parameter into the library list.

The code for this subprocedure is fairly simple and consists of checking the parameters coming into the subprocedure and building the proper ADDLIBLE command, and finally executing that command using the QCMDEXC API.

The #pushLib Subprocedure
The #pushLib subprocedure is a second interface used to add libraries to the library list. Because in applications most of the time you want to add libraries to the top of the library list, the #pushLib subprocedure can be used to perform that action.

The source for the #pushLib subprocedure is shown in Figure 2.

 *//////////////////////////////////////////////////////////////*
 * #pushLib - Push a Library onto the Library List              *
 *                                                              *
 * Notes: This function is used to place a library onto the     *
 * top of the current user library list.                        *
 *                                                              *
 * Lib (Input) - Library to add to library list                 *
 *//////////////////////////////////////////////////////////////*
P #pushLib        B                     EXPORT
 *--------------------------------------------------------------*
D #pushLib        PI
D  Lib                          10      Const
 *--------------------------------------------------------------*
C                   CallP     #AddLibLE(Lib:'*FIRST')
 *--------------------------------------------------------------*
P #pushLib        E


Figure 2 contains the source code for the #pushLib subprocedure


The #pushLib subprocedure accepts one required parameter. This parameter is the name of the library that you want to add to the top of the library list.

Examining the source code for this subprocedure shows that you just call the #addLibLE subprocedure discussed earlier and pass it the library name sent to the #pushLib subprocedure. You also use the value *FIRST to specify that the library is added to the top of the library list.

In most cases, you could leave out the second parameter value of *FIRST. But because it is possible that the default value of *FIRST on the ADDLIBLE command has been changed, you can always make sure the library is added to the top by passing this value.

The #popLib Subprocedure
Now that you have seen ways to add a library to the library list, you need a way to remove a library from the library list when the application ends. This process is necessary because most of the time you only want to change the library list of the job during the application run time.

You remove a library from the library list using the #popLib subprocedure.

The source for the #popLib subprocedure is shown in Figure 3.

 *//////////////////////////////////////////////////////////////*
 * #popLib - Pop a Library off of the Library List              *
 *                                                              *
 * Notes: This function is used to remove a specific library    *
 * from the current user library list. If no library is         *
 * passed to this function or the special value of *FIRST the   *
 * first library is removed.                                    *
 *                                                              *
 * Lib (Input) - Library to remove from the library list        *
 *   *FIRST - Remove the first library in the library list      *
 *   library - Remove "library" from the library list           *
 *//////////////////////////////////////////////////////////////*
P #popLib       B                   EXPORT
 *--------------------------------------------------------------*
D #popLib       PI
D  Lib                        10    Const Options(*NOPASS)
 *
D LibPtr        S               *
 *
D LibData       DS                  Based(LibPtr)
D  #Libs                      10i 0
D  LibArr                     10    Dim(25)
 *
D TempLib       S             10
 *--------------------------------------------------------------*
C                 if        (%Parms < 1) or (Lib = '*FIRST')
C                 eval      LibPtr = #rtvLibL('*USER')
 *
C                 if        (LibPtr <> *NULL) and (#Libs > 0)
C                 eval      TempLib = (LibArr(1))
C                 endif
 *
C                 else
C                 eval      TempLib = Lib
C                 endif
 *
C                 eval      QCmdCmd = 'RMVLIBLE LIB(' +
C                                     %trim(TempLib) +
C                                     ') '
 *
C                 CALL      'QCMDEXC'                              99
C                 PARM                    QCmdCmd
C                 PARM                    QCmdLength
 *--------------------------------------------------------------*
P #popLib       E


Figure 3 contains the source code for the #popLib subprocedure


The #popLib subprocedure accepts one optional parameter. This parameter should contain the name of the library that you want to remove from the library list.

You can also pass the special value of *FIRST as the library and the first library in the user library list will be removed. This value is the default if the first parameter is not used when you call the #popLib subprocedure.

The #popLib subprocedure again functions by building the appropriate RMVLIBLE command and then calling the QCMDEXC API to execute the command.

It also calls the #rtvLibL subprocedure, which I explain later. The #rtvLibL subprocedure is called if the first parameter is blank or if it contains the special value of *FIRST. It retrieves the current user library list and gets the name of the first library so that it can be removed from the library list.

The #chgLibLJD Subprocedure
The Change Library List using Job Description (#chgLibLJD) subprocedure is used to change the current user portion of the library list to the initial library list specified in a job description. This subprocedure is useful in those situations where you need more complicated library list manipulation during an application's run-time.

The source for the #chgLibLJD subprocedure is shown in Figure 4.

 *//////////////////////////////////////////////////////////////*
 * #chgLibLJD - Change Library List using Job Description       *
 *                                                              *
 * Notes: This function is used to change the current user      *
 * library list to that specified in a job description.         *
 *                                                              *
 * JobD (Input) - The job description to reference containing   *
 *                the library list to use.                      *
 * JobDLib(Input) - The library the job description specified   *
 *                  in JobD parameter is located.               *
 *//////////////////////////////////////////////////////////////*
P #chgLibLJD      B                   EXPORT
*--------------------------------------------------------------*
D #chgLibLJD      PI
D  JobD                         10    Const
D  JobDLib                      10    Const
 *
D JobDRtn         DS
D  Filler1                1    360
D  LLOffSet             361    364B 0
D  #Libs                365    368B 0
D  Filler2              369    600
 *
D LibL            S             11    DIM(25)
 *
D JobDLen         S              9B 0 INZ(%size(JobDRtn))
D JobDFmt         S              8    INZ('JOBD0100')
D JobDLoc         S             20
 *
D LLCurLib        S             11    INZ('*SAME')
D LLPrdLib        S             11    INZ('*SAME')
D LL2PrdLib       S             11    INZ('*SAME')
 *
D x               S             10i 0
D y               S             10i 0
 *--------------------------------------------------------------*
C                   eval      JobDLoc = (JobD + JobDLib)
 *
C                   CALL      'QWDRJOBD'
C                   PARM                    JobDRtn
C                   PARM                    JobDLen
C                   PARM                    JobDFmt
C                   PARM                    JobDLoc
C                   PARM                    WPError
*
C                   eval      y = (LLOffSet + 1)
*
C     1             do        #Libs         x
C                   eval      LibL(x) = %subst(JobDRtn:y:10)
C                   eval      y = (y + 11)
C                   enddo
*
C                   CALL                    'QLICHGLL'
C                   PARM                    LLCurLib
C                   PARM                    LLPrdLib
C                   PARM                    LL2PrdLib
C                   PARM                    LibL
C                   PARM                    #Libs
C                   PARM                    WPError
 *--------------------------------------------------------------*
P #chgLibLJD      E

Figure 4 contains the source code for the #chgLibLJD subprocedure

You need to pass two required input parameters into the #chgLibLJD subprocedure. The first parameter indicates where to retrieve the initial library list from, so you need to pass the name of the job description. You use the second parameter to specify where the job description used in the first parameter is located, so you need to pass the library name.

The #chgLibLJD subprocedure uses two system APIs to do its job. First the Retrieve Job Description Information (QWDRJOBD) API is called to retrieve information about the job description specified in the input parameters.

The job description's initial library list is then loaded into an array that is used on the call to the Change Library List (QLICHGLL) API. After this call, the user portion of the library list is changed to the initial library list specified in the job description.

The #rtvLibL Subprocedure
You use the Retrieve Library List subprocedure to retrieve either the current system, product, current or user library list. This subprocedure returns a pointer to a data structure that contains an array used to hold the requested library list.

The source for the #rtvLibL subprocedure is shown in Figure 5.

 *//////////////////////////////////////////////////////////////*
 * (#rtvLibL) Retrieve Library List and return the data as a    *
 * pointer to a data structure that contains the library        *
 * information. If the pointer returned contains the value      *
 * *NULL, an error occured.                                     *
 *//////////////////////////////////////////////////////////////*
P #rtvLibL       B                  EXPORT
*--------------------------------------------------------------*
D #rtvLibL       PI             *
D LibType                     10    Const
 *
D RtvRtnVar      DS
D  RtvSysLibs           65    68B 0
D  RtvPrdLibs           69    72B 0
D  RtvCurLibs           73    76B 0
D  RtvUsrLibs           77    80B 0
D  RtvData              81   400
 *
D SysData        DS                 STATIC
D  #SysLibs                   10i 0
D  SysArr                     10    DIM(25)
 *
D PrdData        DS                 STATIC
D  #PrdLibs                   10i 0
D  PrdArr                     10    DIM(25)
 *
D CurData        DS                 STATIC
D  #CurLibs                   10i 0
D  CurArr                     10    DIM(25)
 *
D UsrData        DS                 STATIC
D  #UsrLibs                   10i 0
D  UsrArr                     10    DIM(25)
 *
D RtvLen         S             9B 0 INZ(400)
D RtvFmt         S             8    INZ('JOBI0700')
D RtvJobName     S            26    INZ('*')
D RtvID          S            16
 *
D x              S            10i 0
D y              S            10i 0
 *--------------------------------------------------------------*
C                  CALL     'QUSRJOBI'
C                  PARM                   RtvRtnVar
C                  PARM                   RtvLen
C                  PARM                   RtvFmt
C                  PARM                   RtvJobName
C                  PARM                   RtvID
 *
C                  eval     y = 1
C                  eval     #SysLibs = RtvSysLibs
C                  eval     #PrdLibs = RtvPrdLibs
C                  eval     #CurLibs = RtvCurLibs
C                  eval     #UsrLibs = RtvUsrLibs
 *
C                  select
C                  when     (LibType = '*SYSTEM')
*
C     1            do       #SysLibs      x
C                  eval     SysArr(x) = %subst(RtvData:y:10)
C                  eval     y = (y + 11)
C                  enddo
*
C                  RETURN   %addr(SysData)
 *
C                  when     (LibType = '*PRODUCT')
C                  eval     y = (y + (#SysLibs * 11))
*
C     1            do       #PrdLibs      x
C                  eval     PrdArr(x) = %subst(RtvData:y:10)
C                  eval     y = (y + 11)
C                  enddo
 *
C                  RETURN   %addr(PrdData)
 *
C                  when     (LibType = '*CURRENT')
C                  eval     y = (y +
C                               ((#SysLibs + #PrdLibs) * 11))
 *
C     1            do       #CurLibs      x
C                  eval     CurArr(x) = %subst(RtvData:y:10)
C                  eval     y = (y + 11)
C                  enddo
 *
C                  RETURN   %addr(CurData)
 *
C                  when     (LibType = '*USER')
C                  eval     y = (y +
C                               ((#SysLibs + #PrdLibs + #CurLibs) * 11))
 *
C     1            do       #UsrLibs      x
C                  eval     UsrArr(X) = %subst(RtvData:y:10)
C                  eval     y = (y + 11)
C                  enddo
 *
C                  RETURN   %addr(UsrData)
C                  other
C                  RETURN   *NULL
C                  endsl
 *--------------------------------------------------------------*
C     *PSSR        BEGSR
C                  RETURN   *NULL
C                  ENDSR
 *--------------------------------------------------------------*
P #rtvLibL       E


Figure 5 contains the source code for the #rtvLibL subprocedure

The #rtvLibL subprocedure calls the Retrieve Job Information (QUSRJOBI) API to retrieve information about the current running job. In this case, you are interested in working with the current system, product, current, or user library list.

Depending on the type of library list you requested in the first parameter, an array is filled with the library list information. Then the pointer to that array is returned to the calling program.


Figure 6 shows the structure of the variables used to call this subprocedure.

 ****************************************************************
 * Data Structure for #rtvLibL function                         *
 ****************************************************************
D LibListDS@      S               *
 *
D LibListDS       DS                  Based(LibListDS@)
D  #Libs                        10i 0
D  LibArray                     10    DIM(25)


Figure 6 contains the variable definitions used to call the #rtvLibL subprocedure


You use the LibListDS@ pointer type variable when you call the #rtvLibL subprocedure. After the call, the data structure LibListDS (which is defined as based on the LibListDS@ pointer) contains the number of libraries in the library list and an array that contains the names of each library in the library list.

The #verLib Subprocedure
The final subprocedure in the F.LIBL service program is Verify Library (#verLib). This subprocedure is used to verify if a library is in the library list or not.

Figure 7 shows the source for the #verLib subprocedure.

 *//////////////////////////////////////////////////////////////*
 * #verLib - Verify a Library is in the Library List            *
 *                                                              *
 * Notes: This function is used to verify a library is in the   *
 * library list. The value returned will be the current         *
 * position in the library list. A value of less than 1         *
 * indicates the library is not in the library list             *
 *                                                              *
 * Lib (Input) - Library to verify                              *
 * LibType (Input) - The library list type to search.           *
 *   *SYSTEM - Retrieve the System library list.                *
 *   *PRODUCT - Retrieve the Product library list.              *
 *   *CURRENT - Retrieve the Current library list.              *
 *   *USER - Retrieve the User library list.                    *
 *//////////////////////////////////////////////////////////////*
P #verLib      B                   EXPORT
 *--------------------------------------------------------------*
D #verLib      PI            10i 0
D  Lib                       10    Const
D  LibType                   10    Const
 *
D LibPtr       S               *
 *
D LibData      DS                  BASED(LibPtr)
D  #Libs                     10i 0
D  LibArr                    10    DIM(25)
 *
D i            S              2  0
*--------------------------------------------------------------*
C                eval      LibPtr = #rtvLibL(LibType)
 *
C                if        (LibPtr = *NULL)
C               RETURN    -1
C                endif
 *
C                eval      i = 1
C     Lib        LOOKUP    LibArr(i)                              99
 *
C                if        (*IN99)
C                RETURN    i
C                else
C                RETURN    0
C                endif
 *--------------------------------------------------------------*
P #verLib      E


Figure 7 contains the source code for the #verLib subprocedure


The first parameter of the #verLib subprocedure should contain the name of the library you want to look for in the current library list.

The second parameter should contain the specific portion of the library list to search. This value can be used to search the *SYSTEM, *PRODUCT, *CURRENT, or *USER portion of the library list.

The #verLib subprocedure functions by calling the #rtvLibL subprocedure. Once it has the appropriate library list information, it searches the library list array for the library specified in the first parameter. If it finds the library, it returns the indexed location of the library. If the library is not found in the library list, it returns a value of zero.

Putting F.LIBL to Work
If you have taken the time to study one of the most important tools an RPG programmer can utilize, the Integrated Language Environment (ILE) and have been looking for an easier way to control the library list at run time, using the functions contained with the F.LIBL subprocedure should be simple.

Remember that the files used in your applications should be defined as *USROPN. This way, when the program first runs, it won't automatically try to open a file that it can't find because of your library list. You call the #pushLib subprocedure at the beginning of your program, OPEN the file(s) you will be using, CLOSE the files when you are done, and finally call the #popLib subprocedure to set your library list back the way it was before your application ran.

When developing Web applications, it is also good to consider the type of environment the jobs will run in. Most importantly, you need to consider which library or libraries the job will use to process data. For example, should the job run with data for the United States? Or perhaps Canada or Germany? Also, should the data reside in libraries for development, testing, or production? All of these attributes affect how the library list is set up for your application.

If you are like my consulting clients, and me this set of library list functions will be especially useful in your Web application environments. The unique job environment that your eRPG programs must reside within (especially if your data libraries exist outside of your eRPG application libraries) make library list functions essential.

Bradley V. Stone is the author of RPG Skills Acceleratorand e-RPG Powertools-Stone on CGIDEV2training courses, and the best selling book e-RGP: Building AS/400 Web Applications with RPG as well as the follow-up book, e-RPG(v2): e-Volving RPG Applications for a Connected World. Brad can be reached at Stone@Lab400.com.