home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / BMULKY.ZIP / MULKEY.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1988-05-16  |  40.7 KB  |  1,041 lines

  1. { MULKEY }                                                                     {
  2.  
  3. +-----------------------------------------------------------------------------+
  4. |                                                                             |
  5. |                  !! IMPORTANT NOTICE and DISCLAIMER !!                      |
  6. |                                                                             |
  7. |  This software is Copyright 1986, 1987 and 1988 by Mark R. Boler.           |
  8. |  All Rights Reserved.  Any references to "the developer" in this notice     |
  9. |  refers to Mark R. Boler.  This source code must not be distributed without |
  10. |  prior written permission from the developer.  The use of these routines    |
  11. |  require use of the Btrieve file manager system by Novell, Version 4.10     |
  12. |  or later.  These routines were tested as well as could be expected but the |
  13. |  developer assumes no responsibility for any errors, omissions, problems or |
  14. |  bugs you may have if you use them.  This software is provided "AS IS"      |
  15. |  without warranty of any kind.  It is your responsibility to test this      |
  16. |  software thoroughly!  Further, the developer does not warrant, guarantee,  |
  17. |  or make any claims of suitability to a particular purpose with regard to   |
  18. |  this software.  The entire risk as to the results and/or performance of    |
  19. |  this software is assumed by you.  The developer shall not be liable for    |
  20. |  any direct, indirect, consequential, or incidental damages including, but  |
  21. |  not limited to, loss of profits or loss of information. By using this      |
  22. |  software you agree to the above terms and conditions.                      |
  23. |                                                                             |
  24. +-----------------------------------------------------------------------------+}
  25.  
  26. { --------------------------- Compiler Directives --------------------------- }
  27.  
  28. {$B-}    { Boolean complete evaluation off }
  29. {$D+}    { Debug information on            }
  30. {$I-}    { I/O checking off                }
  31. {$L+}    { Linker uses memory for a buffer }
  32. {$N-}    { No numeric coprocessor          }
  33. {$R-}    { Range checking off              }
  34. {$S-}    { Stack checking off              }
  35. {$T-}    { .TPM file generation off        }
  36. {$V-}    { No VAR-String checks            }
  37.  
  38. UNIT Mulkey;        { Data file access routines for Btrieve from Turbo Pascal }
  39.  
  40. INTERFACE           { to modify the version - change file MULKEY.INC }
  41.  
  42. USES Heap, Btrieve, Bits, Strings, Convert, Env;
  43.  
  44. { --------------------------------------------------------------------------- }
  45.  
  46. CONST                                     { Exported Constants                }
  47.                                           { --------------------------------- }
  48.       FDescLen      = 25;                 { Length of file description string }
  49.       FSystLen      = 25;                 { Length of file system name string }
  50.       FOwnerLen     = 8;                  { Length of a file owner name       }
  51.       MaxPathLen    = 68;                 { Max length of DOS path & name + 2 }
  52.  
  53. TYPE                                        { Exported Types                  }
  54.                                             { ------------------------------- }
  55.       FDescStr      = STRING[FDescLen];     { File Description string         }
  56.       FSystStr      = STRING[FSystLen];     { System name string              }
  57.       FOwnerStr     = STRING[FOwnerLen];    { Owner name string               }
  58.       PathStr       = STRING[MaxPathLen];   { DOS file path and name string   }
  59.       FSDRec        = RECORD                { Combined system and fdesc key   }
  60.                            FDesc: FDescStr;
  61.                            FSyst: FSystStr;
  62.                       END; {RECORD FSDRec}
  63.  
  64.                                             { Record Locking Attributes       }
  65.                                             { ------------------------------- }
  66.       FLockAttrib   = (NoLocks,             {  Do not use locks               }
  67.                        WaitLocks,           {  Use single wait locks          }
  68.                        NoWaitLocks,         {  Use single no wait locks       }
  69.                        MultiWaitLocks,      {  Use multiple wait locks        }
  70.                        MultiNoWaitLocks);   {  Use multiple no wait locks     }
  71.  
  72. CONST                                       { Exported Variables              }
  73.                                             { DO NOT change these - ONLY READ }
  74.                                             { ------------------------------- }
  75.       MulkeyActive  : BOOLEAN     = FALSE;  { TRUE if Btreive loaded          }
  76.       TransActive   : BOOLEAN     = FALSE;  { TRUE if a transaction is active }
  77.  
  78.                                                { Changed by your program      }
  79.                                                { ---------------------------- }
  80.       KeyOnly       : BOOLEAN     = FALSE;     { TRUE for Get Key only +50    }
  81.       FileLocks     : FLockAttrib = WaitLocks; { Default locks for files      }
  82.       TransLocks    : FLockAttrib = NoLocks;   { Def locks for transactions   }
  83.       SystemName    : FSystStr    = '';        { Empty will find any FileDesc }
  84.  
  85. { --------------------------------------------------------------------------- }
  86.  
  87. VAR                                     { ONLY READ these vars - NEVER change }
  88.                                         { ----------------------------------- }
  89.       IndexError    : BOOLEAN;          { TRUE if last operation was in error }
  90.       LastErrCode   : WORD;             { Btrieves last error code            }
  91.       BtVersion     : STRING[45];       { Returns version of Btrieve loaded   }
  92.       MulkeyVersion : STRING[45];       { Returns version of Mulkey in use    }
  93.  
  94. { ------------------- Exported Procedures and Functions --------------------- }
  95.  
  96. FUNCTION AllowShell: BOOLEAN;    { Inline directive                           }
  97.          INLINE($A0/TransActive/ { mov   al, TransActive                      }
  98.                 $F6/$D0);        { not   al                                   }
  99.  
  100.                                  { Transaction Procedures                     }
  101.                                  { ------------------------------------------ }
  102. PROCEDURE AbortTransaction;      { Aborts a currently running transaction     }
  103.  
  104. PROCEDURE EndTransaction;        { Normally ends the current transaction      }
  105.  
  106. PROCEDURE BeginTransaction;      { Starts a transaction                       }
  107.                                  { ------------------------------------------ }
  108.  
  109.                                        { Owner Procedures                     }
  110.                                        { ------------------------------------ }
  111. PROCEDURE SetOwner(Handle : WORD;      { Handle of the file                   }
  112.                    Owner  : FOwnerStr; { The owner name                       }
  113.                    AllowRO: BOOLEAN;   { Allow read only without an owner     }
  114.                    Encrypt: BOOLEAN);  { Encrypt the data stored in file      }
  115.  
  116. PROCEDURE ClearOwner(Handle: WORD);    { Handle of the file to clear owner on }
  117.                                        { ------------------------------------ }
  118.  
  119.                                               { File Locking Procedures       }
  120.                                               { ----------------------------- }
  121. PROCEDURE SetLocks(Handle    : WORD;          { Handle of the file            }
  122.                    LockAttrib: FLockAttrib);  { Lock attributes               }
  123.  
  124. { sets the locks attributes Btrieve is to use for the file                    }
  125.  
  126. PROCEDURE Unlock(Handle: WORD);               { Handle of the file            }
  127.  
  128. { Releases any locks accumulated on a file                                    }
  129.  
  130.                                             { File Procedures and Functions   }
  131.                                             { ------------------------------- }
  132. FUNCTION OpenFile(FDesc   : FDescStr;       { FileDesc string to search for   }
  133.                   OpenMode: INTEGER;        { Mode to open file in            }
  134.                   Owner   : FOwnerStr;      { Owner of the file               }
  135.                   FileName: PathStr): WORD; { Path and Name of the file       }
  136.  
  137. { OpenFile will return a handle in the range of FirstHandle - MaxMulkey.      }
  138. { This handle is how you refer to the open file.  A handle of 0 means         }
  139. { an error occured and you should check LastErrCode to see what happened.     }
  140.  
  141. PROCEDURE CloseFile(Handle: WORD);          { Closes a file                   }
  142.  
  143. PROCEDURE ResetFiles;                       { Closes all resources            }
  144.  
  145. { Closes any open files and release any locks or resources held by this       }
  146. { station  Usually, you would only want to do this when you must terminate    }
  147. { the program abnormally                                                      }
  148.  
  149. PROCEDURE ExtendFile(VAR Handle: WORD;      { File handle - could change      }
  150.                       FileName : PathStr;   { File name to use for extension  }
  151.                       ExtendNow: BOOLEAN);  { Extend the file now (or later)  }
  152.  
  153. { This procedure will create an extension for a Btrieve file, usually on      }
  154. { another drive.  Use this procedure with caution because it will close       }
  155. { and then re-open a file.  If it ends in error, the file could still         }
  156. { possibly have been extended and the re-open failed.  The file must be       }
  157. { open before using this procedure.  The handle is a VAR parameter since      }
  158. { it could be changed by Mulkey during OpenFile although it is not likely.    }
  159.  
  160. FUNCTION MulkeyFileName(Handle: WORD): PathStr;  { The file handle            }
  161.  
  162. { Returns the file name used to open the file                                 }
  163.  
  164. FUNCTION MulkeyExtension(Handle: WORD): PathStr; { The file handle            }
  165.  
  166. { Returns the name of the extension file if there is one                      }
  167.  
  168. PROCEDURE GetKeyBuffer(Handle, KeyNum: WORD;    { The File and Key number     }
  169.                        VAR k);                  { Your variable               }
  170.  
  171. { Returns the key buffer in Mulkeys key buffer area                           }
  172.  
  173. PROCEDURE Stat(Handle: WORD);               { The file handle                 }
  174.  
  175. { Returns information about the file into Mulkeys buffer area which can be    }
  176. { retrieved later with a call to FileInfo()                                   }
  177.  
  178. FUNCTION FileInfo(Handle: WORD): POINTER;   { The file handle                 }
  179.  
  180. { Returns a pointer to the FileSpecType of the file.                          }
  181. { If Handle is not valid it returns NIL and sets the appropriate error code.  }
  182.  
  183. FUNCTION NumRecords(Handle: WORD): LONGINT; { The file handle                 }
  184.  
  185. { Returns the number of records in a Btrieve file.   It returns 0 if the      }
  186. { handle is not active and sets IndexError (set in Stat).                     }
  187.  
  188.                                              { Miscellaneous Procedures       }
  189.                                              { ------------------------------ }
  190. PROCEDURE MulkeySearchKey(FDesc: FDescStr;   { Creates proper search key and  }
  191.                         VAR Key: BYTE;       { search string for system name }
  192.                        VAR Mult: FSDRec);
  193.  
  194. PROCEDURE SetErr(Status: WORD);              { Set the Mulkey error code      }
  195.  
  196. FUNCTION BtError(Code: WORD): STRING;        { Btrieve Error String           }
  197.  
  198. FUNCTION MulkeyError: STRING;                { Last Btrieve or Mulkey Error   }
  199.                                              { ------------------------------ }
  200.  
  201.                                              { Change Record Procedures       }
  202.                                              { ------------------------------ }
  203. PROCEDURE AddRecord(Handle: WORD;            { The file handle                }
  204.                      VAR d);                 { The record to add to the file  }
  205.  
  206. PROCEDURE UpdateRecord(Handle: WORD;         { The file handle                }
  207.                         VAR d);              { The record to change           }
  208.  
  209. { Update the last retrieved record (you must have just used a get operation)  }
  210. { with data from record d. IndexError usually indicates a duplicate key in a  }
  211. { unique key index.                                                           }
  212.  
  213. PROCEDURE DeleteRecord(Handle: WORD);        { The file handle                }
  214.  
  215. { Deletes the last retrieved record (you must have just used a get operation) }
  216. { from the database and all its keys.  IndexError usually indicates no valid  }
  217. { last record retrieved.                                                      }
  218.  
  219.                                              { Retrieve Record Procedures     }
  220.                                              { ------------------------------ }
  221. FUNCTION GetPosition(Handle: WORD): LONGINT; { Returns file record position   }
  222.  
  223. { This will return the file pointer of the file that Btrieve maintains        }
  224.  
  225. PROCEDURE GetDirect(Handle: WORD;            { The file handle                }
  226.                          p: LONGINT;         { The record position            }
  227.                      VAR d);                 { The data record to read        }
  228.  
  229. PROCEDURE StepDirect(Handle: WORD;           { The file handle                }
  230.                       VAR d);                { The data record to read        }
  231.  
  232.  
  233. PROCEDURE GetLowest(Handle: WORD;            { The file handle                }
  234.                     KeyNum: WORD;            { The key number to use          }
  235.                      VAR d);                 { The data record to read        }
  236.  
  237. PROCEDURE GetHighest(Handle: WORD;           { The file handle                }
  238.                      KeyNum: WORD;           { The key number to use          }
  239.                       VAR d);                { The data record to read        }
  240.  
  241.  
  242. PROCEDURE GetEqual(Handle: WORD;             { The file handle                }
  243.                    KeyNum: WORD;             { The key number to use          }
  244.                     VAR k;                   { The key value to use           }
  245.                     VAR d);                  { The data record to read        }
  246.  
  247. PROCEDURE GetGreater(Handle: WORD;           { The file handle                }
  248.                      KeyNum: WORD;           { The key number to use          }
  249.                       VAR k;                 { The key value to use           }
  250.                       VAR d);                { The data record to read        }
  251.  
  252. PROCEDURE GetLessThan(Handle: WORD;          { The file handle                }
  253.                       KeyNum: WORD;          { The key number to use          }
  254.                        VAR k;                { The key value to use           }
  255.                        VAR d);               { The data record to read        }
  256.  
  257. PROCEDURE GetGreaterOrEqual(Handle: WORD;    { The file handle                }
  258.                             KeyNum: WORD;    { The key number to use          }
  259.                              VAR k;          { The key value to use           }
  260.                              VAR d);         { The data record to read        }
  261.  
  262. PROCEDURE GetLessThanOrEqual(Handle: WORD;   { The file handle                }
  263.                              KeyNum: WORD;   { The key number to use          }
  264.                               VAR k;         { The key value to use           }
  265.                               VAR d);        { The data record to read        }
  266.  
  267. PROCEDURE NextRecord(Handle: WORD;           { The file handle                }
  268.                      KeyNum: WORD;           { The key number to use          }
  269.                       VAR d);                { The data record to read        }
  270.  
  271. PROCEDURE PrevRecord(Handle: WORD;           { The file handle                }
  272.                      KeyNum: WORD;           { The key number to use          }
  273.                       VAR d);                { The data record to read        }
  274.                                              { ------------------------------ }
  275. IMPLEMENTATION
  276.  
  277. {$I MULKEY.INC}
  278.  
  279. VAR
  280.       ExitSave: POINTER;         { Pointer to save the exit procedure         }
  281.       MulPtr  : MulkeyTable;     { The table of pointers for Mulkey handles   }
  282.  
  283. PROCEDURE SetErr(Status: WORD); EXTERNAL;
  284.  
  285. FUNCTION AdjOp(Op: WORD; LockAttr: FLockAttrib): WORD; EXTERNAL;
  286.  
  287. FUNCTION HandleActive(Handle: WORD): BOOLEAN; EXTERNAL;
  288.  
  289. {$L MULKEY.OBJ}
  290.  
  291. PROCEDURE BeginTransaction; EXTERNAL;
  292.  
  293. PROCEDURE AbortTransaction; EXTERNAL;
  294.  
  295. PROCEDURE EndTransaction; EXTERNAL;
  296.  
  297. {$L MULTRANS.OBJ}
  298.  
  299. PROCEDURE SetOwner(Handle : WORD;
  300.                    Owner  : FOwnerStr;
  301.                    AllowRO: BOOLEAN;
  302.                    Encrypt: BOOLEAN);
  303. VAR
  304.       Len,
  305.       Status,
  306.       BitMask: WORD;
  307.  
  308. BEGIN {SetOwner}
  309.       IF HandleActive(Handle) THEN
  310.       BEGIN
  311.           ForceStr(Owner, FOwnerLen);
  312.           Len:= LENGTH(Owner);
  313.           AsciiZ(Owner);
  314.           BitMask:= 0;
  315.           IF AllowRO THEN SetBit(BitMask, ReadOnlyAccessBit);
  316.           IF Encrypt THEN SetBit(BitMask, EncryptBit);
  317.           Status:= Btrv(SetOwnerOp, MulPtr[Handle]^.PosBlock,
  318.                         Owner, Len, Owner, BitMask, SIZEOF(Owner));
  319.       END {if}
  320.       ELSE Status:= BadHandleErr;
  321.       SetErr(Status);
  322. END; {SetOwner}
  323.  
  324. PROCEDURE UC_File(Handle: WORD;
  325.                     Code: WORD);
  326.  
  327. VAR
  328.       x,
  329.       Status: WORD;
  330.  
  331. BEGIN {UC_File}
  332.       IF HandleActive(Handle) THEN
  333.          Status:= Btrv(Code, MulPtr[Handle]^.PosBlock, x, x, x, 0, SIZEOF(x))
  334.            ELSE Status:= BadHandleErr;
  335.       SetErr(Status);
  336. END; {UC_File}
  337.  
  338. PROCEDURE ClearOwner; EXTERNAL;
  339.  
  340. PROCEDURE Unlock; EXTERNAL;
  341.  
  342. {$L MULUCFIL.OBJ}
  343.  
  344. PROCEDURE SetLocks(Handle: WORD;  LockAttrib: FLockAttrib);
  345.  
  346. VAR
  347.       Status: WORD;
  348.  
  349. BEGIN {SetLocks}
  350.       IF HandleActive(Handle) THEN
  351.       BEGIN
  352.           UnLock(Handle);                         { Unlock any existing locks }
  353.           MulPtr[Handle]^.LockAttr:= LockAttrib;  { Set new lock attributes   }
  354.           Status:= NoErr;                         { return no error           }
  355.       END
  356.       ELSE Status:= BadHandleErr;
  357.       SetErr(Status);
  358. END; {SetLocks}
  359.  
  360. FUNCTION FileInfo(Handle: WORD): POINTER;
  361.  
  362. VAR
  363.       Status: WORD;
  364.  
  365. BEGIN {FileInfo}
  366.       IF HandleActive(Handle) THEN Status:= NoErr ELSE Status:= BadHandleErr;
  367.       SetErr(Status);
  368.       IF IndexError THEN FileInfo:= NIL
  369.          ELSE FileInfo:= @MulPtr[Handle]^.FileDesc.FileData;
  370. END; {FileInfo}
  371.  
  372. PROCEDURE Stat(Handle: WORD);
  373.  
  374. { This procedure does a stat operation on the file. All information is        }
  375. { stored into the files data records in the FileDescType. It does nothing     }
  376. { unless the file handle is an active handle. IndexError will be set if not.  }
  377.  
  378. VAR
  379.       x,
  380.       Status  : WORD;
  381.       Extent  : PathStr;
  382.       HoldData: FileSpecType;   { temporary holding area }
  383.  
  384. BEGIN {Stat}
  385.       IF HandleActive(Handle) THEN
  386.       BEGIN
  387.           x:= SIZEOF(HoldData);
  388.           Status:= Btrv(StatOp, MulPtr[Handle]^.PosBlock, HoldData, x,
  389.                         Extent[1], 0, PRED(SIZEOF(PathStr)));
  390.           IF (Status = NoErr) THEN
  391.           BEGIN
  392.               x:= 1;
  393.               WHILE (Extent[x] <> #0) AND (x <= MaxPathLen) DO INC(x);
  394.               Extent[0]:= CHR(PRED(x));
  395.               MulPtr[Handle]^.Changed:= FALSE;
  396.               MulPtr[Handle]^.Extension:= Extent;
  397.               MulPtr[Handle]^.FileDesc.FileData:= HoldData;
  398.           END; {if}
  399.       END
  400.       ELSE Status:= BadHandleErr;
  401.       SetErr(Status);
  402. END; {Stat}
  403.  
  404. PROCEDURE MulkeySearchKey(FDesc: FDescStr;
  405.                         VAR Key: BYTE;
  406.                        VAR Mult: FSDRec);
  407.  
  408. BEGIN {MulkeySearchKey}
  409.       Mult.FDesc:= StripBlanks(FDesc);
  410.       Mult.FSyst:= StripBlanks(SystemName);
  411.       IF (LENGTH(SystemName) > 0) THEN Key:= FDescAndSystemKey
  412.          ELSE Key:= FDescOnlyKey;
  413. END; {MulkeySearchKey}
  414.  
  415. FUNCTION GetFileDesc(FDesc: FDescStr;
  416.                      VAR r: FileDescType): BOOLEAN;
  417.  
  418. VAR
  419.       Found    : BOOLEAN;
  420.       Key      : BYTE;
  421.       x,
  422.       Len      : WORD;
  423.       FileName : PathStr;
  424.       Owner    : FOwnerStr;
  425.       SearchKey: FSDRec;
  426.       PosBlock : PosBlockType;
  427.  
  428. BEGIN {GetFileDesc}
  429.       Found:= FALSE;
  430.       FileName:= GetEnv(EnvVar);
  431.       IF (LENGTH(FileName) > 0) AND (LENGTH(FDesc) > 0) THEN
  432.       BEGIN
  433.           Owner:= FileVersion + Revision;
  434.           Len  := LENGTH(Owner);
  435.           AsciiZ(Owner);
  436.           AsciiZ(FileName);
  437.           IF (Btrv(OpenOp, PosBlock, Owner, Len, FileName, NormalOpen,
  438.                    SIZEOF(FileName)) = NoErr) THEN
  439.           BEGIN
  440.               MulkeySearchKey(FDesc, Key, SearchKey);
  441.               Len  := SIZEOF(r);
  442.               Found:= (Btrv(GetEqualOp, PosBlock, r, Len, SearchKey, PRED(Key),
  443.                        SIZEOF(SearchKey)) = NoErr);
  444.               Len  := Btrv(CloseOp, PosBlock, x, x, x, 0, SIZEOF(x));
  445.           END; {if}
  446.       END; {if}
  447.       GetFileDesc:= Found;
  448. END; {GetFileDesc}
  449.  
  450. FUNCTION OpenFile(FDesc   : FDescStr;
  451.                   OpenMode: INTEGER;
  452.                   Owner   : FOwnerStr;
  453.                   FileName: PathStr): WORD;
  454.  
  455. VAR
  456.       h,
  457.       x, y,
  458.       Len,
  459.       Status,
  460.       OwnLen,
  461.       Indexes,
  462.       TotKeyMem: WORD;
  463.       MemError : BOOLEAN;
  464.       FileOwner: FOwnerStr;
  465.       MemNeeded: ARRAY[MKeyRange] OF WORD;
  466.  
  467. PROCEDURE CleanUp;
  468.  
  469. BEGIN {CleanUp}
  470.       CloseFile(h);
  471.       SetErr(NoMemoryErr);
  472. END; {CleanUp}
  473.  
  474. BEGIN {OpenFile}
  475.       OpenFile:= NoHandle;
  476.       IF MulkeyActive THEN
  477.       BEGIN
  478.          h:= FirstHandle;
  479.          WHILE HandleActive(h) DO INC(h);
  480.          IF (h <= MaxMulkey) THEN
  481.          BEGIN
  482.             IF (MAXAVAIL >= (SIZEOF(MemFileType) + SIZEOF(KBuf) +
  483.                           SIZEOF(KeyBufPtr) + SIZEOF(POINTER))) THEN
  484.             BEGIN
  485.                NEW(MulPtr[h]);                         { Allocate some memory }
  486.                IF (MulPtr[h] <> NIL) THEN
  487.                BEGIN
  488.                   ForceStr(FileName, MaxPathLen);      { Must be right length }
  489.                   ForceStr(Owner, FOwnerLen);          { Must be right length }
  490.                   FDesc := UpCaseStr(FDesc);           { Uppercase for search }
  491.                   Owner := UpCaseStr(Owner);           { Uppercase for owner  }
  492.                   OwnLen:= LENGTH(Owner);              { Set length of owner  }
  493.                   FileOwner:= Owner;                   { Real file owner      }
  494.                   AsciiZ(FileOwner);                   { Null terminate owner }
  495.                   FileName[SUCC(LENGTH(FileName))]:= #0;{ Null terminate file }
  496.                   Status:= Btrv(OpenOp, MulPtr[h]^.PosBlock, FileOwner,
  497.                                 OwnLen, FileName[1], OpenMode, MaxKeyLen);
  498.                   IF (Status = BtNotFoundErr) THEN     { Create the file      }
  499.                   BEGIN
  500.                      IF (OwnLen = 0) OR
  501.                         (StrICmp(MulPtr[h]^.FileDesc.Owner, Owner) = 0) THEN
  502.                      BEGIN
  503.                         IF GetFileDesc(FDesc, MulPtr[h]^.FileDesc) THEN
  504.                         BEGIN
  505.                            Len:= SIZEOF(FileSpecType);
  506.                            Status:= Btrv(CreateOp, MulPtr[h]^.PosBlock,
  507.                              MulPtr[h]^.FileDesc.FileData, Len, FileName[1], 0,
  508.                                 MaxKeyLen);
  509.                            IF (Status = NoErr) THEN    { Open the file        }
  510.                               Status:= Btrv(OpenOp, MulPtr[h]^.PosBlock,
  511.                                      FileOwner, OwnLen, FileName[1], OpenMode,
  512.                                         MaxKeyLen);
  513.                            IF ((Status = NoErr) AND (OwnLen > 0)) THEN
  514.                               SetOwner(h, Owner, MulPtr[h]^.FileDesc.AllowRO,
  515.                                        MulPtr[h]^.FileDesc.Encrypt);
  516.                         END; {if}
  517.                      END
  518.                      ELSE Status:= OwnerMatchErr;
  519.                   END; {IF Status = BtNotFoundErr}
  520.                   SetErr(Status);
  521.                   IF IndexError THEN EXIT;              { Status has been set }
  522.                   Stat(h);
  523.                   SetErr(Status);
  524.                   IF IndexError THEN EXIT;              { Status has been set }
  525.  
  526.                   { See if we can allocate the key buffer pointer array }
  527.  
  528.                   MulPtr[h]^.KeyPtr:= NIL;
  529.                   MulPtr[h]^.KeysAlloc:= 0;
  530.                   Indexes:= MulPtr[h]^.FileDesc.FileData.NumIndexes;
  531.                   TotKeyMem:= SIZEOF(POINTER) * Indexes;
  532.                   IF (MAXAVAIL <= TotKeyMem) THEN
  533.                   BEGIN
  534.                      CleanUp;
  535.                      EXIT;
  536.                   END; {if}
  537.  
  538.                   { Try to allocate the key buffer pointer array }
  539.  
  540.                   GETMEM(MulPtr[h]^.KeyPtr, TotKeyMem);
  541.                   IF (MulPtr[h]^.KeyPtr = NIL) THEN
  542.                   BEGIN
  543.                      CleanUp;
  544.                      EXIT;
  545.                   END; {if}
  546.  
  547.                   { Now calculate the memory needed for the key buffers }
  548.  
  549.                   WITH MulPtr[h]^.FileDesc.FileData DO
  550.                   BEGIN
  551.                      y:= 0;
  552.                      TotKeyMem:= 0;
  553.                      FOR x:= 1 to Indexes DO
  554.                      BEGIN
  555.                         MemNeeded[x]:= SIZEOF(KBLType);
  556.                         REPEAT
  557.                              INC(y);
  558.                              INC(MemNeeded[x], KeyBuf[y].Len);
  559.                         UNTIL NOT BitIsSet(KeyBuf[y].Flags, SegmentedBit);
  560.                         INC(TotKeyMem, MemNeeded[x]);
  561.                      END; {for}
  562.                   END; {with}
  563.  
  564.                   { See if there is memory for them }
  565.  
  566.                   IF (MAXAVAIL < TotKeyMem) THEN
  567.                   BEGIN
  568.                      CleanUp;
  569.                      EXIT;
  570.                   END; {if}
  571.  
  572.                   { now actually allocate them }
  573.  
  574.                   x:= 0;
  575.                   MemError:= FALSE;
  576.                   WITH MulPtr[h]^ DO
  577.                     WHILE (x < Indexes) AND (NOT MemError) DO
  578.                     BEGIN
  579.                        INC(x);
  580.                        GETMEM(KeyPtr^[x], MemNeeded[x]);
  581.                        MemError:= (KeyPtr^[x] = NIL);
  582.                        IF NOT MemError THEN
  583.                        BEGIN
  584.                          KeyPtr^[x]^.BufferLen:= MemNeeded[x] - SIZEOF(KBLType);
  585.                          INC(KeysAlloc);
  586.                          FILLCHAR(KeyPtr^[x]^.KeyBuffer,
  587.                                   MemNeeded[x] - SIZEOF(KBLType), 0);
  588.                        END; {if}
  589.                     END; {with while}
  590.                   IF MemError THEN
  591.                   BEGIN
  592.                      CleanUp;
  593.                      EXIT;
  594.                   END; {if}
  595.  
  596.                   { All went well so set up variables in master records }
  597.  
  598.                   OpenFile:= h;
  599.                   MulPtr[h]^.LastKey := 1;           { store SUCC of last key }
  600.                   MulPtr[h]^.LockAttr:= FileLocks;            { default locks }
  601.                   MulPtr[h]^.FileName:= FileName;                  { filename }
  602.                   MulPtr[h]^.OpenMode:= LO(OpenMode);
  603.                   MulPtr[h]^.FileDesc.Owner:= Owner;
  604.                END
  605.                ELSE SetErr(NoMemoryErr);                  { Not enough memory }
  606.             END {if}
  607.             ELSE SetErr(NoMemoryErr);                     { Not enough memory }
  608.          END {if}
  609.          ELSE SetErr(NoHandlesErr);                         { No more handles }
  610.       END {if}
  611.       ELSE SetErr(NotActiveErr);                 { Mulkey not initialized yet }
  612. END; {OpenFile}
  613.  
  614. PROCEDURE ExtendFile(VAR Handle: WORD;
  615.                       FileName : PathStr;
  616.                       ExtendNow: BOOLEAN);
  617.  
  618. VAR
  619.       m    : WORD;
  620.       Owner: FOwnerStr;
  621.  
  622. BEGIN {ExtendFile}
  623.       IF HandleActive(Handle) THEN
  624.       BEGIN
  625.           AsciiZ(FileName);
  626.           IF ExtendNow THEN m:= ExtNowCode ELSE m:= ExtLaterCode;
  627.           SetErr(Btrv(ExtendOp, MulPtr[Handle]^.PosBlock, m, m, FileName, m,
  628.                       MaxKeyLen));
  629.           IF NOT IndexError THEN
  630.           BEGIN
  631.               m:= MulPtr[Handle]^.OpenMode;
  632.               Owner:= MulPtr[Handle]^.FileDesc.Owner;
  633.               FileName:= MulPtr[Handle]^.FileName;
  634.               CloseFile(Handle);
  635.               Handle:= OpenFile('', m, Owner, FileName);
  636.           END; {if}
  637.       END
  638.       ELSE SetErr(BadHandleErr);
  639. END; {ExtendFile}
  640.  
  641. PROCEDURE AUD_Record(Handle: WORD;
  642.                       VAR d;
  643.                        Code: BYTE);
  644. VAR
  645.       Len,
  646.       Status: WORD;
  647.  
  648. BEGIN {AUD_Record}
  649.       IF HandleActive(Handle) THEN
  650.          WITH MulPtr[Handle]^ DO
  651.          BEGIN
  652.              Len:= FileDesc.FileData.RecordLen;
  653.              Status:= Btrv(Code, PosBlock, d, Len, KeyPtr^[LastKey]^.KeyBuffer,
  654.                            PRED(LastKey), KeyPtr^[LastKey]^.BufferLen);
  655.              Changed:= TRUE;
  656.          END
  657.       ELSE Status:= BadHandleErr;
  658.       SetErr(Status);
  659. END; {AUD_Record}
  660.  
  661. PROCEDURE AddRecord; EXTERNAL;
  662.  
  663. PROCEDURE UpdateRecord; EXTERNAL;
  664.  
  665. PROCEDURE DeleteRecord; EXTERNAL;
  666.  
  667. {$L MULAUD.OBJ}
  668.  
  669. FUNCTION GetPosition(Handle: WORD): LONGINT;
  670.  
  671. VAR
  672.       d,
  673.       Status: WORD;
  674.       p     : LONGINT;
  675.  
  676. BEGIN {GetPosition}
  677.       IF HandleActive(Handle) THEN
  678.          Status:= Btrv(GetPositionOp, MulPtr[Handle]^.PosBlock, p, d, d, 0,
  679.                        SIZEOF(d))
  680.             ELSE Status:= BadHandleErr;
  681.       SetErr(Status);
  682.       IF IndexError THEN GetPosition:= 0 ELSE GetPosition:= p;
  683. END; {GetPosition}
  684.  
  685. FUNCTION NumRecords(Handle: WORD): LONGINT;
  686.  
  687. BEGIN {NumRecords}
  688.       IF HandleActive(Handle) THEN
  689.       BEGIN
  690.           IF MulPtr[Handle]^.Changed THEN Stat(Handle) ELSE SetErr(NoErr);
  691.       END
  692.       ELSE SetErr(BadHandleErr);
  693.       IF IndexError THEN NumRecords:= 0
  694.          ELSE NumRecords:= MulPtr[Handle]^.FileDesc.FileData.NumRecs;
  695. END; {NumRecords}
  696.  
  697. { The following are the get record procedures, they are filtered by procedure }
  698. { RPN_Record, which does the work.  This is where KeyOnly gets reset, not in  }
  699. { AdjOp().  If KeyOnly was set, it is set to FALSE after function execution.  }
  700.  
  701. PROCEDURE RPN_Record(Handle: WORD;
  702.                      KeyNum: WORD;
  703.                       VAR k;
  704.                       VAR d;
  705.                        Code: BYTE);
  706. VAR
  707.       Len,
  708.       Status: WORD;
  709.  
  710. BEGIN {RPN_record}
  711.       IF HandleActive(Handle) THEN
  712.       BEGIN
  713.          WITH MulPtr[Handle]^ DO
  714.          BEGIN
  715.  
  716.             { Check the key number and see if it is ok }
  717.  
  718.             IF (KeyNum <= FileDesc.FileData.NumIndexes) AND (KeyNum > 0) THEN
  719.             BEGIN
  720.  
  721.                 { if we don't use the last key buffer then put new key in.}
  722.  
  723.                 CASE Code OF
  724.                      GetEqualOp, GetGreaterOp..GetLessThanOrEqualOp:
  725.                         WITH KeyPtr^[KeyNum]^ DO MOVE(k, KeyBuffer, BufferLen);
  726.                 END; {case}
  727.                 Len:= FileDesc.FileData.RecordLen;
  728.                 Status:= Btrv(AdjOp(Code, LockAttr), PosBlock, d, Len,
  729.                               KeyPtr^[KeyNum]^.KeyBuffer, PRED(KeyNum),
  730.                               KeyPtr^[KeyNum]^.BufferLen);
  731.             END
  732.             ELSE Status:= InvalidKeyErr;
  733.          END; {with}
  734.       END
  735.       ELSE Status:= BadHandleErr;
  736.       KeyOnly:= FALSE;                  { we reset this every time it is used }
  737.       SetErr(Status);
  738. END; {RPN_Record}
  739.  
  740. PROCEDURE GetDirect(Handle: WORD;
  741.                          p: LONGINT;
  742.                      VAR d);
  743.  
  744. VAR
  745.       MovP: LONGINT ABSOLUTE d;
  746.  
  747. BEGIN {GetDirect}
  748.       IF (p <> 0) THEN
  749.       BEGIN
  750.           MovP:= p;         { Btrieve expects the position in the data buffer }
  751.           RPN_Record(Handle, MulPtr[Handle]^.LastKey, d, d, GetDirectOp);
  752.       END
  753.       ELSE SetErr(GetDirectErr);
  754. END; {GetDirect}
  755.  
  756. PROCEDURE StepDirect(Handle: WORD; VAR d);
  757.  
  758. BEGIN {StepDirect}
  759.       RPN_Record(Handle, MulPtr[Handle]^.LastKey, d, d, StepDirectOp);
  760. END; {StepDirect}
  761.  
  762. PROCEDURE GetLowest; EXTERNAL;
  763.  
  764. PROCEDURE GetHighest; EXTERNAL;
  765.  
  766. PROCEDURE NextRecord; EXTERNAL;
  767.  
  768. PROCEDURE PrevRecord; EXTERNAL;
  769.  
  770. {$L MULGET1.OBJ}
  771.  
  772. PROCEDURE GetEqual; EXTERNAL;
  773.  
  774. PROCEDURE GetGreater; EXTERNAL;
  775.  
  776. PROCEDURE GetLessThan; EXTERNAL;
  777.  
  778. PROCEDURE GetGreaterOrEqual; EXTERNAL;
  779.  
  780. PROCEDURE GetLessThanOrEqual; EXTERNAL;
  781.  
  782. {$L MULGET2.OBJ}
  783.  
  784. FUNCTION MulkeyFileName(Handle: WORD): PathStr;
  785.  
  786. VAR
  787.       Status: WORD;
  788.  
  789. BEGIN {MulkeyFileName}
  790.       IF HandleActive(Handle) THEN Status:= NoErr ELSE Status:= BadHandleErr;
  791.       SetErr(Status);
  792.       IF IndexError THEN MulkeyFileName:= ''
  793.          ELSE MulkeyFileName:= MulPtr[Handle]^.FileName;
  794. END; {MulkeyFileName}
  795.  
  796. FUNCTION MulkeyExtension(Handle: WORD): PathStr;
  797.  
  798. VAR
  799.       Status: WORD;
  800.  
  801. BEGIN {MulkeyExtension}
  802.       IF HandleActive(Handle) THEN Status:= NoErr ELSE Status:= BadHandleErr;
  803.       SetErr(Status);
  804.       IF IndexError THEN MulkeyExtension:= ''
  805.          ELSE MulkeyExtension:= MulPtr[Handle]^.Extension;
  806. END; {MulkeyExtension}
  807.  
  808. PROCEDURE GetKeyBuffer(Handle, KeyNum: WORD; VAR k);
  809.  
  810. VAR
  811.       Status: WORD;
  812.  
  813. BEGIN {GetKeyBuffer}
  814.       IF HandleActive(Handle) THEN
  815.          WITH MulPtr[Handle]^ DO
  816.          BEGIN
  817.              IF (KeyNum <= FileDesc.FileData.NumIndexes) AND (KeyNum > 0) THEN
  818.              BEGIN
  819.                  WITH KeyPtr^[KeyNum]^ DO MOVE(k, KeyBuffer, BufferLen);
  820.                  Status:= NoErr;
  821.              END
  822.              ELSE Status:= InvalidKeyErr;
  823.          END
  824.       ELSE Status:= BadHandleErr;
  825.       SetErr(Status);
  826. END; {GetKeyBuffer}
  827.  
  828. PROCEDURE ResetFiles;
  829.  
  830. VAR
  831.       x,
  832.       Status: WORD;
  833.  
  834. BEGIN {ResetFiles}
  835.       IF MulkeyActive THEN
  836.       BEGIN
  837.           FOR x:= FirstHandle TO MaxMulkey DO CloseFile(x);
  838.           Status:= Btrv(ResetOp, x, x, x, x, 0, SIZEOF(x));
  839.       END
  840.       ELSE Status:= NotActiveErr;
  841.       SetErr(Status);
  842. END; {ResetFiles}
  843.  
  844. PROCEDURE CloseFile(Handle: WORD);
  845.  
  846. VAR
  847.       x,
  848.       d,
  849.       Status: WORD;
  850.  
  851. BEGIN {CloseFile}
  852.       IF HandleActive(Handle) THEN
  853.       BEGIN
  854.           x:= 0;
  855.           REPEAT
  856.                Status:= Btrv(CloseOp, MulPtr[Handle]^.PosBlock, d, d, d, 0,
  857.                              SIZEOF(d));
  858.                INC(x);
  859.           UNTIL (Status = NoErr) OR (x > MaxTries);
  860.           WITH MulPtr[Handle]^ DO
  861.           BEGIN
  862.               FOR x:= KeysAlloc DOWNTO 1 DO
  863.                   FREEMEM(KeyPtr^[x],
  864.                       WORD(KeyPtr^[x]^.BufferLen) + SIZEOF(KBLType));
  865.               IF (KeyPtr <> NIL) THEN
  866.                  FREEMEM(KeyPtr, SIZEOF(POINTER) * FileDesc.FileData.NumIndexes);
  867.           END; {with}
  868.           DISPOSE(MulPtr[Handle]);
  869.           MulPtr[Handle]:= NIL;
  870.       END; {if}
  871.       SetErr(NoErr);    { We never want closefile to return an error }
  872. END; {CloseFile}
  873.  
  874. FUNCTION BtError(Code: WORD): STRING;
  875.  
  876. BEGIN {BtError}
  877.       CASE Code OF
  878.  
  879.       { BTrieves error codes }
  880.  
  881.               NoErr: BtError:= '';    { None }
  882.                   1: BtError:= 'Invalid operation';
  883.                   2: BtError:= 'I/O error';
  884.                   3: BtError:= 'File not open';
  885.                   4: BtError:= 'Key not found';
  886.                   5: BtError:= 'Duplicate key error';
  887.                   6,
  888.       InvalidKeyErr: BtError:= 'Invalid key number';
  889.                   7: BtError:= 'Different key number';
  890.                   8: BtError:= 'Invalid positioning';
  891.                   9: BtError:= 'End of file';
  892.                  10: BtError:= 'Key not modifiable';
  893.                  11: BtError:= 'Invalid file name';
  894.                  12: BtError:= 'File not found';
  895.                  13: BtError:= 'Extension error';
  896.                  14: BtError:= 'Pre-open error';
  897.                  15: BtError:= 'Pre-image error';
  898.                  16: BtError:= 'Expansion error';
  899.                  17: BtError:= 'Close error';
  900.                  18: BtError:= 'Disk full';
  901.                  19: BtError:= 'Unrecoverable error';
  902.                  20: BtError:= 'Btrieve inactive';
  903.                  21: BtError:= 'Key buffer error';
  904.               22,97: BtError:= 'Bad data buffer size';
  905.                  23: BtError:= 'Bad position block size';
  906.                  24: BtError:= 'Bad page size';
  907.                  25: BtError:= 'Could not create file';
  908.                  26: BtError:= 'Bad number of keys';
  909.                  27: BtError:= 'Bad key position';
  910.                  28: BtError:= 'Bad record length';
  911.                  29: BtError:= 'Bad key length';
  912.                  30: BtError:= 'Not a Btrieve file';
  913.                  31,
  914.               32,34: BtError:= 'File extend error';
  915.                  35: BtError:= 'Directory error';
  916.               36,37,
  917.               38,39,
  918.               40,41: BtError:= 'Transaction error';
  919.                  42: BtError:= 'Incomplete accelerated access';
  920.                  43: BtError:= 'Invalid data record address';
  921.                  44: BtError:= 'Null key path';
  922.                  45: BtError:= 'Inconsistent key flags';
  923.                  46: BtError:= 'Access denied';
  924.                  47: BtError:= 'Maximum open files';
  925.                  48: BtError:= 'Invalid alternate sequence';
  926.                  49: BtError:= 'Key type error';
  927.                  50: BtError:= 'Owner already set';
  928.                  51,
  929.       OwnerMatchErr: BtError:= 'Invalid owner';
  930.                  52: BtError:= 'Error writing cache';
  931.                  53: BtError:= 'Invalid interface';
  932.                  54: BtError:= 'Variable page unreadable';
  933.                  56: BtError:= 'Incomplete index';
  934.                  57: BtError:= 'Expanded memory error';
  935.                  80: BtError:= 'Conflict - reread record';
  936.                  81: BtError:= 'Lock full';
  937.                  82: BtError:= 'Lost position';
  938.                  83: BtError:= 'Read outside transaction';
  939.                  84: BtError:= 'Record in use';
  940.                  85: BtError:= 'File in use';
  941.                  86: BtError:= 'File full';
  942.                  87: BtError:= 'Handle full';
  943.                  88: BtError:= 'Mode error';
  944.                  89: BtError:= 'Name error';
  945.                  90: BtError:= 'Device full';
  946.                  91: BtError:= 'Server error';
  947.                  92: BtError:= 'Transaction full';
  948.                  93: BtError:= 'Incompatible lock type';
  949.                  99: BtError:= 'Demo error';
  950.  
  951.       { Mulkey error codes }
  952.  
  953.             GetDirectErr: BtError:= 'No valid position in get direct';
  954.             NotActiveErr: BtError:= 'Mulkey not active';
  955.             BadHandleErr: BtError:= 'Handle not active';
  956.                 GetOpErr: BtError:= 'Error in get operation';
  957.             NoHandlesErr: BtError:= 'No more Mulkey handles';
  958.              NoMemoryErr: BtError:= 'Not enough memory';
  959.            MulkeyInitErr: BtError:= 'Could not initialize Mulkey';
  960.                      ELSE BtError:= 'Unknown error';
  961.       END; {case}
  962. END; {BtError}
  963.  
  964. FUNCTION MulkeyError: STRING;  { Last error of Mulkey }
  965.  
  966. BEGIN {MulkeyError}
  967.       IF (LastErrCode = 0) THEN MulkeyError:= 'NO MULKEY ERROR'
  968.          ELSE MulkeyError:= 'MULKEY ERROR: ' + BtError(LastErrCode);
  969. END; {MulkeyError}
  970.  
  971. {$F+}
  972. PROCEDURE CloseMulkey;
  973. {$F-}
  974.  
  975. VAR
  976.       x: WORD;
  977.  
  978. BEGIN {CloseMulkey}
  979.       FOR x:= FirstHandle TO MaxMulkey DO CloseFile(x);
  980.       EXITPROC:= ExitSave;
  981. END; {CloseMulkey}
  982.  
  983. PROCEDURE InitMulkey;
  984.  
  985. VAR
  986.       x,
  987.       Dummy: WORD;
  988.       BtVer: BtVerType;
  989.       n,
  990.       Min,
  991.       Maj  : STRING[8];
  992.  
  993. BEGIN {InitMulkey}
  994.       x:= SIZEOF(BtVerType);
  995.       SetErr(Btrv(VersionOp, Dummy, BtVer, x, Dummy, 0, SIZEOF(Dummy)));
  996.       MulkeyActive:= (NOT IndexError) AND
  997.                      ((BtVer.Version > MinVersion) OR
  998.                       ((BtVer.Version = MinVersion) AND
  999.                        (BtVer.Revision >= MinRevision))) AND
  1000.                      ((BtVer.Net = 'N') OR (BtVer.Net = ' '));
  1001.  
  1002.       { Initialize the array of pointers EVEN if NOT MulkeyActive! }
  1003.  
  1004.       FILLCHAR(MulPtr, SIZEOF(MulPtr), 0);       { sets all to NIL }
  1005.  
  1006.       { set up MulkeyVersion string for export }
  1007.  
  1008.       STR(MajorVersion, Maj);
  1009.       STR(MinorVersion:2, Min);
  1010.       IF (Min[1] = ' ') THEN Min[1]:= '0';
  1011.       MulkeyVersion:= 'Mulkey version ' + Maj + '.' + Min + Revision + '  ' +
  1012.                        MulkeyDate;
  1013.  
  1014.       IF MulkeyActive THEN
  1015.       BEGIN
  1016.  
  1017.           { set up BtVersion string for export }
  1018.  
  1019.           IF (BtVer.Net = 'N') THEN n:= 'network ' ELSE n:= '';
  1020.           STR(BtVer.Version, Maj);
  1021.           STR(BtVer.Revision, Min);
  1022.           BtVersion:= 'Btrieve ' + n + 'file manager version ' + Maj +'.'+ Min;
  1023.  
  1024.           SetErr(NoErr);
  1025.  
  1026.           { set the exit procedure address }
  1027.  
  1028.           ExitSave:= EXITPROC;
  1029.           EXITPROC:= @CloseMulkey;
  1030.       END
  1031.       ELSE
  1032.       BEGIN
  1033.           SetErr(MulkeyInitErr);
  1034.           BtVersion:= '';
  1035.       END; {elseif}
  1036. END; {InitMulkey}
  1037.  
  1038. BEGIN {Init Mulkey}
  1039.       InitMulkey;
  1040. END. {Init Mulkey}
  1041.