[comp.lang.modula2] Modula-2 interface to Btrieve

mbenveni@tango.irisa.fr (Marc Benveniste) (12/11/89)

%%To: mbenveni@irisa.irisa.fr
%%Subject: Re: TopSpeed Modula-2 for Novell's Btrieve
%%Newsgroups: comp.lang.modula2
%%In-Reply-To: <1799@irisa.irisa.fr>
%%References: <2746@ethz.UUCP>
%%Organization: Pacific Systems Group, Portland Oregon US
%%
%%In article <1799@irisa.irisa.fr> you write:
%%>I am sorry if I interfere in some deal, but I happen to have
%%>a Modula-2 interface to Btrieve which I am willing to share
%%>for free. I wonder if I should post it or mail it directly 
%%>to the one who asked for it. 
%%
%%Post it please. 

So here it goes. 3 modules are necessary: a low level memory copy
module (copymem.{def,mod}), a generic lists module (lists.{def,mod})
and btrieve.{def,mod}. I only ask users NOT to remove the comments
placed at the beginning of each module. I don't promess these modules
are error free but at least I am sure that CopyMemory and Lists
are OK. A great deal of Btrieve has been tested and no apparent
error has arised. I used it in a single user environment so I did
not have the chance to test function calls related to locking and
multiuser applications. 

The btrieve file is defined as an opaque type in order to provide
some protection. Comments in Btrieve.def are not terminated but
with SoftCraft user's manual and source code it shouldn't be too
difficult to finish a proper module documentation.

Marc Benveniste                   mbenveni@irisa.irisa.fr
(Ph.D. student)
IRISA-INRIA
Campus de Beaulieu
35042 Rennes
FRANCE


#################### SOURCES: DEFINITION MODULES ####################

DEFINITION MODULE CopyMem;
(*==========================================================================

  Global Description :  This module provides procedures to copy 
                        unstructured data. Overlapping Address ranges
                        currently not implemented.   
  ________________________________________________________________________

                                  FirstEdit: 20/05/87
                                  LastEdit :  2/07/87
  ________________________________________________________________________

               Author : Marc Benveniste                                      
               System : Logitech MODULA-2/86 V. 2.05
     Developped while  
        working at    : IIMAS-UNAM,
                        Apdo. Postal 20-726, 
                        Mexico D.F., 01000
                        MEXICO  
 ===========================================================================*)


        FROM SYSTEM IMPORT
        (* Type *)         ADDRESS;
        
        
  TYPE
    ByteRange = [0 .. 32*1024-2];
    WordRange = [0 .. 16*1024-2];
    
    
  PROCEDURE CopyByte(From, To: ADDRESS; ByteNum: ByteRange);
(*_________________________________________________________________________

  In         ---> From = Address of source.
                  To   = Address of destination.
                  ByteNum = Number of bytes to copy.
  SideEffect ---> Bytes at addresses From+i are copied to addresses To+i
                  for i=0 to ByteNum-1. Overlapping addresses range are
                  NOT taken care of. Address overflow is NOT checked for.
  _________________________________________________________________________*)
  
  PROCEDURE CopyWord(From, To: ADDRESS; WordNum: WordRange);
(*_________________________________________________________________________

  In         ---> From = Address of source.
                  To   = Address of destination.
                  WordNum = Number of words to copy.
  SideEffect ---> Words at addresses From+2*i are copied to addresses To+2*i
                  for i=0 to WordNum-1. Overlapping addresses range are
                  NOT taken care of. Address overflow is NOT checked for.
  _________________________________________________________________________*)
 
  PROCEDURE FillByte(B: CHAR; To: ADDRESS; ByteNum: ByteRange);
(*_________________________________________________________________________

  In         ---> B = Byte to fill with.
                  To = Address of first byte of area to fill.
                  ByteNum = Number of bytes to write.
  SideEffect ---> Bytes at addresses To+i are overwritten with B for 
                  i=0 to ByteNum. Address overflow is NOT checked for.
  _________________________________________________________________________*)

  PROCEDURE FillWord(C: CARDINAL; To: ADDRESS; WordNum: WordRange);
(*_________________________________________________________________________

  In         ---> C = Word to fill with.
                  To = Address of first word of area to fill.
                  WordNum = Number of words to write.
  SideEffect ---> Words at addresses To+2*i are overwritten with C for 
                  i=0 to WordNum. Address overflow is NOT checked for.
  _________________________________________________________________________*)

END CopyMem.  

DEFINITION MODULE Lists;
(*==========================================================================

  Global Description :  This module provides generic linked lists
  
      A list is a couple ( L, IO ) where L is the list structure and IO is 
  a variable of the type of the elements that L can handle. When creating 
  the structure a variable must be assigned to it. The role of that variable
  is double:   
             1)- It provides the base type of the list structure,      
             2)- it is the port to the list, insuring type checking.
                 
     Operations on the list structure IMPLICITELY use the IO variable. 
  That's why these operations are described as side effects. A single IO 
  variable may be the port to many lists as long as all lists are of the 
  same base type.   
                               _______________
                               __: WARNING :__
                                                                    
     The port of a list structure MUST have the same LIFELENGTH and SCOPE 
  as the structure itself.                      
      
  ________________________________________________________________________

                                  FirstEdit: 23/03/87
                                  LastEdit :  2/07/87
  ________________________________________________________________________

               Author : Marc Benveniste                                      
               System : Logitech MODULA-2/86 V. 2.05
     Developped while  
        working at    : IIMAS-UNAM,
                        Apdo. Postal 20-726, 
                        Mexico D.F., 01000
                        MEXICO  
 ===========================================================================*)

    FROM SYSTEM IMPORT 
    (* Type *)         ADDRESS;

    EXPORT QUALIFIED
    (* Type *)         List,
    (* Var *)          ListError,
    (* Procedure *)    NewList, IsEmpty, First, Remove, 
                       Insert, Next, Dispose;

    TYPE
      List;

    VAR
      ListError : CARDINAL;
      
 (*______: Possible error codes :__________________________________________
                                                                    
          0 -------->  No error ocurred,
          1 -------->  General failure,                            
          2 -------->  Out of memory,                               
          3 -------->  Tried to REMOVE or FIRST on an empty list or position,     
          4 -------->  Tried to DISPOSE of a non-empty list,
          5 -------->  No next element available.
  _________________________________________________________________________*)
  
   
  PROCEDURE NewList(IOPort: ADDRESS; ElemTypeSize: CARDINAL): List; 
 (*______________________________________________________________________

       NewList creates, if possible, a list ready to handle a given TYPE 
   of objects. The only way to interact with the list is through it's 
   port. The port must be a VAR  with the same life length and scope as 
   the new list. 
   
   In --> IO ::=  The address of the port variable of list.
          TypeSize ::=  Size of the TYPE of the list's port.
   Out -> An empty list ready to handle elements of size TypeSize 
          through port at address IO if no error ocurred.
  
   SideEffect ---> ListError is set to 0 or 2.
   ______________________________________________________________________*)


  PROCEDURE IsEmpty(VAR L : List): BOOLEAN;
