There are several kinds of APIs out there. One kind performs a useful system function, such as executing a command or changing some job attribute. Another retrieves a short piece of information, such as the description of an object. And among the other kinds, theres one that produces a list of items into a user space. Its this last one that usually turns the stomach of the beginner, and justifiably so. This article briefly explains how list APIs work and how to retrieve from user spaces the information they produce.
Of course, you may be wondering why you should bother. After all, outfiles have been doing the job wonderfully since the S/38 days. But outfiles are slower than list APIs. And some APIs have no corresponding outfiles.
What Are User Spaces?
User spaces are objects (type *USRSPC) that store information, much like physical files and data areas. Like physical files, they can store an enormous amount of data; like data areas, however, they have no internal structure of any kindtheyre just big containers in which you can dump anything. Its up to you to organize their contents when you write into them.
User spaces are the output medium for list APIs. Take, for example, the List Objects API (QUSLOBJ). It has four required parameters and three optional parameters. Lets look at the required parameters and one optional parameter:
The qualified name of a user space. You have to supply the name of a user space into which the API is to write the list of objects you want to create.
A data format name. There are seven to choose from, but the simplest is OBJL0100. The data format is actually a data structure. In the case of OBJL0100, there are three fields only: the object name, library name, and object typeeach of which is 10 bytes long.
The qualified names of the objects you want listed. You will no doubt want to use
a generic object name (or even *ALL). The library name can be specific, or it can be a special value; you have *LIBL, *USRLIBL, *ALL, *ALLUSR, and *CURLIB to choose from.
The type of object to be listed. The object type would be a value such as *FILE, *PGM, or *ALL.
An error code, in which the API returns information if an error occurs. The error code parameter is another data structure, as you can see in Figure 1. The first subfield tells the API how long the data structure is; I usually initialize this subfield to 96 and replace the asterisk (*) in the last subfield with a 96. When you call QUSLOBJ, the API writes the list of objects to the user space you identified in the first parameter. If the API runs without trouble, the error code parameter will show zero in the second subfield (bytes provided) and blanks in both the error message ID and message data subfields.
Accessing the Data in a User Space
The real challenge comes in reading the information contained in the user space. But before you can read it, you have to retrieve it. To do so, you need another API: the Retrieve User Space (QUSRTVUS) API. Before explaining how QUSRTVUS works, lets digress a bit to explain how the list APIs organize data in the user space. See Figure 2.
At the beginning of the user space, the API writes offsets and lengths to the useful information contained therein. Both are written in binary format (4 bytes for each binary field), but theres no reason to fear them; an offset of 0 merely means the first byte of something. An offset of 573 means the 574th byte of the same something. In fact, the offset just tells you how many bytes you have to add to the first byte in order to reach another.
The Input Section contains a copy of the input parameters you gave the API to do its work. For example, if you called QUSLOBJ to list all objects in QGPL, the Input Section contains *ALL, QGPL, and object type *ALL. Never mind how this is organized, because it is seldom needed.
The Header Section contains information that applies to all the list entries. For example, if you were listing the members of a database file, the Header Section might contain general information about the database file. Again, this information, although useful, is not what youll be after in most cases. Lets skip it.
The List Section is, by far, the most useful one. It contains entries, each one describing a separate item in the list produced by the API just called. For instance, running QUSLOBJ makes each entry an objectwhich would be a record in an outfile.
As you can see in Figure 2, you have to read bytes 125 to 128 to find out on which byte the List Section begins (remember to add 1 to whatever number you obtain from 125 to 128). Bytes 133 to 136 tell you how many entries (objects) are in the list, while bytes 137 to 140 provide the length of each entry.
Armed with this information, you can easily retrieve the list of objects contained in the user space by using the QUSRTVUS API. QUSRTVUS requires the name of the user space from which it is to retrieve data, the beginning byte number, and the number of bytes; it returns the data you requested. In fact, QUSRTVUS works very much like the Retrieve Data Area (RTVDTAARA) command, since RTVDTAARA also requires a name (of the data area, in this case), a beginning byte number, and a number of bytes. And it returns the information you requested.
You can see, then, that the algorithm you should use to read one entry after another is as follows:
1. Read the first 140 bytes in the user space. Store the offset to the List Section in OFFSTL, the number of entries in LSENTQ, and the length of each entry in LSENTS (all binary, 4 bytes long).
2. Read the first entry at byte number OFFSTL + 1, for the number of bytes specified by LSENTS. The information retrieved is placed in a data structure that matches the data format of the list API (such as OBJL0100 for QUSLOBJ).
3. Process that entry. For instance, print the information, or write it to a subfile.
4. Repeat the process for all other entries. The starting byte number for each following entry is equal to the offset to the current entry plus LSENTS. You have to repeat the process a total of LSENTQ times.
Easier Than Subfiles
Actually, processing a user space can be easier than writing a typical subfile program. I have written a generic list API processor program in RPG III. You can see it in Figure 3. It contains the algorithm and data structures mentioned above, ready for use. Before using it, however, you must make changes to adapt the program for the particular list API youll be calling. Lets look at what youd have to do to use the generic processor (GENLSTAPI) to list objects using QUSLOBJ.
There are three externally described data structures: USINPS, USHDRS, and USLSTS, which describe the Input, Header, and List Sections, respectively. Since these data structures change according to the list API youre going to use, you must supply the data structures yourself. One easy way to do so is by defining each one previously as a physical file; as you can see in Figure 4, I have described the List Sections OBJL0100 data format. (You could define the data structures in source members and bring them in with /COPY, but I favor the physical file method, because I can use it with equal ease in any language. The /COPY method doesnt have that flexibility.)
The three data structures are defined in the I-specs and contain a token, (in which x can be I, H, or L), that you have to change to an actual value using SEU. For example, needs to be changed to OBJL0100 in this case. Since I wont be using either the Input Section or the Header Section, I can simply delete those lines from the I-specs. I also have to delete subroutines RTVHS and RTVIS and the EXSR op codes that invoke them.
Two other tokens, and , have to be changed to reflect the actual name of the user space and its library, respectively. For instance, to use a user space name of LIST and put it in QTEMP, change to LIST (followed by four blanks, for a total length of 8 characters), and change to QTEMP (followed by three blanks).
Write appropriate code for the PRCLSE subroutine. This subroutine is called to process each List Section entry, so if you want your program to print the list of objects, code an EXCPT or a WRITE op code there, in addition to whatever code you need to ensure printer overflow conditions.
Write the code for the WRTUS subroutine. This subroutine is meant to be the one that produces the list in the user space. To list objects, for instance, code a call to QUSLOBJ within this subroutine.
Now, you can compile the program. It should be ready for use.
Implementation Notes
To fully implement the generic list processor, you have to define the externally described data structures for the list data formats youre going to use (such as OBJL0100 for QUSLOBJ). I recommend using the list data format name as the file name to keep things simple. Obviously, you dont have to create a physical file for every list data format
contained in the API manualsonly those youll needand you can create them as you go. To save disk space, create each physical file with MBR(*NONE) so the system wont add a member to the file. And create the file in a library that is sure to be in your library list. Instead of creating physical files, you can also create /COPY source members to define the data structures you need. Some words of caution, however: First, the copy member, if written in RPG III, cant be used in an RPG IV programor a COBOL program. Second, you may end up creating copy members that have duplicate field names, something that will produce errors at compile time. If this happened to you when using the externally described method, however, you could rename some of the fields within the program. Third, and last, the /COPY directive itself can give you grief, as it forces you to code a source file name (and perhaps a library) if you use anything other than QRPGSRC.
As you can see, using the list APIs with user spaces is not the nightmare you thought it was. In fact, they may become as easy to use as outfiles, and they certainly show better performance. Using outfiles entails coding a CL program to run the command that produces the outfile, and you may have to issue a file override before you call your RPG program. Besides, the outfile already contains the field names, as chosen by IBM, and those field names (which may not make much sense to you) would result in a less understandable program.
On the other hand, you can take advantage of the generic list processor to quickly clone RPG code, and you need no CL program because you can call all APIs directly from the RPG program. Besides, whatever field names you use depends entirely on you, since it is you who designs the externally described data structures for the list data formats. You can even have two or more list data formats of the same type (e.g., two OBJL0100 data structures) if the application requires it, each using a slightly different externally described data structure, thereby avoiding field name conflicts. Try that using outfiles!
From To Data Type Description
--
1 4 Binary Bytes available.
5 8 Binary Bytes provided.
9 15 Character Error message ID.
16 16 Character (Reserved)
17 * Character Error message data.
From To Data Type Description
--
1 108 Character This information is rarely useful.
109 112 Binary Offset to the Input Section.
113 116 Binary Length of the Input Section.
117 120 Binary Offset to the Header Section.
121 124 Binary Length of the Header Section.
125 128 Binary Offset to the List Section.
129 132 Binary Length of the List Section.
133 136 Binary Number of entries in the List Section.
137 140 Binary Length of each entry in the List Section.
* * Structure Input Section.
* * Structure Header Section.
* * Structure List Section. *****************************************************************
*
* GENLSTAPI = Generic List API Processor.
*
* Instructions:
*
* 1. Copy this source member before making any changes.
Figure 1: Error code parameter
Figure 2: Organization of a user space
* Give the new source member a more suitable name.
* Use option 3 of WRKMBRPDM or the CPYSRCF command.
*
* 2. Edit the _copy_ to make your changes:
*
* a. Replace all variable tokens with the right names:
*
* in which youve described the fields
* contained in the User Space Input Section.
*
*
* in which youve described the fields
* contained in the User Space Header Section.
*
*
* in which youve described the fields
* contained in the User Space List Section
* (the APIs data format).
*
*
*
*
*
* b. Code in the PRCLSE subroutine whatever you want
* done with each list entry. Field names come from
* the
*
* c. Code in the WRTUS subroutine whatever code is
* necessary to list something into a user space.
* Typically, what it takes is calling a list API
* passing suitable parameter values.
*
* 3. If necessary, write additional code. For example,
* if you intend to show the output in a printed list,
* youll have to add a PRINTER file, C-specs to write
* to the file (EXCPT or WRITE), and O-specs if the
* printer file is program-described.
*
* 4. Compile the copy of the source member using the
* CRTRPGPGM command.
*
*****************************************************************
* API error code.
*
IAPIERR DS
I I 96 B 1 40BYTAVL
I B 5 80BYTPRV
I 9 15 EXCPID
I 17 96 EXCPDT
*================================================================
* User space header.
*
IUSHDR DS
I B 109 1120OFFSTI
I B 113 1160SIZEI
I B 117 1200OFFSTH
I B 121 1240SIZEH
I B 125 1280OFFSTL
I B 129 1320SIZEL
I B 133 1360LSENTQ
I B 137 1400LSENTS
*================================================================
* API data formats: Input, header, and list sections.
*
IUSINPS E DS
IUSHDRS E DS
IUSLSTS E DS
*================================================================
* Binary variables.
*
IBINARY DS
I B 1 40STRPOS
I B 5 80DTALEN
I B 9 120OFFSET
I B 13 160USSIZE
*****************************************************************
* Mainline.
*
C MOVEL
C MOVEL
C USNAME CAT USLIBR QUSNAM P
*
C EXSR CRTUS
C EXSR WRTUS
C EXSR RTVUS
C EXSR DLTUS
*
C MOVE *ON *INLR
C RETRN
*================================================================
* CRTUS: Create user space.
*
C CRTUS BEGSR
C CALL QUSCRTUS
C PARM QUSNAM
C PARM USXATR
C PARM 1000000 USSIZE
C PARM *BLANK USVAL
C PARM *USE PUBAUT
C PARM *BLANK USTEXT
C PARM *YES USRPLC
C PARM APIERR
C ENDSR
*================================================================
* DCLVAR: Declare program variables.
*
C DCLVAR BEGSR
C MOVE *BLANK BYTE 1
C MOVE *BLANK CHR050 50
C Z-ADD0 LNGINT 150
C MOVE *BLANK NAME 10
C MOVE *BLANK QNAME 20
*
C *LIKE DEFN LNGINT N
C *LIKE DEFN NAME PUBAUT
C *LIKE DEFN QNAME QUSNAM
C *LIKE DEFN NAME USLIBR
C *LIKE DEFN NAME USNAME
C *LIKE DEFN NAME USRPLC
C *LIKE DEFN CHR050 USTEXT
C *LIKE DEFN BYTE USVAL
C *LIKE DEFN NAME USXATR
C ENDSR
*================================================================
* DLTUS: Delete user space.
*
C DLTUS BEGSR
C CALL QUSDLTUS
C PARM QUSNAM
C PARM APIERR
C ENDSR
*================================================================
* PRCLSE: Process list section entry.
*
C PRCLSE BEGSR
C ENDSR
*================================================================
* RTVHS: Retrieve header section.
*
C RTVHS BEGSR
C OFFSTH ADD 1 STRPOS
C CALL QUSRTVUS
C PARM QUSNAM
C PARM STRPOS
C PARM SIZEH DTALEN
C PARM USHDRS
C ENDSR
*================================================================
* RTVIS: Retrieve input section.
*
C RTVIS BEGSR
C OFFSTI ADD 1 STRPOS
C CALL QUSRTVUS
C PARM QUSNAM
C PARM STRPOS
C PARM SIZEI DTALEN
C PARM USINPS
C ENDSR
*================================================================
* RTVLS: Retrieve list entries.
*
C RTVLS BEGSR
C OFFSTL ADD 1 OFFSET
*
C 1 DO LSENTQ N
C CALL QUSRTVUS
C PARM QUSNAM
C PARM OFFSET STRPOS
C PARM LSENTS DTALEN
C PARM USLSTS
C ADD LSENTS OFFSET
* Process list section entry.
C EXSR PRCLSE
C ENDDO
C ENDSR
*================================================================
* RTVUS: Retrieve information from user space.
*
C RTVUS BEGSR
C EXSR RTVUSH
C EXSR RTVIS
C EXSR RTVHS
C EXSR RTVLS
C ENDSR
*================================================================
* RTVUSH: Retrieve user space header.
*
C RTVUSH BEGSR
C CALL QUSRTVUS
C PARM QUSNAM
C PARM 1 STRPOS
C PARM 140 DTALEN
C PARM USHDR
C ENDSR
*================================================================
* WRTUS: Produce output to user space.
*
C WRTUS BEGSR
C ENDSR
Figure 3: Generic list API processor
A R OBJL010R
A OBJNAM 10A
A OBJLIB 10A
A OBJTYP 10A
LATEST COMMENTS
MC Press Online