Dec 102017
A Clipper news letter with some helpful reviews and hints.
File STRALEY1.ZIP from The Programmer’s Corner in
Category Dbase Source Code
A Clipper news letter with some helpful reviews and hints.
File Name File Size Zip Size Zip Type
UPLOAD.TXT 26797 9855 deflated

Download File STRALEY1.ZIP Here

Contents of the UPLOAD.TXT file

The following text is a preliminary issue of my upcoming newsletter
called "FROM D.O.S.S."(tm) From the Desk of Steve Straley - published
by Four Season Publishing.

When you read this, please let me know the following:

1: You found this helpful.
2: This is the type of thing you'd like to have 2 times a month.
3: You want more of this information.
4: Where can you get more?

The contents of this file are Copyright (c) 1988 by

Stephen J. Straley & Associates
319 Barrow Street - 7B
Jersey City, NJ 07302

All rights reserved.

No part of this text or the accompanying source code may be
reproduced or transmitted in any form or by any means, electronic,
mechanical, photocopying, recording, or otherwise, without the prior
written consent of the publisher, except that the procedures,
functions and other routines contained herein may be incorporated
into programs, applications and routines by the general BBS
community. The concepts and ideas are placed into public domain;
however, the context of the descriptions, wordage, and/or phrases
are not.


* Begin FROM D.O.S.S.

1 Review, Previews, Views

1.1 Review

After seeing this question repeated over and over again, I
thought it valuable to make a brief review on text editors,
namely BRIEF(tm) by Solution Systmes(tm).

Without hesitation, in most circumstances I would want to work
with BRIEF and dBRIEF hands down. There are, of course,
advantages and disadvantages and I will point out a few of each.
But for the dedicated Clipper programmer, BRIEF is the editor of
choice. The list of features and benefits are numerous. And if
you obtain the add-on macro library called dBRIEF, then the

powers of BRIEF are REALLY tapped. Before this editor, I was not
accustomed to column marks and column moves. Now, I do not think
I could live without it. Additionally, the command library is
powerful. I remember in one instance I had to de-bug someone
else's code at First Boston and their coding style was very
C-like: everything was in lower case. I found it difficult to
read and thought how nice it would be to mark off a block of text
and convert it to uppercase (or lowercase if the demand was
there). BRIEF had such a command in it's library and my headache
went away as I worked on the code.

The windowing capabilities are wonderful. In other packages, I
was allotted only 4 windows of equal size. Not so in BRIEF. I
can adjust the borders of any window (up to a point) and have far
more than just 4 of them appear on the screen. Additionally, I
can have two windows look at the same code. I find this very
helpful in long programs where I am adjust something at the top
of the file and at the bottom of the file. By splitting the
window and looking at the same text in different locations in
both windows, I no longer had to bounce back and forth between
page marks.

More features and benefits are with the dBRIEF library. Here,
automatic indenting takes effect and the ability to display
database structures while editing has saved me countless hours.
And with all of this, the on-line help system is extensive,
sensitive, and thorough. When I was developing the Steve
Straley Text Editor (in the ToolkiT) I constantly thought of how
it is implemented in BRIEF because I felt they had a better feel
for creating a product to the developers market. I want that

Now for a couple of problem spots. I find BRIEF too slow to
load up, especially on a regular PC-XT. For that machine, I
still used my old editor EDIX(tm) by Emerging Technologies. The
load time is critical to me, especially in the debugging stages.
I do not like the idea of waiting for my editor to take 30 to 50
seconds to allow me at my text. In that amount of time, I would
typically have the problem fixed and the re-compilation started
again using a product like EDIX. Finally, I also found a small
glitch when trying to use the display database structure feature
in dBRIEF and if my SET CLIPPER= parameters at the DOS line was
set. It seems that the SET CLIPPER= command conflicts with the
buffering and allocation of memory within the dBRIEF library.
The only solution I found was to un-set my SET CLIPPER= to
option, edit the document, compile, link, and then re-set my
Clipper switch at the DOS command line. This can be a bit of
nuisance, but then again, I can think of other editors and the
nightmares I have had so I am willing to live with this.

All in all, whenever I am asked which editor I would recommend,
I first find out the needs and wants of the user, the machine
they are using, and then 90% of the time,. I respond: BRIEF,

