Are you looking for a particular spool file? Do you want to find, archive, and delete a group of reports? Do you need a shortcut to your spool commands? The JWSPLF command, a utility much like IBMs Work with Spooled Files (WRKSPLF) command, can meet all these needs and more. JWSPLF does everything that the standard WRKSPLF command does and gives you a whole host of additional useful features:
You can filter spool files by user name and/or by output queue.
You can limit the list of spool files to those with data values (spool file name, job name, date, user, and user data) that match a value you enter.
You can reposition the list of spool files by spool file name.
You can use options not found in WRKSPLF, such as duplicate, copy into database, and archive.
You can create additional user options with PDM-like runtime parameter insertion.
You can repeat a single option from a selected file to the end of the list. JWSPLF is a powerful replacement for IBMs WRKSPLF command and, as Figure 1 shows, features the same user interface.
There is a lot of code to this utility, far too much to print here. You can download the source code from the Midrange Computing Web site at www.midrangecomputing.com/mc/. However, Id like to briefly describe a few of the techniques I used to build JWSPLF.
Faster Insert and Retrieve Operations with User Indexes
JWSPLF makes heavy use of an object type with which you may not be familiarthe user index. In the past, these objects were only available to system programs. They were designed to give the best performances in insert and retrieve operations and use less of the systems resources. Now application programs can use user indexes as well.
User indexes allow data storage and search functions based on a key but differ from database keyed files in that they have only one unformatted data field, rather than externally described fields. The first portion of this field is the key, whose length you determine when you create the index. The remaining part contains the data. The maximum length of each index entry is now 2,000 bytes (it was 120 bytes in earlier releases).
There are two ways to access user indexes. The first is to use a set of APIs, as documented in the manual OS/400 Object APIs. The second way is to use machine interface (MI) instructions (shown in Figure 2), which can be referenced as functions in ILE languages. In addition, you can use one user index CL commandDelete User Index (DLTUSRIDX)as well as any CL commands that work with objects of any type, such as Create Duplicate Object (CRTDUPOBJ). For more information about user indexes, see
Putting User Indexes into Practice (MC, October 1998) and Applying User Index APIs (MC, February 1996).
Since JWSPLF relies heavily on user indexes, I wrote procedure JWUSIDX, which performs every function needed. This same standard procedure can then be used in many other applications.
Figure 3 contains the code needed to retrieve index entries. The technique used for the other index operations is similar. As seen in Section A, Ive prototyped the Find Index Entries (_FNDINXEN) MI function. Note that all parameters are pointers. The first parameter references the area in the calling program where the retrieved entries will be stored. This can be either a field or an array that contains up to 4,095 entries; the actual number of entries that must be retrieved by the instruction must be indicated in another parameter, as I will explain.
The second parameter is a system pointer that references the user index. The system pointer to an object can be located with one of the available ILE functions, which is derived from the basic MI instruction called Resolve System Pointer (RSLVSP). These functions receive object name, object type, and (by option) the system pointer to the library; the functions then return the pointer to the requested object. This pointer stores the virtual address of the object and must be used as is. If you try to manipulate it, the pointer will be invalidated by the machine and will become inoperative. If you want to resolve a pointer for a user index located in a specific library other than QTEMP, you must pass to the function a system pointer for the library, as I will show you. If you are using the QTEMP library, you can retrieve the pointer and use it.
A QTEMP library, as we know, is automatically created for each job when the job starts and deleted when it ends. However, the operating system gives the QTEMP library an internal, unique name that can be referenced directly by using a pointer stored in a system work area known as Process Communication Object (PCO). That way the ILE programmer can use a built-in function called _PCOPTR to get the pointers to QTEMP and *CURLIB directly from PCO. You can use the Dump System Object (DMPSYSOBJ *PCS) command and see the location of the pointers to QTEMP and *CURLIB within the PCO at offsets x000040 and x000070. Figure 3, Section B, contains the sample code needed to get these pointers using the _PCOPTR function. The returned part of the PCO pointer is redefined in RPG as an array of 16-byte pointers so that QTEMP is the fifth element and *CURLIB is the eighth. After __PCOPTR executes, the Ctx@ pointer is evaluated to address QTEMP or the current library.
If the user index is in a specific library, you must first resolve the librarys pointer. Figure 3, Section C, shows the code for this function. Note that the objects type must be indicated in hexadecimal (for example, the hex code for a library is x0401). Use the QLICVTTP API to convert symbolic names (if they are unknown) to hex MI names or simply dump the object with the Dump Object (DMPOBJ) command. Because no special authority can be set with this instruction, the ObjAuth parameter should be initialized with x0000. When the function is complete, the Ctx@ field will contain the resolved pointer for the requested library. At this point, you can resolve the pointer for the user index (type x0E0A) into Idx@ and pass the pointer to the _FNDINXEN function (see Figure 3, Section D).
The third parameter, Inx_opt@, references a data structure with the find options. This structure has four subfields. The first (inx_opt_rule) indicates how entries should be retrieved; for sequential access, retrieval may begin with the first or last entry; keyed access, however, allows random retrieval. The second (inx_opt_argl) contains the key
length, while the third (inx_opt_occc) contains the number of entries to retrieve (up to 4,095 at a time). After any successful execution, the number of entries actually retrieved is returned into the fourth subfield (inx_opt_rtnc).
The fourth parameter, Inx_arg@, references the field that contains the search key. The keyed access method always requires a key.
Here Comes the JWSPLF Utility
The JWSPLF command calls the command processing program (CPP) JWSPLF, which is built from modules JWSPLFB, JWSPLFC, JWSPLFK, JWSPLFQ, JWSPLFR, JWSPLFU, JWSPLFX, and JWUSIDX. Each module is designed to perform one specific action, so most of the code can be reused, with little or no editing, for any other Work with application.
The entry point of the program is the module JWSPLFB. This module retrieves the spool files, creates a temporary user index, and loads the spool entries into it. The spool file entries are retrieved with the QUSLSPL API within the SPLF0200 format. The SPLF0200 returns all the names and attributes of the requested files by passing an array of keys to the API. This array contains one key for each attributee.g., 201=File name or 202=Job. The API places spool entries into a user space in order of timestamp. Each entry, with the attributes selected here, is 400 bytes long: A user space, then, can hold approximately 40,000 entries. Each spool entry is formatted into a 120-byte line that includes a unique 20-byte key with the spool name, job, and number. The entry is then moved into an array of 4,095 elements (the maximum number allowed for each insert operation). When the array is full, the entries are written into the user index, and the array is reloaded with more data.
The array is loaded directly via a pointer that references the 120-byte line. The array is used as a row area to store the file attributes. The first data structure, splf@l, is initially mapped to the first spool entry in the user space. The second data structure, splf@x, is mapped to the first element of the array. Use the eval op code to copy all the spool attributes from the user space to the array. To move to the next elements in the user space and the array, increase the value of the pointer with the appropriate offset. The first offset is retrieved from the user space header (set by the spool API); the second offset is evaluated as %len(splf@x), or 120 bytes.
If an error occurs in the spool API, the standard error structure returns the CPF message identifier and its message data. These are passed to the QMHSNDPM API to send an *ESCAPE message. The *PSSR routine, which is automatically invoked, resends the error with QMHRSNEM and terminates by resignalling, with the *CANCL option, the originating error. When all the entries have been loaded into the index, control passes to the module JWSPLFR, which displays the list of the selected spool files.
As Ive explained, I designed JWSPLF to look like the standard WRKSPLF panel group. But instead of panel groups, I used a single display for each view and a display for confirming delete requests. The scrollable area, which lists up to nine spool files, is defined by the program as a group of arrays, rather than a subfile. Because there is no subfile that keeps the selected options, I created a second user index that stores only the entries for which the user has entered an option. This second user index makes it easy to know immediately if there are options to execute.
If there are options to execute, a simple CL program is called that reads the entries from this second index and runs the appropriate command for each option. When writing the code, I paid particularly close attention to minor detailse.g., list and cursor positioning (based on whether the user selected one or more options), whether errors were encountered while executing commands, whether entries were removed, and whether any functional key had been pressed.
When the user presses Enter or a roll key, the program saves the current lowest and highest keys. The program uses these stored keys to scroll the list forward and backward
and to reposition the list after it has been reloaded with F5=refresh. If the user has entered any line options, the selected spool file entries are written into the second user index; however, if the user deletes an option that was previously inserted, the corresponding entry is removed from the index.
When the F9 key is pressed, the program calls the Retrieve Messages API, QMHRTVRQ. This API returns, one at a time, the old request messages that are in the job log. Note that the first call must be done with the option initialized to *LAST. If the user presses F9 again, this option should be changed to the value *PRV. The retrieved command (up to 73 bytes) is put into the command line for later execution. If the user presses F13, the first option on the current page is repeated until the last entry is reached; all the entries that are in this range are put into the second user index.
If the user presses Enter, the program queries the second user index to determine if there are line options to be executed. If there are, the program calls the procedure JWSPLFX with the optional parameters in the command line; otherwise, the JWSPLFQ procedure is called. So that the system help is available on any message, errors are displayed at line 24 by means of an error subfile associated with the program queue.
Execute All the Options with a Simple CL Module
The procedure that executes the options is a CL module. The module receives a character string containing any command parameters the user keyed on the command line as well as the F4 indicator; the module then reads all the entries from the second user index. If the option entered was not 4, to delete a spool file, the program calls the well-known QCMDEXC API, passing to it the command built from the spool parameters retrieved from the second user index and the optional parameters.
JWSPLF includes the most common WRKSPLF options, but you can easily add other options. I changed the standard option 7 to duplicate spool files instead of display messages. The option to display messages is still available by calling API QSPDSPFM, but only if your system is at security level 30. The source code for the program contains code for both options.
The procedure builds the command depending on the users choices (based on F4 being pressed and whether or not the user entered any parameters on the command line). Notice the ? before the command that forces prompting and the ?* string, which protects some parameters (like file name) from being changed before the command is executed. If any parameters are keyed on the command line, they are appended at the bottom of the command.
If the user enters option 4 to delete a spool file, the spool entries are read twice. The first time, they are passed to function JWSPLFC, which writes the entries into a subfile. When all the entries have been read once, the JWSPLFC is called again to show the list with all the files ready to be deleted. If the user confirms the list by pressing Enter, the second user index is read again, spool files are deleted from the system, and entries are removed from both indexes.
Customized user options, like predefined ones, are handled by QCMDEXC. Module JWSPLFU builds the command, replacing any & variables with the corresponding spool file values. When the user option is complete, the QUSRSPLA API is called to retrieve the new status of the file. (For example, the user could have cancelled a spool with a user-defined option instead of the standard option 4.) Now, consider the QWCRTVCA and QWCCCJOB APIs: The first API traps whether the F3 or F12 key is pressed from any system or user screen; if the user pressed one of these keys (e.g., while displaying many spool files) the multiple-entry processing task stops, and control passes back to the list display. The second API resets the F-key flags. The procedure has one error branchpoint that transforms any escape message received into an information message and moves the message back to the calling program with the QMHMOVPM API. These messages will be displayed in the subfile line 24.
Module JWSPLFQ directly executes a command from the command line. The module uses the QCAPCMD API to execute or prompt for commands. QCAPCMD provides some additions to QCMDEXDC and QCMDCHK, such as limited user checking and generic command search. Executed commands are written in the job log as request messages and then received so they are not executed again. The user can retrieve these logged commands by pressing F9.
Add All the Options You Need
JWSPLF, like PDM, allows you to define options. The program used to manage the user options stores nonstandard options in a database file. User options have an option code and a command string that usually contains all the substitution variables needed to identify each spool file. The variables that can be put into a command are &SF (Spool file name), &J (Job name), &U (User name), &N (Job number), &SN (Spool file number), and &FJ (Full job name). To replace these variables with values, I call QCAPCMD, specifying a value of 8 in the first subfield of the options control block parameter. See Figure 4 for examples of user-defined options.
Archive Your Spool Files
The JWSPLF utility includes option A=Archive. This option uses the JSPLGET command (included in the tool) to download a spool file with its attributes into a user space. This space can be saved or moved to another system using save and restore CL commands. The spool file with its attributes can be reprinted at any time directly from the user space by using the JSPLPUT command (included in the tool as well). These archival functions use the spool API QSPOPNSP to open the requested spool file, QSPGETSP, to put the data into the user space, and QSPCLOSP to close the spool. The user space can be up to 16 MB, which allows you to store a report of approximately 3,000 to 4,000 pages (archiving bigger reports is the programmers responsibility). My procedure creates as many user spaces as needed to store all the pages of the report. Each space is chained to the next one by writing a space_id in the first 10 bytes of the 64 bytes of user area not used by the spool APIs. At the end of the chain, the value *NONE indicates that this is the last space used. When the spool has to be reprinted, the QSPPUTSP API is called for each user space in the chain until the *NONE indicator is found. The space_id is in the form Snnnnnnnnn, in which n is a number retrieved from the SPCNR data area. The JPLARC *pf contains a list (with chain specifications, attributes, and download elapsed time) of the spool files that have been copied to user spaces. All the APIs described in this paragraph are shipped with the authority *EXCLUDE. (Consider granting *USE authority to all APIs that will run the JSPLPUT and JSPLGET commands.)
If You Need More Information
Hopefully, the information Ive provided will help enhance your everyday use of WRKSPLF. I hope that you find JWSPLF useful in dealing with spool files, as well as in programming Work with applications. The source code provides you with many comments that will help you better understand the techniques used.
References and Related Materials
Applying User Index APIs, Doug Pence and Ron Hawkins, MC, February 1996
MI Library Reference (SC09-2418, CD-ROM QBJADR00)
OS/400 Message Handling APIs (SC41-5862, CD-ROM QB3AMN03)
OS/400 Object APIs (SC41-5865, CD-ROM QB3AMQ03)
Putting User Indexes into Practice, Bruce Guetzkow, MC, October 1998
d*d* FNDINXEN - Find index entry
d*d FndInxEn PR ExtProc(_FNDINXEN)
d Inx_receiver@ * value
d Inx@ *
d Inx_opt@ * value
d Inx_arg@ * value
c *entry plist
c parm p_entry
c eval inx_receiver@ = %addr(p_entry)
d*d* MATPCO - Materialize Process Communication Object
d*d MatPco PR * ExtProc(_PCOPTR)
d*
d Pco s *
d Pco@ s * dim(8) based(Pco)
c** retrieve the qtemp or *curlib system pointer
c select
c** ... qtemp
c when idxlibr = QTEMP
c eval Pco = Matpco
c eval Ctx@ = Pco@(5)
c** ... *curlib
c when idxlibr = *CURLIB
c eval Pco = Matpco
c eval Ctx@ = Pco@(8) d*d* RSLVSP - Resolve system pointer
d*d Rslvsp6 PR ExtProc(_RSLVSP6)
d SysPtr *
d Objname 34A
d ObjAuth 2A
d Rslvsp8 PR ExtProc(_RSLVSP8)
d SysPtr *
d Objname 34A
d ObjCtx *
d ObjAuth 2A
d ObjName ds
d Objtype 2A
d ObjId 30A
d ObjAuth 2A inz(x0000)
c** resolve the library system pointer
c eval ObjType = x0401
c eval ObjId = idxlibr
c CallP RslvSp6(Ctx@:ObjName:Objauth)
c** resolve the user index system pointer
c eval ObjType = x0E0A
c eval ObjId = idxname
c if idxlibr = *LIBL or idxlibr = *blanks
c CallP RslvSp6(Idx@:ObjName:Objauth)
c else
c CallP RslvSp8(Idx@:ObjName:Ctx@:Objauth)
c endif
A
B
C
D
Figure 3: Procedure prototypes simplify the task of accessing user indexes from RPG programs.
Figure 4: JWSPLF allows you to add PDM-like options to the Work with Spooled Files panel.
LATEST COMMENTS
MC Press Online