Dec 082017
 
DBASE.prg file to wrap text in any field.
File WORDWRAP.ZIP from The Programmer’s Corner in
Category Dbase Source Code
DBASE.prg file to wrap text in any field.
File Name File Size Zip Size Zip Type
ARTICLE.TXT 18027 5804 deflated
EMPTY.PRG 647 396 deflated
WRAP.BIN 154 153 deflated
WRAP.PRG 2180 1047 deflated

Download File WORDWRAP.ZIP Here

Contents of the ARTICLE.TXT file


Another Wordwrap

BY KENNETH N. GETZ AND KAREN ROBINSON

In earlier issues, TechNotes has presented articles that described
word-wrapping routines written in the dBASE III PLUS interpretive
language--effective routines, but somewhat slow. Here we'll present an
alternative approach: an assembler routine that you can LOAD and CALL to wrap
long strings.

Wordwrap.ASM is an assembly language program that takes a long string and
returns a wrapped string. The dBASE III PLUS program that you use to print
the wrapped strings must define the margin settings, CALL Wordwrap to parse
the text, and print the string returned by Wordwrap. Wraptest.PRG, a dBASE
III PLUS sample program, demonstrates how to use Wordwrap.


Wordwrap.BIN

Wordwrap accepts up to 254 characters at a time and parses the string
according to the line width you specify. For example, if the character string
is 254 characters wide and your line width is 65, Wordwrap returns to the
calling program 65 characters at a time. If the sixty-fifth character falls
in the middle of a word, it wraps that word to the next line.

You CALL Wordwrap WITH the character string you want to wrap as the
parameter. Although you pass only one parameter on the command line, Wordwrap
uses three: "linewidth" is the ASCII representation of the desired line
width. "buffer" is a temporary buffer area used by Wordwrap to hold portions
of the string when parsing. "wrapstring" is the character string to wrap.

In the program that CALLs Wordwrap, you must declare these variables
consecutively, as shown below. (These variable names are arbitrary.)

linewidth = CHR(x)
wrapstring = SPACE(254)
buffer = SPACE(n)

where x is the line width you specify and n is equal to 254 minus the width of
the field to wrap.

The order in which you declare these variables is important because Wordwrap
locates them by their position in memory. Once you declare these memory
variables, do not change the size of "linewidth" or "buffer." Note that you
initialize "linewidth" using the ASCII representation because the value is
stored in one byte, making access to it simple at the assembler level.


Sending Parameters from dBASE III PLUS

Because Wordwrap.BIN requires the use of three parameters, it is useful to
understand the concept of passing parameters to and from external procedures.
When you CALL a procedure WITH a parameter, the address of the parameter is
passed to the procedure in the DS:BX register pair. Be aware that the DS
register references the data segment from dBASE III PLUS, not the data segment
of the CALLed program. One option is to pass multiple parameter strings as
one string (as done in dBASE TOOLS for C) and then parse the multiple
parameter string in the CALLed program. The second option is to declare dBASE
III PLUS variables in strict order so that they are stored contiguously in
memory. The assembly routine can then access the secondary parameters once it
has established the location of the parameter passed to it on the command line
(the DS:BX register pair). Wordwrap uses the second method. Variables are
stored in strict order and located by their relative positions in memory.

To be sure that you don't make mistakes in external procedures, you need to
understand how dBASE III PLUS stores character variables in memory. Each
character variable has two extra bytes, one at the beginning and one at the
end. The byte at the beginning contains the length of the string and the byte
at the end is a null (00 hex) byte that terminates the string. The length of
the string includes the null byte; so, the length value is one greater than
the number of characters in the string. The diagram below illustrates how the
following two variables are stored in memory:

linewidth = CHR(65)
wrapstring = SPACE(254)
buffer = SPACE(120)

{diagram here}

This method of passing multiple parameters is risky: It is easy to lose track
of where the variables and null bytes are stored in memory. Remember: Once
the variables "linewidth" and "buffer" are declared, do not reassign values to
them.


Setup