Solution Systems(tm)
541 Main Street, Suite 410
South Weymouth, MA 02190


With this issue I would like to look at the dBASE standard's
committee and what it may mean to the Clipper community in

I have a sincere problem with the idea of standards. I have been
involved with standard committees and have been subject to the
whims of standards. Eventually, creativity and progress looses
out to a rule or a standard. The concept of red-tape, mastered
by the government, only thawrts development of new technology.
One of the problems with a standard is where does one draw the
line between a standard implementation and an added feature.
Another may be in some cases where the developer's approach to a
language may mean an inability to completely comply with a
standard. Now this does not mean that I do not understand nor
disagree with the conception of a standard. This is the furthest
from the truth. Standards are important up to a point, but I
feel that right now in the general stage of database technology,
that point is no longer valid. If the standard was in place
before the other vendors took to the market, I would not have a
problem. This would allow them to develop their own areas of
expertise, yet knowing how to work within the parameters of the
standard and the committee.

The concept of dBASE is the standard, and Ashton-Tate does not
own it. They have a claim to its' implementation, but not it's
form. Foxbase took to the speed issue while Clipper skimmed the
speed issue in favor of power and flexibility. Quicksilver
favors another niche in the market clearly defined by their
marketing people. The implementation of these three approaches
should be left in tact while the standard is centered around how
Ashton-Tate handles the subject, whether be it good or bad. I do
not want to nit-pick as to how the three competitors have added
to the language their own styles. I chose Clipper because of the
power and the flexibility and I do not want some committee urging
Nantucket to give that approach up in favor of a standard. From
my perspective, let's make the Clipper language the standard and
ask the other's to follow suit. Now wouldn't that be something!

I also have an ethical problem with the committee. It should not
be headed by Marty Reinhardt, president of WallSoft, Inc. For
every dBASE-like tool he comes out with, he has to try to support
the various versions of the language including the core dBASEIII
language. The support and coding cost are that much greater
because of the special nuances between the languages that he
MUST support. It would benefit his company in profits by being
able to cut general operating costs just because the language has
been standardized and his products would then only support that
base. Because he is in a position to gain financially if the
language is standardized, I feel his altruistic motives are
really backed by financial means. The committee should be
impartial to the language implementation made by ALL vendors, and
only the best approach should be made the standard. Looking at
the others, I would tend to think that Clipper would indeed
become the standard. If this were not so, just look back at the
progress of the language. Which product initially gave the VALID
clause, which introduced user-defined functions, which allowed
both FOR and WHILE clauses, and which introduced arrays. Indeed
the list goes on.

In this day, I think we should all be standardizing around
progressing the industry. For example, I would rather support a
standard that restricted companies from announcing "release
dates" too early, from accepting money before product is ready,
and from avoiding and dis-regarding the truth over problems. If
the standard committee wants to improve the dBASE language, they
should ask Ashton-Tate to listen to the developing community a
bit better, tell FoxBase to open it's architecture to other
languages (like C), tell Wordtech to quit scattering the
company's efforts in too many places and focus, and to Nantucket
to quit being perceived as a junior Ashton-Tate. On top of all
this, maybe a standard in common sense is in order. That's my
opinion, I'd be glad to hear yours!

1.3 Announcements

I am really pleased to announce that several projects are now off
of my desk. The latest being the Steve Straley Clipper(IRMA)
Library(tm). Yes, I've finally joined the rank and file members
of the programming academy to release a special library of useful
functions that added to the Clipper development cycle. However,
this library is rather specialized and unique.

In rough terminology, direct mainframe communication to and from
a Clipper application is now possible within the Clipper
environment. In the past, any mainframe communication had to be
accomplished via BASIC or C sub-routines in a BASIC or C
program. For dBASEIII compiled applications, this meant the use
of the RUN command and a stand-alone C/BASIC program to perform
all of the special keystrokes for the mainframe. Not so any
more. Mainframe screens can be saved to a Clipper memory
variable, mainframe applications can work simultaneously with the
Clipper application, and uploads and downloads are even easier to
watch and write.

2 Watch Out for


