Guild Companies, Inc.  
 
Midrange Guru - Tech Tips
OS/400 Edition
Volume 2, Number 15 - February 27, 2002

Page 1 of  X

Hey, Ted:

We know how to number pages on a report.

We'd like to be able to show the total number of pages on each page. For instance, we'd like to see something such as Page 2 of 9.

Any ideas on how we might accomplish that?

-- Jean

Can you predict the number of pages that will be generated? For example, if you know that each page will contain output from 40 input records, and you know that there are 235 input records, you can predict that there will be six pages of output. The Number of Current Records (NBRCURRCD) parameter of the Retrieve Member Description (RTVMBRD) command may tell you the number of input records. I say "may" because this technique only works if your program is reading all input records.

If you can't predict the number of pages of output, you'll have to wait until the spooled file is generated, then update the report. The Retrieve Spooled File Attributes (QUSRSPLA) API can tell you the number of pages in a spooled file.

DCL  &PAGES     *DEC    5
DCL  &WHICHSPLF *CHAR   4
DCL  &RCVLEN    *CHAR   4
DCL  &RECEIVER  *CHAR 144

/* Get number of pages produced by report */
CHGVAR     VAR(%BIN(&WHICHSPLF)) VALUE(-1)
CHGVAR     VAR(%BIN(&RCVLEN)) VALUE(144)
CALL       PGM(QUSRSPLA) PARM(&RECEIVER &RCVLEN +
             'SPLA0100' '*' ' ' ' ' QSYSPRT &WHICHSPLF)
CHGVAR     VAR(&PAGES) VALUE(%BIN(&RECEIVER 141 4))

QUSRSPLA has nine parameters, but you don't have to put values in all of them and you can omit the last one.

  • Receiver variable -- a data structure to contain data about the spooled file
  • Length of receiver variable
  • Format name -- SPLA0100 has everything you need
  • Qualified job name -- a single * means the current job
  • Internal job identifier -- ignore
  • Internal spooled file identifier -- ignore
  • Spooled file name
  • Spooled file number -- -1 means the last spool file of that name
  • Error code -- ignore

Here's some CL code that might work for you. It performs the following functions:

  • calls a program that generates a report, leaving the special character sequence :P:G: after the word "of".
  • calls QUSRSPLA to find out how many pages are in the report,
  • copies the spooled file to a database file,
  • calls RPG program UPDTPAGE, which replaces :P:G: with the total number of pages
  • rebuilds the spooled file from the updated disk file.

PGM

DCL  &PAGES     *DEC    5
DCL  &WHICHSPLF *CHAR   4
DCL  &RCVLEN    *CHAR   4
DCL  &RECEIVER  *CHAR 144

/* Run a program that creates a report with file QSYSPRT */
/* It should number pages like this: Page xxx of :P:G:
CALL       PGM(somepgm)

/* Get number of pages produced by report */
CHGVAR     VAR(%BIN(&WHICHSPLF)) VALUE(-1)
CHGVAR     VAR(%BIN(&RCVLEN)) VALUE(144)
CALL       PGM(QUSRSPLA) PARM(&RECEIVER &RCVLEN +
             'SPLA0100' '*' ' ' ' ' QSYSPRT &WHICHSPLF)
CHGVAR     VAR(&PAGES) VALUE(%BIN(&RECEIVER 141 4))

/* Copy the report to a database file */
CRTPF      FILE(QTEMP/SPOOLFILE) RCDLEN(133)
MONMSG     MSGID(CPF7302)
CPYSPLF    FILE(QSYSPRT) TOFILE(QTEMP/SPOOLFILE) +
             SPLNBR(*LAST) MBROPT(*REPLACE) CTLCHAR(*FCFC)

/* Update the number of pages */
OVRDBF     FILE(SPOOLFILE) TOFILE(QTEMP/SPOOLFILE)
CALL       PGM(UPDTPAGE) PARM(&PAGES)
DLTOVR     FILE(SPOOLFILE)

/* Rebuild the spooled file */
OVRPRTF    FILE(QSYSPRT) CTLCHAR(*FCFC)
CPYF       FROMFILE(QTEMP/SPOOLFILE) TOFILE(QSYSPRT)
DLTOVR     FILE(QSYSPRT)

ENDPGM

Here's the RPG program that replaces :P:G: with the number of pages:

    * Replace :P:G: with total number of pages in a report
    *
   FSpoolFile UF   f  133        disk
   D PrtLine         ds
   D   PrtImage              2    133
   D UpdtPage        pr                  ExtPgm('UPDTPAGE')
   D  NbrOfPages                    5p 0
   D AlphaPage       s              5
   D Pos             s             10i 0

    * *Entry parameters
   D UpdtPage        pi
   D  NbrOfPages                    5p 0

   C          eval      AlphaPage =
   C                        %triml(%editc(NbrOfPages:'3'))

   C          read      SpoolFile     PrtLine
   C          dow       not %eof
   C          eval      Pos = %scan(':P:G:' : PrtImage)
   C          if        Pos > *zero
   C          eval      %subst(PrtImage : Pos : 5) = AlphaPage
   C          update    SpoolFile     PrtLine
   C          endif
   C          read      SpoolFile     PrtLine
   C          enddo

   C          eval      *inlr = *on

You need to take the code and adapt it for your situation.

