Check out and contribute to CodePedia, the wiki for developers.

View \GSOB_NTX.PAS

Halcyon version 3.0

Submitted By: Unknown
Rating: (Not rated) (Rate It)


unit GSOB_Ntx;
{-----------------------------------------------------------------------------
                           Clipper Index Handler

       GS_DBNtx Copyright (c)  Richard F. Griffin

       08 February 1993

       102 Molded Stone Pl
       Warner Robins, GA  31088

       -------------------------------------------------------------
       This unit handles the objects for all Clipper index (.NTX)
       operations.  This unit may be implemented by adding a conditional
       define to the complier options.  In the IDE this is done in the
       Options|Compile menu, selecting Conditional Define, and adding
       CLIPPER as a Define item.  You must then recompile using the
       Compile|Build option to force recompilation of units that will use
       the index.

       In the command line compiler use the /D option, for example:

               TPC MyProg /DCLIPPER

       That's the only change necessary to replace .NDX indexes with
       Clipper .NTX indexes.

   Changes:

      17 Apr 93 - KeySort routine corrected to handle key string lengths
                  properly in GSOB_NDX.  This correction is included for
                  consistency in the calling structure.  This unit will
                  continue to use Key_Lgth as the string length.

------------------------------------------------------------------------------}

{$O+}

interface

uses
   GSOB_Var,
   GSOB_Dte,
   GSOB_Str,                          {String handler routines}
   GSOB_Inx,
   GSOB_Dsk,                          {File handler routines}
   GSOB_DBF,
   {$IFDEF WINDOWS}
      Objects;
   {$ELSE}
      GSOB_Obj;
   {$ENDIF}

const

   NdxBlokSize = 1024;


