Contributor: SWAG SUPPORT TEAM        

Program Linked;

Type
  FileDescriptor =
    Object
      Fpt       : File;
      Name      : String[80];
      HeaderSize: Word;
      RecordSize: Word;
      RecordPtr : Pointer;
      SoftPut   : Boolean;
      IsOpen    : Boolean;
      CurRec    : LongInt;

      Constructor Init(Nam : String; Hdr : Word; Size : Word; Buff : Pointer;
Put : Boolean);
      Destructor  Done; Virtual;
      Procedure   OpenFile; Virtual;
      Procedure   CloseFile; Virtual;
      Procedure   GetRecord(Rec : LongInt);
      Procedure   PutRecord(Rec : LongInt);
    end;

  FileLable =
    Record
      Eof : LongInt;
      MRD : LongInt;
      Act : LongInt;
      Val : LongInt;
      Sync: LongInt;
    end;

  LabeledFile =
    Object(FileDescriptor)
      Header : FileLable;

      Constructor Init(Nam : String; Size : Word; Buff : Pointer; Put :
Boolean);
      Destructor  Done; Virtual;
      Procedure   OpenFile; Virtual;
      Procedure   CloseFile; Virtual;
      Procedure   WriteHeader;
      Procedure   ReadHeader;
      Procedure   AddRecord;
      Procedure   DelRecord(Rec : LongInt);
    end;

  DetailHeaderPtr = ^DetailHeader;
  DetailHeader =
    Record
      Master : LongInt;
      Prev   : LongInt;
      Next   : LongInt;
    end;

  MasterHeaderPtr = ^MasterHeader;
  MasterHeader =
    Record
      First  : LongInt;
      Last   : LongInt;
    end;

  DetailFileDetailPtr = ^DetailFileDetail;
  DetailFileDetail =
    Object(LabeledFile)
      Constructor Init(Nam : String; Size : Word; Buff : Pointer; Put :
Boolean);
      Procedure   LinkChain(MR, Last, Curr : LongInt);
      Procedure   DelinkChain(Rec : LongInt);
    end;

  DetailFileMaster =
    Object(LabeledFile)
      Constructor Init(Nam : String; Size : Word; Buff : Pointer; Put :
Boolean);
      Procedure   LinkDetail(DF : DetailFileDetailPtr);
      Procedure   DelinkDetail(DF : DetailFileDetailPtr; DR : LongInt);
      Procedure   GetFirst(DF : DetailFileDetailPtr);
      Procedure   GetLast(DF : DetailFileDetailPtr);
      Procedure   GetNext(DF : DetailFileDetailPtr);
      Procedure   GetPrev(DF : DetailFileDetailPtr);
    end;

{---------------------------------------------------------------------------}

Constructor FileDescriptor.Init(Nam : String; Hdr : Word; Size : Word; Buff :
                                Pointer; Put : Boolean);
  begin
    IsOpen := False;
    Name := Nam;
    HeaderSize := Hdr;
    RecordSize := Size;
    RecordPtr := Buff;
    SoftPut := Put;
    CurRec := -1;
  end;

Destructor  FileDescriptor.Done;
  begin
    if SoftPut and (CurRec <> -1) then
        PutRecord(CurRec);
    if IsOpen then
        CloseFile;
  end;

Procedure   FileDescriptor.OpenFile;
  begin
    if IsOpen then
        Exit;
    Assign(Fpt,Name);
    {$I-}
    Reset(Fpt,1);
    if IoResult <> 0 then
        ReWrite(Fpt,1);
    if IoResult = 0 then
        IsOpen := True;
    {$I+}
    CurRec := -1;
  end;

Procedure   FileDescriptor.CloseFile;
  begin
    if not IsOpen then
        Exit;
    {$I-}
    Close(Fpt);
    if IoResult = 0 then
        IsOpen := False;
    {$I+}
    CurRec := -1;
  end;

Procedure   FileDescriptor.GetRecord(Rec : LongInt);
  Var
    Result : Word;
  begin
    if not IsOpen then
        Exit;
    if CurRec = Rec then
        Exit;
    if SoftPut and (CurRec <> -1) then
        PutRecord(CurRec);
    {$I-}
    if Rec = 0 then
      begin
        Seek(Fpt,0);
        if IoResult = 0 then
          begin
            BlockRead(Fpt,RecordPtr^,HeaderSize,Result);
            if (Result <> HeaderSize) or (IoResult <> 0) then
                {Error Routine};
          end;
      end
    else
      begin
        Seek(Fpt,HeaderSize + (Rec - 1) * RecordSize);
        if IoResult = 0 then
          begin
            BlockRead(Fpt,RecordPtr^,RecordSize,Result);
            if (Result <> RecordSize) or (IoResult <> 0) then
                {Error Routine};
          end;
      end;
    {$I+}
    CurRec := Rec;
  end;