To make it easier for you to use try this program on your system, here's something you should be able to compile and run with no modification. It adds total pages to the output of the Display Job (DSPJOB) command. It uses the same techniques as the code given above, but instead of scanning for :P:G:, it looks for the word PAGE. You could probably use this support with many existing reports. There is a risk that PAGE may appear on your report somewhere in the body of your report. To minimize this risk, the scan uses a starting column that is set when the first instance of the scan string is located.

First, the CL program, DSPJOB01C. Use Create Bound CL Program (CRTBNDCL) and compile it to run in the default activation group:

/* Demonstrate the use of a text replacement program to */
/* replace Pagexxxx with Pagexxxx of yyyy.              */

PGM

DCL  &PAGES     *DEC    5
DCL  &SPLNBR    *DEC    5
DCL  &WHICHSPLF *CHAR   4
DCL  &RCVLEN    *CHAR   4
DCL  &RECEIVER  *CHAR 144

/* Create a report */
OVRPRTF FILE(QPDSPJOB) HOLD(*YES)
DSPJOB OUTPUT(*PRINT)

/* Retrieve number of pages and spool file number */
CHGVAR     VAR(%BIN(&WHICHSPLF)) VALUE(-1)
CHGVAR     VAR(%BIN(&RCVLEN)) VALUE(144)
CALL       PGM(QUSRSPLA) PARM(&RECEIVER &RCVLEN +
             'SPLA0100' '*' ' ' ' ' 'QPDSPJOB' &WHICHSPLF)
CHGVAR     VAR(&PAGES) VALUE(%BIN(&RECEIVER 141 4))
CHGVAR     VAR(&SPLNBR) VALUE(%BIN(&RECEIVER 77 4))

/* Create work file in QTEMP if not there already */
CRTPF      FILE(QTEMP/SPOOLFILE) RCDLEN(133)
MONMSG     MSGID(CPF7302)

/* Copy report to work file */
CPYSPLF    FILE(QPDSPJOB) TOFILE(QTEMP/SPOOLFILE) +
             SPLNBR(*LAST) MBROPT(*REPLACE) CTLCHAR(*FCFC)

/* Replace Pagexxxx with Pagexxxx of yyyy */
OVRDBF     FILE(SPOOLFILE) TOFILE(QTEMP/SPOOLFILE)
CALL       PGM(DSPJOB01R) PARM(&PAGES)
DLTOVR     FILE(SPOOLFILE)

/* Rebuild the report */
OVRPRTF    FILE(QSYSPRT) CTLCHAR(*FCFC)
CPYF       FROMFILE(QTEMP/SPOOLFILE) TOFILE(QSYSPRT)
DLTOVR     FILE(QSYSPRT)

/* Delete the original spooled file */
/* DLTSPLF    FILE(QPDSPJOB) SPLNBR(&SPLNBR) */

ENDPGM

I commented out the next to the last line, which deletes the original output of DSPJOB, so you could compare the two reports. You will probably want to delete the original output in a production situation.

Here is RPG program DSPJOB01R, which inserts the number of total pages. Use Create Bound RPG Program (CRTBNDRPG) command and compile it to run in the default activation group.

    * Replace PageXXXX with PageXXXX of YYYY
    *
   FSpoolFile UF   f  133        disk
   D PrtLine         ds
   D   PrtImage              2    133
   D UpdtPage        pr                  ExtPgm('UPDTPAGE')
   D  NbrOfPages                    5p 0
   D AlphaPage       s              4
   D PageNbr         s              8
   D Pos             s             10i 0
   D Start           s             10i 0 INZ(1)

    *Entry
   D UpdtPage        pi
   D  NbrOfPages                    5p 0

   C          evalr     AlphaPage = %editc(NbrOfPages:'3')

   C          read      SpoolFile     PrtLine
   C          dow       not %eof
   C          eval      Pos = %scan('Page' : PrtImage : Start)
   C          if        Pos > *zero
   C          eval      Start = Pos
   C          eval      PageNbr = %subst(PrtImage : Pos : 8) +
   C                                AlphaPage
   C          eval      %subst(PrtImage : Pos - 8 : 16) =
   C                             PageNbr + ' of ' + AlphaPage
   C          update    SpoolFile     PrtLine
   C          endif
   C          read      SpoolFile     PrtLine
   C          enddo

   C          eval      *inlr = *on

See the January 30, 2002, issue for another tip that uses the QUSRSPLA API.

-- Ted

Sponsored By
JACADA LTD.

Gartner Research on Legacy Extension and Jacada

See why Jacada is a leading legacy extension solution according to Gartner.

This 30-page report includes approaches to legacy extension, vendor rankings in the Magic Quadrant, and case studies. It’s a must-read for anyone planning legacy extension projects.

Get your copy at www.jacada.com/gartner/quadrant32

THIS ISSUE
SPONSORED BY:
WorksRight Software, Inc.
Jacada Ltd.
BACK ISSUES
TABLE OF CONTENTS
More than One Way to Skin a Cat
Page 1 of  X
Reader Feedback and Insights: Status Messages May Degrade Performance
  Newsletters | Subscribe | Advertise | About Us | Contact | Search | Home  
  Last Updated: 2/27/02
Copyright © 1996-2008 Guild Companies, Inc. All Rights Reserved.