type

   GSP_InxHeader  = ^GSR_InxHeader;
   GSR_InxHeader  = Record
      Vers1,
      Vers2       : Integer;
      Root        : Longint;
      Unknwn1     : Longint;
      Entry_Sz    : Integer;
      Key_Lgth    : Integer;
      Key_Dcml    : Integer;
      Max_Keys    : Integer;
      Min_Keys    : Integer;
      Key_Form    : array [0..1001] of char;
   end;

   GSP_InxDataBlk  = ^GSR_InxDataBlk;
   GSR_InxDataBlk  = Record
      case integer of
         0 : (Data_Ary    : array [0..NdxBlokSize] of byte);
         1 : (Indx_Ary    : array [0..NdxBlokSize div 2] of word);
         2 : (Entry_Ct    : Integer);
   end;

   GSP_InxElement = ^GSR_InxElement;
   GSR_InxElement = Record
      Block_Ax  : Longint;
      Recrd_Ax  : Longint;
      Char_Fld  : array [1..255] of char;
   end;

   GSP_IndexFile   = ^GSO_IndexFile;
   GSP_InxNode = ^GSO_InxNode;

   GSP_InxTable = ^GSO_InxTable;
   GSO_InxTable = Object(TCollection)
      ixLink      : GSP_IndexFile;
      ActivePage  : GSP_InxNode;
      Elements    : array[0..NdxBlokSize div 12] of GSP_InxElement;
      OkToClear   : boolean;
      constructor Init(ILink : GSP_IndexFile);
      destructor  Done; virtual;
      function    AccessPage(pn : longint) : pointer;
      procedure   AdjustNodePntrs(pn : longint);
      function    FetchBttm : pointer;
      function    FetchCurr : pointer;
      function    FetchNext : pointer;
      function    FetchPrev : pointer;
      function    FetchTop  : pointer;
      procedure   NodeEntryDelete(en : integer);
      procedure   NodeEntryInsert(en : integer; wkey: string;
                                  wb, wr: longint);
      function    NodeGet(pn : longint) : pointer;
      procedure   NodePntrReplace(en : integer; wkey: string; wb,wr: longint);
      function    NodePut(pn : longint) : pointer;
      procedure   ResetBuffers;
   end;

   GSO_InxNode = Object(TObject)
      tbLink      : GSP_InxTable;   {Link to collection owner}
      IndxBufr    : GSP_InxDataBlk;
      NodeLink    : Longint;
      Page_No     : Longint;   {Disk block holding node info}
      Etry_No     : Integer;   {Last entry used in node}
      Count       : Integer;   {Number of keys in this node }
      NonLeaf     : Boolean;   {True for non-leaf nodes}
      Changed     : boolean;
      constructor Init(CLink : GSP_InxTable; pn : longint);
      destructor  Done; virtual;
      procedure   Deliver;
      procedure   Retrieve;
   end;

   GSO_IndexFile   = object(GSO_DiskFile)
      ixColl       : GSP_IndxColl;
      ixKey_St     : ixKeyString;     {Holds last key value found}
      ixKey_Num    : longint;         {Holds last physical record number}
      IxKey_Form   : string[255];     {Holds the key formula in type string}
      ixKey_Siz    : integer;
      ixKey_Typ    : char;
      ixBOF        : boolean;
      ixEOF        : boolean;
      ixFollowKey  : boolean;         {Flag to follow key for next read when}
                                      {the key is modified.  If false, the }
                                      {next record from the old key position }
                                      {is read.  If true, the next record from}
                                      {the new key position is read.  Default}
                                      {is false}
      tbLink       : GSP_InxTable;
      Ndx_Hdr      : GSR_InxHeader;
      Key_Lgth     : Integer;
      Max_Keys     : Integer;
      Entry_Sz     : Integer;
      CurrNode     : GSP_InxNode;
      CurrElmt     : GSP_InxElement;  {Pointer to key entry information}
      CacheBuf     : PByteArray;
      CacheBlok    : word;

      Constructor Init(IName : string);
      Constructor NewInit(filname,formla: string; lth,dcl: integer; typ: char);
      Destructor  Done; virtual;
      Procedure   IndxClear; virtual;
      Procedure   IndxStore(p : GSP_IndxColl; recnode : boolean); virtual;
      Function    KeyFind(st : String) : longint; virtual;
      Procedure   KeyList(st : string); virtual;
      Function    KeyLocRec(rec : longint) : boolean; virtual;
      Function    KeyRead(a : LongInt) : longint; virtual;
      Procedure   KeySort(kl : integer; sa : SortStatus); virtual;
      Procedure   KeyUpdate(rec: longint; st: string; Apnd: boolean); virtual;
      Function    Ndx_AdjVal(st : string): string;
      Procedure   Ndx_Close;
      Procedure   Ndx_Flush;
      Procedure   Ndx_GetHdr;
      Function    Ndx_NextBlock : longint;
      Procedure   Ndx_PutHdr;
      Function    Ndx_Root : Longint;
      Procedure   WriteStatus(RNum : longint); virtual;
   end;

implementation


const

   Same_Record = -5;   {Token value passed to read the same record}

var
   Ndx_Data : GSR_InxDataBlk;

   Work_Key : string;               {Holds key passed in Find and KeyUpdate}
   RPag     : Longint;              {Work variable to hold current index block}
   RNum     : Longint;              {Work variable for record number}
   IsAscend : Boolean;              {Flag for ascending/descending status.}
                                    {Set based on Next/Previous Record read}

{------------------------------------------------------------------------------
                               GSO_InxTable
------------------------------------------------------------------------------}


constructor GSO_InxTable.Init(ILink : GSP_IndexFile);
var
   p  : pointer;
   i  : integer;
begin
   TCollection.Init(32,16);
   for i := 0 to ILink^.Max_Keys do
      Elements[i] := Addr(Ndx_Data.Data_Ary[Ndx_Data.Indx_Ary[i+1]]);
   ixLink := ILink;
   OkToClear := true;
end;

destructor GSO_InxTable.Done;
var
   i : integer;
begin
   TCollection.Done;
end;

function GSO_InxTable.AccessPage(pn : longint) : pointer;
var
   p  : GSP_InxNode;
   px : longint;
   i  : integer;
   ok : boolean;
