Another FAQ, posted just once [LONG] 
Author Message
 Another FAQ, posted just once [LONG]

Below are some questions together with answers given by people on Usenet.
They were collected from alt.comp.databases.xbase.clipper and
comp.lang.clipper.  I saved these messages because I thought they might
come in useful later.  Now and then they pop up in the Clipper newsgroup,
so I think it would be wise to add these to the Clipper FAQ or the
Clipper Mini-FAQ.
I never checked the answers on their validity nor tried to proofrun any
of the pieces of code.  Use at your own risk...

Q: Good books on Clipper ?

Since there's some interest in Clipper book mini-reviews:

_Clipper 5: A Developer's Guide_  (Booth, Lief, Yellick)
M&T Books  ISBN 1-55851-242-X  List US$45  1350 pp.

Yes, the title's right.  I got the pre-5.2 edition, but I don't think
it makes much of a difference.  5.2 - 5.0 = DBFNDX + DBFMDX + DBFCDX
This book, along with lots of practice, made me a Clipper programmer.
I knew other languages, including a little C (which is very helpful in
learning Clipper 5.X) but a little bit of dBASE IV was my only
exposure to xBase.  I read this book cover to cover twice, and I was
ready to start writing Clipper programs.  The style is very clear and
engaging, and though I eventually discovered four or five serious
errors or omissions, I still feel well-served by it.  As I mentioned,
Clipper 5.x bears a suspicious syntactical resemblance to C, and
perhaps it was inevitable that one or more Clipper books would wind up
reading a lot like Kernighan and Ritchie's classic C text.  I see that
resemblance in BLY, so it's no surprise that I like this book -- I
liked K&R too.  If you didn't like K&R, you probably won't like BLY.
If you know a little C, this book will be a quick but thorough read
for you.  I still use it as a reference on occasion.

_Clipper 5.2 Power Tools_  (Straley)
Bantam  ISBN 0-553-35401-9  List US$55  1132 pp.

People speak highly of Straley, so I can't in good conscience say he's
a bad writer.  What I can say is that that his writing style and my
reading style are totally incompatible.  I imagine most literate
people have had the experience of reading a page, then rereading it,
and still not having the slightest idea what they just read.  That's
what reading Straley is like for me, whether in a book or a magazine.
I can't tell you what this book is like because I couldn't get through
the first chapter.

_Memory Management For All of Us_  (Goodman)
Sams  ISBN 0-672-27366-7  List US$30  1137 pp.

Okay, so it's not a Clipper book.  But I think anybody programming or
configuring programs for PCs should read it.  Detailed discussion of
memory issues in PCs, for both real and protected mode.  I have
learned an awful lot from this book.

Also, I like Spence's writing style a lot in the magazines, but do not
have any of his books.  Very spare, concise style, no verbal padding
at all.  One is reminded of a drill instructor who doesn't ask if
you're with him, just sets a steady pace and expects you to keep up.

Q: How to highlite an entire row in a tbrowse ?

// Highlighting the Entire Row
// HiliteRow()
// Highlight current row with passed color pair array
PROCEDURE HiliteRow( oBrowse, aColorPair )
   LOCAL nCurRow

   nCurRow := oBrowse:rowPos

   // Color row
   oBrowse:colorRect( { nCurRow, 1, nCurRow, oBrowse:colCount },;
      aColorPair )

   // And make it happen


Q: What is the structure of a DBF file ?

dbf structure
byte 1  03h normal file, 83h memo attached, anything else indicates
another driver is used, i.e. 127 foxbase
byte 4-7 number of records
byte 10-11 record size
byte 32 thats where the filed labels start
each field entry takes up 32 bytes
byte 1-10 field name
byte 12 field type
byte 16 length
byte 17 decimal
0D 00 terminates the header
records start after the header for n+1 bytes length
1A indicates eof
if a record is deleted 2A appears as the first byte of the record

Q: What is the structure of a DBT file ?

dbt file structure.
header section (mostly garbage) is 1k if data present, otherwise 512 bytes.
only the first 4 bytes are actually used which indicate the total number
of blocks. 1 block is 512 bytes. memos are stored as one chunk and
terminated with ^Z.
the block number is stored in the dbf as a 10 digit number.
memos always start on a 512 byte boundary.

Q: What is the structure of NTX files ?