Do you know that it is necessary to link in the EXTEND.LIB file
for a simple ALLTRIM() function. Sometimes for simplicity I
find it easier to code an LTRIM(TRIM()) function combination
rather than the ALLTRIM() function. It seems that in those
cases, I forget to link in the EXTEND.LIB and have to re-link the
program again. But what is more interesting are the facts.
Consider a small test example.

x = SPACE(10)

This code fragment takes up 62 bytes in the code table, 64 bytes
in the symbol table, and 2 bytes in the constant table. When
linking this with EXTEND.LIB and CLIPPER.LIB, the exectuable file
comes out to 160,620 bytes.

Now, consider this code fragment:

x = SPACE(10)

This code fragment takes up 62 bytes in the code table, 48 bytes
in the symbol table, and 2 bytes in the constant table. And when
linking in only the CLIPPER.LIB, the executable file comes out to
158,682 bytes.

Now why would an "added" function to the Clipper language require
more symbol, executable, and load memory space than the
conventional LTRIM(TRIM()) combination. The point to keep in
mind is that the LTRIM(TRIM()) function combination should be
used in place of the ALLTRIM() function.


You can get this error if the help (F1) key is specifically set
to a procedure which is not linked into the application. For
example, SET KEY 28 TO Inhelp. When compiling and linking, the
procedure Inhelp is not included. This will not cause the error
because the SET KEY TO command is more like an interpreter-based
command and will not be totally evaluated until the special key
is pressed. However, if that key is pressed, at the conclusion
of the operation being performed, an MISSING EXTERNAL (in VALID)
message will appear along with some garbage across the top line
of the screen. The last being no more than a bad pointer
displaying junk buffer information. The key to this is to
remember that the error is NOT in any VALID clause nor is it
necessarily in the line number/proc name displayed to the

2.3 ASORT() is not sorting