Before you use Wordwrap.BIN, you must fulfill these basic requirements.
First, your computer must have at least 320K of RAM. Second, in order to
assemble Wordwrap.ASM as a .BIN file, you need the IBM or Microsoft Macro
Assembler (MASM.EXE), version 2.0 or higher. The smaller assembler, ASM.EXE,
will not suffice because it does not support macros, and Wordwrap.ASM makes
extensive use of macros. You also need the programs LINK.EXE and EXE2BIN.EXE,
which are included as supplemental programs with DOS 2.0 and higher.

Follow the steps below to assemble Wordwrap.ASM, LINK, and convert it to a
LOADable .BIN file.

1. From the DOS prompt, type:

MASM WORDWRAP.ASM;

This creates an object file (Wordwrap.OBJ).

2. Then type:

LINK WORDWRAP;

This creates Wordwrap.EXE.

3. To create a .BIN file, type:

EXE2BIN WORDWRAP.EXE

4. Last, delete Wordwrap.EXE:

ERASE WORDWRAP.EXE

The semicolons used in the MASM and LINK command lines suppress any
interactive prompts for additional parameters.


; Program ...: Wordwrap.ASM
; Author ....: Kenneth N. Getz
; Date ......: February 1, 1987
; Version ...: dBASE III PLUS
;
;
;*****************************************************************************
SAVE MACRO R1,R2,R3,R4,R5,R6,R7,R8
IRP X,
IFNB
PUSH X
ENDIF
ENDM
ENDM

;-----------------------------------------------------------------------------
RESTORE MACRO R1,R2,R3,R4,R5,R6,R7,R8
IRP X,
IFNB
POP X
ENDIF
ENDM
ENDM

;*****************************************************************************
CODESEG SEGMENT BYTE PUBLIC 'CODE'

WRAP PROC FAR
ASSUME CS:CODESEG

START:
JMP SHORT BEGIN
LEN DB ? ; A storage space for the w length.

BEGIN:
PUSH DS
POP ES ; Make ES = DS so we can refere DI.
SAVE AX,DI,SI,CX
CLD ; Clear direction flag.
CALL GETBUFFER ; Point SI to first char Wrapstring
; and DI to first char in Buffer.
CALL ZEROSTRING ; Zero out Buffer string.
CALL FINDSPACE ; Point SI to character after last
; space in Wrapstring.
CMP AL,32 ; See if we searched for a space.
JNE FINISH ; If not, we're done.
CALL COPYSTR ; Copy from SI to DI.
FINISH:
RESTORE AX,DI,SI,CX
RET

;-----------------------------------------------------------------------------
FINDSPACE PROC NEAR
;
; Findspace finds the next space at which to wrap.
; The SI register ends up pointing to the character after the last space,
; if there are any spaces in the section between the beginning of wrapstr
; and the linewidth. It doesn't deal at all with the problem
; of no spaces in the wrapping region. That's
; up to you.
;
SAVE DI,CX
MOV AL,[LEN] ; AL := wrap length.
CMP AL,BYTE PTR [BX-1] ; Greater than length WrapString?
JAE DONE ; If not, Return,
MOV DI,BX ; Otherwise, point DI WrapString.
AND AH,0 ; Zero high byte.
ADD DI,AX ; Point DI to last possi character.
AND CH,0 ; Zero high byte.
MOV CL,[LEN] ; Move WrapLength into CX.
DEC CL ; It's one longer than length word.
MOV AL,32 ; Look for a space.
STD ; Set flag to move backwards.
REPNE SCASB ; Search for space.
INC DI ; DI points to space.
INC DI ; DI points to char after space.
MOV SI,DI ; Need SI to point to gi character.
DONE:
CLD ; Clear direction flag.
RESTORE DI,CX
RET
FINDSPACE ENDP

;-----------------------------------------------------------------------------
ZEROSTRING PROC NEAR
;
; Zeroes out strings with nulls. It expects DI to point to beginning string
; to be cleared out.
;
SAVE CX,AX,DI
AND CH,0 ; Clear out top byte.
MOV CL,[DI-1] ; Get length byte.
DEC CL ; Don't clear out null at end.
MOV AL,32 ; Move space to AL for clear out.
REP STOSB ; Put in CX number of spaces.
RESTORE CX,AX,DI
RET
ZEROSTRING ENDP

