[comp.databases] dBASE IV -- Trials and Tribulations

erbo@lime.ucsb.edu (Eric J. Bowersox) (07/16/89)

Sorry if these bugs have appeared in this newsgroup before, but _I_ cer-
tainly haven't seen them.

At my workplace, I am currently engaged in what amounts to almost a complete
rewrite of a system for expense tracking that I had written some time
earlier in dBASE III Plus.  The new version is being written in dBASE IV,
and uses many of the neat features of that system (like pulldown menus and
.MDX files), but in the process, I have run into some very frustrating
system bugs.  Here are a couple I haven't heard mentioned.

BUG #1:	The Case of the Missing Procedure  (or: Son of Line Eater? :-) )

If you have a .PRG file with more than about 8 procedures in it, and you
try to call the last procedure in that file, dBASE won't find it, and will
hand you a "File does not exist" error.

WORKAROUND: Easy.  Just place a dummy procedure at the very end of each
file that might have a large number of procedures in it, like:

PROCEDURE Blort
*** Never call Blort!
RETURN 

Unfortunately, this bug also affects generated report and label forms,
since the code generator generates dBASE code, and that code has quite a
few procedures in it, the last one of which (Reset) is affected by this bug.
There are two things you can do with this:  edit the .FRG/.LBG files directly
to insert the aforementioned dummy procedure (which you have to then do every
time you re-generate the report), or (if you have the Developer's Edition)
edit the templates to include that procedure at the end.  Since we do have
the "Gold" edition, I chose the latter course, and it works fine.

(Postscript:  I looked at one of the .DBO files under Norton Utilities, and
it seems that, at the end of the file, they have some sort of jump table
showing where all the procedures are; the last procedure was missing from
this table.  So, apparently, the compiler is just forgetting to stick that
last entry in.  Or am I off base here?)

BUG #2: PLAYing Loop-the-Loop with MACROs

At one point in my application, I wanted to send a certain number of down-
arrow keys to the keyboard, but that number might vary at certain times (it
was to position the light-bar of a POPUP menu on a certain item).  So, I
used the Tools/Macros (or something like that) menu in the Control Center to
define a macro consisting of one down arrow key, called DOWNARROW.  Now, with
that macro suitably loaded in my program, you would think the right thing to do would be:

I = 1
DO WHILE I <= NDOWNS	&& the number of down arrows I want
  PLAY MACRO DOWNARROW
  I = I + 1
ENDDO

Right?  WRONG!  Apparently, on the second iteration of this loop, dBASE stopped
my program with an error about "recursive macros" or somesuch (I don't remember
the exact error message, but it was very irritating).  My guess is it doesn't
like to see the same PLAY MACRO twice in a row.

WORKAROUND:  In this case, it wasn't so bad; I just had to remember my binary.
I defined three more macros consisting of two, four, and eight down arrow
keys, called DOWN2, DOWN4, and DOWN8 respectively.  Now my code looked like
this:

I = NDOWNS
IF I >= 8
  PLAY MACRO DOWN8
  I = I - 8
ENDIF
IF I >= 4
  PLAY MACRO DOWN4
  I = I - 4
ENDIF
IF I >= 2
  PLAY MACRO DOWN2
  I = I - 2
ENDIF
IF I >= 1
  PLAY MACRO DOWNARROW
  I = I - 1
ENDIF

Of course, this means you can't play more than 15 down arrows this way, but
since I didn't even need _that_ many maximum, I let it go.  Besides, this
method is extendable if necessary.  Even better, though, would have been
an "ACTIVATE POPUP name BAR number" syntax, similar to "ACTIVATE MENU name
PAD pad_name."

Now, the obligatory disclaimer:  I am not an Ashton-Tate affiliate in any
way, shape, or form, except as YAPSWDBIV (yet another programmer struggling
with dBASE IV), and I sincerely feel that, as long as I can keep hacking
around the difficulties, the advantages of the new system greatly outweigh
the disadvantages, and I will definitely be interested when A-T ships their
v.1.1.  So, no flames from the A-T-bashers, please!

Hope someone, somewhere, can use this :-) :-) :-).

| Disclaimer:  This posting is definitive.  Reality may have got it wrong.    |
| * Eric J. Bowersox (ERBO) *  LIVE! from Isla Vista, California!             |
| erbo@cornu.ucsb.edu ...!{ucbvax,ucsd}!ucsbcsl!cornu!erbo                    |
| If wishes were DRAMs, I don't think we would worry about the Japanese. -me  |

awd@dbase.UUCP (Alastair Dallas) (07/19/89)

