QUSLOBJ can be used as both an enhancement to and even a replacement for QUSROBJD, and this article shows you how.
As I detailed in a previous article, QUSROBJD allows you to see all the details of an object. However, it is specifically designed to return the data for a single object. For example, if you want to get the information for all the programs in a library (is this foreshadowing?), you'd need to have a way to list all those objects. QUSLOBJ is one way to do that, and it provides two basic approaches.
The Basics of the List APIs
The list APIs all work the same way. Please note that the term "list APIs" is a specific term not to be confused with the (newer) "open list APIs." Processing open list APIs is a separate topic that we will address another day. While the open list APIs were introduced in V3R6, the simpler list APIs have been around pretty much since the beginning. As such, they've received extensive coverage over the years, but today's article will, as I always try to do, enhance that coverage into the modern world of free-format RPG.
All list APIs work like this:
- Create a user space.
- Call the API to load entries into the user space.
- Process those entries.
Using a User Space
Creating and accessing a user space has been thoroughly documented, but let me give you the free-format prototypes that I've been using. I use two: one to create the user space (QUSCRTUS) and one to pull some of the data out of the user space (QUSRTVUS). I find it interesting that even with something this simple, there are two ways to do it. The approach in this article is to create a standard data structure and copy the data from the user space into the data structure. The alternative is to create a based data structure and change the basing pointer to walk through the user space directly. Either way is acceptable, and the based approach uses less memory and is probably even a bit faster since you don't have to copy the memory. At the same time, I find that basing pointers can sometimes obscure things a little bit, so I don't always use them. Perhaps I'll write a subsequent article in which I compare the two approaches.
In any case, here are the prototypes I use:
// QUSCRTUS - Create User Space
dcl-pr QUSCRTUS extpgm;
iUserSpace char(20) Const;
iSpaceAttr char(10) Const;
iSpaceLen int(10) Const;
iSpaceVal char(1) Const;
iSpaceAuth char(10) Const;
iSpaceText char(50) Const;
iSpaceRepl char(10) Const;
ErrorDs char(32767) Options( *VarSize );
end-pr;
// QUSRTVUS - Retrieve User Space Data
dcl-pr QUSRTVUS extpgm;
iUserSpace char(20) Const;
iStart int(10) Const;
iLength int(10) Const;
oData char(32767) Options( *VarSize );
end-pr;
Listing 1: These are the two prototypes I use to create a user space and retrieve data from it.
The first API creates the user space, and the second retrieves some data from it. You may have noticed that when I create a prototype for an API, I leave the name of the prototype the same as the external program or procedure. This is my own convention; I like to always know the IBM name of the API. Other people provide meaningful names for the APIs, such as CreateUserSpace, which in turn might make the code a little more readable. It's up to you which approach works better, but in either case, I highly recommend that you use free-format RPG to define your prototypes; it's a great way to learn how to use the language.
Navigating the List
All of the list APIs use the same basic structure. IBM has provided a wonderful overview of this process complete with diagrams, details, and examples. You can start with the overview here. The concept is simple: the list APIs fill the specified user space with data that includes a header in the first positions. This header in turn has pointers (offsets, actually) to the rest of the data, including the actual list of entries. By using that generic header, you can future-proof your program so that even if the API changes in future releases, your code will still work.
IBM provides a great semi-graphical diagram of the user space layout used by all the list APIs:
Figure 1: This is IBM's simple graphical layout of the data; note the offsets to other parts of the data.
As you can see, the user space starts with a 64-byte user area, which in my experience doesn't seem to ever have much in it. Then comes the generic header area, which is very important, especially the offset to the list data. Here is the definition I use in my free-format RPG programs.
dcl-ds QUSL_ListHeader template qualified;
UserArea char(64);
GenHdrSize int(10);
Release char(4);
Format char(8);
API char(10);
CreatedTS13 char(13);
InfoStatus char(1);
SpaceSize int(10);
InputParmOffset int(10);
InputParmSize int(10);
HeaderOffset int(10);
HeaderSize int(10);
ListDataOffset int(10);
ListDataSize int(10);
ListEntryCount int(10);
ListEntrySize int(10);
end-ds;
Listing 2: This is the free-format RPG definition of the generic header in Figure 1.
This includes both the 64-byte user area and the generic header, so you can retrieve it from the start of the user space. Once retrieved, the generic header then provides an offset to the list data, as well as a count of entries and the size of each entry. Armed with just that information, you can now process the entire list!
QUSLOBJ, the List API Version of QUSROBJD
Let's take a look at how we can use QUSLOBJ in place of QUSROBJD. Please note that although QUSLOBJ retrieves the information for many objects at once, each entry is the same with the contents defined by the format parameter. Let's start by inspecting the API parameters.
// QUSLOBJ - List objects
dcl-pr QUSLOBJ extpgm;
iUserSpace char(20) Const;
iFormat char(8) Const;
iObjQName char(20) Const;
iType char(10) Const;
end-pr;
Listing 3: Here’s the prototype for the QUSLOBJ API.
If you refer to the previous article on QUSROBJD, you'll see that the parameters are similar. Instead of a data structure and length, you specify the user space that you created using QUSCRTUS. Next is the format of the data, which we'll go over in more detail in a moment. Then you have the qualified name and the type of the objects whose data you want to retrieve. The primary difference in this case is that you can specify generic values for these parameters, which is what allows you to retrieve a list of objects.
The second parameter, the format parameter, determines the layout of the entries. QUSLOBJ is similar to QUSROBJD in that each format builds on the others: OBJL0200 contains all the fields from OBJL0100 along with some additional fields, OBJL0300 starts with the same fields as OBJL0200, and so on. There are currently seven formats, each one with more information (and requiring correspondingly more time for each entry).
Here are the first couple of layouts:
dcl-ds T_OBJL_0100 template qualified;
Object char(10);
Library char(10);
ObjectType char(10);
end-ds;
dcl-ds T_OBJL_0200 template qualified;
InfoStatus char(1);
ExtObjAttrib char(10);
Description char(50);
UserAttr char(10);
reserved char(7);
end-ds;
Listing 4: These are the first two substructures in the QUSLOBJ API.
dcl-ds T_OBJL0100 qualified template;
A0100 likeds(T_OBJL_0100);
end-ds;
dcl-ds T_OBJL0200 qualified template;
A0100 likeds(T_OBJL_0100);
A0200 likeds(T_OBJL_0200);
end-ds;
Listing 5: The actual list entries are defined by concatenating the substructures.
You select the amount of data you want through the format variable and then pull each entry into the matching structure.
Using the API
I recently wrote a tool that needed access to the last-used date of all the objects in a library. To do that, I had to use the sixth data structure. The code looked like this:
// Header and detail list structures
dcl-ds ListObj likeds(QUSL_ListHeader);
dcl-ds dsOBJL likeds(T_OBJL0600);
// Create user space
CreateUserSpace(USObj);
QUSLOBJ( USObj: 'OBJL0600': '*ALL ' + iLib : '*ALL');
// Retrieve list header from user space
QUSRTVUS( USObj: 1: %size(ListObj): ListObj);
// Do for number of objects in the userspace
wPos = ListObj.ListDataOffset + 1;
for count = 1 to ListObj.ListEntryCount;
QUSRTVUS( USObj : wPos: %size(dsOBJL): dsOBJL);
processEntry(dsOBJL);
wPos += ListObj.ListEntrySize;
endfor;
Listing 6: This is the code to process all the objects in a library.
This simple code processed the entire list of objects in the library. You might worry about one thing, though. The size of the entry gets larger and larger with each level, to the point where the OBJL0600 entry I am using is 576 entries long. Given the limitation for a user space of 16MB, that means that I'm limited to a little less than 28,000 objects. One way to get around that limitation is to use a combination of QUSLOBJ and QUSROBJD. Use the OBJL0100 format, which returns just the object name, library, and type, and then use that to call QUSROBJD to get the information. That then allows you to retrieve a half million objects, which ought to be enough for nearly any purpose.
The other option is to use the open list API QYGOLOBJ. The open list APIs allow you to process a list with an effectively unlimited number of objects. There are other benefits to these APIs as well, but we'll address those in a subsequent installment. If you want to jump ahead in your studies, though, you can start with Bruce Vining's excellent article on QYGOLOBJ. And until next time, enjoy your APIs!
LATEST COMMENTS
MC Press Online