begin
   ok := false;
   i := 0;
   while (i < Count) and not ok do
   begin
      if GSP_InxNode(Items^[i])^.Page_No = pn then
      begin
         ok := true;
         p := Items^[i];
         AtDelete(i);
         Insert(p);
      end;
      inc(i);
   end;
   if not ok then
   begin
      if Count > 7 then Free(Items^[0]);
      p := New(GSP_InxNode, Init(@Self, pn));
      Insert(p);
   end;
   ActivePage := p;
   AccessPage := p;
end;

procedure GSO_InxTable.AdjustNodePntrs(pn : longint);
var
   p : GSP_InxNode;
   q : GSP_InxNode;
   e  : GSP_InxElement;
   i : integer;
   v : integer;
   x : longint;
begin
   p := AccessPage(pn);
   if not p^.NonLeaf then exit;
   for i := 0 to p^.Count-1 do
   begin
      e := Elements[i];
      x := e^.Block_Ax;
      for v := 0 to Count -1 do
      begin
         q := Items^[v];
         if q^.Page_No = x then q^.NodeLink := pn;
      end;
   end;
end;

function GSO_InxTable.FetchBttm : pointer;
var
   n  : longint;
   p  : GSP_InxNode;
   e  : GSP_InxElement;
begin
   p := NodeGet(ixLink^.Ndx_Root);
   if p^.Count > 0 then e := Elements[p^.Count-1] else e := nil;
   while p^.NonLeaf and (p^.Count > 0) do
   begin
      n := p^.Page_No;
      p^.Etry_No := p^.Count;
      if p^.Count > 0 then dec(p^.Etry_No);
      p := NodeGet(e^.Block_Ax);
      p^.NodeLink := n;
      if p^.Count > 0 then e := Elements[p^.Count-1] else e := nil;
   end;
   p^.Etry_No := p^.Count;
   if p^.Count > 0 then dec(p^.Etry_No);
   FetchBttm := e;
end;

function GSO_InxTable.FetchCurr : pointer;
begin
   FetchCurr := Elements[ActivePage^.Etry_No];
end;

function GSO_InxTable.FetchNext : pointer;
var
   p  : GSP_InxNode;
   h  : GSP_InxNode;
   e  : GSP_InxElement;
   n  : longint;
   t  : boolean;
begin
   p := ActivePage;
   p^.Retrieve;
   h := p;
   t := p^.NonLeaf;
   inc(p^.Etry_No);
   while (p^.Etry_No >= p^.Count) and (p^.NodeLink <> -1) do
   begin
      Delete(p);
      AtInsert(0,p);
      p := NodeGet(p^.NodeLink);
      if t or (p^.Etry_No = p^.Count-1) then inc(p^.Etry_No);
   end;
   if (p^.Etry_No >= p^.Count) and (p^.Page_No = ixLink^.Ndx_Root) then
      begin
         FetchNext := nil;
         dec(p^.Etry_No);
      end
         else if not t then
               FetchNext := Elements[p^.Etry_No]
            else
            begin
               e := Elements[p^.Etry_No];
               while p^.NonLeaf do
               begin
                  n := p^.Page_No;
                  p := NodeGet(e^.Block_Ax);
                  p^.NodeLink := n;
                  p^.Etry_No := 0;
                  if p^. Count > 0 then e := Elements[0] else e := nil;
               end;
               FetchNext := e;
            end;
end;

function GSO_InxTable.FetchPrev : pointer;
var
   p  : GSP_InxNode;
   h  : GSP_InxNode;
   e  : GSP_InxElement;
   n  : longint;
   t  : boolean;
begin
   p := ActivePage;
   p^.Retrieve;
   h := p;
   t := p^.NonLeaf;
   if not t then dec(p^.Etry_No);
   while (p^.Etry_No < 0) and (p^.NodeLink <> -1) do
   begin
      Delete(p);
      AtInsert(0,p);
      p := NodeGet(p^.NodeLink);
      dec(p^.Etry_No);
   end;
   if (p^.Etry_No < 0) and (p^.Page_No = ixLink^.Ndx_Root) then
   begin
      FetchPrev := nil;
      inc(p^.Etry_No);
      while p^.NonLeaf do
      begin
         e := Elements[p^.Etry_No];
         p := NodeGet(e^.Block_Ax);
         inc(p^.Etry_No);
      end;
   end
      else if not t then FetchPrev := Elements[p^.Etry_No]
         else
         begin
            e := Elements[p^.Etry_No];
            while p^.NonLeaf and (p^.Count > 0) do
            begin
               n := p^.Page_No;
               p := NodeGet(e^.Block_Ax);
               p^.NodeLink := n;
               p^.Etry_No := p^.Count-1;
               if p^. Count > 0 then e := Elements[p^.Count-1] else e := nil;
            end;
            FetchPrev := e;
         end;
