Dec 102017
 
Aston Tate dBase IV Tech Notes for Sep 90. Useful information.
File TN9009.ZIP from The Programmer’s Corner in
Category Dbase Source Code
Aston Tate dBase IV Tech Notes for Sep 90. Useful information.
File Name File Size Zip Size Zip Type
TNDB0990.TXT 87679 27612 deflated

Download File TN9009.ZIP Here

Contents of the TNDB0990.TXT file


1 The BIG IIF August 1990 dBASE IV


This article is reprinted from the August 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 Big IIF
Michael P. Dean

Indexes are one of the backbones to information management. They are
also a major pain for development. When you are developing an
application for end users that will not have dBASE IV but only a
compiled Runtime version, an ongoing variable factor will be indexes.
Sure, they can tell you what they need during the time you're
developing the system, but most likely those needs will change or they
won't think of it 'til later. This leaves you with lame decisions
like whether or not to include an index for every occasion, taking up
untold amounts of space on their hard drive and slowing down
performance.

Even if your users do have the software allowing to make indexes, do
they know dBASE IV only well enough to type the word "DBASE" followed
by the name of your program at a DOS prompt? In those situations and
more, you'll be the "doctor on call" when things go awry or need
changing. Well, this article may not completely free you from the
situations described but will give the new and inexperienced user an
additional interface to help them help themselves.

Indx.prg is a series of procedures that will allow users to create,
change, delete and display index keys. With Indx.prg, one can create
multiple field indexes, although it will not allow for creating
indexes that use functions (such as SUBSTR() for example). It will,
however, allow for the combination of all field types available in
dBASE IV (except MEMO fields; you cannot index on a MEMO field in
dBASE IV) and it will take care of all the conversion necessary (STR()
for numerics, DTOS() for dates, and a logical field conversion that
will put true values before false values). So all the user has to
worry about is choosing the fields from the structure on which the
index will be based and what to name it.

Notes and Nuances

A few things should be noted before diving into the program.
Throughout this program, READ statements are used in lieu of the more
predictable WAIT because WAIT performs an undesirable linefeed first.
READ will not perform the linefeed but will still behave like WAIT,
delaying processing until a key is pressed.

This program also uses the new indexing feature of dBASE IV which
allows for conditional indexes. Conditional indexes have the
convenience and speed of a typical index but can accommodate a
condition much like a SET FILTER command.

With a conditional index, you can create an index that is controlled
by, for example, Social Security Number but will only contain those
records where the lastname field is equal to "Smith":

INDEX ON ssn TAG temp FOR lastname = "Smith"

.MDX vs. .NDX Usage

Although .ndx files do not support conditional indexing nor can they
be arranged in descending order, .mdx tags can have conditions
applied to them, as well as be in descending order. Indx.prg supports
this functionality for creating unique, conditional and descending
index tags. Indx.prg creates both .ndx files and .mdx tags (see
screen shot on page 18).

After creating or changing an index, the user is asked whether the
index should contain duplicate entries. If answered "No", the index
will be a unique index, including only the first record with a
particular key in the index. The user is also prompted as to whether
the index should be in descending order. If answered "yes", then the
keyword DESCENDING will be added to the INDEX expression being built
behind the scenes. Remember, only .mdx tags have this luxury .

When creating or changing .ndx files, the prompt allows for up to 80
characters for a name which can include a drive designator, a full
directory path and the name. A file name restriction of up to eight
characters is still in effect. Make sure your users understand DOS
naming conventions so they are not calling you asking why they can't
name their index "1990 1st Quarter by Last Name". When typing in the
name, the entry will scroll to the left making use of the "S" function
in the GET statement. This is why it appears that you have
approximately 35 characters in which to type.

Since you are able to type in the name when changing .ndx files or
.mdx tags, you can also change the name and create a new index at the
same time, or you can simply press Enter when the name appears in the
prompt.

The program incorporates shadowing with animated effects when windows
and popups are selected. Therefore, to appreciate this, a general
screen background other than black is recommended on color monitors.
If you have only a text-oriented monochrome display, the shadows may
appear to fragment the elements over which they are painted.

One item of interest to you "Guinness" oriented readers. In the
module entitled ExpBuild, you'll discover some pretty sizeable
expressions with heavy emphasis on the IIF() function.

The program will be available on the Ashton-Tate BBS; names and
operating hours are listed on the back page of this newsletter. This
would obviously be the preferred method for obtaining this file rather
than typing it in from the following pages. If you must do so, take
care with the larger expressions mentioned above that you type quotes
and commas were they are listed. In some cases, IIF() is not used as
a function but as a literal string that is being built into a memory
variable on which the indexing is performed using macro substitution.
The expressions are broken with semi-colons at times to accommodate a
lengthy line or, at times, for readability. When actually typing
this program in, be especially careful to follow the sequence of
characters so syntax errors can be avoided.

Even if you have no immediate need for this program, the animating
effects and use of complex expressions are worth exploring. There may
be an idea there worth incorporating into your own application.

* Program ...: Indx.PRG
* Author ....: Michael P. Dean
* Versions ..: dBASE IV Version 1.1
* Notes .....: Check an Index expression along with Create, Change
* and Delete indexes. Both .MDX Tags, and .NDX files.

CLEAR
SAVE SCREEN TO indx

*--- Setup ON PROCEDURES.
ON ERROR DO errtrap

*--- Initialize system and global variables.
sys_clock = SET("CLOCK")
sys_cursor = SET("CURSOR")
sys_talk = SET("TALK")
sys_safety = SET("SAFETY")
sys_status = SET("STATUS")

PUBLIC ARRAY la_settag[47]
lc_name = SPACE(80)
lc_express = "" && Used in Expdlbd.prg.
ln_numflds = 0 && Used in Expdlbd.prg.

*--- Set up environment.
SET cursor OFF
SET CLOCK OFF
SET TALK OFF
SET SAFETY OFF
SET STATUS OFF

*--- Define popups.
DEFINE POPUP indxmain FROM 9,15 TO 13,34
DEFINE BAR 1 OF indxmain PROMPT "Select Database"
DEFINE BAR 2 OF indxmain PROMPT "MDX Maintenance" MESSAGE;
"Add/Change/Delete a Production Index Tag"
DEFINE BAR 3 OF indxmain PROMPT "NDX Maintenance" MESSAGE;
"Add/Change/Delete an Index"
ON SELECTION POPUP indxmain DO indxsel

DEFINE POPUP dowhat FROM 13,20 TO 18,39 MESSAGE "Press to exit"
DEFINE BAR 1 OF dowhat PROMPT "Add an Index"
DEFINE BAR 2 OF dowhat PROMPT "Change an Index"
DEFINE BAR 3 OF dowhat PROMPT "Delete an Index"
DEFINE BAR 4 OF dowhat PROMPT "Display Index Key"
ON SELECTION POPUP dowhat DO dowhat

DEFINE POPUP dbflist FROM 5,45 TO 17,58 PROMPT FILES LIKE *.dbf
ON SELECTION POPUP dbflist DEACTIVATE POPUP

DEFINE POPUP ndxlist FROM 5,45 TO 17,58 PROMPT FILES LIKE *.ndx
ON SELECTION POPUP ndxlist DEACTIVATE POPUP

*--- This popup is used in different programs with different actions to be taken.
* Because of this, the ON SELECTION is defined within the different programs.
DEFINE POPUP pickfld FROM 3,5 TO 16,16 PROMPT STRUCTURE MESSAGE;
"Press to exit"

*--- DEFINE WINDOWS
DEFINE window wind1 FROM 8,10 TO 16,70 DOUBLE

*--- Draw shadow and box for database in USE display.
DO shadowng with 17,15,19,60
@ 17,15 TO 19,60 DOUBLE
@ 18,16 CLEAR TO 18,59
@ 18,16 FILL TO 18,59 COLOR /B
@ 18,17 SAY "Database in use: "
@ 18,34 SAY iif(len(TRIM(dbf())) = 0, "None", dbf()) COLOR w+/B

* Draw Shadow for the main menu and ACTIVATE the main menu
DO shadowng with 9,15,13,34
ACTIVATE POPUP indxmain

*--- When ESCape is pressed and the popup indxmain is deactivated,control will return to here
* and restore the original screen, reset the system, release all used memory variables and
* close the databases and indexes.
RESTORE SCREEN FROM indx
RELEASE SCREEN indx
SET CLOCK &sys_clock
SET cursor &sys_cursor
SET TALK &sys_talk
SET SAFETY &sys_safety
SET STATUS &sys_status
ON ERROR
RELEASE ALL
CLEAR ALL
RETURN


**** PROCEDURES AND FUNCTIONS ***
PROCEDURE indxsel
*--- Process the selection from the main Indx menu.
IF BAR() = 1
SAVE SCREEN TO indxsel
DO shadowng with 5,45,17,58
ACTIVATE POPUP dbflist
RESTORE SCREEN FROM indxsel
IF len(TRIM(PROMPT())) > 0
use TRIM(PROMPT())
ENDIF
@ 18,34 CLEAR TO 18,59
@ 18,34 FILL TO 18,59 COLOR /B
@ 18,34 SAY iif(len(TRIM(dbf())) = 0, "None", dbf()) COLOR w+/B
ELSE
*--- The UDF NODBF will determine if a database is currently in
* use, and if not, return back to the main menu.
IF .not. nodbf()
RETURN
ENDIF
SAVE SCREEN TO indxsel

lc_which = PROMPT() && Used to determine if the user is using NDXs or MDXs.
DO shadowng with 13,20,18,39
ACTIVATE POPUP dowhat
RESTORE SCREEN FROM indxsel
ENDIF
RETURN


PROCEDURE dowhat
*--- Process the selection from the Dowhat menu.
SAVE SCREEN TO dowhat
lc_express = ""
lc_ans = SPACE(3)
ln_numflds = 0
ln_key = 0
ln_tag = 1
SET MESSAGE TO ""
@ 24,0 CLEAR TO 24,79
DO CASE
CASE BAR() = 1
DO expbuild
DO indxcrea

CASE BAR() = 2
IF "NDX" $ lc_which
SAVE SCREEN TO bar2
DO shadowng with 5,45,17,58
ACTIVATE POPUP ndxlist
IF len(TRIM(PROMPT())) > 0
SET INDEX TO PROMPT()

*--- For NDXs, the user can enter a full path along with a file name, so
* this will give the user up to 80 characters for a path and file name.
lc_name = PROMPT() + replicate(" ", 80 - len(TRIM(lc_name)))
ENDIF
ELSE
SAVE SCREEN TO bar2
DO settag

*--- For MDX tags, the maximum length of the TAG name is 10 characters.
lc_name = ORDER() + replicate(" ",10 - len(TRIM(lc_name)))
IF len(TRIM(lc_name)) > 0

