home *** CD-ROM | disk | FTP | other *** search
/ CD Actual 13 / CDA13.ISO / cdactual / demobin / share / program / Pascal / AIM.ZIP / AIMUNIT.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1991-06-29  |  49.9 KB  |  1,311 lines

  1.  
  2.         {Compiler directives}
  3. {D-}                    {debug information}
  4. {T-}                    {create .TPM file for debugging}
  5. {F-}                    {automatically force far calls}
  6. {V-}                    {var string checking}
  7. {L+}                    {link buffer in memory}
  8. {$R-}                   {range checking}
  9. {$B+}                   {boolean complete evaluation}
  10. {$S+}                   {stack checking}
  11. {$I+}                   {I/O checking}
  12. {$N-}                   {numeric coprocessor}
  13. {$M 65500,0,0}          {memory sizes}
  14.  
  15. {***************************************************************************}
  16. {*                                                                         *}
  17. {*      AIMUNIT.PAS         Copyright - Matt Goodrich                      *}
  18. {*                              Jun 29, 1991                               *}
  19. {*                                                                         *}
  20. {*   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -   *}
  21. {*                                                                         *}
  22. {*      Maintenance History :                                              *}
  23. {*                                                                         *}
  24. {*  1.A  MG  6/29/91 - Initial Keyin.                                      *}
  25. {*                                                                         *}
  26. {***************************************************************************}
  27.  
  28. UNIT AIMUNIT;
  29.  
  30. INTERFACE
  31.  
  32.  
  33. USES
  34.   CRT, DOS, MISCSTUF;
  35.  
  36. CONST
  37.   AimBlockSize   = 512;
  38.   AimMaxKeys     =   7;
  39.  
  40.  
  41.  
  42.  
  43. TYPE
  44.  
  45.   AimVars = RECORD
  46.  
  47.      AimFile       : FILE;
  48.      DataFile      : FILE;
  49.  
  50.      AimFileName   : STRING [40];
  51.      DataFileName  : STRING [40];
  52.  
  53.      RecLenData    : LONGINT;        {length of datafile records}
  54.      BlocksPerHash : LONGINT;        {basically, the number of aimfile blocks}
  55.  
  56.      SeekPosData   : LONGINT;
  57.  
  58.      AimKey        : ARRAY [1..AimMaxKeys] OF STRING [30];
  59.  
  60.      { -- array with beginning & ending columns of aimdex key (eg 2-7,11-17)}
  61.      KeyBegCol     : ARRAY [1..AimMaxKeys] OF INTEGER;
  62.      KeyEndCol     : ARRAY [1..AimMaxKeys] OF INTEGER;
  63.  
  64.      FileOpen      : STRING [ 1];
  65.  
  66.      Found         : STRING [ 1];
  67.  
  68.      NumbValidKeys : INTEGER;
  69.  
  70.      BitOn         : INTEGER;
  71.      BlockOn       : LONGINT;
  72.      ByteOn        : LONGINT;
  73.      LastReadSucc  : STRING [ 1];    {last Read find a record? (for KG & KP)}
  74.  
  75.      AndingBuffer  : ARRAY [1..AimBlockSize] OF BYTE;
  76.      HashArray     : ARRAY [1..200] OF LONGINT;
  77.      HashArrayPtr  : LONGINT;
  78.  
  79.      SearchFor     : ARRAY [1..AimMaxKeys] OF STRING [30];
  80.      WhichKey      : ARRAY [1..AimMaxKeys] OF STRING [ 1];
  81.  
  82.   END;
  83.  
  84.  {**************************************************************************}
  85.  
  86. VAR
  87.  
  88.   Aim   : AimVars;
  89.  
  90. {---------------------------------------------------------------------------}
  91. { These are the routines that you would call from an application.           }
  92. {---------------------------------------------------------------------------}
  93.  
  94. PROCEDURE Aim_OpenFile     (VAR Aim : AimVars);
  95. PROCEDURE Aim_CloseFile    (VAR Aim : AimVars);
  96. PROCEDURE Aim_Read         (VAR Aim : AimVars);
  97. PROCEDURE Aim_ReadKG       (VAR Aim : AimVars);
  98. PROCEDURE Aim_ReadKP       (VAR Aim : AimVars);
  99. PROCEDURE Aim_InsertKey    (VAR Aim : AimVars);
  100.  
  101.  
  102. {---------------------------------------------------------------------------}
  103. { These are used internally and by the AIMDEXP utility - just ignore them.  }
  104. {---------------------------------------------------------------------------}
  105.  
  106. PROCEDURE AimHaltProgram    (    DosErrorLevel : INTEGER);
  107.  
  108. PROCEDURE AimFatalError     (    ErrorNum      : INTEGER;
  109.                                  Message       : STRING);
  110.  
  111. PROCEDURE AimGetHashNumb    (    HashString    : STRING;
  112.                              VAR HashNumb      : LONGINT);
  113.  
  114. PROCEDURE AimWritHeaderRec  (VAR Aim           : AimVars);
  115.  
  116.  
  117. {***************************************************************************}
  118. {***************************************************************************}
  119. {***************************************************************************}
  120. {***************************************************************************}
  121. {***************************************************************************}
  122.  
  123. IMPLEMENTATION
  124.  
  125.  
  126. CONST
  127.   Version          = '1.A';           {Version number of this module.}
  128.   LineFeed         = CHR (10);
  129.  
  130. {$I AIMVAR.PAS}   {Inclusion with shared variable definitions}
  131.  
  132.  
  133. VAR
  134.  
  135.   BitFlicked      : STRING [ 1];
  136.   BlockFound      : STRING [ 1];
  137.   DidDataMatch    : STRING [ 1];
  138.   ErrMssg         : STRING [80];
  139.   HashString      : STRING [ 3];
  140.   HeaderBuffer    : ARRAY [1..HeaderBuffSize] OF CHAR;
  141.   NewByte         : BYTE;
  142.   ReadType        : STRING [ 2];      {'R' or 'KG' or 'KP'}
  143.   Recnum          : LONGINT;
  144.   SeekPosIns      : LONGINT;
  145.   SeekPosSave     : LONGINT;
  146.   StringToAdd     : STRING [ 3];
  147.  
  148. {***************************************************************************}
  149. {***************************************************************************}
  150. {***************************************************************************}
  151. {***************************************************************************}
  152. {***************************************************************************}
  153. {***************************************************************************}
  154. {                                                                           }
  155. {                   General info on how the reads work:                     }
  156. {                                                                           }
  157. { They basically do the following:                                          }
  158. {                                                                           }
  159. { You start out by doing a Read. When you pass in a search key, it builds   }
  160. { 'HashArray' which contains the hash value for each triplet in the search  }
  161. { keys you passed in.  The more search keys you pass in, or the longer they }
  162. { are, the more elements in 'HashArray'. For example, if you passed in      }
  163. { '2JONES', you might end up with hash values like 141, 537, & 982, hence 3 }
  164. { elements in the array.                                                    }
  165. {                                                                           }
  166. { 'AndingBuffer' is then initialized to having all its bits ON.  Then, the  }
  167. { aimdex file is read once for each element in 'HashArray' (starting at     }
  168. { block one), and each of those records is ANDed against 'AndingBuffer'.    }
  169. { You end up with 'AndingBuffer' pointing to all the possible hits in the   }
  170. { datafile.                                                                 }
  171. {                                                                           }
  172. { By using a pointer (ByteOn, BitOn) each bit in 'AndingBuffer' is checked, }
  173. { and for each bit that is ON, that datafile record is read and scanned to  }
  174. { see if the desired search keys really are in the appropriate fields in    }
  175. { the datafile.  If a match is found, stop, leaving 'AndingBuffer' and its  }
  176. { pointers where they are.  If the whole variable is checked and there are  }
  177. { no matches, read in the next block and do it all over again (initialize   }
  178. { to all on, read once for each element in 'HashArray', etc).  If there are }
  179. { no more blocks left in the aimdex file (indicated by 'BlocksPerHash'),    }
  180. { then stop, as the read was not successful.                                }
  181. {                                                                           }
  182. { The ReadKG & ReadKP, are pretty straight forward.  They are based on a    }
  183. { successful Read having been done, leaving 'AndingBuffer' and its          }
  184. { pointers.  ReadKG simply bumps the pointer 1 bit, and starts from there.  }
  185. {                                                                           }
  186. {***************************************************************************}
  187. {***************************************************************************}
  188. {***************************************************************************}
  189. {***************************************************************************}
  190. {***************************************************************************}
  191.  
  192. PROCEDURE AimHaltProgram;
  193.  
  194. BEGIN
  195.  
  196.   GOTOXY (78, 23);
  197.   Cursor_On;
  198.   HALT (DosErrorLevel);
  199.  
  200. END;
  201.  
  202. {***************************************************************************}
  203.  
  204. PROCEDURE AimFatalError;
  205.  
  206. {---------------------------------------------------------------------------}
  207. { Stops the program because some horrible error was encountered (e.g. file  }
  208. {   damage).  Display the appropriate error message based on the error      }
  209. {   number that was passed in.  The error numbers have no particular        }
  210. {   meaning.  They merely need to be unique, so that you can tell where the }
  211. {   error originated.                                                       }
  212. {                                                                           }
  213. {                                                                           }
  214. { INPUT:                                                                    }
  215. {    ErrorNum : INTEGER    - the error number to be displayed.              }
  216. {                                                                           }
  217. {    Message  : STRING     - an additional message string that will display }
  218. {                             next to the error message.  Generally used    }
  219. {                             for things like passing the name of the file  }
  220. {                             that is damaged, etc.                         }
  221. {                                                                           }
  222. {                                                                           }
  223. { OUTPUT:                                                                   }
  224. {    none as such, but this routine stops the program.                      }
  225. {                                                                           }
  226. {---------------------------------------------------------------------------}
  227.  
  228. VAR
  229.   I       : INTEGER;
  230.  
  231. BEGIN
  232.  
  233.   FOR I:= 19 TO 25 DO
  234.     BEGIN
  235.       GOTOXY (1, I);
  236.       CLREOL;
  237.     END;
  238.  
  239.   Disp_String ('CRASH!   AIMUNIT.TPU got error #', 1, 20, Bold_Video);
  240.   Disp_Number (ErrorNum, 33, 20, Reverse_Video, 4, 0);
  241.  
  242.   ErrMssg := '';
  243.  
  244.   CASE ErrorNum OF
  245.  
  246.     10,11,12,13,14,15,16,17 : BEGIN
  247.           ErrMssg := 'There appears to be damage to the data file: ';
  248.         END;
  249.  
  250.     20,21,22,23,24,25,26,27,28,29 : BEGIN
  251.           ErrMssg := 'You gave an invalid aimdex key field: ';
  252.         END;
  253.  
  254.     40,41 : BEGIN
  255.           ErrMssg := 'I had a problem writing to the aimdex file: ';
  256.         END;
  257.  
  258.     50,51 : BEGIN
  259.           ErrMssg := 'I couldn''t OPEN the aimdex file: ';
  260.         END;
  261.  
  262.     60,61,62,63,64,65 : BEGIN
  263.           ErrMssg := 'There appears to be damage to the aimdex file: ';
  264.         END;
  265.  
  266.     70,71 : BEGIN
  267.           ErrMssg := 'I couldn''t OPEN the datafile: ';
  268.         END;
  269.  
  270.     80,81,82,83 : BEGIN
  271.           ErrMssg := 'You tried to do something without ' +
  272.                      'first opening the file. ';
  273.         END;
  274.  
  275.     90 : BEGIN
  276.           ErrMssg := 'The datafile had a record that was too long.';
  277.         END;
  278.  
  279.    100 : BEGIN
  280.           ErrMssg := 'You gave an invalid record length: ';
  281.         END;
  282.  
  283.    110 : BEGIN
  284.           ErrMssg := 'I couldn''t CREATE the aimdex file: ';
  285.         END;
  286.  
  287.    120 : BEGIN
  288.           ErrMssg := 'There are no datafile records, so you must specify ' +
  289.                            'the record length.';
  290.         END;
  291.  
  292.    130,131 : BEGIN
  293.           ErrMssg := 'HeaderBuffSize is more than AimBlockSize.';
  294.         END;
  295.  
  296.    140 : BEGIN
  297.           ErrMssg := 'The datafile has too many records.';
  298.         END;
  299.  
  300.    150,151 : BEGIN
  301.           ErrMssg := 'You gave too many aimdex keys.';
  302.         END;
  303.  
  304.   END;
  305.  
  306.  
  307.   Disp_String (ErrMssg + Message, 1, 22, Bold_Video);
  308.  
  309.  
  310.   AimHaltProgram (1);        {Return a DOS errorlevel of 1.}
  311.  
  312. END;
  313. {***************************************************************************}
  314.  
  315. PROCEDURE AimGetHashNumb;
  316.  
  317. {---------------------------------------------------------------------------}
  318. { Calculate the hash value for a passed string.  It uses the Division       }
  319. {  remainder method to calculate, and returns a number from 1 to 1009       }
  320. {  (inclusive).                                                             }
  321. {                                                                           }
  322. { INPUT:                                                                    }
  323. {    HashString : STRING [ 3] - the 3 byte string that you want a hash      }
  324. {                                value for.                                 }
  325. {                                                                           }
  326. { OUTPUT:                                                                   }
  327. {    HashNumb   : LONGINT     - the calculated hash value.                  }
  328. {                                                                           }
  329. {---------------------------------------------------------------------------}
  330.  
  331. VAR
  332.   TempString03 : STRING [ 3];
  333.  
  334. BEGIN
  335.  
  336.   TempString03 := HashString + Bla50;
  337.  
  338.   { -- Returns a number from 0-1008.}
  339.   HashNumb := (255 * 255 * ORD (TempString03 [1]) +
  340.                      255 * ORD (TempString03 [2]) +
  341.                            ORD (TempString03 [3])) MOD 1009;
  342.  
  343.  
  344.   HashNumb := HashNumb + 1;      {Add one to skip past the header rec. }
  345.  
  346. END;
  347.  
  348. {***************************************************************************}
  349.  
  350. PROCEDURE AimWritHeaderRec;
  351.  
  352. {---------------------------------------------------------------------------}
  353. { Write the header record to the aimdex file.                               }
  354. {                                                                           }
  355. { INPUT:                                                                    }
  356. {    HeaderBuffSize and all the fields that are being written to the        }
  357. {    aimdex header record.                                                  }
  358. {                                                                           }
  359. { OUTPUT:                                                                   }
  360. {    None as such.  It just does the write.                                 }
  361. {                                                                           }
  362. {---------------------------------------------------------------------------}
  363.  
  364.  
  365. VAR
  366.   I,J          : INTEGER;
  367.   TempString04 : STRING [ 4];
  368.   TempString06 : STRING [ 6];
  369.   TempString40 : STRING [40];
  370.  
  371.  
  372. BEGIN
  373.  
  374.   FOR I := 1 TO HeaderBuffSize DO
  375.     BEGIN
  376.       HeaderBuffer [I] := Bla;
  377.     END;
  378.  
  379.  
  380.   { -- Load the header buffer from the various variables.}
  381.  
  382.   TempString40 := Aim.DataFileName + Bla50;
  383.   FOR I:=  1 TO 40 DO HeaderBuffer [I] := TempString40 [I];
  384.  
  385.   STR (Aim.BlocksPerHash:6, TempString06);
  386.   FOR I:= 41 TO 46 DO HeaderBuffer [I] := TempString06 [I-40];
  387.  
  388.   STR (Aim.RecLenData:6, TempString06);
  389.   FOR I:= 47 TO 52 DO HeaderBuffer [I] := TempString06 [I-46];
  390.  
  391.   FOR J:= 1 TO AimMaxKeys DO
  392.     BEGIN
  393.        STR (Aim.KeyBegCol [J] :4, TempString04);
  394.        FOR I:= 1 TO 4 DO HeaderBuffer [J*8+44+I] := TempString04 [I];
  395.  
  396.        STR (Aim.KeyEndCol [J] :4, TempString04);
  397.        FOR I:= 1 TO 4 DO HeaderBuffer [J*8+48+I] := TempString04 [I];
  398.     END;
  399.  
  400.  
  401.   SeekPos := 0;
  402.   SEEK (Aim.AimFile, SeekPos);
  403.   BLOCKWRITE (Aim.AimFile, HeaderBuffer, HeaderBuffSize, CharsWrit);
  404.  
  405. END;
  406.  
  407. {***************************************************************************}
  408. {***************************************************************************}
  409. {***************************************************************************}
  410. {***************************************************************************}
  411. {***************************************************************************}
  412.  
  413. PROCEDURE MatchDataRec (VAR Aim : AimVars);
  414.  
  415. {---------------------------------------------------------------------------}
  416. { Verify that the datafile record that was read actually contains the       }
  417. { search strings that were specified.  The aimdex can return some 'false    }
  418. { hits', so we actually have to read the datafile to be sure we have a      }
  419. { valid record.  Returns 'Y' or 'N' in DidDataMatch.                        }
  420. {                                                                           }
  421. { INPUT:                                                                    }
  422. {    Lots                                                                   }
  423. {                                                                           }
  424. { OUTPUT:                                                                   }
  425. {    DidDataMatch : STRING [ 1]  - does the datarecord match the search     }
  426. {                                   criteria? (Y/N)                         }
  427. {---------------------------------------------------------------------------}
  428.  
  429. VAR
  430.   I,J,M,N       : INTEGER;
  431.   TempInteger   : INTEGER;
  432.   TempString255 : STRING [255];
  433.  
  434. BEGIN
  435.  
  436.    { -- If you want to ignore deleted records by way of an "I'm a deleted }
  437.    { -- record" byte, put that logic here, and don't bother with matching }
  438.    { -- the datafile record.                                              }
  439.  
  440.    DidDataMatch := Yes;
  441.  
  442.    FOR I:= 1 TO Aim.NumbValidKeys DO
  443.      BEGIN
  444.  
  445.        VAL (Aim.WhichKey [I], TempInteger, ValError);
  446.        M := Aim.KeyBegCol [TempInteger];
  447.        N := Aim.KeyEndCol [TempInteger];
  448.  
  449.        TempString255 := '';
  450.        FOR J:= M TO N DO TempString255 := TempString255 + DataBuffer [J];
  451.  
  452.        Convert_Upper (TempString255);
  453.        IF POS (Aim.SearchFor [I], TempString255) = 0
  454.           THEN DidDataMatch := No;
  455.      END;
  456.  
  457. END;
  458.  
  459. {***************************************************************************}
  460.  
  461. PROCEDURE LoadAndBuffer (VAR Aim : AimVars);
  462.  
  463. {---------------------------------------------------------------------------}
  464. { Load the next (or previous) block of the 'AND'ing buffer. Read the aimdex }
  465. { file for each hash value in the hash array and AND each of those records  }
  466. { one at a time to end up with the final 'AND' block.                       }
  467. {                                                                           }
  468. { INPUT:                                                                    }
  469. {    Lots                                                                   }
  470. {                                                                           }
  471. { OUTPUT:                                                                   }
  472. {    AndingBuffer : ARRAY OF BYTE                                           }
  473. {                                                                           }
  474. {---------------------------------------------------------------------------}
  475.  
  476. VAR
  477.   I,J : INTEGER;
  478.  
  479. BEGIN
  480.  
  481.   { -- Initialize the AND buffer to all bits turned on.}
  482.   FOR I:= 1 TO AimBlockSize DO
  483.     BEGIN
  484.        Aim.AndingBuffer [I] := $FF;
  485.     END;
  486.  
  487.  
  488.   FOR I:= 1 TO Aim.HashArrayPtr DO
  489.     BEGIN
  490.       SeekPos := (Aim.BlockOn-1) * 1010 * AimBlockSize +
  491.                  (Aim.HashArray [I] * AimBlockSize);
  492.       SEEK (Aim.AimFile, SeekPos);
  493.       BLOCKREAD (Aim.AimFile, AimBuffer, AimBlockSize, CharsRead);
  494.  
  495.       FOR J:= 1 TO AimBlockSize DO
  496.         BEGIN
  497.           Aim.AndingBuffer [J] := Aim.AndingBuffer [J] AND AimBuffer [J];
  498.         END;
  499.  
  500.     END;
  501.  
  502. END;
  503.  
  504. {***************************************************************************}
  505.  
  506. PROCEDURE PointToNextBit (VAR Aim : AimVars);
  507.  
  508. {---------------------------------------------------------------------------}
  509. { Move the 'AND' buffer pointers (BlockOn, ByteOn, BitOn) to point to the   }
  510. { the next datafile record (moving them forwards if Read or KG, backwards   }
  511. { if KP). If we go past the beginning of the first block (or the end of the }
  512. { last) then we are done searching and will return 'N' in BlockFound.       }
  513. {                                                                           }
  514. { INPUT:                                                                    }
  515. {    Lots                                                                   }
  516. {                                                                           }
  517. { OUTPUT:                                                                   }
  518. {    Buffer pointers                                                        }
  519. {    BlockFound  : STRING [ 1]  - was the next bit found? (Y/N)             }
  520. {                                                                           }
  521. {---------------------------------------------------------------------------}
  522.  
  523. BEGIN
  524.  
  525.   BlockFound := Yes;
  526.  
  527.   IF (ReadType = 'R') OR (ReadType = 'KG')
  528.     THEN BEGIN
  529.            Aim.BitOn := Aim.BitOn + 1;
  530.            IF Aim.BitOn > 8
  531.              THEN BEGIN
  532.                     Aim.BitOn := 1;
  533.                     Aim.ByteOn := Aim.ByteOn + 1;
  534.                     IF Aim.ByteOn > AimBlockSize
  535.                       THEN BEGIN
  536.                              Aim.ByteOn := 1;
  537.                              Aim.BlockOn := Aim.BlockOn + 1;
  538.                              IF Aim.BlockOn > Aim.BlocksPerHash
  539.                                 THEN BlockFound := No
  540.                                 ELSE LoadAndBuffer (Aim);
  541.                            END;
  542.                   END;
  543.          END
  544.  
  545.     ELSE BEGIN
  546.            Aim.BitOn := Aim.BitOn - 1;
  547.            IF Aim.BitOn < 1
  548.              THEN BEGIN
  549.                     Aim.BitOn := 8;
  550.                     Aim.ByteOn := Aim.ByteOn - 1;
  551.                     IF Aim.ByteOn < 1
  552.                       THEN BEGIN
  553.                              Aim.ByteOn := AimBlockSize;
  554.                              Aim.BlockOn := Aim.BlockOn - 1;
  555.                              IF Aim.BlockOn < 1
  556.                                 THEN BlockFound := No
  557.                                 ELSE LoadAndBuffer (Aim);
  558.                            END;
  559.                   END
  560.          END;
  561.  
  562. END;
  563.  
  564. {***************************************************************************}
  565.  
  566. PROCEDURE ReadThruBuff (VAR Aim : AimVars);
  567.  
  568. {---------------------------------------------------------------------------}
  569. { Step through each bit in the AND buffer.  Each bit that is on represents  }
  570. { a record in the datafile that may be a match.  Read that record in the    }
  571. { datafile and see if it is indeed a match.  If not, keep stepping through  }
  572. { the AND buffer bit by bit, byte by byte, block by block, until either a   }
  573. { match is found or the end of the last block is reached.                   }
  574. {                                                                           }
  575. { INPUT:                                                                    }
  576. {    Lots                                                                   }
  577. {                                                                           }
  578. { OUTPUT:                                                                   }
  579. {    Found       : STRING [ 1]  - was a datafile record found? (Y/N)        }
  580. {    SeekPosData : LONGINT      - if so, here's it's offset.                }
  581. {                                                                           }
  582. {---------------------------------------------------------------------------}
  583.  
  584. VAR
  585.   EndLoopByte : STRING [ 1];
  586.   EndLoopBit  : STRING [ 1];
  587.   I           : INTEGER;
  588.  
  589. BEGIN
  590.  
  591.   Aim.Found := No;
  592.  
  593.   PointToNextBit (Aim);
  594.   IF BlockFound = No     {ptr went past last block or beginning of 1st block}
  595.     THEN EXIT;
  596.  
  597.  
  598.   EndLoopByte := No;
  599.   WHILE EndLoopByte = No DO
  600.      BEGIN
  601.          EndLoopBit := No;
  602.  
  603.          WHILE EndLoopBit = No DO
  604.             BEGIN
  605.                BitFlicked := No;
  606.                CASE Aim.BitOn OF
  607.                    1: BEGIN
  608.                         IF (Aim.AndingBuffer [Aim.ByteOn] AND $80) = $80
  609.                              THEN BitFlicked := Yes;
  610.                       END;
  611.                    2: BEGIN
  612.                         IF (Aim.AndingBuffer [Aim.ByteOn] AND $40) = $40
  613.                              THEN BitFlicked := Yes;
  614.                       END;
  615.                    3: BEGIN
  616.                         IF (Aim.AndingBuffer [Aim.ByteOn] AND $20) = $20
  617.                              THEN BitFlicked := Yes;
  618.                       END;
  619.                    4: BEGIN
  620.                         IF (Aim.AndingBuffer [Aim.ByteOn] AND $10) = $10
  621.                              THEN BitFlicked := Yes;
  622.                       END;
  623.                    5: BEGIN
  624.                         IF (Aim.AndingBuffer [Aim.ByteOn] AND $08) = $08
  625.                              THEN BitFlicked := Yes;
  626.                       END;
  627.                    6: BEGIN
  628.                         IF (Aim.AndingBuffer [Aim.ByteOn] AND $04) = $04
  629.                              THEN BitFlicked := Yes;
  630.                       END;
  631.                    7: BEGIN
  632.                         IF (Aim.AndingBuffer [Aim.ByteOn] AND $02) = $02
  633.                              THEN BitFlicked := Yes;
  634.                       END;
  635.                    8: BEGIN
  636.                         IF (Aim.AndingBuffer [Aim.ByteOn] AND $01) = $01
  637.                              THEN BitFlicked := Yes;
  638.                       END;
  639.  
  640.                END;
  641.  
  642.  
  643.                IF BitFlicked = Yes
  644.                  THEN BEGIN
  645.  
  646.                         SeekPosSave := (Aim.BlockOn-1) * Aim.RecLenData
  647.                                * AimBlockSize * 8
  648.                                + (8 * (Aim.ByteOn-1) * Aim.RecLenData)
  649.                                + (Aim.BitOn-1) * Aim.RecLenData;
  650.                         SEEK (Aim.DataFile, SeekPosSave);
  651.                         BLOCKREAD (Aim.DataFile, DataBuffer, Aim.RecLenData,
  652.                                     CharsRead);
  653.  
  654.                         IF CharsRead <> Aim.RecLenData
  655.                            THEN AimFatalError (10, Aim.DataFileName);
  656.  
  657.                         MatchDataRec (Aim);
  658.                         IF DidDataMatch = Yes
  659.                            THEN BEGIN
  660.                                   { -- If you want a datafile readlist you }
  661.                                   { -- would put it here.                  }
  662.  
  663.                                   EndLoopBit      := Yes;
  664.                                   EndLoopByte     := Yes;
  665.                                   Aim.Found       := Yes;
  666.                                   Aim.SeekPosData := SeekPosSave;
  667.                                 END;
  668.  
  669.                       END;
  670.  
  671.  
  672.                IF Aim.Found = No
  673.                   THEN BEGIN
  674.                           PointToNextBit (Aim);
  675.                           IF BlockFound = No
  676.                               THEN BEGIN
  677.                                       EndLoopBit  := Yes;
  678.                                       EndLoopByte := Yes;
  679.                                       Aim.Found   := No;
  680.                                    END;
  681.                        END;
  682.  
  683.             END;
  684.  
  685.      END;
  686.  
  687. END;
  688.  
  689. {***************************************************************************}
  690.  
  691. PROCEDURE CheckValidKeys (VAR Aim : AimVars);
  692.  
  693. {---------------------------------------------------------------------------}
  694. { Check all the AimKeys that were passed in.  For each one that is valid,   }
  695. { add it to the WhichKey & SearchFor arrays and increment NumbValidKeys by  }
  696. { one.  A valid key would look something like '2SMITH'.                     }
  697. {                                                                           }
  698. { INPUT:                                                                    }
  699. {    AimKey        : ARRAY OF STRING [30]    - user supplied key ('2SMITH') }
  700. {                                                                           }
  701. { OUTPUT:                                                                   }
  702. {    WhichKey      : ARRAY OF STRING [ 1]    - 1st character of valid key   }
  703. {    SearchFor     : ARRAY OF STRING [30]    - the rest of that valid key   }
  704. {    NumbValidKeys : INTEGER                                                }
  705. {                                                                           }
  706. {---------------------------------------------------------------------------}
  707.  
  708. VAR
  709.   I,J          : INTEGER;
  710.   TempString01 : STRING [ 1];
  711.   TempString03 : STRING [ 3];
  712.   TempString04 : STRING [ 4];
  713.   TempInteger  : INTEGER;
  714.   TempKey      : STRING [30];
  715.   ValidKey     : STRING [ 1];
  716.  
  717. BEGIN
  718.  
  719.   Aim.NumbValidKeys := 0;
  720.  
  721.   FOR I:= 1 TO AimMaxKeys DO
  722.     BEGIN
  723.  
  724.       ValidKey := Yes;
  725.       TempKey  := Aim.AimKey [I];
  726.  
  727.  
  728.       { -- Don't allow non-ASCII characters.}
  729.       FOR J:= 1 TO LENGTH (TempKey) DO
  730.         BEGIN
  731.           IF (ORD (TempKey [J]) <  32) OR
  732.              (ORD (TempKey [J]) > 127)
  733.              THEN ValidKey := No;
  734.         END;
  735.  
  736.  
  737.  
  738.       Convert_Upper (TempKey);        {Convert to upper case.}
  739.  
  740.  
  741.       { -- If necessary, pad with blanks out to 4 characters. }
  742.       IF LENGTH (TempKey) < 4
  743.           THEN BEGIN
  744.                   TempString04 := TempKey + Bla50;
  745.                   TempKey      := TempString04;
  746.                END;
  747.  
  748.  
  749.       { -- Make sure the first character is a number.}
  750.       TempString01 := COPY (TempKey, 1, 1);
  751.       VAL (TempString01, TempInteger, ValError);
  752.       IF (ValError = 0) AND (TempInteger <= AimMaxKeys) AND (TempInteger > 0)
  753.           THEN BEGIN
  754.                  { -- I did it this way because I don't know/trust the VAL }
  755.                  { -- command enough to use 'OR' logic.                    }
  756.                END
  757.           ELSE BEGIN
  758.                   ValidKey := No;
  759.                END;
  760.  
  761.  
  762.       { -- Make sure there are at least 3 non-blank characters.}
  763.       TempString03 :=  COPY (TempKey, 2, 3);
  764.       IF TempString03 = '   '
  765.         THEN ValidKey := No;
  766.  
  767.  
  768.  
  769.       { -- All clear.  Go ahead and add it.}
  770.       IF ValidKey = Yes
  771.         THEN BEGIN
  772.                Aim.NumbValidKeys := Aim.NumbValidKeys + 1;
  773.                Aim.WhichKey [Aim.NumbValidKeys]  := COPY (TempKey,
  774.                              1, 1);
  775.                Aim.SearchFor [Aim.NumbValidKeys] := COPY (TempKey,
  776.                              2, LENGTH (TempKey) - 1);
  777.              END;
  778.  
  779.     END;
  780.  
  781. END;
  782.  
  783. {***************************************************************************}
  784.  
  785. PROCEDURE ExpandAimFile (VAR Aim : AimVars);
  786.  
  787. {---------------------------------------------------------------------------}
  788. { When inserting records, the aimdex block gradually gets filled up, and    }
  789. {   eventually a new, empty one needs to be created.  This writes a bunch   }
  790. {   of empty records (hex 00) to the end of the aimdex.                     }
  791. {                                                                           }
  792. { INPUT:                                                                    }
  793. {    Lots.                                                                  }
  794. {                                                                           }
  795. { OUTPUT:                                                                   }
  796. {    None as such.  It just does the expand.                                }
  797. {                                                                           }
  798. {---------------------------------------------------------------------------}
  799.  
  800. VAR
  801.   I : INTEGER;
  802.  
  803. BEGIN
  804.    Save_Screen;   {Save the screen so we can restore it later. }
  805.    Cursor_Off;
  806.  
  807.    { -- Pop up a box on the screen with the 'please wait' message.}
  808.    WINDOW (1, 9, 70, 14);
  809.    CLRSCR;
  810.    WINDOW (1, 1, 80, 25);
  811.    Draw_DBox (1, 9, 70, 14, Normal_Video);
  812.    Disp_String ('Please wait a moment while I expand the aimdex file...',
  813.                           3, 11, Normal_Video);
  814.    Disp_String ('(I''m counting to 1009)', 19, 12, Normal_Video);
  815.  
  816.  
  817.    FOR I:= 1 TO AimBlockSize DO
  818.      AimBuffer [I] := $00;
  819.  
  820.  
  821.    { -- Write one block for each of the 1009 hash values plus 1 for header.}
  822.    SeekPos := Aim.BlocksPerHash * 1010 * AimBlockSize;
  823.    SEEK (Aim.AimFile, SeekPos);
  824.    FOR I:= 1 TO 1010 DO
  825.      BEGIN
  826.        BLOCKWRITE (Aim.AimFile, AimBuffer, AimBlockSize, CharsWrit);
  827.        IF CharsWrit < AimBlockSize
  828.          THEN AimFatalError (40, Aim.AimFileName);
  829.  
  830.        Disp_Number (I-1, 56, 11, Normal_Video, 7, 0);
  831.      END;
  832.  
  833.  
  834.    Aim.BlocksPerHash := Aim.BlocksPerHash + 1;
  835.    AimWritHeaderRec (Aim);
  836.  
  837.    Cursor_On;
  838.    Restore_Screen;
  839.  
  840. END;
  841.  
  842. {***************************************************************************}
  843. {***************************************************************************}
  844. {***************************************************************************}
  845. {***************************************************************************}
  846. {***************************************************************************}
  847.  
  848. PROCEDURE Aim_OpenFile;
  849.  
  850. {---------------------------------------------------------------------------}
  851. { This routine opens the aimdex and its associated datafile and reads the   }
  852. { header record to get all the various information.                         }
  853. {                                                                           }
  854. {                                                                           }
  855. { INPUT:                                                                    }
  856. {    AimFileName : STRING [40]  - the aimdex file to be opened.             }
  857. {                                                                           }
  858. {                                                                           }
  859. { OUTPUT:                                                                   }
  860. {    FileOpen    : STRING [ 1]  - 'Y' if both the aimdex and the datafile   }
  861. {                                        were opened successfully           }
  862. {                                 'A' if unable to open the aimdex          }
  863. {                                 'D' if unable to open the datafile        }
  864. {                                                                           }
  865. {---------------------------------------------------------------------------}
  866.  
  867. VAR
  868.   I,J          : INTEGER;
  869.   TempString04 : STRING [ 4];
  870.   TempString06 : STRING [ 6];
  871.  
  872. BEGIN
  873.  
  874.   Aim.HashArrayPtr := 0;     {Initialize this incase they try to do a KG or  }
  875.                              {KP without first doing a Read. (Pointers would }
  876.                              {be all screwed up.)                            }
  877.  
  878.   Aim.FileOpen := No;
  879.  
  880.  
  881.   IF (HeaderBuffSize > AimBlockSize)
  882.     THEN BEGIN
  883.             AimFatalError (130, '');
  884.          END;
  885.  
  886.  
  887.  
  888.  
  889.   ASSIGN (Aim.AimFile, Aim.AimFileName);
  890.  
  891.   {$I-}
  892.   RESET  (Aim.AimFile, 1);
  893.   {$I+}
  894.  
  895.   IF IORESULT <> 0
  896.       THEN BEGIN
  897.              Aim.FileOpen := 'A';
  898.              EXIT;
  899.            END;
  900.  
  901.  
  902.  
  903.  
  904.   { -- Read the header record. }
  905.   SeekPos := 0;
  906.   SEEK (Aim.AimFile, SeekPos);
  907.   BLOCKREAD (Aim.AimFile, HeaderBuffer, HeaderBuffSize, CharsRead);
  908.   IF CharsRead < HeaderBuffSize
  909.      THEN AimFatalError (60, Aim.AimFileName);
  910.  
  911.  
  912.   { -- Load the header record variables. }
  913.  
  914.   Aim.DataFileName := '';
  915.   FOR I:= 1 TO 40 DO
  916.     Aim.DataFileName := Aim.DataFileName + HeaderBuffer [I];
  917.  
  918.  
  919.   TempString06 := '';
  920.   FOR I:= 41 TO 46 DO
  921.     TempString06 := TempString06 + HeaderBuffer [I];
  922.   VAL (TempString06, Aim.BlocksPerHash, ValError);
  923.   IF ValError <> 0
  924.      THEN AimFatalError (61, Aim.AimFileName);
  925.  
  926.  
  927.   TempString06 := '';
  928.   FOR I:= 47 TO 52 DO
  929.     TempString06 := TempString06 + HeaderBuffer [I];
  930.   VAL (TempString06, Aim.RecLenData, ValError);
  931.   IF ValError <> 0
  932.      THEN AimFatalError (62, Aim.AimFileName);
  933.  
  934.  
  935.  
  936.   FOR J:= 1 TO AimMaxKeys DO
  937.     BEGIN
  938.       Aim.AimKey [J] := '';
  939.  
  940.       TempString04 := '';
  941.       FOR I:= (J*8 - 8 + 53) TO (J*8 - 8 + 56) DO
  942.         TempString04 := TempString04 + HeaderBuffer [I];
  943.       VAL (TempString04, Aim.KeyBegCol [J], ValError);
  944.       IF ValError <> 0
  945.          THEN AimFatalError (63, Aim.AimFileName);
  946.  
  947.  
  948.       TempString04 := '';
  949.       FOR I:= (J*8 - 8 + 57) TO (J*8 - 8 + 60) DO
  950.         TempString04 := TempString04 + HeaderBuffer [I];
  951.       VAL (TempString04, Aim.KeyEndCol [J], ValError);
  952.       IF ValError <> 0
  953.          THEN AimFatalError (64, Aim.AimFileName);
  954.  
  955.     END;
  956.  
  957.  
  958.   { -- Open the datafile (whose name was in the header record).}
  959.   ASSIGN (Aim.DataFile, Aim.DataFileName);
  960.  
  961.   {$I-}
  962.   RESET  (Aim.DataFile, 1);
  963.   {$I+}
  964.  
  965.   IF IORESULT <> 0
  966.       THEN BEGIN
  967.              Aim.FileOpen := 'D';
  968.              EXIT;
  969.            END;
  970.  
  971.  
  972.  
  973.   Aim.FileOpen := Yes;
  974.  
  975. END;
  976.  
  977. {***************************************************************************}
  978.  
  979. PROCEDURE Aim_CloseFile;
  980.  
  981. {---------------------------------------------------------------------------}
  982. { This routine closes both the aimdex file and its associated datafile.     }
  983. {                                                                           }
  984. {                                                                           }
  985. { INPUT:                                                                    }
  986. {    none as such.                                                          }
  987. {                                                                           }
  988. { OUTPUT:                                                                   }
  989. {    none as such.                                                          }
  990. {                                                                           }
  991. {---------------------------------------------------------------------------}
  992.  
  993. BEGIN
  994.  
  995.   IF Aim.FileOpen = Yes
  996.     THEN BEGIN
  997.            CLOSE (Aim.AimFile);
  998.            CLOSE (Aim.DataFile);
  999.  
  1000.            Aim.FileOpen := No;
  1001.          END;
  1002.  
  1003. END;
  1004.  
  1005. {***************************************************************************}
  1006.  
  1007. PROCEDURE Aim_Read;
  1008.  
  1009. {---------------------------------------------------------------------------}
  1010. { This routine reads to find the first record that matches the search       }
  1011. { criteria passed in the aimdex key variables.                              }
  1012. {                                                                           }
  1013. {                                                                           }
  1014. { INPUT:                                                                    }
  1015. {    AimKey [n] : STRING [30]  - the array of keys to be searched for.  You }
  1016. {                                can have up to seven (MaxKeys) different   }
  1017. {                                keys, each of the form:                    }
  1018. {                                                                           }
  1019. {                                      '1SMITH'  and  '3ATLANTA'            }
  1020. {                                                                           }
  1021. {                                which would mean find the first record     }
  1022. {                                with 'SMITH' in the first aimdexed field   }
  1023. {                                and 'ATLANTA' in the third aimdexed field. }
  1024. {                                                                           }
  1025. {                                                                           }
  1026. { OUTPUT:                                                                   }
  1027. {    Found : STRING [1]    - 'Y' if a record was found                      }
  1028. {                          - 'N' if not                                     }
  1029. {                                                                           }
  1030. {    SeekPosData : LONGINT - if a record was found, it's offset in the data }
  1031. {                                file is returned.                          }
  1032. {                                                                           }
  1033. {---------------------------------------------------------------------------}
  1034.  
  1035. VAR
  1036.   I,J          : INTEGER;
  1037.  
  1038.  
  1039. BEGIN
  1040.  
  1041.   IF Aim.FileOpen <> Yes
  1042.        THEN AimFatalError (80, Aim.AimFileName);
  1043.  
  1044.  
  1045.   Aim.Found        := No;
  1046.   Aim.HashArrayPtr := 0;
  1047.  
  1048.  
  1049.   { -- Check to see if we got passed any valid search keys.}
  1050.   CheckValidKeys (Aim);
  1051.   IF Aim.NumbValidKeys = 0
  1052.      THEN EXIT;
  1053.  
  1054.  
  1055.   { -- Build the hash array from the search keys.  There will be one entry }
  1056.   { -- (or hash value) for each triplet in all the search keys. }
  1057.   FOR I:= 1 TO Aim.NumbValidKeys DO
  1058.     BEGIN
  1059.        FOR J:= 1 TO (LENGTH (Aim.SearchFor [I])-2) DO
  1060.          BEGIN
  1061.             HashString := COPY (Aim.SearchFor [I], J, 3);
  1062.             IF HashString <> '   '
  1063.               THEN BEGIN
  1064.                      Aim.HashArrayPtr := Aim.HashArrayPtr + 1;
  1065.                      AimGetHashNumb (HashString, Aim.HashArray [Aim.HashArrayPtr]);
  1066.                    END;
  1067.          END;
  1068.     END;
  1069.  
  1070.  
  1071.   { -- Pointers to the AND buffer, indicating which datafile record we are    }
  1072.   { -- considering.  We want it to point to just before the first record, so  }
  1073.   { -- that getting the 'next' record will find the first record.             }
  1074.   Aim.BlockOn := 1;
  1075.   Aim.ByteOn  := 1;
  1076.   Aim.BitOn   := 0;
  1077.  
  1078.   { -- Load AND buffer based on HashArray.}
  1079.   LoadAndBuffer (Aim);
  1080.  
  1081.  
  1082.   { -- Read the datafile for matches based on what is in AND buffer.}
  1083.   ReadType := 'R';
  1084.   ReadThruBuff (Aim);
  1085.  
  1086.   IF Aim.Found = Yes
  1087.     THEN Aim.LastReadSucc := Yes  {This flag is for the ReadKG & ReadKP.}
  1088.     ELSE Aim.LastReadSucc := No;
  1089.  
  1090. END;
  1091.  
  1092. {***************************************************************************}
  1093.  
  1094. PROCEDURE Aim_ReadKG;
  1095.  
  1096. {---------------------------------------------------------------------------}
  1097. { Read key sequential.  This routine reads to find the next record that     }
  1098. {   matches the search criteria that was given with the most recent Read.   }
  1099. {                                                                           }
  1100. {                                                                           }
  1101. { INPUT:                                                                    }
  1102. {    None as such, but you must have first done a Read that found a record. }
  1103. {                                                                           }
  1104. {                                                                           }
  1105. { OUTPUT:                                                                   }
  1106. {                                                                           }
  1107. {    Found  : STRING [ 1]   - 'Y'    if a record was found                  }
  1108. {                           - 'N'    if not                                 }
  1109. {                                                                           }
  1110. {    SeekPosData : LONGINT  - if a record was found, its offset in the data }
  1111. {                             file is returned.                             }
  1112. {---------------------------------------------------------------------------}
  1113.  
  1114. BEGIN
  1115.  
  1116.   IF Aim.FileOpen <> Yes
  1117.        THEN AimFatalError (81, Aim.AimFileName);
  1118.  
  1119.   Aim.Found := No;
  1120.  
  1121.   IF Aim.LastReadSucc <> Yes
  1122.      THEN EXIT;
  1123.  
  1124.   IF Aim.BlockOn > Aim.BlocksPerHash   {Are we already past the last record?}
  1125.      THEN EXIT;
  1126.  
  1127.  
  1128.   { -- Read the datafile for matches based on what is in AND buffer, and our }
  1129.   { -- current position in that buffer (from the most recent read).}
  1130.   ReadType := 'KG';
  1131.   ReadThruBuff (Aim);
  1132.  
  1133. END;
  1134.  
  1135. {***************************************************************************}
  1136.  
  1137. PROCEDURE Aim_ReadKP;
  1138.  
  1139. {---------------------------------------------------------------------------}
  1140. { Read key previous.  This routine reads to find the most recent            }
  1141. { (previously found) record that matches the search criteria that was given }
  1142. { with the most recent Read.                                                }
  1143. {                                                                           }
  1144. {                                                                           }
  1145. { INPUT:                                                                    }
  1146. {    None as such, but you must have first done a Read that found a record. }
  1147. {                                                                           }
  1148. {                                                                           }
  1149. { OUTPUT:                                                                   }
  1150. {                                                                           }
  1151. {    Found  : STRING [ 1]   - 'Y'    if a record was found                  }
  1152. {                           - 'N'    if not                                 }
  1153. {                                                                           }
  1154. {    SeekPosData : LONGINT  - if a record was found, its offset in the data }
  1155. {                             file is returned.                             }
  1156. {---------------------------------------------------------------------------}
  1157.  
  1158. BEGIN
  1159.  
  1160.   IF Aim.FileOpen <> Yes
  1161.        THEN AimFatalError (82, Aim.AimFileName);
  1162.  
  1163.  
  1164.   Aim.Found := No;
  1165.  
  1166.   IF Aim.LastReadSucc <> Yes
  1167.      THEN EXIT;
  1168.  
  1169.   IF Aim.BlockOn < 1   {Are we already past the beginning of the first record?}
  1170.      THEN EXIT;
  1171.  
  1172.  
  1173.   { -- Read the datafile for matches based on what is in AND buffer, and our }
  1174.   { -- current position in that buffer (from the most recent read).}
  1175.   ReadType := 'KP';
  1176.   ReadThruBuff (Aim);
  1177.  
  1178. END;
  1179.  
  1180. {***************************************************************************}
  1181.  
  1182. PROCEDURE Aim_InsertKey;
  1183.  
  1184. {---------------------------------------------------------------------------}
  1185. { This routine inserts a record into the aimdex file.  It reads the         }
  1186. {   datafile to get the info it needs to do the insert.  It assumes the     }
  1187. {   datafile record being added already exists.                             }
  1188. {                                                                           }
  1189. {                                                                           }
  1190. { INPUT:                                                                    }
  1191. {    SeekPosData : LONGINT  - offset in the datafile for the record         }
  1192. {                             that is being inserted into the aimdex file.  }
  1193. {                                                                           }
  1194. {                                                                           }
  1195. { OUTPUT:                                                                   }
  1196. {    None as such.  It simply inserts the record.                           }
  1197. {                                                                           }
  1198. {---------------------------------------------------------------------------}
  1199.  
  1200. VAR
  1201.   I,J           : INTEGER;
  1202.  
  1203. BEGIN
  1204.  
  1205.   IF Aim.FileOpen <> Yes
  1206.        THEN AimFatalError (83, Aim.AimFileName);
  1207.  
  1208.   { -- We may appear to be past EOF because buffer not written out yet. }
  1209.   IF (Aim.SeekPosData >= (FILESIZE (Aim.DataFile)-1))
  1210.       THEN BEGIN
  1211.                Aim_CloseFile (Aim);  { This will force buffer to be written}
  1212.                Aim_OpenFile  (Aim);  {   and directory to be updated.}
  1213.            END;
  1214.  
  1215.   { -- Verify that the data file record exists. }
  1216.   SEEK (Aim.DataFile, Aim.SeekPosData);
  1217.   BLOCKREAD (Aim.DataFile, DataBuffer, Aim.RecLenData, CharsRead);
  1218.  
  1219.   IF CharsRead < Aim.RecLenData
  1220.      THEN AimFatalError (11, Aim.DataFileName);
  1221.   IF DataBuffer [Aim.RecLenData] <> LineFeed
  1222.      THEN AimFatalError (12, Aim.DataFileName);
  1223.  
  1224.  
  1225.   { -- Figure out which byte & block in aimdex is pointed to by data record.}
  1226.   Recnum  := (Aim.SeekPosData DIV Aim.RecLenData) + 1;
  1227.   Aim.BlockOn :=  ((Recnum-1) DIV (AimBlockSize * 8)) + 1;
  1228.   Aim.ByteOn  := (((Recnum-1) MOD (AimBlockSize * 8)) DIV 8) + 1;
  1229.  
  1230.  
  1231.   { -- If block pointed to is past the last block in the aimdex, expand the}
  1232.   { -- aimdex file out to that block. }
  1233.   WHILE (Aim.BlockOn > Aim.BlocksPerHash) DO
  1234.      BEGIN
  1235.         ExpandAimFile (Aim);
  1236.      END;
  1237.  
  1238.  
  1239.  
  1240.   { -- Go through the record once for each key and add it to the aimdex.}
  1241.   FOR I:= 1 TO AimMaxKeys DO
  1242.     BEGIN
  1243.  
  1244.        { -- Go through once for each triplet in the key field.}
  1245.        FOR J:= Aim.KeyBegCol [I] TO (Aim.KeyEndCol [I] -2) DO
  1246.           BEGIN
  1247.             StringToAdd := DataBuffer [J];
  1248.             IF (J+1 <= Aim.KeyEndCol [I])
  1249.               THEN StringToAdd := StringToAdd + DataBuffer [J + 1]
  1250.               ELSE StringToAdd := StringToAdd + Bla;
  1251.             IF (J+2 <= Aim.KeyEndCol [I])
  1252.               THEN StringToAdd := StringToAdd + DataBuffer [J + 2]
  1253.               ELSE StringToAdd := StringToAdd + Bla;
  1254.  
  1255.             { -- Ignore keys that are all blank. }
  1256.             IF StringToAdd <> '   '
  1257.               THEN BEGIN
  1258.                      Convert_Upper (StringToAdd);
  1259.  
  1260.                      AimGetHashNumb (StringToAdd, HashNumb);
  1261.  
  1262.                      SeekPosIns := (Aim.BlockOn-1) * 1010 * AimBlockSize +
  1263.                                 (HashNumb * AimBlockSize);
  1264.                      SEEK (Aim.AimFile, SeekPosIns);
  1265.                      BLOCKREAD (Aim.AimFile, AimBuffer, AimBlockSize,
  1266.                                          CharsRead);
  1267.                      IF CharsRead <> AimBlockSize
  1268.                        THEN BEGIN
  1269.                               AimFatalError (65, Aim.AimFileName);
  1270.                             END;
  1271.  
  1272.  
  1273.                      { -- 'Flick on' the appropriate bit in the aim record }
  1274.                      NewByte := AimBuffer [Aim.ByteOn];
  1275.  
  1276.                      WhichBit := (Recnum-1) MOD 8 + 1;
  1277.                      CASE WhichBit OF
  1278.                         1 : NewByte := NewByte OR $80;
  1279.                         2 : NewByte := NewByte OR $40;
  1280.                         3 : NewByte := NewByte OR $20;
  1281.                         4 : NewByte := NewByte OR $10;
  1282.                         5 : NewByte := NewByte OR $08;
  1283.                         6 : NewByte := NewByte OR $04;
  1284.                         7 : NewByte := NewByte OR $02;
  1285.                         8 : NewByte := NewByte OR $01;
  1286.                      END;
  1287.  
  1288.                      AimBuffer [Aim.ByteOn] := NewByte;
  1289.  
  1290.                      SEEK (Aim.AimFile, SeekPosIns);
  1291.                      BLOCKWRITE (Aim.AimFile, AimBuffer, AimBlockSize,
  1292.                                          CharsWrit);
  1293.  
  1294.                    END;
  1295.           END;
  1296.  
  1297.  
  1298.     END;
  1299.  
  1300.   AimWritHeaderRec (Aim);
  1301.  
  1302. END;
  1303.  
  1304. {***************************************************************************}
  1305.  
  1306. END.
  1307.  
  1308. {***************************************************************************}
  1309. {*                         End of AIMUNIT.PAS                              *}
  1310. {***************************************************************************}
  1311.