In "Service Programs and Signatures," we covered service programs, signatures and using binder language to manage the signature values. We mentioned that it was possible to automate the process of updating all of the programs that reference a service program if you don't want to bother with binder language. Here, we explain how to do that as well as demonstrate how to use "list" APIs.
Many RPGers shy away from using list APIs because doing so effectively involves using pointers and user spaces. If you're unfamiliar with pointers, we suggest you read this previous iSeries EXTRA column for some basics on using pointers as well as some examples.
List APIs dump the information requested into a user space. Why a user space? These APIs, as the name implies, return lists of items, and you can't predict how many items will be returned in the list. Because of this, returning the results as a parameter wouldn't work, and processing an outfile is slower than the direct memory access that a user space provides. In our case, we'll be asking for a list of all of the ILE programs in a library along with some information about which service programs each program references, if any.
First, we use an API call to create the user space object to hold the output of the API. You can think of a user space as a data stream that can be formatted any way the application requires. In our case, the API QBNLPGMI determines the data structure.
In our code sample, we use prototypes when we call the APIs. If you're unfamiliar with using prototypes, this is a great time to learn, because they offer some advantages over the traditional CALL ... PARM... PARM type syntax. See this previous iSeries EXTRA column for a look at how prototypes work.
Let's first examine the prototype for the API to create a user space-QUSCRTUS. We'll use the prototype to change that to the more meaningful name CrtUsrSpc. The parameters to be passed to the API are simple and include:
- The qualified name for the user space object (the library name begins in position 11)
- The type of information we want to put in the user space
- The initial size (in bytes) of the user space
- What initialization value we want
- The authorities for the object
- The text for documentation.
- Replace yes or no
- Error Feedback structure
read this previous iSeries EXTRA column.)
D CrtUsrSpc PR ExtPgm('QUSCRTUS')
D UsrSpcName 20 Const
D SpaceAttr 10 Const
D SpaceSize 10i 0 Const
D SpaceInit 1 Const
D SpaceAuth 10 Const
D SpaceText 50 Const
D Replace 10 Const Options(*NoPass)
D ErrorFeedbk Like(ErrorInfo) Options(*NoPass)
We've defined the error feedback data structure below. This is a standard feedback structure used by many APIs.
D ErrorInfo DS
D BytesAv 10U 0 Inz(%Size(ErrorInfo))
D BytesUsed 10U 0
D ExpID 7A
D Reserved 1A
D ExcData 80A
The next API we need is "Get Pointer to User Space"-QUSPTRUS. Well rename it Ptr2UsrSpc.
The parameters are simple:
- The qualified user name of the user space (as above)
- The pointer field where we want the API to place the address of the user space object we created
The next API we need is "List ILE Program Information"-QBNLPGMI, which was renamed in our prototype to ListPgmInf.
D ListPgmInf PR ExtPgm('QBNLPGMI')
D UsrSpcNam 20A Const
D Format 8A Const
D QualPgmName 20A Const
D ErrorFeedbk Like(ErrorInfo)
The parameters we pass to this API are:
The qualified user space name
The data format required
The name of the program or programs for which we want information
The error feedback structure
Let's more closely examine the second parameter: the format name. Many list APIs have multiple types of information that can be returned about the items in the list. A format is associated with each type of information. In our case, of the many types of information we can get about ILE programs, the information we require is the list of service programs referenced by each of the ILE programs that we find. In the API documentation API documentation, we find that this information is associated with the format named PGML0200. If we had wanted information about the modules used to create the program, we would have used format PGML0100 instead. The API documentation also details the layout of the information for each item in the list it will build. In this case, it shows us that each item will contain:
CHAR(10) Program name
CHAR(10) Program library name
CHAR(10) Bound service program name
CHAR(10) Bound service program library name
CHAR(16) Bound service program signature
Looks like a great use for a data structure. We've defined the appropriate data structure below.
D SrvPgmInf DS Based(pSrvPgmInf)
D Pgm 10A
D PgmLib 10A
D BndSrvPgm 10A
D SrvPgmLib 10A
D Sig 10A
Notice the use of the Based keyword. Its storage is based on the pointer pSrvPgmInf. We'll put an address into this pointer later.
Because we're using a list API, we need one more data structure to describe the header information, which provides the basic information needed to process the list.
D HeaderInfo DS Based(pHeaderInfo)
D GenericHdr 124A
D ListOffset 10I 0
D ListSize 10I 0
D NbrEntries 10I 0
D EntryLen 10I 0
Most notably, the header information identifies the location of the first entry in the list (ListOffSet), the number of entries (NbrEntries) and the length of each entry (EntryLen). Because this header information is at the beginning of the user space, we must base this data structure on a pointer. We'll get the value for this pointer from the aforementioned "Get Pointer to User Space" API.
We have one last API to prototype. This is one we suspect most of you have used before: QCMDEXC. Therefore we won't describe the parameter details. We'll use this to issue the Update Program (UPDPGM) command when we find a program that references the service program.
D UpdPgm PR ExtPgm('QCMDEXC')
D CmdString 500A Options(*VarSize) Const
D CmdLength 15P 5 Const
Now for the Calc specs. It's possible the user space already exists, so we start by calling the Ptr2UsrSpc API to try to get a pointer to it. By checking the BytesUsed field in the error feedback data structure, we can determine if we get an error. We'll make the bold assumption that any error is because the user space doesn't exist and call the CrtUsrSpc API to create it, followed by Ptr2UsrSpc again. By now, we should have a value in the pHeaderInfo pointer field that the HeaderInfo data structure is based on. It's now safe to use that data structure.
C CallP Ptr2UsrSpc ('SRVPGMINFOQTEMP' :
C pHeaderInfo : ErrorInfo )
C If BytesUsed > 0
C CallP CrtUsrSpc ('SRVPGMINFOQTEMP' :
C 'DTA' : 10000 : '*' : '*ALL' :
C 'Srv Pgm Info': '*YES' : ErrorInfo)
C CallP Ptr2UsrSpc('SRVPGMINFOQTEMP' :
C pHeaderInfo : ErrorInfo )
Next we call the ListPgmInf API passing the required parameters, including the list of programs we want information for, which in our example is *ALL programs in the library whose name was passed to the program as a parameter.
C CallP ListPgmInf('SRVPGMINFOQTEMP': 'PGML0200'
C : AllPrograms + Library: ErrorInfo)
Once the API completes its processing, we have the required information in the user space and can begin processing it. We know that the first list item begins ListOffSet bytes from the beginning of the user space. So we can now set the basing pointer for our SrvPgmInf DS, which represents the list details, by adding the ListOffSet value to the pHeaderInfo pointer. We can now reference the data in the SrvPgmInf data structure.
C Eval pSrvPgmInf = pHeaderInfo + ListOffset
From here, the logic is mostly "ordinary" RPG logic, with the exception of the pointer manipulation to move through the list. (Note: We know how many entries to process (NbrEntries) and the length of each entry (EntryLen) from the HeaderInfo data structure.) As we loop through the list, we update any program that references our target service program. Then we move to the next list item by taking the pointer to the current item and adding the entry length.
Pretty simple, isn't it? User spaces, pointers and list APIs are really not difficult to use once you know the basics. Clearly, this program could be made more robust and functional than we've illustrated here. We kept the program as simple as possible to accomplish the task. We'll leave it up to you to use this as an example to write your own.