;-----------------------------------------------------------------------------
GETBUFFER PROC NEAR
;
; Getbuffer expects BX to point to beginning of main string, returns w DI
; pointing to the first character in buffer, and SI pointing to fi character
; in main string.
;
SAVE CX,AX
MOV DI,BX ; Point DI to beginning WrapString.
STD ; Set direction flag to w backwards.
AND AL,0 ; Move 0 into AL for REPNE MOVSB.
MOV CX,0FFFH ; Move a big number into CX.
REPNE SCASB ; Find first null (end of Buffer).
REPNE SCASB ; Find real null (end WrapLength).
MOV AL,BYTE PTR [DI] ; Move length into AL.
MOV LEN,AL ; Store away wrap length.
ADD DI,3 ; Point DI to start of buffer.
MOV SI,BX ; Point SI to beginning WrapString.
CLD ; Clear direction flag.
RESTORE CX,AX
RET
GETBUFFER ENDP

;-----------------------------------------------------------------------------
COPYSTR PROC NEAR
;
; Expects to find SI pointing to main string somewhere, DI pointing buffer's
; first char. Copies out the 'extra' part of the WrapString to the Buffer
; and terminates the WrapString at the wrap position.
;
SAVE AX,CX,DI,SI ; Must save SI since MOVSB mo it.
AND CH,0 ; Clear out top byte.
MOV CL,BYTE PTR [BX-1] ; Put length of original into CX.
MOV AX,SI ; Offset of starting byte in AX.
SUB AX,BX ; Subtract offset of first byte.
AND AH,0
SUB CX,AX ; Substract difference from to len
REPNZ MOVSB ; Move from SI to DI.
DEC DI ; Move back to NULL byte.
MOV BYTE PTR [DI],' ' ; Get rid of final null byte.
POP SI ; Get back to end of WrapString.
DEC SI ; Back up one space.
MOV BYTE PTR [SI],0 ; Put a NULL there to indicate end.
RESTORE AX,CX,DI
RET
COPYSTR ENDP
;-----------------------------------------------------------------------------
WRAP ENDP
CODESEG ENDS
END START


Wraptest.PRG

Wraptest.PRG is a dBASE III PLUS program that demonstrates how to use
Wordwrap. When you execute Wraptest, it prompts you for the line width and
page offset (left margin). It then declares the necessary memory variables
("linewidth," "buffer," and "wrapstring") and CALLs Wordwrap for each record
in Wrap.DBF. Wordwrap accepts the text, storing it in a buffer variable. It
parses the string according to the linewidth and sends back a string for
Wraptest to print. Wraptest then calls Empty.PRG to clear "buffer" before
accepting the next record.

To use Wraptest, you need a text file of information that you want to print
out and a database file named Wrap.DBF with one character field called
"Line." The width of "Line" can vary, but the sum of "linewidth" and "Line"
cannot exceed 254 characters. In other words the following expression must
evaluate to true (.T.).

LEN(Line) = 254 - linewidth

If linewidth = 35, the maximum length of Line is 219. If the sum of
"linewidth" and LEN(Line) exceeds 254, dBASE III PLUS returns the error
message "***Execution error on +: Concatenated string too large."

Use APPEND FROM SDF to read your text file into Wrap.DBF. Execute
the program with the command line:

DO Wraptest


* Program ...: Wraptest.PRG
* Author ....: Kenneth N. Getz
* Date ......: February 1, 1987
* Version ...: dBASE III PLUS
* Note(s) ...: This program demonstrates how to use Wordwrap.BIN,
* an assembly language program that wraps text according
* to the specified line width and offset. It assumes that you
* have available the database file, Wrap.DBF, which has one
* field named Line and which already contains the text to wrap
*
SET TALK OFF
LOAD Wordwrap
SET PROCEDURE TO Empty
USE Wrap

* ---Define formatting memory variables.
pagewidth = 80
linewidth = 0
offset = 0

