home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / MKEY4.ZIP / MULKEY4.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1988-04-19  |  20.6 KB  |  626 lines

  1. UNIT Mulkey4;
  2.  
  3. INTERFACE
  4.  
  5. USES Crt, taccess;
  6.  
  7.   {$I-}
  8.  
  9. CONST
  10.   MaxKeys = 5;
  11.   MaxFields = 25;
  12.  
  13. TYPE
  14.   KEY_TYPE = (KEY_INTEGER, KEY_CHAR, KEY_STRING, KEY_REAL);
  15.   Field_Type = (Valid_Date_Field, Date_Field, Integer_Field,
  16.                 Real_Field, String_Field, Non_Blank, Memo_Field);
  17.  
  18.                 {***** All Date Fields must be a STRING of at LEAST
  19.                        Length 10. Memo fields must be LongInts. All
  20.                        other fields are STRINGS of appropriate lengths *****}
  21.  
  22.  
  23.   KeyDescrip = RECORD
  24.                  Offset : INTEGER;
  25.                  KEYLENGTH : WORD;
  26.                  EXTENSION : STRING[3];
  27.                  UNIQUE : BOOLEAN;
  28.                  UPSHIFT : BOOLEAN;
  29.                  KEYTYPE : KEY_TYPE;
  30.                  INDEX_FILE : IndexFile;
  31.                END;
  32.   DBField = RECORD
  33.               XCoord : INTEGER;
  34.               YCoord : INTEGER;
  35.               FieldData : ^STRING;
  36.               FieldType : Field_Type;
  37.               FieldLength : INTEGER;
  38.               LegalChars : STRING[80];
  39.               ScreenPrompt : STRING[80];
  40.               HelpPrompt : STRING[80];
  41.             END;
  42.   File_Type = RECORD
  43.                 Name : STRING[60];
  44.                 RecSize : INTEGER;
  45.                 IOERROR : BOOLEAN;
  46.                 REC_REF : LONGINT;
  47.                 DATA_FILE : DataFile;
  48.                 NUMBER_OF_KEYS : BYTE;
  49.                 Key : ARRAY[1..MaxKeys] OF KeyDescrip;
  50.                 PromptAttribute : INTEGER;
  51.                 GetAttribute : INTEGER;
  52.                 DisplayAttribute : INTEGER;
  53.                 HelpAttribute : INTEGER;
  54.                 NumOfFields : INTEGER;
  55.                 Field : ARRAY[1..MaxFields] OF DBField;
  56.               END;
  57.   DBFile = ^File_Type;
  58.   Word2 = STRING[254];
  59.  
  60. VAR
  61.   WORK_KEY : TaKeyStr;
  62.   WORK_REC : ARRAY[0..MaxDataRecSize] OF CHAR;
  63.  
  64. FUNCTION WILDMATCH(FirstStr, SecondStr : Word2) : BOOLEAN;
  65. FUNCTION KEYOFFSET(VAR R; VAR F) : INTEGER;
  66. PROCEDURE KEY_TO_STRING(VAR Key; Len : BYTE; TYP : KEY_TYPE; UP : BOOLEAN);
  67. PROCEDURE CLOSE_FILE(VAR F : File_Type);
  68. PROCEDURE OPEN_FILE(VAR F : File_Type);
  69. PROCEDURE DELETE_RECORD(VAR F : File_Type);
  70. PROCEDURE READ_RECORD(VAR F : File_Type; K : INTEGER; VAR R);
  71. PROCEDURE NEXT_RECORD(VAR F : File_Type; K : INTEGER; VAR R);
  72. PROCEDURE PREVIOUS_RECORD(VAR F : File_Type; K : INTEGER; VAR R);
  73. PROCEDURE SEARCH_PARTIAL(VAR F : File_Type; K : INTEGER; Templat : Word2;
  74.                          VAR R);
  75. PROCEDURE ADD_RECORD(VAR F : File_Type; VAR R);
  76. PROCEDURE UPDATE_RECORD(VAR F : File_Type; VAR R);
  77.  
  78.  
  79. IMPLEMENTATION
  80.  
  81.   {$I-}
  82.   {=========================================================================}
  83.   {These are the MULKEY Routines themselves}
  84.   {=========================================================================}
  85.  
  86.   FUNCTION WILDMATCH {(FirstStr, SecondStr : Word2) : Boolean} ;
  87.     {- search forward to find a given variable or scope}
  88.     {- return true if something was found}
  89.   CONST
  90.     maxwrdchr2 = 255;
  91.     any = '* ';
  92.     one = '?';
  93.     termchar = '$$';
  94.     termchar1 = '$';
  95.  
  96.     PROCEDURE simptemp(VAR t : Word2);
  97.       {- simplify the template by removing adjacent * 's}
  98.     VAR
  99.       I, J : INTEGER;
  100.     BEGIN
  101.       I := 1; J := 1;
  102.       WHILE t[I] <> termchar1 DO
  103.         BEGIN
  104.           IF ((t[I] = any) AND (t[SUCC(I)] = any)) THEN
  105.             FOR J := SUCC(I) TO PRED(maxwrdchr2) DO t[J] := t[SUCC(J)]
  106.           ELSE
  107.             I := SUCC(I);
  108.         END;
  109.     END;           {simptemp}
  110.  
  111.     FUNCTION Match(S, t : Word2) : BOOLEAN;
  112.       {- return true if this is a match}
  113.       {- s is the test string, t is the match template}
  114.     LABEL 1;
  115.     VAR
  116.       tp, tpon, spsav, Sp : INTEGER;
  117.       On : BOOLEAN;
  118.     BEGIN
  119.       Sp := 1; tp := 1;
  120.       On := FALSE;
  121.       Match := FALSE;
  122.       WHILE ((S[Sp] <> termchar1) OR (t[tp] <> termchar1)) DO
  123.         BEGIN
  124.           WHILE t[tp] = one DO
  125.             BEGIN {pass over '?' keeping t and s sync'ed}
  126.               IF S[Sp] = termchar1 THEN GOTO 1; {ran out of string needing a ? match}
  127.               Sp := SUCC(Sp);
  128.               tp := SUCC(tp);
  129.             END;
  130.           IF t[tp] = any THEN
  131.             BEGIN {'* ' was found}
  132.               On := TRUE;
  133.               tpon := SUCC(tp); {save the next template position beyond *}
  134.               spsav := Sp; {save string position}
  135.               tp := SUCC(tp); {move to next template check}
  136.               IF t[tp] = termchar1 THEN
  137.                 BEGIN {last template character was '* '}
  138.                   Match := TRUE; {any more string chars must match}
  139.                   GOTO 1;
  140.                 END;
  141.             END;
  142.           IF t[tp] = S[Sp] THEN
  143.             BEGIN {if match found proceed to next comparison}
  144.               Sp := SUCC(Sp);
  145.               tp := SUCC(tp);
  146.             END
  147.           ELSE
  148.             BEGIN {otherwise....}
  149.               IF (NOT On) THEN GOTO 1 {fatal mismatch - no backup from *}
  150.               ELSE
  151.                 BEGIN {non- fatal mismatch}
  152.                   spsav := SUCC(spsav); {account for mismatched char with the *}
  153.                   IF S[spsav] = termchar1
  154.                   THEN GOTO 1; {end of string without fitting template}
  155.                   Sp := spsav;
  156.                   tp := tpon; {try the template constraint just
  157.                               following the * again}
  158.                 END;
  159.             END;
  160.         END; {of both strings reached}
  161.       Match := TRUE;
  162. 1:
  163.     END;              {match}
  164.  
  165.   BEGIN
  166.     WILDMATCH := FALSE;
  167.     FirstStr := FirstStr + '$';
  168.     SecondStr := SecondStr + '$';
  169.     simptemp(SecondStr);
  170.     IF Match(FirstStr, SecondStr) THEN
  171.       WILDMATCH := TRUE
  172.     ELSE
  173.       WILDMATCH := FALSE;
  174.   END;
  175.  
  176.   FUNCTION KEYOFFSET {(VAR R; VAR F) : Integer} ;
  177.   BEGIN
  178.     {Use to compute the OFFSET parameter of a key}
  179.     KEYOFFSET := (OFS(F) + $8000) - (OFS(R) + $8000);
  180.   END;
  181.  
  182.   PROCEDURE KEY_TO_STRING {(VAR Key; LEN : Byte; TYP : KEY_TYPE; UP : Boolean)} ;
  183.     {Converts a key of the designated type to a string in WORK_KEY for Turbo
  184.     Index storage}
  185.   VAR
  186.     INTEGER_KEY : INTEGER ABSOLUTE Key;
  187.     CHAR_KEY : ARRAY[1..MaxKeyLen] OF CHAR ABSOLUTE Key;
  188.     STRING_KEY : STRING[MaxKeyLen] ABSOLUTE Key;
  189.     REAL_KEY : REAL ABSOLUTE Key;
  190.     I : INTEGER;
  191.   BEGIN
  192.     CASE TYP OF
  193.       KEY_INTEGER :
  194.         BEGIN
  195.           I := INTEGER_KEY + $8000;
  196.           WORK_KEY := CHR(Hi(I)) + CHR(Lo(I));
  197.         END;
  198.       KEY_CHAR :
  199.         BEGIN
  200.           IF Len > MaxKeyLen THEN Len := MaxKeyLen;
  201.           WORK_KEY[0] := CHR(Len);
  202.           IF Len > 0 THEN MOVE(Key, WORK_KEY[1], Len);
  203.         END;
  204.       KEY_STRING : WORK_KEY := STRING_KEY;
  205.       KEY_REAL : STR(REAL_KEY:16, WORK_KEY);
  206.     END;
  207.     IF UP AND ((TYP = KEY_CHAR) OR (TYP = KEY_STRING)) THEN
  208.       FOR I := 1 TO LENGTH(WORK_KEY) DO
  209.         WORK_KEY[I] := UPCASE(WORK_KEY[I]);
  210.   END;
  211.  
  212.   PROCEDURE CLOSE_FILE {(VAR F : File_Type)} ;
  213.     {Close database and all index files}
  214.   VAR
  215.     I : INTEGER;
  216.   BEGIN
  217.     WITH F DO
  218.       BEGIN
  219.         CloseFile(DATA_FILE);
  220.         FOR I := 1 TO NUMBER_OF_KEYS DO
  221.           BEGIN
  222.             WITH Key[I] DO CloseIndex(INDEX_FILE);
  223.           END;
  224.       END;
  225.   END;
  226.  
  227.   PROCEDURE OPEN_FILE {(VAR F : File_Type)} ;
  228.     {Opens a multi- key database and all index files, re- builds missing index
  229.     file(s) and database freespace chain}
  230.   VAR
  231.     I, Dup : INTEGER;
  232.     FLAG : INTEGER ABSOLUTE WORK_REC;
  233.     KEY_FILE_OK : ARRAY[1..MaxKeys] OF BOOLEAN;
  234.     ALL_KEYS_OK : BOOLEAN;
  235.   BEGIN
  236.     WITH F DO
  237.       BEGIN
  238.         IF (NUMBER_OF_KEYS < 1) OR (NUMBER_OF_KEYS > MaxKeys) THEN
  239.           BEGIN
  240.             WRITELN('In file ', Name, ', ', NUMBER_OF_KEYS,
  241.                     ' keys specified, 1.. ', MaxKeys, ' keys allowed');
  242.             HALT;
  243.           END;
  244.         ALL_KEYS_OK := TRUE;
  245.         IOERROR := FALSE;
  246.         OpenFile(DATA_FILE, Name + '.DAT', RecSize);
  247.         IF NOT OK THEN
  248.           BEGIN
  249.             MakeFile(DATA_FILE, Name + '.DAT', RecSize);
  250.             FOR I := 1 TO NUMBER_OF_KEYS DO
  251.               BEGIN
  252.                 WITH Key[I] DO
  253.                   BEGIN
  254.                     IF UNIQUE THEN Dup := 0 ELSE Dup := 1;
  255.                     MakeIndex(INDEX_FILE, Name + '.' + EXTENSION, KEYLENGTH, Dup);
  256.                     ClearKey(INDEX_FILE);
  257.                   END;
  258.               END;
  259.           END
  260.         ELSE
  261.           BEGIN
  262.             FOR I := 1 TO NUMBER_OF_KEYS DO
  263.               BEGIN
  264.                 WITH Key[I] DO
  265.                   BEGIN
  266.                     IF Offset < 2 THEN
  267.                       BEGIN
  268.                         WRITELN('Key Offset for key ', I, ' is ', Offset,
  269.                                 ', Minimum is 2 for file ', Name);
  270.                         HALT;
  271.                       END;
  272.                     IF (KEYTYPE = KEY_CHAR)
  273.                     AND ((KEYLENGTH < 1) OR (KEYLENGTH > MaxKeyLen)) THEN
  274.                       BEGIN
  275.                         WRITELN('KeyLength for key ', I, ' is ', KEYLENGTH,
  276.                                 ', it must be between 1 and ', MaxKeyLen, ' in file ',
  277.                                 Name);
  278.                         HALT;
  279.                       END;
  280.                     IF UNIQUE THEN Dup := 0 ELSE Dup := 1;
  281.                     OpenIndex(INDEX_FILE, Name + '.' + EXTENSION, KEYLENGTH, Dup);
  282.                     IF NOT OK THEN
  283.                       BEGIN
  284.                         MakeIndex(INDEX_FILE, Name + '.' + EXTENSION, KEYLENGTH,
  285.                                   Dup);
  286.                         ALL_KEYS_OK := FALSE;
  287.                         KEY_FILE_OK[I] := FALSE;
  288.                       END
  289.                     ELSE
  290.                       KEY_FILE_OK[I] := TRUE;
  291.                     ClearKey(INDEX_FILE);
  292.                   END;
  293.               END;
  294.           END;
  295.         IF NOT ALL_KEYS_OK THEN
  296.           BEGIN
  297.             GoToXY(1, 1);
  298.             WRITELN('Please wait, rebuilding index file(s) in ', Name, ' for ',
  299.                     FileLen(DATA_FILE), ' records');
  300.             REC_REF := 1;
  301.             WITH DATA_FILE DO
  302.               BEGIN
  303.                 FirstFree := - 1;
  304.                 NumberFree := 0;
  305.               END;
  306.             WHILE REC_REF < FileLen(DATA_FILE) DO
  307.               BEGIN
  308.                 GetRec(DATA_FILE, REC_REF, WORK_REC);
  309.                 IF FLAG = 0 THEN
  310.                   BEGIN
  311.                     FOR I := 1 TO NUMBER_OF_KEYS DO
  312.                       IF NOT KEY_FILE_OK[I] THEN WITH Key[I] DO
  313.                         BEGIN
  314.                           KEY_TO_STRING(WORK_REC[Offset], KEYLENGTH, KEYTYPE,
  315.                                         UPSHIFT);
  316.                           AddKey(INDEX_FILE, REC_REF, WORK_KEY);
  317.                           IF NOT OK THEN IOERROR := TRUE;
  318.                         END;
  319.                   END
  320.                 ELSE
  321.                   BEGIN
  322.                     WITH DATA_FILE DO
  323.                       BEGIN
  324.                         IF FLAG <> FirstFree THEN
  325.                           BEGIN
  326.                             FLAG := FirstFree;
  327.                             PutRec(DATA_FILE, REC_REF, WORK_REC);
  328.                             FirstFree := REC_REF;
  329.                           END;
  330.                         NumberFree := SUCC(NumberFree);
  331.                       END;
  332.                   END;
  333.                 REC_REF := SUCC(REC_REF);
  334.               END;
  335.           END;
  336.         REC_REF := 0;
  337.       END;
  338.   END;
  339.  
  340.   PROCEDURE DELETE_RECORD {(VAR F : File_Type)} ;
  341.     {Delete the last record retrieved from the database and all its keys.
  342.     IOERROR indicates no valid last record retrieved.}
  343.   VAR
  344.     K : INTEGER;
  345.   BEGIN
  346.     WITH F DO IF REC_REF <> 0 THEN
  347.       BEGIN
  348.         GetRec(DATA_FILE, REC_REF, WORK_REC);
  349.         DeleteRec(DATA_FILE, REC_REF);
  350.         FOR K := 1 TO NUMBER_OF_KEYS DO WITH Key[K] DO
  351.           BEGIN
  352.             KEY_TO_STRING(WORK_REC[Offset], KEYLENGTH, KEYTYPE, UPSHIFT);
  353.             DeleteKey(INDEX_FILE, REC_REF, WORK_KEY);
  354.           END;
  355.         IOERROR := FALSE;
  356.         REC_REF := 0;
  357.       END
  358.       ELSE
  359.         IOERROR := TRUE;
  360.   END;
  361.  
  362.   PROCEDURE READ_RECORD {(VAR F : File_Type; K : Integer; VAR R)} ;
  363.     {Read a record from the database with a key equal to or higher than that
  364.     indicated by key field K in record R. IOERROR indicates search key was
  365.     higher than any in the index.}
  366.   VAR
  367.     REC : ARRAY[0..MaxDataRecSize] OF CHAR ABSOLUTE R;
  368.     REF : LONGINT;
  369.   BEGIN
  370.     WITH F DO
  371.       BEGIN
  372.         IF (K > NUMBER_OF_KEYS) OR (K < 1) THEN
  373.           BEGIN
  374.             WRITELN('Key ', K, ' Referenced, Keys 1.. ', NUMBER_OF_KEYS,
  375.                     ' Defined in file ', Name);
  376.             HALT;
  377.           END;
  378.         WITH Key[K] DO
  379.           BEGIN
  380.             Ref := 0;
  381.             KEY_TO_STRING(REC[Offset], KEYLENGTH, KEYTYPE, UPSHIFT);
  382.             SearchKey(INDEX_FILE, REF, WORK_KEY);
  383.             IF OK THEN GetRec(DATA_FILE, REF, REC);
  384.             IF OK THEN REC_REF := REF;
  385.             IOERROR := NOT OK;
  386.           END;
  387.       END;
  388.   END;
  389.  
  390.   PROCEDURE NEXT_RECORD {(VAR F : File_Type; K : Integer; VAR R)} ;
  391.     {Read the next record by key K from the database. IOERROR indicates end of
  392.     file by key K.}
  393.   VAR
  394.     REC : ARRAY[0..MaxDataRecSize] OF CHAR ABSOLUTE R;
  395.     REF : LONGINT;
  396.   BEGIN
  397.     WITH F DO
  398.       BEGIN
  399.         IF (K > NUMBER_OF_KEYS) OR (K < 1) THEN
  400.           BEGIN
  401.             WRITELN('Key ', K, ' Referenced, Keys 1.. ', NUMBER_OF_KEYS,
  402.                     ' Defined in file ', Name);
  403.             HALT;
  404.           END;
  405.         WITH Key[K] DO
  406.           BEGIN
  407.             NextKey(INDEX_FILE, REF, WORK_KEY);
  408.             IF OK THEN GetRec(DATA_FILE, REF, REC);
  409.             IF OK THEN REC_REF := REF;
  410.             IOERROR := NOT OK;
  411.             IF NOT OK THEN
  412.               BEGIN
  413.                 NextKey(INDEX_FILE, REF, WORK_KEY);
  414.                 IF OK THEN GetRec(DATA_FILE, REF, REC);
  415.                 IF OK THEN REC_REF := REF;
  416.               END;
  417.           END;
  418.       END;
  419.   END;
  420.  
  421.   PROCEDURE PREVIOUS_RECORD {(VAR F : File_Type; K : Integer; VAR R)} ;
  422.     {Read the previous record by key K from the database. IOERROR indicates start
  423.     of file by key K.}
  424.   VAR
  425.     REC : ARRAY[0..MaxDataRecSize] OF CHAR ABSOLUTE R;
  426.     REF : LONGINT;
  427.   BEGIN
  428.     WITH F DO
  429.       BEGIN
  430.         IF (K > NUMBER_OF_KEYS) OR (K < 1) THEN
  431.           BEGIN
  432.             WRITELN('Key ', K, ' Referenced, Keys 1.. ', NUMBER_OF_KEYS,
  433.                     ' Defined in file', Name);
  434.             HALT;
  435.           END;
  436.         WITH Key[K] DO
  437.           BEGIN
  438.             PrevKey(INDEX_FILE, REF, WORK_KEY);
  439.             IF OK THEN GetRec(DATA_FILE, REF, REC);
  440.             IF OK THEN REC_REF := REF;
  441.             IOERROR := NOT OK;
  442.             IF NOT OK THEN
  443.               BEGIN
  444.                 PrevKey(INDEX_FILE, REF, WORK_KEY);
  445.                 IF OK THEN GetRec(DATA_FILE, REF, REC);
  446.                 IF OK THEN REC_REF := REF;
  447.               END;
  448.           END;
  449.       END;
  450.   END;
  451.  
  452.   PROCEDURE SEARCH_PARTIAL {(VAR F : File_Type; K : Integer; Templat : Word2;
  453.                            VAR R)} ;
  454.     {-- This procedure is to allow a wildcard match of an index field.
  455.     Acceptable wildcards are * and ?. Templat must be a string, no
  456.     greater than 254 characters. Careful attention must be paid that
  457.     the search starts from the Work_Key you specify. To start the search
  458.     from the beginning set the Work_Key to blank, to pick up the
  459.     search from the last point, return Work_Key to the value it had
  460.     after this procedure}
  461.   VAR
  462.     REC : ARRAY[0..MaxDataRecSize] OF CHAR ABSOLUTE R;
  463.     REF : LONGINT;
  464.     Foundit : BOOLEAN;
  465.   BEGIN
  466.     WITH F DO
  467.       BEGIN
  468.         IF (K > NUMBER_OF_KEYS) OR (K < 1) THEN
  469.           BEGIN
  470.             WRITELN('Key ', K, ' Referenced, Keys 1.. ', NUMBER_OF_KEYS,
  471.                     ' Defined in file ', Name);
  472.             HALT;
  473.           END;
  474.         WITH Key[K] DO
  475.           BEGIN
  476.             REF := REC_REF;
  477.             SearchKey(INDEX_FILE, REF, WORK_KEY);
  478.             NextKey(INDEX_FILE, REF, WORK_KEY);
  479.             IF NOT OK THEN
  480.               BEGIN
  481.                 ClearKey(INDEX_FILE);
  482.                 NextKey(INDEX_FILE, REF, WORK_KEY);
  483.               END;
  484.             WHILE OK AND NOT WILDMATCH(WORK_KEY, Templat) DO
  485.               BEGIN
  486.                 NextKey(INDEX_FILE, REF, WORK_KEY);
  487.               END;    {while}
  488.             REC_REF := REF;
  489.             GetRec(DATA_FILE, REC_REF, REC);
  490.             IOERROR := NOT OK;
  491.           END;       {With K}
  492.       END;           {With F}
  493.   END;               {Search}
  494.  
  495.   PROCEDURE ADD_RECORD {(VAR F : File_Type; VAR R)} ;
  496.     {
  497.     Add record R to the database and update all index files. IOERROR usually
  498.     indicates a duplicate key in a unique key index.
  499.     }
  500.   LABEL
  501.     DemoExit;
  502.   VAR
  503.     Ch : CHAR;
  504.     REC : ARRAY[0..MaxDataRecSize] OF CHAR ABSOLUTE R;
  505.     FLAG : INTEGER ABSOLUTE R;
  506.     REF : LONGINT;
  507.     K : INTEGER;
  508.   BEGIN
  509.     WITH F DO
  510.       BEGIN
  511.         IOERROR := FALSE;
  512.         FLAG := 0;
  513.         {$IFDEF DEMO}
  514.         IF UsedRecs(DATA_FILE) > 11 THEN
  515.           BEGIN
  516.             GoToXY(1, 1);
  517.             WRITELN('Only 10 records allowed in demo version');
  518.             Ch := ReadKey;
  519.             GOTO DemoExit;
  520.           END;
  521.         {$ENDIF}
  522.         AddRec(DATA_FILE, REF, REC);
  523.         FlushFile(DATA_FILE);
  524.         K := 1;
  525.         WHILE (K <= NUMBER_OF_KEYS) AND NOT IOERROR DO
  526.           BEGIN
  527.             WITH Key[K] DO
  528.               BEGIN
  529.                 KEY_TO_STRING(REC[Offset], KEYLENGTH, KEYTYPE, UPSHIFT);
  530.                 AddKey(INDEX_FILE, REF, WORK_KEY);
  531.                 FlushIndex(INDEX_FILE);
  532.                 IOERROR := NOT OK;
  533.               END;
  534.             K := SUCC(K);
  535.           END;
  536.         IF IOERROR THEN
  537.           BEGIN
  538.             K := PRED(PRED(K));
  539.             WHILE K > 0 DO
  540.               BEGIN
  541.                 WITH Key[K] DO
  542.                   BEGIN
  543.                     KEY_TO_STRING(REC[Offset], KEYLENGTH, KEYTYPE, UPSHIFT);
  544.                     DeleteKey(INDEX_FILE, REF, WORK_KEY);
  545.                   END;
  546.                 K := PRED(K);
  547.               END;
  548.             DeleteRec(DATA_FILE, REF);
  549.           END
  550.         ELSE
  551.           REC_REF := REF;
  552. DemoExit:
  553.       END;
  554.   END;
  555.  
  556.   PROCEDURE UPDATE_RECORD {(VAR F : File_Type; VAR R)} ;
  557.     {
  558.     Update the last retrieved record with data from record R and update any
  559.     index files whose keys were changed. IOERROR usually indicates a duplicate
  560.     key in a unique key index.
  561.     }
  562.   VAR
  563.     REC : ARRAY[0..MaxDataRecSize] OF CHAR ABSOLUTE R;
  564.     FLAG : INTEGER ABSOLUTE R;
  565.     S : STRING[MaxKeyLen];
  566.     K : INTEGER;
  567.   BEGIN
  568.     WITH F DO
  569.       BEGIN
  570.         IOERROR := FALSE;
  571.         IF REC_REF <> 0 THEN
  572.           BEGIN
  573.             FLAG := 0;
  574.             GetRec(DATA_FILE, REC_REF, WORK_REC);
  575.             K := 1;
  576.             WHILE (K <= NUMBER_OF_KEYS) AND NOT IOERROR DO
  577.               BEGIN
  578.                 WITH Key[K] DO
  579.                   BEGIN
  580.                     KEY_TO_STRING(WORK_REC[Offset], KEYLENGTH, KEYTYPE, UPSHIFT);
  581.                     S := WORK_KEY;
  582.                     KEY_TO_STRING(REC[Offset], KEYLENGTH, KEYTYPE, UPSHIFT);
  583.                     IF S <> WORK_KEY THEN
  584.                       BEGIN
  585.                         DeleteKey(INDEX_FILE, REC_REF, S);
  586.                         AddKey(INDEX_FILE, REC_REF, WORK_KEY);
  587.                         FlushIndex(INDEX_FILE);
  588.                         IOERROR := NOT OK;
  589.                         IF IOERROR THEN AddKey(INDEX_FILE, REC_REF, S);
  590.                       END;
  591.                     K := SUCC(K);
  592.                   END;
  593.               END;
  594.             IF IOERROR THEN
  595.               BEGIN
  596.                 K := PRED(PRED(K));
  597.                 WHILE K > 0 DO
  598.                   BEGIN
  599.                     WITH Key[K] DO
  600.                       BEGIN
  601.                         KEY_TO_STRING(WORK_REC[Offset], KEYLENGTH, KEYTYPE,
  602.                                       UPSHIFT);
  603.                         S := WORK_KEY;
  604.                         KEY_TO_STRING(REC[Offset], KEYLENGTH, KEYTYPE, UPSHIFT);
  605.                         IF S <> WORK_KEY THEN
  606.                           BEGIN
  607.                             DeleteKey(INDEX_FILE, REC_REF, WORK_KEY);
  608.                             AddKey(INDEX_FILE, REC_REF, S);
  609.                           END;
  610.                       END;
  611.                     K := PRED(K);
  612.                   END;
  613.               END
  614.             ELSE
  615.               BEGIN
  616.                 PutRec(DATA_FILE, REC_REF, REC);
  617.                 FlushFile(DATA_FILE);
  618.               END;
  619.           END
  620.         ELSE
  621.           IOERROR := TRUE;
  622.       END;
  623.   END;
  624.   {End of MULTIKEY routines}
  625. END.
  626.