(*_________________________________________________________________________

     IsEmpty is True if L is an empty list; False otherwise.
  
  In  ----> L ::= A list obtained by means of a successful NewList call.
  Out ----> L ::= Untouched
            IsEmpty ::= TRUE if L is empty; FALSE otherwise.
            
  SideEffect ---> ListError is set to 0 or 1.
  _________________________________________________________________________*)


  PROCEDURE First(VAR L: List);
(*_________________________________________________________________________

      First places in the port assigned to L the head element of the list
  structure L, and makes it the current element. ListError may be 0,1 or 3.

  In  ----> L ::= A list obtained by means of a successful New call.
  Out ----> L ::= The Head  of L has become the current element
            
  SideEffect ---> A copy of the Head of L is in its port.
                  ListError is set to 0,1 or 3.
  _________________________________________________________________________*)

  PROCEDURE Insert(VAR L: List);
(*_________________________________________________________________________

      Insert makes a copy of the element in the port assigned to L and 
  inserts it in the list structure immediately before the current element;
  The inserted element becomes the current one. If L is empty then the copy 
  becomes the head and current element. ListError may be 0,1 or 2.
  
  In         ---> L = Any legal List.
  Out        ---> L = The input list with a copy of it's port's content 
                      inserted in front of it's current element.
  SideEffect ---> ListError is set according to the result of the operation.
                  The current element of the list is the inserted one if
                  ListError is 0.
  _________________________________________________________________________*)

 
  PROCEDURE Remove(VAR L: List);
(*_________________________________________________________________________

     The current element is removed from the list. Next element becomes the
  current one. A copy of that element is in the port associated to L.
  ListError may be 0,1,3 or 5.
  
  In         ---> L = Any legal List.
  Out        ---> L = The input list with it's current element removed.
  SideEffect ---> ListError is set to 0,1,3 or 5.
                  The List's port contains a copy of the current element,
                  if any.
  _________________________________________________________________________*)

 
  PROCEDURE Next(VAR L: List);
(*_________________________________________________________________________

      A copy of the next element of the current one is available in the
  port associated to L. That element also becomes the current one.
  ListError may be 0,1 or 5.
  
  In         ---> L = Any legal List.
  Out        ---> L = The input list.
  SideEffect ---> ListError is set to 0,1 or 5.
                  A copy of the next element, if any, of the current one 
                  is in the port. That one also becomes the current one. 
  _________________________________________________________________________*)
  

  PROCEDURE Dispose(VAR L: List);
(*_________________________________________________________________________

      If L is empty, Dispose frees the allocated memory and L becomes an 
  invalid data. ListError may be 0,1 or 4.
  
  In         ---> L = Any legal empty List.
  Out        ---> L = Meaningless data if ListError = 0; same data otherwise.
  SideEffect ---> ListError is set to 0,1 or 4.
_________________________________________________________________________*)


END Lists.


DEFINITION MODULE Btrieve;
(*==========================================================================

  Global Description : Modula-2/86 interface to Btrieve V. 4.04   
  ________________________________________________________________________

                                  FirstEdit: 17/03/87
                                  LastEdit : 25/09/87
  ________________________________________________________________________

               Author : Marc Benveniste                                      
               System : Logitech MODULA-2/86 V. 2.05                                  
      Developped while  
        working at    : IIMAS-UNAM,
                        Apdo. Postal 20-726, 
                        Mexico D.F., 01000
                        MEXICO  
===========================================================================*)
   
   FROM SYSTEM IMPORT
   (* Type *)          ADDRESS;
   
   FROM Lists IMPORT
   (* Type *)          List;


   EXPORT QUALIFIED
   (* Constants *)                     MaxPageSizeMult,MinRecLgth,MaxRecLgth,
                                       MaxKeyNum,MaxKeyLgth,

   (* Type *)                          BtrvFile, BtrvFilePtr,
                                       OrderStruct, KeyNum, 
                                       RelationalOp, OpeningMode, PhysRecAdr,
                                       DataInfo, LogicalDrive, OwnerName, 
                                       LongCard, RecsPerKey,
                                       
   (* Var *)                           Status, FileStruct, StructList,

(* Procedures to manage System *)      GetDirectory, Reset, SetDirectory,
                                       Stop, Version,

(* Procedures to manage Files *)       ClearOwner, Close, Create,
                                       Extend, Open, SetOwner, Stat,

(* Procedures to manage Operations *)  AbortTransaction, BeginTransaction,
                                       ClearLocking, EndTransaction,
                                       SetNoWaitLocking, SetWaitLocking,
                                       Unlock, 