* ---Get line width and offset, checking to make sure that
* ---the text will fit within the page width (pagewidth). If it
* ---will not fit, adjust the offset to accommodate.
CLEAR
INPUT "Enter the line width:" TO linewidth
INPUT "Enter page offset: " TO offset
offset = IIF(linewidth + offset > pagewidth, pagewidth - linewidth, offset)
CLEAR

* ---Initialize the variables that Wrap.BIN will use. They must be
* ---created in the order shown below.
width = CHR(linewidth)
buffer = SPACE(254)
wrapstring = SPACE(254)

DO WHILE .NOT. EOF()

* ---If the field is empty, print a blank line. This is important for
* ---separating paragraphs with a blank line.
IF LEN(TRIM(Line)) = 0
DO Empty WITH 0
?
SKIP
LOOP
ENDIF
* ---If buffer is empty, store the contents of the next record to
* ---wrapstring. If not, add to wrapstring a space and the
* ---contents of the next record.
* ---Line is the field name.
wrapstring = IIF(LEN(TRIM(buffer)) <> 0, TRIM(buffer) + ' ' ;
+ TRIM(Line),TRIM(Line))
CALL Wordwrap WITH wrapstring
@row(), offset SAY wrapstring
* ---If there is data left in the buffer, CALL Wordwrap
* ---again until length of buffer is less than linewidth.
DO Empty WITH linewidth
SKIP
ENDDO

* ---If there is data left in the buffer, CALL Wordwrap again until
* ---the length of buffer is less than 0.
DO Empty WITH 0
CLEAR ALL
RETURN

* EOP Wraptest.PRG


* Program .....: Empty.PRG
* Author ......: Kenneth N. Getz
* Date ........: February 1, 1987
* Version .....: dBASE III PLUS
* Note(s) .....: This procedure is called by Wraptest.PRG to clear the
* variable buffer.
*
PARAMETERS linewidth
DO WHILE LEN(TRIM(buffer)) > linewidth
wrapstring = TRIM(buffer)
CALL Wordwrap WITH wrapstring
@ ROW() + 1, offset SAY TRIM(wrapstring)
ENDDO
* EOP Empty.PRG


Other Uses of Wordwrap and Wraptest

Wordwrap can be useful for many different applications. As the Wraptest.PRG
program demonstrates, you can APPEND a text file of information into a
database file and then print that database file as one continuous document.
You can also use Wordwrap to wrap one record at a time, as you would when
printing a report such as this one:

In this example, Description is a 220-character field that should wrap within
itself, instead of all the Description fields printing together and wrapping
as one continuous paragraph. You can easily modify Wraptest.PRG to
accommodate this type of report. Within the DO WHILE .NOT. EOF() loop, use
ROW() or PROW() to place the additional fields in the correct columns.
Initialize "linewidth" to the length that you want Description to be (in this
case 33) and "offset" to the column position where you want Description to
start printing. In addition, in order to empty the buffer completely between
records, modify the command

DO Empty WITH linewidth

to read

DO Empty WITH 0

This modification separates the records into distinct items to wrap, printing
the contents of the buffer before SKIPping to the next record. Here is an
example of the changes you must make to the DO WHILE loop in order to produce
the example report:

DO WHILE .NOT. EOF()
@ ROW(), 6 SAY Date
@ ROW(), 17 SAY Hours
* ---Line is the field to word wrap.
wrapstring = IIF(LEN(TRIM(buffer)) <> 0, TRIM(buffer) + ;
" " + TRIM(Line), TRIM(Line))
CALL Wordwrap WITH wrapstring
@ ROW(), offset SAY wrapstring
* ---Empty the buffer before going to the next record.
DO Empty WITH 0
* ---Print a blank line between records.
@ ROW() + 2, 0
SKIP
ENDDO


Conclusion

Further Information: See "A Procedure for Wrapping Long Strings" in the April
1985 issue; "LISTing a Database File in a Vertical Form" in the April 1986
issue; and "Mailmerge Application Programming" in the September 1986 issue for
other algorithms and applications using word wrapping.


 December 8, 2017  Add comments

Leave a Reply