end;

function GSO_InxTable.FetchTop : pointer;
var
   p  : GSP_InxNode;
   e  : GSP_InxElement;
   n  : longint;
begin
   p := NodeGet(ixLink^.Ndx_Root);
   if p^.Count > 0 then e := Elements[0] else e := nil;
   while p^.NonLeaf and (p^.Count > 0) do
   begin
      n := p^.Page_No;
      p^.Etry_No := 0;
      p := NodeGet(e^.Block_Ax);
      p^.NodeLink := n;
      if p^.Count <= 0 then e := nil;
   end;
   p^.Etry_No := 0;
   FetchTop := e;
end;

procedure GSO_InxTable.NodeEntryDelete(en : integer);
var
   p  : GSP_InxNode;
begin
   p := ActivePage;
   Move(Elements[en+1]^,Elements[en]^,ixLink^.Entry_Sz*(p^.Count-en));
   dec(Ndx_Data.Entry_Ct);
   p^.Deliver;
end;

procedure GSO_InxTable.NodeEntryInsert
                                (en : integer; wkey: string; wb,wr: longint);
var
   p  : GSP_InxNode;
   e  : GSP_InxElement;
begin
   p := ActivePage;
   e := Elements[en];
   Move(Elements[en]^,Elements[en+1]^,ixLink^.Entry_Sz*(p^.Count-en));
   move(wkey[1],e^.Char_Fld,ixLink^.Key_Lgth);
   e^.Block_Ax := wb;
   e^.Recrd_Ax := wr;
   inc(Ndx_Data.Entry_Ct);
   p^.Deliver;
end;

function GSO_InxTable.NodeGet(pn : longint) : pointer;
var
   p  : GSP_InxNode;
begin
   p := AccessPage(pn);
   p^.Retrieve;
   NodeGet := p;
end;

procedure GSO_InxTable.NodePntrReplace
                                (en : integer; wkey: string; wb,wr: longint);
var
   p  : GSP_InxNode;
   q  : GSP_InxNode;
   e  : GSP_InxElement;
begin
   p := ActivePage;
   q := p;
   p := NodeGet(p^.NodeLink);
   while (p^.Etry_No >= p^.Count-1) and (p^.NodeLink <> -1) do
      p := NodeGet(p^.NodeLink);
   if p^.NodeLink <> -1 then
   begin
      e := Elements[p^.Etry_No];
      FillChar(e^.Char_Fld, ixLink^.Key_Lgth, ' ');
      move(wkey[1],e^.Char_Fld,length(wkey));
      e^.Block_Ax := wb;
      e^.Recrd_Ax := wr;
      p^.Deliver;
   end;
   ActivePage := q;
   ActivePage^.Retrieve;
end;

function GSO_InxTable.NodePut(pn : longint) : pointer;
var
   p  : GSP_InxNode;
begin
   p := AccessPage(pn);
   p^.Deliver;
   NodePut := p;
end;

procedure GSO_InxTable.ResetBuffers;
begin
   if OkToClear then FreeAll;
end;

{------------------------------------------------------------------------------
                               GSO_InxNode
------------------------------------------------------------------------------}


constructor GSO_InxNode.Init(CLink : GSP_InxTable; pn : longint);
var
   i : integer;
   r : word;
begin
   IndxBufr := nil;
   Page_No := pn;
   Etry_No := -1;
   Count := 0;
   NonLeaf := true;
   tbLink := CLink;
   NodeLink := -1;
   Changed := false;
end;