(* Procedures to operate on Records *) Delete, GetDirect, GetKey, GetRecord, 
                                       GetPosition,
                                       Insert, StepDirect, Update;



  CONST

      FileSpec        = TRUE;
      KeySpec         = FALSE;
      PageSizeUnit    = 512;
      MinOverhead     = 12;
      MaxPageSizeMult = 8;
      MaxPageSize     = MaxPageSizeMult*PageSizeUnit;
      MaxKeyLgth      = 255;
      MinRecLgth      = 8;
      MaxRecLgth      = MaxPageSize - MinOverhead;
      MaxKeyNum       = 24;

  TYPE
      BtrvFile ;
      BtrvFilePtr  = POINTER TO BtrvFile;
      PhysRecAdr ;

      OwnerName    = ARRAY[0 .. 8] OF CHAR;

      LongCard     = RECORD
                       Low, High : CARDINAL
                     END;

      KeyNum       = [0 .. MaxKeyNum - 1];

      RecsPerKey   = ARRAY KeyNum OF LongCard;

      DataInfo     = RECORD
                       DataBufAdr  : ADDRESS;
                       DataBufLth  : CARDINAL;
                       KeyBufAdr   : ADDRESS;
                       KeyBufLth   : [1 .. MaxKeyLgth];
                       Key         : POINTER TO KeyNum
                     END; (* record *)

      LogicalDrive = (Default,A,B,C,D,E,F,G);


      OrderStruct  = RECORD
                       CASE Exists: BOOLEAN OF
                       TRUE : Name : ARRAY[0 .. 7] OF CHAR;
                              Def  : ARRAY[0 .. 255] OF CHAR
                       END (* case *)
                     END; (* record *)
                     
      RelationalOp = (Equal,Next,Previous,Greater,GrOrEq,
                      LessThan,LTOrEq,Lowest,Highest);

      OpeningMode  = (Verify,ReadOnly,Accelerated,Normal);

  VAR
      Status     : CARDINAL; 
  (*_______________________________________________________________________*
   *                                                                       *
   * 0   ---> Operation was Successfull,                                   *
   * 1   ---> Out of Memory, used to be invalid operation code.            *
   *                                                                       *
   *            Consult Appendix B of the Btrieve Reference Manual         *
   *                         for the other Status codes.                   *
   *_______________________________________________________________________*)
   
       FileStruct : RECORD
                      CASE Spec : BOOLEAN OF
                        FileSpec : 
                          RecordLgth   : [MinRecLgth .. MaxRecLgth];
                          PageSizeMult : [1 .. MaxPageSizeMult];
                          IndexesNum   : [1 .. MaxKeyNum];
                          VarLgthRec   : BOOLEAN |
                        KeySpec  : 
                          KeyPos       : [0 .. MaxRecLgth - 1];
                          KeyLgth      : [1 .. MaxKeyLgth];
                          Duplicates,
                          Modifiable,
                          Segmented    : BOOLEAN ;
                          CASE NullKey : BOOLEAN OF
                            TRUE : NullChar : CHAR;
                          END; (* case *)
                          CASE Binary : BOOLEAN OF
                            FALSE : AltColSeq : BOOLEAN;
                          END  (* case *)  
                      END (* case *)
                    END; (* record *)
       
       StructList : List;
       
  (* FileStruct and StructList form a List pair as defined in the Definition
     Module Lists.def. The operations allowed on them by the mentioned Module
     should be the only ones used. This pair is implicitely used with the 
     Create and Stat operations. StructList is an empty linked list structure
     ready to handle objects of the same type of FileStruct. The communication
     with the list structure can only be obtained using the variable 
     FileStruct as a communication port as stated in the Lists Module. 
     Please refer the that Module for more information.                     *)
  
  
  
  (*_______________________________________________________________________
  
       P R O C E D U R E S   T O   M A N A G E   T H E   S Y S T E M 
    _______________________________________________________________________*)
  
  
  
  PROCEDURE GetDirectory(Drv: LogicalDrive;VAR DirPath: ARRAY OF CHAR);
  (*_______________________________________________________________________
                                                                           
     In          ----> DirPath = Array of char at least 64 chars long.    
     Out         ----> DirPath = Full path name terminated by a 00h.      
     Side Effect ----> 
    _______________________________________________________________________*)
  
  PROCEDURE Reset(OwnStation: BOOLEAN; VAR OtherInfo: ARRAY OF CHAR);

  PROCEDURE SetDirectory(DirPath: ARRAY OF CHAR);

  PROCEDURE Stop;

  PROCEDURE Version(VAR VerNum,RevNum: CARDINAL; VAR Slash: CHAR);
   
 (*_______________________________________________________________________
  
       P R O C E D U R E S   T O   M A N A G E   T H E   F I L E S 
    _______________________________________________________________________*)
 
 
  PROCEDURE ClearOwner(File : BtrvFile);

  PROCEDURE Close(VAR File : BtrvFile);

  PROCEDURE Create(FileName: ARRAY OF CHAR; VAR AltColSeq: OrderStruct);
  (*_______________________________________________________________________
                                                                           
     In          ----> FileName = Any legal MS-DOS file name ending with  
                                   a 20h or 00h. 
     Out         ---->          
     Side Effect ----> Uses implicitely StructList. If it is empty, Status
                       will be set to 25 and no action will be performed.
    _______________________________________________________________________*)

  PROCEDURE Extend(File: BtrvFile; FileName: ARRAY OF CHAR; Now: BOOLEAN);
  (*_______________________________________________________________________
                                                                           
     In          ----> FileName = Any legal MS-DOS file name ending it    
                                   with a 20h or 00h.                      
     Out         ----> 
     Side Effect ----> 
    _______________________________________________________________________*)

  PROCEDURE Open(FileName: ARRAY OF CHAR; Owner: OwnerName;
                                          Mode : OpeningMode ): BtrvFile;

  PROCEDURE SetOwner(File: BtrvFile;Owner: OwnerName;
                                    AnyAccess, Encryption:BOOLEAN );

  PROCEDURE Stat(File: BtrvFile; VAR RecNum: LongCard;VAR Freq: RecsPerKey;
                 VAR AltColSeq: OrderStruct; VAR ExtFileName: ARRAY OF CHAR);
  (*_______________________________________________________________________
                                                                           
     In          ----> ExtFileName = Array of char at least 64 chars long.
     Out         ----> ExtFileName = Full name, terminated by a 00h, of   
                                      the file to which an Extend was      
                                      previously performed; 00h if no one. 
     Side Effect ----> Uses implicitely StructList.
    _______________________________________________________________________*)
   
 
 (*_______________________________________________________________________
  
       P R O C E D U R E S   T O   M A N A G E   O P E R A T I O N S     
    _______________________________________________________________________*)  
    
  
  PROCEDURE AbortTransaction ;
  
  PROCEDURE BeginTransaction ;
   
  PROCEDURE ClearLocking;

  PROCEDURE EndTransaction;

  PROCEDURE SetNoWaitLocking;

  PROCEDURE SetWaitLocking;

  PROCEDURE Unlock(File: BtrvFile);

 (*_______________________________________________________________________
  
      P R O C E D U R E S   T O   O P E R A T E   O N    R E C O R D S     
    _______________________________________________________________________*)

  
  PROCEDURE Delete(File: BtrvFile;VAR Data: DataInfo);

  PROCEDURE GetDirect(File: BtrvFile; RecAdr : PhysRecAdr;VAR Data: DataInfo);

  PROCEDURE GetRecord(File: BtrvFile;VAR Data: DataInfo; RelOp: RelationalOp);

  PROCEDURE GetKey(File: BtrvFile;VAR Data: DataInfo; RelOp: RelationalOp);

  PROCEDURE GetPosition(File: BtrvFile): PhysRecAdr;

  PROCEDURE Insert(File: BtrvFile; Data: DataInfo);

  PROCEDURE StepDirect(File: BtrvFile;VAR Data: DataInfo);

  PROCEDURE Update(File: BtrvFile; Data: DataInfo);

END Btrieve.

#################### SOURCES: IMPLEMENTATION MODULES ####################

IMPLEMENTATION MODULE CopyMem;
(*==========================================================================

  Global Description :  This module implements procedures to copy 
                        unstructured data.    
  ________________________________________________________________________

                                  FirstEdit: 20/05/87
                                  LastEdit : 26/06/87
  ________________________________________________________________________

               Author : Marc Benveniste                                      
               System : Logitech MODULA-2/86 V. 2.05
     Developped while  
        working at    : IIMAS-UNAM,
                        Apdo. Postal 20-726, 
                        Mexico D.F., 01000
                        MEXICO  
 ===========================================================================*)
 
 
        FROM SYSTEM IMPORT
        (* Type *)         ADDRESS;
        
        
  PROCEDURE CopyByte(From, To: ADDRESS; ByteNum: ByteRange);
    VAR
      Src,Dst : POINTER TO ARRAY ByteRange OF CHAR;
      i       : CARDINAL;
  BEGIN
    Src := From;
    Dst := To;
    IF ByteNum > 0 
      THEN
        FOR i := 0 TO ByteNum - 1 DO
          Dst^[i] := Src^[i]
        END (* for *)
    END (* if *)
  END CopyByte;
  
  PROCEDURE CopyWord(From, To: ADDRESS; WordNum: WordRange);
     VAR
      Src,Dst : POINTER TO ARRAY WordRange OF CARDINAL;
      i       : CARDINAL;
  BEGIN
    Src := From;
    Dst := To;
    IF WordNum > 0 
      THEN
        FOR i := 0 TO WordNum - 1 DO
          Dst^[i] := Src^[i]
        END (* for *)
    END (* if *)
  END CopyWord;
  
  PROCEDURE FillByte(B: CHAR; To: ADDRESS; ByteNum: ByteRange);
    VAR
      Dst : POINTER TO ARRAY ByteRange OF CHAR;
      i   : CARDINAL;
  BEGIN
    Dst := To;
    IF ByteNum > 0 
      THEN
        FOR i := 0 TO ByteNum - 1 DO
          Dst^[i] := B
        END (* for *)
    END (* if *)
  END FillByte;

  PROCEDURE FillWord(C: CARDINAL; To: ADDRESS; WordNum: WordRange);
    VAR
      Dst : POINTER TO ARRAY WordRange OF CARDINAL;
      i   : CARDINAL;
  BEGIN
    Dst := To;
    IF WordNum > 0
      THEN
        FOR i := 0 TO WordNum - 1 DO
          Dst^[i] := C
        END (* for *)
    END (* if *)
  END FillWord;

  