In article <2071@hub.UUCP>, erbo@lime.ucsb.edu (Eric J. Bowersox) writes:
> 
> BUG #1:	The Case of the Missing Procedure  (or: Son of Line Eater? :-) )
> 
> If you have a .PRG file with more than about 8 procedures in it, and you
> try to call the last procedure in that file, dBASE won't find it, and will
> hand you a "File does not exist" error.

I couldn't get this to fail.  I created a 150-line prg containing 14 simple,
but not empty procedures.  The main proc just called the others, and the last
one was never ignored.  Is it possible your code is unusual enough that the
nesting on RETURN statements looks okay to you but looks different to dBASE?
(That's fairly unlikely, though--the keyword PROCEDURE automatically ends
the preceding procedure and the compiler would complain then about missing
control structures like ENDIFs.)

Can you send me a simple failing test case?

> BUG #2: PLAYing Loop-the-Loop with MACROs
> 

This is out of my area; the fellow I would ask about MACRO questions had
a small accident this morning and wound up in the hospital, so I can't help
you.  It sounds like you're playing a game we play here a lot--let's figure
out what the software is _supposed_ to do by trial and error.  Sometimes
subsystems are not very clearly defined (documentation and spec writers 
are not programmers, and, to be fair, vice versa).  Looks like you figured
out what we intended the hard way.  I'm glad the workaround wasn't too
difficult for you.

Thanks for presenting such clear cases.  I think the people on comp.databases
who don't hit 'N' when the subject line says 'dBASE' appreciate hearing 
about any bugs or unexpected features (:-).  I'll try to investigate any
reports of bugs, and although I'll probably shy away from saying "That's
a bug!" (unless I know it will be fixed in v1.1), I may explain why some
behavior might be considered meritorious in such a way that you can read
between the lines.  :-)

/alastair/

awd@dbase.UUCP (Alastair Dallas) (07/19/89)

In article <167@dbase.UUCP>, awd@dbase.UUCP (Alastair Dallas) writes:
> In article <2071@hub.UUCP>, erbo@lime.ucsb.edu (Eric J. Bowersox) writes:
> > 
> > BUG #1:	The Case of the Missing Procedure  (or: Son of Line Eater? :-) )
> > 
> > If you have a .PRG file with more than about 8 procedures in it, and you
> > try to call the last procedure in that file, dBASE won't find it, and will
> > hand you a "File does not exist" error.
> 
> ...Is it possible your code is unusual enough that the
> nesting on RETURN statements looks okay to you but looks different to dBASE?
> (That's fairly unlikely, though...

The software designer here who is actually responsible for procedures had
a better explanation.  He points out that dBASE III PLUS (and IV to be 
compatible) will allow duplicate procedure names and that only the first
procedure encountered will then be "found."  Procedure names are case-
insensitive and silently truncate (I think) at some limit (approx. 10),
thus MyProcedureName and MYPROCEDURENAME2 are identical; the compiler
generates no warning about this (maybe it should).

Hope it helps.

/alastair/

erbo@mango.ucsb.edu (Eric J. Bowersox) (07/20/89)

In article <167@dbase.UUCP> awd@dbase.UUCP (Alastair Dallas) writes:
:In article <2071@hub.UUCP>, erbo@lime.ucsb.edu (Eric J. Bowersox) writes:
:> BUG #1:	The Case of the Missing Procedure  (or: Son of Line Eater? :-) )
:I couldn't get this to fail.  I created a 150-line prg containing 14 simple,
:but not empty procedures.  The main proc just called the others, and the last
:one was never ignored.

After seeing this message for the first time, I ran back to my machine at
work (a true-blue IBM PC-XT with 20Mb hard disk -- slow! :-( ) and tried
to duplicate the problem.  Here's what I came up with:

*** DBFail.prg
*** A failing test case that demonstrates the procedure bug.
***************************************
***
*** Set up error logging.
***
set talk off
set space off
set alternate to "dbfail.run"
set alternate on
on error ? "Error ",error()," at ",program(),"-",lineno(),": ",message()
***
*** Test procedure calls.
***
? "Now starting the procedure call test..."
do test1
do test2
do test3
do test4
do test5
do test6
do test7
do test8
do test9
do test10
***
*** Finish up.
***
? "End of DBFAIL.PRG"
set alternate off
close alternate
return

*** The test procedures, ten in number.

procedure test1
  ? "Procedure TEST1 called!"
return

[The remaining procedures omitted for brevity, but they're just like the first
one]

*** EOF: DBFail.prg

Well, after compiling this with "compile dbfail" and getting no errors (I
know you don't need to do that, since dBASE will do it for you, but I just
wanted to make sure :-) ), I ran the program and got the following results
in DBFAIL.RUN:

Now starting the procedure call test...
Procedure TEST1 called!
Procedure TEST2 called!
Procedure TEST3 called!
Procedure TEST4 called!
Procedure TEST5 called!
Procedure TEST6 called!
Procedure TEST7 called!
Procedure TEST8 called!
Procedure TEST9 called!
Error          1 at DBFAIL-        25: File does not exist
End of DBFAIL.PRG

A-ha!  You think!  Well, I thought of something else, and rebooted my machine,
selecting "local boot" instead of "network boot." (Oops!  Forgot to mention,
my machine is a workstation on a LAN, but dBASE IV was installed single-user
on my own hard disk.)  Cranking up dBASE IV, recompiling DBFAIL.PRG, and
running it again produced the following:

Now starting the procedure call test...
Procedure TEST1 called!
Procedure TEST2 called!
Procedure TEST3 called!
Procedure TEST4 called!
Procedure TEST5 called!
Procedure TEST6 called!
Procedure TEST7 called!
Procedure TEST8 called!
Procedure TEST9 called!
Procedure TEST10 called!
End of DBFAIL.PRG

Checking both .DBO files out with LIST (the program LIST.COM, not dBASE's
LIST command :-) ) in hex dump mode revealed, sure enough, the one compiled
with the LAN on was missing the last entry in its "jump table" (for procedure
TEST10), but the one compiled with the machine booted locally had it in its
correct place.  Logically, though, both files should have been identical
whether the LAN was present or not.  I am afraid I am totally at a loss to
explain this, but I have a sneaking suspicion that Alastair is probably
right.  (sigh)

