(***********************************************************)
(*                                                         *)
(*           TURBO-access version 1.00 (CP/M-80)           *)
(*                                                         *)
(*                       ACCESS module                     *)
(*                                                         *)
(*                   Copyright (C) 1984 by                 *)
(*                        BORLAND Int.                     *)
(*                                                         *)
(***********************************************************)

(*$A+,I-,R-*)

type
  TaStr14   =  string[14];
  DataFile  =  record
               case Integer of
                 0 : (F          : file of Byte;
                      FirstFree,
                      NumberFree,
                      Int1,
                      Int2       : Integer);
                 1 : (Fil1       : array[1..4] of Byte;
                      NewRec,
                      RecL,
                      TaRec,
                      Fil2       : Integer;
                      TaDrive    : Byte;
                      TaName     : array[1..8] of Char;
                      TaType     : array[1..3] of Char);
               end;
  TaKeyStr  =  string[MaxKeyLen];
  TaItem    =  record
                 DataRef,PageRef : Integer;
                 Key : TaKeyStr;
               end;
  TaPage    =  record
                 ItemsOnPage : 0..PageSize;
                 BckwPageRef : Integer;
                 ItemArray : array[1..PageSize] of TaItem;
               end;
  TaPagePtr =  ^TaPage;
  TaSearchStep =
               record
                 PageRef,ItemArrIndex : Integer;
               end;
  TaPath    =  array[1..MaxHeight] of TaSearchStep;
  IndexFile =  record
                 DataF : DataFile;
                 AllowDuplKeys : Boolean;
                 KeyL,RR,PP : Integer;
                 Path : TaPath;
               end;
  IndexFilePtr = ^IndexFile;
  TaStackRec =
               record
                 Page : TaPage;
                 IndexFPtr : IndexFilePtr;
                 PageRef : Integer;
                 Updated : Boolean;
               end;
  TaStackRecPtr = ^TaStackRec;
  TaPageStack = array[1..PageStackSize] of TaStackRec;
  TaPageMap  =  array[1..PageStackSize] of Integer;
  TaRecordBuffer  =
               record
                 case Integer of
                   0 : (Page : TaStackRec);
                   1 : (R : array[1..MaxDataRecSize] of Byte);
               end;

var
  IOstatus  : Integer;
  OK        : Boolean;
  TaRecBuf  : TaRecordBuffer;
  TaPageStk : TaPageStack;
  TaPgMap   : TaPageMap;

procedure TaIOcheck(var DatF : DataFile; R : Integer);
begin
  if IOstatus <> 0 then with DatF do
  begin
    Writeln;
    Writeln('TURBO-access I/O error ',IOstatus);
    Writeln('file ',Chr(TaDrive + 64),':',TaName,'.',TaType,' record ',R);
    Writeln('Program terminated');
    Halt;
  end;
end;

procedure GetRec(var DatF   : DataFile;
                     R      : Integer;
                 var Buffer           );
var
  B : Byte absolute Buffer;
begin
  Seek(DatF.F,R);
  Read(DatF.F,B);
  IOstatus := IOresult;
  TaIOcheck(DatF,R);
end;

procedure PutRec(var DatF   : DataFile;
                     R      : Integer;
                 var Buffer           );
var
  B : Byte absolute Buffer;
begin
  Seek(DatF.F,R);
  Write(DatF.F,B);
  IOstatus := IOresult;
  TaIOcheck(DatF,R);
end;


procedure MakeFile(var DatF   : DataFile;
                       FName  : TaStr14;
                       RecLen : Integer);
begin
  Assign(DatF.F,FName);
  Rewrite(DatF.F);
  IOstatus := IOresult;
  if IOstatus = 3 then
    OK := false
  else
  begin
    TaIOcheck(DatF,0);
    DatF.RecL := RecLen;
    DatF.FirstFree := -1;
    DatF.NumberFree := 0;
    DatF.Int1 := 0;
    DatF.Int2 := 0;
    Move(DatF.FirstFree,TaRecBuf,8);
    PutRec(DatF,0,TaRecBuf);
    OK := true;
  end;