END CopyMem.

IMPLEMENTATION MODULE Lists;
(*==========================================================================

  Global Description :  This module implements generic linked lists
  ________________________________________________________________________

                                  FirstEdit: 18/05/87
                                  LastEdit : 15/01/88
  ________________________________________________________________________

               Author : Marc Benveniste                                      
               System : Logitech MODULA-2/86 V. 2.05
     Developped while  
        working at    : IIMAS-UNAM,
                        Apdo. Postal 20-726, 
                        Mexico D.F., 01000
                        MEXICO  
 ===========================================================================*)


  FROM SYSTEM IMPORT  
    (* Type *)         ADDRESS, WORD,
    (* Procedure *)    TSIZE;
   
  FROM Storage IMPORT 
    (* Procedure *)    ALLOCATE, DEALLOCATE, Available;
    
  FROM CopyMem IMPORT
    (* Procedure *)    CopyByte, CopyWord;


  TYPE
    List       = POINTER TO Descriptor;
    Link       = POINTER TO Node;
    Descriptor = RECORD
                   IOBuffer    : ADDRESS;   (* Port to List *)
                   TSize       : CARDINAL;  (* Size of Elements *)
                   WordUnits   : BOOLEAN;   (* T if Word; F if Bytes *)
                   Head,PreCur,             (* Head, Precurrent & Current *)
                   Current     : Link       (* elements of List *)
                 END;
    Node       = RECORD
                   Elem : ADDRESS;          (* Ptr to Copy of Elem *)
                   Next : Link 
                 END;
                 

  PROCEDURE NewList(IOPort: ADDRESS; ElemTypeSize: CARDINAL): List;
    VAR
      L : List; 
      A : BITSET;
  BEGIN
    IF Available(TSIZE(Descriptor))
      THEN
        ListError := 0;
        A := BITSET(ElemTypeSize);
        NEW(L);
        WITH L^ DO
          IOBuffer := IOPort;
          IF (0 IN A)
           THEN TSize := ElemTypeSize; WordUnits := FALSE
           ELSE TSize := ElemTypeSize DIV 2; WordUnits := TRUE
          END;
          Head     := NIL;
          PreCur   := NIL;
          Current  := NIL
        END; (* with *)
        RETURN L
    END; (* if *)
    ListError := 2;
    RETURN NIL
  END NewList;
  
  PROCEDURE IsEmpty(VAR L : List): BOOLEAN;
  BEGIN
    IF L = NIL
      THEN
        ListError := 1;
        RETURN FALSE
      ELSE
        ListError := 0;
        RETURN (L^.Head = NIL)
    END (* if *)
  END IsEmpty;
  
  PROCEDURE First(VAR L: List);
  BEGIN
    ListError := 0;
    IF    L = NIL
      THEN ListError := 1
    ELSIF L^.Head = NIL
      THEN ListError := 3
    ELSIF L^.WordUnits
      THEN CopyWord(L^.Head^.Elem,L^.IOBuffer,L^.TSize)
      ELSE CopyByte(L^.Head^.Elem,L^.IOBuffer,L^.TSize)
    END; (*if*)
    IF ListError = 0
     THEN
      WITH L^ DO
        Current := Head;
        PreCur  := Head
      END (* with *)
    END (* if *)
  END First;
  
  PROCEDURE Insert(VAR L: List);
    VAR
      p : Link;
  BEGIN
    ListError := 0;
    IF    L = NIL
      THEN ListError := 1;
    ELSIF L^.WordUnits & Available(TSIZE(Node) + L^.TSize*2)
      THEN 
        NEW(p);
        ALLOCATE(p^.Elem,L^.TSize*2);
        CopyWord(L^.IOBuffer,p^.Elem,L^.TSize)
    ELSIF NOT(L^.WordUnits) & Available(TSIZE(Node) + L^.TSize)
      THEN 
        NEW(p);
        ALLOCATE(p^.Elem,L^.TSize);
        CopyByte(L^.IOBuffer,p^.Elem,L^.TSize)
      ELSE
        ListError := 2
    END; (*if*)
    IF ListError = 0
      THEN
        IF L^.Head = L^.Current
          THEN
            WITH L^ DO
              Current := p;
              Current^.Next := Head;
              Head := Current;
              PreCur := Head
            END (* with *)
          ELSE
            p^.Next := L^.Current;
            L^.Current := p;
            L^.PreCur^.Next := L^.Current
        END (* if *)
    END (* if *)
  END Insert;
  
  PROCEDURE Remove(VAR L: List);
    VAR
      p : Link;
  BEGIN
    IF    L = NIL
      THEN ListError := 1
    ELSIF L^.Current = NIL
      THEN ListError := 3
      ELSE
        p := L^.Current;
        WITH L^ DO
          IF Head = Current 
            THEN 
              Head := Current^.Next;
              Current := Head;
              PreCur := Head
            ELSE
              Current := Current^.Next;
              PreCur^.Next := Current
          END (* if *)
        END; (* with *)
        IF L^.Current = NIL
          THEN
            ListError := 5
          ELSE
            ListError := 0;
            WITH L^ DO
             IF WordUnits
              THEN CopyWord(Current^.Elem,IOBuffer,TSize)
              ELSE CopyByte(Current^.Elem,IOBuffer,TSize)
             END
            END
        END; (* if *)
        IF L^.WordUnits
         THEN DEALLOCATE(p^.Elem,L^.TSize*2)
         ELSE DEALLOCATE(p^.Elem,L^.TSize)
        END;
        DISPOSE(p)
    END (* if *)
  END Remove;
  
  PROCEDURE Next(VAR L: List);
  BEGIN
    IF    L = NIL
      THEN ListError := 1
    ELSIF (L^.Current = NIL) OR (L^.Current^.Next = NIL)
      THEN ListError := 5
      ELSE
        ListError := 0;
        WITH L^ DO
          PreCur  := Current;
          Current := Current^.Next;          
          IF WordUnits
           THEN CopyWord(Current^.Elem,IOBuffer,TSize)
           ELSE CopyByte(Current^.Elem,IOBuffer,TSize)
          END
        END (* with *)
    END (* if *) 
  END Next;
  
  PROCEDURE Dispose(VAR L: List);
  BEGIN
    IF    L = NIL
      THEN ListError := 1
    ELSIF L^.Head # NIL
      THEN ListError := 4
      ELSE
        ListError := 0;
        DISPOSE(L);
        L := NIL
    END (* if *)
  END Dispose;

BEGIN
  ListError := 0
END Lists.