Byte    Size     Contents
----    ----     -----------------------------------------------
   1       2     Signature Byte, bit encoded

                 Bit      Contents
                 ---      ---------------------
                 7-3      Reserved
                 4-1      New lock offset bit
                 3-1      Partial index bit
                 2-2      Index types
                 0-1      Conditional index

   3       2     Count of index updates
   5       4     Offset to first index page
   9       4     Offset to list of unused pages
  13       2     Key size + 8 bytes for record pointers
  15       2     Key size
  17       2     Decimal Places in the key expression
  19       2     Maximum entries per index page
  21       2     Minimum entries per index page
  23     256     Key expression, NULL terminated
 279       1     1 if unique, 0 otherwise
 280       1     1 if decending index, 0 otherwise
 281     256     Filter expression
 537      11     Order name

From _NETWORK PROGRAMMING IN CA-CLIPPER 5.2_ by Joseph Booth and Greg Lief
Ziff-Davis, 1993

The book also includes structures for FoxPro .IDX & .CDX and
dBase .NDX & .MDX files.

Q: Decompiling Clipper executables ?

If you have an Clipper 5.x program and you have lost your source codes
(May be an ex-employee take it away), you may find Valkyrie a useful tool
to have around the place.

Valkyrie does decompile your exe to source code .. althought not to what
you have written, it is close.  With it I am quite certain you can
maintain from there.

Q: How to protect Clipper executables from decompilation ?

Ok, then how to protect your codes from decompilation then?

By adding the following lines in your ERRORSYS.PRG


That works for Clipper 87, and I believe it should work for Clipper 5.

Q: How to encrypt/decrypt data using no third party libraries ?

It is truely a surprise how people turn to third party libraries
when it could be achieved in Clipper. Try the function below and
it  would encrypt/decrypt based on the logical value passed to
the function. cString could be your field-name. Hope this helps.

function ENCRYPT(cString,lEncryptYN)
   LOCAL nLoop, cStringNew, nStringLength
   cStringNew    := ''
   nStringLength := Len(cString)
   IF Empty(cString) .AND. Len(cString) = 0
      cStringNew := Space(8)
      cString := Upper(cString)
      FOR nLoop := 1 to nStringLength
         IF lEncryptYN
            cStringNew += Chr(Asc(Substr(cString,nLoop,1))+10)
            cStringNew += Chr(Asc(Substr(cString,nLoop,1))-100)
      NEXT nLoop

Q: How to make an entry screen larger than the actual screen, i.e. being
   able to scroll up and down ?

This can definitely be done in vanilla Clipper.  I think the cleanest
way would be to write reader-level Get system extension.  Instead of
a plain READ command, you'd write your own copy of READMODAL()
which calls a custom version of SETTLE().  At the end of SETTLE()
in GETSYS.PRG you would insert code that looks something like this:

   //    At end of Settle() in GETSYS.PRG:
   // Record exit state
   snLastExitState := nExitState

   IF !( nPos == 0 )
      GetList[ nPos ]:exitState := nExitState

   //  Here's the fun part.  Check the current GET object
   //  to determine whether it's on the visible screen.
   //  If not, nOffset is the number of rows you need to
   //  adjust.  Go through the array of GETs, offsetting
   //  all rows by <nOffset>, and repaint the whole screen.
   IF GetList[nPos]:Row > MAXROW()
      nOffset := MAXROW() - GetList[nPos]:Row
   ELSEIF GetList[nPos]:Row < 0
      nOffset := - GetList[nPos]:Row
      nOffset := 0
   IF nOffset != 0
      AEVAL (GetList, {|z| z:Row += nOffset })
      //  Here you need to insert code that will repaint
      //  everything on the screen EXCEPT the GETs.  An
      //  exercise for the reader.  Where's Phil Schwartz
      //  when you need him?
      AEVAL (GetList, {|z| z:display() })

   RETURN ( nPos )

Q: I experience index corruption regurarly.  What could be wrong ?

DBFNTX/1210 is caused by a database and index being out of
sync. Here are a couple of suggestions you can try :

1) What version of Clipper are you using?  If you are not
running 5.2d try upgrading to that version.

2) Scan your code for instances where you may have your
.dbf open and not your .ntx's

3) If you are running on a network make sure you are
following these guidelines for updates/deletes :

replace // or delete, recall, etc...

The commit will assure that the data/index change is
seen by the network server.

4) Make sure your clients are not using dbase,paradox, etc..
to update the key fields.

5) If your clients have access to DBU make sure they
always open the indexes.

6)  Make sure your index keys are of a fixed length
(ex. don't use trim w/o padding back up to the same size)

7)  Make sure that your index key contains only
references to the current workarea.  Avoid this :

Index on alias1->field1 + alias2->field2 + field3 to indfile