end;

procedure OpenFile(var DatF   : DataFile;
                       FName  : TaStr14;
                       RecLen : Integer);
begin
  Assign(DatF.F,FName);
  Reset(DatF.F);
  IOstatus := IOresult;
  OK:=(IOstatus = 0) or (IOstatus = $90);
  if OK then
  begin
    DatF.RecL := RecLen;
    GetRec(DatF,0,TaRecBuf);
    Move(TaRecBuf,DatF.FirstFree,8);
  end;
end;

procedure CloseFile(var DatF : DataFile);
begin
  Move(DatF.FirstFree,TaRecBuf,8);
  PutRec(DatF,0,TaRecBuf);
  Close(DatF.F);
  IOstatus := IOresult;
  TaIOcheck(DatF,0);
end;

procedure AddRec(var DatF   : DataFile;
                 var R      : Integer;
                 var Buffer           );
begin
  if DatF.FirstFree =  - 1 then
    R := DatF.NewRec
  else
  begin
    R := DatF.FirstFree;
    GetRec(DatF,R,TaRecBuf);
    Move(TaRecBuf,DatF.FirstFree,2);
    DatF.NumberFree := DatF.NumberFree - 1;
  end;
  PutRec(DatF,R,Buffer);
end;

procedure DeleteRec(var DatF : DataFile;
                        R    : Integer);
begin
  Move(DatF.FirstFree,TaRecBuf,2);
  PutRec(DatF,R,TaRecBuf);
  DatF.FirstFree := R; 
  DatF.NumberFree := DatF.NumberFree + 1;
end;

function FileLen(var DatF : DataFile) : Integer;
begin
  FileLen := DatF.NewRec;
end;

function UsedRecs(var DatF : DataFile) : Integer;
begin
  UsedRecs := DatF.NewRec - DatF.NumberFree - 1;
end;

procedure InitIndex;
var
  I : Integer;
begin
  for I := 1 to PageStackSize do
  begin
    TaPageStk[I].IndexFPtr := nil; 
    TaPageStk[I].Updated := false;
    TaPgMap[I] := I;
  end;
end;

procedure TaPack(var Page : TaPage; 
                     KeyL : Integer);
var
  I : Integer;
  P : array[0..MaxInt] of Byte absolute Page;
begin
  if KeyL <> MaxKeyLen then
    for I := 1 to PageSize do 
      Move(Page.ItemArray[I],P[(I - 1) * (KeyL + 5) + 3],KeyL + 5);
end;

procedure TaUnpack(var Page : TaPage; 
                       KeyL : Integer);
var
  I : Integer;
  P : array[0..MaxInt] of Byte absolute Page;
begin
  if KeyL <> MaxKeyLen then
    for I := PageSize downto 1 do 
      Move(P[(I - 1) * (KeyL + 5) + 3],Page.ItemArray[I],KeyL + 5);
end;

procedure MakeIndex(var IdxF   : IndexFile; 
                        FName  : TaStr14; 
                        KeyLen,
                        S      : Integer);
var
  K : Integer;
begin
  K := (KeyLen + 5)*PageSize + 3;
  MakeFile(IdxF.DataF,FName,K);
  IdxF.AllowDuplKeys := S <> 0; 
  IdxF.KeyL := KeyLen;
  IdxF.RR := 0; 
  IdxF.PP := 0;
end;

procedure OpenIndex(var IdxF   : IndexFile; 
                        FName  : TaStr14; 
                        KeyLen,
                        S      : Integer);
var
  K : Integer;
begin
  K := (KeyLen + 5) * PageSize + 3;
  OpenFile(IdxF.DataF,FName,K);
  IdxF.AllowDuplKeys := S <> 0; 
  IdxF.KeyL := KeyLen;
  IdxF.RR := IdxF.DataF.Int1; 
  IdxF.PP := 0;
end;

