home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / pascal / library / dos / database / gsdb28 / gs_dbase.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1992-02-28  |  51.4 KB  |  1,126 lines

  1. {-----------------------------------------------------------------------------
  2.                            dBase III File Handler
  3.  
  4.        GS_DBASE Copyright (c)  Richard F. Griffin
  5.  
  6.        15 November 1990
  7.  
  8.        102 Molded Stone Pl
  9.        Warner Robins, GA  31088
  10.  
  11.        -------------------------------------------------------------
  12.        This unit handles the objects for all dBase III file (.DBF)
  13.        operations.
  14.  
  15.                    SHAREWARE  -- COMMERCIAL USE RESTRICTED
  16.  
  17.  
  18.  
  19.        Changes:
  20.  
  21.        16 Nov 90 - Moved Pack method to GS_dBFld.
  22.  
  23.        02 May 91 - Added an IndexSignature constant to the index units so the
  24.                    GS_dBase unit can confirm the index unit in use.  The flag
  25.                    IsDB3NDX is true if the dBase III index unit is used.  This
  26.                    is needed to properly convert date fields for an index.  The
  27.                    dBase III index requires a julian date instead of the
  28.                    character field stored in the record.  Most other indexes
  29.                    use the field as stored (YYYYMMDD).
  30.  
  31.        03 May 91 - Added routine to convert a date field to julian date when
  32.                    used as an index field in PutRec.
  33.  
  34.        06 Jun 91 - Fixed error in Open that caused the status not to be set
  35.                    to 'NotUpdated'.  Comment close bracket was missing, and
  36.                    caused the next instruction to be ignorred.
  37.  
  38.                    Added a UnInit method to release buffer memory from the
  39.                    Heap when the file is no longer needed.  If the file is
  40.                    to be used again, it must be reinitialized by calling
  41.                    the Init method.  This allows several files to use the
  42.                    same object, one after the other.
  43.  
  44.        08 Jan 92 - Changed UnInit to a destructor to make creating and
  45.                    disposing of dynamic objects possible.
  46.  
  47.                    Added GSP_dBase_DB as a pointer of type GS_dBase_DB for
  48.                    simpler creation of dynamic objects.
  49.  
  50.        02 Feb 92 - Allows multiple indexes to be updated through PutRec.
  51.                    This will slow the PutRec function as a penalty.
  52.  
  53.        18 Feb 92 - Added File_TOF flag to test for attempt to read beyond
  54.                    the top of the file.  Use like File_EOF test.
  55.  
  56.        27 Feb 92 - Fix made to allow easy change of the master index.
  57.                    Modified dbfNdxActv from boolean value to GS_Indx_LPtr
  58.                    index object pointer.  This will hold the master index
  59.                    to be used for GetRec and Find.  Will default to the
  60.                    first file in an Index command.  May be set to other
  61.                    index files by calling the SetIndexMaster procedure
  62.                    with the order number of the index as the argument.
  63.  
  64.        28 Feb 92 - Added FieldName method to return the name of the field
  65.                    in the record at the ordered position.  For example,
  66.                    FieldName(2) would return the name of the second field
  67.                    in the record.
  68.  
  69. ------------------------------------------------------------------------------}
  70. {
  71.                            ┌──────────────────────┐
  72.                            │  INTERFACE SECTION:  │
  73.                            └──────────────────────┘
  74. }
  75.  
  76. unit GS_DBASE;
  77.  
  78. interface
  79. {$D-}
  80.  
  81. uses
  82.      CRT,
  83.      DOS,
  84.      GS_KeyI,
  85.      GS_Date,
  86.      GS_FileH,    {File handler}
  87.      GS_Strng,    {String handling Routines}
  88.      GS_Error,    {Error Handling routines}
  89.      GS_DBNdx;    {Unit for index operations (.NDX files)}
  90.  
  91. const
  92.    GS_dBase_MaxRecBytes = 4000;      {dBASE III record limit }
  93.    GS_dBase_MaxRecField = 128;       {dBASE III field limit}
  94.    GS_dBase_MaxMemoRec  = 512;       {Size of each block of memo file data}
  95.  
  96.    Next_Record = -1;   {Token value passed to read next record}
  97.    Prev_Record = -2;   {Token value passed to read previous record}
  98.    Top_Record  = -3;   {Token value passed to read first record}
  99.    Bttm_Record = -4;   {Token value passed to read final record}
  100.  
  101.    GS_dBase_UnDltChr = 32;   {Character for Undeleted Record}
  102.    GS_dBase_DltChr   = 42;   {Character for Deleted Record}
  103.  
  104. type
  105.  
  106.    GS_dBase_Status = (NotOpen, NotUpdated, Updated);
  107.            {Flags to indicate status of dBase III file }
  108.  
  109.    GS_dBase_dRec = ^GS_dBase_DataRecord;
  110.            {Pointer type used in object descriptions to locate the memory}
  111.            {array in bytes for the dBase record.  Uses GS_dBase_DataRecord}
  112.            {defined below.}
  113.  
  114.    GS_dBase_DataRecord = ARRAY[0..GS_dBase_MaxRecBytes] OF Byte;
  115.            {Defines an array of bytes in memory that is as large as the }
  116.            {maximum size of a dBase record (GS_dBase_MaxRecBytes).}
  117.  
  118. {
  119.         ┌──────────────────────────────────────────────────────────────────┐
  120.         │  ********         Data Structure Description         **********  │
  121.         │                                                                  │
  122.         │  The following record defines the dBase III file header.  Refer  │
  123.         │  to Appendix A for an explanation of each data element.          │
  124.         └──────────────────────────────────────────────────────────────────┘
  125. }
  126.    GS_dBase_Head = Record
  127.                       DBType     : Byte;
  128.                       Year       : Byte;
  129.                       Month      : Byte;
  130.                       Day        : Byte;
  131.                       RecCount   : LongInt;
  132.                       Location   : Integer;
  133.                       RecordLen  : Integer;
  134.                       Reserved   : Array[1..20] of Byte;
  135.                    end;
  136.  
  137. {
  138.      ┌──────────────────────────────────────────────────────────────────┐
  139.      │  *********             Field Descriptor              *********   │
  140.      │                                                                  │
  141.      │  This record defines the field descriptor.  There is one of      │
  142.      │  these for each field defined in the database structure.  They   │
  143.      │  are stacked as 32 bytes following the file header record, as    │
  144.      │  described in Appendix A.                                        │
  145.      └──────────────────────────────────────────────────────────────────┘
  146. }
  147.  
  148.    GS_dBase_Field = Record
  149.                        FieldName    : String[10];
  150.                                       {Array[1..11] of Char actually}
  151.                                       {This is to simplify conversion}
  152.                        FieldType    : Char;
  153.                        FieldAddress : LongInt;
  154.                        FieldLen     : Byte;
  155.                        FieldDec     : Byte;
  156.                        Reserved     : Array[1..14] of Char;
  157.                     end;
  158.  
  159.    GS_dBase_dFld = ^GS_dBase_DataField;
  160.           {Pointer type used in object descriptions to assign memory}
  161.           {for storing the field descriptors.                          }
  162.  
  163.    GS_dBase_DataField = ARRAY[1..GS_dBase_MaxRecField] OF GS_dBase_Field;
  164.           {Defines an array of field descriptors (GS_dBase_Field) that}
  165.           {is as large as the maximum number of dBase fields allowed}
  166.           {(GS_dBase_MaxRecFields).}
  167.  
  168.    GS_dBase_nFld = ^GS_dBase_NameField;
  169.           {Pointer type used in object descriptions to assign memory}
  170.           {for storing the field name strings.                      }
  171.  
  172.    GS_dBase_NameField = Array[1..GS_dBase_MaxRecField] OF string[11];
  173.           {Defines an array of field name strings (GS_dBase_Field) that}
  174.           {is as large as the maximum number of dBase fields allowed}
  175.           {(GS_dBase_MaxRecFields).}
  176.  
  177.  
  178. {
  179.        ┌──────────────────────────────────────────────────────────────┐
  180.        │  ***********      dBase Object Definition      ************  │
  181.        └──────────────────────────────────────────────────────────────┘
  182. }
  183.  
  184.    GSP_dBase_DB = ^GS_dBase_DB;
  185.    GS_dBase_DB = object(GS_KeyI_Objt) {Make it a child for keyboard control}
  186.       FileName     : string[64];      {Stores FileName of dBase File}
  187.       dFile        : file;            {File Type to reference data file}
  188.       mFile        : file;            {File Type to reference memo file}
  189.       HeadProlog   : GS_dBase_Head;   {Image of file header}
  190.       dStatus      : GS_dBase_Status; {Holds Status Code of file}
  191.       WithMemo     : Boolean;         {True if memo file present}
  192.       DateOfUpdate : string[8];       {MM/DD/YY of last update}
  193.       NumRecs      : LongInt;         {Number of records in file}
  194.       HeadLen      : Integer;         {Header + Field Descriptor length}
  195.       RecLen       : Integer;         {Length of record}
  196.       NumFields    : Integer;         {Number of fields in the record}
  197.       Fields       : GS_dBase_dFld;   {Pointer to memory array holding}
  198.                                       {field descriptors}
  199.       FieldsN      : GS_dBase_nFld;   {Pointer to memory array holding}
  200.                                       {Field name strings}
  201.       RecNumber    : LongInt;         {Physical record number last read}
  202.       CurRecord    : GS_dBase_dRec;   {Pointer to memory array holding}
  203.                                       {the current record data.  Refer}
  204.                                       {to Appendix B for record structure}
  205.       DelFlag      : boolean;         {True if record deleted}
  206.       File_TOF     : boolean;
  207.       File_EOF     : boolean;         {True if at end of file }
  208.       Found        : boolean;         {Set True on valid record Find}
  209.       dbfNdxTbl    : array [1..16] of GS_Indx_LPtr;
  210.                                       {Holds addresses of up to 16 Index}
  211.                                       {Objects.  The first array is the}
  212.                                       {Master Index.  For File changes,}
  213.                                       {this array will be used to ensure}
  214.                                       {all indexes are updated. }
  215.       dbfNdxActv   : GS_Indx_LPtr;    {Holds master index object pointer}
  216.  
  217. {
  218.    ┌───────────────────────────────────────────────────────────────────────┐
  219.    │  ***  These methods are described individually in the following  ***  │
  220.    │        pages.  As seen here, their name describes their function      │
  221.    └───────────────────────────────────────────────────────────────────────┘
  222. }
  223.  
  224.       PROCEDURE Append;
  225.       PROCEDURE Blank;
  226.       PROCEDURE Close;
  227.       FUNCTION  Create(FName : string) : boolean;
  228.       PROCEDURE Delete;
  229.       FUNCTION  Find(st : string) : boolean;
  230.       FUNCTION  FieldName(i : integer) : string;
  231.       FUNCTION  Formula(st : string; var ftyp : char) : string; virtual;
  232.       PROCEDURE GetRec(RecNum: LongInt);
  233.       PROCEDURE Index(IName : String);
  234.       PROCEDURE Index_List(RecAct: LongInt; var I_List; var RNum : longint);
  235.       CONSTRUCTOR Init(FName : string);
  236.       PROCEDURE Open;
  237.       PROCEDURE PutRec(RecNum : LongInt);
  238.       PROCEDURE SetIndexMaster(ix : integer);
  239.       PROCEDURE UnDelete;
  240.       Destructor UnInit;
  241.    end;
  242.  
  243. var
  244.    IsDB3NDX : boolean;
  245. {
  246.                          ┌──────────────────────────┐
  247.                          │  IMPLEMENTATION SECTION  │
  248.                          └──────────────────────────┘
  249. }
  250.  
  251. implementation
  252. uses
  253.    GS_dB3Wk;                          {Use shown here to avoid circular def.}
  254.  
  255.  
  256. CONST
  257.   DB3File = 3;                        {First byte of dBase III(+) file}
  258.   DB3WithMemo = $83;                  {First byte of dBase III(+) file}
  259.                                       {if memo file (.DBT) is present }
  260.  
  261.  
  262. PROCEDURE GS_dBase_DB.Append;
  263. BEGIN
  264.    PutRec(0);
  265.         {Calls objectname.PutRec method with a record number of}
  266.         {zero.  This causes the record number to default to }
  267.         {objectname.NumRecs + 1.                              }
  268. END;
  269.  
  270.  
  271. PROCEDURE GS_dBase_DB.Blank;
  272. begin
  273.    FillChar(CurRecord^[0], RecLen, ' ');
  274.                                       {Fill spaces for RecLen bytes}
  275. end;
  276.  
  277.  
  278. PROCEDURE GS_dBase_DB.Close;
  279. CONST
  280.    EofMark : Byte = $1A;              {ASCII code for EOF byte}
  281. var
  282.    rsl,
  283.    yy, mm, dd, wd : word;             {Local variables to get today's}
  284.                                       {date through TP's GetDate procedure}
  285.    i              : integer;          {work variable}
  286. {
  287.        ┌──────────────────────────────────────────────────────────────┐
  288.        │   The Update_File procedure is called if any records are     │
  289.        │   added/updated while the file is open.  This is indicated   │
  290.        │   by objectname.dStatus set to 'UpDated'.  The procedure     │
  291.        │   inserts the current date in the file header, updates the   │
  292.        │   record count, rewrites the file header, and writes an EOF  │
  293.        │   byte at the end of the file.                               │
  294.        └──────────────────────────────────────────────────────────────┘
  295. }
  296.    procedure UpDate_File;
  297.    BEGIN
  298.       GetDate (yy,mm,dd,wd);          {Call TP's GetDate procedure}
  299.       HeadProlog.year := yy-1900;     {Extract the Year}
  300.       HeadProlog.month := mm;         {Extract the Month}
  301.       HeadProlog.day := dd;           {Extract the Day}
  302.       HeadProlog.RecCount := NumRecs; {Update number records in file}
  303.       GS_FileWrite(dFile, 0, HeadProlog, 8, rsl);
  304.       GS_FileWrite(dFile, HeadLen+NumRecs*RecLen, EofMark, 1, rsl); {EOF marker}
  305.    END;   { IF Updated }
  306.  
  307. {
  308.          ┌───────────────────────────────────────────────────────────┐
  309.          │  Beginning of CLOSE Procedure.                            │
  310.          │      1.  Exit if file not open                            │
  311.          │      2.  Update the file header if records added/updated  │
  312.          │      3.  Close the file                                   │
  313.          │      4.  Close the .DBT memo file if applicable           │
  314.          │      5.  Set objectname.dStatus to 'NotOpen'              │
  315.          └───────────────────────────────────────────────────────────┘
  316. }
  317.  
  318. begin
  319.    IF dStatus = NotOpen THEN exit;    {Exit if file not open}
  320.    IF dStatus = Updated THEN UpDate_File;
  321.                                       {Write new header information if the}
  322.                                       {file was updated in any way}
  323.    GS_FileClose(dFile);
  324.    if WithMemo then GS_FileClose(mFile);
  325. {
  326.          ┌──────────────────────────────────────────────────────────┐
  327.          │  The following routine releases index files associated   │
  328.          │  with the .DBF file and releases memory.                 │
  329.          └──────────────────────────────────────────────────────────┘
  330. }
  331.    i := 1;                         {initialize counter}
  332.    while dbfNdxTbl[i] <> nil do
  333.    begin
  334.       dispose(dbfNdxTbl[i], Done);       {Release Heap Memory}
  335.       dbfNdxTbl[i] := nil;         {set pointer to 'empty'}
  336.       inc(i);                      {increment counter}
  337.    end;
  338.    dbfNdxActv := nil;
  339.    dStatus := NotOpen;             {Set objectname.dStatus to 'NotOpen'}
  340. END;                        { GS_dBase_Close }
  341.  
  342.  
  343. Function GS_dBase_DB.Create(FName : string) : boolean;
  344. begin
  345.    if GS_dB3_Create(FName) then Create := true else Create := false;
  346. END;                        { GS_dBase_Create }
  347.  
  348.  
  349. PROCEDURE GS_dBase_DB.Delete;
  350. begin
  351.    DelFlag := true;                   {Set Delete Flag to true}
  352.    CurRecord^[0] := GS_dBase_DltChr;  {Put '*' in first byte of current record}
  353.    PutRec(RecNumber);                 {Write the current record to disk }
  354. end;                 {GS_dBase_Delete}
  355.  
  356.  
  357. Function GS_dBase_DB.FieldName(i : integer) : string;
  358. begin
  359.    if (i > 0) and (i <= NumFields) then
  360.       FieldName := FieldsN^[i]
  361.    else FieldName := '';
  362. end;
  363.  
  364.  
  365. {
  366.                                    FIND
  367.  
  368.  
  369.      ╔══════════════════════════════════════════════════════════════════╗
  370.      ║                                                                  ║
  371.      ║   The FIND method will search the master index file for the      ║
  372.      ║   key string contained in the calling argument.                  ║
  373.      ║                                                                  ║
  374.      ║   Note:  At this time, numeric fields must have a string value   ║
  375.      ║          argument.                                               ║
  376.      ║                                                                  ║
  377.      ║       Calling the Method:                                        ║
  378.      ║                                                                  ║
  379.      ║           objectname.Find(String)                                ║
  380.      ║                                                                  ║
  381.      ║               ( where objectname is of type GS_dBase_DB,         ║
  382.      ║                       String is key value to match)              ║
  383.      ║                                                                  ║
  384.      ║       Result:                                                    ║
  385.      ║                                                                  ║
  386.      ║           Matching record is read if found.  No error check,     ║
  387.      ║           but index object Found flag is set true on match.      ║
  388.      ║                                                                  ║
  389.      ╚══════════════════════════════════════════════════════════════════╝
  390. }
  391.  
  392. Function GS_dBase_DB.Find(st : string) : boolean;
  393. var
  394.    RNum   : longint;
  395. begin
  396. {
  397.          ┌───────────────────────────────────────────────────────────┐
  398.          │  The next statement checks to see if an index is active   │
  399.          │  (dbfNdxActv = true), and calls the index object's        │
  400.          │  KeyFind method if true.  The key string is passed to     │
  401.          │  the method as the only argument.  The matching record    │
  402.          │  is returned from the method.  If there is no match,      │
  403.          │  the method returns a zero value.  Note that the method   │
  404.          │  is called using the first index object pointer in array  │
  405.          │  dbfNdxTabl (the master index).  The ability to use an    │
  406.          │  object pointer in place of an actual object is a highly  │
  407.          │  useful tool.                                             │
  408.          └───────────────────────────────────────────────────────────┘
  409. }
  410.    if (dbfNdxActv <> nil) then
  411.    begin
  412.       RNum := dbfNdxActv^.KeyFind(st);
  413.       if RNum > 0 then                {RNum = 0 if no match, otherwise}
  414.                                       {it holds the valid record number}
  415.       begin
  416.          GetRec(RNum);                {If match found, read the record}
  417.          Found := True;               {Set Match Found flag true}
  418.       end else
  419.       begin                           {If no matching index key, then}
  420.          Found := False;              {Set Match Found Flag False}
  421.       end;
  422.    end else                           {If there is no index file, then}
  423.       Found := False;                 {Set Match Found Flag False}
  424.    Find := Found;
  425. end;                  {GS_dBase_Find}
  426.  
  427.  
  428. function GS_dBase_DB.Formula(st : string; var ftyp : char) : string;
  429. begin
  430.    ShowError(399,'Object for field handling missing');
  431.    Formula := '';
  432. end;
  433.  
  434.  
  435. {
  436.                                    GETREC
  437.  
  438.  
  439.    ╔═══════════════════════════════════════════════════════════════════════╗
  440.    ║                                                                       ║
  441.    ║  The GETREC method will access the dBase III file to retrieve the     ║
  442.    ║  record number passed in the call.                                    ║
  443.    ║                                                                       ║
  444.    ║      Calling the Method:                                              ║
  445.    ║                                                                       ║
  446.    ║            objectname.GetRec (RecNum)                                 ║
  447.    ║                                                                       ║
  448.    ║                   ( where objectname is of type GS_dBase_DB,          ║
  449.    ║                           RecNum is the record number to retrieve.    ║
  450.    ║                           **  If a number greater than 0, record      ║
  451.    ║                               will be physical number from .DBF;      ║
  452.    ║                               if Next_Record, Prev_Record,            ║
  453.    ║                               Top_Record, or Bttm_Record, then        ║
  454.    ║                               the appropriate record will be found.   ║
  455.    ║                               For these codes, if an index is in      ║
  456.    ║                               use, the record will be retrieved       ║
  457.    ║                               based on it's location in the index.)   ║
  458.    ║                                                                       ║
  459.    ║       Result:                                                         ║
  460.    ║                                                                       ║
  461.    ║            1.  Record is retrieved based on record number argument    ║
  462.    ║            2.  Objectname.RecNumber set to record number read         ║
  463.    ║            3.  Objectname.DelFlag set true if deleted record          ║
  464.    ║            4.  If last record of file (.DBF or .NDX), then            ║
  465.    ║                objectname.File_EOF set true.                          ║
  466.    ║                                                                       ║
  467.    ╚═══════════════════════════════════════════════════════════════════════╝
  468. }
  469.  
  470.  
  471. PROCEDURE GS_dBase_DB.GetRec(RecNum : LongInt);
  472. VAR
  473.    dFilea : FileRec absolute dFile;
  474.    i,
  475.    Result : Integer;                  {Local working variable}
  476.    RNum   : LongInt;                  {Local working variable  }
  477.    StrFil : String[80];
  478.    rsl    : word;
  479. BEGIN
  480.    if NumRecs = 0 then
  481.    begin
  482.       File_TOF := true;
  483.       File_EOF := true;
  484.       exit;
  485.    end;
  486.    RNum := RecNum;                    {Store RecNum locally for modification}
  487.    File_TOF := true;
  488.    File_EOF := false;                 {Initialize End of File Flag to false}
  489.  
  490. {
  491.          ┌───────────────────────────────────────────────────────────┐
  492.          │  The next statement checks to see if an index is active   │
  493.          │  (dbfNdxActv = true), and calls the index object's        │
  494.          │  KeyRead method if true and the record requested is       │
  495.          │  a relative record (less than 0).  Note that the method   │
  496.          │  is called using the first index object pointer in array  │
  497.          │  dbfNdxTabl (the master index).  The ability to use an    │
  498.          │  object pointer in place of an actual object is a highly  │
  499.          │  useful tool.  Upon return, the index file's EOF flag is  │
  500.          │  stored as the .DBF's End-of-File Flag.                   │
  501.          └───────────────────────────────────────────────────────────┘
  502. }
  503.    if (dbfNdxActv <> nil) and (RecNum < 0) then
  504.    begin
  505.       RNum := dbfNdxActv^.KeyRead(RecNum);
  506.                                       {Get record number of physical}
  507.                                       {record to read from .DBF.}
  508.       File_EOF :=dbfNdxActv^.KeyEOF;
  509.       File_TOF :=dbfNdxActv^.KeyBOF;
  510.                                       {Get index EOF flag.  The EOF will be}
  511.                                       {set when a KeyRead of Next_Record}
  512.                                       {will go past the last index record}
  513.    end
  514.    else
  515.       if (dbfNdxActv<> nil) and (RNum > 0) and  (RNum <= NumRecs) then
  516.          if not dbfNdxActv^.KeyLocRec(RecNum) then exit;
  517.                                       {If physical record search, set index}
  518.                                       {to the same record.}
  519.    if File_EOF then exit;             {Return if EOF reached}
  520. {
  521.          ┌──────────────────────────────────────────────────────────┐
  522.          │  The value in RNum is tested to see if it is a relative  │
  523.          │  record seek or a physical record number.  The number    │
  524.          │  is also tested to ensure it is in the file record       │
  525.          │  range of valid numbers.  Note, if an index was read,    │
  526.          │  RNum will now be a physical record.                     │
  527.          └──────────────────────────────────────────────────────────┘
  528. }
  529.    case RNum of
  530.       Next_Record : begin
  531.                        RNum := RecNumber + 1;
  532.                                       {Get next sequential record}
  533.                        if RNum > NumRecs then
  534.                        begin          {If beyond number of records in file,}
  535.                                       {you must recover}
  536.                           RNum := NumRecs;
  537.                                       {Reset to final record}
  538.                           File_EOF := true;
  539.                                       {Set EOF Flag to True}
  540.                           exit;       {Return from GetRec}
  541.                        end;
  542.                     end;
  543.       Prev_Record : begin
  544.                        RNum := RecNumber - 1;
  545.                                       {Get Previous Record}
  546.                        if RNum < 1 then
  547.                        begin
  548.                           RNum := 1;  {If at beginning of file, stay}
  549.                           File_TOF := true;
  550.                           exit;
  551.                        end;
  552.                     end;
  553.       Top_Record  : RNum := 1;        {Set to the first record}
  554.       Bttm_Record : RNum := NumRecs;  {Set to the last record}
  555.    end;
  556.    if (RNum < 1) or (RNum > NumRecs) then
  557.    begin                              {if a physical record number is out}
  558.                                       {of range, exit with error}
  559.       i := 0;
  560.       Str(RNum, StrFil);
  561.       StrFil := 'Record ' + StrFil;
  562.       StrFil := StrFil + ' Out of Range for File ';
  563.       while dFilea.Name[i] <> #0 do
  564.       begin
  565.          StrFil := StrFil + dFilea.Name[i];
  566.          inc(i);
  567.       end;
  568.       ShowError(100,StrFil);
  569.       exit;                           {Terminate read attempt if record number}
  570.                                       {is out of range}
  571.    end;
  572.    GS_FileRead(dFile, HeadLen+(RNum-1) * RecLen, CurRecord^, RecLen, rsl);
  573.                                       {Read RecLen bytes into memory buffer}
  574.                                       {for the correct physical record}
  575.    RecNumber := RNum;                 {Set objectname.RecNumber = this record }
  576.    if CurRecord^[0] = GS_dBase_DltChr then DelFlag := true
  577.          else DelFlag := false;       {Set objectname.DelFlag to show status}
  578.                                       {of the record's Delete byte}
  579. END;                  {GetRec}
  580.  
  581.  
  582. {
  583.                                    INDEX
  584.  
  585.  
  586.      ╔══════════════════════════════════════════════════════════════════╗
  587.      ║                                                                  ║
  588.      ║   The INDEX method initializes the index array in objectname     ║
  589.      ║   and assigns the first index as the master index.  The other    ║
  590.      ║   index files will be updated upon .DBF updates (when the        ║
  591.      ║   index write entries are added).                                ║
  592.      ║                                                                  ║
  593.      ║       Calling the Method:                                        ║
  594.      ║                                                                  ║
  595.      ║           objectname.Index(String)                               ║
  596.      ║                                                                  ║
  597.      ║               ( where objectname is of type GS_dBase_DB,         ║
  598.      ║                       String is list of index files, separated   ║
  599.      ║                       by spaces.                                 ║
  600.      ║                                                                  ║
  601.      ║       Result:                                                    ║
  602.      ║                                                                  ║
  603.      ║           Index files are assigned and the master index is       ║
  604.      ║           opened.                                                ║
  605.      ║                                                                  ║
  606.      ╚══════════════════════════════════════════════════════════════════╝
  607. }
  608.  
  609.  
  610. Procedure GS_dBase_DB.Index (IName : String);
  611. var
  612.    i,j : integer;                     {Local working variable  }
  613.    st  : String[64];                  {Local working variable}
  614. begin
  615. {
  616.              ┌───────────────────────────────────────────────────┐
  617.              │  Reset index file array.                          │
  618.              │     1.  Close open index files                    │
  619.              │     2.  Release index objects stored on the heap  │
  620.              │     3.  Set array pointers to nil.                │
  621.              └───────────────────────────────────────────────────┘
  622. }
  623.    i := 1;
  624.    while dbfNdxTbl[i] <> nil do
  625.    begin
  626.       dispose(dbfNdxTbl[i], Done);
  627.       dbfNdxTbl[i] := nil;
  628.       inc(i);
  629.    end;
  630.    dbfNdxActv := nil;
  631. {
  632.            ┌──────────────────────────────────────────────────────┐
  633.            │  This routine scans the input string for the names   │
  634.            │  of index files.  Names must be separated by commas  │
  635.            │  or spaces.  The .NDX extension must not be part     │
  636.            │  of the file name                                    │
  637.            └──────────────────────────────────────────────────────┘
  638. }
  639.    i := 0;                            {i will hold count of index files}
  640.    for j := 1 to length(IName) do if IName[j] = ',' then IName[j] := ' ';
  641.    j := 1;
  642.    st := '';
  643.    while j <= length(IName) do
  644.    begin
  645. {
  646.                ┌───────────────────────────────────────────────┐
  647.                │  Build an index file name in st until end of  │
  648.                │  input string, a comma, or a space is found   │
  649.                └───────────────────────────────────────────────┘
  650. }
  651.       if IName[j] <> ' ' then
  652.          st := st + IName[j]
  653.       else
  654.       begin                           {When file string is complete:}
  655.          st := TrimL(st);
  656.          if st <> '' then             {If not an empty string:}
  657.          begin
  658.             inc(i);                      {Increment index file count}
  659.             New(dbfNdxTbl[i], Init(st));
  660.             st := '';                    {Reset file name to empty for next}
  661.          end;
  662.       end;
  663.       inc(j);                         {Inc counter for next input string char }
  664.    end;
  665. {
  666.               ┌─────────────────────────────────────────────────┐
  667.               │  This routine is needed to finish out when the  │
  668.               │  input string is finished.  Note the routine    │
  669.               │  above does not create an index entry at the    │
  670.               │  end of the input string.  That is done here.   │
  671.               └─────────────────────────────────────────────────┘
  672. }
  673.    st := TrimL(st);
  674.    if st <> '' then
  675.    begin
  676.       inc(i);
  677.       New(dbfNdxTbl[i], Init(TrimL(st)));
  678.    end;
  679.    if i > 0 then dbfNdxActv := dbfNdxTbl[1];
  680.                                   {Set index active pointer to first index}
  681. end;
  682.  
  683. {
  684.                                  INDEX_LIST
  685.  
  686.  
  687.      ╔══════════════════════════════════════════════════════════════════╗
  688.      ║                                                                  ║
  689.      ║   The INDEX_LIST method returns the index key field from the     ║
  690.      ║   index used as the master index.  This is done instead of the   ║
  691.      ║   normal action of reading the .DBF file.  Only the index file   ║
  692.      ║   is read during this method.  A common use of this method is    ║
  693.      ║   to build a memory table of keys and associated record numbers. ║
  694.      ║                                                                  ║
  695.      ║       Calling the Method:                                        ║
  696.      ║                                                                  ║
  697.      ║           objectname.Index_LIST(RecNum, String, RNum)            ║
  698.      ║                                                                  ║
  699.      ║               ( where objectname is of type GS_dBase_DB,         ║
  700.      ║                       RecAct is the index key to retrieve.       ║
  701.      ║                          (Top_Record, Next_Record,               ║
  702.      ║                           Prev_Record, or Bttm_Record)           ║
  703.      ║                                                                  ║
  704.      ║                       String is field to place key value.        ║
  705.      ║                       RNum is field to place record number.      ║
  706.      ║                                                                  ║
  707.      ║       Result:                                                    ║
  708.      ║                                                                  ║
  709.      ║           The master Index file is accessed based on RecAct.     ║
  710.      ║           The value in the key field entry is returned in        ║
  711.      ║           String.  The record's location id the .DBF file is     ║
  712.      ║           returned in RecNum.  File_EOF is set upon an attempt   ║
  713.      ║           to access beyond the last index entry.                 ║
  714.      ║                                                                  ║
  715.      ╚══════════════════════════════════════════════════════════════════╝
  716. }
  717.  
  718.  
  719. Procedure GS_dBase_DB.Index_List(RecAct: LongInt; var I_List;
  720.                                  var RNum : longint);
  721. var
  722.    I_L : string[255] absolute I_List;
  723.                                       {Redefines I_List for internal use}
  724. BEGIN
  725. {
  726.          ┌───────────────────────────────────────────────────────────┐
  727.          │  The next statement checks to see if an index is active   │
  728.          │  (dbfNdxActv = true), and calls the index object's        │
  729.          │  KeyRead method if true and the record requested is       │
  730.          │  a relative record (less than 0).  Note that the method   │
  731.          │  is called using the first index object pointer in array  │
  732.          │  dbfNdxTabl (the master index).                           │
  733.          └───────────────────────────────────────────────────────────┘
  734. }
  735.    if (dbfNdxActv <> nil) and (RecAct < 0) then
  736.    begin
  737.       RNum := dbfNdxActv^.KeyRead(RecAct);
  738.       if RNum > 0 then                {if good read, RNum will be > 0}
  739.       begin
  740.          I_L := dbfNdxActv^.Ndx_Key_St;
  741.                                       {get key value, and store in the}
  742.                                       {I_List variable, using I_L which}
  743.                                       {points to the same memory location}
  744.       end else
  745.       begin
  746.          RNum := 0;                   {set null value if no valid read}
  747.          I_L := '';                   {set null value if no valid read}
  748.       end;
  749.       File_EOF := dbfNdxActv^.KeyEOF;
  750.                                       {move index EOF flag to File_EOF};
  751.    end;
  752. end;
  753.  
  754. {
  755.                                 INIT
  756.  
  757.  
  758.      ╔══════════════════════════════════════════════════════════════════╗
  759.      ║                                                                  ║
  760.      ║   The INIT method initializes objectname by reading the .DBF     ║
  761.      ║   file and loading file structure information into the object.   ║
  762.      ║   It also checks for a memo file (.DBT) and assigns that to      ║
  763.      ║   a file type if it exists.  This routine must be called         ║
  764.      ║   before using the other methods in objectname.                  ║
  765.      ║                                                                  ║
  766.      ║       Calling the Method:                                        ║
  767.      ║                                                                  ║
  768.      ║           objectname.Init(String)                                ║
  769.      ║                                                                  ║
  770.      ║               ( where objectname is of type GS_dBase_DB,         ║
  771.      ║                       String is the file name of the dBase       ║
  772.      ║                       file (without the .DBF extension).         ║
  773.      ║                                                                  ║
  774.      ║       Result:                                                    ║
  775.      ║                                                                  ║
  776.      ║           DBase file object is initialized and memo file is      ║
  777.      ║           initialized.                                           ║
  778.      ║                                                                  ║
  779.      ╚══════════════════════════════════════════════════════════════════╝
  780. }
  781.  
  782. CONSTRUCTOR GS_dBase_DB.Init(FName : string);
  783. var
  784.    i : integer;                       {Local working variable}
  785.  
  786. {
  787.            ┌───────────────────────────────────────────────────────┐
  788.            │  The ProcessHeader Procedure stores information from  │
  789.            │  the dBase III .DBF file into objectname.             │
  790.            └───────────────────────────────────────────────────────┘
  791. }
  792.  
  793.    PROCEDURE ProcessHeader;
  794.    VAR
  795.       dFilea : FileRec absolute dFile;
  796.       StrFil : string[80];
  797.       WSt    : string[12];
  798.       Result : word;
  799.       ofs    : longint;
  800.       o, i   : Integer;               {Local working variables}
  801.       m,dy,y : string[2];             {Local working variables}
  802.    BEGIN             {ProcessHeader}
  803. {
  804.               ┌─────────────────────────────────────────────────┐
  805.               │  Test to ensure file is a dBase III .DBF file.  │
  806.               │  Exit with error if it is not.  Set the         │
  807.               │  objectname.WithMemo flag if memo file present. │
  808.               └─────────────────────────────────────────────────┘
  809. }
  810.       CASE HeadProlog.DBType OF
  811.          DB3File : WithMemo := False;
  812.          DB3WithMemo : WithMemo := True;
  813.          ELSE
  814.          BEGIN
  815.             GS_FileClose(dFile);      {If not a valid dBase file, close}
  816.             StrFil := '';
  817.             i := 0;
  818.             while dFilea.Name[i] <> #0 do
  819.             begin
  820.                StrFil := StrFil + dFilea.Name[i];
  821.                inc(i);
  822.             end;
  823.             StrFil := StrFil + ' not a dBase III file';
  824.             ShowError(157,StrFil);
  825.             Exit;
  826.          END;
  827.       END;                      {CASE}
  828. {
  829.                 ┌─────────────────────────────────────────────┐
  830.                 │  Convert numeric date fields to ASCII text  │
  831.                 └─────────────────────────────────────────────┘
  832. }
  833.       Str(HeadProlog.month,m);
  834.       if length(m) = 1 then m := '0'+m;
  835.       Str(HeadProlog.day,dy);
  836.       if length(dy) = 1 then dy := '0'+dy;
  837.       Str(HeadProlog.year,y);
  838.       if length(y) = 1 then y := '0'+y;
  839.       DateOfUpdate := m + '/' + dy + '/' + y;
  840.  
  841.       NumRecs := HeadProlog.RecCount; {Number of records in file}
  842.       HeadLen := HeadProlog.Location; {Starting byte location of first record}
  843.       RecLen := HeadProlog.RecordLen; {Length of each record}
  844.       RecNumber := 0;                 {Set current record to zero}
  845.       File_EOF := false;              {Set End of File flag to false}
  846.  
  847.       GetMem(Fields, HeadLen-33);     {Allocate memory for fields buffer.}
  848.                                       {Compute total header size as length of}
  849.                                       {header file information (32 bytes),}
  850.                                       {End of Header mark (1 byte), and the}
  851.                                       {field descriptors (32 bytes each).}
  852.                                       {Size - 33 = memory required by fields}
  853.  
  854.       NumFields := (HeadLen - 33) div 32;
  855.                                       {Each field descriptor is 32 bytes}
  856.                                       {Field descriptor area of header can}
  857.                                       {be divided by 32 to get field count}
  858.  
  859.       GS_FileRead(dFile, -1, Fields^, HeadLen-33, Result);
  860.                                       {Read field descriptor portion of header}
  861.  
  862.       GetMem(FieldsN, NumFields*12);  {Allocate memory for fields buffer.}
  863.  
  864.       ofs := 1;                       {Find offset for each field}
  865.       for i := 1 to NumFields do
  866.       begin
  867.          Fields^[i].FieldAddress := ofs;
  868.          ofs := ofs + Fields^[i].FieldLen;
  869.          move(Fields^[i].FieldName,WSt[1],11);
  870.          WSt[0] := #11;
  871.          WSt[0] := char(pred(pos(#0,WSt)));
  872.          WSt := TrimR(WSt);        {Remove trailing spaces}
  873.          FieldsN^[i] := WSt;
  874.       end;
  875.    END;                      {ProcessHeader}
  876.  
  877. {
  878.          ┌──────────────────────────────────────────────────────────┐
  879.          │  The GetHeader Procedure does the initial file read.     │
  880.          │  Reads the first 32 bytes of .DBF file.  This contains   │
  881.          │  information on record size, field descriptor size,      │
  882.          │  last date updated.  Starting point for all other        │
  883.          │  file structure information.                             │
  884.          └──────────────────────────────────────────────────────────┘
  885. }
  886.  
  887.    PROCEDURE GetHeader;
  888.    VAR
  889.       Result : Word;
  890.    BEGIN                { GetHeader }
  891.       GS_FileRead(dFile, 0, HeadProlog, 32, Result);
  892.       ProcessHeader;
  893.    END;                 { GetHeader }
  894.  
  895. {
  896.               ┌─────────────────────────────────────────────────┐
  897.               │  Beginning of INIT Procedure.  It does the      │
  898.               │  following:                                     │
  899.               │      1.  Assigns .DBF extension to the file.    │
  900.               │      2.  Opens the file.                        │
  901.               │      3.  Gets header information for the        │
  902.               │          objectname object.                     │
  903.               │      4.  Closes file.                           │
  904.               │      5.  Allocates memory for a record buffer   │
  905.               │      6.  Sets file status to 'Not Open'.        │
  906.               │      7.  Sets Index Active to false.            │
  907.               │      8.  If memo file, assigns a file type.     │
  908.               └─────────────────────────────────────────────────┘
  909. }
  910.  
  911. begin
  912.    Filename := FName+'.DBF';          {Assign .DBF file extension}
  913.    GS_FileAssign(dFile, FileName);
  914.    GS_FileReset(dFile, 1);
  915.    GetHeader;                         {Load file structure information into}
  916.                                       {objectname}
  917.    GS_FileClose(dFile);               {Finished with file for now}
  918.    GetMem(CurRecord, RecLen);         {Allocate memory for a record buffer}
  919.    dStatus := NotOpen;                {Set file status to 'Not Open'   }
  920.    dbfNdxActv := nil;                 {Set index active pointer nil}
  921.    for i := 1 to 16 do dbfNdxTbl[i] := nil;
  922.                                       {Set index object pointer array to nil}
  923.    if WithMemo then
  924.    begin
  925.       GS_FileAssign(mFile, FName+'.DBT');
  926.                                       {If a memo file is attached, then assign}
  927.                                       {it to a file type.  This must be done}
  928.                                       {here so all future objects can get to}
  929.                                       {the file if necessary.}
  930.    end;
  931.    GS_KeyI_Objt.Init;                 {Initialize parent object}
  932. end;
  933.  
  934.  
  935.  
  936. {
  937.                                      OPEN
  938.  
  939.  
  940.      ╔══════════════════════════════════════════════════════════════════╗
  941.      ║                                                                  ║
  942.      ║   The OPEN method checks to see if the file referenced by        ║
  943.      ║   objectname is already open.  If it is open, no other action    ║
  944.      ║   is taken.  If the file is not open, then it and its memo       ║
  945.      ║   file, if one exists, is opened and flags are set.              ║
  946.      ║                                                                  ║
  947.      ║       Calling the Method:                                        ║
  948.      ║                                                                  ║
  949.      ║           objectname.Open                                        ║
  950.      ║                                                                  ║
  951.      ║               ( where objectname is of type GS_dBase_DB )        ║
  952.      ║                                                                  ║
  953.      ║       Result:                                                    ║
  954.      ║                                                                  ║
  955.      ║           1.  If file already opened, no action is taken.        ║
  956.      ║                                                                  ║
  957.      ║           otherwise:                                             ║
  958.      ║                                                                  ║
  959.      ║           1.  .DBF file is opened.                               ║
  960.      ║           2.  File status set to 'Not Updated'.                  ║
  961.      ║           3.  If memo file exists, .DBT file is opened.          ║
  962.      ║           4.  Current record number is set to zero.              ║
  963.      ║                                                                  ║
  964.      ╚══════════════════════════════════════════════════════════════════╝
  965. }
  966.  
  967.  
  968. PROCEDURE GS_dBase_DB.Open;
  969. BEGIN              { GS_dBase_Open }
  970.    if dStatus = NotOpen then          {Do only if file not already open}
  971.    begin
  972.       GS_FileAssign(dFile, FileName);
  973.       GS_FileReset(dFile, 1);         {Open .DBF file}
  974.       dStatus := NotUpdated;          {Set status to 'Not Updated' }
  975.       if WithMemo then GS_FileReset(mFile,GS_dBase_MaxMemoRec);
  976.                                       {If memo file, then open .DBT file}
  977.       RecNumber := 0;                 {Set current record to zero }
  978.       Blank;                          {Clear the record buffer}
  979.    end;
  980. END;               { GS_dBase_Open }
  981.  
  982. {
  983.                                  PUTREC
  984.  
  985.  
  986.      ╔══════════════════════════════════════════════════════════════════╗
  987.      ║                                                                  ║
  988.      ║   The PUTREC method will write an updated record to the dBase    ║
  989.      ║   III(+) .DBF file.  The data to be written must be stored       ║
  990.      ║   in objectname.CurRecord^ prior to calling the method.          ║
  991.      ║                                                                  ║
  992.      ║       Calling the Method:                                        ║
  993.      ║                                                                  ║
  994.      ║           objectname.PutRec(RecNum)                              ║
  995.      ║                                                                  ║
  996.      ║               ( where objectname is of type GS_dBase_DB,         ║
  997.      ║                       RecNum is physical record number to        ║
  998.      ║                       write to.  If not within the range of      ║
  999.      ║                       existing records, it record will be        ║
  1000.      ║                       appended to the end of the file.           ║
  1001.      ║                                                                  ║
  1002.      ║       Result:                                                    ║
  1003.      ║                                                                  ║
  1004.      ║           1.  If RecNum not in range of existing records         ║
  1005.      ║               it will be appended and objectname.NumRecs         ║
  1006.      ║               incremented by one.                                ║
  1007.      ║           2.  Record will be written.                            ║
  1008.      ║           3.  RecNum will become current record number.          ║
  1009.      ║           4.  File status will be changed to 'Updated'.          ║
  1010.      ║                                                                  ║
  1011.      ╚══════════════════════════════════════════════════════════════════╝
  1012. }
  1013.  
  1014.  
  1015. PROCEDURE GS_dBase_DB.PutRec(RecNum : LongInt);
  1016. VAR
  1017.    Result : Word;                     {Local Variable}
  1018.    RNum   : LongInt;                  {Local Variable}
  1019.    IKey   : String;                   {Local Variable for Key Formula string}
  1020.    ftyp   : Char;
  1021.    fval   : LongInt;
  1022.    i      : Integer;
  1023. BEGIN
  1024.    RNum := RecNum;                    {Move RecNum to local variable for }
  1025.                                       {possible modification}
  1026. {
  1027.                 ┌─────────────────────────────────────────────┐
  1028.                 │  If Record Number not in range of existing  │
  1029.                 │  records, append it to the end of file.     │
  1030.                 └─────────────────────────────────────────────┘
  1031. }
  1032.    IF (RNum > NumRecs) or (RNum < 1) then
  1033.    begin
  1034.       inc(NumRecs);                   {Increment record count}
  1035.       RNum := NumRecs;                {Put last record number in RNum}
  1036.    end;
  1037.    GS_FileWrite(dFile, HeadLen+(RNum-1)*RecLen, CurRecord^, RecLen, Result);
  1038.    RecNumber := RNum;              {Store record number as current record }
  1039.    dStatus := Updated;             {Set file status to 'Updated'}
  1040. {
  1041.          ┌───────────────────────────────────────────────────────────┐
  1042.          │  The next statement checks to see if an index is active   │
  1043.          │  (dbfNdxActv <> nil), and calls the index object's        │
  1044.          │  KeyUpdate method if true.   Note that the method         │
  1045.          │  is called using the index object pointer in dbfNdxActv   │
  1046.          │  (the master index).                                      │
  1047.          └───────────────────────────────────────────────────────────┘
  1048. }
  1049.    if (dbfNdxActv <> nil) then
  1050.    begin
  1051.       i := 1;
  1052.       while dbfNdxTbl[i] <> nil do
  1053.       begin
  1054.          IKey := Formula(dbfNdxTbl[i]^.Ndx_Key_Form,ftyp);
  1055.          if (IsDB3NDX) and (ftyp = 'D') then
  1056.          begin
  1057.             fval := GS_Date_Juln(IKey);
  1058.             str(fval,IKey);
  1059.          end;
  1060.          dbfNdxTbl[i]^.KeyUpdate(IKey,RNum,RecNum);
  1061.          inc(i);
  1062.       end;
  1063.    end;
  1064. END;                        {PutRec}
  1065.  
  1066.  
  1067. Procedure GS_dBase_DB.SetIndexMaster(ix : integer);
  1068. begin
  1069.    if (ix < 1) or (ix > 16) then exit;
  1070.    if dbfNdxTbl[ix] <> nil then
  1071.       dbfNdxActv := dbfNdxTbl[ix];
  1072. end;
  1073.  
  1074. {
  1075.                                   UNDELETE
  1076.  
  1077.  
  1078.    ╔═══════════════════════════════════════════════════════════════════════╗
  1079.    ║                                                                       ║
  1080.    ║  The UNDELETE method will reset the Delete flag in the dBase III(+)   ║
  1081.    ║  file.                                                                ║
  1082.    ║                                                                       ║
  1083.    ║      Calling the Method:                                              ║
  1084.    ║                                                                       ║
  1085.    ║            objectname.UnDelete                                        ║
  1086.    ║                                                                       ║
  1087.    ║                   ( where objectname is of type GS_dBase_DB)          ║
  1088.    ║                                                                       ║
  1089.    ║       Result:                                                         ║
  1090.    ║                                                                       ║
  1091.    ║            1.  objectname.DelFlag is set false.                       ║
  1092.    ║            2.  A ' ' (UnDelete flag) is set in byte 0 of current      ║
  1093.    ║                file.                                                  ║
  1094.    ║            3.  PutRec is called to write current record to disk.      ║
  1095.    ║                                                                       ║
  1096.    ╚═══════════════════════════════════════════════════════════════════════╝
  1097. }
  1098.  
  1099.  
  1100. PROCEDURE GS_dBase_DB.UnDelete;
  1101. begin
  1102.    DelFlag := false;                  {Set Delete flag to false}
  1103.    CurRecord^[0] := GS_dBase_UnDltChr;
  1104.                                       {Put ' ' in first byte of current record}
  1105.    PutRec(RecNumber);                 {Write the current record to disk }
  1106. end;
  1107.  
  1108. {                         Free buffer memory}
  1109.  
  1110. Destructor GS_dBase_DB.UnInit;
  1111. begin
  1112.    Close;
  1113.    FreeMem(FieldsN, NumFields*12);  {DeAllocate memory for fields list.}
  1114.    FreeMem(CurRecord, RecLen);      {DeAllocate memory for record buffer}
  1115.    FreeMem(Fields, HeadLen-33);     {DAllocate memory for fields buffer.}
  1116.    GS_KeyI_Objt.Done;
  1117. end;
  1118.  
  1119.  
  1120.  
  1121. begin
  1122.    if IndexSignature = 'NDX3' then IsDB3NDX := true else IsDB3NDX := false;
  1123. end.
  1124.  
  1125.  
  1126.