As the old saying goes, “with flexibility comes complexity.” Actually, we just made that saying up, but it’s true, nevertheless. In most cases, the more options you give a particular report, the more flexible (and thus useful) it becomes to the user. But adding more options also makes the report more complex to code. It also makes it more difficult for the user to interpret the data on the report. After all, a report that prints accounts receivable totals for an entire organization will show far different totals than one that prints only two of 10 locations. On the outset, this seems obvious, but it may not always be apparent to users that they are looking at a report that only includes locations one and two. For the sake of clarity, your reports need to have a legend that documents the selection criteria used in preparing the report.
Coding a legend to print at the end of a report is not exactly rocket science. It is boring, repetitive work. Anytime we think of “boring, repetitive work,” human nature takes over and we immediately think, “Let somebody else do it!” But when left with a situation where we needed to make changes to the legends of hundreds of reports, we had another thought: What if we could write a single program that could be called at the end of any of our reports and would print the selection criteria used to generate the report? Implementation of such a program would mean that adding to or changing selection parameters on a report would not require that we change the report legend. Such an enhancement would also ensure that the selection criteria always matched all of the options selected.
Implementation of such a procedure would mean that our programmers would not be bored and burdened with this “boring, repetitive work.” We felt that we owed it to our fellow programmers. (And it sure beats writing a couple of hundred subroutines.) Along the way, we could become heroes. Maybe even legends among our peers! How could we resist such a challenge?
Legendary Reasoning
For our purposes, the concept was reasonably simple. To get the selection criteria for any of our reports, we needed only to list the input fields from the display file used to generate the report. The Retrieve Display File API, QDFRTVFD, provided us with this information, but, as we soon found out, you have to do a lot of digging to extract it. Figure 1 illustrates
all of the various data structures we had to go through in order to get to the list of field names. We’ll get to the highlights of the code we used to do this in this article.
The next step in the process was to get the actual data that was keyed into the selection fields. Because all of our reports use the local data area (LDA) to pass the selection criteria from the prompt program to the printing program, this was not much of a problem. Furthermore, because we use a physical file to describe the LDA externally, we could use the text description of each field from that external definition as a label for each selection field used. The List File Fields API, QUSLFLD, provided us with this information. The QUSLFLD API was also able to return the starting buffer position of each field. With this information, we could extract what the user keyed from the LDA by using the offset position in conjunction with the field lengths. Figure 2 shows an example of the final page of a report that was produced using this tool. As you can see, the legend lists a text description for each entry specified and the value keyed into each one. As an added bonus, if any programmer comes along later and adds more operator selections, the new selections will automatically be included in the legend!
To print the legend from one of our reports, we simply insert a call to the PRTLEGEND program at the end of the report program. There were a couple of considerations made when doing this, however. First of all, to get the legend to print in the same spool file as the report, you need to make sure that the printer file QSYSPRT has a shared access path. You can do this by specifying SHARE(*YES) on an Override Printer File (OVRPRTF) command. Second, you must make sure that the printer file is still open when you call the PRTLEGEND program. Failure to consider these factors would result in the legend printing in its own spool file.
The Making of a Legend
The complete code for the PRTLEGEND program, along with a demonstration program, can be downloaded from the Midrange Computing Web site (www.midrangecomputing.com/mc). While PRTLEGEND has 935 source statements, over 450 of the statements are comments describing the data structures used with the Retrieve Display File API, QDFRTVFD. We didn’t have to enter the definition specifications for these structures. They can be found on your system in QSYSINC/QRPGLESRC member name QDFRTVFD. This library has many source files that contain the data structures for many, many APIs. It comes with every AS/400, but you have to install it. If you are going to work with APIs, you owe it to yourself to get acquainted with this library. It will become your principal source of API reference material. Figure 3 shows a portion of the mainline code for the program. The program accepts three parameters: the format name, file name, and library name of the display file used to generate the request for the report. Our program passes these fields to the GetDspFF procedure, which loads array ArryInfo with the names (and length) of each field in the format. Next, it calls the GetFTxt procedure, which loads array AryTxt with the name, description (taken from the TEXT keyword), and buffer position of each field in the REPTDS file. The REPTDS file is the aforementioned external data structure that we use to describe parameters passed to our report programs via LDA. Our program then walks through the AryInfo array, and, for each element, it looks through the AryTxt array to see if the display file field names match names in our REPTDS external data structure. Keep in mind that an external data structure is really nothing more than a physical file. If our program finds a display file field name that matches a name from our external data structure, the program extracts the data from the LDA into a generic print field and prints a legend line.
Figure 4 shows the GetFTxt procedure. This procedure first calls another procedure that returns a pointer to a user space. It then calls the List Database Field API, QUSLFLD, to generate a list of field names for the REPTDS data structure into the newly created user space, using the offsets in each data structure to point to the next structure until it gets to the
list of fields structure using pointer ListPoint. This structure repeats itself for each field in the file. So we process all of the list field structures loading each field name and buffer position into the array AryTxt.
Figure 5 shows some of the code in the GetDspFF procedure. This procedure uses the Retrieve Display File API, QDFRTVDF, to get the names of all the fields in a format in the display file. While this API normally returns its information into one long field, we have directed its output into a user space instead. We did this because a user space can hold much more data than a single field. The way we redirected the output was to base the receiver variable on the same pointer that points to the user space. The rest of the code in this procedure walks through the various data structures (shown in Figure 1) to extract the field names and load them into the array. You’ll have to download the code if you want to see more details on how this all works, but you get the general idea.
Legends in Our Own Minds...
The PRTLEGEND program saved uncounted hours of tedious programming time and tons of money for our company. The programmers hoisted us on their shoulders shouting,
“Hip-Hip-Hooray!” And modest programmers that we are, we simply went about our business looking for other dragons to slay.
OK, we made most of that up. But the PRTLEGEND program does save a lot of hours of mind-numbing programming. If you can use this code (with minor modifications), it might save your programmers a lot of time too. Can’t you hear the cheering?
Doug Pence is the founder and Ron Hawkins is the research and development manager of CPU Medical Management Systems in San Diego, California. Doug can be reached by email at
.
QDFFBASE
Base File Section
QDFFINFO File Header
Section
QDFFINOF
QDFFWUOF QDFFINOF
QDFWFLEI
Where Used File Level
QDFAROF
QDFWNTBO QDFWXLEN
QDFFNTBL
Field Name Table for Entire File
QDFWRCDI Where Used Record Info
QDFWFLDI
Where Used Field Info
QDFARFTE
Record Format Table
QDFFRINF
Record Header Section
QDFFRDPD
Record Level Device Dependant Section
QDFFXRDP
Device Dependant Extension
Section
QDFFRAOF
QDFWRLEN
QDFFXRDO
Figure 1: PRTLEGEND is possible thanks to many APIs.
Report selections:
From Date......................................... 050100
To Date........................................... 053100
Report format..................................... 2
Number of copies.................................. 01
Hold report?...................................... N C *ENTRY PLIST
C PARM InFormat
C PARM InLibrary
C PARM InFile
c If NOT %open(qsysprt)
c Open qsysprt
c endif
c *dtaara define *lda Lda
Figure 2: The output of PRTLEGEND describes record selection criteria.
c in lda
c except Heding
* Get array of field information
c CallP GetDspFF(InFormat:
c InLibrary: InFile) * Get array of text/buffer postion from parameter file
c callp GetFTxt
c Do 1000 x
c eval DsAryInfo = AryInfo(x)
c If Name = *blanks
c leave
c else
c do NbrFlds xx
c move AryTxt(xx) ReturnFmt
c if SfName = Name
c move Length SfLength
c eval PrtField = %subst(Lda:SfBufPos:SfLength)
c eval SfDesc = %trim(sfdesc) + AllPeriods
c except PrtDetail
c leave
c endif
c enddo
c endif
c enddo
c eval *inlr = *on
oqsysprt e Heding1 2 1
o or of
oqsysprt e Heding 12
o or of
o 33 ‘Report selections:’
o ef PrtDetail 1
o SfDesc 65
o PrtField 91 P GetFTxt B export
d GetFTxt PI
d ListFormat S 8
D FileFmt S 10 inz('PARM ')
d FileLib S 20 inz('REPTDS *LIBL')
d I S 4 0 inz
d OverRide S 1
d SpacePtr S *
D UserSpace S 20 inz('RTVTXTSPC QTEMP')
D GenDs DS 140 Based(GenDsPoint)
D OffsetHdr 117 120B 0
D OffsetList 125 128B 0
D NbrInList 133 136B 0
D SizeEntry 137 140B 0
D HeaderDs DS 44 Based(HeadPoint)
D OutFileNam 1 10
D OutLibName 11 20
D OutType 21 25
D OutFormat 31 40
D RecordLen 41 44B 0
D ListDs DS 82 Based(ListPoint)
D SfFld 1 10
D SfType 11 11
D BufferOut 13 16B 0
D FieldLen 21 24B 0
D Digits 25 28B 0
D Decimals 29 32B 0
D FieldDesc 33 82
D ErrorDs DS 116 INZ
D BytesPrv 1 4B 0 inz(116)
D BytesAvl 5 8B 0
D MessageId 9 15
D ERR### 16 16
D MessageDta 17 116
* Create user space for retrieve file description
C Eval SpacePtr = CrtUsrSpc(UserSpace)
*
* List fields to user space
C Call 'QUSLFLD'
C Parm UserSpace
C Parm 'FLDL0100' ListFormat
C Parm FileLIb
C Parm FileFmt
C PARM '1' OverRide
C Parm ErrorDs
Figure 3: The mainline code of PRTLEGEND shows how easy this utility is in concept.
c Eval GenDsPoint = SpacePtr
c Eval HeadPoint = GenDsPoint + OffsetHdr
c Eval NbrFlds = NbrInList
c Eval ListPoint = GenDsPoint + OffsetList * Search list of fields to get size, description, and buffer
* position of each field in file
C Do NbrInList I
C MoveL SfFld SfName
C Move FieldDesc SfDesc
C z-add BufferOut SfBufPos
C Move ReturnFmt AryTxt(i)
c Eval ListPoint = ListPoint + SizeEntry
C EndDo
c eval Nbrflds = i - 1
c return
P GetFTxt E DQDFFBASE DS Based(SpacePtr)
D* Base File Section (QDFFBASE)
D* Base file structure. This is
D* the first structure and is
D* located at offset zero of the
D* returned data.
D QDFFINOF 9 10B 0
D* Displacement to file header
D* section (see structure
D* QDFFINFO)
D QDFFFRCS 11 12B 0
D* Number of record formats
D* specified. This number
D* includes internally generated
D* record formats.
D* Display attribute bits.
D QDFFFSCR 14 15B 0
D* Number of valid file screen
D* sizes (see structure
D* QDFFSCRA,).
D*QDFFSCRS 20 20
D*
D* Screen size table. This area
D* defines the screen sizes
D* valid for externally defined
D* files. This is specified by
D* the DSPSIZ keyword. When not
D* specified, a default
D* DSPSIZ(*DS3) is generated.
D* Structure QDFFSCRA defines
D* the entries. The elements
D* are in the sequence that the
D* DSPSIZ keywords are
D* specified.
* Retrieve display informtion into user space (looking for
* format and field names)
C CALL 'QDFRTVFD'
C PARM QDFFBASE
C PARM 16776704 ReceiveLen
C PARM 'DSPF0100' FileFmt
C PARM SFileLib
C PARM ErrorDs
Figure 4: The GetFTxt procedure extracts information about data in the LDA.
Figure 5: The GetDspFF procedure determines which fields are in the prompting display file.
LATEST COMMENTS
MC Press Online