procedure CloseIndex(var IdxF : IndexFile);
var
  I : Integer;
begin
  for I := 1 to PageStackSize do 
    with TaPageStk[I] do
      if IndexFPtr = Ptr(Addr(IdxF)) then
      begin
        IndexFPtr := nil;
        if Updated then
        begin
          TaPack(Page,IdxF.KeyL);
          PutRec(IdxF.DataF,PageRef,Page);
        end;
      end;
  IdxF.DataF.Int1 := IdxF.RR; 
  CloseFile(IdxF.DataF);
end;

procedure TaLast(I : Integer);
var
  J,K : Integer;
begin
  J := 1; 
  while (TaPgMap[J] <> I) and (J < PageStackSize) do 
    J := J + 1;
  for K := J to PageStackSize - 1 do 
    TaPgMap[K] := TaPgMap[K + 1];
  TaPgMap[PageStackSize] := I;
end;

procedure TaGetPage(var IdxF  : IndexFile;
                        R     : Integer; 
                    var PgPtr : TaPagePtr);
var
  I,J,K : Integer;
  Found : Boolean;
begin
  Found := false;
  for J := 1 to PageStackSize do
    if not Found then
      with TaPageStk[J] do
        if (IndexFPtr = Ptr(Addr(IdxF))) and 
           (PageRef = R)                 then
  begin
    I := J; 
    Found := true;
  end;
  if not Found then
  begin
    I := TaPgMap[1];
    with TaPageStk[I] do
    begin
      if Updated then
      begin
        TaPack(Page,IndexFPtr^.KeyL);
        PutRec(IndexFPtr^.DataF,PageRef,Page);
      end;
      GetRec(IdxF.DataF,R,Page); 
      TaUnpack(Page,IdxF.KeyL);
      IndexFPtr := Ptr(Addr(IdxF)); 
      PageRef := R; 
      Updated := false;
    end;
  end;
  TaLast(I); 
  PgPtr := Ptr(Addr(TaPageStk[I]));
end;

procedure TaNewPage(var IdxF  : IndexFile;
                    var R     : Integer;
                    var PgPtr : TaPagePtr);
var
  I : Integer;
begin
  I := TaPgMap[1];
  with TaPageStk[I] do
  begin
    if Updated then
    begin
      TaPack(Page,IndexFPtr^.KeyL); 
      PutRec(IndexFPtr^.DataF,PageRef,Page);
    end;
    AddRec(IdxF.DataF,R,Page);
    IndexFPtr := Ptr(Addr(IdxF)); 
    PageRef := R; 
    Updated := false;
  end;
  TaLast(I); 
  PgPtr := Ptr(Addr(TaPageStk[I]));
end;

procedure TaUpdatePage(PgPtr : TaPagePtr);
var
  P : TaStackRecPtr absolute PgPtr;
begin
  P^.Updated := true;
end;

procedure TaReturnPage(var PgPtr : TaPagePtr);
var
  P : TaStackRecPtr absolute PgPtr;
begin
  with P^ do
  begin
    DeleteRec(IndexFPtr^.DataF,PageRef); 
    IndexFPtr := nil; 
    Updated := false;
  end;
end;

procedure TaXKey(var K            ;
                     KeyL : Integer);
var
  Key : TaKeyStr absolute K;
begin
  if Ord(Key[0]) > KeyL then Key[0] := Chr(KeyL);
end;

function TaCompKeys(var K1,
                        K2           ; 
                        DR1,
                        DR2 : Integer; 
                        Dup : Boolean ) : Integer;
var
  Key1 : TaKeyStr absolute K1;
  Key2 : TaKeyStr absolute K2;
begin
  if Key1 = Key2 then
    if Dup then 
      TaCompKeys := DR1 - DR2 
    else TaCompKeys := 0
  else
    if Key1 > Key2 then 
      TaCompKeys := 1
    else TaCompKeys :=  - 1;
end;

procedure ClearKey(var IdxF : IndexFile);
begin
  IdxF.PP := 0;
end;

