Dec 102017
Aston Tate dBase IV Tech Notes for July 90. Useful information. | |||
---|---|---|---|
File Name | File Size | Zip Size | Zip Type |
TNDB0790.TXT | 86333 | 28351 | deflated |
Download File TN9007.ZIP Here
Contents of the TNDB0790.TXT file
1 MacroMan! July 1990 dBASE IV
This article is reprinted from the July 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.
Mild-mannered Software Support Technician by day, programmer
extraordinaire by night, Macro Man fights the evils of redundancy and
hard coding. Faster than a SEEKing index, more powerful than a Local
Area Network. Able to LOOP huge code structures in a single FOUND() ,
Macro Man looks for TRUE (.T.), logic, and the dBASE IV way.
Macro Man!
Adam L. Menkes
We have all, at one time or another, made changes to the structure of
a database, only to find out later that the GET statements
incorporated by our format files or programs are not working properly
because either the lengths have changed or the field names have
changed or both. You may have feared that there is now way around
this; that you're stuck with going through all your code changing each
memory variable in every program. "This," you mutter to yourself,
"is the price I pay for power." You're tired of repetitive coding
that results in hours of debugging just because some items were not
consistently spelled correctly or data types mismatched.
In the listing which starts on page 23, you'll find a set of
procedures and functions strategically designed to simplify many of
your future coding needs. You will be able to declare all your
database memory variable fields public, set field memory variables to
blanks or zero, and replace contents of your fields with memory
variables that were changed while in full-screen edit mode.
& What Have We Here?
These procedures make use of the ampersand "&" for macro substitution
or what is more and more being referred to as indirection. This is to
distinguish the use of the & from keyboard macros now available in
dBASE IV. The & is also different than the double-ampersand && which
allows for comments on the same line as a dBASE command. For those
not familiar with macro substitution/indirection, below is an example
of how it works in a program:
*--- Initialize memory variables mtest, mvar1, mvar2, mvar3, mvar4,
* mvar5, mvar6, mvar7
mtest=1234 && Stores the number 1234 to variable 'mtest'.
mvar1="mtest" && Stores the string 'mtest' to variable 'mvar1'.
mvar2=mvar1 && Result is "mtest" (Character).
mvar3=mtest && Result is 1234 (Numeric).
*--- Results of storing these memory variables to others using
* macro substitution.
mvar4=&mtest && Result is SYNTAX ERROR since there is no
* variable 1234 defined.
mvar5=&mvar1 && Result is 1234 (Numeric) since it sets mvar5
* equal to the contents of variable mtest.
mvar6="&mtest" && Result is the string "&mtest".
mvar7="&mvar1" && Result is the string "mtest".
Using the Procedures
MacroMan.prg is an interdependent set procedures and functions that
performs a variety of tasks to allow shorter and more accurate code
with less potential for error. The procedures and functions called by
other procedures are denoted with ** in the in-line comment (&&)
area. These are optional and can be removed from the code and
replaced with explcitly named files and/or variables whenthese
procedures need to be used independently (except where otherwise
noted).
f you use a dBASE IV catalog (.cat) file, which is simply a specially
assigned .dbf file, the field name is File_name and is 12 characters
long. The file name includes the extension of the file. However,
unlike the special database referred to here, a .cat file may contain
any type of dBASE created file (such as formats, reports, labels,
and memos), not just database files.
SetaPub and SetaMem
Procedures SetaPub and SetaMem require a database with one field
called Dbf, which should be defined as a character field with a length
of eight bytes. This field will contain the names of all the
databases that will be used in these procedures, somehwat like a
catalog. These procedures could also be modified to take advantage of
the existing catalog if you so choose. Both of these procedures
require the use of a secondary procedure which use only one database.
The procedure SetaPub is not necessary if each database is passed as a
parameter to Procedure Set1pub as follows :
DO Set1Pub WITH "Database1"
DO Set1Pub WITH "Database2"
DO Set1Pub WITH "Database3"
. .
. .
DO Set1Pub WITH "Databasen"
The preferred method is to have a database, MainDbf for example, with
one field with the characteristics described previously. Each record
would be the name of a different database.
Record 1 Database1
Record 2 Database2
Record 3 Database3
. .
. .
Record n Databasen
This would require only one statememt :
DO SetaPub WITH "Maindbf"
For the purposes of this article, it is important that all database
files should be in the current directory. These procedures have not
been tested for databases in other directories which could be
specified by a path. If your files are in another directory, make
sure the dBASE IV directory is in your DOS path and that you change to
the desired directory before starting dBASE IV. Additionally, field
names in the database should not use more than nine characters, as m
will be added as a prefix to these field names to create matching
memory variables. These memory variables may not behave properly
beyond ten characters. This should not be confused with the prefix
m-> which is used to distinguish a memory variable spelled exactly the
same as a field name.
The accompanying procedure, Set1Pub, checks each field for a field
name. Since a database can not contain a field with length of 0, this
procedure checks each field using the FIELD() function from FIELD(1)
to FIELD(n) until FIELD(n) returns a null.
The procedures SetaMemand Set1Mem are best used in conjunction with
SetaPub or Set1Pub, as they do not check to see if a memory variable
has already been declared, but assume it exists and is either public
or private.
InUse ()
This function checks all ten work areas to see if the database passed
as a parameter is in use in any of these areas. If it is, the value
returned is the number of the work area where the file is in use,
otherwise, 0 is returned. For example:
. SELECT SELECT() && Select next highest open work area
. USE Client
. SELECT SELECT()
. USE Sample
. SELECT SELECT()
. USE Checks
. ? Inuse("Sample")
9
FullDbf ()
This function checks for a file extension, and if one does not exist,
assumes a ".dbf" extension. For example:
. Test1 = FullDbf("Client")
CLIENT.DBF
. Test2 = FullDbf("Sample.dbf")
SAMPLE.DBF
. Test3 = FullDbf("Info.txt")
INFO.TXT
There are those times when you want to branch to a procedure that
switches to an alternate work area. By calling your procedure as a
parameter with this procedure, you are returned to the work area from
which you began.
Exceptions With Memo Fields
Although you can not create a memory variable for a memo field, you
can create a memory variable that references a text file of that name,
creating a pseudo memory variable. This type of functionality is not
included in this article as thorough testing has not been done.
However, the concept is worth mentioning. As we are able to store all
other data to memory variables and then, save or reject the entries we
have made, it would be helpful if the same option were available for
memo fields.
For example, in a network, it is not desirable to retain a record in a
locked mode while changes were being made (as it could be locked for a
while), nor would you want the changes made to the file if the user
decided that the information added or changed in the memo field was
incorrect. In order to add this capability, remove the following
clause in the procedure entitled Set1Pub.
IF mFld_Type <> "M"
PUBLIC m&mFld_Name && Declares memory variable
* 'm' + the field name
PUBLIC.
ENDIF
and in procedure Set1Mem, add the following to the DO CASE clause:
CASE mFld_Type = "M"
SET ALTERNATE TO m&mFld_Type..VMO [ADDITIVE]
*--- Note the two periods. One denotes the end of the
* macro, the second is for the file extension VMO
* (Variable MEMO).
SET ALTERNATE TO
You may want to check first to see if InUse (m&mFld_Type..VMO), and
either overwrite it or add to it with the ADDITIVE option. Remember
to use SET SAFETY OFF so that you won't be continually prompted to
overwrite the file. You may also want to COPY MEMO TO m&mFld_Name
[ADDITIVE] from within the program if a SEEK is performed.
Once changes were made from within the program: (using MODI COMM
&mFld_Name, for example), you could not REPLACE Fld_Name WITH
mFld_Name, since this field is a memo data type (as accounted for in
the case statement above checking for mFld_Type = "M") but you could
APPEND MEMO FROM &mFld_Name [OVERWRITE]. This could be easily added to
the ReplVar procedure as the ELSE condition for IF mFld_Type <> "M".
Now that this article has got you started, try this concept with
arrays, windows, and popups? Ah, yes, the wonders of macro
substitution.
MacroMan.prg
PROCEDURE SetaPub
*-- Initializes PUBLIC memory variables defined as "m" +
FIELDNAME
* for FIELDS in a Database. This is used in conjunction
with the
* procedure Set1Pub.
*-- SYNTAX : DO Procedure WITH
*-- Example: DO SetaPub WITH "Maindbf.DBF"
PARAMETER AllDbf &&
DATABASE that contains a short catalog
*
of DATABASEs with fields that will be
*
used for declaring memory variables.
DO SelArea1 WITH AllDbf && ** Selects Area where
DATABASE is in
*
use or opens it in an unused area.
SCAN
vDbf=DBF &&
Variable 'vDbf' gets database name
* from database
passed as a Parameter.
DO Set1Pub WITH vDbf && Calls sub-procedure
passing this
* variable as
the parameter.
DO SelArea1 WITH Alldbf && R e-SELECTs Work Area of
AllDbf.
ENDSCAN
DO SelArea2 && ** Closes
AllDbf if it was not in USE
*
before the current procedure
RETURN
PROCEDURE Set1Pub
* Initializes PUBLIC memory variables defined as "m" +
FIELDNAME
* for FIELDS in one Database.
*-- SYNTAX : DO Procedure WITH
*-- Example: DO Set1Pub WITH "Clients.DBF"
PARAMETER vDbf
DO SelArea1 WITH vDbf && **
mFldVal=1
DO WHILE LEN(TRIM(FIELD(mFldVal))) <> 0 && Check that Field(n)
*
exists.
mFld_Name = FIELD(mFldVal) && Gets the
name of the field
mFld_Type = TYPE(FIELD(mFldVal)) && and the
field type.
IF mFld_Type <> "M"
PUBLIC m&mFld_Name &&
Declares memory variable
* 'm'
+ the field name PUBLIC.
ENDIF
mFldVal=mFldVal+1
ENDDO
DO SelArea2
&& **
RETURN
PROCEDURE SetaMem
*-- Initializes "empty" memory variables defined as "m" +
FIELDNAME
* for FIELDS in all databases listed by this database for
their
* respective field types. Used in conjunction with Set1Mem.
*-- SYNTAX : DO Procedure WITH
*-- Example: DO SetaMem WITH "Maindbf.DBF"
PARAMETER AllDbf
DO SelArea1 WITH AllDbf && **
SCAN
vDbf=DBF
DO Set1Mem WITH vDbf, .T. && Note the second
parameter.
DO SelArea1 WITH AllDbf && **
ENDSCAN
DO SelArea2 && **
RETURN
PROCEDURE Set1Mem
*-- Initializes memory variables defined as "m" + FIELDNAME
for
* fields in a chosen database for their respective field
types.
PARAMETERS VDbf, mAddEdit
*-- vDbf: variable .dbf name
* mAddEdit: logic type which, if TRUE, sets the variables
empty
* and if FALSE, gets the FIELD contents from the current
record.
*-- SYNTAX : DO Procedure WITH,
*-- Example: DO Set1Mem WITH "Clients.DBF", .F.
DO Efile WITH "Temp" && ** Erase
Temp.DBF.
DO SelArea1 WITH vDbf && **
mArea=Inuse(vDbf) && ** Sets
mArea to Work Area of vDbf.
COPY STRU EXTENDED TO Temp
MSelTemp=SELECT()
&& Highest available Work Area.
SELECT(MSelTemp)
&& SELECTs unUSEd Work Area.
USE Temp
SCAN
mFld_Name = Field_Name
mFld_Type = Field_Type
mFld_Len = Field_Len
IF mAddEdit &&
Initializes memory variables
*
EMPTY if in an ADD type program.
DO CASE
CASE mFld_Type = "C"
m&mFld_Name =
SPACE(mFld_Len)
*-- Stores the appropriate
number of spaces to the
* character variable.
CASE mFld_Type = "D"
m&mFld_Name =
{} && " / / "
CASE mFld_ype = "F"
m&mFld_Name =
FLOAT(0) && 0
CASE mFld_Type = "N"
m&mFld_Name =
FIXED(0) && 0
ENDCASE
ELSE
SELECT(mArea)
m&mFld Name = &mFld_Name
*-- Store the field contents of the current
record into these
* variables.
SELECT(mSelTemp)
ENDIF
ENDSCAN
DO Efile WITH "Temp" && **
DO SelArea2 && **
RETURN
PROCEDURE Efile
*-- Erases a specified file, even if the file is currently in
USE.
*-- SYNTAX : DO Procedure WITH
*-- Example: DO Efile WITH "BadFile.TXT"
PARAMETER vDbf && File to be
erased.
vDbf=FullDbf(vDbf) && ** Checks
for .DBF extension.
IF FILE(vDbf)
IF Inuse(vDbf)<>0 && ** If
database is SELECTed;
MSelDbf=Inuse(vDbf) && ** it
returns the Work Area
SELECT(MSelDbf) && and SELECTs
that Work Area
USE && and closes it so
that it can
ENDIF
ERASE(vDbf) && be
ERASEd.
ENDIF
RETURN
FUNCTION InUse
PARAMETER vDbf
SET EXACT ON
*-- SET EXACT OFF would cause potential problems if you were
* checking for a database such as "Client" without
specifying the
* extension if a file called Client1, Myclient, or similar
existed
* in the same directory.
mAlias=0
vDbf=FullDbf(vDbf) && **
mCount=1
DO WHILE mCount<=10 && This loop checks all 10
Areas.
SELECT(mCount)
IF (vDbf) $ UPPER(DBF())
*--- The '$' is used instead of '=' so the
PATH does not have
* to be specified in the variable.
mAlias=mCount && Sets the Work Area where it
is being USEd.
RETURN mAlias
*--- Exit the loop once the Area is found
ENDIF
mCount=mCount+1 && Increments to check next
Area.
ENDDO
SET EXACT OFF
RETURN mAlias
FUNCTION FullDbf
PARAMETER vDbf
vDbf=UPPER(vDbf)
&& Convert to Uppercase.
IF LEN(TRIM(vDbf))<>0 && checks for null.
IF AT(".",vDbf)=0 &&
Checks for an extension.
vDbf=TRIM(vDbf)+".DBF" && Adds '.DBF' to the
parameter.
ENDIF
ENDIF
RETURN vDbf
PROCEDURE ResetArea
PARAMETERS ProcToDo, Param1
*---Parameters: Procedure to do, Additional parameter passed.
*-- SYNTAX : DO Procedure WITH,
*-- Example: DO ResetArea WITH "SetaPub", "Maindbf"
mStartArea=ALIAS() && Current Area that is SELECTed.
DO &ProcToDo WITH Param1
*--- Calls a procedure that is specified by a parameter,
rather
*--- than a specific procedure, and passes 1 parameter
(Param1).
IF LEN(TRIM(mStartArea)) <> 0
SELECT(mStartArea)
*--- Re-SELECTs the Area from where you started.
ELSE
USE
ENDIF
RETURN
PROCEDURE SelArea1
*-- Selects Area where DATABASE is in
* use or opens it in an unused area.
*-- Must be used in conjunction with SelArea2
PARAMETER mParam1
PUBLIC mWasOpen,mMaindbf
IF Inuse(mParam1) <> 0 &&
**
mMainDbf=Inuse(mParam1)
&& **
SELECT(mMainDbf)
mWasOpen=.T.
*--- mWasOpen is TRUE when the database is in USE
prior to
* being opened by a procedure or function.
ELSE
mMainDbf=SELECT()
SELECT(mMainDbf)
USE(mParam1)
mWasOpen=.F.
*--- mWasOpen is FALSE if the database was not in USE,
whether
* or not it gets USEd during a procedure or
function.
ENDIF
RETURN
PROCEDURE Selarea2
*-- Must be used in conjunction with Selarea1.
IF .NOT. mWasOpen && If the Database was not open
initially,
SELECT(mMainDbf) && SELECT that Work
Area,
USE && and
CLOSE the Database.
ENDIF
RETURN
PROCEDURE ReplVar
*-- REPLACEs FIELDS in a database with their corresponding
memory
* variables.
PARAMETER vDbf,mAddEdit
*-- Database in which to REPLACE variables, whether in Add or
Edit
* mode. Add or Edit will determine if the current record is
* replaced, or if a new record will be added.
PUBLIC mThisDbf
mThisDbf = vDbf
DO Efile WITH "Temp" && **
DO SelArea1 WITH mThisDbf && **
mWOpen = mWasOpen && **
*--- Temporarily stores this variable because it may change.
IF mAddEdit
APPEND BLANK
ENDIF
COPY STRU EXTENDED TO Temp
mSelTemp = SELECT() && Find highest Work Area
available,
SELECT (mSelTemp) && and SELECT this
unused area.
USE Temp
SCAN
mFld_Name = TRIM(Field_name)
mFld_Type = Field_Type
mmFld_Name = "m" + mFld_Name
DO SelArea1 WITH mThisDbf && **
IF mFld_Type <> "M"
*--- Will not work with MEMO fields.
REPLACE &mFld_Name WITH &mmFld_Name
*--- i.e. REPLACE Fieldx with mFieldx
ENDIF
SELECT(mSelTemp)
ENDSCAN
DO Efile WITH "Temp" && **
mWasOpen = mWopen &&
Restore original value.
DO
SelArea2 && **
RELEASE mThisDbf
RETURN
* EoF: MacroMan.prg
2 Dialogue July 1990 dBASE IV
Dialogue
Questions and Answers
Getting Carried Away
Q: I was using the Carry forward option on some fields within my
format file but then decided that I didn't want them to do so. So I
modified the format file and turned off that option on the fields I
had previously set. After saving, I returned to the Control Center
and proceeded to append. The fields were still carried forward. I
had to exit dBASE IV and return before I could get the fields to stop
carrying. What happened?
A: The SET CARRY command is an additive command. So when the new
format file issues the SET CARRY command for the new set of fields, it
did not clear the previous ones from memory, only adding to the list.
Exiting and returning to dBASE IV cleared this setting. However,
closing the .dbf should do the trick. If you are at the dot prompt,
you could enter the command SET CARRY TO at the dot prompt to clear
the setting. Going to the Tools: Settings menu to turn the Carry
setting OFF will not be sufficient.
Traceback Info
Q: While in the Debugger, pressing a P will show the program
traceback information. Is there a command to display the same
information which can be typed at the dot prompt? (When this happens
during the debug process, most of that information gets overlaid by
any existing windows or popups.)
A: Unfortunately, there is not a command to show the traceback
info from the dot prompt and continue running the programs. The info
is shown if you do a cancel, but as you mentioned, it can gets
overwritten depending upon elements affecting the current screen.
Furthermore, attempts we made to route traceback information to a text
file by triggering a SET PRINT TO FILE via an ON ESCAPE or ON ERROR
command provided unsatisfactory results.
Variable Naming Conventions In an .FRG File
Q: I am delving into the modification of an .frg file and am
confused by some of the variables created and used there. Could you
shed any light on naming conventions and their origin?
A: The list is quite extensive and beyond the scope of this
section. However, there is a methodology you should be familiar with
when delving into the code files of labels, reports and formats.
The first letter in the name of the variable is either g or l for
global (public) or local (private). The second letter is the data
type of the variable: c,n,d,f,l. The third character is always an
underscore. From there on, if you're good with mnemonics, you might
decipher the rest of the variable name.
If you're designing your own application or modifying an existing .frg
file, you might consider using other memory elements as well, such as
a for array, w for window, and so on, keeping in form with the naming
structures used.
Further expounding on the specific purpose of .frg generated variables
can be found in the April 1990 edition of TechNotes/dBASE IV in the
article Anatomy of a Report Form.
Pass the Variables, Please
Q: Is it possible to pass a parameter to a program from the DOS
level by entering
DBASE WITH
A: With the syntax you provided, the answer is no. However,
there is a way of parameter passing from DOS into dBASE IV. By making
use of DOS environmental variables, a simple DOS batch file, and the
GETENV() function in dBASE IV, you can easily pass parameters to a
dBASE III PLUS or dBASE IV program.
Create a batch file called DBPROG (a name used for this example but
can be any valid DOS filename you choose) that contains the following
statements:
@ECHO OFF
SET VAR1=%1
DBASE/T
SET VAR1=
CLS
The @ sign preceding the ECHO OFF statement is applicalble only to DOS
version 3.3 and higher. See the article entitled In Search Of. in
this issue for further examples of DOS parameter passing.
Include the following line in your dBASE program:
mvar1 = GETENV("VAR1")
Execute the batch file by typing the name of the file followed by the
parameter you wish to pass at the DOS level:
DBPROG
By using the replaceable parameter capabilities of DOS, one can pass
multiple parameters (up to nine) to a program.
QBE Calculated Field Nesting
Q: When designing a Query, I am getting the message Variable not
found when I try to use a calculated field in another calculated
field. Is there some limit on nested calculated fields?
A: Unlike in a report design, one cannot use a calculated field
in another calculated field in the QBE. It's still possible to get
your results if you expand the entire expression of the first
calculated field inside the second one. For those long-winded
expressions that would be a chore to retype, make note of the answer
to the next question below.
Getting "Snippy" About Cut and Paste
Q: Please say I've overlooked something. I am knee deep in a
massive design screen project that contains many calculated fields
with complex and lengthy expressions. Is there no way to copy an
expression from one field to another? If so, can you copy an
expression tested at the dot prompt into an expression area like a cut
and paste buffer could perform?
A: Pressing F8 COPY will copy a field and it's contents to a new
field. Copying a specific expression to another existing calculated
field is not a feature presently available in dBASE IV. A new product
called Trading Places, features a cut and paste capability. The
software's main functionality is for swapping TSR (RAM resident)
programs in and out of memory, another handy capability to have with
dBASE IV. The software is now available for $99.95 and can be ordered
through Customer Service. You may also obtain this product from
your local software vendor.
3 dBASE File Recovery July 1990 dBASE IV
This article is reprinted from the July 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.
No one wants to think about it. No one wants to admit that it can
happen to them. Every time that you quit a dBASE IV session, you see
that foreboding reminder to make regular back-ups of your data, but
you just don't make the time to do it. You could have bought a backup
utility program, but you couldn't justify the expense. And so,
benignly, you whiz along through months or maybe years without a care,
placing your trust and countless hours of work in the enigmatic world
of well-ordered magnetic blips. Until one day you need.
dBASE File Recovery
Joe Stolz
As a support technician, I can't count the number of times have I
heard someone tell me that they can view and edit their data in their
files, but at some point they receive the Record out of range
message. How many have reported seeing Not a dBASE database when they
merely try to place their file into use? There are those who can see
their data, but it just doesn't show up in the right fields anymore.
First names are in the last name slots. Last names are in the address
field. The data is there, just shifted to the wrong location. Then
there are those unfortunates who used to have data, but the file and
all its contents are gone. Perhaps they zapped the file
unintentionally or maybe they deleted too hastily. Finally, there are
those exposed to the dreaded case of having the computer lock up in
the midst of an edit operation. They were left with the throat-drying
experience of rebooting their computer to discover the file has become
an evil entity that is thoroughly uncontrollable and unreliable.
Yet, as often as it seems I heard one woeful tale after another, it is
ironic but true that data corruption is a pretty rare thing, and
fortunately so. However, when it does occur, you are not always
prepared for the consequences, and let's face it, the
ever-consciencious group that keeps frequent, up to date back-ups are
in the minority. Sooner or later, it's going to happen to you. What
will you do? You may not be surprised to know that there are several
products on the market that can solve your data loss problems. Our
own contribution, dBASE File Recovery, is admirably suited to the task
of recovering lost, damaged or sick database files.
The program was created by Keith Mund, a former Software Support
Technician at Ashton-Tate. Who else could be so steeped in corruption
(of files that is) to understand that the world would be a much
happier place if only damaged files could be salvaged? Keith is
intimately aware of the most common types of file corruption. He also
knows the painfully complicated techniques required that were formerly
the only means to salvage data from these files. The only tools that
were available were DOS commands such as DEBUG and COPY to find and
extract lost data and Borland's Sidekick to aid in the hexadecimal
arithmetic calculations to make heads or tails of it! Beyond that, he
chose to develop a data recovery methodology that would work against
the most common types of corruption such as end of file markers,
shifted data, damaged or corrupted records, and against the most
dreaded cases of all, records that don't exist as a result of using
ZAP or DELETE. He came through with flying colors.
The basis of dBASE File Recovery salvaging techniques are simple and
direct; identify text data that matches the pattern of fields as
specified in the header of your damaged database (the first portion of
the file) and copy it to a new file. dBASE File Recovery never
touches or modifies a damaged file. It always copies the recovered
data into a new file. If the recovery was successful you can delete
the damaged file and return to your work. Often, though, you may find
that the recovery was not complete. You may have to change things and
try to collect more data with a second pass on the damaged file. As
an experienced file recovery jockey, I really appreciate this extra
margin of safety afforded me by dBASE File Recovery. Let's discuss
some specifics of the program.
Going, Going, Gone
When a file is deleted explicitly by zapping or erasing, the data is
not erased from disk, at least not immediately. The area that was
allocated for the file is now open territory for other data. It's no
longer protected by its file handle and can be overwritten with the
next disk I/O activity. It is strongly advisied to discontinue using
your computer the moment you realize you've erased valuable data or
that something is missing. The likelihood for a fuller recovery will
be increased .
General Usage
The dBASE File Recovery screen has a look and feel similar to dBASE
IV. There is a status bar on the bottom, a menu along the top with
pull down menus. Data is displayed on the screen in a manner much
like a dBASE edit screen.
dBASE File Recovery has only a few menu options: File, Position,
Option and Exit. The File menu is the first one used. It is where
you specify the name of your damaged file.
I'd like to point out at this juncture that knowing what is wrong with
your file is absolutely crucial in the safe and effective recovery of
your data. As pointed out in the dBASE File Recovery manual right off
the bat, assessing the damage is the most important first step in
recovering your damage. Chapter 3 is dedicated to explaining the
types of problems that can arise and what needs to be done to recover
your data for every given problem.
Once you know what your plan of attack is, you are ready to start the
program. The first stumbling block for someone who uses dBASE File
Recovery without reading the manual (don't we all?) is, "What is the
difference between the Database filename, the Output data to and the
Input data from prompts in the File menu?" The screen shot at right
shows the choices available from the File menu. The Database filename
prompt is where the name of your damaged file is entered, if it is
usable, and if its field structure is undamaged. If you USE your file
and dBASE IV tells you that the file is Not a dBASE database, you will
need to supply a file name of a non-damaged file having the exact same
file structure, or you will have to work out the file structure using
the other File menu option, Modify structure.
The next menu choice is the Input data from option. This is,
obviously, the name of the file that contains the data that you need
to recover. As a safety feature, dBASE File Recovery always collects
data from damaged files and saves them in a new file. If you have
deleted or zapped your data, you will have to save your data to a
floppy drive, or some other drive since the source disk is completely
tied-up into a temporary file so that no data can be accidentally
overwritten.
What confused me the first few times I used the dBASE File Recovery
was that the Database filename was shown as *DEFAULT*, a structure
that can be modified to match your data structure if you have lost the
structure portion of your file. I thought that this had to be my
choice. I didn't realize that I could specify my file as the Database
filename, and that the Input data from option would automatically take
on the name of my file. I also found that pressing F9 allowed me to
navigate through my directory and locate the desired file.
In the case of a deleted or zapped file, the Input data from filename
can be the special names *DISK* or *FREE*. *DISK* is the name of a
special file to be created from the contents of your entire disk. If
your data is strewn around, perhaps even imbedded inside other files
on your disk, *DISK* will make them available to dBASE File Recovery.
The *FREE* option is similar, but only the free, unallocated space on
you disk is collected temporarily into a file to be searched for your
data.
The Output data to option tells dBASE File Recovery into what file to
place your data. Sometimes you will find yourself saving portions of
the recovered file to a series of files which can later be regrouped
into one file. You do this most often with a file that contains
shifted data.
In most cases, once you have set up your file names in the File menu,
you are ready to collect your data. If all goes well and the damage
is minimal, in just a few minutes, you will be done!
Getting Away From Automatic
I found the recovery process so painless and accurate that I thought I
was destined to become the superstar of data recovery. However, one
day I noticed that I was missing a lot of data during the process. I
had set the program into Automatic mode. This mode, set in the Option
menu, directs dBASE File Recovery to automatically recover your data,
but it skips over any problem records. I don't mind having slightly
damaged records, it's better that way than having to guess at which
records I've lost. I recommend that you remain in the default mode
which is called Write. Write mode causes a continuous writing of all
good records. It stops when a bad record is hit. You can skip the
record, or choose to save it too. Ninety-nine percent of the time I
save these kind of damaged records. They are noted and kept as a
"placeholder" of sorts that I can correct or re-enter later.
dBASE File Recovery replaces "garbage" characters (like NULL or EOF
characters) with a special ASCII character (176) which appears as a
grainy block to show you where your damage was. You should delete
these characters when you are done recovering your file.
When you are in the regular edit mode, you should see the first record
in your file when the file is placed into use. If you are not able to
see it, don't panic. Rather, press the PgDn key or any of the other
cursor movement keys. As you do, you will find yourself moving
through your file. You should see your data shortly. The arrow keys
allow you to shift your data through your database structure a byte at
a time. This is how you can manually adjust shifted data in your
file. If the data is shifted to the right a few characters, you can
easily shift it back and start the saving process. In addition, the
F3 and F4 keys jump the data by the number of bytes in a record which
can be manually adjusted to any number of bytes. The F5 and F6 keys
advance the pointer forward or backward 10 bytes at a time.
Looking for records from the Position menu
An easier way to find your first or next good record, is through the
Position menu. The Look for a good record option automatically
searches and aligns data to your structure. The way this is done is
simple, really. In dBASE IV, character fields are left aligned, flush
left within their field. Numeric fields are right aligned. Dates are
eight bytes long, and logical fields are one character, T, F or
blank. Given these obvious characteristics, dBASE File Recovery makes
a simple assumption: If a character field has a leading blank space
(i.e. it is not flush left) something is wrong. The same is true of
the positioning of data within fields of the other data types. When a
field appears to be skewed, dBASE File Recovery highlights this
field. If the data fits the pattern of both your file's data
structure and of the position of the data within these fields, no
highlighting occurs. When you page through your data, anything out of
the ordinary shows up clearly. If it so happens that your character
field is really supposed to have leading blanks, and you don't want
this to be flagged as an error, the requirement for justified text can
be set off in the Option menu.
Pattern matching
The pattern matching process is very fast and reliable. If your
records fit the pattern, you simply whiz along. If a record is
slightly off, you screech to a halt (if you are in Write mode) to
decide whether the data is intact or not. Data that would be in a
date field is shown in it's true form, as an eight digit numeric
field, with the format CCYYMMDD (first century, then year, then month,
then day). No delimiters such as a slash or dash will be present.
This may look unfamiliar to you, but rest assured that the dates will
be shown correctly when dBASE IV interprets them.
Looking at your data
Another useful feature is the Dump data to screen option in the
Position menu. If you have a file that you suspect contains data, you
can set up the file as your Input data from file. Then move to the
Position menu and try the Dump data to screen option. The contents of
the file roll across your screen. This can show you whether any data
worth collecting exists in the file. If you had some type of mishap,
had to reboot your computer, then found that you had lost clusters
when you ran CHKDSK, you can use this technique to view the files
recovered from these lost clusters to determine whether they contain
your data. It's a quick way to evaluate the file.
The more difficult procedure of divining the file structure of a file
that has a bad header and for which no backups exist is described
well. I have used dBASE File Recovery many times for this purpose.
It's a tedious procedure, but you can see rapid results nonetheless.
The Things You Can Do.
Let me emphasize that you should not underestimate the usefulness (not
to mention power) of dBASE File Recovery. There are quite a number of
menu options that make this program very functional and versatile.
You can search for specific data that you recall was once contained in
the file, allowing you to locate specific records. You can mark
records with a tag and move from tag to tag in the file. You can set
the default drive and directory. You can even run DOS commands while
in a File Recovery session.
The documentation is packed full of a ton of useful information.
There is even an appendix on getting data from disks that DOS
rejects. That, alone, is worth it's weight in microchips. All in
all, this is a great program. It will undoubtedly reunite many lost
files with their anxious owners. Here's wishing you a speedy
recovery!
dBASE File Recovery can be used on all types of .dbf files including
those from dBASE IV SQL and dBASE III. dBASE II files are not
compatible for this software.
4 Cat Catcher July 1990 dBASE IV
This article is reprinted from the July 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.
Cat Catcher
Leah Boselli
So you can't quite collect all of your .cat files into one Catalog.cat
in the main directory? For those who like to keep all of their
various files in separate subdirectories but would like to access them
through the dBASE directory, this can pose a few problems. One
problem surrounds the fact that when files are stored to a catalog and
that catalog is in the same directory as the files, then the path is
not stored with the filename. You can always add the files to the
current catalog one at a time through the Control Center but this can
be a redundant nuisance if you have a lot of files. If you add the
name of the catalog to the main Catalog.cat file when it uses the
catalog, it sees the files but assumes they don't exist because they
are not located in the current directory and their isn't a path
leading to them. So in turn, dBASE IV will delete the files out of
the catalog.
The following program will prompt the user for the path and then go to
that directory and open it's Catalog.cat. Using the Catalog.cat as
the actual .dbf file that it is, it then goes through each record
opening up the various catalogs within it and then, using the STUFF()
function, inserts the path into the path field of the catalog files.
After doing so for each catalog, it then increments the code field to
correlate with the code field of the main Catalog.cat in the dBASE IV
directory. The Catalog.cat file in the dBASE directory is then opened
and the newly modified version from the subdirectory is then appended
into it. Now all catalogs from the subdirectory and the files
included within it will have correct paths and can be accessed from
the main directory.
Remember that you are providing the name of a catalog file in another
directory. The Catalog.cat file will already have this catalog name
referenced, but not the path. This means that, unless you actually
change the directory or start dBASE IV from that directory, that path
wouldn't, under normal circumstances, be available. That's where
CatChase.PRG comes in. By explicitly providing information on where
the catalog is, you can more easily summon those catalogs from the dot
prompt or Control Center.
To physically change directories while in dBASE IV, use the RUN CD\..
command from the dot prompt or the Tools: Dos Utilities: DOS: Set
default drive option from the Control Center
* PROGRAM...: CatChase.PRG
* AUTHOR....: Leah Boselli
* Date......: April 12, 1990
* Versions..: dBASE IV 1.0
* Notes.....: Modifies CATALOG.CAT and adds the path name to catalogs
that
* may exist in other directories.
*--- Set commands
SET TALK OFF
SET ECHO OFF
SET CONFIRM ON
SET ESCAPE OFF
*--- Error checking routines
ON ERROR DO Errprg
ON ESCAPE DO Errprg
* First, open CATALOG.CAT in the current directory
SELECT 1
USE CATALOG.CAT
* Get the highest number in the code field
CALCULATE MAX(Code) TO mcode
mpath = SPACE(30)
m_ok=.T.
myes=" "
CLEAR
DO WHILE m_ok
* Get the correct path where the files are located from the
user and do
* error checking routine
@3,5 SAY "Enter path where files are located: " GET mpath ;
VALID Ckpath(mpath)
@5,5 SAY "Example: C:\dbase\files\ "
READ
* Initialize the mfile with the path for the CATALOG.CAT file
to be
* added and check for existence of CATALOG.CAT
mfile=TRIM(mpath)+"CATALOG.CAT"
CLEAR
* Check to see if CATALOG.CAT exists
IF .NOT. FILE(mfile)
@10,5 SAY "There isn't a CATALOG.CAT in that
directory"
@12,5 SAY "Would you like to reenter the path? (Y/N) "
GET myes ;
PICTURE "!" VALID myes $ "YN" ERROR "Enter Y
or N"
READ
IF myes = "N"
CLOSE ALL
RETURN
ELSE
mpath = SPACE(30)
LOOP
ENDIF
ENDIF
m_ok = .F.
ENDDO
* Use the CATALOG.CAT in the subdirectory and go to first record
SELECT 2
USE &mfile
GO TOP
* This loop will get the filename from the record, then use that file
* and replace the filename to include the correct path until the end
* of the file
DO WHILE .NOT. EOF()
*--- Check to see if the path is already included
IF SUBSTR(mpath,2,1) = ":"
EXIT
ELSE
mrec=TRIM(mpath) + Path
ENDIF
* Use the first file in the Catalog
SELECT 3
USE &mrec
GO TOP
* Check to see if the path is already included, if not, add
the path
* and continue to next file
IF SUBSTR(Path,2,1) = ":"
EXIT
ELSE
REPLACE ALL Path WITH STUFF(Path,1,-1,TRIM(mpath))
ENDIF
SELECT 2
SKIP
ENDDO
* Increment the code field by adding the current code to the highest
* code found in the original CATALOG.CAT
REPLACE ALL Code WITH Code + mcode
CLOSE ALL
* Add the catalogs to the CATALOG.CAT file
USE CATALOG.CAT
APPEND FROM &mfile
*--- Close all files and exit
CLOSE ALL
RETURN
FUNCTION Ckpath
PARAMETERS m_path
* Test to be sure a slash was entered at the end of the path
is_ok = IIF(SUBSTR(m_path, LEN(TRIM(m_path)), 1) = "\", .T.,
.F.)
RETURN(is_ok)
PROCEDURE Errprg
SET BELL TO 1024,10
? CHR(7)
CLEAR
@10,10 SAY "AN ERROR HAS OCCURRED --- PRESS ANY KEY TO EXIT
PROGRAM"
i = INKEY(3)
IF i <> 0
CLOSE ALL
CLEAR ALL
CLEAR
CANCEL
ENDIF
RETURN
* EoF: CatChase.PRG
5 It's .bin Fun July 1990 dBASE IV
It's .bin Fun
Erik McBeth
You might have seen Curson.bin or GetDriv.bin in your samples
directory and wondered, "Just what are "bin" files anyway?" Or
perhaps you have used them and were curious about how they were
created. In either case, this article should give insight into the
wonderful world of .bin files and their creation using the language
simply known as C.
Binary, or .bin, files are assembly language files that can be loaded
into memory from within another program and then executed. Not to be
confused with programs which are executed via the RUN command from
inside of a dBASE IV program or from the dot prompt, .bin files almost
become part of the dBASE IV program itself and have the luxury of
speed because they are only loaded once into memory and from there,
they can be executed quickly. Conversely, programs which are RUN from
the dot prompt must be loaded each time you want to execute them.
dBASE IV has two special commands that it uses when handling .bin
files: LOAD and CALL. The LOAD command does exactly what you'd think
it would do, it loads the .bin file into a special area of memory that
is reserved for this purpose. The CALL command (and the CALL()
function) runs the .bin file with the parameters you supply, (that is,
memory variables, strings, and so on). These parameters are placed in
a special place in memory so that the .bin file can find and act upon
them. You can pass up to seven parameters.
Interfacing dBASE IV with C
Well you might have come to the conclusion that since we're loading
assembly language routines into memory that we are forced into using
that cryptic computer language known as Assembly with all its strange
abbreviated commands. It's not quite that bad. Instead, you'll be
shown how to write programs in the somewhat less cryptic language of
C.
The C language has been around for about 20 years or so. It
originally grew out of a research project and was subsequently used in
the writing of the UNIX operating system. Numerous companies make C
compilers including Borland International and Microsoft and the price
for a compiler has come down substantially in the last few years. Add
to this the large library of reference material available on C and you
can see that C is a good choice for a language we can use to create
our .bin files.
Those familiar with C might be saying "Well great, I'll just convert
my 8000 line directory management C program into a .bin file!"
Wrong! If you have this great program just run it from the dot prompt
with the RUN command. Speaking from personal experience, most
programs written in C probably can't be turned into a .bin file or, at
worst, would take so much rewriting and dipping into assembler that it
wouldn't be worth the effort. Most C functions supplied by the
manufacturer of the compiler (printf for example) probably won't work
in a .bin file. All I/O (input/output) operations for the most part
must be handled in assembler.
If you need to do file, string and math operations (division and
multiplication of long integers, for example), you might check out the
Framework III Developer's Toolkit. The Toolkit includes the source
code to many popular C string functions as well as assembly code
functions that allow for file access (open, read, write, etc.) from
inside of .bin files. By using these routines, you just might be able
to turn that directory manager into a .bin file!
Reversing Character Strings
Now let's do a more in-depth study on how we put together a C file.
We'll look at two sample C files to get an idea of what is involved
when constructing C code which will eventually be used inside of dBASE
IV. The first file, StrRev.C, reverses the order of the letters in a
string. The second one, StrDct.C, is used in instances when you want
to place data in true dictionary order (more on "dictionary" order
later). This example illustrates how multiple parameters to a .bin
file can be passed by referencing an array that contains up to seven
parameter addresses. For this example, we keep it simple and just use
the first address parameter in the array. The hooks are in there to
allow for expansion to more parameters.
Let's start with StrRev.C (a function which reverses the characters in
a string) because of its simplicity. We'll try to dissect this C
program as much as possible so as to get a better understanding of all
that is involved in making .bin files. Once you have a pretty good
understanding of the various sections of a C file you can easily take
this code and substitute in your own function in place of StrRev().
In developing the examples for this article, two C compilers
(Borland's Turbo C 1.5/2.0 and Microsoft's 5.1 C compiler) and two
assemblers (Borland's TASM 1.0 and Microsoft's MASM 5.1) were tested.
Various other compilers should work as long as they produce DOS
compatible object files. LINK.exe (or the Link program that comes
with your C compiler) and EXE2BIN, two well known DOS utilities, will
also be needed in order to construct the examples.
Creating an .exe file
We start out by defining a constant known as EXE. Often times, it is
easier to test your .bin file by first turning it into a small program
that can be run from the DOS level; a file known as an executable
file. In our StrRev example, we have constructed the file in such a
way that if the constant EXE equals 1, we will then get our string
parameter from the DOS prompt and print out its reverse using the C
function printf() (somewhat like the dBASE IV ? command). Since the
size of your C program and other memory limitations can be weighing
factors, there is no guarantee that your .exe program will run as a
.bin file in dBASE IV, but at least you'll feel a little more
confident of your coding.
Next we "#include" the header file known as Strlib.H which contains
various definitions that we'll be using in our program, most notably
the register variables BX, CX, and so on. Our Strlib.H file also
contains a C macro called MK LONG() which is critical to the success
of the bin file creation. If we were constructing an .exe file
instead of a .bin we would also need to read in the header file known
as stdio.H. Our "#if EXE" statement would take care of this
automatically.
The next few lines deal with what would be required to make our .exe
file so it could run from DOS. I'll leave out the details of what
these lines are doing so we can quickly jump into what we're here for:
.bin file creation.
Starting after the "#else" statement we see:
void far main()
This is declaring the main starting point of our program. It is very
important that we declare main() in this way. The use of the keyword
"far" causes the compiler to issue a far return (RETF) at the end of
the program. This type of return is needed so that dBASE IV can jump
back to what it was doing before it called your .bin file. Some C
enthusiasts may have noticed that we are declaring our main function
as "void," which usually means we don't want to return anything to the
function/program that called the routine. In this case, we are not
returning anything, just jumping back to dBASE.
1010 Memory Lane
In the next three lines of code, we grab the string that dBASE IV has
passed to the .bin file. dBASE sends a string to a .bin file by
passing the address of where the string is stored in memory.
Memory addresses are composed of two parts, a segment and the offset
into the segment. Think of this as a street and an address number.
The street is the segment, and the offset is the address number. The
segment address of the string is passed to the DS register and the
offset to the BX register.
Registers are special memory locations within the computer's processor
that programs use to pass information back and forth. In our .bin
file, we cannot directly access the various registers so we need some
kind of bridge which will allow us to do so. The assembly language
procedure Getregs() does just this. It takes the various registers
(DS, BX, and so on) and copies them into variables that the C program
can use. The DS and BX registers that you see in StrRev are actually
program variables. Our MK LONG macro then takes these two
variables/registers and converts them into a memory address that the C
program can use. This address is then assigned to our variable "p"
which can then be processed by a string function such as StrRev(). If
all of this sounds complicated just remember that all you really need
to do in the future is copy in these first few lines into your own
.bin file and you'll be set!
Well, the hard part is over. All we need to do now is write the
string function that we'll be using with our "p" variable. In this
case we have the StrRev function but you could just as easily have a
function which selectively alters upper or lower case, opens access to
non-dBASE files or even encrypts it for security. Use your
imagination. Our StrRev function reverses the order of the characters
in a string by exchanging characters from the two ends until they meet
each other in the middle. Any changes we make in our string function
will be reflected in the string that is returned to dBASE IV. For
this reason do not shorten or lengthen the string in any way because
unpredictable results might follow.
Sorting in Dictionary Order
We've looked at a rather simple example of a C program that we can use
to create a .bin routine. Now lets look at a slightly more
complicated example which has the ability to pass multiple parameters
(a maximum of seven) to a .bin file. Our example will still only use
one parameter though, just to keep it simple. The example in question
is StrDct.C, a rudimentary translating function which we can use to
place a data file in "dictionary order".
We start out the C program in much the same way as our previous
example. We "#include" some information and make sure that we
generate a far return by declaring the main() function "void far
main()". We then declare two variables, argc and argv (seasoned C
programmers will no doubt recognize these old standbys). Argc will
contain the number of parameters passed plus one, and argv will be an
array containing the parameters with the first parameter being in
argv[1]. These values are in keeping with more traditional non-bin
programming.
Remember when we talked about memory registers and how dBASE IV
communicates with .bin files? dBASE IV can talk with .bin files in
either the traditional dBASE III PLUS way (through the DS:BX register
pair) or in its extended mode which allows multiple parameters and a
special register to tell us how many actual parameters were passed.
With this flexibility we can even write .bin files which take a
variable number of parameters, something we can't do in UDFs.
dBASE III PLUS passed its parameter into a BX register. Since dBASE
IV passes an array or table of parameters into a ES:DI register pair,
an additional parsing routine in the C program is required.
Multiple arguments are accomplished in dBASE IV by sending our .bin
file the address of a table in memory which in turn contains the
individual addresses of our parameters. You can think of this as an
usher who directs us to the betting windows at a horse track. Once
we find the betting area, we only have to go up to a specific window
to place our bet or get our parameter. dBASE passes the address of
the "betting windows" in the ES:DI register pair and the number of
windows/parameters in the CX register. We learned earlier of segment
and offset and how they pertain to memory addresses. Each betting
window is four bytes away from its neighbor with the address of the
first window starting at zero. Thus, the address of the first window
is in the ES segment with an offset of DI plus zero and its neighbor
has an offset of four greater (DI plus four). You see, you don't
really have to understand everything about .bin files in order to make
and use them.
After we assign memory locations to the elements in argv we are ready
to call our StrDct function. We first check to see that the value of
argc is greater than one (remember that the ARGument Count is one
greater than the number of parameters passed by dBASE) so that we know
that we have a string to play with before calling StrDct(). StrDct
rearranges the sorting sequence of letters from normal alphabetical
order (which is used by dBASE IV) to dictionary order. Dictionary
order is generally defined as an order in which special symbols (#, &)
are placed first, then letters and then numbers, much like a
dictionary. This dictionary sequencing is borrowed from Framework
III, which places words in this order instead of the ASCII ordering
which dBASE uses by default (some special symbols, then numbers, then
letters). The ordering presented in StrDct is NOT case sensitive;
upper case does not necessarily come before lower case.
We should now examine how we turn our completed and presumably tested
C programs into .bin files. I'll demonstrate the construction process
with the two compilers and assemblers from Borland and Microsoft;
other compilers/assemblers should be fairly similar. Remember,
besides the assemblers and compilers, you will need LINK.exe and
EXE2BIN.exe, both of which come with most versions of DOS. The
process will involve:
1. Compiling the C program to an object file.
2. Assembling the Getregs.ASM file to an object file.
3. Linking these two object file into one .exe file.
4. Running EXE2BIN on the resulting .exe file.
The specific steps for the two compilers mentioned are detailed
below:\
Borland Microsoft
1. ALT-F9 in editor cl -c.C
2. TASM Getregs.asm MASM Getregs.asm,,,,
3. LINK+Getregs,,,,
4. EXE2BIN
The filename reference would be either StrDct, StrRev,
or StrFlp and you should end up with a file like StrDct.bin, and so
on. You can mix and match assemblers and compilers since they all
produce the same kind of object format. You'll know something went
wrong if either LINK gives an error (other than the Warning no stack
message) or EXE2BIN says, File can't be converted. All these examples
work so you shouldn't have any problems. One small note, if you use
TURBO C and set TURBOC to 1 in Strlib.H you can eliminate steps 2 and
3 above.
Running the Routine from dBASE IV
Now the real fun begins, we get to try out our .bin files in dBASE.
We'll look at three different ways that we can use our .bin files with
dBASE. All three ways can be mixed and matched with other .bin files.
Let's first use our simplest .bin file, StrRev, since it is fairly
easy to tell if a string has been reversed or not. From the dBASE IV
dot prompt, type
LOAD StrRev && loads StrRev into memory
x="John" && test string
CALL StrRev WITH x && call Strrev
? x && what is the value now?
You should have seen "nhoJ" displayed if everything went ok. Repeat
steps two through four with different values to make sure our STRing
REVersal is working.
We can go ahead and create a UDF that will CALL StrRev with whatever
string we gave it and then return the reversed string, but let's not.
Instead, let's create a UDF called Strflp() which calls our StrFlp.bin
file. StrFlp is a .bin file which "flips" ASCII characters. Those
characters which have an ASCII value of 127 and below will have the
value of 128 added (the letter 'A' which has a value of 65 would end
up with a value of 193) and those above 127 will have 128 subtracted
(160 becomes 32 or the space character).
Enter the dBASE IV text editor by typing MODIFY COMMAND from the dot
prompt. The code for our UDF is shown below.
FUNCTION StrFlp
* Strflp should have already been LOADed.
PARAMETER str
temp = str
CALL Strflp WITH temp
RETURN temp
After compiling this function, from the dot prompt, type:
? StrFlp("flip")
The result should look something like mq . Remember that StrFlp
flips the value of the characters in a string so that characters which
use to come at the start of the ASCII table now come at the end and
vice-versa. If we do something like:
? StrFlp("mq")
we get "flip" back again since now the characters at the end of the
ASCII table are transposed into characters at the beginning.
Note that on page 9 is a section of code with extensive repetitive
array assignments. At first glance, it would appear as if this could
and should be done more concisely and programmatically. Indeed, this
is the approach you would take if you were creating a stand-alone .exe
program. Unfortunately, this approach does not work when constructing
.bin files. The reason is that most C compilers place array tables
into the uninitialized data segment, also known as the BSS. dBASE IV
could not consistently deal with data in the BSS and would return bad
values intermittently. Therefore, it was necessary to create a table
on the fly by assigning values to the array elements explicitly.
The program is available on the Ashton-Tate BBS if you wish to
download it.
Now for our final example involving StrDct and putting database files
in dictionary order. One of the advantages of .bin files is that they
have the capability of being used for indexing via the CALL() function
(as opposed to CALL command) in dBASE IV. If we have a database file
that we decide we want in dictionary order, or StrRev order, or StrFlp
order, all we need to do is the following:
USE Datafile
LOAD Strdct
INDEX ON CALL("Strdct",fieldname + "") TO Indexfile
dBASE IV will then call StrDct for each record so that it can place
our data in dictionary order. It is important to remember that
whenever we use this datafile and index in the future to LOAD StrDct
first. This indexing feature works well with StrFlp also. Suppose
you wanted to put your file in order by last name plus first name but
the first names needed to be in descending order, you could try this:
LOAD StrFlp
USE Namefile
INDEX ON lastname+CALL("StrFlp",firstname+"") TO Lfname
This puts the last names in ascending order but the first names in
descending order. You might be wondering why we need to add the + ""
to the firstname field. This prevents dBASE IV from permanently
changing the contents of the firstname field to whatever StrFlp would
return.
Well that about wraps up our journey into the world of C and dBASE
.bin files. I hope you've seen that .bin files are really not that
complicated once you understand a few of the guidelines and principles
involved in their creation. .bin files provide for useful and
powerful extensions to the dBASE language that in turn allow
programmers to develop and code more extensive and powerful programs.
StrRev.C
/* Program ...: Strrev.C
Author ....: Erik A McBeth
Version ...: dBASE III Plus 1.0, 1.1
dBASE IV 1.0, 1.1
(Tested compilers/assemblers)
Turbo C 1.5, 2.0 TASM 1.0
Microsoft C 5.1 MASM 5.1
*/
#define EXE 0 /* Set to 1 if we want to create an exe, If
we have an exe then run from DOS like this
STRREV string and you'll see your string
reversed */
#include "strlib.h" /* Various definitions */
#if EXE
#include "stdio.h"
main(argc,argv)
int argc;
char *argv[];
{
unsigned char *p;
p = (unsigned char *)argv[1];
#else /* Creating BIN file */
void far main() /* Need a FAR return */
{
unsigned char *p;
Getregs(); /* Get our memory registers */
p = (unsigned char *)MK_LONG(DS,BX); /* Get our string */
#endif /* If EXE */
if (p)
Strrev(p); /* Same operation if we do a bin or an exe */
#if EXE
printf("%s\n",p);
#endif
}
Strrev(str)
unsigned char *str;
{
unsigned char *p=str,ch;
/* Go to the end of the string and stop */
while(*++p);
/* Now swap the letters until we come to the middle of the string
*/
for(--p;p>str;p--,str++) {
ch = *p;
*p = *str;
*str = ch;
}
}
StrDct.C
/* Program ...: Strdct.C
Version ...: dBASE III Plus 1.0, 1.1
dBASE IV 1.0, 1.1
(Tested compilers/assemblers)
Turbo C 1.5, 2.0 TASM 1.0
Microsoft C 5.1 MASM 5.1
*/
#include "strlib.h"
void far main() /* very important, make sure we get a far return */
{
/* Tried to make this look familiar to 'C' programmers, Notice the
use of argc and argv, I've set argc to have a value of 2 to
simulate the routine being called from the DOS prompt */
int argc;
unsigned char *argv[6];
Getregs(); /* Assign memory registers */
/* Translate the parameter passed by dBASE IV into something we
can use, The argv[2] and argv[3] are placed here to show
you how to read multiple parameters */
argc = CX+1; /* Number of arguments */
argv[1] = (unsigned char *)*((int *)MK_LONG(ES, DI + 0));
argv[2] = (unsigned char *)*((int *)MK_LONG(ES, DI + 4));
argv[3] = (unsigned char *)*((int *)MK_LONG(ES, DI + 8));
if (argc>1) /* Do we have a string to use? */
Strdct(argv[1]);
}
Strdct(str)
unsigned char *str;
{
/* Had to do it this way, couldn't do "trnslt[]={" */
/* Read from AMENG.SO (Framework III). Table is case insensitive
*/
static unsigned char trnslt[256];
trnslt[ 0]= 0; trnslt[ 1]= 1; trnslt[ 2]= 2; trnslt[
3]= 3;
trnslt[ 4]= 4; trnslt[ 5]= 5; trnslt[ 6]= 6; trnslt[
7]= 7;
trnslt[ 8]= 8; trnslt[ 9]= 9; trnslt[ 10]= 10; trnslt[
11]= 11;
trnslt[ 12]= 12; trnslt[ 13]= 13; trnslt[ 14]= 14; trnslt[
15]= 15;
trnslt[ 16]= 16; trnslt[ 17]= 17; trnslt[ 18]= 18; trnslt[
19]= 19;
trnslt[ 20]= 20; trnslt[ 21]= 21; trnslt[ 22]= 22; trnslt[
23]= 23;
trnslt[ 24]= 24; trnslt[ 25]= 25; trnslt[ 26]= 26; trnslt[
27]= 27;
trnslt[ 28]= 28; trnslt[ 29]= 29; trnslt[ 30]= 30; trnslt[
31]= 31;
trnslt[' ']=' '; trnslt['!']='!'; trnslt['\"']='\"';
trnslt['#']='#';
trnslt['$']='$'; trnslt['%']='%'; trnslt['&']='&';
trnslt['\'']='\'';
trnslt['(']='('; trnslt[')']=')'; trnslt['*']='*';
trnslt['+']='+';
trnslt[',']=','; trnslt['-']='-'; trnslt['.']='.';
trnslt['/']='/';
trnslt['0']='k'; trnslt['1']='l'; trnslt['2']='m';
trnslt['3']='n';
trnslt['4']='o'; trnslt['5']='p'; trnslt['6']='q';
trnslt['7']='r';
trnslt['8']='s'; trnslt['9']='t'; trnslt[':']='0';
trnslt[';']='1';
trnslt['<']='2'; trnslt['=']='3'; trnslt['>']='4';
trnslt['?']='5';
trnslt['@']='6'; trnslt['A']='7'; trnslt['B']='>';
trnslt['C']='?';
trnslt['D']='A'; trnslt['E']='B'; trnslt['F']='G';
trnslt['G']='H';
trnslt['H']='I'; trnslt['I']='J'; trnslt['J']='O';
trnslt['K']='P';
trnslt['L']='Q'; trnslt['M']='R'; trnslt['N']='S';
trnslt['O']='U';
trnslt['P']='['; trnslt['Q']='\\'; trnslt['R']=']';
trnslt['S']='^';
trnslt['T']='_'; trnslt['U']='`'; trnslt['V']='e';
trnslt['W']='f';
trnslt['X']='g'; trnslt['Y']='h'; trnslt['Z']='j';
trnslt['[']='u';
trnslt['\\']='w'; trnslt[']']='x'; trnslt['^']='y';
trnslt['_']='z';
trnslt['`']='{'; trnslt['a']='7'; trnslt['b']='>';
trnslt['c']='?';
trnslt['d']='A'; trnslt['e']='B'; trnslt['f']='G';
trnslt['g']='H';
trnslt['h']='I'; trnslt['i']='J'; trnslt['j']='O';
trnslt['k']='P';
trnslt['l']='Q'; trnslt['m']='R'; trnslt['n']='S';
trnslt['o']='U';
trnslt['p']='['; trnslt['q']='\\'; trnslt['r']=']';
trnslt['s']='^';
trnslt['t']='_'; trnslt['u']='`'; trnslt['v']='e';
trnslt['w']='f';
trnslt['x']='g'; trnslt['y']='h'; trnslt['z']='j';
trnslt['{']='|';
trnslt['|']='}'; trnslt['}']='~'; trnslt['~']=127;
trnslt[127]=128;
trnslt[128]='@'; trnslt[129]='a'; trnslt[130]='C';
trnslt[131]=':';
trnslt[132]='8'; trnslt[133]=';'; trnslt[134]='9';
trnslt[135]='@';
trnslt[136]='D'; trnslt[137]='E'; trnslt[138]='F';
trnslt[139]='K';
trnslt[140]='L'; trnslt[141]='M'; trnslt[142]='8';
trnslt[143]='9';
trnslt[144]='C'; trnslt[145]= 0; trnslt[146]= 0;
trnslt[147]='W';
trnslt[148]='V'; trnslt[149]='X'; trnslt[150]='b';
trnslt[151]='c';
trnslt[152]='i'; trnslt[153]='V'; trnslt[154]='a';
trnslt[155]=129;
trnslt[156]=130; trnslt[157]=131; trnslt[158]=132;
trnslt[159]=133;
trnslt[160]='<'; trnslt[161]='N'; trnslt[162]='Y';
trnslt[163]='d';
trnslt[164]='T'; trnslt[165]='T'; trnslt[166]='=';
trnslt[167]='Z';
trnslt[168]=134; trnslt[169]=135; trnslt[170]=136;
trnslt[171]=137;
trnslt[172]=138; trnslt[173]=139; trnslt[174]=140;
trnslt[175]=141;
trnslt[176]=142; trnslt[177]=143; trnslt[178]=144;
trnslt[179]=145;
trnslt[180]=146; trnslt[181]=147; trnslt[182]=148;
trnslt[183]=149;
trnslt[184]=150; trnslt[185]=151; trnslt[186]=152;
trnslt[187]=153;
trnslt[188]=154; trnslt[189]=155; trnslt[190]=156;
trnslt[191]=157;
trnslt[192]=158; trnslt[193]=159; trnslt[194]=160;
trnslt[195]=161;
trnslt[196]=162; trnslt[197]=163; trnslt[198]=164;
trnslt[199]=165;
trnslt[200]=166; trnslt[201]=167; trnslt[202]=168;
trnslt[203]=169;
trnslt[204]=170; trnslt[205]=171; trnslt[206]=172;
trnslt[207]=173;
trnslt[208]=174; trnslt[209]=175; trnslt[210]=176;
trnslt[211]=177;
trnslt[212]=178; trnslt[213]=179; trnslt[214]=180;
trnslt[215]=181;
trnslt[216]=182; trnslt[217]=183; trnslt[218]=184;
trnslt[219]=185;
trnslt[220]=186; trnslt[221]=187; trnslt[222]=188;
trnslt[223]=189;
trnslt[224]=190; trnslt[225]= 0; trnslt[226]=192;
trnslt[227]=193;
trnslt[228]=194; trnslt[229]=195; trnslt[230]=196;
trnslt[231]=197;
trnslt[232]=198; trnslt[233]=199; trnslt[234]=200;
trnslt[235]=201;
trnslt[236]=202; trnslt[237]=203; trnslt[238]=204;
trnslt[239]=205;
trnslt[240]=206; trnslt[241]=207; trnslt[242]=208;
trnslt[243]=209;
trnslt[244]=210; trnslt[245]=211; trnslt[246]=212;
trnslt[247]=213;
trnslt[248]=214; trnslt[249]=215; trnslt[250]=216;
trnslt[251]=217;
trnslt[252]=218; trnslt[253]=219; trnslt[254]=221;
trnslt[255]=222;
/* Go through the string and substitute the new value based on the
value of the old character */
for(;*str;str++) {
*str = trnslt[(int)*str];
}
}
StrFlp.C
/* Program ...: Strflp.C
Version ...: dBASE III Plus 1.0, 1.1,
dBASE IV 1.0, 1.1
(Tested compilers/assemblers)
Turbo C 1.5, 2.0 TASM 1.0
Microsoft C 5.1 MASM 5.1
*/
#include "strlib.h" /* Some definitions */
void far main() /* Need a FAR return */
{
unsigned char *p;
Getregs(); /* Get our memory registers */
p = (unsigned char *)MK_LONG(DS,BX); /* Get our string */
if (p)
Strflp(p);
}
Strflp(str)
unsigned char *str;
{
/* Go through the string and subtract character ASCII value from 255
*/
for(;*str;str++) {
*str = (unsigned char) (((int)*str+128) % 256);
}
}
StrLib.C
/* Program ...: Strlib.H
Version ...: Use with
Strdct.C
Strflp.C
Strrev.C
Header file which contains various definitions and information
on how functions are called.
*/
#define TURBOC 0 /* Set true if compiler supports "pseudoregisters"
like
Turbo C, this way you don't have to link in
getregs.obj */
#define MK_LONG(hi,low) (((unsigned long)(hi) << 16) |
(unsigned)(low))
#define isdigit(ch) (ch >= '0' && ch <= '9')
#define isspace(ch) (ch==' ' || ch=='\t' || ch=='\r')
/* These are function "prototypes", some compilers don't like
these so you can delete them if they give a problem */
int Strdct(unsigned char *str);
int Strflp(unsigned char *str);
int Strrev(unsigned char *str);
int Getregs(void);
unsigned DS, BX, ES, DI, CX;
#if TURBOC
#define Getregs() (DS=_DS, BX=_BX, ES=_ES, DI=_DI, CX=_CX)
#endif
; Program ...: GetRegs.asm
; Version ...: dBASE III Plus 1.0, 1.1
; dBASE IV 1.0, 1.1
; (Tested assemblers)
; TASM 1.0
; MASM 5.1
; File loads global variables with the values of the actual memory
registers.
_DATA SEGMENT PUBLIC 'DATA'
EXTRN _BX:WORD ; These variables are in 'C' file
EXTRN _CX:WORD
EXTRN _ES:WORD
EXTRN _DI:WORD
EXTRN _DS:WORD
PUBLIC __acrtused,__chkstk ; Microsoft C needs to find these
__acrtused = 9876h
__chkstk = 0
_DATA ENDS
DGROUP GROUP _DATA
_TEXT SEGMENT PUBLIC 'CODE'
ASSUME CS:_TEXT,DS:DGROUP,ES:DGROUP,SS:DGROUP
PUBLIC _Getregs
_Getregs PROC NEAR
MOV _BX,BX
MOV _CX,CX
MOV _ES,ES
MOV _DI,DI
MOV _DS,DS
RET
_Getregs ENDP
_TEXT ENDS
CGROUP GROUP _TEXT
END
6 In Search Of July 1990 dBASE IV
This article is reprinted from the July 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.
In Search Of...
Chris Wilson
Many a new user to personal computers is often befuddled by the
maze-like quality of the directory of their hard disk. Often times,
people call Technical Support for help in finding a lost file. If you
don't know your way around your computer, one file among megabytes of
storage space may not be easy to spot.
Commercially available products by Norton, Mace, Central Point, and
others have helped in organizing directories and making files easy to
find. These products include utilities specifically designed to
search for files. However, some users new to the world of personal
computing may not yet know of their existence. Well, with a little
known and seldom used combination of DOS commands, you can create your
own file-find utility in the form of a DOS batch file. The batch
file, Search4.bat, is shown below.
Seach4.bat
@ECHO OFF
IF "%1" == "" GOTO MSG
IF "%2" == "" GOTO MSG
CHKDSK %1/V | FIND "%2" | MORE
GOTO END
:MSG
ECHO Syntax is SEARCH4 [drive:] [search string]
ECHO Example: SEARCH4 C: .DBF
:END
To start off, I use @ECHO OFF so that this will not be seen on the
screen as the commands are being executed. If you are using a version
of DOS earlier than 3.30, you will have to omit the @ symbol. The use
of this symbol suppresses the display of the ECHO OFF command itself.
In versions earlier than 3.30, ECHO OFF will be displayed on screen
while subsequent commands will not when the batch file is run.
We need to give this batch file two pieces of information: the drive
to search and the text to search for. We do this by using two
replaceable parameters, %1 and %2. Replaceable parameters are used to
supply information to the batch file from the command line at the time
of execution. DOS allows up to 10 parameters to be passed to a batch
file. These 10 parameters are numbered %0 to %9. It should be noted
that DOS reserves the %0 parameter for the name of the batch file
itself which actually leaves nine that can be used for other
information.
In our case, %1 represents the drive to be searched while %2
represents the string we're searching for. The batch file will
replace %1 with the first word on the command line after the batch
file name, and %2 with the second word. When specifying the string to
search for, please keep in mind that it must appear in all capital
letters or DOS will not find it. So, if you wantedto find any file
with a .txt extension on your C drive, you would type
SEARCH4 C: .TXT
from the DOS prompt
In Search4.bat, the lines IF "%1" == "" GOTO MSG and IF "%2" == ""
GOTO MSG, perform a test to see if the first and second parameters
have been entered. DOS uses the double equal sign (==) as a test of
equality between two items. If you type only SEARCH4 at the DOS
prompt, %1 will evaluate to a null value, thus triggering the batch
file to GOTO the procedure called MSG. Upon entering this procedure,
the message
Syntax is SEARCH4 [drive:] [search string]
Example: SEARCH4 C: .DBF
is displayed on the screen. This is done with the ECHO command. ECHO
will display any text entered after it as a message to the screen.
After this message is displayed, control is then passed to the
procedure called END which terminates the execution of the batch
file.
The line that reads
CHKDSK %1/V | FIND "%2" | MORE
is the heart of the batch file and does all of the work. The
CHKDSK-generated output that most users are used to seeing looks
something like this:
33421312 bytes total disk space
2048 bytes in 2 hidden files
184320 bytes in 73 directories
28913664 bytes in 1121 user files
4321280 bytes available on disk
655360 bytes total memory
527472 bytes free
However, by using the /V switch, you get an additional listing of all
files in all directories which looks something like this:
Directory C:\DBASEIV\SAMPLES
C:\DBASEIV\SAMPLES\CATALOG.CAT
C:\DBASEIV\SAMPLES\UNTITLED.CAT
C:\DBASEIV\SAMPLES\FORMBROW.COD
C:\DBASEIV\SAMPLES\ACCT_REC.DBF
.
If you've ever wondered what that symbol located above the backslash
on your keyboard is, it's known as the DOS "pipe" character. The pipe
takes the output of one program and turns it into input for the second
program. In our case, it reroutes the output of the CHKDSK utility
from the screen into the DOS FIND utility for processing. This method
is known as I/O redirection. (Please note that not all DOS commands
support I/O redirection.)
The FIND utility locates and displays each occurrence of a string
within a file. In Search4.bat, it searches through the output from
the CHKDSK utility for the string specified by the contents of %2.
Once the selected information is found, screen output is then
controlled by the MORE utility.
MORE has no real value by itself. It is primarily used with the DOS
pipe, taking output from other commands and displaying it on the
screen one page at a time. When there is more than a single page of
information to be displayed, the word
--MORE--
will display at the end of the first screen full of information.
Pressing any key at this point will display as many applicable file
names to fill the next screen.
There is one drawback to this batch file: it does not support the DOS
wildcards. When you enter "PRG" as the string to search for, Search4
will display every filename that contains that sequence of letters,
regardless of whether or not that string appears in the path,
filename, or extension. So when searching for specific extensions, be
sure to include a preceding period prior to the capatilized letters of
the desired extension.
And there you have it, an easy to use utility to locate your files
even if you only have part of a filename to go on. The next time you
need to find a file, this batch file will help you do it right
away.
For information about DOS Utility programs mentioned in this article,
you may contact:
Norton Utilities
Peter Norton Computing
100 Wilshire Blvd, 9th Floor
Santa Monica, CA 90401
800/365-1010
213/453-2361
Mace Utilities
Paul Mace Data Recovery
400 Williamson Way
Ashland, OR 97520
800/523-0258
503/488-2322
PC Tools
Central Point Software, Inc.
15220 N.W. Greenbrier Pkwy.
Beaverton, OR 97006
503/690-8090
This article is reprinted from the July 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.
Mild-mannered Software Support Technician by day, programmer
extraordinaire by night, Macro Man fights the evils of redundancy and
hard coding. Faster than a SEEKing index, more powerful than a Local
Area Network. Able to LOOP huge code structures in a single FOUND() ,
Macro Man looks for TRUE (.T.), logic, and the dBASE IV way.
Macro Man!
Adam L. Menkes
We have all, at one time or another, made changes to the structure of
a database, only to find out later that the GET statements
incorporated by our format files or programs are not working properly
because either the lengths have changed or the field names have
changed or both. You may have feared that there is now way around
this; that you're stuck with going through all your code changing each
memory variable in every program. "This," you mutter to yourself,
"is the price I pay for power." You're tired of repetitive coding
that results in hours of debugging just because some items were not
consistently spelled correctly or data types mismatched.
In the listing which starts on page 23, you'll find a set of
procedures and functions strategically designed to simplify many of
your future coding needs. You will be able to declare all your
database memory variable fields public, set field memory variables to
blanks or zero, and replace contents of your fields with memory
variables that were changed while in full-screen edit mode.
& What Have We Here?
These procedures make use of the ampersand "&" for macro substitution
or what is more and more being referred to as indirection. This is to
distinguish the use of the & from keyboard macros now available in
dBASE IV. The & is also different than the double-ampersand && which
allows for comments on the same line as a dBASE command. For those
not familiar with macro substitution/indirection, below is an example
of how it works in a program:
*--- Initialize memory variables mtest, mvar1, mvar2, mvar3, mvar4,
* mvar5, mvar6, mvar7
mtest=1234 && Stores the number 1234 to variable 'mtest'.
mvar1="mtest" && Stores the string 'mtest' to variable 'mvar1'.
mvar2=mvar1 && Result is "mtest" (Character).
mvar3=mtest && Result is 1234 (Numeric).
*--- Results of storing these memory variables to others using
* macro substitution.
mvar4=&mtest && Result is SYNTAX ERROR since there is no
* variable 1234 defined.
mvar5=&mvar1 && Result is 1234 (Numeric) since it sets mvar5
* equal to the contents of variable mtest.
mvar6="&mtest" && Result is the string "&mtest".
mvar7="&mvar1" && Result is the string "mtest".
Using the Procedures
MacroMan.prg is an interdependent set procedures and functions that
performs a variety of tasks to allow shorter and more accurate code
with less potential for error. The procedures and functions called by
other procedures are denoted with ** in the in-line comment (&&)
area. These are optional and can be removed from the code and
replaced with explcitly named files and/or variables whenthese
procedures need to be used independently (except where otherwise
noted).
f you use a dBASE IV catalog (.cat) file, which is simply a specially
assigned .dbf file, the field name is File_name and is 12 characters
long. The file name includes the extension of the file. However,
unlike the special database referred to here, a .cat file may contain
any type of dBASE created file (such as formats, reports, labels,
and memos), not just database files.
SetaPub and SetaMem
Procedures SetaPub and SetaMem require a database with one field
called Dbf, which should be defined as a character field with a length
of eight bytes. This field will contain the names of all the
databases that will be used in these procedures, somehwat like a
catalog. These procedures could also be modified to take advantage of
the existing catalog if you so choose. Both of these procedures
require the use of a secondary procedure which use only one database.
The procedure SetaPub is not necessary if each database is passed as a
parameter to Procedure Set1pub as follows :
DO Set1Pub WITH "Database1"
DO Set1Pub WITH "Database2"
DO Set1Pub WITH "Database3"
. .
. .
DO Set1Pub WITH "Databasen"
The preferred method is to have a database, MainDbf for example, with
one field with the characteristics described previously. Each record
would be the name of a different database.
Record 1 Database1
Record 2 Database2
Record 3 Database3
. .
. .
Record n Databasen
This would require only one statememt :
DO SetaPub WITH "Maindbf"
For the purposes of this article, it is important that all database
files should be in the current directory. These procedures have not
been tested for databases in other directories which could be
specified by a path. If your files are in another directory, make
sure the dBASE IV directory is in your DOS path and that you change to
the desired directory before starting dBASE IV. Additionally, field
names in the database should not use more than nine characters, as m
will be added as a prefix to these field names to create matching
memory variables. These memory variables may not behave properly
beyond ten characters. This should not be confused with the prefix
m-> which is used to distinguish a memory variable spelled exactly the
same as a field name.
The accompanying procedure, Set1Pub, checks each field for a field
name. Since a database can not contain a field with length of 0, this
procedure checks each field using the FIELD() function from FIELD(1)
to FIELD(n) until FIELD(n) returns a null.
The procedures SetaMemand Set1Mem are best used in conjunction with
SetaPub or Set1Pub, as they do not check to see if a memory variable
has already been declared, but assume it exists and is either public
or private.
InUse (
This function checks all ten work areas to see if the database passed
as a parameter is in use in any of these areas. If it is, the value
returned is the number of the work area where the file is in use,
otherwise, 0 is returned. For example:
. SELECT SELECT() && Select next highest open work area
. USE Client
. SELECT SELECT()
. USE Sample
. SELECT SELECT()
. USE Checks
. ? Inuse("Sample")
9
FullDbf (
This function checks for a file extension, and if one does not exist,
assumes a ".dbf" extension. For example:
. Test1 = FullDbf("Client")
CLIENT.DBF
. Test2 = FullDbf("Sample.dbf")
SAMPLE.DBF
. Test3 = FullDbf("Info.txt")
INFO.TXT
There are those times when you want to branch to a procedure that
switches to an alternate work area. By calling your procedure as a
parameter with this procedure, you are returned to the work area from
which you began.
Exceptions With Memo Fields
Although you can not create a memory variable for a memo field, you
can create a memory variable that references a text file of that name,
creating a pseudo memory variable. This type of functionality is not
included in this article as thorough testing has not been done.
However, the concept is worth mentioning. As we are able to store all
other data to memory variables and then, save or reject the entries we
have made, it would be helpful if the same option were available for
memo fields.
For example, in a network, it is not desirable to retain a record in a
locked mode while changes were being made (as it could be locked for a
while), nor would you want the changes made to the file if the user
decided that the information added or changed in the memo field was
incorrect. In order to add this capability, remove the following
clause in the procedure entitled Set1Pub.
IF mFld_Type <> "M"
PUBLIC m&mFld_Name && Declares memory variable
* 'm' + the field name
PUBLIC.
ENDIF
and in procedure Set1Mem, add the following to the DO CASE clause:
CASE mFld_Type = "M"
SET ALTERNATE TO m&mFld_Type..VMO [ADDITIVE]
*--- Note the two periods. One denotes the end of the
* macro, the second is for the file extension VMO
* (Variable MEMO).
SET ALTERNATE TO
You may want to check first to see if InUse (m&mFld_Type..VMO), and
either overwrite it or add to it with the ADDITIVE option. Remember
to use SET SAFETY OFF so that you won't be continually prompted to
overwrite the file. You may also want to COPY MEMO TO m&mFld_Name
[ADDITIVE] from within the program if a SEEK is performed.
Once changes were made from within the program: (using MODI COMM
&mFld_Name, for example), you could not REPLACE Fld_Name WITH
mFld_Name, since this field is a memo data type (as accounted for in
the case statement above checking for mFld_Type = "M") but you could
APPEND MEMO FROM &mFld_Name [OVERWRITE]. This could be easily added to
the ReplVar procedure as the ELSE condition for IF mFld_Type <> "M".
Now that this article has got you started, try this concept with
arrays, windows, and popups? Ah, yes, the wonders of macro
substitution.
MacroMan.prg
PROCEDURE SetaPub
*-- Initializes PUBLIC memory variables defined as "m" +
FIELDNAME
* for FIELDS in a Database. This is used in conjunction
with the
* procedure Set1Pub.
*-- SYNTAX : DO Procedure WITH
*-- Example: DO SetaPub WITH "Maindbf.DBF"
PARAMETER AllDbf &&
DATABASE that contains a short catalog
*
of DATABASEs with fields that will be
*
used for declaring memory variables.
DO SelArea1 WITH AllDbf && ** Selects Area where
DATABASE is in
*
use or opens it in an unused area.
SCAN
vDbf=DBF &&
Variable 'vDbf' gets database name
* from database
passed as a Parameter.
DO Set1Pub WITH vDbf && Calls sub-procedure
passing this
* variable as
the parameter.
DO SelArea1 WITH Alldbf && R e-SELECTs Work Area of
AllDbf.
ENDSCAN
DO SelArea2 && ** Closes
AllDbf if it was not in USE
*
before the current procedure
RETURN
PROCEDURE Set1Pub
* Initializes PUBLIC memory variables defined as "m" +
FIELDNAME
* for FIELDS in one Database.
*-- SYNTAX : DO Procedure WITH
*-- Example: DO Set1Pub WITH "Clients.DBF"
PARAMETER vDbf
DO SelArea1 WITH vDbf && **
mFldVal=1
DO WHILE LEN(TRIM(FIELD(mFldVal))) <> 0 && Check that Field(n)
*
exists.
mFld_Name = FIELD(mFldVal) && Gets the
name of the field
mFld_Type = TYPE(FIELD(mFldVal)) && and the
field type.
IF mFld_Type <> "M"
PUBLIC m&mFld_Name &&
Declares memory variable
* 'm'
+ the field name PUBLIC.
ENDIF
mFldVal=mFldVal+1
ENDDO
DO SelArea2
&& **
RETURN
PROCEDURE SetaMem
*-- Initializes "empty" memory variables defined as "m" +
FIELDNAME
* for FIELDS in all databases listed by this database for
their
* respective field types. Used in conjunction with Set1Mem.
*-- SYNTAX : DO Procedure WITH
*-- Example: DO SetaMem WITH "Maindbf.DBF"
PARAMETER AllDbf
DO SelArea1 WITH AllDbf && **
SCAN
vDbf=DBF
DO Set1Mem WITH vDbf, .T. && Note the second
parameter.
DO SelArea1 WITH AllDbf && **
ENDSCAN
DO SelArea2 && **
RETURN
PROCEDURE Set1Mem
*-- Initializes memory variables defined as "m" + FIELDNAME
for
* fields in a chosen database for their respective field
types.
PARAMETERS VDbf, mAddEdit
*-- vDbf: variable .dbf name
* mAddEdit: logic type which, if TRUE, sets the variables
empty
* and if FALSE, gets the FIELD contents from the current
record.
*-- SYNTAX : DO Procedure WITH
*-- Example: DO Set1Mem WITH "Clients.DBF", .F.
DO Efile WITH "Temp" && ** Erase
Temp.DBF.
DO SelArea1 WITH vDbf && **
mArea=Inuse(vDbf) && ** Sets
mArea to Work Area of vDbf.
COPY STRU EXTENDED TO Temp
MSelTemp=SELECT()
&& Highest available Work Area.
SELECT(MSelTemp)
&& SELECTs unUSEd Work Area.
USE Temp
SCAN
mFld_Name = Field_Name
mFld_Type = Field_Type
mFld_Len = Field_Len
IF mAddEdit &&
Initializes memory variables
*
EMPTY if in an ADD type program.
DO CASE
CASE mFld_Type = "C"
m&mFld_Name =
SPACE(mFld_Len)
*-- Stores the appropriate
number of spaces to the
* character variable.
CASE mFld_Type = "D"
m&mFld_Name =
{} && " / / "
CASE mFld_ype = "F"
m&mFld_Name =
FLOAT(0) && 0
CASE mFld_Type = "N"
m&mFld_Name =
FIXED(0) && 0
ENDCASE
ELSE
SELECT(mArea)
m&mFld Name = &mFld_Name
*-- Store the field contents of the current
record into these
* variables.
SELECT(mSelTemp)
ENDIF
ENDSCAN
DO Efile WITH "Temp" && **
DO SelArea2 && **
RETURN
PROCEDURE Efile
*-- Erases a specified file, even if the file is currently in
USE.
*-- SYNTAX : DO Procedure WITH
*-- Example: DO Efile WITH "BadFile.TXT"
PARAMETER vDbf && File to be
erased.
vDbf=FullDbf(vDbf) && ** Checks
for .DBF extension.
IF FILE(vDbf)
IF Inuse(vDbf)<>0 && ** If
database is SELECTed;
MSelDbf=Inuse(vDbf) && ** it
returns the Work Area
SELECT(MSelDbf) && and SELECTs
that Work Area
USE && and closes it so
that it can
ENDIF
ERASE(vDbf) && be
ERASEd.
ENDIF
RETURN
FUNCTION InUse
PARAMETER vDbf
SET EXACT ON
*-- SET EXACT OFF would cause potential problems if you were
* checking for a database such as "Client" without
specifying the
* extension if a file called Client1, Myclient, or similar
existed
* in the same directory.
mAlias=0
vDbf=FullDbf(vDbf) && **
mCount=1
DO WHILE mCount<=10 && This loop checks all 10
Areas.
SELECT(mCount)
IF (vDbf) $ UPPER(DBF())
*--- The '$' is used instead of '=' so the
PATH does not have
* to be specified in the variable.
mAlias=mCount && Sets the Work Area where it
is being USEd.
RETURN mAlias
*--- Exit the loop once the Area is found
ENDIF
mCount=mCount+1 && Increments to check next
Area.
ENDDO
SET EXACT OFF
RETURN mAlias
FUNCTION FullDbf
PARAMETER vDbf
vDbf=UPPER(vDbf)
&& Convert to Uppercase.
IF LEN(TRIM(vDbf))<>0 && checks for null.
IF AT(".",vDbf)=0 &&
Checks for an extension.
vDbf=TRIM(vDbf)+".DBF" && Adds '.DBF' to the
parameter.
ENDIF
ENDIF
RETURN vDbf
PROCEDURE ResetArea
PARAMETERS ProcToDo, Param1
*---Parameters: Procedure to do, Additional parameter passed.
*-- SYNTAX : DO Procedure WITH
*-- Example: DO ResetArea WITH "SetaPub", "Maindbf"
mStartArea=ALIAS() && Current Area that is SELECTed.
DO &ProcToDo WITH Param1
*--- Calls a procedure that is specified by a parameter,
rather
*--- than a specific procedure, and passes 1 parameter
(Param1).
IF LEN(TRIM(mStartArea)) <> 0
SELECT(mStartArea)
*--- Re-SELECTs the Area from where you started.
ELSE
USE
ENDIF
RETURN
PROCEDURE SelArea1
*-- Selects Area where DATABASE is in
* use or opens it in an unused area.
*-- Must be used in conjunction with SelArea2
PARAMETER mParam1
PUBLIC mWasOpen,mMaindbf
IF Inuse(mParam1) <> 0 &&
**
mMainDbf=Inuse(mParam1)
&& **
SELECT(mMainDbf)
mWasOpen=.T.
*--- mWasOpen is TRUE when the database is in USE
prior to
* being opened by a procedure or function.
ELSE
mMainDbf=SELECT()
SELECT(mMainDbf)
USE(mParam1)
mWasOpen=.F.
*--- mWasOpen is FALSE if the database was not in USE,
whether
* or not it gets USEd during a procedure or
function.
ENDIF
RETURN
PROCEDURE Selarea2
*-- Must be used in conjunction with Selarea1.
IF .NOT. mWasOpen && If the Database was not open
initially,
SELECT(mMainDbf) && SELECT that Work
Area,
USE && and
CLOSE the Database.
ENDIF
RETURN
PROCEDURE ReplVar
*-- REPLACEs FIELDS in a database with their corresponding
memory
* variables.
PARAMETER vDbf,mAddEdit
*-- Database in which to REPLACE variables, whether in Add or
Edit
* mode. Add or Edit will determine if the current record is
* replaced, or if a new record will be added.
PUBLIC mThisDbf
mThisDbf = vDbf
DO Efile WITH "Temp" && **
DO SelArea1 WITH mThisDbf && **
mWOpen = mWasOpen && **
*--- Temporarily stores this variable because it may change.
IF mAddEdit
APPEND BLANK
ENDIF
COPY STRU EXTENDED TO Temp
mSelTemp = SELECT() && Find highest Work Area
available,
SELECT (mSelTemp) && and SELECT this
unused area.
USE Temp
SCAN
mFld_Name = TRIM(Field_name)
mFld_Type = Field_Type
mmFld_Name = "m" + mFld_Name
DO SelArea1 WITH mThisDbf && **
IF mFld_Type <> "M"
*--- Will not work with MEMO fields.
REPLACE &mFld_Name WITH &mmFld_Name
*--- i.e. REPLACE Fieldx with mFieldx
ENDIF
SELECT(mSelTemp)
ENDSCAN
DO Efile WITH "Temp" && **
mWasOpen = mWopen &&
Restore original value.
DO
SelArea2 && **
RELEASE mThisDbf
RETURN
* EoF: MacroMan.prg
2 Dialogue July 1990 dBASE IV
Dialogue
Questions and Answers
Getting Carried Away
Q: I was using the Carry forward option on some fields within my
format file but then decided that I didn't want them to do so. So I
modified the format file and turned off that option on the fields I
had previously set. After saving, I returned to the Control Center
and proceeded to append. The fields were still carried forward. I
had to exit dBASE IV and return before I could get the fields to stop
carrying. What happened?
A: The SET CARRY command is an additive command. So when the new
format file issues the SET CARRY command for the new set of fields, it
did not clear the previous ones from memory, only adding to the list.
Exiting and returning to dBASE IV cleared this setting. However,
closing the .dbf should do the trick. If you are at the dot prompt,
you could enter the command SET CARRY TO at the dot prompt to clear
the setting. Going to the Tools: Settings menu to turn the Carry
setting OFF will not be sufficient.
Traceback Info
Q: While in the Debugger, pressing a P will show the program
traceback information. Is there a command to display the same
information which can be typed at the dot prompt? (When this happens
during the debug process, most of that information gets overlaid by
any existing windows or popups.)
A: Unfortunately, there is not a command to show the traceback
info from the dot prompt and continue running the programs. The info
is shown if you do a cancel, but as you mentioned, it can gets
overwritten depending upon elements affecting the current screen.
Furthermore, attempts we made to route traceback information to a text
file by triggering a SET PRINT TO FILE via an ON ESCAPE or ON ERROR
command provided unsatisfactory results.
Variable Naming Conventions In an .FRG File
Q: I am delving into the modification of an .frg file and am
confused by some of the variables created and used there. Could you
shed any light on naming conventions and their origin?
A: The list is quite extensive and beyond the scope of this
section. However, there is a methodology you should be familiar with
when delving into the code files of labels, reports and formats.
The first letter in the name of the variable is either g or l for
global (public) or local (private). The second letter is the data
type of the variable: c,n,d,f,l. The third character is always an
underscore. From there on, if you're good with mnemonics, you might
decipher the rest of the variable name.
If you're designing your own application or modifying an existing .frg
file, you might consider using other memory elements as well, such as
a for array, w for window, and so on, keeping in form with the naming
structures used.
Further expounding on the specific purpose of .frg generated variables
can be found in the April 1990 edition of TechNotes/dBASE IV in the
article Anatomy of a Report Form.
Pass the Variables, Please
Q: Is it possible to pass a parameter to a program from the DOS
level by entering
DBASE
A: With the syntax you provided, the answer is no. However,
there is a way of parameter passing from DOS into dBASE IV. By making
use of DOS environmental variables, a simple DOS batch file, and the
GETENV() function in dBASE IV, you can easily pass parameters to a
dBASE III PLUS or dBASE IV program.
Create a batch file called DBPROG (a name used for this example but
can be any valid DOS filename you choose) that contains the following
statements:
@ECHO OFF
SET VAR1=%1
DBASE
SET VAR1=
CLS
The @ sign preceding the ECHO OFF statement is applicalble only to DOS
version 3.3 and higher. See the article entitled In Search Of. in
this issue for further examples of DOS parameter passing.
Include the following line in your dBASE program:
mvar1 = GETENV("VAR1")
Execute the batch file by typing the name of the file followed by the
parameter you wish to pass at the DOS level:
DBPROG
By using the replaceable parameter capabilities of DOS, one can pass
multiple parameters (up to nine) to a program.
QBE Calculated Field Nesting
Q: When designing a Query, I am getting the message Variable not
found when I try to use a calculated field in another calculated
field. Is there some limit on nested calculated fields?
A: Unlike in a report design, one cannot use a calculated field
in another calculated field in the QBE. It's still possible to get
your results if you expand the entire expression of the first
calculated field inside the second one. For those long-winded
expressions that would be a chore to retype, make note of the answer
to the next question below.
Getting "Snippy" About Cut and Paste
Q: Please say I've overlooked something. I am knee deep in a
massive design screen project that contains many calculated fields
with complex and lengthy expressions. Is there no way to copy an
expression from one field to another? If so, can you copy an
expression tested at the dot prompt into an expression area like a cut
and paste buffer could perform?
A: Pressing F8 COPY will copy a field and it's contents to a new
field. Copying a specific expression to another existing calculated
field is not a feature presently available in dBASE IV. A new product
called Trading Places, features a cut and paste capability. The
software's main functionality is for swapping TSR (RAM resident)
programs in and out of memory, another handy capability to have with
dBASE IV. The software is now available for $99.95 and can be ordered
through Customer Service. You may also obtain this product from
your local software vendor.
3 dBASE File Recovery July 1990 dBASE IV
This article is reprinted from the July 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.
No one wants to think about it. No one wants to admit that it can
happen to them. Every time that you quit a dBASE IV session, you see
that foreboding reminder to make regular back-ups of your data, but
you just don't make the time to do it. You could have bought a backup
utility program, but you couldn't justify the expense. And so,
benignly, you whiz along through months or maybe years without a care,
placing your trust and countless hours of work in the enigmatic world
of well-ordered magnetic blips. Until one day you need.
dBASE File Recovery
Joe Stolz
As a support technician, I can't count the number of times have I
heard someone tell me that they can view and edit their data in their
files, but at some point they receive the Record out of range
message. How many have reported seeing Not a dBASE database when they
merely try to place their file into use? There are those who can see
their data, but it just doesn't show up in the right fields anymore.
First names are in the last name slots. Last names are in the address
field. The data is there, just shifted to the wrong location. Then
there are those unfortunates who used to have data, but the file and
all its contents are gone. Perhaps they zapped the file
unintentionally or maybe they deleted too hastily. Finally, there are
those exposed to the dreaded case of having the computer lock up in
the midst of an edit operation. They were left with the throat-drying
experience of rebooting their computer to discover the file has become
an evil entity that is thoroughly uncontrollable and unreliable.
Yet, as often as it seems I heard one woeful tale after another, it is
ironic but true that data corruption is a pretty rare thing, and
fortunately so. However, when it does occur, you are not always
prepared for the consequences, and let's face it, the
ever-consciencious group that keeps frequent, up to date back-ups are
in the minority. Sooner or later, it's going to happen to you. What
will you do? You may not be surprised to know that there are several
products on the market that can solve your data loss problems. Our
own contribution, dBASE File Recovery, is admirably suited to the task
of recovering lost, damaged or sick database files.
The program was created by Keith Mund, a former Software Support
Technician at Ashton-Tate. Who else could be so steeped in corruption
(of files that is) to understand that the world would be a much
happier place if only damaged files could be salvaged? Keith is
intimately aware of the most common types of file corruption. He also
knows the painfully complicated techniques required that were formerly
the only means to salvage data from these files. The only tools that
were available were DOS commands such as DEBUG and COPY to find and
extract lost data and Borland's Sidekick to aid in the hexadecimal
arithmetic calculations to make heads or tails of it! Beyond that, he
chose to develop a data recovery methodology that would work against
the most common types of corruption such as end of file markers,
shifted data, damaged or corrupted records, and against the most
dreaded cases of all, records that don't exist as a result of using
ZAP or DELETE. He came through with flying colors.
The basis of dBASE File Recovery salvaging techniques are simple and
direct; identify text data that matches the pattern of fields as
specified in the header of your damaged database (the first portion of
the file) and copy it to a new file. dBASE File Recovery never
touches or modifies a damaged file. It always copies the recovered
data into a new file. If the recovery was successful you can delete
the damaged file and return to your work. Often, though, you may find
that the recovery was not complete. You may have to change things and
try to collect more data with a second pass on the damaged file. As
an experienced file recovery jockey, I really appreciate this extra
margin of safety afforded me by dBASE File Recovery. Let's discuss
some specifics of the program.
Going, Going, Gone
When a file is deleted explicitly by zapping or erasing, the data is
not erased from disk, at least not immediately. The area that was
allocated for the file is now open territory for other data. It's no
longer protected by its file handle and can be overwritten with the
next disk I/O activity. It is strongly advisied to discontinue using
your computer the moment you realize you've erased valuable data or
that something is missing. The likelihood for a fuller recovery will
be increased .
General Usage
The dBASE File Recovery screen has a look and feel similar to dBASE
IV. There is a status bar on the bottom, a menu along the top with
pull down menus. Data is displayed on the screen in a manner much
like a dBASE edit screen.
dBASE File Recovery has only a few menu options: File, Position,
Option and Exit. The File menu is the first one used. It is where
you specify the name of your damaged file.
I'd like to point out at this juncture that knowing what is wrong with
your file is absolutely crucial in the safe and effective recovery of
your data. As pointed out in the dBASE File Recovery manual right off
the bat, assessing the damage is the most important first step in
recovering your damage. Chapter 3 is dedicated to explaining the
types of problems that can arise and what needs to be done to recover
your data for every given problem.
Once you know what your plan of attack is, you are ready to start the
program. The first stumbling block for someone who uses dBASE File
Recovery without reading the manual (don't we all?) is, "What is the
difference between the Database filename, the Output data to and the
Input data from prompts in the File menu?" The screen shot at right
shows the choices available from the File menu. The Database filename
prompt is where the name of your damaged file is entered, if it is
usable, and if its field structure is undamaged. If you USE your file
and dBASE IV tells you that the file is Not a dBASE database, you will
need to supply a file name of a non-damaged file having the exact same
file structure, or you will have to work out the file structure using
the other File menu option, Modify structure.
The next menu choice is the Input data from option. This is,
obviously, the name of the file that contains the data that you need
to recover. As a safety feature, dBASE File Recovery always collects
data from damaged files and saves them in a new file. If you have
deleted or zapped your data, you will have to save your data to a
floppy drive, or some other drive since the source disk is completely
tied-up into a temporary file so that no data can be accidentally
overwritten.
What confused me the first few times I used the dBASE File Recovery
was that the Database filename was shown as *DEFAULT*, a structure
that can be modified to match your data structure if you have lost the
structure portion of your file. I thought that this had to be my
choice. I didn't realize that I could specify my file as the Database
filename, and that the Input data from option would automatically take
on the name of my file. I also found that pressing F9 allowed me to
navigate through my directory and locate the desired file.
In the case of a deleted or zapped file, the Input data from filename
can be the special names *DISK* or *FREE*. *DISK* is the name of a
special file to be created from the contents of your entire disk. If
your data is strewn around, perhaps even imbedded inside other files
on your disk, *DISK* will make them available to dBASE File Recovery.
The *FREE* option is similar, but only the free, unallocated space on
you disk is collected temporarily into a file to be searched for your
data.
The Output data to option tells dBASE File Recovery into what file to
place your data. Sometimes you will find yourself saving portions of
the recovered file to a series of files which can later be regrouped
into one file. You do this most often with a file that contains
shifted data.
In most cases, once you have set up your file names in the File menu,
you are ready to collect your data. If all goes well and the damage
is minimal, in just a few minutes, you will be done!
Getting Away From Automatic
I found the recovery process so painless and accurate that I thought I
was destined to become the superstar of data recovery. However, one
day I noticed that I was missing a lot of data during the process. I
had set the program into Automatic mode. This mode, set in the Option
menu, directs dBASE File Recovery to automatically recover your data,
but it skips over any problem records. I don't mind having slightly
damaged records, it's better that way than having to guess at which
records I've lost. I recommend that you remain in the default mode
which is called Write. Write mode causes a continuous writing of all
good records. It stops when a bad record is hit. You can skip the
record, or choose to save it too. Ninety-nine percent of the time I
save these kind of damaged records. They are noted and kept as a
"placeholder" of sorts that I can correct or re-enter later.
dBASE File Recovery replaces "garbage" characters (like NULL or EOF
characters) with a special ASCII character (176) which appears as a
grainy block to show you where your damage was. You should delete
these characters when you are done recovering your file.
When you are in the regular edit mode, you should see the first record
in your file when the file is placed into use. If you are not able to
see it, don't panic. Rather, press the PgDn key or any of the other
cursor movement keys. As you do, you will find yourself moving
through your file. You should see your data shortly. The arrow keys
allow you to shift your data through your database structure a byte at
a time. This is how you can manually adjust shifted data in your
file. If the data is shifted to the right a few characters, you can
easily shift it back and start the saving process. In addition, the
F3 and F4 keys jump the data by the number of bytes in a record which
can be manually adjusted to any number of bytes. The F5 and F6 keys
advance the pointer forward or backward 10 bytes at a time.
Looking for records from the Position menu
An easier way to find your first or next good record, is through the
Position menu. The Look for a good record option automatically
searches and aligns data to your structure. The way this is done is
simple, really. In dBASE IV, character fields are left aligned, flush
left within their field. Numeric fields are right aligned. Dates are
eight bytes long, and logical fields are one character, T, F or
blank. Given these obvious characteristics, dBASE File Recovery makes
a simple assumption: If a character field has a leading blank space
(i.e. it is not flush left) something is wrong. The same is true of
the positioning of data within fields of the other data types. When a
field appears to be skewed, dBASE File Recovery highlights this
field. If the data fits the pattern of both your file's data
structure and of the position of the data within these fields, no
highlighting occurs. When you page through your data, anything out of
the ordinary shows up clearly. If it so happens that your character
field is really supposed to have leading blanks, and you don't want
this to be flagged as an error, the requirement for justified text can
be set off in the Option menu.
Pattern matching
The pattern matching process is very fast and reliable. If your
records fit the pattern, you simply whiz along. If a record is
slightly off, you screech to a halt (if you are in Write mode) to
decide whether the data is intact or not. Data that would be in a
date field is shown in it's true form, as an eight digit numeric
field, with the format CCYYMMDD (first century, then year, then month,
then day). No delimiters such as a slash or dash will be present.
This may look unfamiliar to you, but rest assured that the dates will
be shown correctly when dBASE IV interprets them.
Looking at your data
Another useful feature is the Dump data to screen option in the
Position menu. If you have a file that you suspect contains data, you
can set up the file as your Input data from file. Then move to the
Position menu and try the Dump data to screen option. The contents of
the file roll across your screen. This can show you whether any data
worth collecting exists in the file. If you had some type of mishap,
had to reboot your computer, then found that you had lost clusters
when you ran CHKDSK, you can use this technique to view the files
recovered from these lost clusters to determine whether they contain
your data. It's a quick way to evaluate the file.
The more difficult procedure of divining the file structure of a file
that has a bad header and for which no backups exist is described
well. I have used dBASE File Recovery many times for this purpose.
It's a tedious procedure, but you can see rapid results nonetheless.
The Things You Can Do.
Let me emphasize that you should not underestimate the usefulness (not
to mention power) of dBASE File Recovery. There are quite a number of
menu options that make this program very functional and versatile.
You can search for specific data that you recall was once contained in
the file, allowing you to locate specific records. You can mark
records with a tag and move from tag to tag in the file. You can set
the default drive and directory. You can even run DOS commands while
in a File Recovery session.
The documentation is packed full of a ton of useful information.
There is even an appendix on getting data from disks that DOS
rejects. That, alone, is worth it's weight in microchips. All in
all, this is a great program. It will undoubtedly reunite many lost
files with their anxious owners. Here's wishing you a speedy
recovery!
dBASE File Recovery can be used on all types of .dbf files including
those from dBASE IV SQL and dBASE III. dBASE II files are not
compatible for this software.
4 Cat Catcher July 1990 dBASE IV
This article is reprinted from the July 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.
Cat Catcher
Leah Boselli
So you can't quite collect all of your .cat files into one Catalog.cat
in the main directory? For those who like to keep all of their
various files in separate subdirectories but would like to access them
through the dBASE directory, this can pose a few problems. One
problem surrounds the fact that when files are stored to a catalog and
that catalog is in the same directory as the files, then the path is
not stored with the filename. You can always add the files to the
current catalog one at a time through the Control Center but this can
be a redundant nuisance if you have a lot of files. If you add the
name of the catalog to the main Catalog.cat file when it uses the
catalog, it sees the files but assumes they don't exist because they
are not located in the current directory and their isn't a path
leading to them. So in turn, dBASE IV will delete the files out of
the catalog.
The following program will prompt the user for the path and then go to
that directory and open it's Catalog.cat. Using the Catalog.cat as
the actual .dbf file that it is, it then goes through each record
opening up the various catalogs within it and then, using the STUFF()
function, inserts the path into the path field of the catalog files.
After doing so for each catalog, it then increments the code field to
correlate with the code field of the main Catalog.cat in the dBASE IV
directory. The Catalog.cat file in the dBASE directory is then opened
and the newly modified version from the subdirectory is then appended
into it. Now all catalogs from the subdirectory and the files
included within it will have correct paths and can be accessed from
the main directory.
Remember that you are providing the name of a catalog file in another
directory. The Catalog.cat file will already have this catalog name
referenced, but not the path. This means that, unless you actually
change the directory or start dBASE IV from that directory, that path
wouldn't, under normal circumstances, be available. That's where
CatChase.PRG comes in. By explicitly providing information on where
the catalog is, you can more easily summon those catalogs from the dot
prompt or Control Center.
To physically change directories while in dBASE IV, use the RUN CD\..
command from the dot prompt or the Tools: Dos Utilities: DOS: Set
default drive option from the Control Center
* PROGRAM...: CatChase.PRG
* AUTHOR....: Leah Boselli
* Date......: April 12, 1990
* Versions..: dBASE IV 1.0
* Notes.....: Modifies CATALOG.CAT and adds the path name to catalogs
that
* may exist in other directories.
*--- Set commands
SET TALK OFF
SET ECHO OFF
SET CONFIRM ON
SET ESCAPE OFF
*--- Error checking routines
ON ERROR DO Errprg
ON ESCAPE DO Errprg
* First, open CATALOG.CAT in the current directory
SELECT 1
USE CATALOG.CAT
* Get the highest number in the code field
CALCULATE MAX(Code) TO mcode
mpath = SPACE(30)
m_ok=.T.
myes=" "
CLEAR
DO WHILE m_ok
* Get the correct path where the files are located from the
user and do
* error checking routine
@3,5 SAY "Enter path where files are located: " GET mpath ;
VALID Ckpath(mpath)
@5,5 SAY "Example: C:\dbase\files\ "
READ
* Initialize the mfile with the path for the CATALOG.CAT file
to be
* added and check for existence of CATALOG.CAT
mfile=TRIM(mpath)+"CATALOG.CAT"
CLEAR
* Check to see if CATALOG.CAT exists
IF .NOT. FILE(mfile)
@10,5 SAY "There isn't a CATALOG.CAT in that
directory"
@12,5 SAY "Would you like to reenter the path? (Y/N) "
GET myes ;
PICTURE "!" VALID myes $ "YN" ERROR "Enter Y
or N"
READ
IF myes = "N"
CLOSE ALL
RETURN
ELSE
mpath = SPACE(30)
LOOP
ENDIF
ENDIF
m_ok = .F.
ENDDO
* Use the CATALOG.CAT in the subdirectory and go to first record
SELECT 2
USE &mfile
GO TOP
* This loop will get the filename from the record, then use that file
* and replace the filename to include the correct path until the end
* of the file
DO WHILE .NOT. EOF()
*--- Check to see if the path is already included
IF SUBSTR(mpath,2,1) = ":"
EXIT
ELSE
mrec=TRIM(mpath) + Path
ENDIF
* Use the first file in the Catalog
SELECT 3
USE &mrec
GO TOP
* Check to see if the path is already included, if not, add
the path
* and continue to next file
IF SUBSTR(Path,2,1) = ":"
EXIT
ELSE
REPLACE ALL Path WITH STUFF(Path,1,-1,TRIM(mpath))
ENDIF
SELECT 2
SKIP
ENDDO
* Increment the code field by adding the current code to the highest
* code found in the original CATALOG.CAT
REPLACE ALL Code WITH Code + mcode
CLOSE ALL
* Add the catalogs to the CATALOG.CAT file
USE CATALOG.CAT
APPEND FROM &mfile
*--- Close all files and exit
CLOSE ALL
RETURN
FUNCTION Ckpath
PARAMETERS m_path
* Test to be sure a slash was entered at the end of the path
is_ok = IIF(SUBSTR(m_path, LEN(TRIM(m_path)), 1) = "\", .T.,
.F.)
RETURN(is_ok)
PROCEDURE Errprg
SET BELL TO 1024,10
? CHR(7)
CLEAR
@10,10 SAY "AN ERROR HAS OCCURRED --- PRESS ANY KEY TO EXIT
PROGRAM"
i = INKEY(3)
IF i <> 0
CLOSE ALL
CLEAR ALL
CLEAR
CANCEL
ENDIF
RETURN
* EoF: CatChase.PRG
5 It's .bin Fun July 1990 dBASE IV
It's .bin Fun
Erik McBeth
You might have seen Curson.bin or GetDriv.bin in your samples
directory and wondered, "Just what are "bin" files anyway?" Or
perhaps you have used them and were curious about how they were
created. In either case, this article should give insight into the
wonderful world of .bin files and their creation using the language
simply known as C.
Binary, or .bin, files are assembly language files that can be loaded
into memory from within another program and then executed. Not to be
confused with programs which are executed via the RUN command from
inside of a dBASE IV program or from the dot prompt, .bin files almost
become part of the dBASE IV program itself and have the luxury of
speed because they are only loaded once into memory and from there,
they can be executed quickly. Conversely, programs which are RUN from
the dot prompt must be loaded each time you want to execute them.
dBASE IV has two special commands that it uses when handling .bin
files: LOAD and CALL. The LOAD command does exactly what you'd think
it would do, it loads the .bin file into a special area of memory that
is reserved for this purpose. The CALL command (and the CALL()
function) runs the .bin file with the parameters you supply, (that is,
memory variables, strings, and so on). These parameters are placed in
a special place in memory so that the .bin file can find and act upon
them. You can pass up to seven parameters.
Interfacing dBASE IV with C
Well you might have come to the conclusion that since we're loading
assembly language routines into memory that we are forced into using
that cryptic computer language known as Assembly with all its strange
abbreviated commands. It's not quite that bad. Instead, you'll be
shown how to write programs in the somewhat less cryptic language of
C.
The C language has been around for about 20 years or so. It
originally grew out of a research project and was subsequently used in
the writing of the UNIX operating system. Numerous companies make C
compilers including Borland International and Microsoft and the price
for a compiler has come down substantially in the last few years. Add
to this the large library of reference material available on C and you
can see that C is a good choice for a language we can use to create
our .bin files.
Those familiar with C might be saying "Well great, I'll just convert
my 8000 line directory management C program into a .bin file!"
Wrong! If you have this great program just run it from the dot prompt
with the RUN command. Speaking from personal experience, most
programs written in C probably can't be turned into a .bin file or, at
worst, would take so much rewriting and dipping into assembler that it
wouldn't be worth the effort. Most C functions supplied by the
manufacturer of the compiler (printf for example) probably won't work
in a .bin file. All I/O (input/output) operations for the most part
must be handled in assembler.
If you need to do file, string and math operations (division and
multiplication of long integers, for example), you might check out the
Framework III Developer's Toolkit. The Toolkit includes the source
code to many popular C string functions as well as assembly code
functions that allow for file access (open, read, write, etc.) from
inside of .bin files. By using these routines, you just might be able
to turn that directory manager into a .bin file!
Reversing Character Strings
Now let's do a more in-depth study on how we put together a C file.
We'll look at two sample C files to get an idea of what is involved
when constructing C code which will eventually be used inside of dBASE
IV. The first file, StrRev.C, reverses the order of the letters in a
string. The second one, StrDct.C, is used in instances when you want
to place data in true dictionary order (more on "dictionary" order
later). This example illustrates how multiple parameters to a .bin
file can be passed by referencing an array that contains up to seven
parameter addresses. For this example, we keep it simple and just use
the first address parameter in the array. The hooks are in there to
allow for expansion to more parameters.
Let's start with StrRev.C (a function which reverses the characters in
a string) because of its simplicity. We'll try to dissect this C
program as much as possible so as to get a better understanding of all
that is involved in making .bin files. Once you have a pretty good
understanding of the various sections of a C file you can easily take
this code and substitute in your own function in place of StrRev().
In developing the examples for this article, two C compilers
(Borland's Turbo C 1.5/2.0 and Microsoft's 5.1 C compiler) and two
assemblers (Borland's TASM 1.0 and Microsoft's MASM 5.1) were tested.
Various other compilers should work as long as they produce DOS
compatible object files. LINK.exe (or the Link program that comes
with your C compiler) and EXE2BIN, two well known DOS utilities, will
also be needed in order to construct the examples.
Creating an .exe file
We start out by defining a constant known as EXE. Often times, it is
easier to test your .bin file by first turning it into a small program
that can be run from the DOS level; a file known as an executable
file. In our StrRev example, we have constructed the file in such a
way that if the constant EXE equals 1, we will then get our string
parameter from the DOS prompt and print out its reverse using the C
function printf() (somewhat like the dBASE IV ? command). Since the
size of your C program and other memory limitations can be weighing
factors, there is no guarantee that your .exe program will run as a
.bin file in dBASE IV, but at least you'll feel a little more
confident of your coding.
Next we "#include" the header file known as Strlib.H which contains
various definitions that we'll be using in our program, most notably
the register variables BX, CX, and so on. Our Strlib.H file also
contains a C macro called MK LONG() which is critical to the success
of the bin file creation. If we were constructing an .exe file
instead of a .bin we would also need to read in the header file known
as stdio.H. Our "#if EXE" statement would take care of this
automatically.
The next few lines deal with what would be required to make our .exe
file so it could run from DOS. I'll leave out the details of what
these lines are doing so we can quickly jump into what we're here for:
.bin file creation.
Starting after the "#else" statement we see:
void far main()
This is declaring the main starting point of our program. It is very
important that we declare main() in this way. The use of the keyword
"far" causes the compiler to issue a far return (RETF) at the end of
the program. This type of return is needed so that dBASE IV can jump
back to what it was doing before it called your .bin file. Some C
enthusiasts may have noticed that we are declaring our main function
as "void," which usually means we don't want to return anything to the
function/program that called the routine. In this case, we are not
returning anything, just jumping back to dBASE.
1010 Memory Lane
In the next three lines of code, we grab the string that dBASE IV has
passed to the .bin file. dBASE sends a string to a .bin file by
passing the address of where the string is stored in memory.
Memory addresses are composed of two parts, a segment and the offset
into the segment. Think of this as a street and an address number.
The street is the segment, and the offset is the address number. The
segment address of the string is passed to the DS register and the
offset to the BX register.
Registers are special memory locations within the computer's processor
that programs use to pass information back and forth. In our .bin
file, we cannot directly access the various registers so we need some
kind of bridge which will allow us to do so. The assembly language
procedure Getregs() does just this. It takes the various registers
(DS, BX, and so on) and copies them into variables that the C program
can use. The DS and BX registers that you see in StrRev are actually
program variables. Our MK LONG macro then takes these two
variables/registers and converts them into a memory address that the C
program can use. This address is then assigned to our variable "p"
which can then be processed by a string function such as StrRev(). If
all of this sounds complicated just remember that all you really need
to do in the future is copy in these first few lines into your own
.bin file and you'll be set!
Well, the hard part is over. All we need to do now is write the
string function that we'll be using with our "p" variable. In this
case we have the StrRev function but you could just as easily have a
function which selectively alters upper or lower case, opens access to
non-dBASE files or even encrypts it for security. Use your
imagination. Our StrRev function reverses the order of the characters
in a string by exchanging characters from the two ends until they meet
each other in the middle. Any changes we make in our string function
will be reflected in the string that is returned to dBASE IV. For
this reason do not shorten or lengthen the string in any way because
unpredictable results might follow.
Sorting in Dictionary Order
We've looked at a rather simple example of a C program that we can use
to create a .bin routine. Now lets look at a slightly more
complicated example which has the ability to pass multiple parameters
(a maximum of seven) to a .bin file. Our example will still only use
one parameter though, just to keep it simple. The example in question
is StrDct.C, a rudimentary translating function which we can use to
place a data file in "dictionary order".
We start out the C program in much the same way as our previous
example. We "#include" some information and make sure that we
generate a far return by declaring the main() function "void far
main()". We then declare two variables, argc and argv (seasoned C
programmers will no doubt recognize these old standbys). Argc will
contain the number of parameters passed plus one, and argv will be an
array containing the parameters with the first parameter being in
argv[1]. These values are in keeping with more traditional non-bin
programming.
Remember when we talked about memory registers and how dBASE IV
communicates with .bin files? dBASE IV can talk with .bin files in
either the traditional dBASE III PLUS way (through the DS:BX register
pair) or in its extended mode which allows multiple parameters and a
special register to tell us how many actual parameters were passed.
With this flexibility we can even write .bin files which take a
variable number of parameters, something we can't do in UDFs.
dBASE III PLUS passed its parameter into a BX register. Since dBASE
IV passes an array or table of parameters into a ES:DI register pair,
an additional parsing routine in the C program is required.
Multiple arguments are accomplished in dBASE IV by sending our .bin
file the address of a table in memory which in turn contains the
individual addresses of our parameters. You can think of this as an
usher who directs us to the betting windows at a horse track. Once
we find the betting area, we only have to go up to a specific window
to place our bet or get our parameter. dBASE passes the address of
the "betting windows" in the ES:DI register pair and the number of
windows/parameters in the CX register. We learned earlier of segment
and offset and how they pertain to memory addresses. Each betting
window is four bytes away from its neighbor with the address of the
first window starting at zero. Thus, the address of the first window
is in the ES segment with an offset of DI plus zero and its neighbor
has an offset of four greater (DI plus four). You see, you don't
really have to understand everything about .bin files in order to make
and use them.
After we assign memory locations to the elements in argv we are ready
to call our StrDct function. We first check to see that the value of
argc is greater than one (remember that the ARGument Count is one
greater than the number of parameters passed by dBASE) so that we know
that we have a string to play with before calling StrDct(). StrDct
rearranges the sorting sequence of letters from normal alphabetical
order (which is used by dBASE IV) to dictionary order. Dictionary
order is generally defined as an order in which special symbols (#, &)
are placed first, then letters and then numbers, much like a
dictionary. This dictionary sequencing is borrowed from Framework
III, which places words in this order instead of the ASCII ordering
which dBASE uses by default (some special symbols, then numbers, then
letters). The ordering presented in StrDct is NOT case sensitive;
upper case does not necessarily come before lower case.
We should now examine how we turn our completed and presumably tested
C programs into .bin files. I'll demonstrate the construction process
with the two compilers and assemblers from Borland and Microsoft;
other compilers/assemblers should be fairly similar. Remember,
besides the assemblers and compilers, you will need LINK.exe and
EXE2BIN.exe, both of which come with most versions of DOS. The
process will involve:
1. Compiling the C program to an object file.
2. Assembling the Getregs.ASM file to an object file.
3. Linking these two object file into one .exe file.
4. Running EXE2BIN on the resulting .exe file.
The specific steps for the two compilers mentioned are detailed
below:\
Borland Microsoft
1. ALT-F9 in editor cl -c
2. TASM Getregs.asm MASM Getregs.asm,,,,
3. LINK
4. EXE2BIN
The filename reference would be either StrDct, StrRev,
or StrFlp and you should end up with a file like StrDct.bin, and so
on. You can mix and match assemblers and compilers since they all
produce the same kind of object format. You'll know something went
wrong if either LINK gives an error (other than the Warning no stack
message) or EXE2BIN says, File can't be converted. All these examples
work so you shouldn't have any problems. One small note, if you use
TURBO C and set TURBOC to 1 in Strlib.H you can eliminate steps 2 and
3 above.
Running the Routine from dBASE IV
Now the real fun begins, we get to try out our .bin files in dBASE.
We'll look at three different ways that we can use our .bin files with
dBASE. All three ways can be mixed and matched with other .bin files.
Let's first use our simplest .bin file, StrRev, since it is fairly
easy to tell if a string has been reversed or not. From the dBASE IV
dot prompt, type
LOAD StrRev && loads StrRev into memory
x="John" && test string
CALL StrRev WITH x && call Strrev
? x && what is the value now?
You should have seen "nhoJ" displayed if everything went ok. Repeat
steps two through four with different values to make sure our STRing
REVersal is working.
We can go ahead and create a UDF that will CALL StrRev with whatever
string we gave it and then return the reversed string, but let's not.
Instead, let's create a UDF called Strflp() which calls our StrFlp.bin
file. StrFlp is a .bin file which "flips" ASCII characters. Those
characters which have an ASCII value of 127 and below will have the
value of 128 added (the letter 'A' which has a value of 65 would end
up with a value of 193) and those above 127 will have 128 subtracted
(160 becomes 32 or the space character).
Enter the dBASE IV text editor by typing MODIFY COMMAND from the dot
prompt. The code for our UDF is shown below.
FUNCTION StrFlp
* Strflp should have already been LOADed.
PARAMETER str
temp = str
CALL Strflp WITH temp
RETURN temp
After compiling this function, from the dot prompt, type:
? StrFlp("flip")
The result should look something like mq . Remember that StrFlp
flips the value of the characters in a string so that characters which
use to come at the start of the ASCII table now come at the end and
vice-versa. If we do something like:
? StrFlp("mq")
we get "flip" back again since now the characters at the end of the
ASCII table are transposed into characters at the beginning.
Note that on page 9 is a section of code with extensive repetitive
array assignments. At first glance, it would appear as if this could
and should be done more concisely and programmatically. Indeed, this
is the approach you would take if you were creating a stand-alone .exe
program. Unfortunately, this approach does not work when constructing
.bin files. The reason is that most C compilers place array tables
into the uninitialized data segment, also known as the BSS. dBASE IV
could not consistently deal with data in the BSS and would return bad
values intermittently. Therefore, it was necessary to create a table
on the fly by assigning values to the array elements explicitly.
The program is available on the Ashton-Tate BBS if you wish to
download it.
Now for our final example involving StrDct and putting database files
in dictionary order. One of the advantages of .bin files is that they
have the capability of being used for indexing via the CALL() function
(as opposed to CALL command) in dBASE IV. If we have a database file
that we decide we want in dictionary order, or StrRev order, or StrFlp
order, all we need to do is the following:
USE Datafile
LOAD Strdct
INDEX ON CALL("Strdct",fieldname + "") TO Indexfile
dBASE IV will then call StrDct for each record so that it can place
our data in dictionary order. It is important to remember that
whenever we use this datafile and index in the future to LOAD StrDct
first. This indexing feature works well with StrFlp also. Suppose
you wanted to put your file in order by last name plus first name but
the first names needed to be in descending order, you could try this:
LOAD StrFlp
USE Namefile
INDEX ON lastname+CALL("StrFlp",firstname+"") TO Lfname
This puts the last names in ascending order but the first names in
descending order. You might be wondering why we need to add the + ""
to the firstname field. This prevents dBASE IV from permanently
changing the contents of the firstname field to whatever StrFlp would
return.
Well that about wraps up our journey into the world of C and dBASE
.bin files. I hope you've seen that .bin files are really not that
complicated once you understand a few of the guidelines and principles
involved in their creation. .bin files provide for useful and
powerful extensions to the dBASE language that in turn allow
programmers to develop and code more extensive and powerful programs.
StrRev.C
/* Program ...: Strrev.C
Author ....: Erik A McBeth
Version ...: dBASE III Plus 1.0, 1.1
dBASE IV 1.0, 1.1
(Tested compilers/assemblers)
Turbo C 1.5, 2.0 TASM 1.0
Microsoft C 5.1 MASM 5.1
*/
#define EXE 0 /* Set to 1 if we want to create an exe, If
we have an exe then run from DOS like this
STRREV string and you'll see your string
reversed */
#include "strlib.h" /* Various definitions */
#if EXE
#include "stdio.h"
main(argc,argv)
int argc;
char *argv[];
{
unsigned char *p;
p = (unsigned char *)argv[1];
#else /* Creating BIN file */
void far main() /* Need a FAR return */
{
unsigned char *p;
Getregs(); /* Get our memory registers */
p = (unsigned char *)MK_LONG(DS,BX); /* Get our string */
#endif /* If EXE */
if (p)
Strrev(p); /* Same operation if we do a bin or an exe */
#if EXE
printf("%s\n",p);
#endif
}
Strrev(str)
unsigned char *str;
{
unsigned char *p=str,ch;
/* Go to the end of the string and stop */
while(*++p);
/* Now swap the letters until we come to the middle of the string
*/
for(--p;p>str;p--,str++) {
ch = *p;
*p = *str;
*str = ch;
}
}
StrDct.C
/* Program ...: Strdct.C
Version ...: dBASE III Plus 1.0, 1.1
dBASE IV 1.0, 1.1
(Tested compilers/assemblers)
Turbo C 1.5, 2.0 TASM 1.0
Microsoft C 5.1 MASM 5.1
*/
#include "strlib.h"
void far main() /* very important, make sure we get a far return */
{
/* Tried to make this look familiar to 'C' programmers, Notice the
use of argc and argv, I've set argc to have a value of 2 to
simulate the routine being called from the DOS prompt */
int argc;
unsigned char *argv[6];
Getregs(); /* Assign memory registers */
/* Translate the parameter passed by dBASE IV into something we
can use, The argv[2] and argv[3] are placed here to show
you how to read multiple parameters */
argc = CX+1; /* Number of arguments */
argv[1] = (unsigned char *)*((int *)MK_LONG(ES, DI + 0));
argv[2] = (unsigned char *)*((int *)MK_LONG(ES, DI + 4));
argv[3] = (unsigned char *)*((int *)MK_LONG(ES, DI + 8));
if (argc>1) /* Do we have a string to use? */
Strdct(argv[1]);
}
Strdct(str)
unsigned char *str;
{
/* Had to do it this way, couldn't do "trnslt[]={" */
/* Read from AMENG.SO (Framework III). Table is case insensitive
*/
static unsigned char trnslt[256];
trnslt[ 0]= 0; trnslt[ 1]= 1; trnslt[ 2]= 2; trnslt[
3]= 3;
trnslt[ 4]= 4; trnslt[ 5]= 5; trnslt[ 6]= 6; trnslt[
7]= 7;
trnslt[ 8]= 8; trnslt[ 9]= 9; trnslt[ 10]= 10; trnslt[
11]= 11;
trnslt[ 12]= 12; trnslt[ 13]= 13; trnslt[ 14]= 14; trnslt[
15]= 15;
trnslt[ 16]= 16; trnslt[ 17]= 17; trnslt[ 18]= 18; trnslt[
19]= 19;
trnslt[ 20]= 20; trnslt[ 21]= 21; trnslt[ 22]= 22; trnslt[
23]= 23;
trnslt[ 24]= 24; trnslt[ 25]= 25; trnslt[ 26]= 26; trnslt[
27]= 27;
trnslt[ 28]= 28; trnslt[ 29]= 29; trnslt[ 30]= 30; trnslt[
31]= 31;
trnslt[' ']=' '; trnslt['!']='!'; trnslt['\"']='\"';
trnslt['#']='#';
trnslt['$']='$'; trnslt['%']='%'; trnslt['&']='&';
trnslt['\'']='\'';
trnslt['(']='('; trnslt[')']=')'; trnslt['*']='*';
trnslt['+']='+';
trnslt[',']=','; trnslt['-']='-'; trnslt['.']='.';
trnslt['/']='/';
trnslt['0']='k'; trnslt['1']='l'; trnslt['2']='m';
trnslt['3']='n';
trnslt['4']='o'; trnslt['5']='p'; trnslt['6']='q';
trnslt['7']='r';
trnslt['8']='s'; trnslt['9']='t'; trnslt[':']='0';
trnslt[';']='1';
trnslt['<']='2'; trnslt['=']='3'; trnslt['>']='4';
trnslt['?']='5';
trnslt['@']='6'; trnslt['A']='7'; trnslt['B']='>';
trnslt['C']='?';
trnslt['D']='A'; trnslt['E']='B'; trnslt['F']='G';
trnslt['G']='H';
trnslt['H']='I'; trnslt['I']='J'; trnslt['J']='O';
trnslt['K']='P';
trnslt['L']='Q'; trnslt['M']='R'; trnslt['N']='S';
trnslt['O']='U';
trnslt['P']='['; trnslt['Q']='\\'; trnslt['R']=']';
trnslt['S']='^';
trnslt['T']='_'; trnslt['U']='`'; trnslt['V']='e';
trnslt['W']='f';
trnslt['X']='g'; trnslt['Y']='h'; trnslt['Z']='j';
trnslt['[']='u';
trnslt['\\']='w'; trnslt[']']='x'; trnslt['^']='y';
trnslt['_']='z';
trnslt['`']='{'; trnslt['a']='7'; trnslt['b']='>';
trnslt['c']='?';
trnslt['d']='A'; trnslt['e']='B'; trnslt['f']='G';
trnslt['g']='H';
trnslt['h']='I'; trnslt['i']='J'; trnslt['j']='O';
trnslt['k']='P';
trnslt['l']='Q'; trnslt['m']='R'; trnslt['n']='S';
trnslt['o']='U';
trnslt['p']='['; trnslt['q']='\\'; trnslt['r']=']';
trnslt['s']='^';
trnslt['t']='_'; trnslt['u']='`'; trnslt['v']='e';
trnslt['w']='f';
trnslt['x']='g'; trnslt['y']='h'; trnslt['z']='j';
trnslt['{']='|';
trnslt['|']='}'; trnslt['}']='~'; trnslt['~']=127;
trnslt[127]=128;
trnslt[128]='@'; trnslt[129]='a'; trnslt[130]='C';
trnslt[131]=':';
trnslt[132]='8'; trnslt[133]=';'; trnslt[134]='9';
trnslt[135]='@';
trnslt[136]='D'; trnslt[137]='E'; trnslt[138]='F';
trnslt[139]='K';
trnslt[140]='L'; trnslt[141]='M'; trnslt[142]='8';
trnslt[143]='9';
trnslt[144]='C'; trnslt[145]= 0; trnslt[146]= 0;
trnslt[147]='W';
trnslt[148]='V'; trnslt[149]='X'; trnslt[150]='b';
trnslt[151]='c';
trnslt[152]='i'; trnslt[153]='V'; trnslt[154]='a';
trnslt[155]=129;
trnslt[156]=130; trnslt[157]=131; trnslt[158]=132;
trnslt[159]=133;
trnslt[160]='<'; trnslt[161]='N'; trnslt[162]='Y';
trnslt[163]='d';
trnslt[164]='T'; trnslt[165]='T'; trnslt[166]='=';
trnslt[167]='Z';
trnslt[168]=134; trnslt[169]=135; trnslt[170]=136;
trnslt[171]=137;
trnslt[172]=138; trnslt[173]=139; trnslt[174]=140;
trnslt[175]=141;
trnslt[176]=142; trnslt[177]=143; trnslt[178]=144;
trnslt[179]=145;
trnslt[180]=146; trnslt[181]=147; trnslt[182]=148;
trnslt[183]=149;
trnslt[184]=150; trnslt[185]=151; trnslt[186]=152;
trnslt[187]=153;
trnslt[188]=154; trnslt[189]=155; trnslt[190]=156;
trnslt[191]=157;
trnslt[192]=158; trnslt[193]=159; trnslt[194]=160;
trnslt[195]=161;
trnslt[196]=162; trnslt[197]=163; trnslt[198]=164;
trnslt[199]=165;
trnslt[200]=166; trnslt[201]=167; trnslt[202]=168;
trnslt[203]=169;
trnslt[204]=170; trnslt[205]=171; trnslt[206]=172;
trnslt[207]=173;
trnslt[208]=174; trnslt[209]=175; trnslt[210]=176;
trnslt[211]=177;
trnslt[212]=178; trnslt[213]=179; trnslt[214]=180;
trnslt[215]=181;
trnslt[216]=182; trnslt[217]=183; trnslt[218]=184;
trnslt[219]=185;
trnslt[220]=186; trnslt[221]=187; trnslt[222]=188;
trnslt[223]=189;
trnslt[224]=190; trnslt[225]= 0; trnslt[226]=192;
trnslt[227]=193;
trnslt[228]=194; trnslt[229]=195; trnslt[230]=196;
trnslt[231]=197;
trnslt[232]=198; trnslt[233]=199; trnslt[234]=200;
trnslt[235]=201;
trnslt[236]=202; trnslt[237]=203; trnslt[238]=204;
trnslt[239]=205;
trnslt[240]=206; trnslt[241]=207; trnslt[242]=208;
trnslt[243]=209;
trnslt[244]=210; trnslt[245]=211; trnslt[246]=212;
trnslt[247]=213;
trnslt[248]=214; trnslt[249]=215; trnslt[250]=216;
trnslt[251]=217;
trnslt[252]=218; trnslt[253]=219; trnslt[254]=221;
trnslt[255]=222;
/* Go through the string and substitute the new value based on the
value of the old character */
for(;*str;str++) {
*str = trnslt[(int)*str];
}
}
StrFlp.C
/* Program ...: Strflp.C
Version ...: dBASE III Plus 1.0, 1.1,
dBASE IV 1.0, 1.1
(Tested compilers/assemblers)
Turbo C 1.5, 2.0 TASM 1.0
Microsoft C 5.1 MASM 5.1
*/
#include "strlib.h" /* Some definitions */
void far main() /* Need a FAR return */
{
unsigned char *p;
Getregs(); /* Get our memory registers */
p = (unsigned char *)MK_LONG(DS,BX); /* Get our string */
if (p)
Strflp(p);
}
Strflp(str)
unsigned char *str;
{
/* Go through the string and subtract character ASCII value from 255
*/
for(;*str;str++) {
*str = (unsigned char) (((int)*str+128) % 256);
}
}
StrLib.C
/* Program ...: Strlib.H
Version ...: Use with
Strdct.C
Strflp.C
Strrev.C
Header file which contains various definitions and information
on how functions are called.
*/
#define TURBOC 0 /* Set true if compiler supports "pseudoregisters"
like
Turbo C, this way you don't have to link in
getregs.obj */
#define MK_LONG(hi,low) (((unsigned long)(hi) << 16) |
(unsigned)(low))
#define isdigit(ch) (ch >= '0' && ch <= '9')
#define isspace(ch) (ch==' ' || ch=='\t' || ch=='\r')
/* These are function "prototypes", some compilers don't like
these so you can delete them if they give a problem */
int Strdct(unsigned char *str);
int Strflp(unsigned char *str);
int Strrev(unsigned char *str);
int Getregs(void);
unsigned DS, BX, ES, DI, CX;
#if TURBOC
#define Getregs() (DS=_DS, BX=_BX, ES=_ES, DI=_DI, CX=_CX)
#endif
; Program ...: GetRegs.asm
; Version ...: dBASE III Plus 1.0, 1.1
; dBASE IV 1.0, 1.1
; (Tested assemblers)
; TASM 1.0
; MASM 5.1
; File loads global variables with the values of the actual memory
registers.
_DATA SEGMENT PUBLIC 'DATA'
EXTRN _BX:WORD ; These variables are in 'C' file
EXTRN _CX:WORD
EXTRN _ES:WORD
EXTRN _DI:WORD
EXTRN _DS:WORD
PUBLIC __acrtused,__chkstk ; Microsoft C needs to find these
__acrtused = 9876h
__chkstk = 0
_DATA ENDS
DGROUP GROUP _DATA
_TEXT SEGMENT PUBLIC 'CODE'
ASSUME CS:_TEXT,DS:DGROUP,ES:DGROUP,SS:DGROUP
PUBLIC _Getregs
_Getregs PROC NEAR
MOV _BX,BX
MOV _CX,CX
MOV _ES,ES
MOV _DI,DI
MOV _DS,DS
RET
_Getregs ENDP
_TEXT ENDS
CGROUP GROUP _TEXT
END
6 In Search Of July 1990 dBASE IV
This article is reprinted from the July 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.
In Search Of...
Chris Wilson
Many a new user to personal computers is often befuddled by the
maze-like quality of the directory of their hard disk. Often times,
people call Technical Support for help in finding a lost file. If you
don't know your way around your computer, one file among megabytes of
storage space may not be easy to spot.
Commercially available products by Norton, Mace, Central Point, and
others have helped in organizing directories and making files easy to
find. These products include utilities specifically designed to
search for files. However, some users new to the world of personal
computing may not yet know of their existence. Well, with a little
known and seldom used combination of DOS commands, you can create your
own file-find utility in the form of a DOS batch file. The batch
file, Search4.bat, is shown below.
Seach4.bat
@ECHO OFF
IF "%1" == "" GOTO MSG
IF "%2" == "" GOTO MSG
CHKDSK %1/V | FIND "%2" | MORE
GOTO END
:MSG
ECHO Syntax is SEARCH4 [drive:] [search string]
ECHO Example: SEARCH4 C: .DBF
:END
To start off, I use @ECHO OFF so that this will not be seen on the
screen as the commands are being executed. If you are using a version
of DOS earlier than 3.30, you will have to omit the @ symbol. The use
of this symbol suppresses the display of the ECHO OFF command itself.
In versions earlier than 3.30, ECHO OFF will be displayed on screen
while subsequent commands will not when the batch file is run.
We need to give this batch file two pieces of information: the drive
to search and the text to search for. We do this by using two
replaceable parameters, %1 and %2. Replaceable parameters are used to
supply information to the batch file from the command line at the time
of execution. DOS allows up to 10 parameters to be passed to a batch
file. These 10 parameters are numbered %0 to %9. It should be noted
that DOS reserves the %0 parameter for the name of the batch file
itself which actually leaves nine that can be used for other
information.
In our case, %1 represents the drive to be searched while %2
represents the string we're searching for. The batch file will
replace %1 with the first word on the command line after the batch
file name, and %2 with the second word. When specifying the string to
search for, please keep in mind that it must appear in all capital
letters or DOS will not find it. So, if you wantedto find any file
with a .txt extension on your C drive, you would type
SEARCH4 C: .TXT
from the DOS prompt
In Search4.bat, the lines IF "%1" == "" GOTO MSG and IF "%2" == ""
GOTO MSG, perform a test to see if the first and second parameters
have been entered. DOS uses the double equal sign (==) as a test of
equality between two items. If you type only SEARCH4 at the DOS
prompt, %1 will evaluate to a null value, thus triggering the batch
file to GOTO the procedure called MSG. Upon entering this procedure,
the message
Syntax is SEARCH4 [drive:] [search string]
Example: SEARCH4 C: .DBF
is displayed on the screen. This is done with the ECHO command. ECHO
will display any text entered after it as a message to the screen.
After this message is displayed, control is then passed to the
procedure called END which terminates the execution of the batch
file.
The line that reads
CHKDSK %1/V | FIND "%2" | MORE
is the heart of the batch file and does all of the work. The
CHKDSK-generated output that most users are used to seeing looks
something like this:
33421312 bytes total disk space
2048 bytes in 2 hidden files
184320 bytes in 73 directories
28913664 bytes in 1121 user files
4321280 bytes available on disk
655360 bytes total memory
527472 bytes free
However, by using the /V switch, you get an additional listing of all
files in all directories which looks something like this:
Directory C:\DBASEIV\SAMPLES
C:\DBASEIV\SAMPLES\CATALOG.CAT
C:\DBASEIV\SAMPLES\UNTITLED.CAT
C:\DBASEIV\SAMPLES\FORMBROW.COD
C:\DBASEIV\SAMPLES\ACCT_REC.DBF
.
If you've ever wondered what that symbol located above the backslash
on your keyboard is, it's known as the DOS "pipe" character. The pipe
takes the output of one program and turns it into input for the second
program. In our case, it reroutes the output of the CHKDSK utility
from the screen into the DOS FIND utility for processing. This method
is known as I/O redirection. (Please note that not all DOS commands
support I/O redirection.)
The FIND utility locates and displays each occurrence of a string
within a file. In Search4.bat, it searches through the output from
the CHKDSK utility for the string specified by the contents of %2.
Once the selected information is found, screen output is then
controlled by the MORE utility.
MORE has no real value by itself. It is primarily used with the DOS
pipe, taking output from other commands and displaying it on the
screen one page at a time. When there is more than a single page of
information to be displayed, the word
--MORE--
will display at the end of the first screen full of information.
Pressing any key at this point will display as many applicable file
names to fill the next screen.
There is one drawback to this batch file: it does not support the DOS
wildcards. When you enter "PRG" as the string to search for, Search4
will display every filename that contains that sequence of letters,
regardless of whether or not that string appears in the path,
filename, or extension. So when searching for specific extensions, be
sure to include a preceding period prior to the capatilized letters of
the desired extension.
And there you have it, an easy to use utility to locate your files
even if you only have part of a filename to go on. The next time you
need to find a file, this batch file will help you do it right
away.
For information about DOS Utility programs mentioned in this article,
you may contact:
Norton Utilities
Peter Norton Computing
100 Wilshire Blvd, 9th Floor
Santa Monica, CA 90401
800/365-1010
213/453-2361
Mace Utilities
Paul Mace Data Recovery
400 Williamson Way
Ashland, OR 97520
800/523-0258
503/488-2322
PC Tools
Central Point Software, Inc.
15220 N.W. Greenbrier Pkwy.
Beaverton, OR 97006
503/690-8090
December 10, 2017
Add comments