Starting with Version 1, Release 3 of OS/400, IBM has made available several openness APIs. An API, or application programming interface, allows you to access several system functions. Types of APIs that IBM is documenting with V1R3 include system programming APIs, office APIs and PC support APIs, among others.
The system programming APIs, which will be discussed both in this article and "A List Processor API," which begins on the next page, fall into several broad categories. Object APIs are used to get lists of objects and information about them, similar to the WRKOBJ or DSPOBJD commands. Using the database file APIs, you can retrieve lists of database file members, member descriptions and record formats in files (instead of using the DSPFD command), and lists of fields in files (similar to the DSPFFD command).
Other APIs let you create lists of spooled files and lists of jobs. Many programs have been written over the years to gain access to these lists and manipulate the system based upon their content, using the WRKSPLF, WRKSPLFA and WRKACTJOB commands. There are other APIs to create Machine Interface (MI) programs, change storage pool attributes, and display and process a command line on your screens. Other APIs are provided to work with new types of user objects: user spaces, user queues and user indexes.
How do I Retrieve Information From an API?
Prior to these APIs, system information was accessible by using outfiles on a few selected commands. Where the system did not provide an outfile, you could get at the information with the more laborious technique of directing the output of a command to the printer, then copying the spool file and reading it into a program. System programming APIs eliminate the need for either of these techniques.
In some cases, you can now directly access system information from within your program. In other cases, the information that you want is placed by the API into a new type of system object -- the user space, which is used in place of an outfile or a spool file. Because a user space is not a file, your programs access the contents of the user space using additional APIs created just for that purpose.
V1R3 APIs that provide lists of information use the user space object. To use those APIs, it is important that you have an understanding of what a user space is, how to create it and how to retrieve information from it. When you use a list-producing API, the API generates quite a lot of information into the user space, in addition to the list. The generated header section tells you about the API that generated the list following the header, and also tells you about the length and count of the list entries.
It is possible to use the various List APIs without having an in-depth understanding of how those APIs format the user space. For example, you can use the program shown in "A List API Processor" without having to know all the details about the user space. But if you want to know more about how the user space object is used with the List APIs, this article is for you.
What is a User Space?
Similar to a data area, a user space is an object that you create which is a collection of bytes. Unlike a data area, the user space can contain up to 16MB, compared to the 2000 bytes allowed in a data area. Using a pointer- capable language, you can retrieve and manipulate the contents of a user space with pointers.
Somewhat curiously, V1R3 provides a program that you call to create a user space rather than including an OS/400 command. Parameters used to call this program are shown in 1. Key in the external physical file as shown in 1, and reference it in RPG programs as an externally described data structure. 2 shows an example of creating a user space from an RPG program.
Somewhat curiously, V1R3 provides a program that you call to create a user space rather than including an OS/400 command. Parameters used to call this program are shown in Figure 1. Key in the external physical file as shown in Figure 1, and reference it in RPG programs as an externally described data structure. Figure 2 shows an example of creating a user space from an RPG program.
There are a few simple rules to follow when setting the parameters. Since a user space is an object, it will be placed into a library. Identify the name of the user space and the library in the CSQLSN field. The name of the user space must appear in positions 1 - 10 of this field, and the library must appear in positions 11 - 20. Note that you do not enter this field as library/name. You must left-justify both the user space name and the library name. For the library name, specify a library or enter the special value *CURLIB.
Although the extended attribute doesn't have any real significance, you can use the field to further describe the user space. If you are using the user space for a List Object API, you might want an extended attribute of LSTOBJ. The extended attribute can be seen on WRKOBJ displays and listings. Use the extended attribute to further qualify the type of object. An object of type "program" may have extended attributes of CLP, RPG, CBL, etc.
The initial size can be a value from 1 to 16,776,704. Since you are creating an object, the minimum size of the object will be 1024 bytes, used by the system. When the user space is filled, the system automatically extends the size of the space as needed, up to the maximum 16MB. So when you specify an initial size, try to estimate it as something that you reasonably need. Otherwise, you will needlessly consume disk space with large amounts of user spaces. The length of time that the system takes to create a user space is proportional to the size of the space, since it has to be initialized to the beginning value that you specify.
Any initial value may be specified for the user space. I can't imagine initializing a user space to values other than blank (hex 40), null (hex 00) or zero (hex F0), but you can set any value you want.
As with other objects, you can specify the public authority and include a text description of the object.
Manipulating a User Space
OS/400 does provide a DLTUSRSPC command. The only parameters needed are the user space name and the library. As with other types of objects, you can save and restore the user space. You can also move, rename and create duplicates of the space.
Three APIs are provided that allow you to access the user space, making it useful. These are: Retrieve User Space (QUSRTVUS), Change User Space (QUSCHGUS) and Pointer to User Space (QUSPTRUS). When you use the list APIs, they put data into the user space. You then get the data with the Retrieve User Space or Pointer to User Space APIs. For user spaces that you create for your own purposes, use the Change User Space API to format data into the user space, and the other APIs to retrieve the data.
Examples of these APIs are included in the companion article in this issue, "A List API Processor."
User Spaces and List APIs
The List APIs provided with OS/400 are described in the System Programmer's Interface Reference (SC21-8223). When you use any of the List APIs, the information is placed into a user space that you have previously created. To help locate the information, the APIs also format the first part of the user space with control information.
The rest of this article describes the control information and shows you how to access that information. Understanding this will help you make use of the List APIs.
When you use any of the List APIs (i.e., to create a list of objects, or a list of fields in a file), you tell the List API to place the data into a specific user space that you created. The List APIs, in addition to placing information for a specific type of list into the user space, precede that information with a common header which is used to determine how many items are in the list, the length of each of the list entries, and how the list was generated. This information is necessary in order to conveniently process the List API data.
You usually process a list like a sequential file read; however, unlike a file, there is no concept of an "end of file" (or list) indicator being returned to your program. The more you know about the list, the better you can control the list processing.
Creating a List API Header
3 provides the information that a List API places at the start of the user space. As with the Create User Space parameters shown in 1, you can enter this description as an externally described physical file, and reference it in your programs as an externally defined data structure.
Figure 3 provides the information that a List API places at the start of the user space. As with the Create User Space parameters shown in Figure 1, you can enter this description as an externally described physical file, and reference it in your programs as an externally defined data structure.
To illustrate the List API process, we will create a user space, use a List API to put information into it, and dump the contents of the user space. We can then correlate the parameters shown in 3 with the user space.
To illustrate the List API process, we will create a user space, use a List API to put information into it, and dump the contents of the user space. We can then correlate the parameters shown in Figure 3 with the user space.
Create and run the CL program shown in 4, or simply enter the CL commands interactively to follow along with the article. Be sure that you enter the blank pad spaces in the parameters. The result is a one-page "AS/400 Dump," similar to 5. Run the commands and get the dump printed if you can, as we will be marking it up as we describe the contents of the user space.
Create and run the CL program shown in Figure 4, or simply enter the CL commands interactively to follow along with the article. Be sure that you enter the blank pad spaces in the parameters. The result is a one-page "AS/400 Dump," similar to Figure 5. Run the commands and get the dump printed if you can, as we will be marking it up as we describe the contents of the user space.
You can spend some time looking over this dump if you want, but the only section that we'll review is the part in the middle, below the heading "Space -." To help you follow the article, mark your dump with the letters A through H across each of the columns, as shown in 5. There are two parts of the dump that we'll be concerned with: the offset section, shown as the leftmost column of six-digit hexadecimal numbers, and the contents section, shown as eight columns across the page.
You can spend some time looking over this dump if you want, but the only section that we'll review is the part in the middle, below the heading "Space -." To help you follow the article, mark your dump with the letters A through H across each of the columns, as shown in Figure 5. There are two parts of the dump that we'll be concerned with: the offset section, shown as the leftmost column of six-digit hexadecimal numbers, and the contents section, shown as eight columns across the page.
In the contents section, each byte is represented by two adjacent characters. For example, the first byte of the user space is a blank space character, shown as "40." You see this on the line that has offset "000000" in column A. Be sure you understand this coordinate system before moving on, as I will be referring you to the contents section using this notation.
Information Shown in the Contents Section
Using the dump and the chart shown in 3, examine the contents section of the user space. The first 64 bytes of the user space are reserved for the caller. Information may be inserted into this part of the space with the Change User Space API. You might want to do this if you are generating a number of user spaces in a program and need to identify them later. On the dump, these 64 bytes are set to blanks. This is offset lines 000000 and 000020, all columns. (Each column represents four bytes. There are eight columns of four bytes, times two, so that gives us the 64 bytes.)
Using the dump and the chart shown in Figure 3, examine the contents section of the user space. The first 64 bytes of the user space are reserved for the caller. Information may be inserted into this part of the space with the Change User Space API. You might want to do this if you are generating a number of user spaces in a program and need to identify them later. On the dump, these 64 bytes are set to blanks. This is offset lines 000000 and 000020, all columns. (Each column represents four bytes. There are eight columns of four bytes, times two, so that gives us the 64 bytes.)
The next field, size of generic header, is found at offset line 000040, column A. This tells us the length of the header information, in this case, hex 80 (decimal 128) bytes. The header information, which starts at offset 000040, takes up four lines of our dump, lines 000040, 000060, 000080 and 0000A0. Our List API data starts at offset 0000C0. Keep this offset in mind, as you will see it again soon.
The structure level field is found on offset line 000040, column B. This is always set to 0100. You can actually see this field in the text column to the right of the hex columns. The manual explains that this field "contains the release and level of this structure... this is not the level of the system." I presume that future modifications of the List APIs will place a different value into this field, so that your programs can determine which level of the List API generated the information.
When you ran the CL program or commands to create the user space and list the contents of QTEMP into the user space, you specified that the List Objects API was to list information using the OBJL0100 (minimum information) format. Look back at 4 and examine the call to program QUSLOBJ to verify this. Then, look at offset line 000040, columns C and D. The List API places the name of the format that we used into those positions, in the format name field. If you want to try something different, you can rerun the commands using formats OBJL0200 through OBJL0700, and examine each of the user space dumps to see the differences.
When you ran the CL program or commands to create the user space and list the contents of QTEMP into the user space, you specified that the List Objects API was to list information using the OBJL0100 (minimum information) format. Look back at Figure 4 and examine the call to program QUSLOBJ to verify this. Then, look at offset line 000040, columns C and D. The List API places the name of the format that we used into those positions, in the format name field. If you want to try something different, you can rerun the commands using formats OBJL0200 through OBJL0700, and examine each of the user space dumps to see the differences.
Closely allied with the format name is the name of the API that generated the list. This is shown at offset line 000040, columns E, F and half of G (since the program name is a 10-character field). Using the API name field and the format name field, you should be able to examine, manually or in a program, any user space produced by a List API and identify how the list was generated.
The date and time the list was produced is recorded in the next 13 bytes, starting at offset line 000040, the second half of column G, and continuing through column H, onto the next line (000060), all of column A and the first three bytes of column B. The first byte is the century (0=20th, 1=21st), the next six, the date (year, month, day), and the last six bytes are the time the list was created (hours, minutes, seconds, using the 24-hour clock).
The last byte of offset line 000060, column B, is the information status field. This tells you about the list: "I" indicates incomplete information, "P" is partial and accurate, and "C" is complete and accurate. You should examine this field in programs, and if the information is critical, use it only when you have a "C" value.
The next several fields are of great importance to programs that process List API user spaces since these fields identify where in the user space the list starts, the length of each entry, and the number of entries. When you write programs that process List API user spaces, it is recommended that you always use the values in these fields since the values represent the actual values within the user space. Also, the values are correct for the version of the API that produced the list. IBM warns in its manual that the information returned through any of the APIs is subject to extension, meaning that offsets within the user space may change with each new release and modification. So you must not hard code values into your programs, but rather, use the values that are placed into the List API header by the List API.
The first of these fields is the total size of the user space used. This includes the List API header and all of the list data placed into the user space. In the sample dump, this is found on offset line 000060, column C; the sample shows this value as x'0000011E,' meaning that we have used a total of 286 bytes of the user space. In the sample dump, we use everything from offset line 000000, column A, through and including offset line 000100, the first half of column H.
The next field, offset to input parameters, points to a section of the user space where the List API has recorded your request. In our dump, this field is found on offset line 000060, column D, and shows a value of x'000000C0.' Using this value, you can look directly at offset line 0000C0: this is the start of the input parameters. The next field -- input parameter section size -- indicates how many bytes are used to store this information. This is illustrated on offset line 000060 in column E as x'40,' or 64 bytes.
So, starting at offset line 0000C0, we have 64 bytes of information telling us what the API received as a request. The information shown on the dump in this section may be correlated with the CL command that you used to call the QUSLOBJ program.
The dump shows that we requested information to be listed into user space QTEMP/TESTSPACE, using format OBJL0100. We asked for *ALL objects in QTEMP, object type *ALL, to be listed with the List API. Use this information either in your program or when you need to examine a user space to determine how the list was created.
The next two fields, offset to header section and header size, are not used with the List APIs. Therefore, these two fields are shown as x'00000000' on offset line 000060, columns F and G.
The offset to the list data section field is probably the most critical of all of the fields that you will use in programs that process List API user spaces. This is the field that I particularly had in mind when I warned against not hard coding values in your programs. I mistakenly hard coded a value, used a different type of List API, and started retrieving information from the user space at the wrong location. In the dump, this field is shown on offset line 000060 in column H, with a value x'00000100.' Use this value to look directly at offset line 000100; this is where the actual list starts.
The total length of the list is shown at offset line 000080, column A. In our example, this is x'1E' or 30 bytes long.
The number of list entries field, shown on offset line 000080, column B, is of utmost importance in programs that process List API user spaces. In our example, this is shown as a value 00000001, which means that we have only one entry in the list. You can see how this varies by creating or moving some more objects into QTEMP and rerunning the sample program shown in 4. The number of items in the list will increase when you have more objects being listed. The reason why this number is so important is that it provides an easy method to process the list: you can simply enter a DO loop in your program that is performed the number of times given by this number. Each pass through the loop, you can use the Retrieve User Space command to retrieve the next list entry. An example of this technique is given in the companion article, "A List API Processor."
The number of list entries field, shown on offset line 000080, column B, is of utmost importance in programs that process List API user spaces. In our example, this is shown as a value 00000001, which means that we have only one entry in the list. You can see how this varies by creating or moving some more objects into QTEMP and rerunning the sample program shown in Figure 4. The number of items in the list will increase when you have more objects being listed. The reason why this number is so important is that it provides an easy method to process the list: you can simply enter a DO loop in your program that is performed the number of times given by this number. Each pass through the loop, you can use the Retrieve User Space command to retrieve the next list entry. An example of this technique is given in the companion article, "A List API Processor."
When using a List API, each of the entries in the list is the same length. Generating a list is similar to writing to a fixed length file: each list entry (record) is the same length, one following another. The size of each entry on the list is shown as a field in the dump, at offset line 000080, column C. In this example, each entry is x'1E', or 30 bytes long. You can see how this varies by rerunning the List API, and using formats OBJL0200 through OBJL0700 instead of OBJL0100. Each of the formats generates additional information, so the entry length will increase.
When retrieving list entries from a user space, you need to know three things: where the list data starts, the number of list entries, and the length of each entry. Use the Retrieve User Space API to specify a starting offset into the user space, and also the number of bytes to retrieve. For each subsequent call to the Retrieve API, increment the starting position by the length of each entry. Perform the Retrieve API for the number of times specified in the number of list entries field.
At this point, we have reviewed the header information that List APIs generate into user spaces. Try some additional lists by moving objects into QTEMP or by listing a different library on the QUSLOBJ command shown in the sample program. You can then work through some of the dumps of the user space. Doing this a few times will increase your knowledge of working with the List APIs.
Other Uses of User Spaces
Although this article has described user spaces in the context of using them with the List APIs, you can use a user space for any other purpose that you want, apart from the APIs. When you create and use a user space for your own purposes, you are responsible for its entire contents. None of the header information described above is generated, or applicable, to your specific usage.
I am still investigating where I will employ user spaces in my programming. There should be some utility here as a space without the usual constraints imposed by other types of space simulations that I have used, e.g., using a large array or sequential file to store work information. Both of those techniques are constrained to fixed length records.
By designing your own headers and processing techniques, user spaces can store and access data any number of ways. As additional uses for user spaces are developed, Midrange Computing will describe how to use them. In the meantime, we would welcome hearing from you about applications in which you use the user space object in your programming.
APIs and the Anatomy of a User Space
Figure 1 Parameters for Create User Space (QUSCRTUS)
A***************************************************************** A* QUSCRTUS: create user space request parms A***************************************************************** A R CRTUS TEXT('Create User Space rqst parms') A CSQLSN 20 TEXT('Qualified Space name ') A CSEXTA 10 TEXT('Extended attribute ') A CSINSZ 9B 0 TEXT('Initial size ') A CSINVL 1 TEXT('Initial value ') A CSPAUT 10 TEXT('Public authority ') A CSTEXT 50 TEXT('Text description ')
APIs and the Anatomy of a User Space
Figure 2 Create User Space in RPG
ICRTUS E DSQUSCRTUS C MOVEL'USRSPACE'CSQLSN C MOVEL'QTEMP' W10 10 C MOVE W10 CSQLSN C MOVEL'LSTAPI' CSEXTA C Z-ADD10000 CSINSZ C MOVEL*BLANKS CSINVL C MOVEL'*ALL' CSPAUT C MOVEL*BLANKS CSTEXT C* C CALL 'QUSCRTUS' C PARM CSQLSN C PARM CSEXTA C PARM CSINSZ C PARM CSINVL C PARM CSPAUT C PARM CSTEXT
APIs and the Anatomy of a User Space
Figure 3 List API Space Header
A***************************************************************** A* QUSRTVUS: RETRIEVE USER SPACE GENERIC LAYOUT A***************************************************************** A R RTVUS TEXT('Retrieve User Space layout ') A RSRSCL 64 TEXT('Reserved for caller ') A RSHDSZ 9B 0 TEXT('Size of generic header ') A RSSTLV 4 TEXT('Structure level ') A RSFMTN 8 TEXT('Format name ') A RSPGM 10 TEXT('Pgm/API generating list ') A RSLSDT 13 TEXT('List written date/time ') A RSINST 1 TEXT('Information status ') A RSUSSZ 9B 0 TEXT('Total size of space used ') A RSOFIP 9B 0 TEXT('Offset - input parms ') A RSSZIP 9B 0 TEXT('Size - input parms ') A RSOFHD 9B 0 TEXT('Offset - header ') A RSSZHD 9B 0 TEXT('Size - header ') A RSOFLS 9B 0 TEXT('Offset - list data ') A RSSZLS 9B 0 TEXT('Size - list data ') A RSCTLS 9B 0 TEXT('Count - list data ') A RSSZEN 9B 0 TEXT('Size - each entry ')
APIs and the Anatomy of a User Space
Figure 4 CL program to create a dump for a User Space
/********************************************************************/ /* */ /* CL program to do the following: */ /* */ /* - create a user space in QTEMP */ /* */ /* - use the List Objects API to list information about */ /* objects in QTEMP into the user space in QTEMP */ /* */ /* - dump the user space for examination */ /* */ /* You can compile and run the CL program, or enter the */ /* commands interactively */ /* */ /********************************************************************/ LSTAPI: + PGM CLRLIB LIB(QTEMP) CALL PGM(QUSCRTUS) PARM('TESTSPACE QTEMP ' 'USERSPACE ' + X'00001000' ' ' '*ALL ' 'TESTSPACE user space') CALL PGM(QUSLOBJ) PARM('TESTSPACE QTEMP ' 'OBJL0100' '*ALL - QTEMP ' '*ALL ') DMPOBJ OBJ(QTEMP/TESTSPACE) OBJTYPE(*USRSPC) ENDPGM
APIs and the Anatomy of a User Space
Figure 5 Dump of the User Space (unable to reproduce)
LATEST COMMENTS
MC Press Online