Procedure   FileDescriptor.PutRecord(Rec : LongInt);
  Var
    Result : Word;
  begin
    if not IsOpen then
        Exit;
    {$I-}
    if Rec = 0 then
      begin
        Seek(Fpt,0);
        if IoResult = 0 then
          begin
            BlockWrite(Fpt,RecordPtr^,HeaderSize,Result);
            if (Result <> HeaderSize) or (IoResult <> 0) then
                {Error Routine};
          end;
      end
    else
      begin
        Seek(Fpt,HeaderSize + (Rec - 1) * RecordSize);
        if IoResult = 0 then
          begin
            BlockWrite(Fpt,RecordPtr^,RecordSize,Result);
            if (Result <> RecordSize) or (IoResult <> 0) then
                {Error Routine};
          end;
      end;
    CurRec := Rec;
    {$I+}
  end;

{---------------------------------------------------------------------------}

Constructor LabeledFile.Init(Nam : String; Size : Word; Buff : Pointer; Put :
Boolean);
  begin
    if Size < 4 then
      begin
        WriteLN('Record size must be 4 or larger');
        Fail;
      end;
    FileDescriptor.Init(Nam,Sizeof(Header),Size,Buff,Put);
    Header.Eof := 0;
    Header.MRD := 0;
    Header.Act := 0;
    Header.Val := 0;
    Header.Sync:= 0;
  end;

Destructor LabeledFile.Done;
  begin
    CloseFile;
    FileDescriptor.Done;
  end;

Procedure LabeledFile.OpenFile;
  begin
    FileDescriptor.OpenFile;
    if IsOpen then
        ReadHeader;
  end;

Procedure LabeledFile.CloseFile;
  begin
    {$I-}
    if IsOpen then
      begin
        if SoftPut and (CurRec <> -1) then
            PutRecord(CurRec);
        Header.Val := 0;
        WriteHeader;
        CurRec := -1;
      end;
    FileDescriptor.CloseFile;
    {$I+}
  end;

Procedure LabeledFile.ReadHeader;
  Var
    Result : Word;
  begin
    {$I-}
    Seek(Fpt,0);
    if IoResult = 0 then
      begin
        BlockRead(Fpt,Header,HeaderSize,Result);
        if (Result <> HeaderSize) or (IoResult <> 0) then
            {Error Routine};
      end;
    {$I+}
  end;

Procedure LabeledFile.WriteHeader;
  Var
    Result : Word;
  begin
    {$I-}
    Seek(Fpt,0);
    if IoResult = 0 then
      begin
        BlockWrite(Fpt,Header,HeaderSize,Result);
        if (Result <> HeaderSize) or (IoResult <> 0) then
            {Error Routine};
      end;
    {$I+}
  end;

Procedure LabeledFile.AddRecord;
  Var
    TmpRec : Pointer;
    Result : Word;
    Next   : LongInt;
  begin
    {$I-}
    if Header.MRD <> 0 then
      begin
        GetMem(TmpRec,RecordSize);
        Seek(Fpt,HeaderSize + (Header.MRD - 1) * RecordSize);
        if IoResult = 0 then
          begin
            BlockRead(Fpt,TmpRec^,RecordSize,Result);
            if (Result <> RecordSize) or (IoResult <> 0) then
                {Error Routine};
            Next := LongInt(TmpRec^);
            PutRecord(Header.MRD);
            Header.MRD := Next;
            Header.Act := Header.Act + 1;
          end;
        FreeMem(TmpRec,RecordSize);
      end
    else
      begin
        PutRecord(Header.Eof);
        Header.Eof := Header.Eof + 1;
        Header.Act := Header.Act + 1;
      end;
    WriteHeader;
    {$I+}
  end;

Procedure LabeledFile.DelRecord(Rec : LongInt);
  Var
    TmpRec : Pointer;
    Result : Word;
  begin
    {$I-}
    GetMem(TmpRec,RecordSize);
    Seek(Fpt,HeaderSize + (Rec - 1) * RecordSize);
    if IoResult = 0 then
      begin
        BlockRead(Fpt,TmpRec^,RecordSize,Result);
        LongInt(TmpRec^) := Header.MRD;
        BlockWrite(Fpt,TmpRec^,RecordSize,Result);
        if (Result <> RecordSize) or (IoResult <> 0) then
           {Error Routine};
        Header.MRD := Rec;
        Header.Act := Header.Act - 1;
        WriteHeader;
      end;
    {$I+}
  end;

{---------------------------------------------------------------------------}

Constructor DetailFileDetail.Init(Nam : String; Size : Word; Buff : Pointer;
Put : Boolean);
  begin
    if Size < 12 then
      begin
        WriteLn('Detail File Records must be 12 Bytes or more');
        Fail;
      end;
    LabeledFile.Init(Nam,Size,Buff,Put);
  end;

