Category : Dbase (Clipper, FoxBase, etc) Languages Source Code
Archive   : PRNSAY.ZIP

Output of file : CUSTMIZE.DOC contained in archive : PRNSAY.ZIP

- Exploring a PRINT-SAY* .PRG file -

[This section and the next are intended as reference material for
later interactions with PRINT-SAY and should probably be skipped if
you are reading through for the first time.]

As mentioned above, PRINT-SAY is primarily a programmer's tool. In
a professional environment we expect our computers and software to
adapt to our designs and not vice versa.
A program which would generate virtually any possible report would
be at least as large and complicated as dBASE itself and has yet to
be written. In the meantime the dBASE programming language provides
the minimum necessary tools to create more sophisticated reports than
LIST and REPORT FORM can provide. PRINT-SAY is intended to alleviate
most of the routine drudgery of such programming for the programmer.
The programmer will take the .PRG output file created by PRINT-SAY
and customize it to suit the demands of a more complicated report.
Non-programmers with a modicum of dot-prompt expertise can also
customize the PRINT-SAY .PRG files in many ways which require no
understanding or experience with programming. The reason for this
is that a dBASE program is to a large degree simply a batch of dot-
prompt commands grouped together to function as one task. The pur-
pose of this section of the manual, then, is to provide a second
guided tour of a more advanced nature. A tour which examines the
details of the PRINT-SAY .PRG file and illustrates some customization
changes you may want to use at some point in the future.

I strongly recommend that the following material be read with
your dBASE manual in hand. It would also be highly useful for the
novice programmer to read the really excellent chapters provided in
the dBASE III Plus manual on how to program; or one of the many ex-
cellent books on the subject, if you are still using non-Plus, such
as Alan Simpson's Understanding dBASE III from SYBEX.

I will presume you are using PRINT-SAY from within dBASE itself by
using the RUN command explained in the first manual. I will also
presume (as an example) that you have already created the PRINT-SAY
.PRG file called REPORT-A.PRG. from Section 3, A Guided Tour, in the
first manual.

5.3.1. STEP ONE:
The first step is to copy REPORT-A.PRG and work with the copy in
case there are any mishaps:


5.3.2. STEP TWO:
Next, you need a way to view and change NEWREPRT.PRG. (dBASE
provides a primitive editor MODIFY COMMAND; many people use WordStar
(tm); for my part, I find that the notepad of SideKick (tm) is by
far the handiest tool for this task.) However, for the sake of the
example let's stick to the lowest common denominator.