IMPLEMENTATION MODULE Btrieve;
(*==========================================================================

  Global Description : This module implements the MODULA-2/86 interface
                       to SoftCraft Btrieve version 4.04. 
  ________________________________________________________________________

                                  FirstEdit: 17/03/87
                                  LastEdit : 30/09/87
  ________________________________________________________________________

               Author : Marc Benveniste                                      
               System : Logitech MODULA-2/86 V. 2.05                                  
     Developped while  
        working at    : IIMAS-UNAM,
                        Apdo. Postal 20-726, 
                        Mexico D.F., 01000
                        MEXICO  
 ===========================================================================*)


     FROM SYSTEM IMPORT 
     (* Type *)       ADDRESS,BYTE,WORD,
     (* Procedure *)  CODE,SWI,SETREG,GETREG,ADR,TSIZE,SIZE,
     (* Const *)      AX,BX,DX,DS;

     FROM Storage IMPORT
     (* Procedure *)  Available, ALLOCATE, DEALLOCATE;
     
     FROM CopyMem IMPORT
     (* Procedure *)  CopyWord;
     
     IMPORT Lists;
     
     FROM Lists IMPORT
     (* Type *)       List,
     (* Var *)        ListError,
     (* Procedure *)  NewList, IsEmpty, First, Remove, Dispose;

CONST                               
     Mod2Id = 1111h;                    (* Modula-2 Language id*)
     VarId  = 6176h;                    (* Variable Length records Id -'va' *)
     MultiFunction = 0abh * 256;
     MultiLoad = MultiFunction + 4dh;
     ProcIdGiven = 2;
     BMultiParam = ProcIdGiven + MultiFunction;

TYPE
    BtrvFile     = POINTER TO PosBlock ;
    PosBlock     = ARRAY[0 .. 127] OF CHAR;

    PhysRecAdr   = ADDRESS;

    BtrvParams   = RECORD
                     BufAdr     : ADDRESS;
                     BufLth     : CARDINAL;
                     CurAdr     : ADDRESS;
                     FCBAdr     : ADDRESS;
                     FunCode    : CARDINAL;
                     KeyAdr     : ADDRESS;
                     KeyLth     : BYTE;
                     UserKey    : BYTE;
                     StatAdr    : ADDRESS;
                     IntFaceId  : INTEGER;
                   END ;

    CallType      = PROCEDURE(ADDRESS);

    LockType      = (NoLock,Wait,NoWait);

VAR
   ProcId         : INTEGER;
   LockStat       : LockType;
   Param          : BtrvParams ;
   ParamAdr       : ADDRESS;
   BtrvCallProc   : CallType;
   FileRecordSize : CARDINAL;


         (*  Btrieve Call Interface *) 

PROCEDURE SingleUser(A: ADDRESS);
BEGIN
    CODE(1eh,16h,06h,55h); (* PUSH DS,SS,ES,BP *)
    SETREG(DX,A.OFFSET);
    SETREG(DS,A.SEGMENT);
    SWI(07bh);
    CODE(5dh,07h,17h,1fh); (* POP BP,ES,SS,DS *)
END SingleUser;

PROCEDURE MultiUser(A: ADDRESS);
 VAR
     ProcIdAux     : INTEGER;
     CallProcessed : CARDINAL;
     AXReg         : CARDINAL;