Procedure   DetailFileDetail.LinkChain(MR, Last, Curr : LongInt);
  Var
    Hdr : DetailHeaderPtr;
  begin
    Hdr := RecordPtr;
    if Last <> 0 then
      begin
        GetRecord(Last);
        Hdr^.Next := Curr;
        PutRecord(Last);
      end;
    GetRecord(Curr);
    Hdr^.Prev := Last;
    Hdr^.Master := MR;
    Hdr^.Next := 0;
    PutRecord(Curr);
  end;

Procedure   DetailFileDetail.DelinkChain(Rec : LongInt);  Var
    Hdr : DetailHeaderPtr;
    Tmp : LongInt;
  begin
    Hdr := RecordPtr;
    GetRecord(Rec);
    if Hdr^.Next <> 0 then
      begin
        Tmp := Hdr^.Prev;
        GetRecord(Hdr^.Next);
        Hdr^.Prev := Tmp;
        PutRecord(CurRec);
        GetRecord(Rec);
      end;
    if Hdr^.Prev <> 0 then
      begin
        Tmp := Hdr^.Next;
        GetRecord(Hdr^.Prev);
        Hdr^.Next := Tmp;
        PutRecord(CurRec);
        GetRecord(Rec);
      end;
    Hdr^.Master := 0;
    Hdr^.Next := 0;
    Hdr^.Prev := 0;
    PutRecord(Rec);
  end;

{---------------------------------------------------------------------------}

Constructor DetailFileMaster.Init(Nam : String; Size : Word; Buff : Pointer;
Put : Boolean);
  begin
    if Size < 8 then
      begin
        WriteLn('Master File Records must be 8 Bytes or more');
        Fail;
      end;
    LabeledFile.Init(Nam,Size,Buff,Put);
  end;

Procedure   DetailFileMaster.LinkDetail(DF : DetailFileDetailPtr);
  Var
    Hdr : MasterHeaderPtr;
  begin
    Hdr := RecordPtr;
    DF^.AddRecord;
    DF^.LinkChain(CurRec,Hdr^.Last,DF^.CurRec);
    Hdr^.Last := DF^.CurRec;
    if Hdr^.First = 0 then Hdr^.First := DF^.CurRec;
    PutRecord(CurRec);
  end;

Procedure   DetailFileMaster.DelinkDetail(DF : DetailFileDetailPtr; DR :
LongInt);
  Var
    Hdr : MasterHeaderPtr;
  begin
    Hdr := RecordPtr;
    DF^.GetRecord(DR);
    if Hdr^.Last = DR then
        Hdr^.Last := DetailHeader(DF^.RecordPtr^).Prev;
    if Hdr^.First = DR then
        Hdr^.First := DetailHeader(DF^.RecordPtr^).Next;
    DF^.DelinkChain(DR);
    PutRecord(CurRec);
  end;

Procedure   DetailFileMaster.GetFirst(DF : DetailFileDetailPtr);
  Var
    Hdr : MasterHeaderPtr;
  begin
    Hdr := RecordPtr;
    if Hdr^.First = 0 then
      begin
        FillChar(DF^.RecordPtr^,DF^.RecordSize,#0);
        DF^.CurRec := -1;
        Exit;
      end;
    DF^.GetRecord(Hdr^.First);
  end;

Procedure   DetailFileMaster.GetLast(DF : DetailFileDetailPtr);
  Var
    Hdr : MasterHeaderPtr;
  begin
    Hdr := RecordPtr;
    if Hdr^.Last = 0 then
      begin
        FillChar(DF^.RecordPtr^,DF^.RecordSize,#0);
        DF^.CurRec := -1;
        Exit;
      end;
    DF^.GetRecord(Hdr^.Last);
  end;

Procedure   DetailFileMaster.GetNext(DF : DetailFileDetailPtr);
  Var
    Hdr : DetailHeaderPtr;
  begin
    Hdr := DF^.RecordPtr;
    if Hdr^.Next = 0 then
      begin
        FillChar(DF^.RecordPtr^,DF^.RecordSize,#0);
        DF^.CurRec := -1;
        Exit;
      end;
    DF^.GetRecord(Hdr^.Next);
  end;

Procedure   DetailFileMaster.GetPrev(DF : DetailFileDetailPtr);
  Var
    Hdr : DetailHeaderPtr;
  begin
    Hdr := DF^.RecordPtr;
    if Hdr^.Prev = 0 then
      begin
        FillChar(DF^.RecordPtr^,DF^.RecordSize,#0);
        DF^.CurRec := -1;
        Exit;
      end;
    DF^.GetRecord(Hdr^.Prev);
  end;

{---------------------------------------------------------------------------}

begin
end.