destructor GSO_InxNode.Done;
var
   r : word;
begin
   if IndxBufr <> nil then dispose(IndxBufr);
   TObject.Done;
end;

procedure GSO_InxNode.Deliver;
var
   v : longint;
begin
   Count := Ndx_Data.Entry_Ct;
   move(Ndx_Data.Data_Ary[(Count *  tbLink^.ixLink^.Entry_Sz)],v,4);
   NonLeaf := v <> 0;
   if NonLeaf then Count := Count + 1;
   if IndxBufr = nil then New(IndxBufr);
   move(Ndx_Data,IndxBufr^,NdxBlokSize);
   tbLink^.ixLink^.Write(Page_No,IndxBufr^,NdxBlokSize);
end;

procedure GSO_InxNode.Retrieve;
var
   v : longint;
begin
   if IndxBufr = nil then
   begin
      New(IndxBufr);
      tbLink^.ixLink^.Read(Page_No,IndxBufr^,NdxBlokSize);
   end;
   move(IndxBufr^,Ndx_Data,NdxBlokSize);
   Count := Ndx_Data.Entry_Ct;
   move(Ndx_Data.Data_Ary[Ndx_Data.Indx_Ary[1]],v,4);
   NonLeaf := v <> 0;
   if nonLeaf then Count := Count + 1;
end;

{-----------------------------------------------------------------------------
                                 GSO_IndexFile
------------------------------------------------------------------------------}


constructor GSO_IndexFile.Init(IName : string);
var
   i : integer;