:Thanks for presenting such clear cases.  I think the people on comp.databases
:who don't hit 'N' when the subject line says 'dBASE' appreciate hearing 
:about any bugs or unexpected features (:-).

You're welcome.  Actually, I tune in _just_ to find out anything new I can
about dBASE...as my paycheck depends on it, this is understandable :-).
I think it's probably worth it in the end, though.

| Disclaimer:  This posting is definitive.  Reality may have got it wrong.    |
| * Eric J. Bowersox (ERBO) *  LIVE! from Isla Vista, California!             |
| erbo@cornu.ucsb.edu ...!{ucbvax,ucsd}!ucsbcsl!cornu!erbo                    |
| If wishes were DRAMs, I don't think we would worry about the Japanese. -me  |

dukel@dbase.UUCP (Duke Luper) (07/20/89)

In article <2071@hub.UUCP>, erbo@lime.ucsb.edu (Eric J. Bowersox) writes:
> Sorry if these bugs have appeared in this newsgroup before, but _I_ cer-
> tainly haven't seen them.
> 
> At my workplace, I am currently engaged in what amounts to almost a complete
> rewrite of a system for expense tracking that I had written some time
> earlier in dBASE III Plus.  The new version is being written in dBASE IV,
> and uses many of the neat features of that system (like pulldown menus and
> .MDX files), but in the process, I have run into some very frustrating
> system bugs.  Here are a couple I haven't heard mentioned.
> 
> BUG #1:	The Case of the Missing Procedure  (or: Son of Line Eater? :-) )
> 
> If you have a .PRG file with more than about 8 procedures in it, and you
> try to call the last procedure in that file, dBASE won't find it, and will
> hand you a "File does not exist" error.
> 
> WORKAROUND: Easy.  Just place a dummy procedure at the very end of each
> file that might have a large number of procedures in it, like:
> 
> PROCEDURE Blort
> *** Never call Blort!
> RETURN 
> 
> Unfortunately, this bug also affects generated report and label forms,
> since the code generator generates dBASE code, and that code has quite a
> few procedures in it, the last one of which (Reset) is affected by this bug.
> There are two things you can do with this:  edit the .FRG/.LBG files directly
> to insert the aforementioned dummy procedure (which you have to then do every
> time you re-generate the report), or (if you have the Developer's Edition)
> edit the templates to include that procedure at the end.  Since we do have
> the "Gold" edition, I chose the latter course, and it works fine.
> 
> (Postscript:  I looked at one of the .DBO files under Norton Utilities, and
> it seems that, at the end of the file, they have some sort of jump table
> showing where all the procedures are; the last procedure was missing from
> this table.  So, apparently, the compiler is just forgetting to stick that
> last entry in.  Or am I off base here?)
> 

Attempt to find BUG1:  Now, I'm not doubting that this happened to you.  I
just wanted to try it for myself and report to you my findings.  I ran the
following program:

* DUKE.PRG

do 1
do 2
do 3
do 4
do 5
do 6
do 7
do 8
do 9
do 10
do 11
do 12
do 13
do 14
do 15
do 16
do 17 
do 18
do 19
do 20
return

proc 1
? "this is proc 1"
return

proc 2
? "this is proc 2"
return
.
.
.
proc 20
? "this is proc 20"
return
* EOF: duke.prg

Here is the output:

this is test 1
this is test 2
this is test 3
this is test 4
this is test 5
this is test 6
this is test 7
this is test 8
this is test 9
this is test 10
.
.
this is test 19
this is test 20

Unfortunately, I couldn't reproduce it.  I also tried SET PROC TO DUKE and
removed all of the DO's and the first RETURN from DUKE.PRG and placed them
in TEST.PRG and ran TEST.PRG.  The output was the same.  I am running dBASE 1.0
on a Novell network (SFT Netware 286 V2.12) on a Compaq 386/16 with a HICARD.

> BUG #2: PLAYing Loop-the-Loop with MACROs
> 
> At one point in my application, I wanted to send a certain number of down-
> arrow keys to the keyboard, but that number might vary at certain times (it
> was to position the light-bar of a POPUP menu on a certain item).  So, I
> used the Tools/Macros (or something like that) menu in the Control Center to
> define a macro consisting of one down arrow key, called DOWNARROW.  Now, with
> that macro suitably loaded in my program, you would think the right thing to do would be:
> 
> I = 1
> DO WHILE I <= NDOWNS	&& the number of down arrows I want
>   PLAY MACRO DOWNARROW
>   I = I + 1
> ENDDO
> 
> Right? WRONG!  Apparently, on the second iteration of this loop, dBASE stopped
> my program with an error about "recursive macros" or somesuch(I don't remember
> the exact error message, but it was very irritating).  My guess is it doesn't
> like to see the same PLAY MACRO twice in a row.

I tried this test case on 1.0 multi-user version on Novell too and could not
reproduce the bug (unfortunately):

restore macros from test.key     && contains DOWNARROW as you described
use testdb1
i = 1
ndowns = 10
do while i <= ndowns
   play macro downarrow
   i = i+1
enddo
browse
return

Since successive PLAY MACRO statements load on a LIFO basis (refer to the
language reference manual page 2-196), then we would suspect to see the
BROWSE screen and the cursor move down to the 11th record (which it did 
without a problem).  I BELIEVE YOU that it happened to you, but my environment
was apparently not set up in a manner to catch the bug.

> 
> WORKAROUND:  In this case, it wasn't so bad; I just had to remember my binary.
> I defined three more macros consisting of two, four, and eight down arrow
> keys, called DOWN2, DOWN4, and DOWN8 respectively.  Now my code looked like
> this:
> 
> I = NDOWNS
> IF I >= 8
>   PLAY MACRO DOWN8
>   I = I - 8
> ENDIF
> IF I >= 4
>   PLAY MACRO DOWN4
>   I = I - 4
> ENDIF
> IF I >= 2
>   PLAY MACRO DOWN2
>   I = I - 2
> ENDIF
> IF I >= 1
>   PLAY MACRO DOWNARROW
>   I = I - 1
> ENDIF
> 
> Of course, this means you can't play more than 15 down arrows this way, but
> since I didn't even need _that_ many maximum, I let it go.  Besides, this
> method is extendable if necessary.  Even better, though, would have been
> an "ACTIVATE POPUP name BAR number" syntax, similar to "ACTIVATE MENU name
> PAD pad_name."
> 
> Now, the obligatory disclaimer:  I am not an Ashton-Tate affiliate in any
> way, shape, or form, except as YAPSWDBIV (yet another programmer struggling
> with dBASE IV), and I sincerely feel that, as long as I can keep hacking
> around the difficulties, the advantages of the new system greatly outweigh
> the disadvantages, and I will definitely be interested when A-T ships their
> v.1.1.  So, no flames from the A-T-bashers, please!
> 
> Hope someone, somewhere, can use this :-) :-) :-).
> 
> | Disclaimer:  This posting is definitive.  Reality may have got it wrong.    |
> | * Eric J. Bowersox (ERBO) *  LIVE! from Isla Vista, California!             |
> | erbo@cornu.ucsb.edu ...!{ucbvax,ucsd}!ucsbcsl!cornu!erbo                    |
> | If wishes were DRAMs, I don't think we would worry about the Japanese. -me  |

Good luck Eric.  Sorry I couldn't have been of more help.  If I could have 
reproduced it, I would have checked our tracking system here at AT and entered
a bug report.  

Duke Luper
Consultant to Ashton-Tate

I don't think I have to disclaim anything I've said but just in case, they are
my views and not Ashton-Tate's.  After all, what are Ashton-Tate's views?