* KEY() requires the number of the index in order to return the key
* expression. This loop will use the array values set in Settag.PRG
* to find a match with ORDER() (ORDER() returns the name of the controlling
* index. it will then use the number of the array element with KEY() to
* return the index expression. Array elements are initialized as logical
* memory variables, so this loop will continue until it finds a match or it
* hits an array that has no index name stored to it.
DO while TYPE("ln_tag") # "L"
ln_key = iif(ORDER() = la_settag[ln_tag], ln_tag, 0)
ln_tag = ln_tag + 1
IF ln_key > 0
EXIT
ENDIF
ENDDO
ENDIF
ENDIF
IF len(TRIM(lc_name)) > 0
RESTORE SCREEN FROM bar2
SAVE SCREEN TO bar2
DO shadowng with 8,10,16,70
ACTIVATE window wind1
CLEAR
@ 0,1 SAY "The KEY for "
? PROMPT() + " is: " + KEY(iif("NDX" $ lc_which,1,ln_key));
AT 1 FUNCTION "V57"
@ 6,15 SAY "Press any key to continue" COLOR w+*/B
READ
DEACTIVATE window wind1
RESTORE SCREEN FROM bar2
RELEASE SCREEN bar2
DO expbuild
DO indxcrea
ENDIF

CASE BAR() = 3
lc_ans = SPACE(3)
DO indxshow
IF len(TRIM(lc_name)) > 0
@ 0,0 CLEAR TO 0,58
@ 6,0 CLEAR TO 6,58
@ 0,1 SAY "Delete this index?" GET lc_ans;
FUNCTION "M Yes,No";
MESSAGE "Press space bar to toggle choices"
READ
IF lc_ans = "Yes"
@ 0,0 CLEAR TO 0,58
@ 0,5 SAY "Deleting " + PROMPT() COLOR w+*/B
IF "NDX" $ lc_which
SET INDEX TO
ERASE PROMPT()
ELSE
SET ORDER TO
DELETE TAG PROMPT()
ENDIF
ENDIF
ENDIF

CASE BAR() = 4
DO while .t.
DO indxshow
DEACTIVATE window wind1
RESTORE SCREEN FROM dowhat
IF lastkey() = 27
EXIT
ENDIF
ENDDO
ENDCASE

* If there is still an index open, close it.
IF len(TRIM(ORDER())) > 0
IF "NDX" $ lc_which
SET INDEX TO
ELSE
SET ORDER TO
ENDIF
ENDIF
DEACTIVATE window wind1
RESTORE SCREEN FROM dowhat
RELEASE SCREEN dowhat
RETURN


PROCEDURE indxshow
* This will set the index and show the index key.

ln_tag = 1
ln_key = 0
SAVE SCREEN TO indxshow
IF "NDX" $ lc_which
DO shadowng with 5,45,17,58
ACTIVATE POPUP ndxlist
ELSE
DO settag
lc_name = ORDER() + replicate(" ",10 - len(TRIM(lc_name)))
ENDIF
IF len(TRIM(PROMPT())) > 0
IF "NDX" $ lc_which
SET INDEX TO PROMPT()
lc_name = PROMPT() + replicate(" ",80 - len(TRIM(lc_name)))
ELSE
IF len(TRIM(lc_name)) > 0
DO while TYPE("ln_tag") # "L"
ln_key = iif(ORDER() = la_settag[ln_tag], ln_tag, 0)
ln_tag = ln_tag + 1
IF ln_key > 0
EXIT

ENDIF
ENDDO
ENDIF
ENDIF
RESTORE SCREEN FROM indxshow
SAVE SCREEN TO indxshow
DO shadowng with 8,10,16,70
ACTIVATE window wind1
CLEAR
@ 0,1 SAY "The KEY for "
? PROMPT() + " is: " + KEY(iif("NDX" $ lc_which,1,ln_key));
AT 1 FUNCTION "V57"
@ 6,15 SAY "Press any key to continue" COLOR w+*/B
READ
ENDIF
RETURN


PROCEDURE indxcrea
* Prompt the user with different index choices and create/redo the index.

IF len(TRIM(lc_express)) > 0
DO shadowng with 8,10,16,70
ACTIVATE window wind1
lc_ans = "Yes"
@ 1,1 SAY "Do you want this index to include DUPLICATE Entries?";
GET lc_ans FUNCTION "M Yes,No";
MESSAGE "Press space bar to toggle choices"
READ
IF lc_ans = "No"
lc_express = lc_express + " UNIQUE"
ENDIF
IF "MDX" $ lc_which
CLEAR
lc_ans = "No "
@ 1,1 SAY "Do You want this index to be in DESCENDING Order?";
GET lc_ans FUNCTION "M Yes,No";
MESSAGE "Press space bar to toggle choices"
READ
IF lc_ans = "Yes"
lc_express = lc_express + " DESCENDING"
ENDIF
CLEAR
m_cond = SPACE(80)
@ 0,1 SAY ;
"Enter the condition for a Conditional Index (Press "
@ 1,1 SAY "for no condition):" GET m_cond FUNCTION "S38" MESSAGE;
"Press for a fields picklist"
ON KEY LABEL shift-f1 DO pickfld
SET cursor ON
READ
SET cursor OFF
ON KEY LABEL shift-f1
m_cond = TRIM(m_cond)
ENDIF
CLEAR
lc_ans = "Yes"
@ 0,1 SAY "Create an index using the expression below?" GET lc_ans;
FUNCTION "M Yes,No";
MESSAGE "Press space bar to toggle choices"
@ 1,1 SAY ""
IF "NDX" $ lc_which
? lc_express AT 1 FUNCTION "V57"
ELSE
? lc_express + iif(len(m_cond) > 0, " FOR " + m_cond, "");
AT 1 FUNCTION "V57"
ENDIF
READ
IF lc_ans = "Yes"
lc_name = lc_name + SPACE(iif("NDX" $ lc_which,;
80 - len(TRIM(lc_name)),10 - len(TRIM(lc_name))))
@ 0,0 CLEAR TO 0,58
@ 0,1 SAY "Enter the index name:" GET lc_name FUNCTION "S35";
MESSAGE iif("NDX" $ lc_which,;
"Enter The drive and full path with the filename","")
SET cursor ON
READ
SET cursor OFF
lc_name = TRIM(lc_name)
@ 0,0 CLEAR TO 0,58
@ 0,10 SAY "Creating Index ..." COLOR w+*/B
IF "NDX" $ lc_which
INDEX ON &lc_express TO lc_name + ;
iif(".NDX" $ lc_name,"",".NDX")
ELSE
IF len(m_cond) = 0
INDEX ON &lc_express TAG &lc_name
ELSE
INDEX ON &lc_express TAG &lc_name FOR &m_cond
ENDIF
ENDIF
ENDIF
ENDIF
lc_name = ""
RETURN


PROCEDURE pickfld
* Define a field picklist for selecting a FOR condition.

*--- Re-Define the action to take upon selecting an option from Pickfld.
ON SELECTION POPUP pickfld DEACTIVATE POPUP

SAVE SCREEN TO pickfld
ACTIVATE SCREEN
DO shadowng with 3,5,16,16
ACTIVATE POPUP pickfld
IF len(TRIM(PROMPT())) > 0
KEYBOARD TRIM(lower(PROMPT())) CLEAR
ENDIF
RESTORE SCREEN FROM pickfld
RELEASE SCREEN pickfld
ACTIVATE window wind1
RETURN
PROCEDURE settag
* Define a popup menu using the tags of a production index and display
* the expression as a message.

SAVE SCREEN TO settag
DEFINE window xtag FROM 4,5 TO 11,45
DEFINE POPUP x_tag FROM 4,15 TO 12,27 MESSAGE "Press to exit"
ON SELECTION POPUP x_tag DO x_select
DECLARE la_settag[47]
DO shadowng with 4,5,11,45
ACTIVATE window xtag

*--- Start main loop that will continue as long as the first element of
* x_tag array is a logical field.
DO while TYPE("la_settag[1]") = "L"
ln_cnt = 1

*--- If the Tagchk UDF (below) returns a value of FALSE, RETURN back to
* PROCEDURE Dowhat and allow the user to make another selection.
IF .not. tagchk()
RELEASE window xtag
RESTORE SCREEN FROM settag
RELEASE SCREEN settag
RELEASE POPUP x_tag
RETURN TO dowhat
ENDIF
CLEAR
@ 2,5 SAY "Creating Index Menu ..." COLOR w+*/B

*--- Loop to define bar(s) of the popup menu.
DO while len(TRIM(TAG(ln_cnt))) > 0
la_settag[ln_cnt] = TAG(ln_cnt)
DEFINE BAR ln_cnt OF x_tag PROMPT la_settag[ln_cnt]
ln_cnt = ln_cnt + 1
ENDDO

DEACTIVATE window xtag
RESTORE SCREEN FROM settag
DO shadowng with 4,15,12,27
ACTIVATE POPUP x_tag
ENDDO

*--- Release the popup, windows, and memory variables exclusive to Settag.prg and return
* to calling program.
RELEASE window xtag
RESTORE SCREEN FROM settag
RELEASE SCREEN settag
RELEASE POPUP x_tag
RETURN


PROCEDURE x_select
* Sets the order to a MDX tag selected, or do nothing if ESCape is pressed.

IF len(TRIM(PROMPT())) > 0
SET ORDER TO PROMPT()
ENDIF
DEACTIVATE POPUP
RETURN



FUNCTION tagchk
* Determine if any tags exist for the current .MDX, if none exist, display a message
* and RETURN a value of false.

IF len(TAG(1)) = 0
CLEAR
@ 1,5 SAY "There is no Production Index" COLOR w+/B
@ 2,5 SAY "File for " + dbf(1) COLOR w+/B
@ 4,5 SAY "Press any key to continue" COLOR w+*/B
READ
ll_return = .f.
ELSE
ll_return = .t.
ENDIF
RETURN(ll_return)


PROCEDURE expbuild
* Front end menu to Build the index expression.

*--- Display wait screen
DO shadowng with 8,10,16,70
ACTIVATE window wind1
CLEAR
@ 3,17 SAY "Just a Moment Please..." COLOR w+*/B

*--- Setup a database with the data structure as records in a database to determine the
* length and number of decimals of numeric and floating point fields.
COPY STRUCTURE EXTENDED TO expbuild
SELECT 2
use expbuild
INDEX ON field_name TAG field_name
SELECT 1

*--- Re-Define the action to take upon selecting an option from Pickfld.
ON SELECTION POPUP pickfld DO build1

*--- Initialize needed memory variables, clear wait screen and redraw the screen to
* show the fields selected for the index.
lc_row = 5
lc_col = 23
DEACTIVATE window wind1
CLEAR
SAVE SCREEN TO expbuild


@ 1,23 SAY "Fields used in the Index expression"
@ 3,23 SAY "Field Name"
@ 3,36 SAY "Field Type"
@ 3,53 SAY "Field Name"
@ 3,66 SAY "Field Type"
@ 4,23 SAY replicate(CHR(196),53)

DO shadowng with 3,5,16,16
ACTIVATE POPUP pickfld

*--- Closing commands.
RESTORE SCREEN FROM expbuild
RELEASE SCREEN expbuild
SELECT 2
use
ERASE expbuild.dbf
ERASE expbuild.mdx
SELECT 1
RETURN


PROCEDURE build1
* Controls the building of the index expression.

IF TYPE(PROMPT()) = "M" && You can't index on a Memo field.
SAVE SCREEN TO build2
DO shadowng with 8,10,16,70
ACTIVATE window wind1
CLEAR
@ 1,12 SAY "You cannot index on memo field"
@ 3,14 SAY "Press any key to continue" COLOR w+*/B
READ
DEACTIVATE window wind1
RESTORE SCREEN FROM build2
RELEASE SCREEN build2
RETURN
ENDIF

lc_ans = SPACE(3)
ln_numflds = ln_numflds + 1

IF ln_numflds = 1 && Is this the first field of the expression?
IF TYPE(PROMPT()) $ "NFLD" && Is this field a Numeric, Logical or Date field?
SAVE SCREEN TO build1
DO shadowng with 8,10,16,70
ACTIVATE window wind1
@ 1,4 SAY "Is this index going to contain other fields?";
GET lc_ans;
FUNCTION "M Yes,No";
MESSAGE "Press space bar to toggle choices"
READ

*--- If this is the only field in the expression, there is no need to convert
* the numerics, dates or logical fields to character expressions.
IF lc_ans = "Yes"
DO CASE
CASE TYPE(PROMPT()) $ "NF"
* Convert the numeric into a character string by using the STR() function,
* and doing a LOOKUP() on the database of the structure to determine
* the length and number of decimals.

lc_express = "STR(" + lower(PROMPT()) + "," +;
LTRIM(STR(lookup(B->field_len,
PROMPT(), B->field_name), 3, 0)) + "," +;
LTRIM(STR(lookup(B->field_dec,;
PROMPT(),B->field_name),3,0))+ ")"

CASE TYPE(PROMPT()) = "D"
* Convert date to a character using DTOS(). DTOS() will use the
* format YYYYMMDD to give a true date sort order.
lc_express = "DTOS(" + lower(PROMPT()) + ")"

OTHERWISE
*--- Logical fields can't be combined with any fields,
*... IIF() converts .T. and .F. to "Y" and "N"
lc_express = "IIF(" + lower(PROMPT()) + ",'Y','N')"
ENDCASE
ELSE
lc_express = lower(PROMPT())
DEACTIVATE window wind1
RESTORE SCREEN FROM build1
RELEASE SCREEN build1
DEACTIVATE POPUP
ENDIF
DEACTIVATE window wind1
RESTORE SCREEN FROM build1
RELEASE SCREEN build1
ELSE
lc_express = lower(PROMPT())
ENDIF
ELSE
*--- Test for the length of the index expression to be greater than 200. The maximum
* length that the expression can be is 220, the extra 20 spaces is a buffer.
* If the expression does exceed 200, prompt the user and exit ExpBuild to the
* calling program and continue from there. The IIF()s are used for converting
* non-character fields, and are counted in the length of the expression.

IF len(lc_express) + len(PROMPT()) + (iif(TYPE(PROMPT()) $ "NF", 9,;
iif(TYPE(PROMPT()) = "D",6, ;
iif(TYPE(PROMPT()) = "L",13,0)))) > 220
SAVE SCREEN TO build2
DO shadowng with 8,10,16,70
ACTIVATE window wind1
CLEAR
@ 1,2 SAY "By adding the selected field, you will exceed the"
@ 2,2 SAY "maximum length available for the index EXPRESSION."
@ 3,2 SAY "Please select another field, or stop here."
@ 5,10 SAY "Press Enter to continue" COLOR w+*/B
DO while inkey() # 13
ENDDO
DEACTIVATE window wind1
RESTORE SCREEN FROM build2
RELEASE SCREEN build2
RETURN
ENDIF

* The length of the contents of the fields cannot exceed 100 characters, so this must
* also be checked. The memory variable m_chklen will be used to convert the field
* contents to a character variable so that LEN() will return the length of the field
* and can then be added to the total field length of the current expression.
m_chklen = iif(TYPE(PROMPT()) $ "NF", " + STR(" + PROMPT() ;
+ "," + LTRIM(STR(lookup(B->field_len, PROMPT(), B->field_name),;
3, 0)) + "," + LTRIM(STR(lookup(B->field_dec, PROMPT(), ;
B->field_name), 3, 0)) + ")",;
iif(TYPE(PROMPT()) = "L", "+ IIF(" +; PROMPT() + ",'Y','N')",;
iif(TYPE(PROMPT()) = "D","+DTOS(" + PROMPT() + ")","+" +;
PROMPT())))
IF len(&lc_express) + len(&m_chklen) > 100
SAVE SCREEN TO build2
DO shadowng with 8,10,16,70
ACTIVATE window wind1
CLEAR
@ 1,2 SAY "By adding the selected field, you will exceed the"
@ 2,2 SAY "maximum length available for index KEY. Please"
@ 3,2 SAY "select another field, or stop here."
@ 5,10 SAY "Press Enter to continue" COLOR w+*/B
DO while inkey() # 13
ENDDO
DEACTIVATE window wind1
RESTORE SCREEN FROM build2
RELEASE SCREEN build2
RETURN
ENDIF

*--- This expression will test the type of each field, and if necessary add the
* appropriate conversion necessary to convert the field contents to a character
* expression so it can be combined with other character fields.
lc_express = lc_express + iif(TYPE(lower(PROMPT())) $ "NF", +;
"+STR(" lower(PROMPT()) + "," + LTRIM(STR(lookup(B->field_len, PROMPT(), B->field_name), 3, 0)) + "," + ;
LTRIM(STR(lookup(B->field_dec, PROMPT(), B->field_name),;
3, 0)) + ")", iif(TYPE(lower(PROMPT())) = "L",;
"+IIF(" + lower(PROMPT()) + ",'Y','N')",;
iif(TYPE(lower(PROMPT())) = "D", "+DTOS(" + lower(PROMPT()) + ;
")","+" + lower(PROMPT()))))
ENDIF

*--- Display the field name and the type of field on the screen
@ lc_row,lc_col SAY PROMPT()
@ lc_row,lc_col + 13 SAY iif(TYPE(PROMPT()) = "C","Character",;
iif(TYPE(PROMPT()) = "D","Date",iif(TYPE(PROMPT()) = "F",;
"Float (Numeric)",iif(TYPE(PROMPT()) = "L","Logical",;
"Regular Numeric"))))
lc_col = iif(lc_row = 22,53,lc_col)
lc_row = iif(lc_row = 22,5,lc_row + 1)
RETURN


PROCEDURE shadowng
* Draw Zooming Shadow for Popups and windows.

PARAMETER ln_x1,ln_y1,ln_x2,ln_y2

ln_x0 = ln_x2+1
ln_y0 = ln_y2+2
ln_dx = 1
ln_dy = (ln_y2-ln_y1) / (ln_x2-ln_x1)
DO while ln_x0 <> ln_x1 .or. ln_y0 <> ln_y1+2
@ ln_x0,ln_y0 FILL TO ln_x2+1,ln_y2+2 COLOR n+/n
ln_x0 = iif(ln_x0<>ln_x1,ln_x0 - ln_dx,ln_x0)
ln_y0 = iif(ln_y0<>ln_y1+2,ln_y0 - ln_dy,ln_y0)
ln_y0 = iif(ln_y0ENDDO

RETURN


FUNCTION nodbf
* Check for a database file in use.
IF len(TRIM(dbf())) = 0
SAVE SCREEN TO nodbf
DO shadowng with 8,10,16,70
ACTIVATE window wind1
CLEAR
@ 1,4 SAY "There is no database in USE. Use Select Database" ;
COLOR w+/B
@ 2,4 SAY "from the main menu to put a database in USE." COLOR w+/B
@ 4,14 SAY "Press any key to continue" COLOR w+*/B
READ
DEACTIVATE window wind1
RESTORE SCREEN FROM nodbf
RELEASE SCREEN nodbf
ll_return = .f.
ELSE
ll_return = .t.
ENDIF

RETURN(ll_return)

PROCEDURE errtrap
* Trap for dBASE errors.

ln_error = ERROR()
lc_message = MESSAGE()
DEFINE window m_err FROM 8,10 TO 16,70 DOUBLE

SAVE SCREEN TO errtrap
ACTIVATE SCREEN
DO shadowng with 8,10,16,70
ACTIVATE window m_err
CLEAR
DO CASE

CASE ln_error = 12
* Error "Variable Not Found". The only reason you should get this error message is
* if you select .an .NDX and it does not belong with the database currently in USE.
@ 1,5 SAY "Index does not match database" COLOR w+/B
OTHERWISE
@ 1,5 SAY "ERROR: " + LTRIM(STR(ln_error))
@ 2,5 SAY lc_message
ENDCASE
@ 4,10 SAY "Press any key to continue" COLOR w+*/B
READ
DEACTIVATE window m_err
RESTORE SCREEN FROM errtrap
RELEASE SCREEN errtrap
RETURN
* EoF: Indx.prg



2 Dialogue September 1990 dBASE IV

Dialogue
Questions and Answers

Suppressing DOS messages

Q: I have a program wherein I run a DOS program from an active
window. When completed, the DOS program returns a message which I
would prefer to suppress. In addition, dBASE IV also supplies a Press
any key to continue message which would also be nice to eliminate.
How can I do this?

A: The message from your DOS program can be suppressed with the >>nul
redirection parameter.

For example, when you use the DOS COPY command and a file is
successfully copied, you receive the message

1 File(s) copied

To suppress this message, your copy command would like like:

COPY Thisfile.txt Thatfile.txt>>nul

The problem of suppressing the message that dBASE IV generates after a
return from a DOS call is a little more involved. Try this function:

FUNCTION DosRun
PARAMETER lc_cmd

lc_window = WINDOW()
IF LEN(TRIM(lc_window)) > 0
DEACTIVATE WINDOW &lc_window
ENDIF
RUN &lc_cmd
IF LEN(TRIM(lc_window)) > 0
ACTIVATE WINDOW &lc_window
ENDIF
RETURN ""

This UDF will save the name of a previous window if there is one
active. It will deactivate it, run the DOS command then reactivate
your window. The addition of a null string in the RETURN statement is
to suppress the display of a .T. when the UDF is completed.

Format Files: A Million and One Uses

Q: Is there any way to limit the width of a calculated numeric field
when in BROWSE? The WIDTH qualifier does not seem to work for me, and
when I use /10 my calculated field is divided by 10.

A: Yes, there is. Create a custom screen and use Quick Layout.
Exclude any fields that you do not wish to appear in the BROWSE
table. Establish your calculated field and use Shift-F7 to set the
width of fields. Save the file. In your program or at the dot
prompt, enter SET FORMAT TO followed by the name of the format file
you've created prior to using BROWSE FORMAT to make those picture
clauses (expressions and widths) active in the BROWSE table as well.

Bull-headed Menu

Q: I'm stumped on this one. I'm trying to suppress the menu when
editing a memo field. Although I tried resorting to the use of EDIT
with the NOMENU option (which was not my first choice), the menu still
appears and there is no apparent way of preventing it. Any ideas that
I haven't tried?

A: There is no setting to suppress this menu while editing a memo as
you've found. You can hide the menu by setting STATUS off and then
issuing ON KEY LABEL statements to suppress all the menu options
(Alt-L, Alt-R, Alt-O and so on). Although the options are suppressed
in an EDIT screen, once you switch to the memo editing screen, the
options are once again available. So, you can hide the menu display,
but the menus will still pull down when F10 or applicable Alt-key
sequences are pressed.

Your only alternative is to use a different text editor which you
would setup using the WP = setting in your Config.db file. Make sure
the word processor you choose retrieves and saves it's information in
ASCII and not some proprietary format.

You Can't Touch 'Dis

Q: In my screen form, I wish to prevent the editing of a memo. I
would prefer for the operator to view the memo information in a window
I've set up. I tried turning the Edit options: Editing allowed to NO
but then the memo only shows up as a marker.

A: The reason for this is that when Editing allowed is turned off (to
NO), the form generator only issues an @.SAY command in the resultant
.fmt file. The problem is that the OPEN WINDOW clause needed to
display your memo in a window is only available in an @.GET.
Catch-22, you say? Not at all. To achieve a displayable memo that
can't be edited, simply enter .F. in the Permit edit if option. The
cursor will always bypass the memo field but the contents will be
visible.

That's Not How You Delete

Q: On a BROWSE screen, is there anyway to prohibit record blanking?
My intent is to prevent the tendency some users have to blank out all
the fields of a record in an attempt to delete it. I don't have any
objections to normal editing of a record or marking a record for later
deletion but some users have a tendency to delete a record by removing
data from all the fields, thus leaving a blank record in the
database. Using the NOEDIT or NODELETE options wouldn't apply in my
case so now what?

A: You will have to individually assign this requirement to each of
the fields in a screen form. Since, numeric or float fields cannot be
blanked and logical fields that are blanked still evaluate to false,
it is only practical to devote your attention to the blanking of
character fields and, perhaps, date fields. To do this, choose Edit
options: Accept value when and place an expression as is shown below,
using the actual name of the field in place of the marker
. For character fields:

# ""

For date fields,

# {}

After establishing these parameters in a screen form, you would use
BROWSE with the FORMAT option to access these the field options you've
set in the screen design.


3 dBASE IV version 1.1 Change Summary September 1990 dBASE IV


This article is reprinted from the September 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.

dBASE IV Version 1.1
Change Summary

There's a number of new or modified commands and functions that can
enhance your programming palette. In applicable cases, the new syntax
is provided. If you're a menu-sytem user, you may want to skip
directly to page 13 to read about Labels, Reports and QBE.

COMMANDS

@...GET...RANGE/VALID [REQUIRED]

Syntax:

@ ,
[GET
[RANGE [REQUIRED] [] [,]]
[VALID [REQUIRED]
[ERROR ]]]

Increased functionality has been added with the REQUIRED verb. This
restores dBASE III PLUS compatibility, while retaining the
functionality of dBASE IV version 1.0.

In dBASE III PLUS if a RANGE was specified on a variable which was
initialized to a value that was outside of the range and Enter was
pressed, the value of the variable was held static and the RANGE was
not tested. In dBASE IV version 1.0, the RANGE and VALID clauses
specified were always tested, even if no change was made to the
default value.

Now in version 1.1, the additional verb REQUIRED can be appended to a
RANGE or VALID clause to force checking of the GET variable, even if
Enter is pressed. If the REQUIRED option is not specified, the
variable is tested only if a change is made to its initial value, the
way dBASE III PLUS performed its RANGE checking.

The order of the command verbs is crucial; the REQUIRED verb must
follow the verb VALID and preceed the specified VALID condition.
Similarly, you must place REQUIRED between RANGE and the specified
high and low values. Placing the verb anywhere else in the command
line will cause a Syntax error.


The ERROR option applies only to VALID clauses, not RANGEs. ON
READERROR, however, applies to both. If you have a preceding ON
READERROR statement and then use ERROR in an @.GET statement, the ON
READERROR assignment takes precedence.

BROWSE/EDIT

The ORGANIZE menu is now available in BROWSE and EDIT. In dBASE IV
version 1.0, this menu was only available when modifying the structure
of a database via MODIFY STRUCTURE. This allows the ability to set
indexes, to SORT and to create indexes from within BROWSE and EDIT.

Calculated field widths are now calculated in a new fashion.
Calculated field expressions are evaluated to determine their data
type (and maximum length if it is a character field).

If the expression contains a UDF, dBASE IV may not be able to
determine the type or length. For example, if the expression of the
calculated field is "LEFT(UDF(XYZ), 3)", dBASE IV will determine that
the calculated field has a character type and a length of 3. But if
the expression is "UDF(XYZ)", dBASE IV will not be able to determine
the type or length.

In cases where the type or length cannot be determined, the following
steps are performed:

1. The calculated field's expression is immediately evaluated
(using the current database record).

2. If the result of the calculation is of character type, the
length is set to MAX(10, ), meaning that it has a width of at
least 10. If the calculated width is larger than 10, that value is
chosen for the width of the calculated field.

3. For other type results, the following lengths are used:

LOGICAL 1
DATE 8
NUMBER 20
Once the data type and length of the calculated field have been
determined, the column size is computed as if the calculated field
were a normal field. Calculated fields can be sized to other widths,
just like regular character types.

COPY STRUCTURE

Syntax:

COPY STRUCTURE TO
[FIELDS ] [[WITH] PRODUCTION]

The COPY STRUCTURE command, which copies the structure of the
currently active .dbf (and .dbt file if one exists) to a new file, has
been enhanced with the ability to copy .mdx tags from the source
database to its copied structure file.

If the WITH PRODUCTION clause is specified and the source file is a
dBASE IV database file with a production .mdx file, all index tags,
both simple and complex, that would be valid in the new .dbf file are
copied to a new production .mdx file.

If selected fields are copied to the new .dbf, and a key expression of
the tag (including conditional expressions which utilize the FOR
clause) cannot be evaluated with these fields or currently defined
memory variables and aliases, then the tag will not be copied to the
new .mdx. It should be noted that if a tag is not copied, no message
is displayed informing the user of its omission.

If the COPY STRUCTURE command is unable to create an .mdx file for any
other reason, a prompt box will display, describing the problem (such
as Disk full). Regardless of whether the .mdx file is successfully
created, the .dbf (and .dbt if appropriate) will be created.

COPY TO

Syntax:

COPY TO
[[TYPE] ]/[[WITH] PRODUCTION]
[FIELDS ] []
[FOR ]
[WHILE ]

The COPY TO command has been enhanced with the ability to copy .mdx
tags from the source database structure to a newly created target
database and production .mdx file.

If the PRODUCTION clause is specified and the source file is a dBASE
IV database file with an associated production .mdx file, all index
tags (simple and complex) that are valid for the new .dbf file will be
copied to a new production .mdx file.

Like the COPY STRUCTURE command, the key expression of a tag,
including conditional expressions cannot be evaluated if selected
fields used in these expressions are not copied and no message is
displayed noting the omission.

In order to use the WITH PRODUCTION keywords, a free work area must be
available. If all of the work areas are in use, error message #399
displays Copying production .MDX requires one free work area.

If the COPY TO command is unable to create an .mdx file for any other
reason (such as Disk full), a prompt box will be displayed describing
the problem. Regardless of whether the .mdx file is successfully
created, the .dbf (and .dbt if appropriate) will be created.

The TYPE keyword and WITH PRODUCTION clauses are mutually exclusive.
If the COPY TO command contains both keywords, error message #36,
Unrecognized phrase/keyword in command will display.

For example, if you have three tags, where the fields Name and Street
exist, but City is the name of a memory variable that currently does
not exist: Expression

Name Name
Street UPPER(Street)
Citystreet Street+City

only the Name and Street tag will appear in the new .mdx. Both complex
and simple tags are copied but the tag that relies on nonexistent data
is left out.

dBASE III PLUS Memos

In dBASE IV version 1.0, if a dBASE III PLUS file that contained a
memo was USEd in dBASE IV, the header's first byte became altered,
flagging the file as a dBASE IV file. Subsequently, attempting to use
the file in dBASE III PLUS gave the error, Not a dBASE database. To
avoid that problem, the database had to be exported from dBASE IV
using the command

COPY TO.TYPE DBMEMO3

In dBASE IV version 1.1, a memo can be a dBASE III PLUS memo or a
dBASE IV memo; both types can exist in a single memo file. A .dbf
file will be marked as a dBASE IV file only if you edit a dBASE III
PLUS memo. This means that you can use a dBASE III PLUS file in dBASE
IV and even append records without changing the file to a dBASE IV
file. The newly added memos will be dBASE IV memos and will appear to
be corrupted if viewed in dBASE III PLUS, so it's a good idea to use
the COPY TO.TYPE DBMEMO3 command if you're planning on sharing this
dBASE IV file with dBASE III PLUS.

If you use an external editor, dBASE IV has no means of determining
whether or not data has been changed in the editor. Consequently, the
record will be saved as if it were changed and the .dbf file will be
marked as a dBASE IV file.

DEBUG

In dBASE IV version 1.0, to exit temporarily (suspend) during a Debug
session, you pressed X. To quit the Debug session you pressed Q. Now
in version 1.1 you still press Q to quit, but to exit to the dot
prompt temporarily you must press U (sUspend). To continue back in
the debug session, you should type RESUME from the dot prompt. If you
do not wish to continue the session, type CANCEL at the dot prompt.

INDEX ON

Syntax:

INDEX ON
TO /TAG
[OF ] [FOR ]
[UNIQUE] [DESCENDING]

Using the new FOR clause you can index only those records that meet a
particular condition. This new option is very similar to the SET
FILTER command but can offer a quicker and more preferable access to
selected records of a large database.

TAGs using a FOR condition take space and time to initially create.
Once created however, they can offer huge savings in time when
accessing records. Consider these points:

SET FILTER will look at each record to see if it meets the
selection criterion. Accessing a large file where few records match
the SET FILTER condition can result in substantial wait time while
dBASE IV is skipping past all the non-matching records. Using a FOR
index, dBASE IV does not have to check each record when skipping
through a database, it just goes to the next record in the index.

The FOR clause can only be used with .mdx tags, not with dBASE III
PLUS compatible .ndx files.

The index file space used depends upon how many records meet the
FOR condition, not how many records are in the database.

If the index expression is a character type, dBASE IV tries to
figure out the maximum length by parsing the elements of the
expression. If a fixed length cannot be determined, the index key
length will be set to 10 characters. For example, if the index key
expression is MDY() or DMY(), the index key length will default to 10
because these functions return a variable length character string and
dBASE IV can not determine a maximum length. In this particular
example the file may not be indexed as expected because only the first
10 characters of the character string are included in the index. For
example, if MDY() was used, "February 03, 1988" could appear before
"February 01, 1987". Because of this intelligent length checking,
users may encounter trouble when using SEEK or FIND operations and may
even assume corruption problems.

There are situations where dBASE IV can figure out a maximum
length of an expression. For example, if the index key expression is
TRIM(LASTNAME) + FIRSTNAME, the index key length will be the sum of
the field length of LASTNAME (untrimmed) and FIRSTNAME. Another
example is if the index key expression is LEFT(MDY(datefield),20), the
index key length will be 20 because the LEFT() function specifies a
length.

KEYBOARD

Syntax:

KEYBOARD [CLEAR]

KEYBOARD places a string into the typeahead buffer. These characters
are then read by subsequent commands in the program and acted upon
just as if the operator typed them at the keyboard. This allows
keyboard simulation without the use of macros.

The parameter is any valid character expression. The CHR()
function may be used to stuff any keystroke (including Ctrl keys, Esc,
and Home) that returns a valid INKEY() value.

You can insert all ASCII characters that are returned by the CHR()
function for values from 1 to 255. If you use the KEYBOARD command to
enter a null string or a string of length 0, the command will execute
and return without error, but nothing will be placed into the
typeahead buffer.

You cannot insert Alt keystroke combinations (like Alt-C) or function
keys (except F1) with the KEYBOARD command since all of these return
negative INKEY() values.

Note that although KEYBOARD will not play Alt key combinations, you
may enter ASCII special characters using the Alt key, rather than
entering them via the CHR() function. This is done by holding down
the Alt key and pressing the numeric value desired on the numeric
keypad. The exact ASCII symbol will display on the screen. This
symbol must be enclosed in quotes.

Invalid character expressions () result in the error message,
Not a valid character expression. Note that as soon as an invalid
is detected, the command fails and the typeahead buffer is NOT
cleared.

CLEAR is an optional keyword that, if included, clears the typeahead
buffer before any characters from the KEYBOARD command are read into
it. If the CLEAR option is not used, the buffer is not cleared
automatically.

This, for example, may be done to ensure that if a user has been
typing while the command immediately preceding the KEYBOARD command is
executing (for instance while an INDEX command is executing) the
user's input to the typeahead buffer will be flushed before the
KEYBOARD input is written to the buffer.

KEYBOARD respects the size of the typeahead buffer set with SET
TYPEAHEAD. This means that if TYPEAHEAD has been set to 3 and a
GET/READ is active, if you attempt to KEYBOARD in 20 characters, only
3 will be entered, and the rest will be lost.

MODIFY COMMAND/FILE

MODIFY COMMAND/FILE now allows parameters to be passed to external
editors defined by TEDIT= in Config.db. This allows the use of
wildcard characters, such as an asterisk (*) or question mark (?) in
the passed parameter.

For example, assume you need to edit a series of .prg files that exist
in the current directory and the external editor accepts an asterisk
(*) wildcard character for filenames. The following command could be
issued:

MODIFY COMMAND *.PRG

Error messages elicited from invalid parameters are displayed at the
discretion of the external editor. dBASE IV will, however, pause so
that the user can see the error message.

If a file extension is not specified, the default extension will be
.prg for MODFIY COMMAND and .txt for MODIFY FILE.

MODIFY STRUCTURE

The MODIFY STRUCTURE command has been enhanced so that backup files
are once again created when the file structure has been modified. All
backup files are created in the same directory as the original, parent
file. Their file extensions will be changed as follows:

Default Backup
Extension Extension

Modifying a database file .dbf .dbk
which contains a memo file .dbt .tbk
and a production MDX file .mdx .mbk

MODIFY STRUCTURE will not create a backup file if the database itself
has a .dbk (or .tbk) extension to begin with. The status of SET
SAFETY is ignored when you MODIFY STRUCTURE, the backup files are
always created. Changing only the index fields (creating or
deleting tags in the production index file) will not necessitate the
creation of a backup file. It goes without saying that the creation
of an .ndx file has no effect on backing up the file. However, if the
actual file structure is modified, backups of all applicable files
will occur automatically.

ON SELECTION POPUP

Syntax:

ON SELECTION POPUP /ALL [BLANK]
[]

The new keyword BLANK will cause a popup to temporarily be blanked
from the screen when a selection is made.

Normally, when a selection has been made from a popup, command passes
to the program or command specified by . In this case the
popup remains active and is still displayed on the screen. If you
desire to display something else on the screen, you must cover the
popup with a window or some other type of full-screen editing device.
The BLANK option allows you to clear the popup from the screen after a
selection has been made. Once the selection routine or command has
been completed, the popup will re-display and become active again.

The verb order for ON SELECTION requires that the word BLANK come
after the popup name and before the command action clause.

RELEASE SCREENS

Syntax:

RELEASE SCREENS []

RELEASE SCREENS is a new option of the RELEASE command which delete
screen variables from memory. RELEASE is also used with the
appropriate keyword to remove LOADed assembly language programs,
menus, popups, screens and windows from memory and to remove memory
variables from memory.

The new RELEASE SCREENS option clears memory of screen images. If a
list of names is specified, only the specified screen images are
cleared. If no list is specified, all screen images are cleared from
memory. The only other command that can be used to clear memory of
screen images is CLEAR ALL.

The maximum number of screens that can be specified with list> is limited only by the amount of physical and virtual memory
available.

If a specified does not exist, the name is ignored, no
error occurs and the processing of the RELEASE SCREENS command
continues.

A can be used as a elsewhere without
conflict to the .

REPLACE FROM ARRAY

Syntax:

REPLACE FROM ARRAY
[FIELDS ]
[] [FOR ]
[WHILE ]

The new command REPLACE FROM ARRAY works analogously to the dBASE IV
version 1.0 command APPEND FROM ARRAY except that REPLACE FROM ARRAY
replaces values in the current record or in the set of records
specified by the scope, FOR or WHILE conditions.

If the source array is a single dimensioned array, (that is, A1[6]
which is an array of 6 columns) for each record to be REPLACEd in the
database (as determined by the scope, FOR or WHILE conditions) the
value of the first element or column in the array replaces the value
of the first field in the database record. The second element is
copied to the second field, and so on, until there are no more array
elements or no more fields to process. The same array element is used
as the basis of the REPLACE to all records in the file that match the
scope, FOR/WHILE condition. The REPLACE process continues until there
are no more records that match the scope.

If a SET FIELDS list is active, the first array element REPLACEs the
first field in the FIELDS list, and so on. If the data type of an
array element does not match the data type of the corresponding field
in the file, the error message Data type mismatch will be returned,
the REPLACE will terminate, but any fields REPLACEd up to that point
will not be backed out!

The case of two dimensional arrays is slightly more complicated. A
two dimensional array has a row dimension specified (for example,
Two_dim[5,30], made up of 5 rows or records, each of 30 columns or
fields). The array elements are matched to the database fields (or
FIELDS list) as described above for single dimensioned arrays.
However, array elements from the first row of the array are copied to
the first record of the file that matches the scope, FOR/WHILE
condition. Elements of the second array row REPLACE values in the
second record, and so on. The process continues until either there
are no more array rows, or no more applicable records in the file.

Memo fields cannot be changed by the REPLACE FROM ARRAY command, as a
memo cannot be stored to an array element.

RESTORE SCREEN FROM

Syntax:

RESTORE SCREEN FROM

RESTORE SCREEN FROM restores the screen image from the memory location
specified by . The old screen image is overwritten by
the new image.

Screen images can be restored repeatedly from a single saved image
during a dBASE IV session. When used in conjunction with SAVE SCREEN
TO, RESTORE SCREEN FROM allows recovery of a previously saved
full-screen display.

If the screen image specified by does not exist, the
error message Screen does not exist: is displayed.

Note that the cursor position is neither saved nor restored using the
SAVE/RESTORE SCREEN combination of commands. The cursor remains at
the location it was immediately before the RESTORE SCREEN command was
executed.

The screen images which are restored with RESTORE SCREEN reside only
in virtual memory, SAVE does not decrement the available memory from
that returned by DISPLAY MEMORY or MEMORY().

The same can be used as a elsewhere
without conflict to the .

SAVE SCREEN TO

Syntax:

SAVE SCREEN TO

SAVE SCREEN saves the current screen contents in virtual memory for
use during a dBASE IV session. When used in conjunction with the
command RESTORE SCREEN, SAVE SCREEN allows recovery of a previous
full-screen display.

Only the currently visible contents of the screen at the time that
SAVE SCREEN is executed will be saved. If the user has an active
menu, window, or is in the menu system, anything behind the window or
the menu will not be saved.

A subsequent SAVE SCREEN TO command to a variable of the same name
causes the last SAVE SCREEN image to replace the earlier one without
warning.

SAVE SCREEN produces no visible output. The screen image is saved to
a memory location using the specified.

NOTE: The cursor position is neither saved nor restored using the
SAVE/RESTORE SCREEN combination of commands.

SET COMMANDS

SET CURSOR

Syntax:

SET CURSOR ON/off

The new SET CURSOR command allows the user to control whether the
cursor is displayed. Previously, this could only be accomplished by
using LOAD and CALL with .bin files.

If you use the RUN command (or otherwise access the operating system,
or an external application, via CALL for example), the cursor is
returned to the state it was in prior to invoking dBASE IV. When you
return to dBASE IV, the cursor is restored to its dBASE IV setting.

SET CURSOR is respected throughout the product, including full screen
editing operations, the Control Center and Applications Generator.

SET DIRECTORY

Syntax:

SET DIRECTORY TO [[][]]

SET DIRECTORY TO is a new command that can be used in place of the SET
DEFAULT TO command and RUN CD (CHDIR). Use SET DIRECTORY TO to set
both the operating system default drive and directory. The advantage
of SET DIRECTORY TO is that by issuing this command, dBASE IV is kept
aware of the current operating system default directory.

Issuing SET DIRECTORY TO is preferred over SET DEFAULT TO because SET
DEFAULT does not actually change the operating system working drive,
while SET DIRECTORY TO does. Further, issuing RUN CD will not update
dBASE IV itself and dBASE IV will not be aware that the DOS default
directory has been changed.

When you issue SET DIRECTORY TO and specify only a drive designation,
dBASE IV only sets the drive, leaving the currently active directory
on that drive as that default directory. Similarly, if only a path is
specified, dBASE IVsets that path as active on the currently active
drive. When you issue SET DIRECTORY TO with no parameters, or when
you quit dBASE IV, the startup drive and directory are reset.

DISPLAY STATUS now shows the default working drive and directory, as
set by SET DIRECTORY and designated as "OS working drive/directory
C:\DBASE". In full screen SET, there is a new entry titled OS Working
drive/directory which can be used to set the DIRECTORY in the same
manner as the SET DIRECTORY command.

Whereas the SET DIRECTORY command will reset the current SET DEFAULT
drive, issuing SET DEFAULT will not reset the OS Working
drive/directory. Once a SET DIRECTORY command has been issued,
subsequent SET DEFAULT commands will be ignored.

SET MESSAGE TO

Syntax:

SET MESSAGE TO
[ [AT [, ]]]

SET MESSAGE TO displays a user-defined character string on the screen.

When SET STATUS is ON, the message is centered on row 23 of the
screen. When SET STATUS is OFF, message is either centered on row 23
or at the location designated by the coordinates of the new AT option.


The AT option displays the user-defined message on the specified row
and column during READ/EDIT/POPUP/MENU operations. The message text
will be centered if a single coordinate is specified as part of the AT
clause. If the second (column) coordinate is specified, the message
text will begin at the column corresponding to the second . If
the AT option is used, it MUST be preceded by a message string of some
sort.

If a SET MESSAGE TO is issued with AT coordinates, and STATUS is ON,
then the AT clause is disregarded and the message is centered at row
23 of the screen.

If a SET MESSAGE TO is issued with AT coordinates, and STATUS is OFF,
the user-defined message is displayed at the designated coordinates
for READ/EDIT/POPUP/MENU operations. When these operations are
deactivated, the message is cleared.

Messages display across the full screen, not inside windows. Therefore
the screen row (and column) specified by SET MESSAGE TO is
always absolute, relative to the full screen. If windows are present,
it is the programmer's responsibility to avoid any overlaps that may
result when a message line overwrites a window.

In ASSIST and other full-screen operations, user-defined messages are
overridden by dBASE IV messages. Messages from @...GET commands,
popups, and menus override the message from SET MESSAGE TO.

If an application is using the status bar and message line, avoid
specifying any line below 21 in your format file. When SET STATUS is
ON, you can use SET MESSAGE TO to convey useful information on line
23.

SET MESSAGE TO is not directly supported in Config.db (although the
COMMAND = option can be used). SET MESSAGE TO with no string
parameter resets the message to the default.

SET PRINTER TO

Syntax:

SET PRINTER TO

=

NUL && NUL device - no action
LPT1 && first parallel printer port
LPT2 && second parallel printer port
LPT3 && third parallel printer port
COM1 && first serial printer port
COM2 && second serial printer port
COM3 && third serial printer port
COM4 && fourth serial printer port

Three new devices are supported in dBASE IV version 1.1: NUL, COM3,
and COM4

The purpose of SET PRINTER TO NUL is to provide a nonexistent (dummy)
device to be used to test applications. As an output device, write
operations are simulated, but no data is actually printed.

FUNCTIONS

COUNTC()

Syntax:

COUNTC()

The COUNTC() function, which is found in the Template Language,
returns the current index of a cursor into a specific object, element,
or attribute within a collection of the same type. A cursor holds a
numeric value with respect to the position of the specific member
within the collection.

This function returns a number which represents the current relative
position of a cursor. is the name of the pointer variable to
examine. A return value of 0 indicates that the cursor was invalid or
uninitialized, or referenced an empty collection.

This function was available in the dBASE IV Developer's Edition
version 1.0, however it was not documented in Template Language.

CERROR()

The CERROR() function returns the error number of the last
compile-time error message reported during compilation. The error
number returned is a one to three digit numeric value. The CERROR()
function has no arguments.

The CERROR() function has a default value of zero. CERROR() can
return a non-zero value only when the function is used within the
programming environment; CERROR() queried at the dot prompt always
returns a value of zero.

If a compilation is executed without errors or if compilation is
unsuccessfully completed (for example, File not found), the value is
set to zero. CERROR() is updated each time the compiler is invoked,
therefore, when multiple compiles are performed, the value returned by
CERROR() reflects the status of the last compile. The compiler is
invoked by the following:

the COMPILE command

the DO command when no .dbo file exists, or when SET DEVELOPMENT
is ON and the .dbo file is outdated

a user-defined function (UDF) is referenced in an expression and
the UDF has not been previously compiled

compilation of any non-procedure (.prg or .prs) files, i.e. files
with the following file extensions: .fmt, .lbg, .upd, and .qbe.

CERROR() can be used in UDFs and in SQL statements and .prs files.
The behavior in SQL and .prs files is identical to standard dBASE
IV.

The following program segment uses CERROR() in a DO WHILE loop to
force the user to edit the program until it successfully compiles.

DO WHILE .T.
CLEAR
MODIFY COMMAND foo.prg
ON ERROR ? ERROR(), MESSAGE()
COMPILE foo.prg
ON ERROR
IF CERROR() > 0
?
WAIT "Unsuccessful compile. " ;
+ "Press any key."
LOOP
ENDIF
EXIT
ENDDO

DO foo

CHANGE()

Syntax:

CHANGE()

The CHANGE() function will now accept an alias parameter. This
modification makes for more efficient programming in a network
environment. When several files are open in a program, using
CHANGE() with the alias of a selected file allows you determine the
change status of a CONVERTed file without explicitly SELECTing that
work area.

INKEY()

Syntax:

INKEY([expN])

The INKEY() function, which returns an integer representing the key
pressed by the operator, has been enhanced to accept decimal numbers.
The optional argument , originally added in dBASE IV version 1.0
to allow delay of program execution for n seconds, now accepts decimal
values. This allows the program to be delayed for fractions of a
second.

LKSYS()

Syntax:

LKSYS()

n = 0 returns the time when the lock was placed.
n = 1 returns the date when the lock was placed.
n = 2 returns the log-in name of the user who locked the record.
n = 3 returns the time of the last update or lock.
n = 4 returns the date of the last update or lock.
n = 5 returns the log-in name of the user who last updated or locked
the record or file.

Three additional parameter values can be tested using LKSYS() on a
converted file in dBASE IV version 1.1. These values, 3, 4 and 5, can
be tested against any record, regardless of the record/file lock
status at the time that LKSYS() is tested. Arguments 0, 1 and 2 will
return values only after an unsuccessful record or file lock. At the
time of the failed lock, argument 2 returns the log-in name of the
user who is currently locking the record.

When a record is locked, the _dbaselock field of a CONVERTed file is
updated with the date and time of the lock and user name of the person
who issued the lock. A previously locked record will return values
for arguments 3, 4 and 5. When a file is locked, the information
about who performed the lock, and when, is stored in the first
physical record of the file.

For the first three arguments, after a failed lock attempt,
information from the _dbaselock field of the locked record is copied
to a buffer in the memory of the workstation. This provides a
snapshot of the date and time of the lock, and the user name of the
person holding the lock. This buffer is not automatically cleared,
but is rewritten when another failed lock attempt occurs. Therefore,
most frequently, arguments 0,1 and 2 would not be expected to match
values returned for 3,4 and 5 for any particular record. The buffer
will either be blank if no lock attempts were made, or contain
information from some past lock attempt.

Testing arguments 3, 4 and 5 against a locked record will not indicate
that the record is currently locked since the lock may have been
removed by the time that LKSYS() was tested. Also, testing arguments
3, 4 and 5 after a failed lock attempt (via RLOCK()) will not verify
the exact name of the user who foiled the lock attempt, because after
the RLOCK(), a different user may have captured the record.

In a more complicated example, if RLOCK() is used to lock several
records in a file and one or more of the desired records are locked,
LKSYS(5) will return the name of the person holding one (or more) of
the desired records. LKSYS() generally only displays the contents of
the _dbaselock field of the current record. However, if multiple
record locks are attempted and one of the list is currently being
locked by another workstation, LKSYS() shows the user name, date and
time of the lock being held by the other workstation, regardless of
which record in the lock list is being held. To determine the exact
record number that is being locked, and by whom, the user must
navigate to every desired record and test LKSYS(). In most cases,
knowing the name of the user who is preventing the lock is
sufficient.

RUN()

Syntax:

RUN()

The new RUN() function offers users an analogous function that matches
the RUN command just as the CALL() function can be used instead of the
CALL command. The RUN() function should return the DOS program
completion code. Currently, only 0 is returned. However, the RUN()
function will accept a character expression as a parameter,
eliminating the need for macro substitution when variables containing
a DOS program name are used.

SET()

Syntax:

SET()

The dBASE IV version 1.1 SET() function can now return many
environment settings:

SET("ATTRIBUTES"): Returns a character string in a form that can
be used by the SET COLOR TO command. The string returned is of the
form:
,, &&
,,<BOX>,<INFORMATION>,<FIELDS><br /><br /> To retrieve the various color attributes, the string must be<br />parsed. Applications Generator programs contain a user defined<br />function (UDF) called COLOR that can parse this string; this function<br />can be copied into your programs if desired.<br /><br /> SET("FILTER"): Returns a character string containing the filter<br />condition. If no filter is active, a null character string is<br />returned. <br /><br /> SET("FORMAT"): Returns a character string containing the name of<br />the currently active format file. If no format file is active, a null<br />character string is returned. <br /><br /> SET("RELATION"): Returns a character string in the form of the<br />SET RELATION TO command: <br /><br /> <exp> INTO <file alias><br /><br /> [, <exp> INTO <file alias> ...]<br /><br /> If no relation is active, a null character string is returned. <br /><br /> SET("DISPLAY"): Returns a character string containing the current<br />display mode.<br /><br /> SET("DIRECTORY"): Returns a character string containing the full<br />path of the current directory.<br /><br /> SET("DEFAULT"): Returns a character string containing the current<br />default drive.<br /><br /> SET("PATH"): Returns a character string containing the entire<br />dBASE IV search path set with the SET PATH TO command. If SET PATH TO<br />was not used, a null character string is returned. <br /><br /> SET("PROCEDURE"): Returns a character string containing the full<br />path and filename of the active procedure file. Procedure files are<br />activated with the SET PROCEDURE TO command; if SET PROCEDURE TO was<br />not issued, a null character string is returned. <br /><br /> SET("VIEW"): Returns a character string containing the full path<br />and filename of the current view. Views can be set through the<br />Control Center or with the SET VIEW TO command. A null character<br />string is returned if no view has been set. For example,<br /><br /> .? SET("VIEW")<br /><br /> C:\DBASE\DATA\MYVIEW.QBE<br /><br /> SET("DBTRAP"): Returns a character string containing the current<br />state of DBTRAP (either ON or OFF).<br /><br />WINDOW()<br /><br />Syntax:<br /><br />WINDOW() <br /><br />WINDOW() returns the name of the currently active window. The window<br />name is returned as an upper case, alphanumeric string. If there is<br />no active window the function returns a null string. <br /><br />For a window to be considered active by WINDOW(), the cursor must be<br />inside the window or the current processing must be occurring within<br />the window. Using ACTIVATE SCREEN from within a window deactivates<br />that window and causes the screen to be the active display area. When<br />this occurs, there are no windows considered active (even though you<br />can return to DECLAREd windows with the ACTIVATE WINDOW command) and<br />WINDOW() returns a null string. <br /><br />To use WINDOW() to query for the currently active window, W1: <br /><br />? WINDOW()<br />W1<br /><br />To use WINDOW() in a program,to store the name of the currently active<br />window and then reactivate it later. <br /><br />DEFINE WINDOW ENTRYAREA1 ;<br /> FROM 2,4 TO 15,30 DOUBLE<br />DEFINE WINDOW W1 ;<br /> FROM 5,5 TO 20, 20 DOUBLE<br />ACTIVATE WINDOW ENTRYAREA1<br />*--- Store ENTRYAREA1 in NAMEHOLDER.<br />NAMEHOLDER = WINDOW() <br />ACTIVATE WINDOW W1<br />...<br />ACTIVATE WINDOW &NAMEHOLDER<br /><br />WINDOW() is allowed in SQL mode, but not in SQL statements.<br /><br />MENU SYSTEM<br /><br />Form Generation<br /><br />In dBASE IV version 1.1 double quotes can be used as static text in<br />the report, form, or label design screens. Double quotes are<br />specially processed; bracket delimiters are added before being passed<br />to the code generator. <br /><br />In addition, the ability to create popups for VALID clause field<br />validation, as provided previously on the Ashton-Tate BBS, is now<br />incorporated into FORM.GEN. The other feature added on the BBS, field<br />sensitive HELP, is also supported. <br /><br />For example, the following statement placed on the forms design<br />screen:<br /><br />"This is a "test" of double quotes"<br /><br />is handled in dBASE IV version 1.1 by the following seven lines of<br />code after code generation: <br /><br />@ 7,17 SAY ["]<br />@ 7,18 SAY "This is a"<br />@ 7,28 SAY ["]<br />@ 7,29 SAY "test"<br />@ 7,33 SAY ["]<br />@ 7,34 SAY " of double quotes"<br />@ 7,51 SAY ["]<br /><br />Label Generation<br /><br />As in Reports and Forms, the double quote is supported for use in<br />static text on the design surface. <br /><br />Special support for A4 labels (a commonly used form outside of the<br />U.S.) is being supplied through the use of the special DOS<br />environmental variable SET DTL_LBLOPT=ON. See the special Release<br />Edition of TechNotes contained within the 1.1 update package for a<br />full description of this option. <br /><br />QBE Design Surface<br /><br />The following is a summary of changes made to the QBE design surface:<br /><br /> Columns in a file skeleton are now variable length. The minimum<br />width is 2 characters and the maximum is 60. Shift-F7 can be used to<br />size these columns and typing below field markers causes an elastic<br />stretching of the columns to accommodate the information entered. <br /><br /> F9 Zoom of text in a file skeleton field, calculated field, or<br />condition box places the cursor at the end of the text. When F9 Zoom<br />is pressed again, the cursor will be placed at the beginning of the<br />text. <br /><br /> Execution of a MARK query is now followed by the prompt: xx<br />records will be marked for deletion - OK (Y/N)?. If the answer is Y<br />the update will be done. If the answer is N the query will be<br />abandoned and the system will return to the previous context. <br /><br />Report Generation<br /><br />In dBASE IV version 1.0 double quotes could not be used as static text<br />in the report, form, or label design screens. The double quote was a<br />reserved character in the template language compiler. Single quotes<br />were used as acceptable substitutions. <br /><br />In dBASE IV version 1.1 double quotes can be used as static text in<br />the report screen. Double quotes are specially processed; bracket<br />delimiters are added before being passed to the code generator. <br /><br />CONFIGURATION<br /><br />New Config.db Options<br /><br />Several new Config.db options have been added. These are:<br /><br />DBTRAP<br />ASCIISORT<br />NOCLOCK<br />MARK<br /><br />DBTRAP sets the default status of the DBTRAP command. The DBTRAP<br />option does not show up in the menus of DBSETUP but can be set anyway<br />if entered by editing the Config.db file.. <br /><br />ASCIISORT is functional in the UK version only. If set ON, it causes<br />an ASCII sort order to take precedence. If OFF, the international<br />sort order (the default) will take precedence. <br /><br />NOCLOCK = ON prevents the clock from showing in the menu bar. It only<br />suppresses the clock when a menu shows, such as in EDIT or CREATE<br />REPORT. Issuing SET CLOCK ON will force the clock to show again. <br /><br />MARK sets the type of mark that is used between day, month and year in<br />date fields. The character specified should not be bound in quotes. <br />No error will be reported if this is done. The MARK character will,<br />however, show as a quotation mark and not the desired character.<br /><br />Dbcache<br /><br />Dbcache is a user configurable disk caching utility that enhances the<br />performance of dBASE IV version 1.1 input/output operations and<br />applications developed in dBASE IV. <br /><br />Performance enhancement is made possible by caching most recently read<br />data and most likely to be read data into memory, reducing the need to<br />access disk media for data. The speed of I/O and applications are<br />therefore enhanced because their associated data is already in memory<br />and can referenced immediately. <br /><br />The Dbcache utility of dBASE IV version 1.1 operates in expanded or<br />extended memory (usually memory on 286 and 386 based machines that is<br />unformatted by an expanded memory driver, or memory that is formatted<br />by an XMS driver such as Himem.sys). Alternatively, the cache can be<br />placed in Expanded memory (referred to as LIM memory, setup by such<br />drivers as Ems.sys and Cemm.sys or other memory managers that support<br />the Lotus Intel Microsoft standard such as QEMM and 386Max). <br /><br /> Caching data to conventional memory (640K) would undermine the goal<br />of performance enhancement because it would reduce the amount of<br />memory needed by dBASE IV and applications developed under dBASE IV to<br />execute. <br /><br />Dbcache is provided for use within dBASE IV version 1.1 only. It is<br />supplied as an overlay file that, if installed, is automatically<br />loaded when dBASE IV is started. The cache can be installed,<br />uninstalled and switched from Extended to Expanded memory support<br />through the use of a batch file. The cache is internal to dBASE IV<br />and can be tuned from within dBASE IV. It is not recommended to use<br />Dbcache if you are already using caching software on your computer<br />(such as SuperSpool, SmartDrive, and so on) because having two caching<br />programs can cause performance decreases, undermining advantages of<br />the cache. <br /><br />DOS Environmental Variables<br /><br />Syntax:<br /><br />SET DBTMP=[<path>] <br />SET TMP=[<path>] <br /><br />SET DBHEAP=[<1 to 100>]<br /><br />dBASE IV version 1.1 now utilizes several new DOS environmental<br />variables during start up. Here are the variables and what they do:<br /><br />DBTMP and TMP determine where temporary files are created<br /><br />DBHEAP determines the mvar memory/overlay ratio<br /><br />Use DBTMP to set the drive and directory into which you want dBASE IV<br />to create its temporary files. If DBTMP was not set in DOS, dBASE IV<br />will use the TMP as the target directory for temporary files. dBASE<br />IV performs a test against the target directory to assure that it<br />exists, and that it can be written to. If neither DBTMP nor TMP have<br />been assigned, dBASE IV tests the directory that contains Dbase.exe to<br />see if it can be written to. If that directory fails too, dBASE IV<br />tests the startup disk. If the startup disk is of a size greater than<br />2 MB, it too is tested. If all tests fail, the error message Cannot<br />create/write temporary file: <target directory> is returned. <br /><br />DBHEAP is set to allow a level of performance tuning within dBASE IV. <br />DBHEAP is set to a number from 1 through 100, which represents the<br />ratio of the amount of heap memory or free memory (above the<br />approximately 450K required for dBASE IV) allocated to memory variable<br />memory over the amount of heap memory allocated to overlay swapping. <br />Setting DBHEAP to 0 if equivalent to leaving it at its default value,<br />50. The higher the DBHEAP value, the more free memory is allocated to<br />memory variable usage. The lower the value, the more memory is<br />allocated to overlay swapping, aiding performance. In memory<br />intensive procedures like the Application Generator, setting DBHEAP<br />higher can eliminate Insufficient memory errors, allowing a larger<br />number of objects to be created on the design surface. See the<br />Readme.doc file for more information.<br /><br />Installation and Associated Changes<br /><br />The installation procedures for dBASE IV version 1.1 have changed<br />radically from those used in dBASE IV version 1.0. The single most<br />dramatic change is that there are now two separate programs in the<br />place of the version 1.0 Dbsetup. <br /><br />Install.exe handles installation only. Dbsetup.exe handles the<br />modification of the Config.db file. (The system information menu that<br />existed in version 1.0 of DBSETUP is no longer available.) <br /><br />Installation is now handled in three ways: Quick, Full and Menu<br />driven. The Menu driven option is the one closest to the 1.0<br />installation method. Quick install is designed to simply and quickly<br />install a user, with the least amount of user interaction. It also<br />has limited flexibility. For example, one cannot install multi-user<br />dBASE IV in Quick install. Full install (like Menu driven install)<br />offers installation of all options; Multi-user, Template Language,<br />Samples, Tutorial and Runtime/Developer's Edition files. <br /><br />All of the installation disks contain ZIPped (or compressed) files. <br />This saves disk space. However, it makes viewing files on the disks<br />more involved. To view the contents of the PKSFX produced .exe files<br />(self-extracting ZIP files) simply type the name of the file followed<br />by a /T. This tests the integrity of the ZIP file contents, and lists<br />them at the same time. <br /><br />In dBASE IV version 1.0 as each file was being copied, the file name<br />and its size, was displayed on the status bar during installation. As<br />a consequence of the ZIP scheme used, there is no indication of the<br />files being copied to the target directory. On a slower speed<br />computer, this may be interpreted as a lock-up. Patience on the part<br />of the user is important during installation. <br /><br />Runtime install:<br /><br />For owners of the dBASE IV Developer's Edition, Runtime can be<br />installed directly from INSTALL with the command line option /R. In<br />addition, Runtime supports the cache, and has a special installation<br />scheme to facilitate the distribution of the Runtime.ovl file which is<br />larger than 1.7 MB.<br /><br /><br /><br />PRINTING<br /><br />Printer drivers<br /><br />The way Dbsetup accesses and installs printer drivers is new too. <br />Only ASCII.pr2 and Generic.pr2 drivers are copied to the hard disk<br />during install. Dbsetup will automatically extract the user requested<br />drivers from the Drivers.exe file. <br /><br />PostScript Printer Driver<br /><br />dBASE IV now supports a PostScript printer driver. In DBSETUP, this<br />driver is found in the "Apple" printer driver section given the name<br />"Laserwriter". <br /><br />There is limited Postscript support supplied by this driver,<br />Postscri.pr3 and its associated file, Postscri.dld. Many of the<br />settings in the .dld file can be customized and the functionality of<br />this driver can be modified by the user. A complete description of<br />the contents of the .dld file and what the macro symbols contained<br />mean can be found in the section entitled "What's New in dBASE IV<br />version 1.1" of Getting Started in dBASE IV. <br /><br />New Hewlett Packard Printer Drivers<br /><br />Several new drivers have been created for the HP LaserJet printer. <br />These new drivers fix several reported problems. The full text<br />describing each driver is available on the BBS as Anom_17.txt, found<br />most commonly in the Anomaly.exe file. Here is a brief discussion of<br />the changes. <br /><br />First, three of the drivers that are now shipped as replacements for<br />the dBASE IV version 1.0 drivers are <br /><br />HPLas100.pr2<br /><br />HPLas2I.pr2<br /><br />HPLas2ID.pr2.<br /><br />The printer initialization code for these drivers has been changed to<br />set Skip over perforation ON. This change allows commands such as<br />LIST TO PRINT to flow from page to page without having records<br />disappear into the dead zones of the pages (the first three and last<br />three lines on a page). These drivers still produce 66 printed lines<br />per page, but the first printable line is now one line higher than it<br />was with the version 1.0 drivers. <br /><br />The same is true for the landscape driver HPLasL.pr2, so the driver<br />HPLasL45.pr2 was developed to prevent printing in the dead zones. <br />However, this driver prints 45 lines per page at 6 lines per inch<br />rather than the usual 51. To use this driver in dBASE IV, you will<br />have to set page length to 45 lines in the Print menu. <br /><br />Because the original HP drivers were designed to print 66 lines on a<br />page, and still allow for dead zones at the top and bottom, the actual<br />printed line spacing is 6.5 lines per inch. This makes printing<br />labels and pre-printed forms impossible. To allow for greater<br />flexibility, we now supply 6 line per inch drivers in addition to the<br />original drivers. <br /><br />The HPLasL45.pr2 driver (discussed above) solves this problem for<br />landscape printing. The following printer drivers print at 6.5 lines<br />per inch:<br /><br />HPLas100.pr2<br />HPLas2I.pr2<br />HPLas2ID.pr2.<br /><br />New printer drivers are now included that print at 6 lines per inch. <br />These are:<br /><br />HPLas60.pr2<br />HPLas260.pr2<br />HPLaD60.pr2<br /><br />These drivers print 60 lines per page instead of 66, so the page<br />length within the Print menu must be set to 60 lines. <br /><br />The next set of drivers have been designed to access the internal bold<br />face font in the HP Series II, III and IID printers (The original<br />LaserJet and LaserJet Plus can only emulate bold face but have no<br />actual bold font). They are listed on the next page.<br /><br />HPLas260.pr2<br />HPLaD60.pr2<br />HPLas2I.pr2<br />HPLas2ID.pr2<br /><br />HPL245.pr2 <br /><br />These drivers will access and print the bold font if it is available<br />for the current font. The Series II and III printers have an internal<br />Courier bold font. If a bold font is not available for some other<br />font, no bolding will occur. <br /><br />Finally, the HPLasL.pr2 driver was designed for the original LaserJet<br />printer which does not support the IBM extended character set for line<br />and box drawing. The new driver, HPLs2L45.PR2, does support IBM<br />character set line and box drawing, at 6 lines per inch, 45 lines per<br />page. Again, the line spacing must be set to 45 within the Print<br />menu. <br /><br />XON/XOFF Support of Serial Printers<br /><br />As part of the PostScript printer support for dBASE IV version 1.1,<br />XON/XOFF functionality has been added so that reports larger than one<br />page can be appropriately printed. <br /><br />The XON/XOFF functionality is based on the software handshaking<br />protocol used by the Apple LaserWriter, and some other serial printers<br />for flow control during printing. <br /><br />The Apple LaserWriter sends the XOFF code (Ctrl-S) to dBASE IV when<br />the printer needs to signal for a pause in the flow of information<br />(e.g., when its printer buffer fills). When dBASE IV receives the<br />XOFF code, it temporarily stops sending information to the printer. <br />When the printer is ready to receive more information, it sends dBASE<br />IV the XON code (Ctrl-Q), which causes dBASE IV to continue sending<br />information to the printer. <br /><br />MISCELLANY<br /><br />SQL Support at the Dot Prompt<br /><br />The functionality of SQL has been vastly expanded in dBASE IV version<br />1.1. It is now possible to perform a number of dBASE IV commands,<br />using SQL tables, that were previously unavailable. This expansion of<br />functionality makes SQL a more attractive feature of dBASE IV version<br />1.1. <br /><br />The full description of dot prompt commands that are now supported in<br />version 1.1 when using SQL tables can be found in the section of<br />Getting Started in dBASE IV version 1.1 entitled "What's New in dBASE<br />IV version 1.1."<br /><br />Recursive Macro Calls<br /><br />In dBASE IV version 1.1, a macro can contain calls to itself and other<br />existing macros. However, a macro cannot call itself during its own<br />creation. Creation of a recursive macro requires that the macro in<br />question be modified and the call to itself be entered manually. This<br />can be accomplished through the Tools: Macros: Modify option. Select<br />the macro letter identifier, function key or name from the macro<br />display table and add {Alt-F10}<macro letter/function key/ name><br />wherever appropriate. For example:<br /><br />Assume a macro named A is defined by the following:<br /><br />dir{Enter}<br /><br />To make A recursive, select it from the Macro Display Table of the<br />Tools: Macros: Modify option and add the following: <br /><br />{Alt-F10}a<br /><br />The macro now contains the following keystrokes:<br /><br />dir{Enter}<br /><br />{Alt-F10}a<br /><br />When macro A is invoked from the dot prompt, it will issue a DIR<br />command and then invoke itself again. You will have to press escape<br />to terminate this particular macro. <br /></div></div><br><br> <div id='jp-relatedposts' class='jp-relatedposts' > <h3 class="jp-relatedposts-headline"><em>Related</em></h3> </div> </div><!--/entry --> </div><!-- .entry-container --> <footer class="post-footer postdata fix"> </footer><!-- .post-footer --> <div class='postdata line'> <span class='line-date'><span class='icon'> </span>December 10, 2017</span> <span class="comments"><span class="icon"> </span><a href="#respond">Add comments</a></span> </div> <section id="comments"> <div id="respond" class="comment-respond"> <h3 id="reply-title" class="comment-reply-title">Leave a Reply <small><a rel="nofollow" id="cancel-comment-reply-link" href="/list/DBASE/TN9009.ZIP/INFO/#respond" style="display:none;">Cancel reply</a></small> </h3> <form id="commentform" class="comment-form"> <iframe title="Comment Form" src="https://jetpack.wordpress.com/jetpack-comment/?blogid=30175801&postid=6272&comment_registration=0&require_name_email=1&stc_enabled=1&stb_enabled=1&show_avatars=1&avatar_default=mystery&greeting=Leave+a+Reply&greeting_reply=Leave+a+Reply+to+%25s&color_scheme=light&lang=en_US&jetpack_version=6.8&show_cookie_consent=10&has_cookie_consent=0&sig=668f65d49c94aac3d71f15c9b9202bc949b81ce8#parent=https%3A%2F%2Fwww.pcorner.com%2Flist%2FDBASE%2FTN9009.ZIP%2FINFO%2F" style="width:100%; height: 430px; border:0;" name="jetpack_remote_comment" class="jetpack_remote_comment" id="jetpack_remote_comment" sandbox="allow-same-origin allow-top-navigation allow-scripts allow-forms allow-popups"></iframe> <!--[if !IE]><!--> <script> document.addEventListener('DOMContentLoaded', function () { var commentForms = document.getElementsByClassName('jetpack_remote_comment'); for (var i = 0; i < commentForms.length; i++) { commentForms[i].allowTransparency = false; commentForms[i].scrolling = 'no'; } }); </script> <!--<![endif]--> </form> </div> <input type="hidden" name="comment_parent" id="comment_parent" value="" /> </section> <!-- #comments --> </article><!--/post --> <nav class='post-nav fix'> <table> <tr> <td class='previous'><a href="https://www.pcorner.com/list/DBASE/WEDIT.ZIP/INFO/" rel="prev"><span class="icon"> </span> DBASE – WEDIT.ZIP</a></td> <td class='next'><a href="https://www.pcorner.com/list/TUTOR/720TO14M.ZIP/INFO/" rel="next"><span class="icon"> </span> TUTOR – 720TO14M.ZIP</a></td> </tr> </table> </nav> </div><!-- content --> </div><!-- main col --> <div id='sidebar-shell-1' class='sidebar-shell sidebar-shell-right'> <div class="dbx-group right boxed warea" id="sidebar"> <!--widget start --><aside id="paypal_donations-2" class="dbx-box suf-widget widget_paypal_donations"><div class="dbx-content"><h3 class="dbx-handle plain">Donate</h3><p>Please help defray the cost of running this free service.</p> <!-- Begin PayPal Donations by https://www.tipsandtricks-hq.com/paypal-donations-widgets-plugin --> <form action="https://www.paypal.com/cgi-bin/webscr" method="post"> <div class="paypal-donations"> <input type="hidden" name="cmd" value="_donations" /> <input type="hidden" name="bn" value="TipsandTricks_SP" /> <input type="hidden" name="business" value="gjsmith66@pcorner.com" /> <input type="hidden" name="rm" value="0" /> <input type="hidden" name="currency_code" value="USD" /> <input type="image" style="cursor: pointer;" src="https://www.paypal.com/en_US/i/btn/btn_donate_LG.gif" name="submit" alt="PayPal - The safer, easier way to pay online." /> <img alt="" src="https://www.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1" /> </div> </form> <!-- End PayPal Donations --></div></aside><!--widget end --><!--widget start --><aside id="text-7" class="dbx-box suf-widget widget_text"><div class="dbx-content"> <div class="textwidget"><script type="text/javascript"> amzn_assoc_placement = "adunit0"; amzn_assoc_enable_interest_ads = "true"; amzn_assoc_tracking_id = "zca-20"; amzn_assoc_ad_mode = "auto"; amzn_assoc_ad_type = "smart"; amzn_assoc_marketplace = "amazon"; amzn_assoc_region = "US"; amzn_assoc_linkid = "ea8faac85a6c9ee94ab5174bccaeb487"; amzn_assoc_emphasize_categories = "13900871"; amzn_assoc_fallback_mode = {"type":"search","value":"DOS Windows"}; amzn_assoc_default_category = "All"; </script> <script src="//z-na.amazon-adsystem.com/widgets/onejs?MarketPlace=US"></script> <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script> <ins class="adsbygoogle" style="display:inline-block;width:160px;height:600px" data-ad-client="ca-pub-8001169946558833" data-ad-slot="3404908173"></ins> <script> (adsbygoogle = window.adsbygoogle || []).push({}); </script> </div> </div></aside><!--widget end --></div><!--/sidebar --> </div> </div><!-- /container --> </div><!--/wrapper --> <footer> <div id='page-footer'> <div class='col-control'> <div id="cred"> <table> <tr> <td class="cred-left">© 2018 <a href='http://www.pcorner.com'>The Programmer's Corner</a> by Personalized Computer Systems </td> <td class="cred-center"><script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script> <ins class="adsbygoogle" style="display:inline-block;width:728px;height:90px" data-ad-client="ca-pub-8001169946558833" data-ad-slot="6568049104"></ins> <script> (adsbygoogle = window.adsbygoogle || []).push({}); </script> </td> <td class="cred-right"></td> </tr> </table> </div> </div> </div> </footer> <!-- 95 queries, 11MB in 0.285 seconds. --> <!-- location footer --> <script type='text/javascript'> //<![CDATA[ jQuery(document).ready(function($) { $('html').MagicLiquidizerTable({ whichelement: 'table', breakpoint: '780', headerSelector: 'thead td, thead th, tr th', bodyRowSelector: 'tbody tr, tr', table: '1' }) }) //]]> </script> <!-- tracker added by Ultimate Google Analytics plugin v1.6.0: http://www.oratransplant.nl/uga --> <script type="text/javascript"> var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www."); document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E")); </script> <script type="text/javascript"> var pageTracker = _gat._getTracker("UA-10500370-7"); pageTracker._initData(); pageTracker._trackPageview(); </script> <div style="display:none"> </div> <script type='text/javascript' src='https://www.pcorner.com/wp-includes/js/comment-reply.min.js?ver=4.9.8'></script> <script type='text/javascript' src='https://www.pcorner.com/wp-includes/js/jquery/ui/core.min.js?ver=1.11.4'></script> <script type='text/javascript' src='https://www.pcorner.com/wp-includes/js/jquery/ui/datepicker.min.js?ver=1.11.4'></script> <script type='text/javascript'> jQuery(document).ready(function(jQuery){jQuery.datepicker.setDefaults({"closeText":"Close","currentText":"Today","monthNames":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthNamesShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"nextText":"Next","prevText":"Previous","dayNames":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"dayNamesShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"dayNamesMin":["S","M","T","W","T","F","S"],"dateFormat":"MM d, yy","firstDay":1,"isRTL":false});}); </script> <script type='text/javascript' src='https://www.pcorner.com/wp-includes/js/jquery/ui/widget.min.js?ver=1.11.4'></script> <script type='text/javascript' src='https://www.pcorner.com/wp-includes/js/jquery/ui/progressbar.min.js?ver=1.11.4'></script> <script type='text/javascript' src='https://s0.wp.com/wp-content/js/devicepx-jetpack.js?ver=201850'></script> <script type='text/javascript' src='https://www.pcorner.com/wp-includes/js/jquery/ui/mouse.min.js?ver=1.11.4'></script> <script type='text/javascript' src='https://www.pcorner.com/wp-includes/js/jquery/ui/sortable.min.js?ver=1.11.4'></script> <script type='text/javascript' src='https://secure.gravatar.com/js/gprofiles.js?ver=2018Decaa'></script> <script type='text/javascript'> /* <![CDATA[ */ var WPGroHo = {"my_hash":""}; /* ]]> */ </script> <script type='text/javascript' src='https://www.pcorner.com/wp-content/plugins/jetpack/modules/wpgroho.js?ver=4.9.8'></script> <script type='text/javascript' src='https://www.pcorner.com/wp-content/plugins/jetpack/_inc/build/likes/queuehandler.min.js?ver=6.8'></script> <script type='text/javascript' src='https://www.pcorner.com/wp-includes/js/wp-embed.min.js?ver=4.9.8'></script> <script async="async" type='text/javascript' src='https://www.pcorner.com/wp-content/plugins/akismet/_inc/form.js?ver=4.1'></script> <!--[if IE]> <script type="text/javascript"> if ( 0 === window.location.hash.indexOf( '#comment-' ) ) { // window.location.reload() doesn't respect the Hash in IE window.location.hash = window.location.hash; } </script> <![endif]--> <script type="text/javascript"> (function () { var comm_par_el = document.getElementById( 'comment_parent' ), comm_par = ( comm_par_el && comm_par_el.value ) ? comm_par_el.value : '', frame = document.getElementById( 'jetpack_remote_comment' ), tellFrameNewParent; tellFrameNewParent = function () { if ( comm_par ) { frame.src = "https://jetpack.wordpress.com/jetpack-comment/?blogid=30175801&postid=6272&comment_registration=0&require_name_email=1&stc_enabled=1&stb_enabled=1&show_avatars=1&avatar_default=mystery&greeting=Leave+a+Reply&greeting_reply=Leave+a+Reply+to+%25s&color_scheme=light&lang=en_US&jetpack_version=6.8&show_cookie_consent=10&has_cookie_consent=0&sig=668f65d49c94aac3d71f15c9b9202bc949b81ce8#parent=https%3A%2F%2Fwww.pcorner.com%2Flist%2FDBASE%2FTN9009.ZIP%2FINFO%2F" + '&replytocom=' + parseInt( comm_par, 10 ).toString(); } else { frame.src = "https://jetpack.wordpress.com/jetpack-comment/?blogid=30175801&postid=6272&comment_registration=0&require_name_email=1&stc_enabled=1&stb_enabled=1&show_avatars=1&avatar_default=mystery&greeting=Leave+a+Reply&greeting_reply=Leave+a+Reply+to+%25s&color_scheme=light&lang=en_US&jetpack_version=6.8&show_cookie_consent=10&has_cookie_consent=0&sig=668f65d49c94aac3d71f15c9b9202bc949b81ce8#parent=https%3A%2F%2Fwww.pcorner.com%2Flist%2FDBASE%2FTN9009.ZIP%2FINFO%2F"; } }; if ( 'undefined' !== typeof addComment ) { addComment._Jetpack_moveForm = addComment.moveForm; addComment.moveForm = function ( commId, parentId, respondId, postId ) { var returnValue = addComment._Jetpack_moveForm( commId, parentId, respondId, postId ), cancelClick, cancel; if ( false === returnValue ) { cancel = document.getElementById( 'cancel-comment-reply-link' ); cancelClick = cancel.onclick; cancel.onclick = function () { var cancelReturn = cancelClick.call( this ); if ( false !== cancelReturn ) { return cancelReturn; } if ( ! comm_par ) { return cancelReturn; } comm_par = 0; tellFrameNewParent(); return cancelReturn; }; } if ( comm_par == parentId ) { return returnValue; } comm_par = parentId; tellFrameNewParent(); return returnValue; }; } // Do the post message bit after the dom has loaded. document.addEventListener( 'DOMContentLoaded', function () { var iframe_url = "https:\/\/jetpack.wordpress.com"; if ( window.postMessage ) { if ( document.addEventListener ) { window.addEventListener( 'message', function ( event ) { var origin = event.origin.replace( /^http:\/\//i, 'https://' ); if ( iframe_url.replace( /^http:\/\//i, 'https://' ) !== origin ) { return; } jQuery( frame ).height( event.data ); }); } else if ( document.attachEvent ) { window.attachEvent( 'message', function ( event ) { var origin = event.origin.replace( /^http:\/\//i, 'https://' ); if ( iframe_url.replace( /^http:\/\//i, 'https://' ) !== origin ) { return; } jQuery( frame ).height( event.data ); }); } } }) })(); </script> <script type='text/javascript' src='https://stats.wp.com/e-201850.js' async='async' defer='defer'></script> <script type='text/javascript'> _stq = window._stq || []; _stq.push([ 'view', {v:'ext',j:'1:6.8',blog:'30175801',post:'6272',tz:'0',srv:'www.pcorner.com'} ]); _stq.push([ 'clickTrackerInit', '30175801', '6272' ]); </script> </body> </html>