home *** CD-ROM | disk | FTP | other *** search
-
- {Compiler directives}
- {D-} {debug information}
- {T-} {create .TPM file for debugging}
- {F-} {automatically force far calls}
- {V-} {var string checking}
- {L+} {link buffer in memory}
- {$R-} {range checking}
- {$B+} {boolean complete evaluation}
- {$S+} {stack checking}
- {$I+} {I/O checking}
- {$N-} {numeric coprocessor}
- {$M 65500,0,0} {memory sizes}
-
- {***************************************************************************}
- {* *}
- {* AIMUNIT.PAS Copyright - Matt Goodrich *}
- {* Jun 29, 1991 *}
- {* *}
- {* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *}
- {* *}
- {* Maintenance History : *}
- {* *}
- {* 1.A MG 6/29/91 - Initial Keyin. *}
- {* *}
- {***************************************************************************}
-
- UNIT AIMUNIT;
-
- INTERFACE
-
-
- USES
- CRT, DOS, MISCSTUF;
-
- CONST
- AimBlockSize = 512;
- AimMaxKeys = 7;
-
-
-
-
- TYPE
-
- AimVars = RECORD
-
- AimFile : FILE;
- DataFile : FILE;
-
- AimFileName : STRING [40];
- DataFileName : STRING [40];
-
- RecLenData : LONGINT; {length of datafile records}
- BlocksPerHash : LONGINT; {basically, the number of aimfile blocks}
-
- SeekPosData : LONGINT;
-
- AimKey : ARRAY [1..AimMaxKeys] OF STRING [30];
-
- { -- array with beginning & ending columns of aimdex key (eg 2-7,11-17)}
- KeyBegCol : ARRAY [1..AimMaxKeys] OF INTEGER;
- KeyEndCol : ARRAY [1..AimMaxKeys] OF INTEGER;
-
- FileOpen : STRING [ 1];
-
- Found : STRING [ 1];
-
- NumbValidKeys : INTEGER;
-
- BitOn : INTEGER;
- BlockOn : LONGINT;
- ByteOn : LONGINT;
- LastReadSucc : STRING [ 1]; {last Read find a record? (for KG & KP)}
-
- AndingBuffer : ARRAY [1..AimBlockSize] OF BYTE;
- HashArray : ARRAY [1..200] OF LONGINT;
- HashArrayPtr : LONGINT;
-
- SearchFor : ARRAY [1..AimMaxKeys] OF STRING [30];
- WhichKey : ARRAY [1..AimMaxKeys] OF STRING [ 1];
-
- END;
-
- {**************************************************************************}
-
- VAR
-
- Aim : AimVars;
-
- {---------------------------------------------------------------------------}
- { These are the routines that you would call from an application. }
- {---------------------------------------------------------------------------}
-
- PROCEDURE Aim_OpenFile (VAR Aim : AimVars);
- PROCEDURE Aim_CloseFile (VAR Aim : AimVars);
- PROCEDURE Aim_Read (VAR Aim : AimVars);
- PROCEDURE Aim_ReadKG (VAR Aim : AimVars);
- PROCEDURE Aim_ReadKP (VAR Aim : AimVars);
- PROCEDURE Aim_InsertKey (VAR Aim : AimVars);
-
-
- {---------------------------------------------------------------------------}
- { These are used internally and by the AIMDEXP utility - just ignore them. }
- {---------------------------------------------------------------------------}
-
- PROCEDURE AimHaltProgram ( DosErrorLevel : INTEGER);
-
- PROCEDURE AimFatalError ( ErrorNum : INTEGER;
- Message : STRING);
-
- PROCEDURE AimGetHashNumb ( HashString : STRING;
- VAR HashNumb : LONGINT);
-
- PROCEDURE AimWritHeaderRec (VAR Aim : AimVars);
-
-
- {***************************************************************************}
- {***************************************************************************}
- {***************************************************************************}
- {***************************************************************************}
- {***************************************************************************}
-
- IMPLEMENTATION
-
-
- CONST
- Version = '1.A'; {Version number of this module.}
- LineFeed = CHR (10);
-
- {$I AIMVAR.PAS} {Inclusion with shared variable definitions}
-
-
- VAR
-
- BitFlicked : STRING [ 1];
- BlockFound : STRING [ 1];
- DidDataMatch : STRING [ 1];
- ErrMssg : STRING [80];
- HashString : STRING [ 3];
- HeaderBuffer : ARRAY [1..HeaderBuffSize] OF CHAR;
- NewByte : BYTE;
- ReadType : STRING [ 2]; {'R' or 'KG' or 'KP'}
- Recnum : LONGINT;
- SeekPosIns : LONGINT;
- SeekPosSave : LONGINT;
- StringToAdd : STRING [ 3];
-
- {***************************************************************************}
- {***************************************************************************}
- {***************************************************************************}
- {***************************************************************************}
- {***************************************************************************}
- {***************************************************************************}
- { }
- { General info on how the reads work: }
- { }
- { They basically do the following: }
- { }
- { You start out by doing a Read. When you pass in a search key, it builds }
- { 'HashArray' which contains the hash value for each triplet in the search }
- { keys you passed in. The more search keys you pass in, or the longer they }
- { are, the more elements in 'HashArray'. For example, if you passed in }
- { '2JONES', you might end up with hash values like 141, 537, & 982, hence 3 }
- { elements in the array. }
- { }
- { 'AndingBuffer' is then initialized to having all its bits ON. Then, the }
- { aimdex file is read once for each element in 'HashArray' (starting at }
- { block one), and each of those records is ANDed against 'AndingBuffer'. }
- { You end up with 'AndingBuffer' pointing to all the possible hits in the }
- { datafile. }
- { }
- { By using a pointer (ByteOn, BitOn) each bit in 'AndingBuffer' is checked, }
- { and for each bit that is ON, that datafile record is read and scanned to }
- { see if the desired search keys really are in the appropriate fields in }
- { the datafile. If a match is found, stop, leaving 'AndingBuffer' and its }
- { pointers where they are. If the whole variable is checked and there are }
- { no matches, read in the next block and do it all over again (initialize }
- { to all on, read once for each element in 'HashArray', etc). If there are }
- { no more blocks left in the aimdex file (indicated by 'BlocksPerHash'), }
- { then stop, as the read was not successful. }
- { }
- { The ReadKG & ReadKP, are pretty straight forward. They are based on a }
- { successful Read having been done, leaving 'AndingBuffer' and its }
- { pointers. ReadKG simply bumps the pointer 1 bit, and starts from there. }
- { }
- {***************************************************************************}
- {***************************************************************************}
- {***************************************************************************}
- {***************************************************************************}
- {***************************************************************************}
-
- PROCEDURE AimHaltProgram;
-
- BEGIN
-
- GOTOXY (78, 23);
- Cursor_On;
- HALT (DosErrorLevel);
-
- END;
-
- {***************************************************************************}
-
- PROCEDURE AimFatalError;
-
- {---------------------------------------------------------------------------}
- { Stops the program because some horrible error was encountered (e.g. file }
- { damage). Display the appropriate error message based on the error }
- { number that was passed in. The error numbers have no particular }
- { meaning. They merely need to be unique, so that you can tell where the }
- { error originated. }
- { }
- { }
- { INPUT: }
- { ErrorNum : INTEGER - the error number to be displayed. }
- { }
- { Message : STRING - an additional message string that will display }
- { next to the error message. Generally used }
- { for things like passing the name of the file }
- { that is damaged, etc. }
- { }
- { }
- { OUTPUT: }
- { none as such, but this routine stops the program. }
- { }
- {---------------------------------------------------------------------------}
-
- VAR
- I : INTEGER;
-
- BEGIN
-
- FOR I:= 19 TO 25 DO
- BEGIN
- GOTOXY (1, I);
- CLREOL;
- END;
-
- Disp_String ('CRASH! AIMUNIT.TPU got error #', 1, 20, Bold_Video);
- Disp_Number (ErrorNum, 33, 20, Reverse_Video, 4, 0);
-
- ErrMssg := '';
-
- CASE ErrorNum OF
-
- 10,11,12,13,14,15,16,17 : BEGIN
- ErrMssg := 'There appears to be damage to the data file: ';
- END;
-
- 20,21,22,23,24,25,26,27,28,29 : BEGIN
- ErrMssg := 'You gave an invalid aimdex key field: ';
- END;
-
- 40,41 : BEGIN
- ErrMssg := 'I had a problem writing to the aimdex file: ';
- END;
-
- 50,51 : BEGIN
- ErrMssg := 'I couldn''t OPEN the aimdex file: ';
- END;
-
- 60,61,62,63,64,65 : BEGIN
- ErrMssg := 'There appears to be damage to the aimdex file: ';
- END;
-
- 70,71 : BEGIN
- ErrMssg := 'I couldn''t OPEN the datafile: ';
- END;
-
- 80,81,82,83 : BEGIN
- ErrMssg := 'You tried to do something without ' +
- 'first opening the file. ';
- END;
-
- 90 : BEGIN
- ErrMssg := 'The datafile had a record that was too long.';
- END;
-
- 100 : BEGIN
- ErrMssg := 'You gave an invalid record length: ';
- END;
-
- 110 : BEGIN
- ErrMssg := 'I couldn''t CREATE the aimdex file: ';
- END;
-
- 120 : BEGIN
- ErrMssg := 'There are no datafile records, so you must specify ' +
- 'the record length.';
- END;
-
- 130,131 : BEGIN
- ErrMssg := 'HeaderBuffSize is more than AimBlockSize.';
- END;
-
- 140 : BEGIN
- ErrMssg := 'The datafile has too many records.';
- END;
-
- 150,151 : BEGIN
- ErrMssg := 'You gave too many aimdex keys.';
- END;
-
- END;
-
-
- Disp_String (ErrMssg + Message, 1, 22, Bold_Video);
-
-
- AimHaltProgram (1); {Return a DOS errorlevel of 1.}
-
- END;
- {***************************************************************************}
-
- PROCEDURE AimGetHashNumb;
-
- {---------------------------------------------------------------------------}
- { Calculate the hash value for a passed string. It uses the Division }
- { remainder method to calculate, and returns a number from 1 to 1009 }
- { (inclusive). }
- { }
- { INPUT: }
- { HashString : STRING [ 3] - the 3 byte string that you want a hash }
- { value for. }
- { }
- { OUTPUT: }
- { HashNumb : LONGINT - the calculated hash value. }
- { }
- {---------------------------------------------------------------------------}
-
- VAR
- TempString03 : STRING [ 3];
-
- BEGIN
-
- TempString03 := HashString + Bla50;
-
- { -- Returns a number from 0-1008.}
- HashNumb := (255 * 255 * ORD (TempString03 [1]) +
- 255 * ORD (TempString03 [2]) +
- ORD (TempString03 [3])) MOD 1009;
-
-
- HashNumb := HashNumb + 1; {Add one to skip past the header rec. }
-
- END;
-
- {***************************************************************************}
-
- PROCEDURE AimWritHeaderRec;
-
- {---------------------------------------------------------------------------}
- { Write the header record to the aimdex file. }
- { }
- { INPUT: }
- { HeaderBuffSize and all the fields that are being written to the }
- { aimdex header record. }
- { }
- { OUTPUT: }
- { None as such. It just does the write. }
- { }
- {---------------------------------------------------------------------------}
-
-
- VAR
- I,J : INTEGER;
- TempString04 : STRING [ 4];
- TempString06 : STRING [ 6];
- TempString40 : STRING [40];
-
-
- BEGIN
-
- FOR I := 1 TO HeaderBuffSize DO
- BEGIN
- HeaderBuffer [I] := Bla;
- END;
-
-
- { -- Load the header buffer from the various variables.}
-
- TempString40 := Aim.DataFileName + Bla50;
- FOR I:= 1 TO 40 DO HeaderBuffer [I] := TempString40 [I];
-
- STR (Aim.BlocksPerHash:6, TempString06);
- FOR I:= 41 TO 46 DO HeaderBuffer [I] := TempString06 [I-40];
-
- STR (Aim.RecLenData:6, TempString06);
- FOR I:= 47 TO 52 DO HeaderBuffer [I] := TempString06 [I-46];
-
- FOR J:= 1 TO AimMaxKeys DO
- BEGIN
- STR (Aim.KeyBegCol [J] :4, TempString04);
- FOR I:= 1 TO 4 DO HeaderBuffer [J*8+44+I] := TempString04 [I];
-
- STR (Aim.KeyEndCol [J] :4, TempString04);
- FOR I:= 1 TO 4 DO HeaderBuffer [J*8+48+I] := TempString04 [I];
- END;
-
-
- SeekPos := 0;
- SEEK (Aim.AimFile, SeekPos);
- BLOCKWRITE (Aim.AimFile, HeaderBuffer, HeaderBuffSize, CharsWrit);
-
- END;
-
- {***************************************************************************}
- {***************************************************************************}
- {***************************************************************************}
- {***************************************************************************}
- {***************************************************************************}
-
- PROCEDURE MatchDataRec (VAR Aim : AimVars);
-
- {---------------------------------------------------------------------------}
- { Verify that the datafile record that was read actually contains the }
- { search strings that were specified. The aimdex can return some 'false }
- { hits', so we actually have to read the datafile to be sure we have a }
- { valid record. Returns 'Y' or 'N' in DidDataMatch. }
- { }
- { INPUT: }
- { Lots }
- { }
- { OUTPUT: }
- { DidDataMatch : STRING [ 1] - does the datarecord match the search }
- { criteria? (Y/N) }
- {---------------------------------------------------------------------------}
-
- VAR
- I,J,M,N : INTEGER;
- TempInteger : INTEGER;
- TempString255 : STRING [255];
-
- BEGIN
-
- { -- If you want to ignore deleted records by way of an "I'm a deleted }
- { -- record" byte, put that logic here, and don't bother with matching }
- { -- the datafile record. }
-
- DidDataMatch := Yes;
-
- FOR I:= 1 TO Aim.NumbValidKeys DO
- BEGIN
-
- VAL (Aim.WhichKey [I], TempInteger, ValError);
- M := Aim.KeyBegCol [TempInteger];
- N := Aim.KeyEndCol [TempInteger];
-
- TempString255 := '';
- FOR J:= M TO N DO TempString255 := TempString255 + DataBuffer [J];
-
- Convert_Upper (TempString255);
- IF POS (Aim.SearchFor [I], TempString255) = 0
- THEN DidDataMatch := No;
- END;
-
- END;
-
- {***************************************************************************}
-
- PROCEDURE LoadAndBuffer (VAR Aim : AimVars);
-
- {---------------------------------------------------------------------------}
- { Load the next (or previous) block of the 'AND'ing buffer. Read the aimdex }
- { file for each hash value in the hash array and AND each of those records }
- { one at a time to end up with the final 'AND' block. }
- { }
- { INPUT: }
- { Lots }
- { }
- { OUTPUT: }
- { AndingBuffer : ARRAY OF BYTE }
- { }
- {---------------------------------------------------------------------------}
-
- VAR
- I,J : INTEGER;
-
- BEGIN
-
- { -- Initialize the AND buffer to all bits turned on.}
- FOR I:= 1 TO AimBlockSize DO
- BEGIN
- Aim.AndingBuffer [I] := $FF;
- END;
-
-
- FOR I:= 1 TO Aim.HashArrayPtr DO
- BEGIN
- SeekPos := (Aim.BlockOn-1) * 1010 * AimBlockSize +
- (Aim.HashArray [I] * AimBlockSize);
- SEEK (Aim.AimFile, SeekPos);
- BLOCKREAD (Aim.AimFile, AimBuffer, AimBlockSize, CharsRead);
-
- FOR J:= 1 TO AimBlockSize DO
- BEGIN
- Aim.AndingBuffer [J] := Aim.AndingBuffer [J] AND AimBuffer [J];
- END;
-
- END;
-
- END;
-
- {***************************************************************************}
-
- PROCEDURE PointToNextBit (VAR Aim : AimVars);
-
- {---------------------------------------------------------------------------}
- { Move the 'AND' buffer pointers (BlockOn, ByteOn, BitOn) to point to the }
- { the next datafile record (moving them forwards if Read or KG, backwards }
- { if KP). If we go past the beginning of the first block (or the end of the }
- { last) then we are done searching and will return 'N' in BlockFound. }
- { }
- { INPUT: }
- { Lots }
- { }
- { OUTPUT: }
- { Buffer pointers }
- { BlockFound : STRING [ 1] - was the next bit found? (Y/N) }
- { }
- {---------------------------------------------------------------------------}
-
- BEGIN
-
- BlockFound := Yes;
-
- IF (ReadType = 'R') OR (ReadType = 'KG')
- THEN BEGIN
- Aim.BitOn := Aim.BitOn + 1;
- IF Aim.BitOn > 8
- THEN BEGIN
- Aim.BitOn := 1;
- Aim.ByteOn := Aim.ByteOn + 1;
- IF Aim.ByteOn > AimBlockSize
- THEN BEGIN
- Aim.ByteOn := 1;
- Aim.BlockOn := Aim.BlockOn + 1;
- IF Aim.BlockOn > Aim.BlocksPerHash
- THEN BlockFound := No
- ELSE LoadAndBuffer (Aim);
- END;
- END;
- END
-
- ELSE BEGIN
- Aim.BitOn := Aim.BitOn - 1;
- IF Aim.BitOn < 1
- THEN BEGIN
- Aim.BitOn := 8;
- Aim.ByteOn := Aim.ByteOn - 1;
- IF Aim.ByteOn < 1
- THEN BEGIN
- Aim.ByteOn := AimBlockSize;
- Aim.BlockOn := Aim.BlockOn - 1;
- IF Aim.BlockOn < 1
- THEN BlockFound := No
- ELSE LoadAndBuffer (Aim);
- END;
- END
- END;
-
- END;
-
- {***************************************************************************}
-
- PROCEDURE ReadThruBuff (VAR Aim : AimVars);
-
- {---------------------------------------------------------------------------}
- { Step through each bit in the AND buffer. Each bit that is on represents }
- { a record in the datafile that may be a match. Read that record in the }
- { datafile and see if it is indeed a match. If not, keep stepping through }
- { the AND buffer bit by bit, byte by byte, block by block, until either a }
- { match is found or the end of the last block is reached. }
- { }
- { INPUT: }
- { Lots }
- { }
- { OUTPUT: }
- { Found : STRING [ 1] - was a datafile record found? (Y/N) }
- { SeekPosData : LONGINT - if so, here's it's offset. }
- { }
- {---------------------------------------------------------------------------}
-
- VAR
- EndLoopByte : STRING [ 1];
- EndLoopBit : STRING [ 1];
- I : INTEGER;
-
- BEGIN
-
- Aim.Found := No;
-
- PointToNextBit (Aim);
- IF BlockFound = No {ptr went past last block or beginning of 1st block}
- THEN EXIT;
-
-
- EndLoopByte := No;
- WHILE EndLoopByte = No DO
- BEGIN
- EndLoopBit := No;
-
- WHILE EndLoopBit = No DO
- BEGIN
- BitFlicked := No;
- CASE Aim.BitOn OF
- 1: BEGIN
- IF (Aim.AndingBuffer [Aim.ByteOn] AND $80) = $80
- THEN BitFlicked := Yes;
- END;
- 2: BEGIN
- IF (Aim.AndingBuffer [Aim.ByteOn] AND $40) = $40
- THEN BitFlicked := Yes;
- END;
- 3: BEGIN
- IF (Aim.AndingBuffer [Aim.ByteOn] AND $20) = $20
- THEN BitFlicked := Yes;
- END;
- 4: BEGIN
- IF (Aim.AndingBuffer [Aim.ByteOn] AND $10) = $10
- THEN BitFlicked := Yes;
- END;
- 5: BEGIN
- IF (Aim.AndingBuffer [Aim.ByteOn] AND $08) = $08
- THEN BitFlicked := Yes;
- END;
- 6: BEGIN
- IF (Aim.AndingBuffer [Aim.ByteOn] AND $04) = $04
- THEN BitFlicked := Yes;
- END;
- 7: BEGIN
- IF (Aim.AndingBuffer [Aim.ByteOn] AND $02) = $02
- THEN BitFlicked := Yes;
- END;
- 8: BEGIN
- IF (Aim.AndingBuffer [Aim.ByteOn] AND $01) = $01
- THEN BitFlicked := Yes;
- END;
-
- END;
-
-
- IF BitFlicked = Yes
- THEN BEGIN
-
- SeekPosSave := (Aim.BlockOn-1) * Aim.RecLenData
- * AimBlockSize * 8
- + (8 * (Aim.ByteOn-1) * Aim.RecLenData)
- + (Aim.BitOn-1) * Aim.RecLenData;
- SEEK (Aim.DataFile, SeekPosSave);
- BLOCKREAD (Aim.DataFile, DataBuffer, Aim.RecLenData,
- CharsRead);
-
- IF CharsRead <> Aim.RecLenData
- THEN AimFatalError (10, Aim.DataFileName);
-
- MatchDataRec (Aim);
- IF DidDataMatch = Yes
- THEN BEGIN
- { -- If you want a datafile readlist you }
- { -- would put it here. }
-
- EndLoopBit := Yes;
- EndLoopByte := Yes;
- Aim.Found := Yes;
- Aim.SeekPosData := SeekPosSave;
- END;
-
- END;
-
-
- IF Aim.Found = No
- THEN BEGIN
- PointToNextBit (Aim);
- IF BlockFound = No
- THEN BEGIN
- EndLoopBit := Yes;
- EndLoopByte := Yes;
- Aim.Found := No;
- END;
- END;
-
- END;
-
- END;
-
- END;
-
- {***************************************************************************}
-
- PROCEDURE CheckValidKeys (VAR Aim : AimVars);
-
- {---------------------------------------------------------------------------}
- { Check all the AimKeys that were passed in. For each one that is valid, }
- { add it to the WhichKey & SearchFor arrays and increment NumbValidKeys by }
- { one. A valid key would look something like '2SMITH'. }
- { }
- { INPUT: }
- { AimKey : ARRAY OF STRING [30] - user supplied key ('2SMITH') }
- { }
- { OUTPUT: }
- { WhichKey : ARRAY OF STRING [ 1] - 1st character of valid key }
- { SearchFor : ARRAY OF STRING [30] - the rest of that valid key }
- { NumbValidKeys : INTEGER }
- { }
- {---------------------------------------------------------------------------}
-
- VAR
- I,J : INTEGER;
- TempString01 : STRING [ 1];
- TempString03 : STRING [ 3];
- TempString04 : STRING [ 4];
- TempInteger : INTEGER;
- TempKey : STRING [30];
- ValidKey : STRING [ 1];
-
- BEGIN
-
- Aim.NumbValidKeys := 0;
-
- FOR I:= 1 TO AimMaxKeys DO
- BEGIN
-
- ValidKey := Yes;
- TempKey := Aim.AimKey [I];
-
-
- { -- Don't allow non-ASCII characters.}
- FOR J:= 1 TO LENGTH (TempKey) DO
- BEGIN
- IF (ORD (TempKey [J]) < 32) OR
- (ORD (TempKey [J]) > 127)
- THEN ValidKey := No;
- END;
-
-
-
- Convert_Upper (TempKey); {Convert to upper case.}
-
-
- { -- If necessary, pad with blanks out to 4 characters. }
- IF LENGTH (TempKey) < 4
- THEN BEGIN
- TempString04 := TempKey + Bla50;
- TempKey := TempString04;
- END;
-
-
- { -- Make sure the first character is a number.}
- TempString01 := COPY (TempKey, 1, 1);
- VAL (TempString01, TempInteger, ValError);
- IF (ValError = 0) AND (TempInteger <= AimMaxKeys) AND (TempInteger > 0)
- THEN BEGIN
- { -- I did it this way because I don't know/trust the VAL }
- { -- command enough to use 'OR' logic. }
- END
- ELSE BEGIN
- ValidKey := No;
- END;
-
-
- { -- Make sure there are at least 3 non-blank characters.}
- TempString03 := COPY (TempKey, 2, 3);
- IF TempString03 = ' '
- THEN ValidKey := No;
-
-
-
- { -- All clear. Go ahead and add it.}
- IF ValidKey = Yes
- THEN BEGIN
- Aim.NumbValidKeys := Aim.NumbValidKeys + 1;
- Aim.WhichKey [Aim.NumbValidKeys] := COPY (TempKey,
- 1, 1);
- Aim.SearchFor [Aim.NumbValidKeys] := COPY (TempKey,
- 2, LENGTH (TempKey) - 1);
- END;
-
- END;
-
- END;
-
- {***************************************************************************}
-
- PROCEDURE ExpandAimFile (VAR Aim : AimVars);
-
- {---------------------------------------------------------------------------}
- { When inserting records, the aimdex block gradually gets filled up, and }
- { eventually a new, empty one needs to be created. This writes a bunch }
- { of empty records (hex 00) to the end of the aimdex. }
- { }
- { INPUT: }
- { Lots. }
- { }
- { OUTPUT: }
- { None as such. It just does the expand. }
- { }
- {---------------------------------------------------------------------------}
-
- VAR
- I : INTEGER;
-
- BEGIN
- Save_Screen; {Save the screen so we can restore it later. }
- Cursor_Off;
-
- { -- Pop up a box on the screen with the 'please wait' message.}
- WINDOW (1, 9, 70, 14);
- CLRSCR;
- WINDOW (1, 1, 80, 25);
- Draw_DBox (1, 9, 70, 14, Normal_Video);
- Disp_String ('Please wait a moment while I expand the aimdex file...',
- 3, 11, Normal_Video);
- Disp_String ('(I''m counting to 1009)', 19, 12, Normal_Video);
-
-
- FOR I:= 1 TO AimBlockSize DO
- AimBuffer [I] := $00;
-
-
- { -- Write one block for each of the 1009 hash values plus 1 for header.}
- SeekPos := Aim.BlocksPerHash * 1010 * AimBlockSize;
- SEEK (Aim.AimFile, SeekPos);
- FOR I:= 1 TO 1010 DO
- BEGIN
- BLOCKWRITE (Aim.AimFile, AimBuffer, AimBlockSize, CharsWrit);
- IF CharsWrit < AimBlockSize
- THEN AimFatalError (40, Aim.AimFileName);
-
- Disp_Number (I-1, 56, 11, Normal_Video, 7, 0);
- END;
-
-
- Aim.BlocksPerHash := Aim.BlocksPerHash + 1;
- AimWritHeaderRec (Aim);
-
- Cursor_On;
- Restore_Screen;
-
- END;
-
- {***************************************************************************}
- {***************************************************************************}
- {***************************************************************************}
- {***************************************************************************}
- {***************************************************************************}
-
- PROCEDURE Aim_OpenFile;
-
- {---------------------------------------------------------------------------}
- { This routine opens the aimdex and its associated datafile and reads the }
- { header record to get all the various information. }
- { }
- { }
- { INPUT: }
- { AimFileName : STRING [40] - the aimdex file to be opened. }
- { }
- { }
- { OUTPUT: }
- { FileOpen : STRING [ 1] - 'Y' if both the aimdex and the datafile }
- { were opened successfully }
- { 'A' if unable to open the aimdex }
- { 'D' if unable to open the datafile }
- { }
- {---------------------------------------------------------------------------}
-
- VAR
- I,J : INTEGER;
- TempString04 : STRING [ 4];
- TempString06 : STRING [ 6];
-
- BEGIN
-
- Aim.HashArrayPtr := 0; {Initialize this incase they try to do a KG or }
- {KP without first doing a Read. (Pointers would }
- {be all screwed up.) }
-
- Aim.FileOpen := No;
-
-
- IF (HeaderBuffSize > AimBlockSize)
- THEN BEGIN
- AimFatalError (130, '');
- END;
-
-
-
-
- ASSIGN (Aim.AimFile, Aim.AimFileName);
-
- {$I-}
- RESET (Aim.AimFile, 1);
- {$I+}
-
- IF IORESULT <> 0
- THEN BEGIN
- Aim.FileOpen := 'A';
- EXIT;
- END;
-
-
-
-
- { -- Read the header record. }
- SeekPos := 0;
- SEEK (Aim.AimFile, SeekPos);
- BLOCKREAD (Aim.AimFile, HeaderBuffer, HeaderBuffSize, CharsRead);
- IF CharsRead < HeaderBuffSize
- THEN AimFatalError (60, Aim.AimFileName);
-
-
- { -- Load the header record variables. }
-
- Aim.DataFileName := '';
- FOR I:= 1 TO 40 DO
- Aim.DataFileName := Aim.DataFileName + HeaderBuffer [I];
-
-
- TempString06 := '';
- FOR I:= 41 TO 46 DO
- TempString06 := TempString06 + HeaderBuffer [I];
- VAL (TempString06, Aim.BlocksPerHash, ValError);
- IF ValError <> 0
- THEN AimFatalError (61, Aim.AimFileName);
-
-
- TempString06 := '';
- FOR I:= 47 TO 52 DO
- TempString06 := TempString06 + HeaderBuffer [I];
- VAL (TempString06, Aim.RecLenData, ValError);
- IF ValError <> 0
- THEN AimFatalError (62, Aim.AimFileName);
-
-
-
- FOR J:= 1 TO AimMaxKeys DO
- BEGIN
- Aim.AimKey [J] := '';
-
- TempString04 := '';
- FOR I:= (J*8 - 8 + 53) TO (J*8 - 8 + 56) DO
- TempString04 := TempString04 + HeaderBuffer [I];
- VAL (TempString04, Aim.KeyBegCol [J], ValError);
- IF ValError <> 0
- THEN AimFatalError (63, Aim.AimFileName);
-
-
- TempString04 := '';
- FOR I:= (J*8 - 8 + 57) TO (J*8 - 8 + 60) DO
- TempString04 := TempString04 + HeaderBuffer [I];
- VAL (TempString04, Aim.KeyEndCol [J], ValError);
- IF ValError <> 0
- THEN AimFatalError (64, Aim.AimFileName);
-
- END;
-
-
- { -- Open the datafile (whose name was in the header record).}
- ASSIGN (Aim.DataFile, Aim.DataFileName);
-
- {$I-}
- RESET (Aim.DataFile, 1);
- {$I+}
-
- IF IORESULT <> 0
- THEN BEGIN
- Aim.FileOpen := 'D';
- EXIT;
- END;
-
-
-
- Aim.FileOpen := Yes;
-
- END;
-
- {***************************************************************************}
-
- PROCEDURE Aim_CloseFile;
-
- {---------------------------------------------------------------------------}
- { This routine closes both the aimdex file and its associated datafile. }
- { }
- { }
- { INPUT: }
- { none as such. }
- { }
- { OUTPUT: }
- { none as such. }
- { }
- {---------------------------------------------------------------------------}
-
- BEGIN
-
- IF Aim.FileOpen = Yes
- THEN BEGIN
- CLOSE (Aim.AimFile);
- CLOSE (Aim.DataFile);
-
- Aim.FileOpen := No;
- END;
-
- END;
-
- {***************************************************************************}
-
- PROCEDURE Aim_Read;
-
- {---------------------------------------------------------------------------}
- { This routine reads to find the first record that matches the search }
- { criteria passed in the aimdex key variables. }
- { }
- { }
- { INPUT: }
- { AimKey [n] : STRING [30] - the array of keys to be searched for. You }
- { can have up to seven (MaxKeys) different }
- { keys, each of the form: }
- { }
- { '1SMITH' and '3ATLANTA' }
- { }
- { which would mean find the first record }
- { with 'SMITH' in the first aimdexed field }
- { and 'ATLANTA' in the third aimdexed field. }
- { }
- { }
- { OUTPUT: }
- { Found : STRING [1] - 'Y' if a record was found }
- { - 'N' if not }
- { }
- { SeekPosData : LONGINT - if a record was found, it's offset in the data }
- { file is returned. }
- { }
- {---------------------------------------------------------------------------}
-
- VAR
- I,J : INTEGER;
-
-
- BEGIN
-
- IF Aim.FileOpen <> Yes
- THEN AimFatalError (80, Aim.AimFileName);
-
-
- Aim.Found := No;
- Aim.HashArrayPtr := 0;
-
-
- { -- Check to see if we got passed any valid search keys.}
- CheckValidKeys (Aim);
- IF Aim.NumbValidKeys = 0
- THEN EXIT;
-
-
- { -- Build the hash array from the search keys. There will be one entry }
- { -- (or hash value) for each triplet in all the search keys. }
- FOR I:= 1 TO Aim.NumbValidKeys DO
- BEGIN
- FOR J:= 1 TO (LENGTH (Aim.SearchFor [I])-2) DO
- BEGIN
- HashString := COPY (Aim.SearchFor [I], J, 3);
- IF HashString <> ' '
- THEN BEGIN
- Aim.HashArrayPtr := Aim.HashArrayPtr + 1;
- AimGetHashNumb (HashString, Aim.HashArray [Aim.HashArrayPtr]);
- END;
- END;
- END;
-
-
- { -- Pointers to the AND buffer, indicating which datafile record we are }
- { -- considering. We want it to point to just before the first record, so }
- { -- that getting the 'next' record will find the first record. }
- Aim.BlockOn := 1;
- Aim.ByteOn := 1;
- Aim.BitOn := 0;
-
- { -- Load AND buffer based on HashArray.}
- LoadAndBuffer (Aim);
-
-
- { -- Read the datafile for matches based on what is in AND buffer.}
- ReadType := 'R';
- ReadThruBuff (Aim);
-
- IF Aim.Found = Yes
- THEN Aim.LastReadSucc := Yes {This flag is for the ReadKG & ReadKP.}
- ELSE Aim.LastReadSucc := No;
-
- END;
-
- {***************************************************************************}
-
- PROCEDURE Aim_ReadKG;
-
- {---------------------------------------------------------------------------}
- { Read key sequential. This routine reads to find the next record that }
- { matches the search criteria that was given with the most recent Read. }
- { }
- { }
- { INPUT: }
- { None as such, but you must have first done a Read that found a record. }
- { }
- { }
- { OUTPUT: }
- { }
- { Found : STRING [ 1] - 'Y' if a record was found }
- { - 'N' if not }
- { }
- { SeekPosData : LONGINT - if a record was found, its offset in the data }
- { file is returned. }
- {---------------------------------------------------------------------------}
-
- BEGIN
-
- IF Aim.FileOpen <> Yes
- THEN AimFatalError (81, Aim.AimFileName);
-
- Aim.Found := No;
-
- IF Aim.LastReadSucc <> Yes
- THEN EXIT;
-
- IF Aim.BlockOn > Aim.BlocksPerHash {Are we already past the last record?}
- THEN EXIT;
-
-
- { -- Read the datafile for matches based on what is in AND buffer, and our }
- { -- current position in that buffer (from the most recent read).}
- ReadType := 'KG';
- ReadThruBuff (Aim);
-
- END;
-
- {***************************************************************************}
-
- PROCEDURE Aim_ReadKP;
-
- {---------------------------------------------------------------------------}
- { Read key previous. This routine reads to find the most recent }
- { (previously found) record that matches the search criteria that was given }
- { with the most recent Read. }
- { }
- { }
- { INPUT: }
- { None as such, but you must have first done a Read that found a record. }
- { }
- { }
- { OUTPUT: }
- { }
- { Found : STRING [ 1] - 'Y' if a record was found }
- { - 'N' if not }
- { }
- { SeekPosData : LONGINT - if a record was found, its offset in the data }
- { file is returned. }
- {---------------------------------------------------------------------------}
-
- BEGIN
-
- IF Aim.FileOpen <> Yes
- THEN AimFatalError (82, Aim.AimFileName);
-
-
- Aim.Found := No;
-
- IF Aim.LastReadSucc <> Yes
- THEN EXIT;
-
- IF Aim.BlockOn < 1 {Are we already past the beginning of the first record?}
- THEN EXIT;
-
-
- { -- Read the datafile for matches based on what is in AND buffer, and our }
- { -- current position in that buffer (from the most recent read).}
- ReadType := 'KP';
- ReadThruBuff (Aim);
-
- END;
-
- {***************************************************************************}
-
- PROCEDURE Aim_InsertKey;
-
- {---------------------------------------------------------------------------}
- { This routine inserts a record into the aimdex file. It reads the }
- { datafile to get the info it needs to do the insert. It assumes the }
- { datafile record being added already exists. }
- { }
- { }
- { INPUT: }
- { SeekPosData : LONGINT - offset in the datafile for the record }
- { that is being inserted into the aimdex file. }
- { }
- { }
- { OUTPUT: }
- { None as such. It simply inserts the record. }
- { }
- {---------------------------------------------------------------------------}
-
- VAR
- I,J : INTEGER;
-
- BEGIN
-
- IF Aim.FileOpen <> Yes
- THEN AimFatalError (83, Aim.AimFileName);
-
- { -- We may appear to be past EOF because buffer not written out yet. }
- IF (Aim.SeekPosData >= (FILESIZE (Aim.DataFile)-1))
- THEN BEGIN
- Aim_CloseFile (Aim); { This will force buffer to be written}
- Aim_OpenFile (Aim); { and directory to be updated.}
- END;
-
- { -- Verify that the data file record exists. }
- SEEK (Aim.DataFile, Aim.SeekPosData);
- BLOCKREAD (Aim.DataFile, DataBuffer, Aim.RecLenData, CharsRead);
-
- IF CharsRead < Aim.RecLenData
- THEN AimFatalError (11, Aim.DataFileName);
- IF DataBuffer [Aim.RecLenData] <> LineFeed
- THEN AimFatalError (12, Aim.DataFileName);
-
-
- { -- Figure out which byte & block in aimdex is pointed to by data record.}
- Recnum := (Aim.SeekPosData DIV Aim.RecLenData) + 1;
- Aim.BlockOn := ((Recnum-1) DIV (AimBlockSize * 8)) + 1;
- Aim.ByteOn := (((Recnum-1) MOD (AimBlockSize * 8)) DIV 8) + 1;
-
-
- { -- If block pointed to is past the last block in the aimdex, expand the}
- { -- aimdex file out to that block. }
- WHILE (Aim.BlockOn > Aim.BlocksPerHash) DO
- BEGIN
- ExpandAimFile (Aim);
- END;
-
-
-
- { -- Go through the record once for each key and add it to the aimdex.}
- FOR I:= 1 TO AimMaxKeys DO
- BEGIN
-
- { -- Go through once for each triplet in the key field.}
- FOR J:= Aim.KeyBegCol [I] TO (Aim.KeyEndCol [I] -2) DO
- BEGIN
- StringToAdd := DataBuffer [J];
- IF (J+1 <= Aim.KeyEndCol [I])
- THEN StringToAdd := StringToAdd + DataBuffer [J + 1]
- ELSE StringToAdd := StringToAdd + Bla;
- IF (J+2 <= Aim.KeyEndCol [I])
- THEN StringToAdd := StringToAdd + DataBuffer [J + 2]
- ELSE StringToAdd := StringToAdd + Bla;
-
- { -- Ignore keys that are all blank. }
- IF StringToAdd <> ' '
- THEN BEGIN
- Convert_Upper (StringToAdd);
-
- AimGetHashNumb (StringToAdd, HashNumb);
-
- SeekPosIns := (Aim.BlockOn-1) * 1010 * AimBlockSize +
- (HashNumb * AimBlockSize);
- SEEK (Aim.AimFile, SeekPosIns);
- BLOCKREAD (Aim.AimFile, AimBuffer, AimBlockSize,
- CharsRead);
- IF CharsRead <> AimBlockSize
- THEN BEGIN
- AimFatalError (65, Aim.AimFileName);
- END;
-
-
- { -- 'Flick on' the appropriate bit in the aim record }
- NewByte := AimBuffer [Aim.ByteOn];
-
- WhichBit := (Recnum-1) MOD 8 + 1;
- CASE WhichBit OF
- 1 : NewByte := NewByte OR $80;
- 2 : NewByte := NewByte OR $40;
- 3 : NewByte := NewByte OR $20;
- 4 : NewByte := NewByte OR $10;
- 5 : NewByte := NewByte OR $08;
- 6 : NewByte := NewByte OR $04;
- 7 : NewByte := NewByte OR $02;
- 8 : NewByte := NewByte OR $01;
- END;
-
- AimBuffer [Aim.ByteOn] := NewByte;
-
- SEEK (Aim.AimFile, SeekPosIns);
- BLOCKWRITE (Aim.AimFile, AimBuffer, AimBlockSize,
- CharsWrit);
-
- END;
- END;
-
-
- END;
-
- AimWritHeaderRec (Aim);
-
- END;
-
- {***************************************************************************}
-
- END.
-
- {***************************************************************************}
- {* End of AIMUNIT.PAS *}
- {***************************************************************************}