Q: I experience NTX corruptions regurarly on my Novell network.
   What should I check ?

The list below is by no means complete ...

   1) If NetWare Transaction Tracking is enabled on the data/indexes
      for a multi-user system, you will get "index corrupted"
      messages. Look for a "T" attribute when using the FLAG

   2) Check that the servers have enough memory. "Novell
      Application" Notes for Dec 94 has a section giving updated
      memory server guidelines for 3.x and 4.x . It may well be
      more than you think: it is often more than the original
      published estimates for 3.11, certainly.
      A server without enough memory can certainly get you
      corrupted indexes.

   3) [ from Bob Profitt - thanks ]
      Network Interface Cards that use High Memory RAM locations
      can easily get into conflict with memory managers such as
      QEMM, EMM. This can cause a range of problems, including
      failure to write to the network properly.

   4) "Magic Number" bugs
      The symptom is that suddenly you can't construct a
      particular index on a particular DBF correctly. If you
      add some records, delete some records, or add some fields
      to the expression it starts to work again.

      Spooky, yes?

      I don't know if 5.2d has any of these, but they've come up
      in many other versions of Clipper (I've seen it in 3 old
      Summer '87 systems recently, and I always thought Summer '87
      indexes were pretty stable).

   5) [ from Donald Rodemer - thanks again ]
      He had problems when using both Summer '87 and 5.x
      applications to update the same set of indexes. He
      got it working by moving the Summer '87 app to the
      same version as the 5.x application.
      This is presumably not your problem, but I thought I'd
      stick it in the list in case anyone else found it of
      interest ... I have also heard from elsewhere that
      if you're doing this sort of thing, you should create
      the indexes using the earlier version of Clipper:
      Summer '87 in this case. Can't remember where I got it
      from though - probably this group ...

   6) Check the server hardware is installed OK. This is unlikely
      to be your problem, as you apparently have this over many
      different networks. But if all else fails, and the pain is
      getting worse, it may be worth considering paying for a
      good Novell consultancy to come in and check a network. Or
      getting a friend who works for a good Novell consultancy
      and owes you a favour to come in and check a network ...

Q: How do I limit the amount of characters entered by a user
   in a memoedit ?

Here's how I do it...and it works great.  Thanks to Mr. Straley:

Function Comment()
   local cComment := ""
   private nRestrict := 2
   // give memoedit a window one line longer than nRestrict
   cComment := hardcr(MemoEdit(cComment, ;
        19, 03, 21, 78, T, "MemoRestrict" ))
   release nRestrict

// mode, row, and col are automatically passed by memoedit
Function MemoRestrict(mode, row, col)
   local nRetVal := 0
   // save screen
   memvar nRestrict
   // must open an editing window one row bigger than restriction
   // nRestrict is a private declared in calling function
   if row > nRestrict
      err_msg(str(nRestrict) + "-LINE LIMIT...")
      KEYBOARD Replicate( Chr(8), col ) + ;
         chr( K_UP ) + chr( K_END )
   // restore screen

Q: Is there a memoedit replacement function (to overcome the file
   size limit) ?

Assuming you want Read Only access to the file, you can use FBROWSE(), a
public domain virtual file browser.

FBROWSE.???  (look in the _INDEX.TXT file)


The NanForum Toolkit has a set of functions just for viewing text files.
Grab it and take a look at what it can do.


If you want to just browse the ASCII file, you can make use of the TB Browse
Object. Rick Spence's book "Clipper 5.2 Power Programmer's Guide,
Page 624 -633 " gives all the source code for a ASCII file browser.

However, if you want to edit the ASCII file, there is a commerical
product called CLTEXT, (From Building Blocks Publishing . Inc.)

Q: How do I add mouse functionality to Clipper applications ?

Funcky library has great mouse support, selecting fields, menus etc. I have
been writing software with mouse support using funcky for a number of years.

Q: How do I modify Errorsys.prg such that the program doesn't Quit() when
   there is a printer error ?

// around line 70 (ie. just after:
  //    if ( e:genCode == EG_APPENDLOCK .and. e:canDefault )
  if ( e:gencode == EG_PRINT )
     return PrintError()