begin
   GSO_DiskFile.Init(IName+'.NTX',dfReadWrite+dfSharedDenyNone);
   dfFileFlsh := WriteFlush;
   if dfFileExst then Reset(1)
      else
      begin
         Error(dosFileNotFound,ndxInitError);
         exit;
      end;
   Read(0,Ndx_Hdr,NdxBlokSize);
   Key_Lgth := Ndx_Hdr.Key_Lgth;
   Max_Keys := Ndx_Hdr.Max_Keys;
   Entry_Sz := Ndx_Hdr.Entry_Sz;
   move(Ndx_Hdr.Key_Form[0], ixKey_Form[1],241);
   ixKey_Form[0] := #241;
   ixKey_Form[0] := chr(pos(#0,ixKey_Form)-1);
   ixKey_Form := TrimR(ixKey_Form);
   ixKey_Form := TrimL(ixKey_Form);
   ixKey_Siz := Key_Lgth;
   ixBOF := false;
   ixEOF := false;
   ixKey_St := '';
   ixKey_Num := 0;
   ixFollowKey := false;
   Read(Ndx_Root,Ndx_Data,NdxBlokSize);
   tbLink := New(GSP_InxTable, Init(@Self));
end;

Constructor GSO_IndexFile.NewInit(filname,formla : string; lth,dcl: integer;
                                  typ : char);
var
   i : integer;
begin
   GSO_DiskFile.Init(filname+'.NTX',dfReadWrite);
   dfFileFlsh := WriteFlush;
   Rewrite(1);
   FillChar(Ndx_Hdr, SizeOf(Ndx_Hdr),#0);
   Ndx_Hdr.Root := NdxBlokSize;
   Ndx_Hdr.Vers1 := 6;
   Ndx_Hdr.Vers2 := 1;
   lth := lth+dcl;
   if dcl > 0 then inc(lth){account for decimal point}
   Ndx_Hdr.Key_Lgth := lth;
   Ndx_Hdr.Key_Dcml := dcl;
   i := lth+8;
   Ndx_Hdr.Max_Keys := ((NdxBlokSize-4) div (i+2)) - 1;
   if odd(Ndx_Hdr.Max_Keys) then dec(Ndx_Hdr.Max_Keys);
   Ndx_Hdr.Min_Keys := Ndx_Hdr.Max_Keys div 2;
   Ndx_Hdr.Entry_Sz := i;
   CnvStrToAsc(formla,Ndx_Hdr.Key_Form, length(formla)+1);
   Write(0,Ndx_Hdr,NdxBlokSize);
   Key_Lgth := lth;
   Max_Keys := Ndx_Hdr.Max_Keys;
   Entry_Sz := Ndx_Hdr.Entry_Sz;
   ixKey_Form := formla;
   ixKey_Form := TrimR(ixKey_Form);
   ixKey_Form := TrimL(ixKey_Form);
   ixKey_Siz := Key_Lgth;
   ixKey_Typ := typ;
   ixBOF := false;
   ixEOF := false;
   ixKey_St := '';
   ixKey_Num := 0;
   ixFollowKey := false;
   FillChar(Ndx_Data, SizeOf(Ndx_Data),#0);
   for i := 0 to Ndx_Hdr.Max_Keys do Ndx_Data.Indx_Ary[succ(i)] :=
                     ((Ndx_Hdr.Max_Keys + 2) * 2) + (Ndx_Hdr.Entry_Sz * i);
   Write(-1,Ndx_Data,NdxBlokSize);
   tbLink := New(GSP_InxTable, Init(@Self));
end;

Destructor GSO_IndexFile.Done;
var
   i : integer;
begin
   Ndx_Close;
   GSO_DiskFile.Done;
end;

Procedure GSO_IndexFile.IndxClear;
var
   i : integer;
begin
   Ndx_Flush;
   Ndx_GetHdr;
   Ndx_Hdr.Root := 1;
   Write(0,Ndx_Hdr,NdxBlokSize);
   ixBOF := false;
   ixEOF := false;
   ixKey_St := '';
   ixKey_Num := 0;
   FillChar(Ndx_Data, SizeOf(Ndx_Data),#0);
   Write(-1,Ndx_Data,NdxBlokSize);
   Truncate(-1);
end;

Procedure GSO_IndexFile.IndxStore(p: GSP_IndxColl; recnode: boolean);
var
   i         : integer;
   rc        : integer;
   rl        : word;
   dt        : longint;
   ec        : longint;
   kc        : integer;
   mh        : integer;
   mk        : integer;
   mm        : integer;
   mr        : integer;
   mv        : integer;
   rf        : GSP_IndxEtry;
   rr        : GSP_IndxEtry;
   sc        : integer;
   sv        : string[104];
   dl        : integer;
   ixFiller  : array[0..NdxBlokSize+108] of byte;
   ixData    : GSR_InxDataBlk absolute ixFiller;
   ixPntr    : GSP_InxElement;
   ixBlok    : longint;
   NodeColl  : GSP_IndxColl;

   procedure CacheWrite;
   begin
      move(ixData,CacheBuf^[CacheBlok],NdxBlokSize);
      CacheBlok := CacheBlok+NdxBlokSize;
      if CacheBlok >= NdxBlokSize*16 then
      begin
         Write(-1,CacheBuf^,CacheBlok);
         CacheBlok := 0;
      end;
   end;

   procedure CollectNodes;
   begin
      ixData.Entry_Ct := rc-1;
      CacheWrite;
      if recnode then
      begin
         move(rr^.Tag,sv[Key_Lgth+1],4){Hang on to Record number}
         sv[0] := chr(Key_Lgth+4);
      end;
      NodeColl^.InsertKey(ixBlok, sv);
      rc := 0;
      inc(ixBlok,NdxBlokSize);
      mk := mv;
      if mm > 0 then
      begin
         inc(mk);
         dec(mm);
      end;
   end;

begin
   mk := Max_Keys;
   if recnode then mr := 1 else mr := 0;
   kc := p^.KeyCount;
   if kc <= mk then
   begin
      mk := kc+1;
      mv := mk;
      mm := 0;
   end
   else
   begin
      i := kc;
      mv := 0;
      repeat
         mh := mv;
         mv := i div mk;
         inc(mv);
         i := (kc - mv) + mr;
      until mh = mv;
      mm := i mod mv;
      mk := i div mv;
      inc(mk);               {to keep things balanced on leaf nodes}
      mv := mk;
      if mm > 0 then
      begin
         inc(mk);
         dec(mm);
      end;
   end;
   if recnode then
   begin
      ixBlok := NdxBlokSize;
      GetMem(CacheBuf,NdxBlokSize*16);
      Read(0,CacheBuf^,NdxBlokSize);    {Position to initial loc}
   end
   else
   begin
      ixBlok := Ndx_NextBlock;
   end;
   CacheBlok := 0;
   New(NodeColl, Init(Key_Lgth+4,NoSort));
   rr := p^.RetrieveKey;
   rc := 0;
   ec := 0;
   FillChar(ixData, SizeOf(ixData),#0);
   dl := (Max_Keys + 2) * 2;
   for i := 0 to Max_Keys do ixData.Indx_Ary[i+1] := (dl + (Entry_Sz * i));
   while rr <> nil do
   begin
      rf := rr;
      ixPntr :=  Addr(ixData.Data_Ary[ixData.Indx_Ary[rc+1]]);
      sv := rr^.KeyStr;
      if (ixKey_Typ = 'N') and recnode then
      begin
         sv := PadL(sv, Key_Lgth);
         for sc := 1 to length(sv) do
              if sv[sc] = ' ' then sv[sc] := '0';
      end;
      move(sv[1],IxPntr^.Char_Fld[1],Key_Lgth);
      if recnode then
      begin
         IxPntr^.Recrd_Ax := rr^.Tag;
         IxPntr^.Block_Ax := 0;
      end
      else
      begin
         move(rr^.KeyStr[Key_Lgth+1],IxPntr^.Recrd_Ax,4); {Load Record number}
         IxPntr^.Block_Ax := rr^.Tag;
      end;
      inc(rc);
      inc(ec);
      WriteStatus(ec);
      if rc >= mk then CollectNodes;
      rr := p^.RetrieveKey;
   end;
   if rc > 0 then
   begin
      rr := rf;
      if recnode then inc(rc);
      CollectNodes;
   end;
   p^.EndRetrieve;
   if CacheBlok > 0 then Write(-1,CacheBuf^,CacheBlok);
   if ec > Max_Keys then IndxStore(NodeColl, false);
   Dispose(NodeColl, Done);
   if recnode then
   begin
      FreeMem(CacheBuf,NdxBlokSize*16);
      Dispose(ixColl, Done);
      Ndx_Hdr.Root := Ndx_NextBlock-NdxBlokSize;
      Ndx_Flush;
   end;
end;


Function GSO_IndexFile.KeyFind(st : string) : LongInt;
var
   i         : integer;               {Work variable}
   rl        : integer;               {Result code for Val procedure}
   ct        : integer;               {Variable to hold BlockRead byte count}
   IsEqual   : boolean;               {Flag to hunt for key match}
   PNode     : longint;
   Match_Cnd : integer;
   LeafPag   : longint;
   LeafEtry  : integer;


   procedure StoreMatchValue;
   begin
      move(CurrElmt^.Char_Fld,ixKey_St[1],Key_Lgth);
                                      {Move the key field to Ndx_Key_St.}
      ixKey_St[0] := Work_Key[0];   {Now insert the length into Ndx_Key_St}
   end;

   function DoMatchValue : integer;
   begin
      Match_Cnd := StrCompare(ixKey_St, Work_Key);
      DoMatchValue := Match_Cnd;
   end;

   function SearchMatchValue(var Index: Integer): Boolean;
   var
      L,
      H,
      I,
      C: Integer;
   begin
      SearchMatchValue := False;
      L := 0;
      H := CurrNode^.Count - 1;
      while L <= H do
      begin
         I := (L + H) shr 1;
         CurrElmt := tbLink^.Elements[I];
         if (CurrNode^.NonLeaf) and (CurrNode^.Count-1 = I) then
            C := 1
         else
         begin
            StoreMatchValue;
            C := DoMatchValue;
         end;
         if C < 0 then L := I + 1 else
         begin
            H := I - 1;
            if C = 0 then SearchMatchValue := true;
         end;
      end;
      CurrElmt := tbLink^.Elements[L];
      StoreMatchValue;
      Index := L;
   end;

begin
{   tbLink^.ResetBuffers;}
   ixKey_Num := 0;                    {Initialize}