In a new development project that I am working on I had to use
the ASORT() function. In this discovery I double checked plenty
of possibilities before calling Nantucket, including the time
stamp of my Clipper.lib (2:00 am), the length of the element (all
were test to be the same), the data types of the elements (all
were character types), and if I am calling the function
improperly (I wasn't). Below is a fragment of a TEST.PRG which
I have sent off to Nantucketland (which is right next to
Adventureland), as well as a screen dump of the output.

USE T.yac
b = 0
COUNT TO b FOR ( level + toggl = "1 0 " .AND. !DELETED() .AND. ;
EMPTY(field) )
DECLARE order[b]
SET FILTER TO ( level + toggl = "1 0 " .AND. !DELETED() .AND. ;
EMPTY(field) )
FOR x = 1 TO b
order[x] = CHR(64 + order) + SUBSTR(code, 1, 12) + SUBSTR(text, 1, 30)
? "Before"
FOR x = 1 TO b
? order[x], STR(LEN(order[x]))
? "After"
FOR x = 1 TO b
? order[x], STR(LEN(order[x]))

* Here is the screen dump of the above code.

CACCOUNTS Chart of Account Information
AITEMS This is the Item file
BPAYMENT This is a payment order file

CACCOUNTS Chart of Account Information
AITEMS This is the Item file
BPAYMENT This is a payment order file

If you are like me, these three elements DON'T look sorted. After
having another developer look at the problem, the work-around is
simple. The name of the array can not be the same as a field in a
database. Though the ASORT() yielded NO run-time error, it didn't
work. As soon as I changed the name of the array "order" to
something else (like "aorder"), ASORT() worked. A rule in advance:
make sure arrays have unique names other than fields... even with the
-v parameter.


3.1 Explanation

For this issue, I thought that it would be of some benefit to
give an in-depth look at the new enhanced DBEDIT() function.
Originally, this function was designed to allow for some
primitive browse-like capabilities in a Clipper application.
Since its inception and with the release of the Summer 87, these
crude beginnings have been transformed into a powerful and useful
tool. First let's look at the specifics of the function, a
better look the "modes" and the return values expected from the
user-defined function. Next, we'll look at a basic possibility
with the DBEDIT() function. Finally, some possibilities in
tapping other functions and other collective powers in
conjunction with the DBEDIT() function.

3.2 Simple - The UDF in DBEDIT()

While DBEDIT() maintains its basic configuration from the Autumn
86 days, the major new enhancement with this function is the
ability to pass DBEDIT() the name of a user defined function.
The parameter must not include the parentheses and will not permit
the name of a value (parameter) to be passed to it. Think of the
basic root name of a user defined function, or any function for
that matter. For example, the end-of-file function's root name
is "EOF", while the root name for the sub-string function would be
"SUBSTR". The user defined function you could program for will
have a similar root name.

Now, the reason the programmed user-defined function need not
have any parameters passed to it is because Clipper passes 2
values to it automatically. The first parameter is the status
parameter. It tells the user defined function the internal
status condition of DBEDIT(). The second parameter points to the
array element representing the field being worked on. Or, if the
field array is not passed to DBEDIT(), this parameter will point
to the current field being worked on in the database. If the
user defined function is specified, every keystroke will be
analyzed through that function. While Clipper will assume some
default actions for the keystrokes, you can capture them and
change their meanings before DBEDIT() performs any action. For
example, if the down arrow is pressed and you want it to mean to
go to the end of file, you can program the user defined function
to look at that keystroke and to tell DBEDIT() to do something

The status parameter is nothing more that a conditional
statement telling your function what is the condition of the
DBEDIT() function. This does not analyze the keystroke being
performed nor does it dictate what will happen inside of the
DBEDIT() function. Even though the status parameter may dictate
a keystroke exception (by having a value of 4), this does not
mean that specific keystrokes can be captured and re-directed.
Therefore, in order to capture all keystroke, I suggest that
initially, you write a user defined function for DBEDIT() to look
something like this:

FUNCTION Keystroke

PARAMETERS dbstatus, dbfield

whatkey = LASTKEY()

@ 22,00 CLEAR
@ 22,00 SAY " The Status of DBEDIT() => " + STR(dbstatus)
@ 23,00 SAY " The field pointing to => " + STR(dbfield)
@ 24,00 SAY "The ASCII value of keystroke => " + STR(whatkey)

Try working with ALL sorts of key values including ALT Z, and ALT
=. You'll find that these keys will have values as well. What
this function gives you is a beginning foundation to see all of
the possible key combinations available.

Keystroke manipulations are simple. For example, the first field
is restricted (i.e. the cursor is not allowed on the field), and
the cursor is placed to the next field over. The above function
could then look like this:

FUNCTION Keystroke

PARAMETERS dbstatus, dbfield

whatkey = LASTKEY()

@ 22,00 CLEAR
@ 22,00 SAY " The Status of DBEDIT() => " + STR(dbstatus)
@ 23,00 SAY " The field pointing to => " + STR(dbfield)
@ 24,00 SAY "The ASCII value of keystroke => " + STR(whatkey)
IF dbfield = 1
KEYBOARD CHR(4) && This is ^D, or Right-Arrow

And having the ability to toggle the keystrokes to mean special
things is now a viable reality. For example, in the DBEDIT()
function, the ability to quickly access a DOS Shell, only to
return to the function with the word "EXIT" is typed in at the
DOS command line. The Keystroke Function would then look
something like this:

FUNCTION Keystroke

PARAMETERS dbstatus, dbfield

whatkey = LASTKEY()

@ 22,00 CLEAR
@ 22,00 SAY " The Status of DBEDIT() => " + STR(dbstatus)
@ 23,00 SAY " The field pointing to => " + STR(dbfield)
@ 24,00 SAY "The ASCII value of keystroke => " + STR(whatkey)
IF dbfield = 1
KEYBOARD CHR(4) && This is ^D, or Right-Arrow
CASE whatkey = 300
oldcolor = SETCOLOR()
dos_row = ROW()
dos_col = COL()
@ 0,0 SAY "Type 'EXIT' to Return to Program"
RUN \Command
@ dos_row, dos_col SAY ""

3.3 Medium - Using the various parameters

The next level to conquer with the DBEDIT() function is to look
at the various array parameters and what they entail. The most
interesting possibility with these parameters is the ability to
add not only field information, be function information as well.
Let me explain. Say you want to show a database with DBEDIT(),
but you want the corresponding record number to appear along with
the other fields in that database. Since the record number is
NOT a field in the database, how can it appear as a field.
Simple! First, DECLARE the field array to be one greater than
the maximum. Next, store the names of the fields into that array
starting with the SECOND array element. Finally, think of the
RECNO() function as an array value that can be expanded "as-is"
when DBEDIT() is first coming up to the screen. To accomplish
all of this, here is a possible code extract!

FOR x = 2 TO FCOUNT()+1
names[x] = FIELDNAME(x - 1)
names[1] = "RECNO()"

By off-setting the X variable, the subscript range for the array
is also off-set. But keep in mind that the name of the field is
one element behind the array element counter and must be adjusted
accordingly. Finally, note that the name of the function is now
an element in the NAMES array. When called with DBEDIT(), the
element will be expanded and RECNO() will be evaluated. This
will cause the current record number to appear as a field in the

Now, let's say a code value in a database is defined in another
database. For example, a customer account code in an invoice
database. It would do no good to have the code appear in the
display area for DBEDIT(). Instead, it is possible to include
the name of a field IN ANOTHER DATABASE as an element in the
array of fields to be displayed by DBEDIT(). The point to this
exercise is to encourage you to think beyond the narrow means of
just one database and the field therein. Think of all of the
tools available to you: standard functions, user defined
functions, and fields in other databases. For this example
consider the following code extract:

USE Client INDEX Code
USE Invoice INDEX Code
FOR x = 2 TO FCOUNT()+1
names[x] = FIELDNAME(x - 1)
names[1] = "RECNO()"
names[2] = "B->Comp_name"

If the relation between the two databases are correct and the tie
is based on the "CODE" field in both databases, then DBEDIT()
will display properly. As the field element in the array is
evaluated, it will note to look over to the field in the second
database ("B->"), which will be the corresponding "comp_name"
field. As the pointer in the main database moves, so will the
pointer in the related database. This means that DBEDIT() can
display related information easily, and that with DBEDIT(),
information can mean something more than just code names and

3.4 Difficult - Not Using the various parameters.
Now this next technique takes the proceeding foundations and goes one
step further into the potential. It is possible to combine text and
field information in a database, in the display window of DBEDIT().
For example, in my most recent project, I wanted to have a listbox of
my defined databases and corresponding indexes. One solid bar
bounced through the information in the windowed area, and the text
looked something like this:

Established Indexes

DBF => Name Index => Name1
*DBF => People Index => People
DBF => Vendor Index => V_order
*DBF => Places Index => Places
*DBF => Invoice Index => Order
DBF => Table Index => Table

The astericks on the side signify whether the record in the database
is marked for deletion or not. Now, it is VERY easy to combine text
and field information within a single area inside DBEDIT(). I will
show the coded extract from this application. The Procedures
PUSHSCREEN and POPSCREEN are part of the "Steve Straley Toolkit(tm)"
published by Four Seasons Publishing Co., Inc.

DECLARE disp_it[1]
disp_it[1] = "SAYINDEX()"
thefile = "Established Indexes"
DO Pushscreen WITH 4,34,17,75,.T.,.T.,.T.,.T.
DBEDIT(5,35,16,74,disp_it, "INPUTIT2",.F.,thefile)
DO Popscreen WITH 4,34,17,75,.T.




whatkey = LASTKEY()
IF whatkey = 27

IF instat = 4 .AND. whatkey = 7



RETURN(FILL_OUT(IF(DELETED(), "*", " ") + "DBF => " + ;
SUBSTR(A->code, 1, 8) + " Index => " + ;
TRIM(SUBSTR(A->code, 20,12)), 40))

There are several tricks involved. First, the name of a user-defined
function is passed as a field element in the array which in turn is
passed to DBEDIT(). Second, there is only 1 parameter accepted in the
user-defined function called by DBEDIT(). In this example, that
would be Function Inputit2. If the second parameter is accepted,
there would be a MACRO EXPANSION error because DBEDIT() would try to
find a field named "DBF =>". Since this is text, there is no such
field. Just because Clipper passes a parameter to the function,
does not mean we have to have it. This trick does not corrupt the
stack. I have been running this trick now for a month without
any hint of a problem... even without the 2:00 am library.

4 Next Issue

In the next issue, let's look at a few more items with the
DBEDIT() function including multiple databases, add-hoc data
editing, more list-box technology, and using DBEDIT() in conjunction
with other Clipper features. Also, I'll look at the concept of
dBASEIV and what to possibly expect from Nantucket.

* End of FROM D.O.S.S.

 December 10, 2017  Add comments

 Leave a Reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>