Now you should see a new screen filled with programmer-ese. In
fact, there is little mystery here. Most lines of a program are
dBASE commands, which could be typed in at the dot-prompt: 'set
margin to 0', 'set device to print', 'PageNo = 1', etc. Some lines,
such as: 'do while .not. eof()', however, are valid only in programs.
To see more of the program the [PgDn], [PgUp], arrow keys, etc. of
the numeric keypad will move you through the file. The key combin-
ations [Ctrl-A] and [Ctrl-F] move left and right from one blank space
to the next to speed up left/right motion. (To exit use the [Esc]
[Note that you can also get a print-out of NEWREPRT.PRG (or any .PRG
file) with the dBASE command


This allows you to see more of the code at a glance, which in turn
to clarify relations between various parts of a program. (To see even
more of the code at a glance use the Cotton-Ware utility, Prim - with
which, incidentally, you can also print this manual.)]
Let's read through the program - whether via screen or print-out -
to see what some of the more cryptic lines do:

*** Program: REPORT-A.PRG
*** Called from:
*** Programmer: A. PROGRAMMER
*** Created on: 10/11/87
*** Last Update:
*** Purpose:
*** (This report is in Compressed pitch)

'*** Program: REPORT-A.PRG' - This is the program title. Every
line in a dBASE program which starts with an asterisk is ignored
by the computer and is purely for human consumption. These lines
are called comments or documentation; the computer has no need
for a title.

'*** Called from:' you may insert the name of another .PRG file
if it calls this file in the course of its execution.

'*** Programmer: A. PROGRAMMER' this line identifies you as the

'*** Purpose:' insert a brief explanation of exactly what this
report is supposed to report on. It may be obvious to you now;
6 months from now this brief statement will save you some aggra-

** --------------------- Initialize Environment --------------------- **
set talk off
set echo off
set index to CLIQNAME

'** --- Initialize Environment --- **' - since this line starts
with an asterisk it is ignored by the computer. This is what is
technically known as a comment, and its purpose it both to explain
the code which follows it and to section off that code into a
logical group to make program reading easier. All the in-code
comments created by PRINT-SAY take this form of two asterisks fol-
lowed by dashes, but this is purely an arbitrary artifact.

'set talk off' and 'set echo off' are commands which tell dBASE
not to send anything to the monitor which isn't specifically re-
quested by the programmer. Note: if your report is intended to
be called from another .PRG program, be sure to delete these two
lines and both other pairs of 'set talk on' and 'set echo on' lines
farther down in the code.

'use CLIQUE' and 'set index to CLIQNAME' - these lines are the
familiar dot-prompt commands you are hopefully accustomed to
using every day.

** ---------------------- Initialize Variables ---------------------- **
ResetCode = chr(27)+chr(64)
PicaCode = chr(27)+chr(80)
CompCode = chr(15)
CompOffCode = chr(18)
DbleOnCode = chr(27)+chr(71)
DbleOffCode = chr(27)+chr(72)
Bar = "=============================================================;
PageNo = 1
store 0 to Sub_1, Sub_2

'ResetCode = chr(27)+chr(64)' - "ResetCode" is NOT a dBASE func-
tion or command. Instead it is a dBASE MemVar (memory variable),
which is a way of getting the computer to store a certain number,
letter, or phrase and recall it each time the MemVar name is used.
This is exactly like saying X = 1 in algebra; it's just that
programmers prefer to use more descriptive variable names than
X, Y, and Z. In this case, however, ResetCode is not even being
assigned a numerical value but that of a group of characters
treated as a whole - a "string". Once a MemVar has been assigned
a value, the programmer may change that value at whim but dBASE
won't let him change the TYPE of that value. In this case Reset-
Code may become any single character or group of characters, as
long as they are bunched together between quotes, but never a
numeric (non-quoted digit) type.
The 'chr(27)+chr(64)' phrase is what is being stored for future
use. More on MemVars below at PageNo.
Almost all printer manuals explain these chr() codes in more
detail than I care to go into here. The dBASE chr() function sends
a (hopefully) non-printable instruction to the printer which modi-
fies all further activities of the printer in a specific fashion.
Most of these instructions start with the character chr(27)
(Escape) which tells Epson printers that what follows is a command
and not for printing. (Incidentally, if your non-Epson printer
starts each PRINT-SAY report with an unwanted '@' character,
remove this line.)
If your printer is totally non-Epson compatible, as for example
a Hewlett-Packard LaserJet, you will have to change each of these
codes to the 'escape sequences' in your manual which perform the
equivalent task.

- Let's skip down a few past the rest of the printer codes:

'Bar = "======...==' - this MemVar will be used further along to
create the double line which separates the header of the printed
report from its body. The long row of characters repeats whatever
character you entered as the answer to Question 8 in the Re-Usables

'PageNo = 1' Reading this line a programmer knows that the value
stored under the name of the MemVar 'PageNo' will only be used
for assigning a page number to the report, and that the variable
will stay at the value '1' until the next time the MemVar is as-
signed a value. Because the 1 is not in quotes we know it is a
numeric variable and arithmetic may be performed with it.

'store 0 to Sub_1, Sub_2' - this line creates two new MemVars
in one stroke using the dBASE STORE x TO command. Obviously,
one could create as many MemVars as wanted with one command -
as long as you wanted them all to have the same value.

** ---------------------- Create Screen Display --------------------- **
Begin = " "
@ 16,34 say "To proceed:"
@ 18,17 say '1. Start printer 2. Insert 8 1/2"-wide paper'
@ 20,17 say "3. Scroll to start of new page or perforation"
@ 22,17 say "4. Press A to Abandon; any other key to begin. . ." get Begin
if Begin = "A"
set talk on
set echo on
@ 12,35 say "PRINTING..."

- this line is an example from this group of messages to the
monitor. The '@ say' combination is explained in detail in
each dBASE manual. In brief, '@ say' says:

'at row X, column Y say the following'.

X and Y are the addresses of a point on the monitor screen (or
the printed page) exactly like the X,Y co-ordinates on graphs we
so tediously studied in algebra many years ago. Normally, you
may be used to using the "?" character to write a message to the
screen; @ SAY allows more control of message placement.

** ----------------------- Set-up For Printing ---------------------- **
set device to print
set console off
@ prow(),pcol() say ResetCode
do while .not. eof()

'set device to print' sends all following commands to the printer
instead of the monitor.

'set console off' tells dBASE not to send any further messages to
the monitor or repeat out-put there.

'@ prow(),pcol() say ResetCode' this cryptic line sends the
printer code defined in the MemVar command described above to
the printer which tells it to clear all previous format settings
from its memory.
As in the monitor '@ say' commands above, the formula is also:
at row X, column Y say the following. But in this line X and Y
are not directly specified as numbers; instead two dBASE FUNCTIONS
are used which tell dBASE to ask the printer for the present row
address [prow()] and the present column address [pcol()] and to
substitute whatever numbers it receives from the printer into the
program at this point. (See the Functions section of your manual.)
When the computer is forced to look up an address like this,
we are said to be using RELATIVE ADDRESSING.
Now that was a pretty technical discussion: you should be pleas-
antly surprised that you nearly understood it the first time

'do while .not. eof()' - this is the beginning of what programmers
call a LOOP. Loops are the most powerful tool in the programmer's
arsenal. Near the end of the program you will see another command:
'enddo'. Everything between the words 'do while' and 'enddo' are
repeated over and over by the computer.
Notice that all the lines which are enclosed between each pair
of 'do while' and 'enddo' commands are indented by two characters.
The indentation makes the program hugely more understandable for
human beings (the computer ignores it). Because of the indentation
we can see at a glance all the lines which are part of the loop and
which are therefore to be repeated.
The '.not. eof()' part of the line is the CONDITION which deter-
mines when the computer will STOP repeating the loop. In this case
the condition reads: "DO what follows, WHILE you are NOT at the end
of the database file which is currently opened". Eof() is another
dBASE function, which returns a True or a False, depending on whether
or not the database has been opened to the point just after the last
record - the END-OF-FILE point. This end-of-file point is reached
after every SUM or TOTAL and after every LOCATE, SEEK, or FIND which
fails to find its target. The SKIP command below will also finally
reach the eof() point. Eof() is also True if the file is completely
empty - contains zero records.
After reading this line the computer will first check to see if
eof() is true or false. If true (hopefully not) it has nothing to
do and skips down to the matching 'enddo' and proceeds from there.
If false (in other words, the record pointer is pointing to a
record) it proceeds to execute each of the following commands
until it reaches a matching 'enddo'. When it reaches the 'enddo'
it then LOOPS up to the beginning of the 'do while' loop, checks
on eof() and repeats the same steps over and over, as long as the
function eof() does not return a true.
The periods around the not in '.not.' tell dBASE that the word
enclosed is a CONDITIONAL word and not a MemVar. '.and.', '.or.',
and '.not.' are the three logic conditional words. While '.t.'
and '.f.' mean True and False to dBASE.

** ---------------- Code For Printing Page Header ----------------- **
set margin to 0
@ 2,0 say CompOffCode+PicaCode+DbleOnCode
@ prow()+2,6 say date()
@ prow(),71 say "Page" + ltrim(str(PageNo))

'set margin to 0' - set margin is a normal dBASE command which is
often issued from the dot-prompt. This is also the first of the
repeated commands in this 'do while' loop.

'@ 2,0 say CompOffCode+PicaCode+DbleOnCode' - for this report,
PRINT-SAY has chosen to use Compressed pitch to squeeze all the
fields in we asked for; in other reports this may be PicaCode or
EliteCode. However, in every report PRINT-SAY starts with PICA pitch
so that the banner lines of each report will have the same visual
format and since compression simply isn't needed at this point in
the printout.
Note also that this the one of the few lines in which the X,Y
co-ordinates in an '@ say' command are simply given straight
out - without forcing the computer to perform a prow() or pcol()
calculation. The technical term for sending actual numbers as an
address is ABSOLUTE ADDRESSING. This line tells the printer to
move its print head to the first character of the 2nd row down
from its initial position; the chr() values mean: switch to pica

'@ prow()+2,6 say date()' - this @ SAY uses the other type of
addressing, relative, again for the row value, and an absolute
(non-look-up) value for the column. When it gets there it is
commanded by the 'date()' function to ask the computer for the
current date from its clock and put the answer at this location
on the printed page.
More importantly, the '+2' phrase tells the computer it is time
to cease printing on the previous line and skip down to rows to
start a new line of print.

Two more mixed-address commands list your report title (if any)
and the page number, completing the banner or header portion of
the printed page.
Because these commands are all inside the initial 'do while'
loop, they will be executed as many times as dBASE fills a page
with records and starts a new one. Towards the end of the code we
see another statement containing PageNo which reads: 'PageNo =
PageNo + 1'. This happens when a full page is printed, causing
the value '1' to be added to the memory variable called PageNo,
so that the next page will have one higher number than the one
previous. This sort of formula, X = X + 1, is called a COUNTER
by programmers and is used frequently.

** ---------------- Code For Printing Column-Titles --------------- **
set margin to 9
@ prow()+1,0 say CompCode
@ prow()+1,0 say "Title"
@ prow(),12 say "Last Name"
@ prow(),31 say "First Name"
@ prow(),54 say "Street Address"
@ prow(),78 say "Phone No."
@ prow(),89 say "Dues Paid"
@ prow(),99 say "Dues Owing"
@ prow(),111 say "Date Rec'd"
@ prow()+1,0 say Bar + DbleOffCode

This group of code, all starting with '@ prow()' change the
print style to Compressed pitch and list the field titles across
the paper. Because each of these commands starts with a plain
'prow()' they are all sent to the same row on the page.

** ------------------- Code For Printing Records ------------------ **
do while .not. eof() .and. prow() < 60
@ prow()+1,0 say TITLE
@ prow(),7 say SURNAME
@ prow(),29 say FIRST
@ prow(),46 say STREET
@ prow(),78 say PHONE
@ prow(),89 say DUES_PAID picture "999.99"
Sub_1 = Sub_1 + DUES_PAID
@ prow(),101 say 100 - DUES_PAID picture "999.99"
Sub_2 = Sub_2 + 100 - DUES_PAID
@ prow(),113 say DATE_PAID
PageNo = PageNo + 1
@ prow()+1,0 say DbleOnCode + Bar

'do while .not. eof() .and. prow() < 60'. The code lines fol-
lowing this one until the "enddo" line all work together to write
a single line of field-data across the page then they skip down
and repeat the process until either the end of the file or the
end of the current page is reached. The result of all this is a
loop within a loop! This is a very powerful tool in the pro-
grammer's repertoire; and it is by no means unheard of to have
a loop within a loop within a loop, etc. When loops come inside
other loops they are called nested; with the first loop being
referred to as the outer and the second the inner loop.
While this may seem a bit mind-boggling, it becomes easy to fol-
low if you simply keep in mind that each line of the outer loop is
executed once. . .until the first line of an inner loop is up to
bat. At that point the inner loop is executed over and over again
as long as its condition holds true. This may be zero times, once,
or ten thousand - but eventually something changes which makes its
condition (not end-of-file and not printing past row 61 in this
example) untrue. When that happens the outer loop takes control
back, any further of its lines are executed until the last line when
an 'enddo' line is reached. At that point control is looped back to
the start of the loop; its condition (not end-of-file, in this case)
is checked for true and false again; and, if eof() says false, the
loop is run through a second time. Eventually, the beginning of the
inner loop is reached - its condition is checked for true or false,
and, if true, it is executed any of number of times again.
What happens when we apply this theory to the particular case
in this program is that the outer loop prints a banner section
at the top of a page. Then it prints the underlined field-titles.
Then it meets the inner loop which goes to database, grabs a
handful of fields from that file, and spreads them across the
next row on the printed page under the field-titles. Then the
program comes to the single word 'skip'. This DOESN'T mean 'skip
a line on the page'; it means 'skip' down to the next record in
the database. It is entirely because of this line that the con-
dition at the start of the outer loop can ever come to the answer
'false' - and therefore end the print job. The inner loop is more
touchy: it will knock-off whenever end-of-file is reached, like the
outer loop, but also whenever the bottom of a page of paper is
We might think of the computer as bouncing frantically back and
forth between two walls - 'do while' and 'enddo' for each loop it
gets snared into by the program. Each bounce moves a little farther
along the walls between the entrance and the exit - though frequent-
ly there may be an inner entrance in one of the walls into which the
computer pops when it gets that far along the wall. Then it finds
itself in an inner room with an inner wall it must bounce its way
along, hoping to avoid yet a deeper trap, before it can exit back to
outer room to complete its work-out. . .and very likely be zapped
right back to the beginning of the trap again.

Also intermingled in the lines of code sandwiched into this do
while loop are two lines using the Sub_1 and Sub_2 MemVars. Because
of the "formula" used here, in both cases these MemVars act as ac-
cumulators, successively accumulating each value of Dues_Paid and
100 - Dues_Paid as the program writes each new line. At the end of
a page the total number accumulated into these MemVars is shown as
a sub-total.
Notice the single PageNo = PageNo + 1 line sandwiched between
the two 'enddo' lines. This is the only work the computer needs
to do after completing the inner loop before bouncing back to
the top of the outer loop.
Next, the loop which draws a line of equal signs across the
page is repeated.

** ----------------- Code For Printing Sub-Totals ----------------- **
@ prow()+1,86 say Sub_1 picture "99,999.99"
@ prow(),98 say Sub_2 picture "99,999.99"
@ prow(),pcol() say DbleOffCode

Next, the sub-total MemVars are displayed at the appropriate rows
and columns on the printed page. After these four lines a lone
'enddo' closes the largest, outside loop which started with 'do
while .not. eof()'. Therefore, all the lines which follow this line
are outside all loops and will only be executed once.

** ----------------------- Restore Environment ---------------------- **
@ prow(),pcol() say ResetCode
set console on
set device to screen
set talk on
set echo on

'eject' - the line following both loops - tells the printer to eject
the final page of printout so you can tear it off.

The 'chr(27)+chr(64)' code sent by the second ResetCode MemVar,
politely clears the printer of all the other chr() codes left
over from the print job.

The remaining lines simply undo the work of the first several
by restoring control of the monitor screen to dBASE and turning
the printer back over to itself.

- Making Changes -

[Note: as in Section 5, in Section 6 we will be using MODIFY
COMMAND to examine and change REPORT-A.PRG. See the beginning
of Section 5 for details if you're unclear on how to proceed.]

Drop down a few lines from the top of REPORT-A to enter a sentence
or two giving a general description of what this report does. What
may be obvious today may be mysterious next year. Also the logo line
which follows 'Property of:' may or may not be accurate. To change
it merely position the cursor at the beginning of the logo phrase then
start typing, what you type will over-write what PRINT-SAY wrote. To
get rid of any excess hold down the space bar. To delete this line
altogether simply position the cursor anywhere on that line and press

New lines further controlling the environment might be inserted
here, such as 'set status off' for dBASE III Plus. In that case,
drop all the way down to the second-last line and insert a corre-
sponding 'set status on'. To insert a line press enter at the
beginning of the following line or the end of the preceding line.
If more than one database needs to be opened simultaneously to
provide all the fields your report needs insert the appropriate
"SELECT 1" (or "SELECT A"), "SELECT 2", etc. lines with appropriate
USE and INDEX commands following each area. For example, NEWREPRT
currently reads:

set index to CLIQNAME

But a different report might have needed the lines

select 1
set index to CLIQNAMES
select 2
set index to OLDNAMES

If your printer uses some non-Epson compatible codes, you would
of course need to change those provided here accordingly. In some
cases it may be possible to enter a Near Letter Quality code as
well. Most printers will not allow simultaneous pitch control and
draft/NLQ control but yours may. In any case, it can't hurt to try
adding a line such as "NLQCode = #27#110" in this group, then below
(See 6.5.) another line to call the code.
Technical users please note: Many printer manuals offer a detailed
description of what Epson calls the Master Select command, chr(27)+
'!'+chr(x). Unfortunately, you can write this off: dBASE makes
mincemeat out of Master Select codes on any printer I've tried.

Quite possibly you may want to delete this altogether or change
the text it presents to the screen. You can do no harm I can think
of by changing any of the text between the double-quotes, changing
the numbers which show which row and column each line will appear
at, or even by inserting new lines. The lines involving the MemVar
`Begin' and the verb `read' illustrate only one way of gathering
the all-important fact that the user has turned on the printer and
gotten it adjusted for printing. See the dBASE manual for an ex-
lanation of the WAIT verb for another alternative.

Neither the 'set console off' nor the '- say ResetCode' lines
are absolutely necessary. In particular if the ResetCode is missing
from your printer's bag of tricks you may want to delete this line
(position the cursor anywhere on its length then press [Ctrl-Y]) and
change a line in the Screen Display section prompting the user to
turn the printer off then on again before printing.
The reset code clears the printer of any previous format commands
such as a margin setting or a different pitch; simply turning the
printer off then back on accomplishes the same function as the reset
code used here.
If you are attempting to use an NLQ code, tag your MemVar name on
to the ResetCode line here to send your NLQ code to the printer. For
example this line might become:

'@ prow(),pcol() say ResetCode+NLQCode'.

Just about anything in this block is game for easy customization:

One cute trick would be to add an expanded print code to the logo
display line. Chr(14) starts expanded print; chr(20) cancels it.
Because each character between these commands will occupy twice the
number of spaces as normal pica text, the number after the comma
in the prow()+1,xx segment will have to be reduced accordingly.
This number shown here by "xx" is the column address - the left-most
point on the line at which the say portion of the command will be
A typical expanded logo might be:

@ prow()+1,23 say chr(14)+"Metro Police Dept."+chr(20)

Since the logo is 17 characters long, it will consume 34 chars.
of space in expanded print; presuming normal paper which gives us
80 characters of space in Pica, we're left with 46 blanks: 23 on
each side of the logo to centre it.

Nothing could be simpler. Immediately after the logo line add
a new line with exactly the same format, changing only the number
after the comma and the string of characters in quotes. An example:

@ prow()+1,20 say 'Homicide, Suicide, and Ax-Murders Division'.

Since this text is 41 characters long, centering it would require
20 spaces on one side and 19 on the other, therefore, the number
following the comma can be either 19 or 20.

The page number and date positions may be changed within the report
title line to suit your taste by simply modifying the column number
following the '@ prow(),' portion of these lines.
A second report title line may be added above or beneath the one
PRINT-SAY created simply by following the same procedure described
above for adding a second logo line.

The whole body of the report may be moved left or right by chang-
ing the margin numeral.

Any of the column titles may be moved are left or right of the
centre of their fields by changing the column address numbers on
their lines. Often a field for a name of an address needs to be
kept as long as the longest name or address, while the vast major-
ity of the names or addresses are much shorter, leaving an unsightly
gap in the report. A further consequence of this situation is that
the titles appear to be too far to the right of the names or addres-
ses below them.

Occasionally it is desirable to break a field title into two
phrases one above the other, especially if the title needs to be
significantly longer than the width of the field itself. Perhaps
this is a little more complicated than what we've done so far, but
it is far from being difficult. Let's use the titles section from
REPORT-A.PRG, created in section 4 of this manual as an example.

Last Name First Name Street Address Phone No. Received Owing Date Rec'd

Here again is the section of code which generated these titles
(I had to leave the first title off, above):

@ prow()+1,0 say CompCode
@ prow()+1,0 say 'Title'
@ prow(),12 say 'Last Name'
@ prow(),31 say 'First Name'
@ prow(),54 say 'Street Address'
@ prow(),78 say 'Phone No.'
@ prow(),89 say 'Received'
@ prow(),99 say 'Dues Owing'
@ prow(),111 say "Date Rec'd"

The first one starts the ball rowing by turning on compressed pitch
after dropping down one line (row) from the date/report title/page
number line. The second statement drops down yet another row and
prints the first column title, 'Title'. All the rest of the commands
stay on the same line (whatever line number prow() refers to in this
case) but move to a different COLUMN address: 0, 12, 31, 54, etc. When
that much is clear read on:
Now, suppose we wanted to change the field titles to look something
like this:
Dues Dues
Last Name First Name Street Address Phone No. Received Owing Date Rec'd

which is a fairly common practice. What we need to do is to insert
a row with three titles between the date/report title/page number
line and the current titles line. So, between the first two lines of
the block of code shown above we need to insert a new line which
will print the word "Dues", then a second new line which will also
print the word "Dues" (but in a farther-right location). We can
steal a lot from the code PRINT-SAY generated to do this. Since the
first "Dues" is going above the word "Received" we can borrow "Re-
ceived"'s address and add two to it to get the location for the first
"Dues", and borrow the exact address from the word "Owing" for the
second "Dues". Like so:

@ prow()+1,0 say CompCode
@ prow()+1,91 say 'Dues' { - new line
@ prow(),100 say 'Dues' { - new line
@ prow()+1,0 say 'Title'

- It's as simple as that!

In the last fraction of the line `do while .not. eof() .and. prow()
< 60' - the `60' is the last row of print the printer will write
before starting a new page. Change this to any value you like less
that the maximum which will fit on one page.
Any fields which you've entered as being numeric have a dBASE
picture statement added to them. Occasionally, you may want to
modify this picture in one of the multitude of variations shown in
your dBASE manual.

When using MODI COMM to edit a file, always remember to exit with
[Ctrl-W] or [Ctrl-End]: NEVER use [Esc] unless you have consciously
decided to abandon the changes you've made.
A true programmer will possibly make countless other modifications
to this code to handle any number of special situations. The above
represent changes which can be easily controlled by the average dot-
prompt user.


@ say - 5.3.6
ASSIST - 1.2, 2.2
Addressing, Absolute - 3.1.2, 4.2, 5.3.9
Addressing, Relative - 3.1.2, 5.3.7
Asterisk - 5.3.3
Chr() - 5.3.5, 6.6.1
Clique.dbf - 4.1
Code generators - 1.1
Column address - 5.3.6, 5.3.7
Comments - 5.3.3, 5.3.4
Compatibilities, Printer - 1.1, 1.2
Compressed pitch - 3.1.3, 4.8
Contents, field - 3.2.2, 4.5
Ctrl-C - 2.3
Ctrl-A - 5.3.2
Ctrl-F - 5.3.2
Ctrl-W - 5.3.5, 6.11
Ctrl-End - 5.3.5, 6.11
DO - 2.2
DO WHILE - 5.3.8, 5.3.11, 5.3.12
Databases - 3.1.9
Decimals - 3.2.5
Default - 3.1.2
DrawLineChr - 5.3.5
Eject - 5.3.14
Elite pitch - 3.1.3
Enddo - 5.3.8, 5.3.12
Eof() - 5.3.8
Epson - 1.1
Esc - 5.3.2, 6.11
Exiting PRINT-SAY - 2.3; MODI COMM - 5.3.2
Expanded print - 6.6.1
Field titles - 3.2.1, 4.5, 5.3.10; re-locating - 6.7.1, adding - 6.7.2
Field contents - 3.2.2, 4.5
Field size - 3.2.4
Generator, Code - 1.1, 1.2
IF - 3.1.11
Indexes - 3.1.10, 4.3
Installation (of PRINT-SAY) - 2.1
LIST - 1.1
LIST STRUcture - 2.2, 3.2.2, 4.1
Length of paper - 3.1.5
Licensing, Site - 7.1
Logo - 3.1.1, 6.1; additional - 6.6.2
Loops - 5.3.8, 5.3.11, 5.3.12
MEMO fields - 1.5
MODIFY COMMAND - 4.8, 5.3.2
MemVars - 5.3.5
Memory Variables - 5.3.5
NLQ - 6.3, 6.5
Naming files - 3.1.7, 4.3
Number of fields - 3.1.12
Numeric, fields - 3.2.3, 4.7
Out-put file - 3.1.7
PageNo = 5.3.5
PCOL() - 5.3.6, 5.3.7
Pica pitch - 3.1.3
Printers - 1.1
PRINTSAY.DAT - 3.3, 4.8
PROW() - 5.3.6, 5.3.7
RUN PRINTSAY - 2.2, 4.1
RUN DEL - 4.1
RUN COPY - 5.3.1
Re-Usables - 3.1, 4.2
Relative addressing - 3.1.2, 5.3.7
ResetCode - 5.3.5, 6.5
Revision Mode, Initial Questions - 4.4
Revision Mode, Field Questions - 4.8
Row address - 5.3.6, 5.3.7
SELECT - 1.3, 6.2
SET CONSOLE OFF/ON - 5.3.7, 6.5
Separators - 3.1.6
Size, fields - 3.2.4
Skip - 5.3.12
Sub-totals - 3.2.6, 5.3.13, 6.10
TRIM() - 3.2.2
Throw-aways - 3.1.7, 4.3
Title, report - 3.1.8
Titles, Field - 3.2.1, 4.5, 5.3.10; re-locating - 6.7.1, adding - 6.7.2
USE - 3.1.9
Underline - 5.3.11
Variables - 5.3.5
Width of paper - 3.1.4

1988, by Dale Cotton, Toronto, Canada.
All Rights Reserved.

  3 Responses to “Category : Dbase (Clipper, FoxBase, etc) Languages Source Code
Archive   : PRNSAY.ZIP

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: