DMS Notes
# 89-001
01/03/89




In this issue .....

     Welcome to the premier issue of DMS Notes. The aim of this newsletter
is to pass along those tips, tricks, and traps that we uncover as we push
Clipper to its limits.

     Feel free to distribute this and all other issues of DMS Notes. Our
only request is that you maintain the integrity of the .ARC file. We've
included the source code, where appropriate, for your unlimited usage.

     In this issue we will look at:

          1. Using arrays as macros
          2. SMINDEX.PRG
          3. DOTS()


Using Arrays As Macros .....

     In an attempt to re-write a two-year old system maintenance indexing
routine, we decided to place all of the .DBFs into arrays, along with the
.NTX names and the indexing expression. Our first attempt looked something
like:


     DECLARE DBF_NAME[2]
     DECLARE NTX_NAME[2]
     DECLARE NTX_EXPR[2]

     DBF_NAME[1] = "CADET"
     DBF_NAME[2] = "FORADDR"

     NTX_NAME[1] = "CADET1"
     NTX_NAME[2] = "FORADDR"

     NTX_EXPR[1] = "CDNAME"
     NTX_EXPR[2] = "FASERIAL"

     FOR X = 1 TO LEN(DBF_NAME)
          USE &DBF_NAME[X]
          INDEX ON &NTX_EXPR[X] TO &NTX_NAME[X]
     NEXT


     Looked nice, compliled great, but generated the runtime error 
"open error &DBF_NAM.DBF (2)". After a little experimentation, we discovered
that Clipper drops off the square brackets ( [] ) when expanding the macro
&DBF_NAME[X]. A quick reference to the Clipper manual told us that both USE and
INDEX ON will accept character expressions, surrounded by parenthesis, as
valid .DBF and .NTX names. Summer '87 gave us this ability with many of 
Clippers other commands. They make the code a little easier to follow, and 
execute faster than macros. Changing lines 14 - 17 to:


     FOR X = 1 TO LEN(DBF_NAME)
          USE (DBF_NAME[X])
          INDEX ON &NTX_EXPR[X] TO (NTX_NAME[X])
     NEXT


     This worked better, but still generated a runtime error "expression error
(in index key)". Unfortunately, INDEX ON <index expression> will not accept
a character expression as we used in the first fix, so the following change
was made:


     FOR X = 1 TO LEN(DBF_NAME)
          USE (DBF_NAME[X])
          T_NTX_EXPR = NTX_EXPR[X]
          INDEX ON &T_NTX_EXPR. TO (NTX_NAME[X])
     NEXT


     By assigning the value of NTX_EXPR[X] to T_NTX_EXPR we can still use a 
macro with an array, though not directly. The period (.) after the macro
helps Clipper determine the end of the macro expression - most macros WILL
work without the period, but not all. We use the period for all macro
expressions for consistency.

     Here is the final form of our example, with comments:


     * Declare arrays
     DECLARE DBF_NAME[2]                && .DBF file names to index
     DECLARE NTX_NAME[2]                && .NTX file names
     DECLARE NTX_EXPR[2]                && index expression

     * Assign array values
     DBF_NAME[1] = "CADET"
     DBF_NAME[2] = "FORADDR"

     NTX_NAME[1] = "CADET1"
     NTX_NAME[2] = "FORADDR"

     NTX_EXPR[1] = "CDNAME"
     NTX_EXPR[2] = "FASERIAL"

     * Perform indexing
     FOR X = 1 TO LEN(DBF_NAME)         && LEN(DBF_NAME) = length of array
                                        &&  DBF_NAME[]
          USE (DBF_NAME[X])             && USE .DBF
          T_NTX_EXPR = NTX_EXPR[X]      && assign index expression to T_NTX_EXPR

          INDEX ON &T_NTX_EXPR. TO (NTX_NAME[X])

     NEXT


     The full source code for our system maintenance routine can be found
in SMINDEX.PRG, included with this issue's .ARC file. If you haven't already
done so, print it out. If you like what you see, feel free to use it in your
applications.


SMINDEX.PRG .....

     Every application has a need for an (re)indexing routine. Index files
become corrupted or overloaded and require occasional maintenance. The concept 
behind SMINDEX.PRG is a simple one. Load all of the .DBF, .NTX, and index
expressions into arrays and run the indexing through a FOR...NEXT loop. Because
people like to see some sort of 'progress report' during these kind of 'batch'
routines, display each iteration of the FOR...NEXT loop. Make the code and
display(s) generic enough to use over and over in other applications. We think
we've accomplished these goals with SMINDEX.PRG.

     Lines 16 - 130 set up the memvars & arrays needed. You'll notice
