home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / modula2 / library / dbf_file / dbf.doc < prev    next >
Text File  |  1991-10-26  |  104KB  |  2,839 lines

  1.  
  2.  
  3.  
  4.  
  5.  
  6.  
  7.  
  8.  
  9.  
  10.  
  11.  
  12.  
  13.  
  14.  
  15.  
  16.  
  17.  
  18.  
  19.  
  20.  
  21.  
  22.  
  23.  
  24.  
  25.  
  26.  
  27.  
  28.  
  29.  
  30.  
  31.                                       Modula-Tools
  32.                                      dBase Toolkit
  33.  
  34.                        Copyright 1989, 1990, 1991 by David Albert
  35.                                   All rights reserved
  36.  
  37.  
  38.  
  39.  
  40.  
  41.  
  42.  
  43.  
  44.  
  45.  
  46.  
  47.  
  48.  
  49.  
  50.  
  51.  
  52.  
  53.  
  54.  
  55.  
  56.  
  57.  
  58.  
  59.  
  60.  
  61.  
  62.  
  63.  
  64.  
  65.  
  66.  
  67.  
  68.  
  69.  
  70.  
  71.  
  72.  
  73.  
  74.  
  75.  
  76.  
  77.  
  78.  
  79.  
  80.  
  81.  
  82.  
  83.  
  84.  
  85.  
  86.  
  87.  
  88.  
  89.  
  90.  
  91.                                    Table of Contents                                   Table of Contents                                   _________________
  92.  
  93.  
  94.                     License/Registration                   1
  95.                     Introduction                           3
  96.  
  97.                     Database Basics                        4
  98.                     DBF Module                             7
  99.                     NDX Module                            18
  100.                     BuildNDX Module                       28
  101.                     Modula Tools Utilities                33
  102.  
  103.                     Appendices
  104.                       A  DBF technical specs              34
  105.                       B  NDX technical data               37
  106.                       C  Multi-user programming           39
  107.  
  108.  
  109.  
  110.  
  111.  
  112.  
  113.  
  114.  
  115.  
  116.  
  117.  
  118.  
  119.  
  120.  
  121.  
  122.  
  123.  
  124.  
  125.  
  126.  
  127.  
  128.  
  129.  
  130.  
  131.  
  132.  
  133.  
  134.  
  135.  
  136.  
  137.  
  138.  
  139.                                         Legalese                                        Legalese                                        ________
  140.  
  141.           Copyright          Copyright
  142.                The software  documented herein is  copyrighted and  all rights
  143.                are reserved by David Albert.
  144.  
  145.           Warranty          Warranty
  146.                Neither  Digital  Engineering  nor  David   Albert  assume  any
  147.                responsibility for damages or difficulties  arising from use of
  148.                this  product.   For registered  users  only, if  the  original
  149.                diskettes or  manual are found to  be defective within 90  days
  150.                of the date  of purchase, Digital Engineering will replace them
  151.                upon proof  of purchase.   This software is  distributed on  an
  152.                'as  is' basis, without any warranty express  or implied except
  153.                as set  forth above.   In particular,  Digital Engineering  and
  154.                David  Albert  disclaim  all warranties  of  merchantability or
  155.                fitness for  any  particular  purpose.   The  user  waives  all
  156.                claims  against  Digital   Engineering  and  David  Albert  for
  157.                special,  direct, indirect,  or consequential  damages  arising
  158.                out  of or  in connection with  the use or  performance of this
  159.                software product.
  160.  
  161.  
  162.  
  163.  
  164.  
  165.  
  166.  
  167.  
  168.  
  169.  
  170.  
  171.  
  172.  
  173.  
  174.  
  175.  
  176.  
  177.  
  178.  
  179.  
  180.  
  181.  
  182.  
  183.  
  184.  
  185.  
  186.  
  187.  
  188.  
  189.  
  190.  
  191.  
  192.                                  Introduction - Page 1
  193.  
  194.  
  195.  
  196.  
  197.  
  198.  
  199.  
  200.  
  201.  
  202.  
  203.  
  204.  
  205.                                       Registration                                      Registration                                      ____________
  206.  
  207.           This software is provided as shareware.  This allows  you to try the
  208.           software without  purchasing it.   You may use this  software for up
  209.           to  30 days free  of charge.  Thereafter, you  must register and pay
  210.           for it.
  211.  
  212.           A  great deal of work  has gone into the development of this product
  213.           and the  accompanying manual.  As  a software developer, you  should
  214.           recognize  and protect  this  work  by  respecting the  distribution
  215.           agreement.   The pricing  is extremely reasonable and will encourage
  216.           further  development of this  package.  It  will also  assure you of
  217.           future updates and bug fixes.
  218.  
  219.           Several additions  and expansions  are  under  development for  this
  220.           package including  an applications  generator.  In order  to provide
  221.           you with  technical support and to  keep you informed about  updates
  222.           and new  releases,  you  should  send  in  this  registration  form.
  223.           Technical support  and upgrades  are  ONLY  available to  registered
  224.           users, so please fill out and send yours in today.
  225.  
  226.  
  227.                                                                            
  228.                                     Registration Form                      
  229.                                                                            
  230.                    Name   : ____________________________________________   
  231.                                                                            
  232.                    Company: ____________________________________________   
  233.                                                                            
  234.                    Address: ____________________________________________   
  235.                                                                            
  236.                    City   : _______________  State: ___  Zip:___________   
  237.                                                                            
  238.                                                                            
  239.                                                                            
  240.                    Computer Brand   : __________________________________   
  241.                                                                            
  242.                          Processor  : 8088  80286  80386  80486            
  243.                                                                            
  244.                    Modula-2 compiler: __________________________________   
  245.                                                                            
  246.                                                                            
  247.                    Modula-Tools ver.: __________________________________   
  248.                                                                            
  249.                    Pricing:   Students:                          $10.00    
  250.                               Personal/Company:                  $30.00    
  251.                               Commercial Use:                    $50.00    
  252.                               VA Residents add sales tax                   
  253.                                                                            
  254.  
  255.  
  256.  
  257.  
  258.                                  Introduction - Page 2
  259.  
  260.  
  261.  
  262.  
  263.  
  264.  
  265.  
  266.  
  267.  
  268.  
  269.  
  270.  
  271.                                       Introduction                                      Introduction                                      ____________
  272.  
  273.           Congratulations  on  your   selection  of  the  Modula-Tools   dBase
  274.           toolkit.   With this toolkit and your Modula-2 compiler, you will be
  275.           able to  create sophisticated database  applications for  single and
  276.           multi-user environments with ease.  
  277.  
  278.           The   Modula-Tools  dBase  toolkit  provides   three  basic  library
  279.           modules:  DBF, NDX, and BuildNDX.  The DBF module provides access to
  280.           dBase III, III+, and IV  data files.  The NDX module provides access
  281.           to  dBase III, III+, and IV index files.  The BuildNDX module allows
  282.           your programs to  build sophisticated  indices much faster than  you
  283.           can with dBase.  
  284.  
  285.           There are  numerous advantages to  using the dBase  toolkit.   These
  286.           include:
  287.  
  288.                -    Compatibility with dBase industry standard.
  289.                -    Easy of use.
  290.                -    Flexibility
  291.                -    Full Multi-user support.
  292.                -    High-level databasing language.
  293.                -    Compatibility with most popular Modula-2 compilers. 
  294.  
  295.           Naturally, the  biggest advantage is the ability to produce advanced
  296.           database   applications  with   the  powerful  Modula-2  programming
  297.           language.
  298.  
  299.           This  manual  assumes you  understand the  basics of  databasing and
  300.           Modula-2.  Experience with  dBase* is useful, but not required.   If
  301.           you have not worked  with databases previously, you should read  the
  302.           overviews carefully and refer  to the appendix for a suggested  list
  303.           of introductory reading materials.
  304.  
  305.           On  the  following  pages, you  will  find  a  general  overview  of
  306.           databasing as well as detailed explanations  of the library modules.
  307.           Each  exported  procedure  is  outlined,   followed  by  a  detailed
  308.           explanation  of its functioning.   More technical information can be
  309.           found in the appendices.
  310.  
  311.           We  believe  the  dBase  toolkit  will  provide  the  best  database
  312.           solution for your applications.  However, we are always looking  for
  313.           ways to  improve.  If  you find  an area that needs  improvement, or
  314.           perhaps  a feature  that should  be added,  or maybe  even (gasp!) a
  315.           bug, please don't hesitate to contact us.
  316.  
  317.           * dBase III, III+, and IV are registered trademarks of Ashton Tate
  318.             IBM is a registered trademark of International Business Machines
  319.  
  320.  
  321.  
  322.  
  323.  
  324.                                  Introduction - Page 3
  325.  
  326.  
  327.  
  328.  
  329.  
  330.  
  331.  
  332.  
  333.  
  334.  
  335.  
  336.  
  337.                                     Database Basics
  338.  
  339.           What is a database?
  340.  
  341.                A database  is a  collection of  pieces of  information of  the
  342.                same  general  type.   For  example,  a  telephone  book  is  a
  343.                database.  Each  entry in  a phone  book is  of the same  type:
  344.                Name, Address,  and Telephone number.   Computer  databases are
  345.                similar;   each  entry in  a computer database  is of  the same
  346.                general format.  There  can be an unlimited number of  entries,
  347.                as long as they have the same structure.
  348.  
  349.           How Are Databases Used?
  350.  
  351.                Most  computer applications  require some  sort  of databasing.
  352.                For  example, accounting  programs must  store records  of each
  353.                check written and bill  received.  Inventory programs must keep
  354.                records  of orders  received, current  stock, vendors, clients,
  355.                etc.   A computer bulletin  board needs to  keep a database  of
  356.                its users, files, messages, etc.
  357.  
  358.           Database Semantics
  359.  
  360.                Before continuing,  let's  examine  some database  terminology.
  361.                An entire  database (such as  a computerized telephone book) is
  362.                called  a database file.   Each entry in a database is called a                         database file
  363.                data record.    Within  each  record are  data  fields.   In  a                    record                                     fields
  364.                telphone  directory,  each  person  occupies  a  record;   each
  365.                record has three fields: name, address, and telephone number.
  366.  
  367.  
  368.           Database Tools
  369.  
  370.                In  order to work  with databases, you  must be  able to create
  371.                database   files,  store  information  in  them,  and  retrieve
  372.                information from  them.  Since  most general  purpose languages
  373.                such as Modula-2 do not  provide an easy way  to do this, other
  374.                tools are required.  
  375.  
  376.                Special languages such as dBase  and Paradox have  been created
  377.                to  facilitate   working  with   databases.     However,  these
  378.                languages lack the speed, power,  and flexibility of  Modula-2.
  379.                With the Modula-Tools dBase  toolkit, you get the best of  both
  380.                worlds:   speed and power,  plus the ability  to easily  create
  381.                and work with industry standard database files.
  382.  
  383.  
  384.  
  385.  
  386.  
  387.  
  388.  
  389.  
  390.                                 Database Basics - Page 4
  391.  
  392.  
  393.  
  394.  
  395.  
  396.  
  397.  
  398.  
  399.  
  400.  
  401.  
  402.  
  403.           A more detailed example
  404.  
  405.                A  typical business  program  might need  to  store a  list  of
  406.                customers   and  keep   records  of   their  names,  addresses,
  407.                purchases,  credit,  etc.   This  list  of  customers  would be
  408.                referred to as the customer database.   A record would  be kept                                  customer database
  409.                for each  customer.   You could  visualize such  a database  as
  410.                follows:
  411.  
  412.                                                                  
  413.                                                                  
  414.                   Customer   Customer   Customer   Customer      
  415.                    Record     Record     Record     Record      etc.
  416.                      1          2          3          4               
  417.                                                                  
  418.                                                                  
  419.  
  420.                Each customer record can be seen  as a fill-in-the-blanks  type
  421.                form with  the same questions asked  of each customer, and  the
  422.                same  space allowed for their  answers.  Each blank on the form
  423.                is called a  field.  In  this example, there  are 9  fields for
  424.                each customer record:
  425.  
  426.                     Field     Description         Type           Length                    ______    ___________         ____           ______
  427.                1    NAME      Customer's name     Characters     30
  428.                2    ADDRESS   address             Characters     30
  429.                3    CITY      city                Characters     15
  430.                4    STATE     state               Characters     2
  431.                5    ZIP       Zip code            Characters     10
  432.                6    PHONE     Phone number        Characters     15
  433.                7    PURCHASED Total purchased     Numeric        12
  434.                8    BALANCE   Total balance due   Numeric        12
  435.                9    CREDIT    Credit limit        Numeric        12
  436.  
  437.                Notice that  each field  has a  name, fixed  length, and  type.
  438.                When information is entered into a customer record, it must  be
  439.                of  the correct type, and  must fit in  the space alloted.  For
  440.                example, you can only  enter numbers up to  12 digits in length
  441.                into the BALANCE field.
  442.  
  443.                Each customer record will  occupy exactly 138  spaces (the  sum
  444.                of  the lengths of each  field).  What changes from customer to
  445.                customer is the data contained in those fields.  
  446.  
  447.                Notice that each  customer record occupies  the same  amount of
  448.                space  regardless of  how much  of that  space is  used.   This
  449.                makes  it easy to  calculate the size of a  database file.  For
  450.                example.   If there were 1,000  customers in this database,  it
  451.                would occupy 138,000 bytes.
  452.  
  453.  
  454.  
  455.  
  456.                                 Database Basics - Page 5
  457.  
  458.  
  459.  
  460.  
  461.  
  462.  
  463.  
  464.  
  465.  
  466.  
  467.  
  468.  
  469.           Storing and Retrieving 
  470.  
  471.                The purpose of a  database is to allow you to store,  retrieve,
  472.                and manipulate  data quickly and easily.   This means you  must
  473.                have  a way to  access a database file, add  new records to it,
  474.                modify  existing  records,  and  quickly  find  any  record you
  475.                desire.  The Modula-Tools dBase toolkit makes this easy.
  476.  
  477.           More Terminology
  478.  
  479.                Before you  can use  a  database  file, you  must open  it  for                                                                 open
  480.                general access.   Once the file is  opened, you can  append new                                                                    append
  481.                records to  the end of the  file, or retrieve existing  records
  482.                from it.  Note that new records are always added  to the end of                                                                        ___
  483.                a data file.  
  484.  
  485.                To retrieve  a data record, you  must specify the database  the
  486.                record is in (eg. the customer database) and the record  number                                                                record  number
  487.                of the record you wish  to retrieve.  Once you have retrieved a
  488.                data record, you can  display or modify the information in  it.
  489.                If modified,  you must  place the  updated record  back in  the
  490.                data file.
  491.  
  492.                When you  have finished accessing a  database file, you  should
  493.                close  it to  be certain  sure all  of your  changes have  been               close
  494.                saved.
  495.  
  496.           Fields and Buffers
  497.  
  498.                When the  dBase  Toolkit  retrieves a  record from  a  database
  499.                file,  it is  initially  placed  in  a temporary  storage  area
  500.                called  a  data buffer.    All  data going  into  or out  of  a                          data buffer
  501.                database  file  must  pass through  this  buffer.    The  dBase
  502.                toolkit automatically creates and maintains  a buffer for  each
  503.                database file you work with.  
  504.  
  505.                The purpose  of the buffer is  to isolate your program from the
  506.                details and inner workings of the database file. 
  507.  
  508.  
  509.  
  510.  
  511.  
  512.  
  513.  
  514.  
  515.  
  516.  
  517.  
  518.  
  519.  
  520.  
  521.  
  522.                                 Database Basics - Page 6
  523.  
  524.  
  525.  
  526.  
  527.  
  528.  
  529.  
  530.  
  531.  
  532.  
  533.  
  534.  
  535.                                        DBF Module                                       __________
  536.  
  537.           The DBF  module provides a  complete set of  procedures for  working
  538.           with  dBase  III, III+,  and  IV  database files.    The  procedures
  539.           provided will allow you  to access (open) a  database file, add data
  540.           records to it, retrieve and modify  existing records, etc.   The DBF
  541.           module  insulates  you  from the  details of  working  with database
  542.           files allowing you to concentrate on the application being created.
  543.  
  544.           The database files used by the DBF module are completely  compatible
  545.           with  those created and used  by Ashton Tate's  dBase III, III+, and
  546.           IV.   dBase data  files have  been in use  for many  years and  have
  547.           become  the  industry standard  for  data  storage.   Numerous other
  548.           database  systems use  the dBase  file format  and a  host of  third
  549.           party utilities  are available for them.  By using the dBase format,
  550.           all of these utilities become available for your applications.
  551.  
  552.           DBF Usage          _________
  553.  
  554.           The  basic unit  of the DBF module  is the database file.   You must
  555.           declare a variable  of type DBFile  for each database file  you will
  556.           use. For example:
  557.  
  558.                     VAR CustomerFile : DBFile;
  559.  
  560.           This variable,  known as  the file  variable, is  used whenever  you
  561.           which to reference the  data file.  For  example, to open (initially
  562.           access) a database file you would use the statement:
  563.  
  564.                     OpenDBF(CustomerFile, 'CUSTFILE.DBF');
  565.  
  566.           When  this statement  is  run, the  DBF module  searches for  a file
  567.           named CUSTFILE.DBF,  opens it and  reads the structure  of the  data
  568.           file.  Information,  such as  the number  and types  of each  field,
  569.           record size,  number of records, etc. is digested and  stored by the
  570.           DBF  module.    If  the file  does  not  exist, or  is  not  a valid
  571.           database, an error is flagged.
  572.  
  573.           Opening the file also creates, in  memory, an area to store customer
  574.           data records known as the  record buffer.  Whenever a record is read
  575.           from the  file, it is  placed in this buffer.   For example, to read
  576.           the  third customer  record from  the data file,  you would  use the
  577.           statement:
  578.  
  579.                     GetRec(CustomerFile, 3);
  580.  
  581.           This reads the  third customer record, and  stores it in the  record
  582.           buffer.   You can then  access individual fields  within the  record
  583.           from this buffer.  For  example, to get the customer's name from the
  584.           record buffer:
  585.  
  586.                VAR CustName : ARRAY [0..29] OF CHAR;
  587.  
  588.                                   DBF Module - Page 7
  589.  
  590.  
  591.  
  592.  
  593.  
  594.  
  595.  
  596.  
  597.  
  598.  
  599.  
  600.  
  601.                     GetField(CustomerFile, 'NAME', CustName);
  602.  
  603.           This  copies the  customer's  name from  the  record buffer  to  the
  604.           variable   CustName  where   it  can   be  displayed,   edited,  and
  605.           manipulated  as desired.    Likewise,  to  put  information  into  a
  606.           customer field, you would use the statement:
  607.  
  608.                     PutField(CustomerFile, 'NAME', CustName);
  609.  
  610.           This copies  the contents  of the  variable CustName  into the  NAME
  611.           field in the record buffer.  Once the  record in the buffer has been
  612.           modified,  it  should  then  be  written  back  to  disk  with   the
  613.           statement:
  614.  
  615.                     PutRec(CustomerFile, 3);
  616.  
  617.           This places the  contents of the  record buffer  back into the  data
  618.           file  on disk.   If  you do  not do this,  the changes  made to that
  619.           record will be lost when  your program finishes.   When you are done
  620.           working with a data file, you should close it with the statement:
  621.  
  622.                     CloseDBF(CustomerFile);
  623.  
  624.           At the  end of this  chapter, a  short example  program is  provided
  625.           which demonstrates how these procedures are used together.
  626.  
  627.           On the  following page  is the  DBF definition  module.  This  lists
  628.           each procedure  and type that is provided by the DBF module.  To use
  629.           these  procedures, you  import the DBF module  into your application
  630.           program with the following statement:
  631.  
  632.                IMPORT DBF;
  633.  
  634.           You may then use any  procedure listed by preceding it with the DBF.
  635.           prefix.  For example:
  636.  
  637.                DBF.GetRec(CustomerFile, 3);
  638.  
  639.           This  is best illustrated in the example program at  the end of this
  640.           chapter.
  641.  
  642.           The  remainder of  this chapter  explains in  detail each  procedure
  643.           offered by  the DBF  module.   For technical  information about  the
  644.           structure of a DBF  database file, refer to appendix A at the end of
  645.           this manual.
  646.  
  647.  
  648.  
  649.  
  650.  
  651.  
  652.  
  653.  
  654.                                   DBF Module - Page 8
  655.  
  656.  
  657.  
  658.  
  659.  
  660.  
  661.  
  662.  
  663.  
  664.  
  665.  
  666.  
  667.           DBF Definition Module          _____________________
  668.  
  669.           DEFINITION MODULE DBF; 
  670.  
  671.           CONST
  672.              MaxFields = 200;
  673.              MaxRecLen = 4000;
  674.  
  675.              ErrOpen   = 1;       ErrSeek   = 5;          ErrMemory = 9;
  676.              ErrClose  = 2;       ErrLock   = 6;
  677.              ErrRead   = 3;       ErrUnLock = 7;
  678.              ErrWrite  = 4;       ErrBadDBF = 8;
  679.  
  680.           TYPE DBFile;
  681.           VAR OK        : BOOLEAN;
  682.               Error     : CARDINAL;
  683.               MultiUser : BOOLEAN;
  684.  
  685.           PROCEDURE AddRec    (D : DBFile);
  686.           PROCEDURE CloseDBF  (VAR D : DBFile);
  687.           PROCEDURE Deleted   (D : DBFile): BOOLEAN;
  688.           PROCEDURE DelRec    (D : DBFile);
  689.           PROCEDURE Encrypted (D : DBFile) : BOOLEAN;
  690.           PROCEDURE FieldData (D : DBFile; FieldName:ARRAY OF CHAR;
  691.                                VAR Type     : CHAR;
  692.                                VAR Len, Dec : CARDINAL);
  693.           PROCEDURE FieldName (D : DBFile; FieldNum : CARDINAL;
  694.                                VAR FieldName : ARRAY OF CHAR);
  695.           PROCEDURE FileName  (D : DBFile; VAR Name : ARRAY OF CHAR);
  696.           PROCEDURE GetField  (D : DBFile; FieldName: ARRAY OF CHAR;
  697.                                VAR TheField : ARRAY OF CHAR);
  698.           PROCEDURE GetRec    (D : DBFile; RecNum   : LONGCARD);
  699.           PROCEDURE GetRecBuf (D : DBFile; Buf      : ADDRESS);
  700.           PROCEDURE HasMDX    (D : DBFile) : BOOLEAN;
  701.           PROCEDURE Incomplete(D : DBFile) : BOOLEAN;
  702.           PROCEDURE LockRec   (D : DBFile; RecNum   : LONGCARD);
  703.           PROCEDURE OldField  (D : DBFile; FieldName: ARRAY OF CHAR;
  704.                                VAR TheField : ARRAY OF CHAR);
  705.           PROCEDURE OpenDBF   (VAR D : DBFile; FileName : ARRAY OF CHAR);
  706.           PROCEDURE PutField  (D : DBFile; FieldName: ARRAY OF CHAR;
  707.                                TheField : ARRAY OF CHAR);
  708.           PROCEDURE PutRec    (D : DBFile; RecNum   : LONGCARD);
  709.           PROCEDURE PutRecBuf (D : DBFile; Buf      : ADDRESS);
  710.           PROCEDURE RecCount  (D : DBFile) : LONGCARD;
  711.           PROCEDURE RecNo     (D : DBFile) : LONGCARD;
  712.           PROCEDURE RecSize   (D : DBFile) : CARDINAL;
  713.           PROCEDURE UnDelRec  (D : DBFile);
  714.           PROCEDURE UnLockRec (D : DBFile; RecNum   : LONGCARD);
  715.  
  716.           END DBF.
  717.  
  718.  
  719.  
  720.                                   DBF Module - Page 9
  721.  
  722.  
  723.  
  724.  
  725.  
  726.  
  727.  
  728.  
  729.  
  730.  
  731.  
  732.  
  733.           DBF Exports - Detail          ____________________
  734.  
  735.           TYPE DBFile;
  736.  
  737.                Your application must declare a variable of this type for  each
  738.                data file you will use.   For example, to  use a customer  data
  739.                file and  an  inventory data  file, declare  the following  two
  740.                variables:
  741.  
  742.                     VAR CustomerFile, InventoryFile : DBFile;
  743.  
  744.                The DBFile type is an  opaque data type.   This means that  the
  745.                details of  its structure  are  hidden  from your  application.
  746.                This is  done so that  changes can be  made in future  releases
  747.                without affecting compatibility.
  748.  
  749.           VAR OK : BOOLEAN;
  750.  
  751.                After any  DBF procedure  is called,  this variable  is set  to
  752.                indicate  whether   the  operation   was   successful.     Your
  753.                application should check  OK after each procedure call and make
  754.                sure the  operation was successful  (OK =  TRUE).  If  an error
  755.                occurred,  OK will be set to FALSE, and the you should give the
  756.                user  an error  message and  close any  open files  before  you
  757.                terminate your program.
  758.  
  759.           VAR Error : CARDINAL;
  760.  
  761.                If  an operation fails,  this variable returns the exact nature
  762.                of the failure.  For example, if OpenDBF fails, OK will  return
  763.                FALSE,  and the  value  in  Error  will provide  more  specific
  764.                details as to the  cause of the failure.   Error = 8  (Bad DBF)
  765.                would indicate that the  datafile is probably damaged or not  a
  766.                dBase data file.  The current error codes are as follows:
  767.  
  768.                     ErrOpen   = 1;      ErrSeek   = 5;      ErrMemory = 9;
  769.                     ErrClose  = 2;      ErrLock   = 6;
  770.                     ErrRead   = 3;      ErrUnLock = 7;
  771.                     ErrWrite  = 4;      ErrBadDBF = 8;
  772.  
  773.                More error  codes will be added  in future releases to  provide
  774.                more specific information.
  775.  
  776.           VAR MultiUser : BOOLEAN;
  777.  
  778.                If your  program will  be running in  a multi-user  environment
  779.                such  as a  network or multi-user operating  system, you should
  780.                set  this variable  TRUE.   When  MultiUser  is TRUE,  the  DBF
  781.                module will take special measures  when accessing data files to
  782.                make certain no damage  is caused be  two users  simultaneously
  783.                making changes.   The default setting  for MultiUser is  FALSE.
  784.                For more information on multi-user operation, see appendix D.
  785.  
  786.                                   DBF Module - Page 10
  787.  
  788.  
  789.  
  790.  
  791.  
  792.  
  793.  
  794.  
  795.  
  796.  
  797.  
  798.  
  799.           PROCEDURE AddRec(D : DBFile);
  800.  
  801.                AddRec adds the current  contents of the data record buffer  to
  802.                the end of the file on disk.
  803.  
  804.           PROCEDURE CloseDBF(VAR D : DBFile);
  805.  
  806.                CloseDBF makes  certain all changes  to the file  are saved  to
  807.                disk, closes the specified data file,  and dissolves the record
  808.                buffer.   This  procedure should  always  be called  when  your
  809.                program finishes with a data file.
  810.  
  811.           PROCEDURE Deleted(D : DBFile) : BOOLEAN;
  812.  
  813.                If the  current record  is marked for  deletion, this  function
  814.                returns TRUE; otherwise, it returns FALSE.
  815.  
  816.           PROCEDURE DelRec(D : DBFile);
  817.  
  818.                Marks  the current record for deletion by placing an '*' in the
  819.                first  byte of the record.  Note that if  the current record is
  820.                already marked for deletion, this procedure has no effect.
  821.  
  822.           PROCEDURE Encrypted(D : DBFile) : BOOLEAN;        (DBASE IV Only)
  823.  
  824.                dBase  IV provides  a  facility  for  encrypting (coding)  data
  825.                records for  security purposes.  If  a data file's records  are
  826.                encrypted,  this  function  will  return  TRUE;  otherwise,  it
  827.                returns FALSE.
  828.  
  829.           PROCEDURE FieldData(D : DBFile; FieldName : ARRAY OF CHAR;
  830.                               VAR Type     : CHAR;
  831.                               VAR Len, Dec : CARDINAL);
  832.  
  833.                This function returns the  type, length, and  number of decimal
  834.                places allocated for  the field specified  in FieldName.   It's
  835.                purpose is  to allow  the programmer to  get information  about
  836.                the data field's definition.
  837.  
  838.           PROCEDURE FieldName(D : DBFile; FieldNum : CARDINAL;
  839.                               VAR FieldName : ARRAY OF CHAR);
  840.  
  841.                This  function  will  return the  field  name  of a  particular
  842.                field.   For example,  to find the name of  field number 3, the
  843.                procedure would be called as follows:
  844.  
  845.                     VAR FieldName : ARRAY [0..10] OF CHAR;
  846.                     FieldName(CustomerFile, 3, FieldName);
  847.  
  848.                Note that  the maximum field name  length is 11 characters  and
  849.                that no white-space (blanks, tabs, etc.) is allowed in a  field
  850.                name.
  851.  
  852.                                   DBF Module - Page 11
  853.  
  854.  
  855.  
  856.  
  857.  
  858.  
  859.  
  860.  
  861.  
  862.  
  863.  
  864.  
  865.           PROCEDURE FileName(D : DBFile; VAR Name : ARRAY OF CHAR);
  866.  
  867.                This procedure returns  the name of the  file as it was  opened
  868.                or created.  For example:
  869.  
  870.                     VAR  FName : ARRAY [0..40] OF CHAR;
  871.                          CustomerFile : DBFile;
  872.  
  873.                     OpenDBF(CustomerFile, 'CUSTFILE');
  874.                     FileName(CustomerFile, FName);
  875.  
  876.                At the  end of  this sequence,  FName will  contain the  string
  877.                'CUSTFILE.DBF'.
  878.  
  879.           PROCEDURE GetField(D : DBFile; FieldName : ARRAY OF CHAR;
  880.                               VAR TheField : ARRAY OF CHAR);
  881.  
  882.                GetField gets  the data from a  particular field in the  record
  883.                buffer and copies it  into a variable.  Fields are referred  to
  884.                by  field name.   All  fields are stored  as ARRAYs OF  CHAR in
  885.                dBase data files (see appendix  A).  If you  wish to convert  a
  886.                numeric field  to one of  the standard  Modula-2 numeric types,
  887.                you  must first  read it  in as a  string, and then  convert it
  888.                using  one  of  the  conversion  routines  provided  with  your
  889.                compiler. For example:
  890.  
  891.                     VAR ZipStr : ARRAY [0..8] OF CHAR;
  892.                         ZipNum : LONGCARD;
  893.                         Done   : BOOLEAN;
  894.  
  895.                     GetField(CustomerFile, 'ZIP', ZipStr);
  896.                     ZipNum := StrToCard(ZipStr, 10, Done);
  897.  
  898.                Date fields are also stored as strings in the format  YYYYMMDD.
  899.                This format is convenient for most date operations.  
  900.  
  901.           PROCEDURE GetRec(D : DBFile; RecNum : LONGCARD);
  902.  
  903.                GetRec loads  in the  record specified  from the  data file  on
  904.                disk into the record buffer.   If the record cannot be found or
  905.                an  error occurs  during the  read, OK  is set  to FALSE.   For
  906.                example, to load the third record from the customer data file:
  907.  
  908.                     GetRec(CustomerFile, 3);
  909.  
  910.           PROCEDURE GetRecBuf(D : DBFile; Buf : ADDRESS);
  911.  
  912.                Once a  record is in the  record buffer, it is usually accessed
  913.                a field  at a time  via the GetField  and PutField  procedures.
  914.                However, GetRecBuf  provides a  means  for  copying the  entire
  915.                record from the record buffer to a user specified buffer. 
  916.  
  917.  
  918.                                   DBF Module - Page 12
  919.  
  920.  
  921.  
  922.  
  923.  
  924.  
  925.  
  926.  
  927.  
  928.  
  929.  
  930.  
  931.                This is  particularly useful in two  circumstances.  It  allows
  932.                the  programmer  to copy  records without  having to  deal with
  933.                individual  fields.   It  also allows  a  record to  be  copied
  934.                directly into a standard Modula-2 record variable.
  935.  
  936.                Note that it is important that  the user buffer is large enough
  937.                to accommodate  the entire  record or else unknown  (and almost
  938.                certainly  bad) things  will happen.   If  the record  size  is
  939.                unknown, play  it safe and  allocate a 4K  buffer, the  maximum
  940.                size for a dBase record.   An example of  how this procedure is
  941.                used follows:
  942.  
  943.                     TYPE CustRecType = RECORD
  944.                               DeletedFlag    : CHAR;
  945.                               Name           : ARRAY [0..29] OF CHAR;
  946.                               Address        : ARRAY [0..29] OF CHAR;
  947.                               City           : ARRAY [0..14] OF CHAR;
  948.                               State          : ARRAY [0..1] OF CHAR;
  949.                               Zip            : ARRAY [0..9] OF CHAR;
  950.                               Phone          : ARRAY [0..14] OF CHAR;
  951.                               Purchased      : ARRAY [0..11] OF CHAR;
  952.                               Balance        : ARRAY [0..11] OF CHAR;
  953.                          END;
  954.                     VAR  CustomerFile: DBFile;
  955.                          CustomerRec : CustRecType;
  956.  
  957.                     OpenDBF(CustomerFile, 'CUSTFILE.DBF', TRUE);
  958.                     GetRec(CustomerFile, 3);            
  959.                     GetRecBuf(CustomerFile, ADR(CustomerRec));    
  960.  
  961.           PROCEDURE HasMDX(D : DBFile) : BOOLEAN;      (dBase IV Only)
  962.  
  963.                dBase  IV has  finally  included  the much  needed  feature  of
  964.                multi-keyed index  (MDX) files.   These files will be supported
  965.                in a future  release of the dBase Toolkit.  If a  data file has
  966.                a multi-key index file associated with  it, this procedure will
  967.                return TRUE; otherwise, it will return FALSE.
  968.  
  969.           PROCEDURE Incomplete(D : DBFile) : BOOLEAN;
  970.  
  971.                Incomplete returns TRUE if the last write to the data file  was
  972.                not successfully completed.  This is usually caused by a  power
  973.                outage or  other catastrophic failure.   In situations in which
  974.                data integrity is crucial,  this procedure should  be called to
  975.                check for damage to the  data file.  If  it returns TRUE,  your
  976.                program should instruct the user to restore their last backup.
  977.  
  978.  
  979.  
  980.  
  981.  
  982.  
  983.  
  984.                                   DBF Module - Page 13
  985.  
  986.  
  987.  
  988.  
  989.  
  990.  
  991.  
  992.  
  993.  
  994.  
  995.  
  996.  
  997.           PROCEDURE LockRec(D : DBFile; RecNum : LONGCARD);      (Multi-user)
  998.  
  999.                LockRec allows a user to 'lock' a particular record so that  no
  1000.                other user has access to  it.  Locking a record is usually done
  1001.                to  prevent two users from making changes  to it simultaneously
  1002.                which would  result in  one user's  changes being  lost.   This
  1003.                procedure  is ignored  in single-user  environments.   For more
  1004.                information on multi-user programming, refer  to Appendix D  on
  1005.                multi-user programming.
  1006.  
  1007.           PROCEDURE OldField(D : DBFile; FieldName : ARRAY OF CHAR;
  1008.                               VAR TheField : ARRAY OF CHAR);
  1009.  
  1010.                Occasionally, a user makes changes to a record and then  wishes
  1011.                to undo those changes.  This procedure returns the contents  of
  1012.                a  field as  it was  originally read  into the  record  buffer,
  1013.                before  any changes  were made.   Note  however that  once  the
  1014.                record is written to disk, changes are final.
  1015.  
  1016.           PROCEDURE OpenDBF(VAR D : DBFile; FileName: ARRAY OF CHAR);
  1017.  
  1018.                OpenDBF  searches the  disk  for  the  data file  specified  in
  1019.                FileName.   If  the file is found,  it is opened,  and the file
  1020.                header is  read.   The file header  contains information  about
  1021.                the structure  of  the file  including record  size, number  of
  1022.                records, number  of fields, and  the type of each  field.  This
  1023.                information is read and  stored in the file variable.   OpenDBF
  1024.                also  allocates (creates)  the file's  record buffer.   If  the
  1025.                MultiUser variable is set to TRUE, then several users can  open
  1026.                the file at the same  time.  If MultiUser is set to FALSE,  the
  1027.                file is locked by  the first who  opens it.  No other  user can
  1028.                open the file  until the  user who first  locked it closes  the
  1029.                file.
  1030.  
  1031.           PROCEDURE PutField(VAR D : DBFile; FieldName : ARRAY OF CHAR;
  1032.                               TheField : ARRAY OF CHAR);
  1033.  
  1034.                PutField  places  the  contents  of   TheField  into  the  file
  1035.                variables record  buffer in the  field specified  by FieldName.
  1036.                For  example, to store the value '12345' in  the zip code field
  1037.                in the customer file, the procedure call would be: 
  1038.  
  1039.                     PutField(CustomerFile, 'ZIP', '12345');
  1040.  
  1041.           PROCEDURE PutRec(VAR D : DBFile; RecNum : LONGCARD);
  1042.  
  1043.                PutRec takes the record  in the record buffer and writes it  to
  1044.                disk at  the record number  specified.  For  example, to  write
  1045.                the current  record to disk as  the 10th record, the  procedure
  1046.                call would be:
  1047.  
  1048.                     PutRec(CustomerFile, 10);
  1049.  
  1050.                                   DBF Module - Page 14
  1051.  
  1052.  
  1053.  
  1054.  
  1055.  
  1056.  
  1057.  
  1058.  
  1059.  
  1060.  
  1061.  
  1062.  
  1063.           PROCEDURE PutRecBuf(VAR D : DBFile; Buf : ADDRESS);
  1064.  
  1065.                PutRecBuf  copies the  contents of  a separate  (user provided)
  1066.                record buffer into the file  variable's record buffer,  usually
  1067.                as  a prelude to writing the record to the disk.  See GetRecBuf
  1068.                for more  details.  For example  to write the record  contained
  1069.                in  CustRec  as  the 3rd  record  in  the  Customer  file,  the
  1070.                procedures would be:
  1071.  
  1072.                     PutRecBuf(CustomerFile, CustRec);
  1073.                     PutRec(CustomerFile, 3);
  1074.  
  1075.           PROCEDURE RecCount(D : DBFile) : LONGCARD;
  1076.  
  1077.                Returns the number of records currently in the data file.
  1078.  
  1079.           PROCEDURE RecNo(D : DBFile) : LONGCARD;
  1080.  
  1081.                Returns the number of the current record (the last record  read
  1082.                from or written to the data file).
  1083.  
  1084.           PROCEDURE RecSize(D : DBFile) : CARDINAL;
  1085.  
  1086.                Returns  the size  of each  data record.   This  is  useful for
  1087.                calculating the  size of a data  file, or for anticipating  the
  1088.                amount of disk space  required for a  particular operation. See
  1089.                related procedure GetRecBuf.
  1090.  
  1091.           PROCEDURE UnDelRec(D : DBFile);
  1092.  
  1093.                The opposite of the DelRec  procedure, UnDelRec places  a space
  1094.                in  the first  byte of the current  record.  If  the record was
  1095.                marked  for  deletion  with  an  '*'  in  the  first  position,
  1096.                UnDelRec will  remove the asterisk.   If the current record  is
  1097.                not marked for deletion, this procedure has no effect.
  1098.  
  1099.           PROCEDURE UnLockRec(D : DBFile; RecNum : LONGCARD);
  1100.  
  1101.                The opposite  of the LockRec  procedure, UnLockRec  removes the
  1102.                lock placed on a record so that it  becomes accessible to other
  1103.                users.   For more information on  record locking, refer to  the
  1104.                appendix on multi-user operation.
  1105.  
  1106.  
  1107.  
  1108.  
  1109.  
  1110.  
  1111.  
  1112.  
  1113.  
  1114.  
  1115.  
  1116.                                   DBF Module - Page 15
  1117.  
  1118.  
  1119.  
  1120.  
  1121.  
  1122.  
  1123.  
  1124.  
  1125.  
  1126.  
  1127.  
  1128.  
  1129.           Manipulating Records          ____________________
  1130.  
  1131.           Two   procedures,  GetRecBuf   and  PutRecBuf,   are  provided   for
  1132.           manipulating  records  as  a unit  rather than  a  field at  a time.
  1133.           These  commands copy an entire record from the data file into a user
  1134.           provided buffer.   This is particularly  useful for such  operations
  1135.           as  quickly  copying  records from  one  file  to  another.    These
  1136.           commands  can also be used to read a record into a standard Modula-2
  1137.           record structure.  (See the example in GetRecBuf).
  1138.  
  1139.           However,  it should be noted  that there is  an advantage in dealing
  1140.           with  fields  rather than  records as  the  basic  data unit.   When
  1141.           working with data by field  name, if changes are made to a data file
  1142.           such as  adding new fields, your  application program will not  need
  1143.           to be  modified or re-compiled.   However, programs  that deal  with
  1144.           records as a  unit will  need to  be modified  each time the  record
  1145.           structure changes.
  1146.  
  1147.  
  1148.  
  1149.  
  1150.  
  1151.  
  1152.  
  1153.  
  1154.  
  1155.  
  1156.  
  1157.  
  1158.  
  1159.  
  1160.  
  1161.  
  1162.  
  1163.  
  1164.  
  1165.  
  1166.  
  1167.  
  1168.  
  1169.  
  1170.  
  1171.  
  1172.  
  1173.  
  1174.  
  1175.  
  1176.  
  1177.  
  1178.  
  1179.  
  1180.  
  1181.  
  1182.                                   DBF Module - Page 16
  1183.  
  1184.  
  1185.  
  1186.  
  1187.  
  1188.  
  1189.  
  1190.  
  1191.  
  1192.  
  1193.  
  1194.  
  1195.           Sample Program Using DBF Module          _______________________________
  1196.  
  1197.           MODULE CustomerReport;
  1198.  
  1199.           IMPORT DBF, IO;
  1200.  
  1201.           VAR CustFile : DBF.DBFile;
  1202.               RecNum   : LONGCARD;
  1203.  
  1204.           PROCEDURE DisplayCustRec;
  1205.           VAR Field : ARRAY [0..29] OF CHAR;
  1206.           BEGIN
  1207.              DBF.GetField(CustFile, 'NAME', Field);    (* Get NAME field   *)
  1208.              IO.WrStr(Field);                          (* Display it       *)
  1209.              IO.WrLn;                                  (* Go to next line. *)
  1210.              DBF.GetField(CustFile, 'ADDR', Field);    (* Get ADDR field   *)
  1211.              IO.WrStr(Field);                          (* Display it       *)
  1212.              IO.WrLn;
  1213.                (* Continue for all fields. *)
  1214.           END DisplayCustRec;
  1215.              
  1216.           BEGIN
  1217.              DBF.Open(CustFile, 'CUSTFILE.DBF');       (* Open data file   *)
  1218.              IF NOT DBF.OK THEN                        (* If Open not OK,  *)
  1219.                 IO.WrStr('Error: file not found.');    (*    display an    *)
  1220.                 IO.WrLn;                               (*    error message *)
  1221.                 HALT;                                  (*    and quit now. *)
  1222.              END;
  1223.              RecNum := VAL(LONGCARD, 1);               (* Start at rec 1   *)
  1224.              WHILE DBF.OK AND                          (* While no errors, *)
  1225.                 (RecNum <= DBF.RecCount(CustFile)) DO  (* and more records *)
  1226.                 DBF.GetRec(CustFile, RecNum);          (*    Get record    *)
  1227.                 DisplayCustRec;                        (*    Display record*)
  1228.                 INC(RecNum);                           (*    Bump rec num. *)
  1229.              END;                                      (* Continue for all *)
  1230.              DBF.Close(CustFile);                      (* Close when done  *)
  1231.           END CustomerReport;
  1232.  
  1233.  
  1234.  
  1235.  
  1236.  
  1237.  
  1238.  
  1239.  
  1240.  
  1241.  
  1242.  
  1243.  
  1244.  
  1245.  
  1246.  
  1247.  
  1248.                                   DBF Module - Page 17
  1249.  
  1250.  
  1251.  
  1252.  
  1253.  
  1254.  
  1255.  
  1256.  
  1257.  
  1258.  
  1259.  
  1260.  
  1261.                                        NDX Module                                       __________
  1262.  
  1263.           The NDX library module provides complete access to dBase III,  III+,
  1264.           and  IV  index files.    Included  are procedures  to  open,  close,
  1265.           search, and update a standard dBase NDX file.  Specific  information
  1266.           on  each  item  exported  by  the  NDX  module  is  provided on  the
  1267.           following pages.  However,  in order to better understand the  usage
  1268.           of this module, you should first read the overview provided below.
  1269.  
  1270.           Overview          ________
  1271.  
  1272.           An index file  does for a data file  pretty much what an index  does
  1273.           for a book: it allows  you to quickly find  a particular topic based
  1274.           on a 'key' word  or phrase.  To use the index in a book, you quickly
  1275.           search  the index for the topic you are interested  in, get the page
  1276.           number, and  then go read that page.   An index  file works the same
  1277.           way.
  1278.  
  1279.           For  example, a  customer  data  file might  contain  1000  customer
  1280.           records.   In each customer's record  are fields for the  customer's
  1281.           name, address, phone  number, zip code,  etc.  To find  a customer's
  1282.           record, you  could search each  record until you  found the  desired
  1283.           customer,  but that  might take  a long time  (what if there  were a
  1284.           million customers?).  
  1285.  
  1286.           It would  certainly help to  have an alphabetical  list of  customer
  1287.           names, and  their corresponding record  numbers.  With  such a  list
  1288.           (an index),  you could  use a  binary search  to quickly  locate the
  1289.           name and record number  of any customer.   To  find a customer in  a
  1290.           file  of one  thousand  customers would  require examining  only ten
  1291.           names.   In a  file of  one million, you would  need to examine only
  1292.           twenty names! 
  1293.  
  1294.           The  NDX module  allows you  to create  and use  such indices.   For
  1295.           example,  you could create a  customer index file based  on the NAME
  1296.           field;  you  might call this index  'CUSTNAME.NDX'.  The index  file
  1297.           would contain an alphabetical  list of the customer names and  their
  1298.           record  numbers.   Each time a new  customer were added  to the data
  1299.           file, you  would insert the name  and record number  into the index,
  1300.           keeping it up-to-date.
  1301.  
  1302.           This  index could be used to rapidly locate any  customer.  It could
  1303.           also be used  to list the customers in alphabetical order creating a
  1304.           customer  report.     An   index  could   also  be   created  called
  1305.           'CUSTZIP.NDX' which  would keep  an ordered  list of  zip codes  and
  1306.           their corresponding customer record  numbers.  Such a list could  be
  1307.           useful for marketing and mailing purposes.
  1308.  
  1309.           You can  visualize  such  an  index  as  a list  of  key  fields  in
  1310.           alphabetical  order.  Each key  has with it the record number of the
  1311.           customer record which it came from.  For example:
  1312.  
  1313.  
  1314.                                   NDX Module - Page 18
  1315.  
  1316.  
  1317.  
  1318.  
  1319.  
  1320.  
  1321.  
  1322.  
  1323.  
  1324.  
  1325.  
  1326.  
  1327.                                   Customer Name Index
  1328.  
  1329.              Index       Customer Name (Key)           Record Number
  1330.            Pointer                                                      
  1331.                 -->   Astra, Inc.                            5          
  1332.                       Candi's Computer Center                2          
  1333.                       Digital Engineering                    1          
  1334.                       Fashions by Diana                      6          
  1335.                       HoshMed Research                       3          
  1336.                       Janisoft                               4          
  1337.                       Laura's Hiking Supplies                8          
  1338.                       Len's Literature                       7          
  1339.                                                                         
  1340.                                              etc.
  1341.  
  1342.           Because the list is  arranged alphabetically, it is easy to find the
  1343.           name  you  are looking  for.   With the  name is  the record  number
  1344.           allowing you to quickly pull up the associated record.
  1345.  
  1346.           The compatibility  of the  NDX index  files with  dBase index  files
  1347.           allows your applications to  be used together with dBase to  produce
  1348.           more sophisticated applications than are  possible with dBase alone.
  1349.           For  example, sophisticated  data  entry and  query  screens  can be
  1350.           written   in  Modula-2   with  features   not  available   to  dBase
  1351.           programmers.    However,  reports  and  custom  additions  are often
  1352.           quicker  and easier to program using dBase.  The DBF and NDX modules
  1353.           make it easy  to build hybrid  programs with the  ease of  dBase and
  1354.           the power of Modula-2.
  1355.  
  1356.           NDX Usage          _________
  1357.  
  1358.           The  NDXFile is the basic unit of the NDX  module.  You must declare
  1359.           a  variable of  this type  for each  index file you  will use.   For
  1360.           example:
  1361.  
  1362.                     VAR CustNameIndex : NDXFile;
  1363.  
  1364.           This  variable is  known  as  the index  file variable  and  is used
  1365.           whenever you reference  the index.   For example,  to open an  index
  1366.           file:
  1367.  
  1368.                     OpenNDX(CustNameIndex, 'CUSTNAME.NDX');
  1369.  
  1370.           When this  statement is executed, the  NDX module searches the  disk
  1371.           for a file  named CUSTNAME.NDX.   If found, NDX  opens it  and reads
  1372.           the structure  of the index file.  Information about  the index file
  1373.           is digested and stored.  
  1374.  
  1375.  
  1376.           The  index pointer (the current position in the index) is set to the
  1377.           beginning of  the file when the  file is opened.   The Next key  and
  1378.           Prev key  procedures allow  you to get  the next  and previous  keys
  1379.  
  1380.                                   NDX Module - Page 19
  1381.  
  1382.  
  1383.  
  1384.  
  1385.  
  1386.  
  1387.  
  1388.  
  1389.  
  1390.  
  1391.  
  1392.  
  1393.           (relative to the current index pointer).  For example:
  1394.  
  1395.                     RecNum := Next(CustNameIndex);
  1396.  
  1397.           This reads the next  key (alphabetically) in the index, and  returns
  1398.           the  number of the data record containing that key.   The DBF module
  1399.           can then be used to retrieve that record.
  1400.  
  1401.           The Find procedure can be used to quickly locate any name  or even a
  1402.           partial name.  For example:
  1403.  
  1404.                     RecNum := Find(CustNameIndex, 'Laura');
  1405.  
  1406.           This  will  search for  a  key  which is  equal  to or  larger  than
  1407.           'Laura'.   In our example, the  search would stop at "Laura's Hiking
  1408.           Supplies" and would return the record number of 8.
  1409.  
  1410.           When your application is done working with an  index file, it should
  1411.           be closed with the statement:
  1412.  
  1413.                          CloseNDX(CustNameIndex);
  1414.  
  1415.           This  makes sure  any changes to the  index file are  saved to disk,
  1416.           and that the file is safely closed before the program finishes.
  1417.  
  1418.                To summarize, using an index file involves 
  1419.  
  1420.                     1)   Declaring an index file variable,
  1421.                     2)   Opening the index file
  1422.                     3)   Searching for key(s)
  1423.                     4)   Loading the corresponding record(s)
  1424.                     5)   Closing the index file.
  1425.  
  1426.           The  NDX module  is intended  for use  in  conjunction with  the DBF
  1427.           module.   At  the end  of this chapter,  a short example  program is
  1428.           provided to  demonstrate how  the NDX  and DBF  procedures are  used
  1429.           together.   The  remainder  of this  chapter  will explain  in  more
  1430.           detail the use of each procedure provided by the NDX module.
  1431.  
  1432.  
  1433.  
  1434.  
  1435.  
  1436.  
  1437.  
  1438.  
  1439.  
  1440.  
  1441.  
  1442.  
  1443.  
  1444.  
  1445.  
  1446.                                   NDX Module - Page 20
  1447.  
  1448.  
  1449.  
  1450.  
  1451.  
  1452.  
  1453.  
  1454.  
  1455.  
  1456.  
  1457.  
  1458.  
  1459.           NDX Definition Module          _____________________
  1460.  
  1461.           DEFINITION MODULE NDX; 
  1462.  
  1463.           CONST  MaxKeyLen = 100;
  1464.  
  1465.                  ErrOpen   = 1;       ErrSeek   = 5;        ErrMemory = 9;
  1466.                  ErrClose  = 2;       ErrLock   = 6;
  1467.                  ErrRead   = 3;       ErrUnLock = 7;
  1468.                  ErrWrite  = 4;       ErrBadNDX = 8;
  1469.  
  1470.           TYPE  NDXFile;
  1471.  
  1472.           VAR OK        : BOOLEAN;
  1473.               Error     : CARDINAL;
  1474.               FOUND     : BOOLEAN;
  1475.               MultiUser : BOOLEAN;
  1476.               Retries   : CARDINAL;
  1477.  
  1478.           PROCEDURE OpenNDX  (VAR I : NDXFile; Name : ARRAY OF CHAR);
  1479.           PROCEDURE CloseNDX (VAR I : NDXFile);
  1480.           PROCEDURE CreateNDX(VAR I : NDXFile; FileName : ARRAY OF CHAR;
  1481.                               KeyField : ARRAY OF CHAR;
  1482.                               KeyType  : CHAR;
  1483.                               KeyLen   : CARDINAL);
  1484.  
  1485.           PROCEDURE AddKey   (I:NDXFile; Key:ARRAY OF CHAR; Ptr:LONGCARD);
  1486.           PROCEDURE DelKey   (I:NDXFile);
  1487.  
  1488.           PROCEDURE Find     (I:NDXFile; Key:ARRAY OF CHAR) : LONGCARD;
  1489.           PROCEDURE Next     (I:NDXFile) : LONGCARD;
  1490.           PROCEDURE Prev     (I:NDXFile) : LONGCARD;
  1491.  
  1492.           PROCEDURE GoTop    (I:NDXFile);
  1493.           PROCEDURE GoBottom (I:NDXFile);
  1494.  
  1495.           PROCEDURE BOF      (I : NDXFile) : BOOLEAN;           
  1496.           PROCEDURE EOF      (I : NDXFile) : BOOLEAN;           
  1497.           PROCEDURE KeyField (I : NDXFile;                      
  1498.                               VAR Field : ARRAY OF CHAR);       
  1499.           PROCEDURE Unique   (I : NDXFile) : BOOLEAN;           
  1500.  
  1501.           END NDX.
  1502.  
  1503.  
  1504.  
  1505.  
  1506.  
  1507.  
  1508.  
  1509.  
  1510.  
  1511.  
  1512.                                   NDX Module - Page 21
  1513.  
  1514.  
  1515.  
  1516.  
  1517.  
  1518.  
  1519.  
  1520.  
  1521.  
  1522.  
  1523.  
  1524.  
  1525.           NDX Exports - Detail          ____________________
  1526.  
  1527.           TYPE NDXFile;
  1528.  
  1529.                Your application must declare  a variable for  each index  file
  1530.                it  will use.   The  variables must  be of  type NDXFile.   For
  1531.                example, if  you will be accessing  the customer file by  name,
  1532.                you would declare the following variable:
  1533.  
  1534.                     VAR CustNameIndex : NDXFile;
  1535.  
  1536.                The  NDXFile type is an opaque data type.   This means that the
  1537.                details of  its structure  are  hidden  from your  application.
  1538.                This is  done so that  changes can be  made in future  releases
  1539.                without affecting compatibility.
  1540.  
  1541.           VAR OK : BOOLEAN;
  1542.  
  1543.                After any  NDX procedure  is called,  this variable  is set  to
  1544.                indicate  whether   the  operation   was   successful.     Your
  1545.                application should check  OK after each procedure call and make
  1546.                sure the  operation was successful  (OK =  TRUE).  If  an error
  1547.                occurred,  OK will be set to FALSE and you should give the user
  1548.                an error message and close any open files before you  terminate
  1549.                your program.
  1550.  
  1551.           VAR Error : CARDINAL;
  1552.  
  1553.                If  an operation fails,  this variable returns the exact nature
  1554.                of the failure.  For example, if OpenNDX fails, OK will  return
  1555.                FALSE,  and the  value  in  Error  will provide  more  specific
  1556.                details as to the  cause of the failure.   Error = 8  (Bad NDX)
  1557.                would indicate that the  index file is probably damaged or  not
  1558.                a dBase NDX file.  The current error codes are as follows:
  1559.  
  1560.                     ErrOpen   = 1;      ErrSeek   = 5;      ErrMemory = 9;
  1561.                     ErrClose  = 2;      ErrLock   = 6;
  1562.                     ErrRead   = 3;      ErrUnLock = 7;
  1563.                     ErrWrite  = 4;      ErrBadNDX = 8;
  1564.  
  1565.                More error  codes will be added  in future releases to  provide
  1566.                more specific information.
  1567.  
  1568.           VAR FOUND : BOOLEAN;
  1569.  
  1570.                After a  search procedure (Find, Next,  or Prev) this  variable
  1571.                is  set to indicate whether the key searched for was found.  If
  1572.                FOUND is FALSE after a Next or Prev operation,  then the end of
  1573.                the  file has  been  hit.    If FOUND  is  FALSE  after a  Find
  1574.                procedure, then an exact match was not found.
  1575.  
  1576.  
  1577.  
  1578.                                   NDX Module - Page 22
  1579.  
  1580.  
  1581.  
  1582.  
  1583.  
  1584.  
  1585.  
  1586.  
  1587.  
  1588.  
  1589.  
  1590.  
  1591.           VAR MultiUser : BOOLEAN;
  1592.  
  1593.                If  your program  will be  running in  a multi-user environment
  1594.                such as  a network or  multi-user operating  system, you should
  1595.                set  this variable  TRUE.   When  MultiUser  is TRUE,  the  NDX
  1596.                module will  take special measures  when accessing  index files
  1597.                to   make   certain   no  damage   is  caused   be   two  users
  1598.                simultaneously  making  changes.    The   default  setting  for
  1599.                MultiUser is FALSE.
  1600.  
  1601.           VAR Retries : CARDINAL;
  1602.  
  1603.                When the  NDX module  is running  in multi-user  mode, it  must
  1604.                frequently place locks on  the index file to prevent two  users
  1605.                from performing  conflicting operations.  If  one user has  the
  1606.                index locked  and a second  user attempts to  gain access,  the
  1607.                second user  must wait  and try  again.   The  NDX module  will
  1608.                automatically wait  and retry a  fixed number of  times.   That
  1609.                number is  held in the  variable Retries, and  may be read  and
  1610.                modified by the application program.
  1611.  
  1612.           PROCEDURE AddKey(I : NDXFile; Key : ARRAY OF CHAR; Ptr : LONGCARD);
  1613.  
  1614.                AddKey  inserts a  new  key into  the index.    Whenever  a new
  1615.                record  is added  to a  data file,  the key  field(s) for  that
  1616.                record should be added to their corresponding index files.   If
  1617.                an existing record's key  field is changed, the old key  should
  1618.                first be  deleted, and then AddKey  should be called to  insert
  1619.                the updated key.
  1620.  
  1621.           PROCEDURE BOF(I : NDXFile) : BOOLEAN;
  1622.  
  1623.                This procedure indicates when  the index pointer is at the  top
  1624.                (beginning) of the index.
  1625.  
  1626.           PROCEDURE CloseNDX(VAR I : NDXFile);
  1627.  
  1628.                CloseNDX makes certain all changes to the index file are  saved
  1629.                to  disk, updates  the index  header, and  closes the specified
  1630.                index file.  This  procedure should always be called when  your
  1631.                program finishes working with an index file.
  1632.  
  1633.           PROCEDURE CreateNDX(VAR I : NDXFile; FileName : ARRAY OF CHAR;
  1634.                               KeyField : ARRAY OF CHAR;
  1635.                               KeyType  : CHAR;
  1636.                               KeyLen   : CARDINAL);
  1637.  
  1638.                This procedure is  used to create  a new NDX  file.   To create
  1639.                the file,  the procedure  must be  given the  name for the  new
  1640.                file, the  key field  name (expression),  the type  of the  key
  1641.                (<C>haracter, <N>umeric,  <D>ate, or <L>ogical), and the length
  1642.                of the key.  For  example, to create  a new index file for  the
  1643.  
  1644.                                   NDX Module - Page 23
  1645.  
  1646.  
  1647.  
  1648.  
  1649.  
  1650.  
  1651.  
  1652.  
  1653.  
  1654.  
  1655.  
  1656.  
  1657.                customer  database  based  on  the  name  field  (which  is   a
  1658.                character  field of length 30) the following statement might be
  1659.                used:
  1660.  
  1661.                     CreateNDX(CustName, 'CUSTNAME.NDX', 'NAME', 'C', 30);
  1662.  
  1663.                Creating an  index will  overwrite any existing index  with the
  1664.                same name.  The newly created index will contain no keys.
  1665.  
  1666.  
  1667.           PROCEDURE DelKey(I : NDXFile);
  1668.  
  1669.                The delete  key procedure deletes the  last key found from  the
  1670.                index.  If the last search operation was not successful  (FOUND
  1671.                = TRUE), the DelKey procedure will do nothing.
  1672.  
  1673.           PROCEDURE EOF(I : NDXFile) : BOOLEAN;
  1674.  
  1675.                This  procedure indicates  when the  index  pointer is  at  the
  1676.                bottom (end) of the index.
  1677.  
  1678.  
  1679.           PROCEDURE Find(I : NDXFile; Key : ARRAY OF CHAR) : LONGCARD;
  1680.  
  1681.                The  Find  key  procedure quickly  searches the  index  for the
  1682.                specified key.  The  search method is a modified binary  search
  1683.                requiring no more than 20 accesses to find one  key in an index
  1684.                of one million keys.   If an exact match isn't found, the first
  1685.                key that  is higher than  the key specified  is returned.   The
  1686.                value returned  is the number  of the record  in the data  file
  1687.                containing the key higher  than or equal to the key  specified.
  1688.                An  example of how the Find procedure might be used to find the
  1689.                customer Laura's Hiking Supplies is as follows: 
  1690.  
  1691.                     RecNum := Find(CustName, "Laura's Hiking Supplies");
  1692.                     IF FOUND THEN
  1693.                          DBF.GetRec(CustomerFile, RecNum);
  1694.                     END;
  1695.  
  1696.                The variable FOUND will be set if an exact match is made.  
  1697.  
  1698.           PROCEDURE GoBottom(I : NDXFile);
  1699.  
  1700.                The GoBottom  procedure moves  the file  pointer to  the bottom
  1701.                (end) of the  index file.   After  this procedure  is run,  the
  1702.                Prev key  procedure will return the  last (largest) key in  the
  1703.                index.  
  1704.  
  1705.           PROCEDURE GoTop(I : NDXFile);
  1706.  
  1707.                The GoTop  procedure moves the  index file pointer  to the  top
  1708.                (beginning) of  the index file.   After this procedure is  run,
  1709.  
  1710.                                   NDX Module - Page 24
  1711.  
  1712.  
  1713.  
  1714.  
  1715.  
  1716.  
  1717.  
  1718.  
  1719.  
  1720.  
  1721.  
  1722.  
  1723.                the Next key procedure will return the first (smallest) key  in
  1724.                the index.
  1725.  
  1726.           PROCEDURE KeyField(I : NDXFile; VAR Field : ARRAY OF CHAR);
  1727.  
  1728.                The KeyField  procedure returns the  key field  expression (the
  1729.                name of the key field) used by the index.
  1730.  
  1731.  
  1732.  
  1733.           PROCEDURE Next(I : NDXFile) : LONGCARD;
  1734.  
  1735.                The Next  key procedure finds  the next sequential  key in  the
  1736.                index and  returns the number  of the record  in the data  file
  1737.                containing that key.   When the  end of the  file is  hit, this
  1738.                procedure will return a  null (Zero) pointer.  For example,  to
  1739.                list  each customer  record  in  alphabetical  order, the  code
  1740.                might be as follows:
  1741.  
  1742.                          GoTop(CustName);
  1743.                          WHILE NOT EOF(CustName) DO
  1744.                               RecNum := Next(CustName);
  1745.                               IF FOUND THEN
  1746.                                    DBF.GetRec(CustomerFile, RecNum);
  1747.                                    DisplayCustRec;
  1748.                               END;
  1749.                          END;
  1750.  
  1751.                As is the case  in the Find key  procedure, the FOUND  variable
  1752.                will reflect the success of the Next key operation.
  1753.  
  1754.           PROCEDURE Prev(I : NDXFile) : LONGCARD;
  1755.  
  1756.                The Prev  key procedure  finds the  previous sequential key  in
  1757.                the index  and returns the  number of  the record  in the  data
  1758.                file containing that  key.  When the  beginning of the file  is
  1759.                hit, this  procedure will return a null (Zero) pointer.  It can
  1760.                be used  to search  backwards  alphabetically  through a  file.
  1761.                For example, to  list the customer file in reverse alphabetical
  1762.                order, the code fragment might be:
  1763.  
  1764.                     GoBottom(CustName);
  1765.                     WHILE NOT BOF(CustName) DO
  1766.                          RecNum := Prev(CustName);
  1767.                          IF FOUND THEN
  1768.                               DBF.GetRec(CustomerFile, RecNum);
  1769.                               DisplayCustRec;
  1770.                          END;
  1771.                     END;
  1772.  
  1773.                As is  the case with  the Find and  Next procedures,  the FOUND
  1774.                variable reflects  the success of this procedure in finding the
  1775.  
  1776.                                   NDX Module - Page 25
  1777.  
  1778.  
  1779.  
  1780.  
  1781.  
  1782.  
  1783.  
  1784.  
  1785.  
  1786.  
  1787.  
  1788.  
  1789.                previous key.
  1790.  
  1791.           PROCEDURE Unique(I : NDXFile) : BOOLEAN;
  1792.  
  1793.                If  an  index  created  by  dBase  or  one of  its  clones  has
  1794.                specified that the index should only contain unique keys  (i.e.
  1795.                no  duplicate keys  should  be  allowed),  this procedure  will
  1796.                return  TRUE.   This can  be used  to maintain  the unique  key
  1797.                requirement in your applications.
  1798.  
  1799.  
  1800.  
  1801.  
  1802.  
  1803.  
  1804.  
  1805.  
  1806.  
  1807.  
  1808.  
  1809.  
  1810.  
  1811.  
  1812.  
  1813.  
  1814.  
  1815.  
  1816.  
  1817.  
  1818.  
  1819.  
  1820.  
  1821.  
  1822.  
  1823.  
  1824.  
  1825.  
  1826.  
  1827.  
  1828.  
  1829.  
  1830.  
  1831.  
  1832.  
  1833.  
  1834.  
  1835.  
  1836.  
  1837.  
  1838.  
  1839.  
  1840.  
  1841.  
  1842.                                   NDX Module - Page 26
  1843.  
  1844.  
  1845.  
  1846.  
  1847.  
  1848.  
  1849.  
  1850.  
  1851.  
  1852.  
  1853.  
  1854.  
  1855.           NDX Sample Program          __________________
  1856.  
  1857.           MODULE NDXTest;
  1858.           IMPORT IO, DBF, NDX;
  1859.           VAR CustomerFile : DBF.DBFile;
  1860.               CustNameIdx  : NDX.NDXFile;
  1861.               Name         : ARRAY [0..29] OF CHAR;
  1862.               RecNum       : LONGCARD;
  1863.  
  1864.           PROCEDURE MakeNameIdx;
  1865.           BEGIN
  1866.              NDX.CreateNDX(CustNameIdx, 'CUSTNAME.NDX', 'NAME, 'C', 30);
  1867.              RecNum := VAL(LONGCARD, 1);
  1868.              WHILE RecNum <= DBF.RecCount(CustomerFile) DO
  1869.                 DBF.GetRec(CustomerFile, RecNum);
  1870.                 DBF.GetField(CustomerFile, 'NAME', Name);
  1871.                 NDX.AddKey(CustNameIdx, Name, RecNum);
  1872.                 INC(RecNum);
  1873.              END;
  1874.           END MakeNameIdx;
  1875.  
  1876.           PROCEDURE DisplayCustomer;
  1877.           VAR FieldBuf : ARRAY OF CHAR;
  1878.           BEGIN
  1879.              DBF.GetField(CustomerFile, 'NAME', FieldBuf);
  1880.              IO.WrStr(FieldBuf); IO.WrLn;
  1881.              DBF.GetField(CustomerFile, 'ADDR', FieldBuf);
  1882.              IO.WrStr(FieldBuf); IO.WrLn;
  1883.                (* Same for all fields: get field and display it. *)
  1884.           END DisplayCustomer;
  1885.  
  1886.           BEGIN
  1887.              DBF.OpenDBF(CustomerFile, 'CUSTOMER.DBF');     (* Open DBF    *)
  1888.              MakeNameIdx;                                   (* Create NDX  *)
  1889.              REPEAT                                         (* Main loop!  *)
  1890.                 IO.WrStr('Enter name of customer.');        (* Get name of *)
  1891.                 IO.RdStr(Name);                             (* cust to find*)
  1892.                 IF Name # '' THEN                           (* If not blank*)
  1893.                    RecNum := NDX.Find(CustNameIdx, Name);   (*  Find name  *)
  1894.                    IF NDX.FOUND THEN                        (*  If found,  *)
  1895.                       DBF.GetRec(CustomerFile, RecNum);     (*   Get record*)
  1896.                       DisplayCustRec;                       (*   Display it*)
  1897.                    ELSE                                     (*  Otherwise, *)
  1898.                       IO.WrStr('Customer not found.');      (*   show msg. *)
  1899.                    END;                                     (* Continue    *)
  1900.                 END;                                        (* loop till no*)
  1901.               UNTIL (SearchName = '');                      (* name entered*)
  1902.               DBF.CloseDBF(CustomerFile);                   (* Close DBF,  *)
  1903.               NDX.CloseNDX(CustNameIdx);                    (* Close NDX   *)
  1904.           END NDXTest.                                      (* Done!       *)
  1905.  
  1906.  
  1907.  
  1908.                                   NDX Module - Page 27
  1909.  
  1910.  
  1911.  
  1912.  
  1913.  
  1914.  
  1915.  
  1916.  
  1917.  
  1918.  
  1919.  
  1920.  
  1921.                                     BuildNDX Module                                    _______________
  1922.  
  1923.           The BuildNDX library  module provides procedures  which allow you to
  1924.           quickly  generate complex  indices  for  database  files.   Using  a
  1925.           modified  QuickSort  technique,  and  processing  large  numbers  of
  1926.           records at  a time, the BuildNDX  module builds indices much  faster
  1927.           than dBase III+ can.  
  1928.  
  1929.           The indices built  are completely compatible  with those  created by
  1930.           dBase III+ or IV and may be used with either or with the NDX  module
  1931.           from  the  previous chapter.    Specific  information on  each  item
  1932.           exported by the BuildNDX module is provided on the following  pages.
  1933.           However, in  order to better  understand the usage  of this  module,
  1934.           you  should first  read  the overview  provided  in the  NDX  module
  1935.           chapter as well as the overview below.
  1936.  
  1937.           Overview          ________
  1938.  
  1939.           When working  with large databases,  it is important  to be able  to
  1940.           rearrange  and work  with the data  records in a  variety of orders.
  1941.           For example an  application might need to  list all customers in the
  1942.           customer  file organized  by  city and  within  each city  list  the
  1943.           customers alphabetically.  
  1944.  
  1945.           To do this, a new index needs to be generated with a key  consisting
  1946.           of the  CITY  and NAME  fields concatenated.   This  index could  be
  1947.           generated using only the  NDX module by first  creating an empty NDX
  1948.           file, and then entering a loop  that reads each customer record one-
  1949.           at-a-time,  concatenating  the  CITY  and   NAME  fields  from  each
  1950.           customer record and adding the  new key to the new index file.   For
  1951.           example:
  1952.  
  1953.                VAR RecNum : LONGCARD;
  1954.                    Name   : ARRAY[0..29] OF CHAR;
  1955.                    City   : ARRAY[0..9]  OF CHAR;
  1956.                    NewKey : ARRAY[0..39] OF CHAR;
  1957.  
  1958.                    NDX.OpenNDX(CityNameNDX, 'CITYNAME.NDX', 
  1959.                                'CITY+NAME', 'C', 40);
  1960.                    FOR RecNum := 1 TO DBF.NumRecs(CustFile) DO
  1961.                        DBF.GetRec(CustFile, RecNum);
  1962.                        DBF.GetField(CustFile, 'NAME', Name);
  1963.                        DBF.GetField(CustFile, 'CITY', City);
  1964.                        Str.Concat(NewKey, City, Name);
  1965.                        NDX.AddKey(CityNameNDX, NewKey, RecNum);
  1966.                    END;
  1967.                    NDX.CloseNDX(CityNameNDX);
  1968.  
  1969.             For  small files, this  would work  fine.  However,  if there were
  1970.           several thousand customer records, this method would be very slow. 
  1971.  
  1972.  
  1973.  
  1974.                                BuildNDX Module - Page 28
  1975.  
  1976.  
  1977.  
  1978.  
  1979.  
  1980.  
  1981.  
  1982.  
  1983.  
  1984.  
  1985.  
  1986.  
  1987.           MakeNDX          _______
  1988.  
  1989.           The BuildNDX module  provides several  procedures to aid in  rapidly
  1990.           creating  indices.   The main  procedure  is MakeNDX  which requires
  1991.           only  the name  of the  database file,  the name  for the  new index
  1992.           file,  the  field(s) to  use  in  constructing the  new  index,  and
  1993.           optionally  the  key length.    For  example, to  create  the  index
  1994.           described above would require only the following statement:
  1995.  
  1996.               MakeNDX('CUSTFILE', 'CITYNAME', 'CITY+NAME', 0);
  1997.  
  1998.           Note  that  the  key-field parameter  lists  the  name  of  the  key
  1999.           field(s)  to use in generating  the index.   More than one field can
  2000.           be concatenated with the '+' sign.  Note too that  if the key length
  2001.           parameter is set to 0, the MakeNDX procedure will automatically  set
  2002.           the length as required to hold the key(s).
  2003.  
  2004.           The MakeNDX procedure is able to generate indices more rapidly  than
  2005.           other techniques by performing most of its sorting in memory  rather
  2006.           than  on disk.   When the procedure  is called, it loads  as much of
  2007.           the specified data file as possible into memory buffers.  
  2008.  
  2009.           For each data record  in memory, MakeNDX calls the Filter  procedure
  2010.           (see  below) to decide whether  to include that record  in the index
  2011.           it is creating. If the filter procedure returns TRUE, the record  is
  2012.           to be included.  If  it returns FALSE, the record is skipped and the
  2013.           next  one examined.    The  DefaultFilter  procedure always  returns
  2014.           TRUE, causing all records to be included in the index.
  2015.  
  2016.           When  the  filter  procedure  returns true,  MakeNDX  calls  the Key
  2017.           procedure  (see below) to extract the key data from the data record.
  2018.           The  key  data is  added  to  the index,  then  the next  record  is
  2019.           examined.   The DefaultKey procedure  returns the  field(s) selected
  2020.           in the MakeNDX procedure's KeyField parameter.
  2021.  
  2022.           Once all records have been filtered, their keys extracted and  added
  2023.           to the  index, they are sorted  via the Quicksort algorithm, and the
  2024.           dbase compatible B-tree is  built.  Finally, the index is closed and
  2025.           is ready for use.
  2026.  
  2027.           A good  examples of the use of the MakeNDX procedure is found in the
  2028.           MAKENDX utility  program.  This program  allows you to create  dBase
  2029.           compatible indices from the DOS command line using only the  MakeNDX
  2030.           procedure.
  2031.  
  2032.  
  2033.  
  2034.  
  2035.  
  2036.  
  2037.  
  2038.  
  2039.  
  2040.                                BuildNDX Module - Page 29
  2041.  
  2042.  
  2043.  
  2044.  
  2045.  
  2046.  
  2047.  
  2048.  
  2049.  
  2050.  
  2051.  
  2052.  
  2053.           Filter Procedure          ________________
  2054.  
  2055.           Some  applications may  require  more  sophisticated  indices.   For
  2056.           example, a  program might need to generate a listing of customers by
  2057.           city as  above, but showing  only customers with  balances over  one
  2058.           thousand  dollars.    You could  accomplish  this  using  the  index
  2059.           created  above  and  then  retrieving  each  customer's  record  and
  2060.           checking their  balance before  deciding  whether to  print them  or
  2061.           not.  For Example:
  2062.  
  2063.                VAR RecNum : LONGCARD;
  2064.                    BalStr : ARRAY[0..15] OF CHAR;
  2065.                    Balance: LONGREAL;
  2066.  
  2067.                     BuildNDX.MakeNDX('CUSTFILE', 'CITYNAME', 'CITY+NAME', 0);
  2068.                     NDX.OpenNDX(CityName, 'CITYNAME');
  2069.                     WHILE NOT (NDX.EOF(CityName)) DO
  2070.                        RecNum := NDX.Next(CityName);
  2071.                        DBF.GetRec(CustFile, RecNum);
  2072.                        DBF.GetField(CustFile, 'BALANCE', BalStr);
  2073.                        StrToCard(BalStr, Balance);
  2074.                        IF Balance > 1000.00 THEN
  2075.                           PrintRec;
  2076.                        END; (* If balance > 1000 *)
  2077.                     END; (* While not at end of index *)
  2078.                     NDX.CloseNDX(CityName);
  2079.  
  2080.           The  above method  would work.    However,  if there  were very  few
  2081.           customers  with  balances over  $1000, most  of  the program's  time
  2082.           would  be  spent  retrieving  and  examining  the records  of  those
  2083.           customers that need not be  printed.  A better  solution would be to
  2084.           create  an  index such  that  it  only contained  the  records  that
  2085.           belonged  in the  listing i.e.  to filter  out those  records  which
  2086.           don't belong. 
  2087.  
  2088.           To  accomplish  this,  the  BuildNDX   module  provides  for  filter
  2089.           procedures.   You can create  your own filter  procedure to  specify
  2090.           which database records should  be included in an index and then tell
  2091.           the BuildNDX module to  use your filter procedure when building  new
  2092.           indices.
  2093.  
  2094.           Filter procedures  are always of the  type FilterType as defined  in
  2095.           the BuildNDX module. i.e. of the format:
  2096.  
  2097.                     PROCEDURE procname () : BOOLEAN;
  2098.  
  2099.           Your  filter procedure  will simply  get information about  the data
  2100.           record currently being  processed and based on that information will
  2101.           return  TRUE  or  FALSE  indicating  whether  the  record  should be
  2102.           included in the index or not.
  2103.  
  2104.  
  2105.  
  2106.                                BuildNDX Module - Page 30
  2107.  
  2108.  
  2109.  
  2110.  
  2111.  
  2112.  
  2113.  
  2114.  
  2115.  
  2116.  
  2117.  
  2118.  
  2119.           For  example, the  above program  fragment  could be  re-written  as
  2120.           follows:
  2121.  
  2122.                VAR RecNum : LONGCARD;
  2123.  
  2124.                    PROCEDURE BalanceFilter() : BOOLEAN;
  2125.                    VAR BalStr : ARRAY [0..15] OF CHAR;
  2126.                        Balance: LONGREAL;
  2127.                    BEGIN
  2128.                       BuildNDX.GetField('BALANCE', BalStr);
  2129.                       StrToCard(BalStr, Balance);
  2130.                       RETURN (Balance > 1000.00);
  2131.                    END;
  2132.  
  2133.                BEGIN
  2134.                    BuildNDX.Filter := BalanceFilter;
  2135.                    BuildNDX.MakeNDX('CUSTFILE', 'CITYNAME', 'CITY+NAME', 0);
  2136.                    NDX.OpenNDX(CityName, 'CITYNAME);
  2137.                    WHILE NOT (NDX.EOF(CityName)) DO
  2138.                        PrintRec;
  2139.                    END; (* While not at end of index *)
  2140.                    NDX.CloseNDX(CityName);
  2141.                END;
  2142.  
  2143.           In  this program  fragment,  a  filter  procedure: BalanceFilter  is
  2144.           defined.  This procedure  extracts the Balance field from each  data
  2145.           record it examines and determines whether the record belongs in  the
  2146.           index based  on the  balance being  over or  under $1000,  returning
  2147.           TRUE or FALSE accordingly.
  2148.  
  2149.           The  BuildNDX module  is  told to  use  this filter  procedure  when
  2150.           creating new indices by the statement:
  2151.  
  2152.                     BuildNDX.Filter := BalanceFilter;
  2153.  
  2154.           Note  that   this  statement  is   not  calling   the  BalanceFilter
  2155.           procedure.    It  is  storing  the  address  of  the   BalanceFilter
  2156.           procedure in the variable Filter.
  2157.  
  2158.           When the MakeNDX procedure  is called, as it processes each  record,
  2159.           it calls the filter procedure indicated by the variable Filter.   If
  2160.           the  filter  procedure  called  returns  true,  the  key  field   is
  2161.           extracted  from the record and  added to the  index being built.  If
  2162.           it returns false, the record is skipped and no key is added.
  2163.  
  2164.           You  can  use  the procedures  Deleted  and  GetField,  exported  by
  2165.           BuildNDX,  to decide  whether  to include  a  record or  not.    Use
  2166.           GetField to extract  the field information from the record currently
  2167.           being  processed then  use that  information  to decide  whether the
  2168.           record should be included in the index or not.
  2169.  
  2170.  
  2171.  
  2172.                                BuildNDX Module - Page 31
  2173.  
  2174.  
  2175.  
  2176.  
  2177.  
  2178.  
  2179.  
  2180.  
  2181.  
  2182.  
  2183.  
  2184.  
  2185.           Advanced Keys          _____________
  2186.  
  2187.           It  is sometimes  necessary to  generate index  keys that  are  more
  2188.           complex than  a single field  or even  multiple fields concatenated.
  2189.           To accomodate  these complex keys,  the BuildNDX  module allows  for
  2190.           key procedures.   Similar to filter  procedures, key procedures  are
  2191.           called once  for  each record  processed by  the MakeNDX  procedure.
  2192.           However, unlike filter procedures,  a key procedure is passed a  key
  2193.           buffer and returns a key.
  2194.  
  2195.           The  key  procedure  can  access information  and  field(s)  in  the
  2196.           current  data  record  via  the   GetField  and  Deleted  procedures
  2197.           exported by the  BuildNDX module.   Using this and  any other  data,
  2198.           the  key procedure  should construct  a key  value for  the  current
  2199.           record and return it in the key buffer passed.
  2200.  
  2201.           A  key procedure is always of the type KeyExpType as exported by the
  2202.           BuildNDX module i.e. of the format:
  2203.  
  2204.                     PROCEDURE procname (VAR key : ARRAY OF CHAR);
  2205.  
  2206.           Your  key  expression  will  extract  field  data  from  the  record
  2207.           currently being  processed (using the  GetField procedure)  and will
  2208.           generate a key for this record.  The DefaultKeyExp procedure  simply
  2209.           extracts  and  returns  the  field(s)   specified  in  the   KeyName
  2210.           parameter when MakeNDX was called.
  2211.  
  2212.           To install your Key procedure, use the statement:
  2213.  
  2214.                          BuildNDX.KeyExp := procname;
  2215.  
  2216.  
  2217.  
  2218.  
  2219.  
  2220.  
  2221.  
  2222.  
  2223.  
  2224.  
  2225.  
  2226.  
  2227.  
  2228.  
  2229.  
  2230.  
  2231.  
  2232.  
  2233.  
  2234.  
  2235.  
  2236.  
  2237.  
  2238.                                BuildNDX Module - Page 32
  2239.  
  2240.  
  2241.  
  2242.  
  2243.  
  2244.  
  2245.  
  2246.  
  2247.  
  2248.  
  2249.  
  2250.  
  2251.                                 DBF Toolkit - Utilities                                _______________________
  2252.  
  2253.           To  make database  development  easier,  some  utility programs  are
  2254.           included  to create  and edit  dBase  compatible database  files and
  2255.           dBase compatible indices:
  2256.  
  2257.           MAKEDBF.EXE
  2258.  
  2259.                Using  easy  menu  driven  techniques,   you  can  create   new
  2260.                databases  and modify  the  structure  of existing  ones.    To
  2261.                execute the program type:
  2262.  
  2263.                          MAKEDBF filename
  2264.  
  2265.           MAKENDX.EXE
  2266.  
  2267.                From the command line,  generate an index for a given  database
  2268.                file.    You  can  specify  single  or  multiple (concatenated)
  2269.                fields for the key field(s).  To create an index, type:
  2270.  
  2271.                          MAKENDX dbfname ndxname keyfield
  2272.                                    or
  2273.                          MAKENDX dbfname ndxname key1+key2+key3...
  2274.  
  2275.  
  2276.           Many other  utilities are  available both  as shareware  and in  the
  2277.           public domain for working with dBase compatible database files.   If
  2278.           there  is  sufficient  demand,  other  utilities  including  a  file
  2279.           browser will  be released in  the next version  of the  Modula-Tools
  2280.           dBF toolkit.  Suggestions and contributions are welcome.
  2281.  
  2282.  
  2283.  
  2284.  
  2285.  
  2286.  
  2287.  
  2288.  
  2289.  
  2290.  
  2291.  
  2292.  
  2293.  
  2294.  
  2295.  
  2296.  
  2297.  
  2298.  
  2299.  
  2300.  
  2301.  
  2302.  
  2303.  
  2304.                                   Utilities - Page 33
  2305.  
  2306.  
  2307.  
  2308.  
  2309.  
  2310.  
  2311.  
  2312.  
  2313.  
  2314.  
  2315.  
  2316.  
  2317.                                      DBF Data Files
  2318.  
  2319.           The  data files  used by  the DBF  module are  100% compatible  with
  2320.           industry standard  dBase III,  III+, and  IV data files.   DBF  data
  2321.           files contain  three major sections:   the DBF header, field  array,
  2322.           and data records.    
  2323.  
  2324.                                    DBF File Structure
  2325.                                                                              
  2326.                                                                               
  2327.                        DBF          Field                  Data               
  2328.                       Header        Array                Records          
  2329.                                                                              
  2330.                                                                              
  2331.  
  2332.           Header
  2333.                The  DBF Header is located  at the beginning of the file and is
  2334.                32 bytes  long.  It  contains information about  the length  of
  2335.                the  header (including  the field  array), the  length of  each
  2336.                data  record, whether  there are associated Memo  or MDX files,
  2337.                etc.  The header structure is outlined below:
  2338.  
  2339.                     Position    Type         Description
  2340.                         0     SHORTCARD      Has Memo File (03H or 83H)
  2341.                         1     3 BYTES        Last update date
  2342.                         4     LONGCARD       Number of Records
  2343.                         8     CARDINAL       Header len (incl. field array)
  2344.                        10     CARDINAL       Record length
  2345.                        12     2 BYTES        Reserved for future use
  2346.                        14     BOOLEAN        Incomplete transaction flag
  2347.                        15     BOOLEAN        Encrypted file flag
  2348.                        16     12 BYTES       Reserved for network use
  2349.                        28     BOOLEAN        Has associated MDX file
  2350.                        29     3 BYTES        Reserved for future use
  2351.  
  2352.           Field Array
  2353.                Each field  record is  32 bytes  long and  contains a  complete
  2354.                description of one of  the data fields.   For example, if  each
  2355.                data record  contains 8  fields, the  field array  will have  8
  2356.                field records.   The field record  array will occupy 256  bytes
  2357.                (8 * 32), and  the total file header length, including the  DBF
  2358.                header will  be 288  bytes (256  + 32).    Thus  the number  of
  2359.                fields in a data file can be calculated as:   
  2360.  
  2361.                          NumFields := (DBFHeaderLength - 32) DIV 32;  
  2362.  
  2363.  
  2364.  
  2365.  
  2366.  
  2367.  
  2368.  
  2369.  
  2370.                                   Appendix A - Page 34
  2371.  
  2372.  
  2373.  
  2374.  
  2375.  
  2376.  
  2377.  
  2378.  
  2379.  
  2380.  
  2381.  
  2382.  
  2383.                Each field record is structured as follows:
  2384.                     Position    Type         Description
  2385.                         0     11 BYTES       Field Name
  2386.                        11     CHAR           Field type ('C', 'N', 'L', 'D')
  2387.                        12     4 BYTES        Reserved for future use
  2388.                        16     SHORTCARD      Field length
  2389.                        17     SHORTCARD      Field decimal places
  2390.                        18     2 BYTES        Reserved for future use
  2391.                        20     SHORTCARD      Work Area ID
  2392.                        21     11 BYTES       Reserved for future use
  2393.  
  2394.           Data Records
  2395.  
  2396.                Each data record starts with a single  character that indicates
  2397.                whether  the record  has  been marked  for  deletion.   If  the
  2398.                record  is  to  be  deleted,  the  character  is  an  asterisk;
  2399.                otherwise, it  is a  space.   The balance  of each  data record
  2400.                contains the  data fields as  listed in the  field array.   All
  2401.                field data is stored in string format.  For example:
  2402.  
  2403.                          Numeric field       '  123.45'
  2404.                          Date field          '19900704'
  2405.                          Character field     'Test    '
  2406.                          Logical field       'T'
  2407.  
  2408.                Numeric data  is stored  as  characters right  adjusted in  the
  2409.                field.  The field  length is defined in  the field array record
  2410.                and includes decimal point and decimal places (if any).
  2411.  
  2412.                Date  fields  are  stored  using  8  characters in  the  format
  2413.                YYYYMMDD.
  2414.  
  2415.                Character data  is stored  left adjusted with  any extra  space
  2416.                filled with  blanks.   The DBF  module automatically trims  the
  2417.                trailing  blank spaces  from  the field  when  it is  read  and
  2418.                terminates   it   with   a   null    character   for   Modula-2
  2419.                compatibility.   When  placing a  Modula-2  string into  a  DBF
  2420.                field, the DBF module adds the required spaces.
  2421.  
  2422.                Logical (boolean)  fields are  stored  as  a single  character,
  2423.                either a 'T' or a 'F'.
  2424.  
  2425.                The  length of  each record  is defined  in the DBF  header and
  2426.                includes  the  character  to  indicate  whether  the  record is
  2427.                deleted or not.  
  2428.  
  2429.                The  number of  records in  a file  is also  listed in  the DBF
  2430.                header.   32 bit Long cardinals are used for record numbers, so
  2431.                dBase files can hold a  virtually unlimited number  of records,
  2432.                limited only by disk space.
  2433.  
  2434.  
  2435.  
  2436.                                   Appendix A - Page 35
  2437.  
  2438.  
  2439.  
  2440.  
  2441.  
  2442.  
  2443.  
  2444.  
  2445.  
  2446.  
  2447.  
  2448.  
  2449.           General
  2450.                Although  it may  seem  that this  method  of storing  data  is
  2451.                cumbersome  compared  to simply  declaring  a  Modula-2  record
  2452.                structure and  a file of  that record type,  there are  several
  2453.                advantages to the DBF  method.  The first  and most apparent is
  2454.                compatibility  with   an  industry  standard.     Due   to  the
  2455.                prevalence  of  dBase  and its  clones in  the  business world,
  2456.                almost every  product dealing with  databases will  support the
  2457.                DBF file  format.   There are  numerous  report generators  and
  2458.                utilities designed  to work with  dBase data files;   with  the
  2459.                DBF  module, all of these third party products become available
  2460.                to support your applications.
  2461.  
  2462.           Another  advantage of the DBF file format is that it makes it easier
  2463.           to make changes to the data file without requiring program  changes.
  2464.           Since the record structure is  defined in the DBF file and not hard-
  2465.           coded into  your program, fields can  by added or  moved without any
  2466.           program changes.  Since many applications  involve large  numbers of
  2467.           separate programs and modules, this can save a great deal of time.
  2468.  
  2469.  
  2470.           Limitations - (for dBase compatibility)
  2471.  
  2472.                Maximum Field length            255
  2473.                Maximum Number of Fields        200
  2474.                Maximum Record length          4000
  2475.                Maximum Number of Records      Unlimited
  2476.  
  2477.  
  2478.  
  2479.  
  2480.  
  2481.  
  2482.  
  2483.  
  2484.  
  2485.  
  2486.  
  2487.  
  2488.  
  2489.  
  2490.  
  2491.  
  2492.  
  2493.  
  2494.  
  2495.  
  2496.  
  2497.  
  2498.  
  2499.  
  2500.  
  2501.  
  2502.                                   Appendix A - Page 36
  2503.  
  2504.  
  2505.  
  2506.  
  2507.  
  2508.  
  2509.  
  2510.  
  2511.  
  2512.  
  2513.  
  2514.  
  2515.                                     NDX Index Files
  2516.  
  2517.           The index files  created and used by  the NDX module are  compatible
  2518.           with the NDX files used  by DBase III, III+,  and IV with exceptions
  2519.           noted at the end of this appendix.   Use of the dBase NDX format for
  2520.           your applications indices will allow you to freely access your  data
  2521.           from both Modula-2 and dBase or one of  its clones without requiring
  2522.           re-indexing.
  2523.  
  2524.           Like  the  DBF files,  NDX  files  begin with  a  header  containing
  2525.           information about the format  of the index including the key  field,
  2526.           length,  root page  pointer, number  of keys  per index  page,  etc.
  2527.           Following the NDX header are  the actual pages which form the B tree
  2528.           index.
  2529.  
  2530.                                    NDX File Structure
  2531.                                                              
  2532.                                     
  2533.                             NDX            Index              
  2534.                            Header          Pages
  2535.                                      
  2536.                                                              
  2537.           Header  
  2538.  
  2539.                The NDX Header is located  at the beginning of  the file and is
  2540.                512 bytes (one page)  long.  It contains information about  the
  2541.                key  field expression,  the length  of the  key, the  key  type
  2542.                (numeric or  not), etc.  The  NDX header structure is  outlined
  2543.                below:
  2544.  
  2545.                     Position    Type         Description
  2546.                         0     LONGCARD       Root page number
  2547.                         4     LONGCARD       Next free page number
  2548.                         8     4 BYTES        Reserved for future use
  2549.                        12     CARDINAL       Key field length
  2550.                        14     CARDINAL       Number of keys per page
  2551.                        16     CARDINAL       Numeric key flag (1 = numeric)
  2552.                        18     CARDINAL       Key size (including pointers)
  2553.                        20     CARDINAL       Reserved for future use
  2554.                        22     CARDINAL       Unique keys flag (1 = unique)
  2555.                        24     100 BYTES      Key field expression
  2556.                       124     388 BYTES      Unused
  2557.  
  2558.  
  2559.  
  2560.  
  2561.  
  2562.  
  2563.  
  2564.  
  2565.  
  2566.  
  2567.  
  2568.                                   Appendix B - Page 37
  2569.  
  2570.  
  2571.  
  2572.  
  2573.  
  2574.  
  2575.  
  2576.  
  2577.  
  2578.  
  2579.  
  2580.  
  2581.           Index Pages
  2582.  
  2583.                After the  NDX header, which  occupies the first  512 bytes  of
  2584.                the file, come the actual  index pages.  Each page is 512 bytes
  2585.                long and is structured as follows:
  2586.  
  2587.                     Position    Type         Description
  2588.                         0     CARDINAL       Number of keys in page
  2589.                         2     2 BYTES        Unused
  2590.                         4     508 BYTES      NDX Keys
  2591.  
  2592.                The  pages are  not stored  in any  particular order,  but  are
  2593.                allocated  one-at-a-time  as  needed.     Pages  are   numbered
  2594.                starting  at 1 (page 0 contains the NDX header).  The pages are
  2595.                organized into  a B+  Tree structure  which allows  them to  be
  2596.                quickly searched for any particular key.
  2597.  
  2598.           Index Keys
  2599.  
  2600.                Within  each page are 1 or more index keys.  Each key has three
  2601.                components:
  2602.                     Position    Type         Description
  2603.                         0     LONGCARD       Page pointer (0 for leaf pages)
  2604.                         4     LONGCARD       Record pointer
  2605.                         8     KeyLen Bytes   Actual Key field
  2606.  
  2607.                The  page pointer is  used in the  node pages  to point  to the
  2608.                page  containing keys  lower than  that  contained in  the  key
  2609.                field.   The record pointer contains  the number of the  record
  2610.                that supplied the key in the key field.
  2611.  
  2612.           General
  2613.  
  2614.                Ashton Tate  chose to use a  traditional B+ tree structure  for
  2615.                their NDX  files.  Accordingly, the  NDX module does the  same.
  2616.                For  a  more  efficient  index   structure,  see  the  appendix
  2617.                pertaining to the IDX module.   It is beyond  the scope of this
  2618.                article to explain B+  tree structure and usage.  However,  for
  2619.                more information you  should refer  to Donald Knuth's 'The  Art
  2620.                of Computer  Programming' or Namir  Shammas' 'The  Turbo Pascal
  2621.                Toolbook.'
  2622.  
  2623.           Limitations
  2624.  
  2625.                dBase NDX files allow the user to  create index files based  on
  2626.                complex expressions  involving the  data  file  fields and  the
  2627.                dBase  programming language.  The NDX module does not currently
  2628.                support this feature.  For the time being, NDX key  expressions
  2629.                must consist of a single field name from a data file.
  2630.                The unique key field in the NDX header is also ignored.
  2631.  
  2632.  
  2633.  
  2634.                                   Appendix B - Page 38
  2635.  
  2636.  
  2637.  
  2638.  
  2639.  
  2640.  
  2641.  
  2642.  
  2643.  
  2644.  
  2645.  
  2646.  
  2647.                                  Multi-user Programming
  2648.  
  2649.           Overview          ________
  2650.  
  2651.           Most applications  for personal computers  operate in  a single-user
  2652.           environment (i.e.  only one  user at  a time  per computer  system).
  2653.           However,   databases,   particularly   for   business   applications
  2654.           frequently  require several  users to  be  simultaneously performing
  2655.           updates and  additions.   For example,  a large  firm might  require
  2656.           several people accessing and updating  inventory information at  the
  2657.           same time.  Under these circumstances certain problems can develop.
  2658.  
  2659.           The  problems  usually  occur  when  two  users  simultaneously make
  2660.           changes  to the same  record.  In this case,  one users changes will
  2661.           be lost.  To help  prevent this, functions such  as file and  record
  2662.           locking are provided by DOS.  When  a file is locked, only  one user
  2663.           can access it.   DOS will not allow a  second user to open the  file
  2664.           until the first user has closed it.
  2665.  
  2666.           A  more elegant  mechanism in  DOS allows users  to lock  a selected
  2667.           area within a file such as a data record.   Only one user at  a time
  2668.           can  lock each  area.   This  feature allows  programs to  lock only
  2669.           small areas of  a file while the  rest remains available  to others.
  2670.           Clearly this is a better solution.  
  2671.  
  2672.           Record locking  is used extensively by the DBF, NDX, and IDX modules
  2673.           to prevent users from damaging data and index files.   In fact, most
  2674.           aspects of  multi-user databasing are  handled automatically  by the
  2675.           modules.    The locking  feature is  also available  to applications
  2676.           programs  via  the  LockRec  and  UnLockRec  procedures in  the  DBF
  2677.           module.
  2678.  
  2679.           If  your programs are run  in a multi-user  environment, you must be
  2680.           careful to  prevent multi-user  conflicts.   These can easily  occur
  2681.           when more than one  user is working with  the same data  file.   For
  2682.           example:  
  2683.  
  2684.           Two  sales people at  Dataworks Widget Factory are receiving orders.
  2685.           Sales Person A finds and loads  the widget inventory record into his
  2686.           record  buffer and sees  that 17,000  widgets are  in stock.   Sales
  2687.           Person  B loads the  same record into hers and  sees the same thing.
  2688.           Sales  Person  A then  sells  7,000 widgets  to  his customer.    He
  2689.           updates the  inventory and  saves the  new updated inventory  record
  2690.           showing only 10,000 widgets.   Sales Person B, not  realizing that A
  2691.           has reduced the available inventory believes  that 17,000 are  still
  2692.           available.  She sells 12,000 widget to her customer  and reduces the
  2693.           number of widgets in  her record buffer accordingly.  She saves  her
  2694.           changes, overwriting the changes saved by A.
  2695.  
  2696.  
  2697.  
  2698.  
  2699.  
  2700.                                   Appendix C - Page 39
  2701.  
  2702.  
  2703.  
  2704.  
  2705.  
  2706.  
  2707.  
  2708.  
  2709.  
  2710.  
  2711.  
  2712.  
  2713.           Such  a situation  would be a  disaster.   At the  end of  the above
  2714.           sequence,  the   widget  record  would  incorrectly  indicate  5,000
  2715.           widgets  still  available,  and one  of  the  orders  could  not  be
  2716.           shipped.   To avoid such a  nightmare, the application program  must
  2717.           control  access to the  widget record;  allowing only  one user at a
  2718.           time to make changes.   This is done via  the LockRec and  UnLockRec
  2719.           procedures.   Returning to the above example this  is the way things
  2720.           should have been handled:
  2721.  
  2722.                     1. A Finds Widget Record 
  2723.                     2. A Locks Record
  2724.                     3. A Reads Record
  2725.                     4. B Finds Widget Record 
  2726.                     5. B tries to lock record but can't since A locked it
  2727.                     6. B keeps trying 
  2728.                     7. A makes changes to record
  2729.                     8. A writes record to file
  2730.                     9. A unlocks record
  2731.                     10. Now B locks record
  2732.                     11. B Reads record
  2733.                          etc.
  2734.  
  2735.           In  the  above  sequence, the  application  program  locks  a record
  2736.           before it  reads it.   If  the record  cannot  be  locked, it  means
  2737.           another user  has already locked  it.  In  such a circumstance,  the
  2738.           program  should ask  the user  if he  or she  wishes to  wait or try
  2739.           again.  The DBF  module will automatically retry  the lock a certain
  2740.           number of times before indicating that  the lock failed.  The number
  2741.           of times it will retry is controlled by the variable: Retries.
  2742.  
  2743.           Locking  records  can  create  some  problems  of  its  own  if  the
  2744.           programmer is not careful.  A particularly nasty situation known  as
  2745.           deadlock  can occur  when locking  is  incorrectly used.    Deadlock
  2746.           results when  two users need  to access  the same two records.   For
  2747.           example, two users need to access and modify records 1 and 5:
  2748.  
  2749.                     1.   User A locks record 1
  2750.                     2.   User B locks record 5
  2751.                     3.   User A tries  to lock 5  but can't  because B has  it
  2752.                          locked, so A must wait.
  2753.                     4.   User B  tries to lock  1 but can't  because A has  it
  2754.                          locked, so B must wait.
  2755.                     5.   Endless loop.
  2756.  
  2757.           A good way to  avoid deadlock is to always  lock records in  a fixed
  2758.           order so that  both users would first try  to lock 1.  Only one user
  2759.           would succeed in locking 1 and  the other would have to wait, but no
  2760.           deadlock would occur.
  2761.  
  2762.           A  great  deal  has  been  written  about  concurrency  in  database
  2763.           operation.   If  you will  be  writing multi-user  applications,  it
  2764.           would be wise to purchase and read a good book covering this topic.
  2765.  
  2766.                                   Appendix C - Page 40
  2767.  
  2768.  
  2769.  
  2770.  
  2771.  
  2772.  
  2773.  
  2774.  
  2775.  
  2776.  
  2777.  
  2778.  
  2779.  
  2780.           Technical information          _____________________
  2781.  
  2782.           All  locking  in the  DBF,  NDX,  and IDX  modules  is done  via the
  2783.           standard  DOS  record  and  file  locking  functions  (Interrupt 21h
  2784.           Functions 3Dh  and 5Ch).   To  use  the multi-user  features of  the
  2785.           modules,  you must be running  DOS 3.0 or  higher, and SHARE must be
  2786.           loaded.   Most networks  and  multi-user  operating systems  support
  2787.           these functions  allowing your applications to  run on a wide  range
  2788.           of systems.
  2789.  
  2790.           If your  programs may run  in a multi-user  environment, you  should
  2791.           set  the variable 'MultiUser' from  each module to TRUE.   This will
  2792.           enable automatic locking for  data and index files and will  prevent
  2793.           files from becoming corrupted.
  2794.  
  2795.           It  should  be  noted  that   multi-user  support  adds  substantial
  2796.           overhead  to most database  operations when  enabled.   Therefore if
  2797.           your program  will be  run only  in a  single-user environment,  you
  2798.           should   keep  the  variable  'MultiUser'  set  to  FALSE.    Future
  2799.           enhancements to this  package will increase  the speed of multi-user
  2800.           access and will add automatic detection of multi-user access.
  2801.  
  2802.           Two variables in each  module control the functioning of the  multi-
  2803.           user mode.    The boolean  variable MultiUser  enables and  disables
  2804.           multi-user access to data files.  The variable Retries controls  the
  2805.           number  of times  a lock  will be  attempted before  it fails.   The
  2806.           default  value  for MultiUser  is  FALSE.   The  default  value  for
  2807.           Retries is 500.
  2808.  
  2809.           Two procedures LockRec and UnLockRec from the DBF module allow  your
  2810.           applications  to explicitly  control  the locking  of  records.   If
  2811.           several  users will be accessing  the same data  file, it is prudent
  2812.           to use these procedures to prevent conflicts (see overview above).
  2813.  
  2814.           It  is important  to  understand that  in  some systems,  locking  a
  2815.           record  may not  prevent  other users  from  reading it,  only  from                                                       _______
  2816.           locking it.  Therefore it  may be possible to read a record that  is
  2817.           locked by another user, but  not to lock it until the first user has
  2818.           unlocked it. 
  2819.  
  2820.  
  2821.  
  2822.  
  2823.  
  2824.  
  2825.  
  2826.  
  2827.  
  2828.  
  2829.  
  2830.  
  2831.  
  2832.                                   Appendix C - Page 41
  2833.  
  2834.  
  2835.  
  2836.  
  2837.  
  2838.  
  2839.