Contents of the ANATOMY.TXT file
This article is reprinted from the April 1990 edition of
TechNotes/dBASE IV. Due to the limitations of this media, certain
graphic elements such as screen shots, illustrations and some tables
have been omitted. Where possible, reference to such items has been
deleted. As a result, continuity may be compromised.
TechNotes is a monthly publication from the Ashton-Tate Software
Support Center. For subscription information, call 800-545-9364.
The Anatomy Of A dBASE IV Report Form
In this article we'll take a look at the internal workings of a Report
Form in dBASE IV. We will peruse the procedures and subroutines that
produce the Header, Report Intro, Group Intro, Detail, Group Summary,
Report Summary and Footer of a typical dBASE IV report. We will also
look at how and where calculations are performed and how the report
form knows what records to print when a FOR or WHILE condition has
I've taken a fairly generic report form file (Anatomy.FRG), added line
numbers and broken it up into sections for our discussion.
When generating an .FRG file, the Report Form generator uses a number
of memory variables, called system variables because they are defined
by Report Form. These system variable are listed in Table 1.
Table 1 Report Form system variables
r_???????? Storage variable created by the Report Form Generator
for passing field values to print subroutines.
g?_??????? Report Form system variable defined by the report form
gl_??????? Report Form system variable that will contain a
gc_??????? Report Form system variable that will contain a
gn_??????? Report Form system variable that will contain a
gd_??????? Report Form system variable that will contain a DATE
_????????? dBASE IV system variable.
?????????? User-defined variable. Usually the name of a
calculated field from the REPORT FORM definition.
Group Levels with gn_Level
One of the Report Form system variables you will see used frequently
throughout the .FRG file is gn_level. This variable is used to keep
track of what stage of the report process the .FRG is at. It is most
often referenced when the Group Intro and Group Summary print and
calculation routines are called. Because we can have multiple group
breaks in a report definition, rather than have separate print and
calculation routines for each, we have routines that check the value
of gn_level to determine which group to calculate or print.
The first group break level is assigned a value of four, the second is
five, the third is six and so on. The sub routines that print the
Group Intros and reset the summary variables for the group breaks,
test for these values. The Group Footer (or summary) tests for the
value of 100 - gn_level (i.e. the first Group Footer would be 100 - 4
or 96, the second would be 100 - 5 or 95, the third 100 - 6 or 94 and
so on). As we go through the example .FRG file you will see gn_level
11 *-- Parameters
12 PARAMETERS gl_noeject, gl_plain, gl_summary, gc_heading, gc_extra
13 ** The first three parameters are of type Logical.
14 ** The fourth parameter is a string. The fifth is extra.
15 PRIVATE _peject, _wrap
When issuing the dBASE IV REPORT FORM command from the dot prompt or
from within a program there are a few optional parameters. They are:
PLAIN For continuous flow text with no page breaks and no headings
after the first page.
NOEJECT Eliminate initial page advance before report printing begins.
SUMMARY Print GROUP and REPORT totals only.
HEADING Specify text to be printed at the top of each page in addition
to the defined report header.
These parameters are accounted for in the .FRG file via the dBASE IV
PARAMETERS command. When the report is run, dBASE IV passes the
appropriate values to the first PARAMETERS statement in the report
The variables _peject and _wrap are dBASE IV system variables.
_peject will contain any one of the words BEFORE, AFTER, BOTH or NONE
meaning that dBASE should perform a form feed either BEFORE or AFTER
the report is run, at BOTH times or (NONE) not at all. The variable
_wrap will contain a .T. (TRUE) or .F. (FALSE). If .T. then dBASE IV
will print all output as word wrapped text. If .F., text will be
printed in the positions specified in the report form definition.
Here both of these variables have been declared as PRIVATE so that any
changes made to their values will affect only the current report being
Test for Records
17 *-- Test for no records found
18 IF EOF() .OR. .NOT. FOUND()
When executing a dBASE IV report form, dBASE internally issues a
LOCATE command using the FOR, WHILE and SCOPE information from the
REPORT FORM command as the conditions for the LOCATE. If no
conditions were specified, it performs a LOCATE for the logical .T.
(TRUE). This will position the record pointer at the first record in
the file. The IF statement tests the result of the LOCATE command to
ensure there where records found to be printed. If there where not,
execution of the report is terminated.
Word Wrap Mode
22 *-- turn word wrap mode off
As mentioned earlier, dBASE IV has the option of printing Word Wrapped
Text. In other words, any characters that exceed the right margin
would be wrapped to the next line. Here, the dBASE IV System Variable
_wrap is being set to .F. (FALSE), thereby disabling the Word Wrap
Page length test
25 IF _plength < 4
26 SET DEVICE TO SCREEN
27 DEFINE WINDOW gw_report FROM 7,17 TO 11,62 DOUBLE
28 ACTIVATE WINDOW gw_report
29 @ 0,1 SAY "Increase the page length for this report."
30 @ 2,1 SAY "Press any key ..."
32 DEACTIVATE WINDOW gw_report
33 RELEASE WINDOW gw_report
A typical report would at least consist of PAGE HEADER, REPORT INTRO,
DETAIL and REPORT SUMMARY bands. Each of these bands has a default
height or size of one line. The sum of the size of each of these
bands would be four. If a page length less than four were specified,
not all of these bands would fit on one page. This test checks to see
that we have allowed enough lines per page to print at least these
bands. If not, it requests that we change our setting.
Page advance BEFORE, AFTER, BOTH, NONE?
37 _plineno=0 && set lines to zero
38 *-- NOEJECT parameter
39 IF gl_noeject
40 IF _peject="BEFORE"
43 IF _peject="BOTH"
As I mentioned earlier, dBASE IV makes use of a system variable called
_peject to determine whether a page should be ejected at the
beginning, the end or both ends of a report form printout. At the
same time the syntax of the REPORT FORM command allows us to specify
the option NOEJECT which eliminates the initial page eject before
printing. The test here is to determine whether or not to perform the
initial page advance and then set the system variable _peject
48 *-- Set-up environment
49 ON ESCAPE DO prnabort
50 IF SET("TALK")="ON"
51 SET TALK OFF
57 SET SPACE OFF
58 gc_time=TIME() && system time for predefined field
59 gd_date=DATE() && system date " " " "
60 gl_fandl=.F. && first and last page flag
61 gl_prntflg=.T. && Continue printing flag
62 gl_widow=.T. && flag for checking widow bands
63 gn_length=LEN(gc_heading) && store length of the HEADING
64 gn_level=2 && current band being processed
65 gn_page=_pageno && grab current page number
This section defines the environment settings for the report.
The first item defined is the user way-out. The Escape key is set up
so that when pressed, the report process is canceled.
Next, TALK is set OFF so that talk-back resulting from the execution
of some dBASE IV commands and the storing of values to memory
variables does not display on the screen.
Because the "?" and "??" commands are used exclusively for producing
the report output, SPACE is set OFF so that extra spaces are not
generated between printed fields in the report.
In addition, a number of program variables are assigned their default
values. Study the "&&" notes that follow the assignments for an
explanation of these variables.
Initialize Calculated Variables
68 *-- Initialize calculated variables.
Here is where user-defined calculated fields are initialized. The
actual calculations are performed elsewhere. The variables are
created here so that they will be available as PUBLIC variables to all
Determining the Page Break
71 *-- Set up procedure for page break
72 IF _pspacing > 1
73 gn_atline=_plength - (_pspacing + 1)
75 gn_atline=_plength - 2
77 ON PAGE AT LINE gn_atline EJECT PAGE
The set up procedure for determining the page break is used to define
the variable gn_atline. This variable holds the line number at which
a page break should occur on every page. The variable itself is used
in conjunction with the ON PAGE command. The ON PAGE command
determines what steps to process when the desired page length has been
The Heart Of The Report Form
79 *-- Print Report
This is where the report processing takes place. The section starts
off with the PRINTJOB command. Issuing this command causes the
printer initialization codes from the selected printer driver and the
Starting Control Codes from the "Print" menu and/or dBASE IV system
variables to be sent to the printer. In addition, the dBASE IV
internal line and page counters are reset.
Initialize Group Break Variables
83 *-- Initialize group break vars.
Here is where the group break variables are initialized. As with the
variables for calculated fields the Group Break variables are
initialized here so as to make them available as PUBLIC variables to
all other sub-routines. In addition, the initial grouping values for
the first group are stored to the Group Break Variables.
Initialize Summary Variables
86 *-- Initialize summary variables.
Here the Summary Variables for both the Group and Report summaries are
initialized. Again the variables are initialized here to make them
Assign Initial Values to Calculated Variables (Fields)
94 *-- Assign initial values to calculated variables.
Here, the values for the user-defined calculated fields are assigned
for the first record to be printed. After this first assignment
further calculation will be handled in the UpdVars sub-routine which I
will discuss later.
Start First Physical Page
97 IF gl_plain
98 ON PAGE AT LINE gn_atline DO Pgplain
100 ON PAGE AT LINE gn_atline DO Pgfoot
103 DO Pghead
105 gl_fandl=.T. && first physical page started
107 DO Rintro
109 DO Grphead
The report is ready to begin printing. A final check is made to see
if the PLAIN option was supplied with the REPORT FORM command and the
ON PAGE command is set accordingly.
Next the page heading is printed. The First Page flag is then set to
.T. (TRUE). The Report Intro is printed as well as the first Group
Heading. The next step is what I called the Pace Maker. That's the
File Loop where the records themselves are processed.
The Pace Maker
111 *-- File Loop
112 DO WHILE FOUND() .AND. .NOT. EOF() .AND. gl_prntflg
113 DO CASE
114 CASE .NOT. (FNAME = r_mvar4)
119 *-- test whether an expression didn't match
120 IF gn_level <> 0
121 DO Grpfoot WITH 100-gn_level
122 DO Grpinit
124 *-- Repeat group intros
125 IF gn_level <> 0
126 DO Grphead
128 DO Upd_Vars
129 *-- Detail lines
130 IF .NOT. gl_summary
131 DO Detail
The first line of the File Loop is a DO WHILE command. The conditions
for the DO WHILE are
FOUND() is there a record to be processed
.NOT. EOF() make sure the record pointer is not at the End Of File
gl_prntflg the print flag is set to .T. (TRUE) [if the user
presses the Escape key to cancel the report this flag
is set to .F. (FALSE)].
The next step is to test the current record to insure that it is a
member of the group of records being printed. If it is not,
indicating that the group has changed, the report variable gn_level is
set to the value of four. If the record is of the same group, the
report variable is set to a value of zero. These values are
meaningful to the tests that follow.
If the variable gn_level has been set to a value other than zero (in
this case it has been set to four) then the Group Footer (better known
as the Group Summary) information is printed and the Group Summary
variables are initialized for the next group.
The Group Heading is then printed for the next group, again only if
gn_level is anything other than zero.
The routine Upd_Vars is called to calculate the values of any User
Defined calculated fields for the current record and to update the
Group and Report Summary fields.
NOTE: Unnamed calculated fields are not calculated in the Upd_Vars
routine. Because these fields have no name against which to store
their value they are calculated at the time of being printed in a
direct print statement (refer to the March '89 issue of
TechNotes/dBASE IV, "Using dBASE IV" on page 28 for more information
on the order of precedence for calculated fields).
Next the .FRG checks to see if this is to be a summary report only.
If not, the Detail routine is called and the information for the
current record is printed.
The CONTINUE command is the compliment to the internal LOCATE that
dBASE IV performed prior to executing the report. It will cause the
record pointer to move to the next record matching the condition
supplied to the REPORT FORM command (i.e. A FOR, WHILE or SCOPE
condition) or, if no condition was specified, to the next record in
The ENDDO takes us back to the DO WHILE command and the process is
repeated for the next record.
Ending the Report
136 IF gl_prntflg
138 DO Grpfoot WITH 97
139 DO Rsumm
140 IF _plineno <= gn_atline
141 EJECT PAGE
145 DO Rsumm
146 DO Reset
150 ON PAGE
154 DO Reset
156 * EOP: C:\DB4\MIKE.FRG
Once all of the records have been processed or if the user has pressed
the Escape key, the conditions for the DO WHILE loop become no longer
true. Process continues with the commands following the ENDDO
If the user had pressed the Escape key the variable gl_prntflg would
have been set to .F. (FALSE). The IF statement here checks for that
occurrence. Assuming the Escape key has not been pressed and all of
the records have been processed, the Grpfoot routine is called and the
Group Summary is printed for the last group. Then the Rsumm routine
is called to print the summaries for the entire report.
The current line number is then checked to see if a forced page
advance will be necessary to print out the page footer on the final
page of the report.
Had the user pressed the Escape key the Report Summary would be output
for those records already printed. The environment would be reset and
the report terminated.
Assuming the report has completed naturally, the ON PAGE definition is
cleared so as not to interfere with any other printing that may
occur. Then the ENDPRINTJOB is executed.
The ENDPRINTJOB is similar to the PRINTJOB we saw earlier. In this
case the ENDPRINTJOB sends the ending reset codes from the currently
selected printer driver as well as any Ending Control Codes that may
have been specified in the PRINT menu or the dBASE IV system
The Reset routine is then called to set the environment back to the
state it was prior to running the report. Control is then passed back
to the calling program, the DOT prompt or the Control Center depending
on where the report was called from.
Now lets look at the sub-routines that are called throughout the
.FRG. We will look at these in the order in which they appear in the
Updating of Variables: Upd_Vars
158 *-- Update summary fields and/or calculated fields in the detail
159 PROCEDURE Upd_Vars
161 *-- Summary calculation - Sum
163 *-- Summary calculation - Sum
165 *-- Summary calculation - Sum
167 *-- Summary calculation - Sum
169 *-- Summary calculation - Sum
171 *-- Summary calculation - Sum
174 * EOP: Upd_Vars
The first subroutine is the Upd_Vars or Update Variables routine. As
mentioned earlier, this routine performs all of the Group and Report
Summary calculations as well as calculated fields defined by the user
in the report definition.
Aborting the Print Routine: PrnAbort
176 *-- Set flag to get out of DO WHILE loop when escape is pressed.
177 PROCEDURE prnabort
180 * EOP: prnabort
The next routine is the PrnAbort or Print Abort routine. This routine
is called when the user presses the Escape key to cancel the running
of the report form. It sets the print flag variable (gl_prntflg) to
.F. (FALSE) causing the DO WHILE loop to stop processing records (see
"The Pace Maker" on page 7).
Resetting Group Break Variables: Grpinit
182 *-- Reset group break variables. Reinit summary
183 *-- fields with reset set to a particular group band.
184 PROCEDURE Grpinit
185 IF gn_level <= 4
190 IF gn_level <= 4
194 * EOP: Grpinit
The Grpinit or Group Initialization routine resets the summary field
variables for the Group Summary bands of the report form. It uses
the report form system variable gn_level to determine which Group
Summary needs resetting (as discussed in the introduction).
The Group Header (Intro) Routine: Grphead
196 *-- Process Group Intro bands during group breaks
197 PROCEDURE Grphead
198 IF EOF()
201 gl_widow=.T. && enable widow checking
202 IF gn_level <= 4
203 DO Head4
207 * EOP: Grphead.PRG
The Grphead or Group Header routine is used to determine which Group
Heading (Intro) is to be printed (in case there is more than one group
First the routine tests to see if the report has reached the end of
the database file. If it has, there is no need to print a Group
Heading. Instead, the routine is exited.
If the End Of File has not been reached, the routine continues
processing. The report system variable gl_widow is set to .T. (TRUE)
which enables widow and orphan checking. Widow and Orphan checking
insures that a section of a report (i.e. Group Intro, Detail, Report
Heading, etc...) does not get broken between two pages.
Next the report form system variable gn_level is tested to see which
Group Heading is to be printed. The corresponding print routine is
gn_level is set to 0 to indicate that the new group has been started.
Control is then passed back to the main loop ("The Pace Maker", page
The Group Footer (Summary) Routine: Grpfoot
209 *-- Process Group Summary bands during group breaks
210 PROCEDURE Grpfoot
211 PARAMETER ln_level
212 IF ln_level >= 96
213 DO Foot96
216 * EOP: Grpfoot.PRG
The Grpfoot or Group Footer routine performs the same function as the
Grphead routine for the Group Summary bands.
Here the routine checks for a value passed as a parameter to determine
which print routine is to be called for the current Group. The value
tested is equal to 100 - gn_level as described in the introduction.
The Page Header Routine: Pghead
218 PROCEDURE Pghead
219 ?? "This is the PAGE HEADER" AT 26
221 *-- Print HEADING parameter ie. REPORT FORM HEADING
222 IF .NOT. gl_plain .AND. gn_length > 0
223 ?? gc_heading FUNCTION "I;V"+LTRIM(STR(_rmargin-_lmargin))
227 * EOP: Pghead
The Pghead or Page Header routine prints the page header defined in
the report form definition. In addition it prints the text passed to
the report form from the HEADING portion of the REPORT FORM command
(i.e. REPORT FORM HEADING "This is a heading").
The Report Intro Routine: Rintro
229 PROCEDURE Rintro
230 ?? "This is the REPORT INTRO" AT 26
233 * EOP: Rintro
The Rintro, or Report Intro, procedure is the routine that prints the
text specified in the Report Intro Band of the Report Form definition.
A Group Intro Print routine: Head4
235 PROCEDURE Head4
236 IF gn_level=1
239 IF 1 < _plength
240 IF (gl_widow .AND. _plineno+1 > gn_atline)
241 EJECT PAGE
244 ?? FNAME FUNCTION "T" AT 0,
245 ?? "Group Intro Band Grouped on The Database Field Fname" AT 18
Head4 is one example of many possible print routines for outputting
the text of the Group Intro Bands. Called by the Grphead routine,
Head4 specifically prints the Group Intro text for the first defined
group of the report form definition (in this example .FRG there is
only one Group break defined).
In the second IF statement we can see the Widow and Orphan checking
(described earlier) being performed. The embedded IF statement is
checking for gl_widow to be set to .T. (TRUE) and for the current line
number + 1 to be greater than the line number at which a page break
would normally occur. If this condition is true then a page break is
forced before attempting to print the text of the Group Intro.
The Group Intro text is then output.
The Detail Print Routine: Detail
249 PROCEDURE Detail
250 ?? AMOUNT PICTURE "99999.99" AT 0,
251 ?? COST PICTURE "99.99" AT 9,
252 ?? ARRIVE AT 16,
253 ?? CALC1 PICTURE "9999999.99" AT 26
256 * EOP: Detail
The routine Detail is where the text from the Detail Band of the
Report Form is output. This routine is called from the main loop
("The Pace Maker", page 7).
A Group Summary Print routine: Foot96
258 PROCEDURE Foot96
259 ?? GRPSUM1 PICTURE "99999.99" AT 0,
260 ?? GRPSUM2 PICTURE "99.99" AT 9,
261 ?? GRPSUM3 PICTURE "9999999.99" AT 26
Like Head4, Foot96 is one example many possible print routines for
outputting the text of the Group Summary Bands. Called by the Grpfoot
routine, Foot96 specifically prints the Group Summary text for the
first defined group of the report form definition (in this example
.FRG there is only one Group break defined).
The Report Summary Routine: Rsumm
265 PROCEDURE Rsumm
266 ?? RPTSUM1 PICTURE "99999.99" AT 0,
267 ?? RPTSUM2 PICTURE "99.99" AT 9,
268 ?? RPTSUM3 PICTURE "9999999.99" AT 26
269 gl_fandl=.F. && last page finished
272 * EOP: Rsumm
The Rsumm, or Report Summary, procedure is the routine that outputs
the totals, averages, etc... specified in the Report Summary Band of
the Report Form definition.
The Page Footer Routine: Pgfoot
274 PROCEDURE Pgfoot
275 PRIVATE _box
276 gl_widow=.F. && disable widow checking
278 IF .NOT. gl_plain
279 ?? "This is the PAGE FOOTER" AT 26
281 EJECT PAGE
282 *-- is the page number greater than the ending page
283 IF _pageno > _pepage
284 GOTO BOTTOM
288 IF .NOT. gl_plain .AND. gl_fandl
289 DO Pghead
291 IF gn_level = 0 .AND. gl_fandl
293 DO Grphead
295 gl_widow=.T. && enable widow checking
297 * EOP: Pgfoot
The Pgfoot or Page Footer routine performs four functions. First and
foremost it prints the footer text defined in the Report Footer Band
of the Report Form definition and then performs a form feed to put us
at the top of the next page.
One of the options we have available to us from the Print menu or
through the dBASE IV system variables is to print a specific range of
pages (i.e. page 10 to page 20). The Page Footer routine's second
task then is to check to see whether all of the requested pages have
been printed and if so, force the record pointer to the EOF (End Of
File) marker. Doing so will cause the main DO WHILE loop ("The Pace
Maker") to be exited and the report to stop printing.
The third activity for the Page Footer routine is to call the Pghead
or Page Header routine to print the Page Heading on the next page.
The fourth activity is to call the Grphead or Group Header routine to
print the text for the Group Header band. However this will only be
done if gn_level is equal to 0 meaning that we are in the process of
printing the records of the current group and not at the end of a
group where the Group Intro text would be printed anyway.
Some of the code seen here may or may not appear in all report form
.FRG files. It is dependent on whether or not these sections have
been included in the Report Form definition. for example, if no Group
breaks had been defined, the code testing for the Group Intro would
not have been included.
Page Break Routine for a Plain Page Report: Pgplain
299 *-- Process page break when PLAIN option is used.
300 PROCEDURE Pgplain
301 PRIVATE _box
302 EJECT PAGE
303 IF gn_level = 0 .AND. gl_fandl
305 DO Grphead
308 * EOP: Pgplain
If the option PLAIN had been included when issuing the REPORT FORM
command, the Pgplain or Page Plain routine would be called to handle
page breaks. The Page Plain routine causes a form feed and then
returns control to the main report loop without repeating the Page
Header or printing a Page Footer.
Resetting the Environment: Reset
310 *-- Reset dBASE environment prior to calling report
311 PROCEDURE Reset
312 SET SPACE &gc_space.
313 SET TALK &gc_talk.
314 ON ESCAPE
315 ON PAGE
317 * EOP: Reset
The last routine is Reset. Reset is used to put the dBASE IV
environment back to the state it was at prior to running the