that an incremental counter for the arrays is used as opposed to hard coding
the array element to each indexing 'set'. This allows you to insert and
delete indices as desired without a major rewrite. Make sure that the size
of the arrays match the total number of indices (lines 20 - 22). An alternative
method for supplying the .DBF, .NTX, and index expressions to SMINDEX would
be to place them into a .DBF and read them into the arrays as you go. We
chose not to do this, as our index requirements seldom change without a
re-write of other .PRGs in the system and we will eventually use this system
on a LAN, so we like to keep open .DBFs to a minimum. There is also the problem
with with length of the index expression field to be considered. It could
change as you add new indices to your system, requiring a new structure to
the .DBF file.

     However, to accomplish this 'externalizing' of data you could replace 
lines 16 - 130 with something like:

     USE DBF_NTX              && holds .DBF, .NTX, and index expressions

     DECLARE DBF_NAME[LASTREC()]   && # of records in DBF_NTX = the total
     DECLARE NTX_NAME[LASTREC()]   &&  number of elements in the arrays
     DECLARE NTX_EXPR[LASTREC()]

     GOTO TOP
     DO WHILE .NOT. EOF()
          DBF_NAME[RECNO()] = DN_DBF    && use RECNO() to increment elements
          NTX_NAME[RECNO()] = DN_NTX    &&  automatically
          NTX_EXPR[RECNO()] = DN_EXPR
          SKIP
     ENDDO


          The structure of DBF_NTX could be:

          DN_DBF    C,   8, .DBF name to use
          DN_NTX    C,   8, .NTX to create
          DN_EXPR   C, 100, index expression (length determined by largest
                             expression required)

     Whichever method you use to assign the array elements, the 'meat' of
this program occurs in lines 137 - 157. This is basically the same coding that
we saw earlier, with one twist - the 'progress report' display. Line 137 sets
up the starting row for the display (5). Lines 139 - 142 watch the current
row being displayed to see if it is beyond our pre-set lower limit (20). If
it is, the entire display is SCROLLed up one line. The last 'goodie' is found
in line 148. Its called DOTS() and is discussed in detail later.

     Here's a sample of the output that SMINDEX will provide:

          ͻ
           Reindex Data Base Files                            
          ͹
                                                               
            Indexing  CADET     to  CADET2 ......... Complete  
            Indexing  FORADDR   to  FORADDR ........ Complete  
            Indexing  OTHADDR   to  OTHADDR1 ....... Complete  
            Indexing  OTHADDR   to  OTHADDR2 ....... Complete  
            Indexing  OTHFADDR  to  OTHFADDR ....... Complete  
            Indexing  INSTRUCT  to  INST1 .......... Complete  
            Indexing  INSTRUCT  to  INST2 .......... Complete  
            Indexing  SCHEDULE  to  SD2 ............ Complete  
            Indexing  SCHEDULE  to  SD3 ............ Complete  
                                                               
          ͹
                                                               
          ͼ

     The border, of course, was added for your 'viewing pleasure', but
we think you'll get the idea. Noticed the dots, did you ??? That comes
next .....


DOTS() .....

     This is one of those 'luxury' UDFs that help round out a developer's
toolbox. The sample screen above shows its basic function; pass to it
a character expression and receive back the expression padded with dots,
right aligned to a desired length. DOTS.PRG contains the entire source
code for DOTS(). To use it in your applications either rename the .PRG file
to something else (you can't have a .PRG and a Function with the same name),
or append DOTS.PRG to your 'standard' routines .PRG. We use STANDARD.PRG to
hold these type of universally-used UDFS and procedures. If you use this
method, be sure to SET PROCEDURE TO STANDARD near the beginning of the first
.PRG (or main load module) which starts up your application.


Until Next Time .....

     We hope that you've found this issue of DMS Notes useful. Comments,
questions, suggestions, and gripes can be sent to:

     Bob Laszko                         The File Bank BBS
     Data Management Systems            Clipper Support Conference
     PO Box 3104                        Fallbrook, Ca
     Fallbrook, Ca  92028               (619) 728-4318
     (619) 728-0984                     1200 - 19,200 baud HST
                                        24 hours / 7 days a week

     DMS Notes is Copyright 1989 Data Managment Systems
     All Rights Reserved