BEGIN
    AXReg := BMultiParam;
    ProcIdAux := ProcId;
    REPEAT
      CODE(1eh,16h,06h,55h); (* PUSH DS,SS,ES,BP *)
      SETREG(AX,AXReg);
      SETREG(BX,ProcIdAux);
      SETREG(DX,A.OFFSET);
      SETREG(DS,A.SEGMENT);
      SWI(02fh);
      CODE(5dh,07h,17h,1fh); (* POP BP,ES,SS,DS *)      
      GETREG(AX,CallProcessed);
      IF (CallProcessed # 0) 
        THEN SETREG(AX,0200h);
             SWI(07fh);
      END;
    UNTIL (CallProcessed = 0);
END MultiUser;

PROCEDURE Dummy(A : ADDRESS);
BEGIN
    Status := 20
END Dummy;

                (* Procedure for general Initialization *)


PROCEDURE Init;
 CONST
     MSDOSCall      = 21h;
     GetIntrVect    = 357bh;
     GetDOSVer      = 3000h;
     BtrvInstalled  = 0033h;
     ProcIdNotGiven = 1;
 VAR
     A             : ADDRESS;
     AXReg         : CARDINAL;
     ALReg         : BYTE;
     ProcIdAux     : INTEGER;
     CallProcessed : CARDINAL;
BEGIN
    Status := 0;
    ProcId := 0;
    LockStat := NoLock;
    ParamAdr := ADR(Param);
    FileRecordSize := TSIZE(PosBlock);
    StructList := NewList(ADR(FileStruct),SIZE(FileStruct));
    WITH Param DO
       StatAdr   := ADR(Status);
       IntFaceId := VarId;
    END;
    BtrvCallProc := Dummy;
    SETREG(AX,GetIntrVect);
    CODE(06h);    (* PUSH ES *)
    SWI(MSDOSCall);
    CODE(07h);    (* POP ES *)
    GETREG(BX,AXReg);
    IF (AXReg = BtrvInstalled) 
      THEN SETREG(AX,GetDOSVer);
           CODE(06h); SWI(MSDOSCall); CODE(07h);
           GETREG(AX,AXReg);
           ALReg := VAL(BYTE,AXReg);
           IF (ORD(ALReg) >= 3)
             THEN SETREG(AX,MultiFunction);
                  SWI(02fh);
                  GETREG(AX,AXReg);
                  IF (AXReg = MultiLoad)
                    THEN BtrvCallProc := MultiUser; 

  (*     Call a dummy Reset to initialize BtrvMulti and get        *)
  (*      a Process Identifier that will remain unchanged.         *)

                         Param.FunCode := 28;
                         Param.UserKey := VAL(BYTE,0); 
                         A := ADR(Param);     
                         AXReg := ProcIdNotGiven + MultiFunction;
                         ProcIdAux := ProcId;
                         REPEAT
                           CODE(1eh,16h,06h,55h); (* PUSH DS,SS,ES,BP *)
                           SETREG(AX,AXReg);
                           SETREG(BX,ProcIdAux);
                           SETREG(DX,A.OFFSET);
                           SETREG(DS,A.SEGMENT);
                           SWI(02fh);
                           CODE(5dh,07h,17h,1fh); (* POP BP,ES,SS,DS *)      
                           GETREG(AX,CallProcessed);
                           GETREG(BX,ProcIdAux);
                           IF (CallProcessed # 0) 
                             THEN SETREG(AX,0200h);
                                  SWI(07fh);
                             END;
                         UNTIL (CallProcessed = 0);
                         ProcId := ProcIdAux;
                    ELSE BtrvCallProc := SingleUser;
                  END;
             ELSE BtrvCallProc := SingleUser;
           END;
    END;
    IF ListError # 0 THEN Status := 1 END
END Init;


                    (* MISCELLANEOUS PROCEDURES *)


PROCEDURE MakeRecord(AltCol: OrderStruct;VAR MemAllo: CARDINAL): ADDRESS;
 CONST
     SpecRecordSize = 16;
     NULL           = 00h;
 VAR
     RecordNum      : CARDINAL;
     SpecPtr        : ADDRESS;
     Q              : ADDRESS;
     KeyFlag        : BITSET;
     Reserved       : CARDINAL;

BEGIN
    Reserved := NULL;
    First(StructList);
    RecordNum := 0;
    WHILE ListError = 0 DO
      INC(RecordNum);
      Lists.Next(StructList)
    END; (* while *)
    MemAllo := RecordNum * SpecRecordSize;
    IF AltCol.Exists THEN INC(MemAllo,265) END;
    IF Available(MemAllo)
      THEN
        ALLOCATE(SpecPtr,MemAllo);
        
                           (* FileSpec *)
                           
        First(StructList); Q := SpecPtr; 
        Q^ := WORD(FileStruct.RecordLgth); INC(Q,2);
        Q^ := WORD(FileStruct.PageSizeMult * PageSizeUnit); INC(Q,2); 
        Q^ := WORD(FileStruct.IndexesNum); INC(Q,2);
        Q^ := WORD(Reserved) ; INC(Q,2);
        Q^ := WORD(Reserved) ; INC(Q,2);
        IF FileStruct.VarLgthRec THEN Q^ := WORD(1)
                                 ELSE Q^ := WORD(NULL) 
        END; INC(Q,2);
        Q^ := WORD(Reserved) ; INC(Q,2);
        Q^ := WORD(Reserved); INC(Q,2);

                             (* KeySpec *)
                             
        WHILE ListError = 0 DO
          KeyFlag := {};
          Lists.Next(StructList); 
          Q^ := WORD(FileStruct.KeyPos); INC(Q,2);
          Q^ := WORD(FileStruct.KeyLgth); INC(Q,2);
          IF FileStruct.Segmented THEN INCL(KeyFlag,4) END;
          IF FileStruct.Duplicates THEN INCL(KeyFlag,0) END;
          IF FileStruct.Modifiable THEN INCL(KeyFlag,1) END;
          IF FileStruct.Binary
            THEN INCL(KeyFlag,2) 
            ELSIF FileStruct.AltColSeq 
               THEN INCL(KeyFlag,5)
          END; (* if *)
          INC(Q,6);
          IF FileStruct.NullKey    
            THEN Q^ := WORD(ORD(FileStruct.NullChar)*256); INCL(KeyFlag,3)
            ELSE Q^ := WORD(Reserved)
          END; (* if *)
          DEC(Q,6);
          Q^ := WORD(KeyFlag); INC(Q,2);
          Q^ := WORD(Reserved); INC(Q,2);
          Q^ := WORD(Reserved); INC(Q,4);
          Q^ := WORD(Reserved); INC(Q,2);
          Q^ := WORD(Reserved); INC(Q,2)
        END; (* while *)

                         (* AltColSpec *)
                         
        IF AltCol.Exists 
          THEN
            Q^ := WORD(0ac00h); INC(Q);  
            CopyWord(ADR(AltCol.Name),Q,132)
        END; (* if *)

        RETURN SpecPtr
      ELSE
        RETURN NIL
    END; (* if *)
END MakeRecord;

PROCEDURE MakeStruct(Q: ADDRESS;VAR AltCol : OrderStruct;
                     VAR RecNum: LongCard; VAR Freq: RecsPerKey);
 VAR
    DefKeys  : CARDINAL;
    Index    : KeyNum;
    KeyFlag  : BITSET;
    OK       : BOOLEAN;

BEGIN
  First(StructList);
  WHILE ListError = 0 DO Remove(StructList) END;
  Lists.Insert(StructList);                           (* Dummy last *)
  FileStruct.Spec := FileSpec;
  FileStruct.RecordLgth := CARDINAL(Q^); INC(Q,2);
  FileStruct.PageSizeMult := CARDINAL(Q^) DIV PageSizeUnit; INC(Q,2);
  FileStruct.IndexesNum := CARDINAL(Q^); INC(Q,2);
  RecNum.Low := CARDINAL(Q^); INC(Q,2);
  RecNum.High := CARDINAL(Q^); INC(Q,2);
  FileStruct.VarLgthRec := (CARDINAL(Q^) = 1); INC(Q,6);
  DefKeys := FileStruct.IndexesNum - 1;
  Lists.Insert(StructList);
  OK := (ListError = 0);
  Lists.Next(StructList);
  Index := 0;
  WHILE (Index <= DefKeys) & OK DO
    WITH FileStruct DO
      Spec := KeySpec;
      KeyPos := CARDINAL(Q^); INC(Q,2);
      KeyLgth := CARDINAL(Q^); INC(Q,2)
    END; (* with *)
    KeyFlag := BITSET(Q^); INC(Q,6);
    WITH FileStruct DO
      Duplicates := (0 IN KeyFlag);
      Modifiable := (1 IN KeyFlag);
      Segmented  := (4 IN KeyFlag);
      NullKey    := (3 IN KeyFlag);
      IF NullKey THEN NullChar := CHR(CARDINAL(Q^) DIV 256) END;
      Binary     := (2 IN KeyFlag);
      IF NOT(Binary) THEN AltColSeq := (5 IN KeyFlag) END
    END; (* with *)
    INC(Q,6);
    IF (NOT(FileStruct.Binary) & FileStruct.AltColSeq) 
      THEN AltCol.Exists := TRUE 
    END; (* if *)
    IF NOT(FileStruct.Segmented) 
      THEN 
        DEC(Q,10);
        Freq[Index].Low  := CARDINAL(Q^); INC(Q,2);
        Freq[Index].High := CARDINAL(Q^); INC(Q,8);
        INC(Index)
    END; (* if *)
    Lists.Insert(StructList);
    OK := (ListError = 0);
    Lists.Next(StructList)
  END; (* while *)
  Remove(StructList);         (* Remove dummy last inserted before loop *) 
  FOR Index := DefKeys + 1 TO MaxKeyNum - 1 DO 
    Freq[Index].Low  := 0;
    Freq[Index].High := 0
  END; (* for *)
  IF NOT OK
    THEN
      First(StructList);
      WHILE ListError = 0 DO Remove(StructList) END;
      AltCol.Exists := FALSE
  ELSIF AltCol.Exists
    THEN
      INC(Q);
      CopyWord(Q,ADR(AltCol.Name),132)
  END (* if *)
END MakeStruct;


                   (* PROCEDURES TO MANAGE THE SYSTEM *)
                   

PROCEDURE GetDirectory(Drv: LogicalDrive;VAR DirPath: ARRAY OF CHAR);
BEGIN
    IF (HIGH(DirPath) >= 64)
      THEN
        WITH Param DO
          FunCode := 18;
          KeyAdr  := ADR(DirPath);
          KeyLth  := VAL(BYTE,HIGH(DirPath));
          UserKey := VAL(BYTE,ORD(Drv))
        END; (* with *)
        BtrvCallProc(ParamAdr)
      ELSE
        Status := 21
    END (* if *)
END GetDirectory;

PROCEDURE Reset(OwnStation: BOOLEAN; VAR OtherInfo: ARRAY OF CHAR);
BEGIN
    WITH Param DO
      FunCode := 28;
      KeyAdr  := ADR(OtherInfo);
      KeyLth  := VAL(BYTE, HIGH(OtherInfo));
      IF NOT(OwnStation)
        THEN UserKey   := VAL(BYTE,-1)
        ELSE UserKey   := VAL(BYTE,0)
      END (* if *)
    END; (* with *)
    BtrvCallProc(ParamAdr)
END Reset;

PROCEDURE SetDirectory(DirPath: ARRAY OF CHAR);
BEGIN
    WITH Param DO
      FunCode := 17;
      KeyAdr  := ADR(DirPath);
      KeyLth  := VAL(BYTE,HIGH(DirPath))
    END; (* with *)
    BtrvCallProc(ParamAdr)
END SetDirectory;

PROCEDURE Stop;
BEGIN
    Param.FunCode := 25;
    BtrvCallProc(ParamAdr);
    IF (Status = 0) THEN BtrvCallProc := Dummy END;
    First(StructList);
    WHILE ListError = 0 DO Remove(StructList) END;
    Dispose(StructList)
END Stop;

PROCEDURE Version(VAR VerNum,RevNum: CARDINAL; VAR Slash: CHAR);
  TYPE
    SysInfo = RECORD
                V,R : CARDINAL;
                S   : CHAR
              END;
  VAR
    Present : SysInfo;
    
BEGIN
    WITH Present DO
      V := VerNum;
      R := RevNum;
      S := Slash
    END; (* with *)
    WITH Param DO
      BufAdr  := ADR(Present);
      BufLth  := TSIZE(SysInfo);
      FunCode := 26
    END; (* with *)
    BtrvCallProc(ParamAdr);
    VerNum := Present.V;
    RevNum := Present.R;
    Slash := Present.S
END Version;

                      (* PROCEDURES TO MANAGE THE FILES *)
                      

PROCEDURE ClearOwner(File : BtrvFile);
BEGIN
  IF File # NIL
    THEN
      WITH Param DO
        FCBAdr := File;
        CurAdr := FCBAdr; INC(CurAdr,38);
        FunCode := 30;
      END (* with *);
      BtrvCallProc(ParamAdr)
    ELSE
      Status := 3
  END (* if *)
END ClearOwner;

PROCEDURE Close(VAR File : BtrvFile);
BEGIN
  IF File # NIL
    THEN
      WITH Param DO
        FCBAdr := File;
        CurAdr := FCBAdr; INC(CurAdr,38);
        FunCode := 1;
      END (* with *);
      BtrvCallProc(ParamAdr);
      IF (Status = 0) THEN DISPOSE(File) END (* if *)
    ELSE
      Status := 3
  END (* if *)
END Close;

PROCEDURE Create(FileName : ARRAY OF CHAR; VAR AltColSeq : OrderStruct);
  VAR
     Dum0      : ARRAY[0 .. 127] OF CHAR;
     MemAllo   : CARDINAL;
     Q         : ADDRESS;
BEGIN
    Q := MakeRecord(AltColSeq,MemAllo);
    IF (Q = NIL)
      THEN Status := 1
      ELSE
        WITH Param DO
          BufAdr  := Q;
          BufLth  := MemAllo;
          FCBAdr  := ADR(Dum0);
          CurAdr  := FCBAdr; INC(CurAdr,38);
          FunCode := 14;
          KeyAdr  := ADR(FileName);
          KeyLth  := VAL(BYTE,HIGH(FileName))
        END; (* with *)
        BtrvCallProc(ParamAdr);
        DEALLOCATE(Q,MemAllo)
    END (* if *)
END Create;

PROCEDURE Extend(File: BtrvFile; FileName: ARRAY OF CHAR; Now: BOOLEAN);
BEGIN
  IF File # NIL
    THEN
      WITH Param DO
        FCBAdr  := File;
        CurAdr  := FCBAdr; INC(CurAdr,38);
        FunCode := 16;
        KeyAdr  := ADR(FileName);
        KeyLth  := VAL(BYTE,HIGH(FileName));
        IF Now THEN UserKey := VAL(BYTE,-1) ELSE UserKey := VAL(BYTE,0) END
      END; (* with *)
      BtrvCallProc(ParamAdr)
    ELSE
      Status := 3
  END (* if *)
END Extend;

PROCEDURE Open(FileName: ARRAY OF CHAR; Owner : OwnerName; 
                                        Mode  : OpeningMode ): BtrvFile;
    
  VAR
    Created   : BtrvFile;
BEGIN
    IF NOT(Available(FileRecordSize))
      THEN
        Status := 1;
        RETURN NIL
    END; (* if *)
    NEW(Created);
    WITH Param DO
      BufAdr  := ADR(Owner);
      BufLth  := HIGH(Owner);
      FCBAdr  := Created;
      CurAdr  := FCBAdr; INC(CurAdr,38);
      FunCode := 0;
      KeyAdr  := ADR(FileName);
      KeyLth  := VAL(BYTE, HIGH(FileName));
      UserKey := VAL(BYTE, INTEGER(ORD(Mode))-3)
    END; (* with *)
    BtrvCallProc(ParamAdr);
    IF (Status # 0) THEN RETURN NIL END; (* if *)
    RETURN Created
END Open;

PROCEDURE SetOwner(File: BtrvFile; Owner: OwnerName; 
                                   AnyAccess, Encryption:BOOLEAN );
  VAR
    Dum : CARDINAL;
BEGIN
  IF File # NIL
    THEN
      Dum := 0;
      IF NOT(AnyAccess) THEN Dum := 1 END;
      IF Encryption THEN INC(Dum,2) END;
      WITH Param DO
        BufAdr  := ADR(Owner);
        BufLth  := 9;
        FCBAdr  := File;
        CurAdr  := FCBAdr; INC(CurAdr,38);
        FunCode := 29;
        KeyAdr  := BufAdr;
        KeyLth  := VAL(BYTE,BufLth);
        UserKey := VAL(BYTE,Dum)
      END; (* with *)
      BtrvCallProc(ParamAdr)
    ELSE
      Status := 3
  END (* if *)
END SetOwner;

PROCEDURE Stat(File: BtrvFile; VAR RecNum: LongCard;VAR Freq: RecsPerKey;
               VAR AltColSeq: OrderStruct; VAR ExtFileName: ARRAY OF CHAR);
  CONST
    SpecRecordSize = 16;
    MemAllo        = SpecRecordSize*(MaxKeyNum + 1) + 265;
    
  VAR
    Q       : ADDRESS;
BEGIN
  IF File = NIL
    THEN
      Status := 3
    ELSIF Available(MemAllo)
      THEN
        ALLOCATE(Q,MemAllo);
        WITH Param DO
          BufAdr  := Q;
          BufLth  := MemAllo;
          FCBAdr  := File;
          CurAdr  := FCBAdr; INC(CurAdr,38);
          FunCode := 15;
          KeyAdr  := ADR(ExtFileName);
          KeyLth  := VAL(BYTE, HIGH(ExtFileName))
        END; (* with *)
        BtrvCallProc(ParamAdr);
        IF (Status = 0)
          THEN
            MakeStruct(Q,AltColSeq,RecNum,Freq)
          ELSE
            First(StructList);
            WHILE ListError = 0 DO Remove(StructList) END;
            AltColSeq.Exists := FALSE
        END; (* if *)
        DEALLOCATE(Q,MemAllo);
      ELSE
        Status := 1
    END (* if *)
END Stat;


                  (* PROCEDURES TO MANAGE OPERATIONS *)
                  
                  
PROCEDURE AbortTransaction ;
BEGIN
    Param.FunCode := 21;
    BtrvCallProc(ParamAdr)
END AbortTransaction ;

PROCEDURE BeginTransaction ;
BEGIN
    IF (LockStat = NoWait) THEN Param.FunCode := 219 ELSE Param.FunCode := 19 END;
    BtrvCallProc(ParamAdr)
END BeginTransaction ;

PROCEDURE ClearLocking;
BEGIN
    LockStat := NoLock
END ClearLocking;

PROCEDURE EndTransaction;
BEGIN
    Param.FunCode := 20;
    BtrvCallProc(ParamAdr)
END EndTransaction;

PROCEDURE SetWaitLocking;
BEGIN
    LockStat := Wait
END SetWaitLocking;

PROCEDURE SetNoWaitLocking;
BEGIN
    LockStat := NoWait
END SetNoWaitLocking;

PROCEDURE Unlock(File: BtrvFile);
BEGIN
  IF File # NIL
    THEN
      WITH Param DO
        FCBAdr  := File;
        CurAdr  := FCBAdr; INC(CurAdr,38);
        FunCode := 27
      END; (* with *)
      BtrvCallProc(ParamAdr)
    ELSE
      Status := 3
  END (* if *)
END Unlock;


          (* PROCEDURES TO OPERATE ON RECORDS *)
          
          
PROCEDURE Delete(File: BtrvFile;VAR Data: DataInfo);
BEGIN
  IF File # NIL
    THEN
      WITH Param DO
        BufAdr  := Data.DataBufAdr;
        BufLth  := Data.DataBufLth;
        FCBAdr  := File;
        CurAdr  := FCBAdr; INC(CurAdr,38);
        FunCode := 4;
        KeyAdr  := Data.KeyBufAdr;
        UserKey := VAL(BYTE,Data.Key^);
        KeyLth  := VAL(BYTE,Data.KeyBufLth)
      END; (* with *)
      BtrvCallProc(ParamAdr)
    ELSE
      Status := 3
  END (* if *)
END Delete;

PROCEDURE GetDirect(File: BtrvFile; RecAdr : PhysRecAdr;VAR Data: DataInfo);
BEGIN
  IF File # NIL
    THEN
      CopyWord(ADR(RecAdr),Data.DataBufAdr,2);
      WITH Param DO
        BufAdr  := Data.DataBufAdr;
        BufLth  := Data.DataBufLth;
        FCBAdr  := File;
        CurAdr  := FCBAdr; INC(CurAdr,38);
        FunCode := 23 + 100*ORD(LockStat);
        KeyAdr  := Data.KeyBufAdr;
        KeyLth  := VAL(BYTE,Data.KeyBufLth);
        UserKey := VAL(BYTE,Data.Key^)
      END; (* with *)
      BtrvCallProc(ParamAdr);
      Data.DataBufLth := Param.BufLth
    ELSE
      Status := 3
  END (* if *)
END GetDirect;

PROCEDURE GetRecord(File: BtrvFile;VAR Data: DataInfo; RelOp: RelationalOp);
BEGIN
  IF File # NIL
    THEN
      WITH Param DO
        BufAdr  := Data.DataBufAdr;
        BufLth  := Data.DataBufLth;
        FCBAdr  := File;
        CurAdr  := FCBAdr; INC(CurAdr,38);
        FunCode := 5 + ORD(RelOp) + 100*ORD(LockStat);
        KeyAdr  := Data.KeyBufAdr;
        KeyLth  := VAL(BYTE,Data.KeyBufLth);
        UserKey := VAL(BYTE,Data.Key^)
      END; (* with *)
      BtrvCallProc(ParamAdr);
      Data.DataBufLth := Param.BufLth
    ELSE
      Status := 3
  END (* if *)
END GetRecord;

PROCEDURE GetKey(File: BtrvFile;VAR Data: DataInfo; RelOp: RelationalOp);
BEGIN
  IF File # NIL
    THEN
      WITH Param DO
        BufAdr  := Data.DataBufAdr;
        BufLth  := Data.DataBufLth;
        FCBAdr  := File;
        CurAdr  := FCBAdr; INC(CurAdr,38);
        FunCode := 55 + ORD(RelOp);
        KeyAdr  := Data.KeyBufAdr;
        KeyLth  := VAL(BYTE,Data.KeyBufLth);
        UserKey := VAL(BYTE,Data.Key^)
      END; (* with *)
      BtrvCallProc(ParamAdr)
    ELSE
      Status := 3
  END (* if *)
END GetKey;

PROCEDURE GetPosition(File: BtrvFile): PhysRecAdr;
  VAR
    Aux : PhysRecAdr;
BEGIN
  IF File # NIL
    THEN
      WITH Param DO
        BufAdr    := ADR(Aux);
        BufLth    := TSIZE(PhysRecAdr);
        FCBAdr    := File;
        CurAdr    := FCBAdr; INC(CurAdr,38);
        FunCode   := 22
      END; (* with *)
      BtrvCallProc(ParamAdr);
      RETURN Aux 
    ELSE
      Status := 3
  END (* if *)
END GetPosition;

PROCEDURE Insert(File: BtrvFile; Data: DataInfo);
BEGIN
  IF File # NIL
    THEN
      WITH Param DO
        BufAdr  := Data.DataBufAdr;
        BufLth  := Data.DataBufLth;
        FCBAdr  := File;
        CurAdr  := FCBAdr; INC(CurAdr,38);
        FunCode := 2;
        KeyAdr  := Data.KeyBufAdr;
        KeyLth  := VAL(BYTE,Data.KeyBufLth);
        UserKey := VAL(BYTE,Data.Key^)
      END; (* with *)
      BtrvCallProc(ParamAdr)
    ELSE
      Status := 3
  END (* if *)
END Insert;

PROCEDURE StepDirect(File: BtrvFile; VAR Data: DataInfo);
BEGIN
  IF File # NIL
    THEN
      WITH Param DO
        BufAdr  := Data.DataBufAdr;
        BufLth  := Data.DataBufLth;
        FCBAdr  := File;
        CurAdr  := FCBAdr; INC(CurAdr,38);
        FunCode := 24 + 100*ORD(LockStat)
      END; (* with *)
      BtrvCallProc(ParamAdr);
      Data.DataBufLth := Param.BufLth
    ELSE
      Status := 3
  END (* if *)
END StepDirect;

PROCEDURE Update(File: BtrvFile; Data: DataInfo);
BEGIN
  IF File # NIL
    THEN
      WITH Param DO
        BufAdr  := Data.DataBufAdr;
        BufLth  := Data.DataBufLth;
        FCBAdr  := File;
        CurAdr  := FCBAdr; INC(CurAdr,38);
        FunCode := 3;
        KeyAdr  := Data.KeyBufAdr;
        KeyLth  := VAL(BYTE,Data.KeyBufLth);
        UserKey := VAL(BYTE,Data.Key^)
      END; (* with *)
      BtrvCallProc(ParamAdr)
    ELSE
      Status := 3
  END (* if *)
END Update;

                    (*  Main initializing code *)

BEGIN
    Init
END Btrieve.