home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / pascal / library / dos / btree / tree / btree1.inc < prev    next >
Encoding:
Text File  |  1989-06-27  |  54.1 KB  |  1,259 lines

  1. (*****************************************************************************)
  2. (*                                                                           *)
  3. (*   I N S E R T  /  D E L E T E  /  M I S C    B T R E E   R O U T I N E S  *)
  4. (*                                                                           *)
  5. (*****************************************************************************)
  6.  
  7. (* These definitions and routines are the 'guts' of the BTREE unit.  This
  8.    contain all routines which manipulate the index nodes (physical records)
  9.    and bitmaps, put values and logical record numbers in and out of the
  10.    indexes and perform other important functions.  Most of the routines in
  11.    this file are internal to the BTREE unit, although several are not.       *)
  12.  
  13. const
  14.     NULL     : RecordNumber = 0;                   (* used to dilineate null
  15.                                                                record number *)
  16.  
  17. type
  18.     NodeType = (INVALIDNODETYPE,INDEXNODE,SEQUENCENODE);
  19.                              (* INVALIDNODETYPE is not used for anything.
  20.                                 It serves one purpose.  It gives positive
  21.                                 values to the two remaining legal values
  22.                                 for this enumerated type.  I didn't want
  23.                                 to have zero be valid.  This helped in
  24.                                 debugging and there is no reason to
  25.                                 change it.                                   *)
  26.  
  27.  
  28. (* These parameters are contained in the first record (0) in the index file
  29.  
  30.      variable        parameter               type         range
  31.      --------        ---------               ----         -----
  32.       userData       user data array      UserDataArray   N/A
  33.       version        version info         VersionString   N/A
  34.       nextAvail      next available node  NodePtrType     0 - MAXLONGINT
  35.       firstBMRec     first bitmap record  PrNumber        0 - MAXLONGINT
  36.       lastBMRec      last bitmap record   PrNumber        0 - MAXLONGINT
  37.       fType          file type              FileTypes       INDEX,DATA,
  38.                                                             LLIST,VLRDATA
  39.       vSize          value size           Byte            1 - 245
  40.       dummy          place holder         Byte            N/A
  41.       rNode          root node            Longint         1 - MAXLONGINT
  42.       fSNode         first sequence node  Longint         1 - MAXLONGINT
  43.       lSNODE         last  sequence node  LongInt         1 - MAXLONGINT
  44.       vType          value type           Byte            0 = INVALIDVALUE
  45.                                                           1 = BYTEVALUE
  46.                                                           2 = SHORTINTVALUE
  47.                                                           3 = INTEGERVALUE
  48.                                                           4 = LONGINTVALUE
  49.                                                           5 = WORDVALUE
  50.                                                           6 = STRINGVALUE
  51.                                                           7 = REALVALUE
  52.                                                           8 = SINGLEVALUE
  53.                                                           9 = DOUBLEVALUE
  54.                                                          10 = EXTENDEDVALUE
  55.                                                          11 = COMPVALUE
  56.                                                          12 = BYTEARRAYVALUE
  57.       cursor         tree cursor info     TreeCursor     N/A                 *)
  58.  
  59. (*\*)
  60. type
  61.     NodePtrType   = PrNumber;                (* pointer to index records     *)
  62.  
  63.     TreeCursor = record
  64.          prNum : PrNumber;
  65.          entryNum : Byte;
  66.          valid : boolean;
  67.          end;
  68.  
  69.     ParameterRecord = record
  70.          userData   : UserDataArray;                     (* for use by users *)
  71.          version    : VersionString;               (* version of TBTREE used
  72.                                                              to create index *)
  73.          nextAvail  : NodePtrType;        (* next index node available       *)
  74.          firstBMRec : PrNumber;           (* first record used for bitmap    *)
  75.          lastBMRec  : PrNumber;           (* last record used for bitmap     *)
  76.          fType      : FileTypes;          (* type of file                    *)
  77.          vSize      : VSizeType;
  78.          dummy      : Byte;               (* This came about because of the
  79.                                              change in the definition of
  80.                                              vSize.  vSize was previously
  81.                                              defined to be in the range 1 ..
  82.                                              494 but has been changed to be
  83.                                              between 1 and 245.  It used to
  84.                                              take up a word, now it takes up
  85.                                              only a byte.  dummy serves as a
  86.                                              place holder so that the rest of
  87.                                              the parameter record would not
  88.                                              shift one byte to the left.     *)
  89.          rNode      : NodePtrType;
  90.          fSNode     : NodePtrType;
  91.          lSNode     : NodePtrType;
  92.          vType      : ValueType;
  93.          cursor     : TreeCursor;
  94.          end;
  95.  
  96.  
  97. (* These parameters is found in every index and sequence node in the index
  98.    file.
  99.  
  100.      variable      parameter       location   size   type    range
  101.      --------      ---------       --------   ----   ----    -----
  102.       prev         prev sequence     503        4    int     -1 - MAXINT
  103.                       node
  104.  
  105.       next         next sequence     507        4    int     -1 - MAXINT
  106.                       node
  107.  
  108.       nType        node type         511        1    Byte    0 = INVALIDNODETYPE
  109.                                                              1 = INDEXNODE
  110.                                                              2 = SEQUENCENODE
  111.  
  112.       vCnt         value count       512        1    Byte    1 - MAXBYTE     *)
  113.  
  114.  
  115. const
  116.     PREVLOC   = 503;
  117.     NEXTLOC   = 507;
  118.     NTYPELOC  = 511;
  119.     VCNTLOC   = 512;
  120.     MAXUSABLE = 502;  (* how much can be used for entries and record numbers *)
  121.  
  122.  
  123. var
  124.     mustMoveCursor : Boolean;
  125.  
  126. (*\*)
  127. (* This routine will return the record number for the first unused index
  128.    record (node).  If the first unused node is the first used bitmap record
  129.    then the bitmap records will be moved down to free up disk space.  The
  130.    number of physical pages freed up depends on the size of the index file   *)
  131.  
  132. function FirstUnusedIndexRecord(var iFName : FnString;
  133.                                                        (* var for speed only *)
  134.                                 var pRec : ParameterRecord) : NodePtrtype;
  135.  
  136. var
  137.     prNum : PrNumber;
  138.     byteNum : PageRange;
  139.     bitNum : BytePosition;
  140.     newRecord : NodePtrType;                 (* record number to be returned *)
  141.     recsToMove : PrNumber;
  142.  
  143.     begin
  144.     newRecord := pRec.nextAvail;                  (* record number to return *)
  145.     pRec.nextAvail := FindNextAvailInBitmap(iFName,pRec.firstBMRec,
  146.                                             pRec.lastBMRec,newRecord);
  147.     if newRecord = pRec.firstBMRec then
  148.         begin                                 (* need to move bitmap records *)
  149.         if newRecord <= 4 then
  150.             begin
  151.             recsToMove := 1;
  152.             end
  153.         else
  154.             begin
  155.             if newRecord <= 10 then
  156.                 begin
  157.                 recsToMove := 3;
  158.                 end
  159.             else
  160.                 begin
  161.                 recsToMove := 5;
  162.                 end;
  163.             end;
  164.         MoveRecords(iFName,pRec.firstBMRec,pRec.lastBMRec,recsToMove);
  165.         end;
  166.     FirstUnUsedIndexRecord := newRecord;         (* record number to return *)
  167.     end;                            (* end of FirstUnusedIndexRecord routine *)
  168.  
  169. (*\*)
  170. (* This routine will delete a node from the index file by setting the
  171.    appropriate bitmap bit to zero                                            *)
  172.  
  173. procedure DeleteIndexRecord(var iFName : FnString;     (* var for speed only *)
  174.                             thisNode : NodePtrType;
  175.                             var pRec : ParameterRecord);
  176.  
  177. var
  178.     page : SinglePage;
  179.  
  180.     begin
  181.     SetBitInBitmap(iFName,pRec.firstBMRec,thisNode,0);     (* mark as unused *)
  182.     ReleasePage(iFName,thisNode);  (* more for efficiency  .. not required   *)
  183.     if thisNode < pRec.nextAvail then
  184.         begin
  185.         pRec.nextAvail := thisNode;
  186.         end;
  187.     end;                                 (* end of DeleteIndexRecord routine *)
  188.  
  189. (*\*)
  190. (* This routine will insert a node between prevNode and nextNode in a node list.
  191.    It will accomplish this by setting the prev and next ptrs as necessary
  192.    for a node and its prev and next nodes.  Obviously, the node ptr and the
  193.    next and prev node pointers must be known.  If the node type is
  194.    SEQUENCENODE and this node is the first node in the sequential list, the
  195.    parameter record will be updated to reflect this change (the sNode parameter
  196.    will be set to this node ).                                               *)
  197.  
  198. procedure InsertNodeInList(var iFName : FnString;      (* var for speed only *)
  199.                            thisNode : NodePtrType;
  200.                            var prevNode;
  201.                            var nextNode;
  202.                            var pRec : ParameterRecord);
  203.  
  204. var
  205.     page : SinglePage;
  206.     tempPrevNode,
  207.     tempNextNode : NodePtrType;
  208.  
  209.     begin
  210.     Move(prevNode,tempPrevNode,RNSIZE);
  211.     Move(nextNode,tempNextNode,RNSIZE);
  212.     FetchPage(iFName,thisNode,page);
  213.     Move(prevNode,page[PREVLOC],RNSIZE);
  214.     Move(nextNode,page[NEXTLOC],RNSIZE);
  215.     StorePage(iFName,thisNode,page);
  216.     if tempPrevNode <> NULL then
  217.         begin
  218.         FetchPage(iFName,tempPrevNode,page);
  219.         Move(thisNode,page[NEXTLOC],RNSIZE);
  220.         StorePage(iFName,tempPrevNode,page);
  221.         end
  222.     else
  223.         begin                               (* new node is first node *)
  224.         if page[NTYPELOC] = Byte(SEQUENCENODE) then
  225.             begin             (* set first seq node pointer to this new node *)
  226.             pRec.fSNode := thisNode;
  227.             end;
  228.         end;
  229.     if tempNextNode <> NULL then
  230.         begin
  231.         FetchPage(iFName,tempNextNode,page);
  232.         Move(thisNode,page[PREVLOC],RNSIZE);
  233.         StorePage(iFName,tempNextNode,page);
  234.         end
  235.     else
  236.         begin                                       (* new node is last node *)
  237.         if page[NTYPELOC] = Byte(SEQUENCENODE) then
  238.             begin              (* set last seq node pointer to this new node *)
  239.             pRec.lSNode := thisNode;
  240.             end;
  241.         end;
  242.     end;                                  (* end of InsertNodeInList routine *)
  243.  
  244. (*\*)
  245. (* This routine will delete a node from a node list and set its neighbors prev
  246.    and next node pointers as appropriate.  It will also delete the record from
  247.    the index file to allow it to be reused.                                  *)
  248.  
  249. procedure DeleteNodeFromList(var iFName : FnString;    (* var for speed only *)
  250.                              thisNode : NodePtrType;
  251.                              var pRec : ParameterRecord);
  252.  
  253. var
  254.     page : SinglePage;
  255.     prevNode,
  256.     nextNode : NodePtrType;
  257.  
  258.     begin
  259.     FetchPage(iFName,thisNode,page);
  260.     Move(page[PREVLOC],prevNode,RNSIZE);                (* get Prev node ptr *)
  261.     Move(page[NEXTLOC],nextNode,RNSIZE);                (* get Next node ptr *)
  262.     if prevNode <> NULL then
  263.         begin
  264.         FetchPage(iFName,prevNode,page);
  265.         Move(nextNode,page[NEXTLOC],RNSIZE);
  266.         StorePage(iFName,prevNode,page);
  267.         end
  268.     else
  269.         begin
  270.         if NodeType(page[NTYPELOC]) = SEQUENCENODE then
  271.             begin
  272.             pRec.fSNode := nextNode;
  273.             end;
  274.         end;
  275.     if nextNode <> NULL then
  276.         begin
  277.         FetchPage(iFName,nextNode,page);
  278.         Move(prevNode,page[PREVLOC],RNSIZE);
  279.         StorePage(iFName,nextNode,page);
  280.         end
  281.     else
  282.         begin
  283.         if NodeType(page[NTYPELOC]) = SEQUENCENODE then
  284.             begin
  285.             pRec.lSNode := nextNode;
  286.             end;
  287.         end;
  288.     DeleteIndexRecord(iFName,thisNode,pRec);          (* get rid of phys rec *)
  289.     end;                                        (* end of DeleteNodeFromList *)
  290.  
  291. (*\*)
  292. (* This routine will create a new node and set the node type parameter
  293.    and will insert this node between the prev node and the next node
  294.    in the node linked list.  Remember, this level linked list is required to
  295.    facilitate deletions.                                                     *)
  296.  
  297. function CreatedNode(var iFName : FnString;            (* var for speed only *)
  298.                      var prevNode;
  299.                      var nextNode;
  300.                      nType : NodeType;
  301.                      var pRec : ParameterRecord) : NodePtrType;
  302.  
  303. var
  304.     page : SinglePage;
  305.     newNode : NodePtrType;
  306.  
  307.     begin
  308.     newNode := FirstUnUsedIndexRecord(iFName,pRec);
  309.     FillChar(page,PAGESIZE,0);
  310.     page[NTYPELOC] := Byte(nType);                      (* set the node type *)
  311.     StorePage(iFName,newNode,page);    (* will create new node automatically *)
  312.     InsertNodeInList(iFName,newNode,prevNode,nextNode,pRec);
  313.     CreatedNode := newNode;         (* return the node ptr for this new node *)
  314.     end;                                       (* end of CreatedNode routine *)
  315.  
  316. (*\*)
  317. (* This routine will create an index file with the file name as specified
  318.    by iFName.  The valSize parameter specifies the size of the index
  319.    entries.  The easiest way to determine this is to use the SizeOf
  320.    function.  The valType parameter specifies the type for the index
  321.    entries.  The types supported are those enumerated by the ValueType
  322.    enumerated type.
  323.  
  324.    note - Extremely important - WARNING - for STRINGVALUE indexes only - the
  325.    valSize must be 1 greater than the number of characters of the longest
  326.    string.  This will allow 1 byte for the string length to be stored.
  327.    for example - if 'abc' is the longest string then valSize = 4.            *)
  328.  
  329. procedure CreateIndexFile(iFName : FnString;
  330.                           valSize : VSizeType;
  331.                           valType : ValueType);
  332.  
  333. var
  334.     pRec : ParameterRecord;
  335.     page : SinglePage;
  336.  
  337.     begin
  338.     CreateGenericFile(iFName);
  339.     FillChar(page,PAGESIZE,0);
  340.     StorePage(iFName,0,page);                            (* parameter record *)
  341.     StorePage(iFName,1,page);                               (* bitmap record *)
  342.     FillChar(pRec.userData,SizeOf(pRec.userData),0);
  343.     pRec.version := CURRENTVERSION;
  344.     pRec.nextAvail  := 1;
  345.     pRec.firstBMRec := 1;
  346.     pRec.lastBMRec  := 1;
  347.     pRec.fType := INDEX;
  348.     pRec.vSize := valSize;
  349.     pRec.dummy := 0;
  350.     pRec.rNode := CreatedNode(iFName,NULL,NULL,INDEXNODE,pRec);      (* create
  351.                                                                         root *)
  352.     pRec.fSNode := NULL;
  353.     pRec.lSNode := NULL;
  354.     pRec.fSNode := CreatedNode(iFName,NULL,NULL,SEQUENCENODE,pRec);  (* create
  355.                                                          first Sequence node *)
  356.     FetchPage(iFName,pRec.rNode,page);                      (* get root page *)
  357.     Move(pRec.fSNode,page[1],RNSIZE);                (* put seq node in root *)
  358.     StorePage(iFName,pRec.rNode,page);                     (* store the root *)
  359.     pRec.vType := valType;
  360.     pRec.cursor.prNum := 0;
  361.     pRec.cursor.entryNum := 0;
  362.     pRec.cursor.valid := FALSE;
  363.     SaveFileParameters(iFName,pRec,SizeOf(pRec));          (* write parameters
  364.                                                               back to buffer *)
  365.     end;                                       (* end of CreateIndex routine *)
  366.  
  367. (*\*)
  368. (* This routine will delete an index file.                                   *)
  369.  
  370. procedure DeleteIndexFile(iFName : FnString);
  371.  
  372.     begin
  373.     DeleteGenericFile(iFName);
  374.     end;                                   (* end of DeleteIndexFile routine *)
  375.  
  376. (* This routine will calculate and return the proper byte pointer position for
  377.    the given entry number.  The byte pointer position will be equal to the
  378.    location of the node pointer, not the value.                              *)
  379.  
  380. function BytePointerPosition(cnt : Byte;
  381.                              vSize : VSizeType) : PageRange;
  382.  
  383.     begin
  384.     BytePointerPosition := ((cnt - 1) * (vSize + RNSIZE)) + 1;
  385.     end;                                       (* end of BytePointerPosition *)
  386.  
  387. (*\*)
  388. (* This routine will return the entry number for the first entry in the node
  389.    which has a value equal to paramValue.  If no value matches paramValue, the
  390.    first entry which has a value greater than paramValue will be returned.  If
  391.    paramValue is greater than the last value in the node, then the last entry
  392.    number + 1 will be returned.  The routine will return 0 iff the particular
  393.    node contains no entries.                                                 *)
  394.  
  395. function BinarySearchEntry(var pg : SinglePage;        (* var for speed only *)
  396.                            var paramValue;
  397.                            var pRec : ParameterRecord  (* var for speed only *)
  398.                            ) : Byte;
  399.  
  400. var
  401.     startCnt,
  402.     midCnt,
  403.     maxCnt : Byte;
  404.  
  405.     begin
  406.     maxCnt := pg[VCNTLOC];
  407.     if maxCnt = 0 then
  408.         begin
  409.         BinarySearchEntry := 0;
  410.         Exit;
  411.         end;
  412.     if CompareValues(pg[RNSIZE + 1],paramValue,pRec.vType) <> LESSTHAN then
  413.         begin
  414.         BinarySearchEntry := 1;
  415.         Exit;
  416.         end;
  417.     if CompareValues(pg[((maxCnt - 1) * (pRec.vSize + RNSIZE)) +
  418.                          RNSIZE + 1],
  419.                      paramValue,
  420.                      pRec.vType) = LESSTHAN then
  421.         begin
  422.         BinarySearchEntry := maxCnt + 1;
  423.         Exit;
  424.         end;
  425.     startCnt := 1;
  426.     while startCnt < (maxCnt - 1) do
  427.         begin
  428.         midCnt := (maxCnt + startCnt) Div 2;
  429.         if CompareValues(pg[((midCnt - 1) * (pRec.vSize + RNSIZE)) +
  430.                          RNSIZE + 1],
  431.                          paramValue,
  432.                          pRec.vType) = LESSTHAN then
  433.             begin
  434.             startCnt := midCnt;
  435.             end
  436.         else
  437.             begin
  438.             maxCnt := midCnt;
  439.             end;
  440.         end;
  441.     BinarySearchEntry := maxCnt;
  442.     end;                                 (* end of BinarySearchEntry routine *)
  443.  
  444. (*\*)
  445. (* This routine will search an index node and return the record number for the
  446.    next lower node corresponding to the paramValue.  The returned node will
  447.    either be another index node or a sequence node.
  448.  
  449.    Note : this assumes that there are lower nodes.  Prior to calling this
  450.    routine check for an empty root                                           *)
  451.  
  452. function FindNextLevelPtr(var page : SinglePage;       (* var for speed only *)
  453.                           var paramValue;
  454.                           var pRec : ParameterRecord   (* var for speed only *)
  455.                           ) : NodePtrType;
  456.  
  457.  var
  458.      cnt : Byte;
  459.      bytePtr : PageRange;
  460.      p : NodePtrType;                 (* temporarily holds pointer to return *)
  461.  
  462.      begin
  463.      cnt := BinarySearchEntry(page,paramValue,pRec);
  464.      if cnt = 0 then
  465.          begin
  466.          bytePtr := 1;
  467.          end
  468.      else
  469.          begin
  470.          bytePtr := BytePointerPosition(cnt,pRec.vSize);
  471.          end;
  472.      Move(page[bytePtr],p,RNSIZE);                     (* ptr to be returned *)
  473.      FindNextLevelPtr := p;
  474.      end;                                 (* end of FindNextLevelPtr routine *)
  475.  
  476. (*\*)
  477. (* This recursive routine will start at the specified node (rNum) and work
  478.    down the tree until the correct sequence node is found.  It will return
  479.    the record number of the sequence node.
  480.  
  481.    This routine assumes that as long as an index node is not empty, there
  482.    should be one more pointer than there are values.  In other words, there
  483.    is always a trailing valid pointer which takes care of the case of values
  484.    larger than the largest value in the tree.
  485.  
  486.    This routine also assumes that some sequence node exists.  This will not
  487.    work for an empty root.  This must be checked by caller.                  *)
  488.  
  489. Function FindSNode(var iFName : FnString;              (* var for speed only *)
  490.                    rNum : NodePtrType;
  491.                    var paramValue;
  492.                    var pRec : ParameterRecord          (* var for speed only *)
  493.                    ) : NodePtrType;
  494.  
  495. var
  496.     page : SinglePage;
  497.  
  498.     begin
  499.     FetchPage(iFName,rNum,page);                                 (* get node *)
  500.     if NodeType(page[NTYPELOC]) = INDEXNODE then
  501.         begin
  502.         FindSNode := FindSNode(iFName,
  503.                                FindNextLevelPtr(page,paramValue,pRec),
  504.                                paramValue,pRec);
  505.         end
  506.     else
  507.         begin
  508.         FindSNode := rNum;
  509.         end;
  510.     end;                                        (* end of FindSNode function *)
  511.  
  512. (*\*)
  513. (* This function locates the value within the index and returns a list of
  514.    logical record numbers for this associated value.  It accomplishes this by
  515.    using FindSNode to locate the sequence node.  It will fetch the sequence
  516.    set node and search until the target key is found or it is determined that
  517.    it does not exist (no entry).  If it is found all lrNumbers with a matching
  518.    target key value will be returned in lrLst.  If the matching key value is
  519.    not found then an empty list will be returned.                            *)
  520.  
  521. procedure GetEqualsFromBTree(var iFName : FnString;    (* var for speed only *)
  522.                              var paramValue;
  523.                              var lrLst : LrList;
  524.                              var pRec : ParameterRecord (* var for speed only *)
  525.                              );
  526.  
  527. var
  528.     next : PrNumber;          (* used as pointer to next sequence node *)
  529.     page : SinglePage;
  530.     done : Boolean;
  531.     cnt,                      (* used to count number of values *)
  532.     vCnt : Byte;              (* count of values in a node *)
  533.     lrNum : LrNumber;
  534.     bytePtr : PageRange;      (* used to keep track of current byte *)
  535.  
  536.     begin
  537.     CreateLrList(lrLst);
  538.     FetchPage(iFName,
  539.               FindSNode(iFName,pRec.rNode,paramValue,pRec),
  540.               page);
  541.     vCnt := page[VCNTLOC];
  542.     cnt := BinarySearchEntry(page,paramValue,pRec);
  543.     if (cnt <> 0) and (cnt <= vCnt) then
  544.         begin                                                 (* value found *)
  545.         Move(page[NEXTLOC],next,RNSIZE);
  546.         bytePtr := BytePointerPosition(cnt,pRec.vSize);
  547.         done := FALSE;
  548.         while not done do
  549.             begin
  550.             if CompareValues(paramValue,
  551.                              page[bytePtr+RNSIZE],
  552.                              pRec.vType) = EQUALTO then
  553.                 begin
  554.                 Move(page[bytePtr],lrNum,RNSIZE);
  555.                 AddToLrList(lrNum,lrLst);
  556.                 if cnt = vCnt then
  557.                     begin
  558.                     if next <> NULL then              (* if next is not null *)
  559.                         begin                      (* then get next seq node *)
  560.                         cnt := 1;       (* need to continue in case there is *)
  561.                         bytePtr := 1;             (* more than one occurrence
  562.                                                               for this value *)
  563.                         FetchPage(iFName,next,page);
  564.                         vCnt := page[VCNTLOC];
  565.                         Move(page[NEXTLOC],next,RNSIZE);
  566.                         end
  567.                     else
  568.                         begin                           (* no more seq nodes *)
  569.                         done := TRUE;
  570.                         end;
  571.                     end
  572.                 else
  573.                     begin
  574.                     bytePtr := bytePtr + pRec.vSize + RNSIZE;
  575.                     Inc(cnt);
  576.                     end;
  577.                 end
  578.             else
  579.                 begin
  580.                 done := TRUE;           (* this will get us out of this loop *)
  581.                 end;
  582.             end;
  583.         end;
  584.     end;                                (* end of GetEqualsFromBTree routine *)
  585.  
  586. (*\*)
  587. (* This routine inserts a new value into a node.  It will locate the
  588.    proper place, move all values and pointers past the spot to allow room for
  589.    the new value and pointer, and insert the new value and pointer.  If there
  590.    is a value equal to this new value, the new value will be inserted in
  591.    front of the old one.  This routine will not work if there is not enough
  592.    room in the node.  This must be checked prior to calling this routine.
  593.    This routine works with both sequence and index nodes.  It assumes that the
  594.    proper page has been read in prior to this routine being called.  This is
  595.    why a page is passed in as a parameter in lieu of physical record number.
  596.  
  597.    This works for both index and sequence nodes                              *)
  598.  
  599. procedure InsertValueIntoNode(var page : SinglePage;
  600.                               var paramValue;
  601.                               rNum : RecordNumber;
  602.                               var pRec : ParameterRecord);
  603.  
  604. var
  605.     cnt,
  606.     vCnt : Byte;
  607.     bytePtr : PageRange;
  608.  
  609.     begin
  610.     vCnt := page[VCNTLOC];                               (* get value count *)
  611.     cnt := BinarySearchEntry(page,paramValue,pRec);
  612.     if cnt = 0 then
  613.         begin                                              (* node is empty *)
  614.         bytePtr := 1;
  615.         end
  616.     else
  617.         begin
  618.         bytePtr := BytePointerPosition(cnt,pRec.vSize);
  619.         end;
  620.     FastMover(page[bytePtr],                                    (* make room *)
  621.               page[bytePtr + pRec.vSize + RNSIZE],
  622.               (((vCnt - cnt) + 1) * (pRec.vSize + RNSIZE)) + RNSIZE);
  623.     Move(rNum,page[bytePtr],RNSIZE);                       (* insert pointer *)
  624.     FastMover(paramValue,page[bytePtr + RNSIZE],pRec.vSize);   (*insert value*)
  625.     page[VCNTLOC] := vCnt + 1;                            (* new value count *)
  626.     if mustMoveCursor and (cnt <= pRec.cursor.entryNum) then
  627.         begin
  628.         Inc(pRec.cursor.entryNum);
  629.         end;
  630.     end;                               (* end of InsertValueIntoNode routine *)
  631.  
  632. (*\*)
  633. (* This function will check to see if there is room to insert a new value into
  634.    any type node.  If room exists then a routine is called to insert the
  635.    node into the proper place.  The result is returned (True or False)
  636.    This routine assumes that the proper page has been read in prior to this
  637.    routine being called.  This is why page number is used in lieu of physical
  638.    record number.                                                            *)
  639.  
  640. function InsertedInNode(var page : SinglePage;
  641.                         var paramValue;
  642.                         rNum : RecordNumber;
  643.                         var pRec : ParameterRecord) : Boolean;
  644.  
  645. var
  646.     maxValues : Byte;
  647.  
  648.     begin
  649.     maxValues := (MAXUSABLE - RNSIZE) Div (pRec.vSize + RNSIZE);
  650.     if page[VCNTLOC] < maxValues then
  651.         begin
  652.         InsertValueIntoNode(page,paramValue,rNum,pRec);
  653.         InsertedInNode := TRUE;
  654.         end
  655.     else
  656.         begin
  657.         InsertedInNode := FALSE;
  658.         end;
  659.     end;                                    (* end of InsertedInNode routine *)
  660.  
  661. (*\*)
  662. (* This routine will move n/2 (rounded down) values from the right node
  663.    (rtNode) to the empty left node (ltNode).                                 *)
  664.  
  665. procedure MoveValues(var iFName : FnString;            (* var for speed only *)
  666.                      var rtPage : SinglePage;
  667.                      var ltPage : SinglePage;
  668.                      ltNode : NodePtrType;
  669.                      var pRec : ParameterRecord);
  670.  
  671. var
  672.     bytesToMove,                            (* total number of bytes to move *)
  673.     numToMove,                                   (* number of values to move *)
  674.     vCnt : Byte;                            (* count of values in right node *)
  675.  
  676.     begin
  677.     vCnt := rtPage[VCNTLOC];                       (* get right node's count *)
  678.     numToMove := vCnt Div 2;
  679.     bytesToMove := (RNSIZE + pRec.vSize) * numToMove;     (* calc # of bytes
  680.                                                                      to move *)
  681.     FastMover(rtPage[1],ltPage[1],bytesToMove);
  682.     FastMover(rtPage[bytesToMove + 1],rtPage[1],MAXUSABLE - bytesToMove);
  683.     Dec(vCnt,numToMove);
  684.     if NodeType(rtPage[NTYPELOC]) = INDEXNODE then
  685.         begin
  686.         FillChar(rtPage[(vCnt * (pRec.vSize + RNSIZE)) + 1 + RNSIZE],
  687.                  numToMove * (pRec.vSize + RNSIZE),
  688.                  0);
  689.         end
  690.     else
  691.         begin
  692.         FillChar(rtPage[(vCnt * (pRec.vSize + RNSIZE)) + 1],
  693.                  numToMove * (pRec.vSize + RNSIZE),
  694.                  0);
  695.         end;
  696.     rtPage[VCNTLOC] := vCnt;
  697.     ltPage[VCNTLOC] := numToMove;
  698.     if mustMoveCursor then
  699.         begin
  700.         if numToMove < pRec.cursor.entryNum then
  701.             begin
  702.             Dec(pRec.cursor.entryNum,numToMove);
  703.             end
  704.         else
  705.             begin
  706.             pRec.cursor.prNum := ltNode;
  707.             end;
  708.         end;
  709.     end;                                        (* end of MoveValues routine *)
  710.  
  711. (*\*)
  712. (* This recursive routine will start at a given node (usually the root) and
  713.    follow the tree down until the correct sequence node is found.  The new
  714.    value and record number will be inserted into the correct sequence node.
  715.    In the event that the sequence node is full, the node will be split.  The
  716.    value and record number will be put in the proper node if a split occurs.
  717.    The routine will return NULL if no split occurs.  If a split occurs, the
  718.    record number (node pointer) of the newly created node will be returned.
  719.    This new node will be inserted in the parent index node.  If it won't
  720.    fit the index node will be split and the new child record number will
  721.    be inserted in the proper index node.  The value associated with the child
  722.    record number is the largest value in the newly created child node.  This
  723.    process continues until we bubble back to the root in the node.  Once at
  724.    the root the routine will return back to the original caller.  If the root
  725.    was not split then NULL will be returned.  If the root was split, then the
  726.    newly created child record number is returned.  The caller will have to
  727.    create a new root node and insert the new value and the child record
  728.    number.  Be sure that the caller also inserts the newly inserted child's
  729.    right sibling (record number only) since all indexes have one more pointer
  730.    than they do values.
  731.  
  732.    This routine expects at least one pointer in the root.  This needs to be
  733.    checked by the caller.                                                    *)
  734.  
  735. function InsertValue(var iFName : FnString;            (* var for speed only *)
  736.                      rNum : RecordNumber;    (* record number to be inserted *)
  737.                      var paramValue;         (* value to be inserted *)
  738.                      thisNode : NodePtrType; (* node *)
  739.                      var pRec : ParameterRecord) : NodePtrType;
  740.  
  741. var
  742.     newNode,                    (* newly created node if needed (node split) *)
  743.     lowerNode : NodePtrType;
  744.     thisPage,
  745.     newPage,
  746.     lowerPage : SinglePage;
  747.     lastValLoc : PageRange;       (* used to hold buffer position *)
  748.  
  749.     begin
  750.     FetchPage(iFName,thisNode,thisPage);
  751.     case NodeType(thisPage[NTYPELOC]) of
  752.         INDEXNODE:
  753.             begin
  754.             lowerNode := InsertValue(iFName,rNum,paramValue,
  755.                                      FindNextLevelPtr(thisPage,paramValue,
  756.                                                       pRec),pRec);
  757.             if lowerNode <> NULL then
  758.                 begin                     (* lower node must have been split *)
  759.                 FetchPage(iFName,lowerNode,lowerPage);
  760.                 lastValLoc := (((lowerPage[VCNTLOC] - 1)
  761.                               * ( pRec.vSize + RNSIZE)) + RNSIZE) + 1;
  762.                 if InsertedInNode(thisPage,lowerPage[lastValLoc],
  763.                                   lowerNode,pRec) then
  764.                     begin
  765.                     InsertValue := NULL;    (* node not split .. return NULL *)
  766.                     end
  767.                 else
  768.                     begin
  769.                     newNode := CreatedNode(iFName,
  770.                                            thisPage[PREVLOC],
  771.                                            thisNode,
  772.                                            INDEXNODE,
  773.                                            pRec);
  774.                     FetchPage(iFName,thisNode,thisPage);         (* required *)
  775.                     FetchPage(iFName,newNode,newPage);
  776.                     MoveValues(iFName,thisPage,newPage,newNode,pRec);
  777.                     if CompareValues(paramValue,thisPage[RNSIZE + 1],
  778.                                      pRec.vType) = GREATERTHAN then
  779.                         begin
  780.                         InsertValueIntoNode(thisPage,
  781.                                             lowerPage[lastValLoc],
  782.                                             lowerNode,pRec);
  783.                         end
  784.                     else
  785.                         begin
  786.                         InsertValueIntoNode(newPage,
  787.                                             lowerPage[lastValLoc],
  788.                                             lowerNode,pRec);
  789.                         end;
  790.                     StorePage(iFName,newNode,newPage);
  791.                     InsertValue := newNode;      (* newly added node will be
  792.                                                     returned                 *)
  793.                     end;
  794.                 StorePage(iFName,thisNode,thisPage);
  795.                 end
  796.             else
  797.                 begin
  798.                 InsertValue := NULL;    (* it fit at lower level therefore
  799.                                            this level is fine .. return NULL *)
  800.                 end;
  801.             end;
  802.         SEQUENCENODE :
  803.             begin
  804.             mustMoveCursor := pRec.cursor.valid and
  805.                               (pRec.cursor.prNum = thisNode);
  806.             if InsertedInNode(thisPage,paramValue,rNum,pRec) then
  807.                 begin
  808.                 InsertValue := NULL;   (* it fits .. no split .. return NULL *)
  809.                 end
  810.             else
  811.                 begin
  812.                 newNode := CreatedNode(iFName,
  813.                                        thisPage[PREVLOC],
  814.                                        thisNode,
  815.                                        SEQUENCENODE,
  816.                                        pRec);
  817.                 FetchPage(iFName,thisNode,thisPage);             (* required *)
  818.                 FetchPage(iFName,newNode,newPage);
  819.                 MoveValues(iFName,thisPage,newPage,newNode,pRec);
  820.                 if CompareValues(paramValue,thisPage[RNSIZE + 1],
  821.                                  pRec.vType) = GREATERTHAN then
  822.                     begin
  823.                     InsertValueIntoNode(thisPage,paramValue,rNum,pRec);
  824.                     end
  825.                 else
  826.                     begin
  827.                     InsertValueIntoNode(newPage,paramValue,rNum,pRec);
  828.                     end;
  829.                 StorePage(iFName,newNode,newPage);
  830.                 InsertValue := newNode;
  831.                 end;
  832.             StorePage(iFName,thisNode,thisPage);
  833.             mustMoveCursor := FALSE;
  834.             end;
  835.         end;                                   (* end of case statement      *)
  836.     end;                                       (* end of InsertValue routine *)
  837.  
  838. (*\*)
  839. (* This routine will insert a value and its associated logical record number
  840.    into the given index file.  This routine will guard against duplicate
  841.    entries. An index should have no more than one occurence of any
  842.    lrNum,paramValue pair (no two entries match on paramValue and lrNum).  This
  843.    routine assures this by calling DeleteValueFromBTree prior to performing
  844.    the insert.  This will get rid of a previous occurence if it exists.      *)
  845.  
  846. procedure InsertValueInBTree(iFName : FnString;
  847.                              lrNum : LRNumber;
  848.                              var paramValue);
  849.  
  850. var
  851.     lowerNode : PrNumber;
  852.     pRec : ParameterRecord;
  853.     lowerPage,
  854.     page : SinglePage;             (* used for root and first seq node pages *)
  855.     lastValLoc : PageRange;                  (* used to hold buffer position *)
  856.  
  857.     begin
  858.     DeleteValueFromBTree(iFName,lrNum,paramValue);   (* ensure no duplicates *)
  859.     FetchFileParameters(iFName,pRec,SizeOf(pRec));
  860.     lowerNode := InsertValue(iFName,lrNum,paramValue,pRec.rNode,pRec);
  861.     if lowerNode <> NULL then
  862.         begin                                (* we need to create a new root *)
  863.         pRec.rNode := CreatedNode(iFName,NULL,NULL,INDEXNODE,pRec);
  864.                                                     (* root has  no siblings *)
  865.         FetchPage(iFName,pRec.rNode,page);                  (* get root node *)
  866.         FetchPage(iFName,lowerNode,lowerPage);             (* get child node *)
  867.         lastValLoc := (((lowerPage[VCNTLOC] - 1)
  868.                          * ( pRec.vSize + RNSIZE)) + RNSIZE) + 1;
  869.         Move(lowerPage[NEXTLOC],page[1],RNSIZE);
  870.                                                (* insert ptr for right child *)
  871.         InsertValueIntoNode(page,                  (* insert child into root *)
  872.                             lowerPage[lastValLoc],
  873.                             lowerNode,pRec);
  874.         StorePage(iFName,pRec.rNode,page);
  875.         end;
  876.     SaveFileParameters(iFName,pRec,SizeOf(pRec));
  877.     end;                 (* end of InsertValueInBTree routine *)
  878.  
  879. (*\*)
  880. (* This routine will locate and delete a value and its associated record
  881.    pointer from within a node/list of nodes.  It will first locate the value.
  882.    The value will be found in this node or in succeeding nodes. The search will
  883.    continue until the value and the correct associated record number are found
  884.    or it is determined that it does not exist.If the correct value and record
  885.    number are not found then the routine will return false indicating that no
  886.    value was deleted.  If the correct value and record number are found they
  887.    will be deleted.  In this case the node where the value was deleted from
  888.    will be returned.  If the value deleted was the last in the node or the
  889.    only one in the node these facts will be returned.  This is important
  890.    because the calling node may have to alter or delete values as a result.
  891.  
  892.    note : if a node is the last node in a level node list the node will not
  893.    be deleted.  In this case the value will be deleted but last will be set
  894.    to FALSE.  This is because the parent needs to make no adjustment.        *)
  895.  
  896. function DeleteValueFromNode(var iFName : FnString;    (* var for speed only *)
  897.                              rNum : RecordNumber;
  898.                              var paramValue;
  899.                              var thisNode : NodePtrType;
  900.                              var pRec : ParameterRecord;
  901.                              var last : Boolean;
  902.                              var nodeDeleted : Boolean) : Boolean;
  903.  
  904. var
  905.     done : Boolean;
  906.     cnt,
  907.     vCnt : Byte;
  908.     bytePtr : PageRange;
  909.     page : SinglePage;
  910.     nextNode : NodePtrType;
  911.     recNum : RecordNumber;
  912.  
  913.     begin
  914.     FetchPage(iFName,thisNode,page);             (* fetch page for this node *)
  915.     vCnt := page[VCNTLOC];                                (* get value count *)
  916.     cnt := BinarySearchEntry(page,paramValue,pRec);
  917.     if (cnt <> 0) and (cnt <= vCnt) then
  918.         begin
  919.         bytePtr := BytePointerPosition(cnt,pRec.vSize);
  920.         done := FALSE;
  921.         end
  922.     else
  923.         begin
  924.         DeleteValueFromNode := FALSE;
  925.         last := FALSE;
  926.         nodeDeleted := FALSE;
  927.         done := TRUE;
  928.         end;
  929.     while not done do
  930.         begin
  931.         if CompareValues(paramValue,
  932.                          page[bytePtr + RNSIZE],
  933.                          pRec.vType) = LESSTHAN then
  934.             begin
  935.             done := TRUE;
  936.             DeleteValueFromNode := FALSE;
  937.             last := FALSE;
  938.             nodeDeleted := FALSE;
  939.             end
  940.         else
  941.             begin            (* value found .. look for record number match *)
  942.             Move(page[bytePtr],recNum,RNSIZE);
  943.             if rNum = recNum then
  944.                 begin
  945.                 done := TRUE;
  946.                 DeleteValueFromNode := TRUE;
  947.                 Move(page[NEXTLOC],nextNode,RNSIZE);
  948.                 if (vCnt = 1) and (nextNode <> NULL) then
  949.                     begin                       (* only 1 entry in this node *)
  950.                     last := TRUE;
  951.                     nodeDeleted := TRUE;
  952.                     end
  953.                 else
  954.                     begin
  955.                     page[VCNTLOC] := vCnt - 1;
  956.                     nodeDeleted := FALSE;
  957.                     FastMover(page[bytePtr + RNSIZE + pRec.vSize],
  958.                               page[bytePtr],
  959.                               (RNSIZE + pRec.vSize) * (vCnt - cnt) + RNSIZE);
  960.                     FillChar(page[(((vCnt - 1) * (pRec.vSize + RNSIZE)) + 1) +
  961.                              RNSIZE],
  962.                              pRec.vSize + RNSIZE,
  963.                              0);
  964.                     StorePage(iFName,thisNode,page);   (* store the page *)
  965.                     last := (cnt = vCnt) and (vCnt <> 1) and (nextNode <> NULL);
  966.                              (* the nextNode check is used since the last node
  967.                              at level only has a node pointer and not a
  968.                              corresponding value at the  next higher level.
  969.                              Therefore, no value adjustment will be required *)
  970.                     end;
  971.                 if mustMoveCursor then
  972.                     begin
  973.                     if pRec.cursor.entryNum > cnt then
  974.                         begin
  975.                         Dec(pRec.cursor.entryNum);
  976.                         end
  977.                     else
  978.                         begin
  979.                         if pRec.cursor.entryNum = cnt then
  980.                             begin
  981.                             pRec.cursor.valid := FALSE;
  982.                             end;
  983.                         end;
  984.                     end;
  985.                 end;
  986.             end;
  987.         if not done then
  988.             begin
  989.             if (cnt = vCnt) then
  990.                 begin                       (* no more values .. get brother *)
  991.                 Move(page[NEXTLOC],thisNode,RNSIZE);          (* get brother *)
  992.                 if thisNode = NULL then
  993.                     begin                              (* no brother .. quit *)
  994.                     done := TRUE;
  995.                     DeleteValueFromNode := FALSE;
  996.                     last := FALSE;
  997.                     nodeDeleted := FALSE;
  998.                     end
  999.                 else
  1000.                     begin                                   (* brother found *)
  1001.                     FetchPage(iFName,thisNode,page);        (* fetch brother *)
  1002.                     bytePtr := 1;
  1003.                     cnt := 1;
  1004.                     vCnt := page[VCNTLOC];
  1005.                     end;
  1006.                 end
  1007.             else
  1008.                 begin
  1009.                 Inc(cnt);
  1010.                 bytePtr := bytePtr + RNSIZE + pRec.vSize;
  1011.                 end;
  1012.             end;
  1013.         end;
  1014.     end;                               (* end of DeleteValueFromNode routine *)
  1015.  
  1016. (*\*)
  1017. (* This recursive routine will start at a given node (normally the root) and
  1018.    follow the tree down until the correct sequence node is found.  Once it
  1019.    is found DeleteValueFromNode is used to delete the value from the node if
  1020.    it exists.  The routine returns TRUE if the value (including the correct
  1021.    physical record pointer) is found and deleted.  Otherwise, FALSE is
  1022.    returned.  If this deletion causes an empty node, DeleteValueFromNode will
  1023.    delete the node.  This routine will take this into account and delete the
  1024.    lowernode and lowernode node pointer.  The variable nodeDeleted will be set
  1025.    TRUE by DeleteValueFromNode to denote that the lower node was deleted.  If
  1026.    the lower node was not deleted but the value deleted was the last value in
  1027.    the node (not the only value but the last) and was not the last node of a
  1028.    given level then this routine will change the value pointing to the lower
  1029.    node to take this into account.  This will be noted by last being set to
  1030.    TRUE by DeleteValueFromNode.                                              *)
  1031.  
  1032. function DeleteValue(var iFName : FnString;            (* var for speed only *)
  1033.                      rNum : RecordNumber;
  1034.                      var paramValue;
  1035.                      var thisNode : NodePtrType;
  1036.                      var pRec : ParameterRecord;
  1037.                      var last : Boolean;
  1038.                      var nodeDeleted : Boolean) : Boolean;
  1039.  
  1040. var
  1041.     lowerPage,
  1042.     thisPage : SinglePage;
  1043.     lastValLoc,
  1044.     bytePtr : PageRange;
  1045.     lowerNode : NodePtrType;
  1046.     cnt : Byte;
  1047.  
  1048.     begin
  1049.     FetchPage(iFName,thisNode,thisPage);
  1050.     case NodeType(thisPage[NTYPELOC]) of
  1051.         INDEXNODE :
  1052.             begin
  1053.             lowerNode := FindNextLevelPtr(thisPage,paramValue,pRec);
  1054.             if DeleteValue(iFName,
  1055.                            rNum,
  1056.                            paramValue,
  1057.                            lowerNode,      (* will become the lower node where
  1058.                                                   the value was deleted from *)
  1059.                            pRec,
  1060.                            last,
  1061.                            nodeDeleted) then
  1062.                 begin      (* value was successfully deleted from node below *)
  1063.                 if nodeDeleted then    (* check to see if lower node deleted *)
  1064.                     begin              (* it was - delete corresponding node
  1065.                                           pointer from this node             *)
  1066.                     DeleteValue := DeleteValueFromNode(iFName,
  1067.                                                        lowerNode,  (*lower node
  1068.                                                                     pointer *)
  1069.                                                        paramValue,
  1070.                                                        thisNode,  (* node to
  1071.                                                                      delete
  1072.                                                                      from *)
  1073.                                                        pRec,
  1074.                                                        last,
  1075.                                                        nodeDeleted);
  1076.                     DeleteNodeFromList(iFName,lowerNode,pRec);
  1077.                                               (* delete lower node from list *)
  1078.                     end
  1079.                 else
  1080.                     begin                               (* node not deleted *)
  1081.                     if last then         (* value deleted was last entry in
  1082.                                             lower node and lower node was not
  1083.                                             last at that level              *)
  1084.                         begin
  1085.                         bytePtr := 1;
  1086.                         cnt := BinarySearchEntry(thisPage,paramValue,pRec);
  1087.                         if (cnt <> 0) and (cnt <= thisPage[VCNTLOC]) then
  1088.                             begin
  1089.                             bytePtr := BytePointerPosition(cnt,pRec.vSize);
  1090.                                              (* now find record number match *)
  1091.                             if CompareValues(lowerNode,
  1092.                                              thisPage[bytePtr],
  1093.                                              LONGINTVALUE) = EQUALTO then
  1094.                                 (* It is not obvious, but if the first entry
  1095.                                    for paramValue is not the one that matches
  1096.                                    the lower node, no adjustment will be
  1097.                                    required.  This is because, if the lower node
  1098.                                    is not the first node with this value, the
  1099.                                    new last value for the lower node will be
  1100.                                    paramValue                                *)
  1101.                                 begin
  1102.                                 FetchPage(iFName,lowerNode,lowerPage);
  1103.                                 lastValLoc := ((lowerPage[VCNTLOC] - 1)
  1104.                                               * (pRec.vSize + RNSIZE)) + 1;
  1105.                                 FastMover(lowerPage[lastValLoc + RNSIZE],
  1106.                                           thisPage[bytePtr + RNSIZE],
  1107.                                           pRec.vSize);
  1108.                                 StorePage(iFName,thisNode,thisPage);
  1109.                                 end;
  1110.                             end;
  1111.                         end;
  1112.                     DeleteValue := FALSE;
  1113.                     end;
  1114.                 end
  1115.             else
  1116.                 begin
  1117.                 DeleteValue := FALSE;
  1118.                 end;
  1119.             end;
  1120.         SEQUENCENODE :
  1121.             begin
  1122.             mustMoveCursor := pRec.cursor.valid and
  1123.                               (pRec.cursor.prNum = thisNode);
  1124.             DeleteValue := DeleteValueFromNode(iFName,rNum,paramValue,
  1125.                                                thisNode,pRec,last,nodeDeleted);
  1126.             mustMoveCursor := FALSE;
  1127.             end;
  1128.         end;                                        (* end of case statement *)
  1129.     end;                                       (* end of DeleteValue routine *)
  1130.  
  1131. (*\*)
  1132. (* This routine will delete a value and its associated logical record number
  1133.    from a given index file.  If the logical record number passed in is zero
  1134.    then all records with a matching paramValue will be deleted.  Otherwise,
  1135.    only the entry with the matching paramValue and the matching logical record
  1136.    number will be deleted.                                                   *)
  1137.  
  1138. procedure DeleteValueFromBTree(iFName : FnString;
  1139.                                lrNum : LrNumber;
  1140.                                var paramValue);
  1141.  
  1142. var
  1143.     tempLrNum : LrNumber;
  1144.     lrLst : LrList;
  1145.     pRec : ParameterRecord;
  1146.     last,
  1147.     nodeDeleted : Boolean;
  1148.  
  1149.     begin
  1150.     FetchFileParameters(iFName,pRec,SizeOf(pRec));
  1151.     if lrNum = 0 then
  1152.         begin                         (* delete all occurences of paramValue *)
  1153.         CreateLrList(lrLst);
  1154.         GetEqualsFromBTree(iFName,paramValue,lrLst,pRec);
  1155.         tempLrNum := GetFirstLr(lrLst);
  1156.         while tempLrNum <> 0 do
  1157.             begin
  1158.             DeleteValueFromBTree(iFName,tempLrNum,paramValue);
  1159.             tempLrNum := GetNextLr(lrLst);
  1160.             end;
  1161.         DestroyLrList(lrLst);
  1162.         end
  1163.     else
  1164.         begin
  1165.         if DeleteValue(iFName,lrNum,paramValue,
  1166.                        pRec.rNode,pRec,last,nodeDeleted) then ;
  1167.         end;
  1168.     SaveFileParameters(iFName,pRec,SizeOf(pRec));
  1169.     end;                                      (* end of DeleteValueFromBTree *)
  1170.  
  1171. (*\*)
  1172. (* This routine will start at the root node and return the number of levels
  1173. that exist in a BTree.  The index file name is the only required input.      *)
  1174.  
  1175. function NumberOfBTreeLevels(iFName : FnString) : Byte;
  1176.  
  1177. var
  1178.     pRec : ParameterRecord;
  1179.     page : SinglePage;
  1180.  
  1181.     function CountLevels(thisNode : NodePtrType) : Byte;
  1182.  
  1183.     var
  1184.         lowerNode : NodePtrType;
  1185.  
  1186.         begin
  1187.         FetchPage(iFName,thisNode,page);
  1188.         case NodeType(page[NTYPELOC]) of
  1189.             INDEXNODE :
  1190.                 begin
  1191.                 Move(page,lowerNode,RNSIZE);
  1192.                 CountLevels := CountLevels(lowerNode) + 1;
  1193.                 end;
  1194.             SEQUENCENODE :
  1195.                 begin
  1196.                 CountLevels := 1;
  1197.                 end;
  1198.             end;                                    (* end of case statement *)
  1199.         end;                                   (* end of CountLevels routine *)
  1200.  
  1201.     begin
  1202.     FetchFileParameters(iFName,pRec,SizeOf(pRec));
  1203.     NumberOfBTreeLevels := CountLevels(pRec.rNode);
  1204.     end;                               (* end of NumberOfBTreeLevels routine *)
  1205.  
  1206. (*\*)
  1207. (* This routine will search an index and determine whether the given logical
  1208.    record number is in the index.  If it is, TRUE is returned in found and the
  1209.    value associated with the logical record number is returned in paramValue.
  1210.    If it is not found, found will be returned as FALSE and paramValue will
  1211.    remain unchanged.  This is primarily used for debugging or determining if
  1212.    an index has somehow been damaged.                                        *)
  1213.  
  1214. procedure FindLrNumInBTree(iFName : FnString;
  1215.                            lrNum : LrNumber;
  1216.                            var paramValue;
  1217.                            var found : Boolean);
  1218.  
  1219. var
  1220.     pRec : ParameterRecord;
  1221.     page : SinglePage;
  1222.     tempLrNum : LrNumber;
  1223.     thisNode : NodePtrType;
  1224.     cnt,
  1225.     vCnt : Byte;
  1226.     bytePtr : PageRange;
  1227.  
  1228.     begin
  1229.     FetchFileParameters(iFName,pRec,SizeOf(pRec));
  1230.     thisNode := pRec.fSNode;
  1231.     found := FALSE;
  1232.     while thisNode <> 0 do
  1233.         begin
  1234.         FetchPage(iFName,thisNode,page);
  1235.         vCnt := page[VCNTLOC];
  1236.         cnt := 1;
  1237.         bytePtr := 1;
  1238.         while cnt <= vCnt do
  1239.             begin
  1240.             Move(page[bytePtr],tempLrNum,RNSIZE);
  1241.             if tempLrNum = lrNum then
  1242.                 begin
  1243.                 found := TRUE;
  1244.                 FastMover(page[bytePtr + RNSIZE],paramValue,pRec.vSize);
  1245.                 Exit;
  1246.                 end
  1247.             else
  1248.                 begin
  1249.                 Inc(cnt);
  1250.                 if cnt <= vCnt then
  1251.                     begin               (* required to keep bytePtr in range *)
  1252.                     bytePtr := bytePtr + RNSIZE + pRec.vSize;
  1253.                     end;
  1254.                 end;
  1255.             end;
  1256.         Move(page[NEXTLOC],thisNode,RNSIZE);      (* set up to get next node *)
  1257.         end;
  1258.     end;                                  (* end of FindLrNumInBTree routine *)
  1259.