static Function PrintError()
  local key, altFile, oldColor := set( _SET_COLOR, "w+/r" ), erSysScrn
  local lOverRide := NO, lDevice := set( _SET_DEVICE ) == "PRINTER"
  local lConsole := set( _SET_CONSOLE, ON )

  if lDevice
    set( _SET_DEVICE, "SCREEN" )

    set( _SET_PRINTER, OFF )


  #xtranslate Center( <r>, <m>[, <c> ] ) => CenterAt( <r>, NIL, <m>[, <c> ] )

  // display warning:
  Center( 12, "With your help, the system will attempt diagnosis!" )
  Center( 13, "Is the light labelled ON LINE or READY lit ?" )

    Center( 10, "Check the cables to ensure they are connected" )
    Center( 11, "properly to both the printer and computer." )
    Center( 13, "Are they tight and connected properly ?" )

      Center( 10, "The system will now attempt to access the" )
      Center( 11, "printer again. if an error occurs again," )
      Center( 12, "have the ADP staff examine the problem." )
      Center( 14, "Press any key to continue printing... ", "i+" )

    elseif !lOverRide
      Center( 10, "Connect the cables properly. if you are not" )
      Center( 11, "sure about how they are connected, contact" )
      Center( 12, "the ADP staff for assistance." )
      Center( 14, "When the cables are reconnected," )
      Center( 15, "press any key to continue printing... ", "i+" )


  elseif !lOverRide
    Center( 10, "Make sure the power to the printer is on. if the" )
    Center( 11, "ON LINE or READY light is still off, press the" )
    Center( 12, "ON LINE button." )
    Center( 13, "Is the ON LINE or READY light on now ? ", "i+" )

      Center( 10, "The system will now attempt to access the" )
      Center( 11, "printer. if an error occurs again," )
      Center( 12, "have the ADP staff examine the problem." )
      Center( 14, "Press any key to continue printing... ", "i+" )

    elseif  !lOverRide
      Center( 9, "Check to make sure that paper is loaded into" )
      Center( 10, "the printer properly and is not jammed. When" )
      Center( 11, "the paper is ready, press the ON LINE button" )
      Center( 12, "if the light is not on." )
      Center( 14, "Is the paper loaded and clear and the light on ?" )

        Center( 10, "The system will now attempt to access the" )
        Center( 11, "printer. if an error occurs again," )
        Center( 12, "have the ADP staff examine the problem." )
        Center( 14, "Press any key to continue printing... ", "i+" )

      elseif !lOverRide
        Center( 10, "The system will attempt to access the printer" )
        Center( 11, "anyway, if an error occurs again contact the" )
        Center( 12, "ADP staff for assistance." )
        Center( 14, "Press any key to continue printing... ", "i+" )




  if lOverRide
    altfile := dtoj( date() )+left( time(), 2 )+substr( time(), 4, 2 ) + ".RPT"
    set( _SET_PRINTFILE, altfile, YES )

    Center( 10, "Due to printer error the system is " )
    Center( 11, "saving this report to disk " )
    Center( 12, "Filename: "+set( _SET_DEFAULT )+"\"+CURDIR()+"\"+altfile+" ", "i+" )
    Center( 14, "Press any key to continue... ", "i+" )


  inkey( TIMEOUT )
  restore screen from erSysScrn

  if lDevice
    set( _SET_DEVICE, "PRINTER" )

    set( _SET_PRINTER, ON )


  set( _SET_COLOR, oldColor )
  set( _SET_CONSOLE, lConsole )

return OK

// lOverRide must be passed by reference
static Function YesNoSave( erSysLin, lOverRide )
  local oMenu, key, nNum


  nNum := readmenu( oMenu )
  if nNum < 1
    nNum := 2

  lOverRide := ( nNum == 3 )

return nNum == 1

// end of cut
The one area this does not address, is if the destination file fills up the
target drive, multiple errors will occur...

Q: How do I show progress bars while indexing ?

Clipper 5.2 added the following arguments to the INDEX command:
INDEX ON .... EVAL <bBlock> EVERY <nInterval>

EVAL <bBlock> evaluates a code block every <nInterval>.
The default value is 1.
The return value of <bBlock> must be a logical data type.
Returning false (.F.) halts the indexing.

An example:

#define Progress_Inc  10

USE Computer NEW
INDEX ON Computer->Serial_No TO Comp_Sn ;
      EVAL {|| Prog_Bar() } EVERY Progress_Inc

STATIC   Count := 0

Count := Count + Progress_Inc


Show_Bar() should draw the status bar.

Q: How to do a printscreen from Clipper.

Here's a print screen function I put together with the advice of
a.c.d.x.clipperheads a while back.  One could hardwire a printer
port, include a port selector, or make sure TheTarget and FormFeed
are defined, and Set Key Mon-Key pscreen().  The GoodChars business
prevents the passage of characters outside the usual printables.
The FormFeed is a logical as to whether to kick out the page or not.

function pscreen(TheTarget,FormFeed)

local i, j, TheScreen, AChar := '', GoodChars := '', ASpace := ' '

for i = 32 to 125
    GoodChars := GoodChars + Chr(i)

Set Console Off
Set Printer To (TheTarget) Additive
Set Printer On
SetPrc(0, 0)

TheScreen := SaveScreen(0,0,23,79)         // Stash the screen

For i = 0 to 23
  For j = 1 to 160 step 2

     AChar := SubStr(TheScreen,160*i+j,1) // Every Other Byte
     If At(AChar,GoodChars) != 0          // Check for Alphanumeric character
        ?? Achar                          // Print the good ones
        ?? ASpace                         // Don't print box draw chars, etc.
  Next                                    // Next j (character)
  ?? Chr(13) + Chr(10)                    // CR + LF
Next                                      // Next i (line)

If FormFeed
   ?? Chr(12)                                // Form Feed

Set Printer To
Set Printer Off
Set Console On

return nil

Q: Which telecommunication package should I use ?

Telepathy. Call Programmers Warehouse or ZAC.

It works in standard, protected and dual mode.

High speed is no problem, but you will need to insist on 16550 uarts for it
to work consistantly. This is because interrupts cannot be handled in
protected mode, so the processor is toggled back and forth from protected
to standard mode. If you don't use a buffered uart, and you want to run the
DTE rate at the proper speed for a 28.8, ie, 110kbps, you WILL lose data
unless it is a buffered uart. I imagine that a 386 would lose bytes at
speeds as low as 19.2.

Even though I prefer SilverClip for my communications work, they never
produced a protected mode product due to the above limitations of the
hardware. Too bad.

Q: How do I draw shadow boxes in Clipper ?

Try this:

  function shadow( nt, nl, nb, nr )
    local cScrn( nb+1, nl+1, nb+1, nr+1 )
    local i, x
    // do the bottom
    x := len( cScrn )
    for i := 2 to x step 2
      cScrn := stuff( cScrn, i, 1, chr( asc( substr( cScrn, i, 1 ) ) % 8 )
    restscreen( nb+1, nl+1, nb+1, nr+1, cScrn )
    // do the right
    cScrn := savescreen( nt+1, nr+1, nb+1, nr+1, cScrn )
    x := len( cScrn )
    for i:= 2 to x step 2
      cScrn := stuff( cScrn, i, 1, chr( asc( substr( cScrn, i, 1 ) ) % 8 )
    restscreen( nt+1, nr+1, nb+1, nr+1, cScrn )
  return( NIL )

  Typed on the fly so check it carefully. Basically all it does is save the
section of screen where you want the shadow to be and then change the
attribute byte to a transparent, darkened shadow. This puts a 1 character
shadow to the right and bottom of the area passed by the parameters.  You can
easily change it to do a 2char by 1char if you want it symmetrical, etc.  

  There is no bounds checking.  You might want to add that.  I also have a
version in C which is faster, but a user won't be able to tell the

Q: How can I direct the swapfiles Clipper creates ($T??????) to another
   drive/directory than the default ?

set clipper=swappath:"c:\temp"

The temp variable is only used internally for temporary files with set
filter statements, copying, sorting, etc. VM looks for the swappath.
On networks the swappath is absolutely needed even so the manual states
that the swapdirectory is the currently logged on directory.

With Blinker, you can burn the swappath into the exe.

Q: What about the bug ?

The specifics are:

      LOCAL aNumbers := {1, 2}

      DO CASE
         CASE aNumbers[1] == 2
            ? "First element is 2"

         CASE aNumbers[2] == 2
            ? "Second element is 2"


  This will not print anything.  The 'optimization' the compiler does on
  CASE statements mistakenly keeps hold of the first comparison value
  because it thinks that the next is the same.  They're obviously just
  comparing the simbol name and not the whole expression...

Sat, 11 Jul 1998 03:00:00 GMT  
 [ 1 post ] 

 Relevant Pages 

1. some haskell FAQ info (occasional posting, long)

2. Ruby FAQ -- pretty long post.

3. Send thousands of Posting to the Newsgroups at once with Mailloop


5. How to make a Python Long from a long long in an extension

6. IT World Interview with Robert C. Martin [Long post]

7. Parsing Logs...Please advise (long post)

8. FREE COURSE PLACES - UK (Staffordshire University) LONG POST

9. nobody post in comp.long.smalltalk

10. Long post : Full lists of errorcodes I've found so far (with a few solutions)


Powered by phpBB® Forum Software