home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / MKEY.ZIP / MKEY4.PAS next >
Encoding:
Pascal/Delphi Source File  |  1988-06-05  |  17.0 KB  |  508 lines

  1. UNIT Mkey4;
  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.   ColSeqType = RECORD
  24.                  ColSeqSign : BYTE; { The signature byte }
  25.                  ColSeqName : ARRAY[1..10] OF CHAR;
  26.                  ColSeq : ARRAY[1..10] OF CHAR;
  27.                END; {colseqtype}
  28.   KeyDescrip = RECORD
  29.                  Offset : INTEGER;
  30.                  KEYLENGTH : WORD;
  31.                  Flags : WORD;
  32.                  KEYTYPE : KEY_TYPE;
  33.                  EXTENSION : STRING[3];
  34.                  UNIQUE : BOOLEAN;
  35.                  UPSHIFT : BOOLEAN;
  36.                  INDEX_FILE : IndexFILE;
  37.                END;
  38.   DBField = RECORD
  39.               XCoord : INTEGER;
  40.               YCoord : INTEGER;
  41.               FieldData : ^STRING;
  42.               FieldType : Field_Type;
  43.               FieldLength : INTEGER;
  44.               LegalChars : STRING[80];
  45.               ScreenPrompt : STRING[80];
  46.               HelpPrompt : STRING[80];
  47.             END;
  48.  
  49.   FileSpecType = RECORD
  50.                    CASE BYTE OF
  51.                      0 : (RecSize : WORD; { Record length in bytes }
  52.                           PageSize : WORD; { Page size to use       }
  53.                           Number_of_keys : WORD; { # of keys NOT segments }
  54.                           NumRecs : LONGINT; { # of records in file }
  55.                           FileFlags : WORD; { Bit flags for file     }
  56.                           ReservedWord : WORD; { Fill with $0000        }
  57.                           Allocation : WORD; { # pages to preallocate }
  58.                           {Key : ARRAY[0..MaxKeys] OF KeySpecType;}
  59.                           AltColSeq : ColSeqType); { Alternate col seq }
  60.                      1 : (MinSize : ARRAY[1..128] OF BYTE);
  61.                  END; {FileSpecType}
  62.   File_Type = RECORD
  63.                 Name : STRING[60];
  64.                 PositionBlock : ARRAY[1..128] OF BYTE;
  65.                 IOERROR : BOOLEAN;
  66.                 REC_REF : LONGINT;
  67.                 DATA_FILE : DATAFILE;
  68.                 LastKeyUsed : WORD;
  69.                 Key : ARRAY[0..MaxKeys] OF KeyDescrip;
  70.                 PromptAttribute : INTEGER;
  71.                 GetAttribute : INTEGER;
  72.                 DisplayAttribute : INTEGER;
  73.                 HelpAttribute : INTEGER;
  74.                 NumOfFields : INTEGER;
  75.                 Field : ARRAY[1..MaxFields] OF DBField;
  76.                 FileSpec : FileSpecType;
  77.               END;
  78. VAR
  79.   WORK_KEY : TaKeyStr;
  80.   WORK_REC : ARRAY[0..MaxDataRecSize] OF CHAR;
  81.  
  82. FUNCTION KEYOFFSET(VAR R; VAR F) : INTEGER;
  83. PROCEDURE CLOSE_FILE(VAR F : File_Type; VAR R);
  84. PROCEDURE OPEN_FILE(VAR F : File_Type);
  85. PROCEDURE DELETE_RECORD(VAR F : File_Type; VAR R);
  86. PROCEDURE READ_RECORD(VAR F : File_Type; K : INTEGER; VAR R);
  87. PROCEDURE NEXT_RECORD(VAR F : File_Type; K : INTEGER; VAR R);
  88. PROCEDURE PREVIOUS_RECORD(VAR F : File_Type; K : INTEGER; VAR R);
  89. PROCEDURE ADD_RECORD(VAR F : File_Type; VAR R);
  90. PROCEDURE UPDATE_RECORD(VAR F : File_Type; VAR R);
  91.  
  92.  
  93. IMPLEMENTATION
  94.  
  95.   {$I-}
  96.   {=========================================================================}
  97.   {These are the MULKEY Routines themselves}
  98.   {=========================================================================}
  99.  
  100.   FUNCTION KEYOFFSET(VAR R; VAR F) : INTEGER;
  101.   BEGIN
  102.     {Use to compute the OFFSET parameter of a key}
  103.     KEYOFFSET := (OFS(F) + $8000) - (OFS(R) + $8000);
  104.   END;
  105.  
  106.   PROCEDURE KEY_TO_STRING(VAR Key; LEN : BYTE; TYP : KEY_TYPE; UP : BOOLEAN);
  107.     {Converts a key of the designated type to a string in WORK_KEY for Turbo
  108.     Index storage}
  109.   VAR
  110.     INTEGER_KEY : INTEGER ABSOLUTE Key;
  111.     CHAR_KEY : ARRAY[1..MaxKeyLen] OF CHAR ABSOLUTE Key;
  112.     STRING_KEY : STRING[MaxKeyLen] ABSOLUTE Key;
  113.     REAL_KEY : REAL ABSOLUTE Key;
  114.     I : INTEGER;
  115.   BEGIN
  116.     CASE TYP OF
  117.       KEY_INTEGER :
  118.         BEGIN
  119.           I := INTEGER_KEY + $8000;
  120.           WORK_KEY := CHR(Hi(I)) + CHR(Lo(I));
  121.         END;
  122.       KEY_CHAR :
  123.         BEGIN
  124.           IF LEN > MaxKeyLen THEN LEN := MaxKeyLen;
  125.           WORK_KEY[0] := CHR(LEN);
  126.           IF LEN > 0 THEN MOVE(Key, WORK_KEY[1], LEN);
  127.         END;
  128.       KEY_STRING : WORK_KEY := STRING_KEY;
  129.       KEY_REAL : STR(REAL_KEY:16, WORK_KEY);
  130.     END;
  131.     IF UP AND ((TYP = KEY_CHAR) OR (TYP = KEY_STRING)) THEN
  132.       FOR I := 1 TO LENGTH(WORK_KEY) DO
  133.         WORK_KEY[I] := UPCASE(WORK_KEY[I]);
  134.   END;
  135.  
  136.   PROCEDURE CLOSE_FILE(VAR F : File_Type; VAR R);
  137.     {Close database and all index files}
  138.   VAR
  139.     I : INTEGER;
  140.   BEGIN
  141.     WITH F DO
  142.       BEGIN
  143.         CloseFile(DATA_FILE);
  144.         FOR I := 1 TO FileSpec.Number_of_keys DO
  145.           BEGIN
  146.             WITH Key[I] DO CloseIndex(INDEX_FILE);
  147.           END;
  148.       END;
  149.   END;
  150.  
  151.   PROCEDURE OPEN_FILE(VAR F : File_Type);
  152.     {Opens a multi- key database and all index files, re- builds missing index
  153.     file(s) and database freespace chain}
  154.   VAR
  155.     I, Dup : INTEGER;
  156.     FLAG : INTEGER ABSOLUTE WORK_REC;
  157.     KEY_FILE_OK : ARRAY[1..MaxKeys] OF BOOLEAN;
  158.     ALL_KEYS_OK : BOOLEAN;
  159.   BEGIN
  160.     WITH F DO
  161.       BEGIN
  162.         IF (FileSpec.Number_of_keys < 1) OR (FileSpec.Number_of_keys > MaxKeys) THEN
  163.           BEGIN
  164.             WRITELN('In file ', Name, ', ', FileSpec.Number_of_keys,
  165.                     ' keys specified, 1.. ', MaxKeys, ' keys allowed');
  166.             HALT;
  167.           END;
  168.         ALL_KEYS_OK := TRUE;
  169.         IOERROR := FALSE;
  170.         OpenFile(DATA_FILE, Name + '.DAT', FileSpec.RecSize);
  171.         IF NOT OK THEN
  172.           BEGIN
  173.             MakeFile(DATA_FILE, Name + '.DAT', FileSpec.RecSize);
  174.             FOR I := 1 TO FileSpec.Number_of_keys DO
  175.               BEGIN
  176.                 WITH Key[I] DO
  177.                   BEGIN
  178.                     IF UNIQUE THEN Dup := 0 ELSE Dup := 1;
  179.                     MakeIndex(INDEX_FILE, Name + '.' + EXTENSION, KEYLENGTH, Dup);
  180.                     ClearKey(INDEX_FILE);
  181.                   END;
  182.               END;
  183.           END
  184.         ELSE
  185.           BEGIN
  186.             FOR I := 1 TO FileSpec.Number_of_keys DO
  187.               BEGIN
  188.                 WITH Key[I] DO
  189.                   BEGIN
  190.                     IF Offset < 2 THEN
  191.                       BEGIN
  192.                         WRITELN('Key Offset for key ', I, ' is ', Offset,
  193.                                 ', Minimum is 2 for file ', Name);
  194.                         HALT;
  195.                       END;
  196.                     IF (KEYTYPE = KEY_CHAR)
  197.                     AND ((KEYLENGTH < 1) OR (KEYLENGTH > MaxKeyLen)) THEN
  198.                       BEGIN
  199.                         WRITELN('KeyLength for key ', I, ' is ', KEYLENGTH,
  200.                                 ', it must be between 1 and ', MaxKeyLen, ' in file ',
  201.                                 Name);
  202.                         HALT;
  203.                       END;
  204.                     IF UNIQUE THEN Dup := 0 ELSE Dup := 1;
  205.                     OpenIndex(INDEX_FILE, Name + '.' + EXTENSION, KEYLENGTH, Dup);
  206.                     IF NOT OK THEN
  207.                       BEGIN
  208.                         MakeIndex(INDEX_FILE, Name + '.' + EXTENSION, KEYLENGTH,
  209.                                   Dup);
  210.                         ALL_KEYS_OK := FALSE;
  211.                         KEY_FILE_OK[I] := FALSE;
  212.                       END
  213.                     ELSE
  214.                       KEY_FILE_OK[I] := TRUE;
  215.                     ClearKey(INDEX_FILE);
  216.                   END;
  217.               END;
  218.           END;
  219.         IF NOT ALL_KEYS_OK THEN
  220.           BEGIN
  221.             GoToXY(1, 1);
  222.             WRITELN('Please wait, rebuilding index file(s) in ', Name, ' for ',
  223.                     FileLen(DATA_FILE), ' records');
  224.             REC_REF := 1;
  225.             WITH DATA_FILE DO
  226.               BEGIN
  227.                 FirstFree := - 1;
  228.                 NumberFree := 0;
  229.               END;
  230.             WHILE REC_REF < FileLen(DATA_FILE) DO
  231.               BEGIN
  232.                 GetRec(DATA_FILE, REC_REF, WORK_REC);
  233.                 IF FLAG = 0 THEN
  234.                   BEGIN
  235.                     FOR I := 1 TO FileSpec.Number_of_keys DO
  236.                       IF NOT KEY_FILE_OK[I] THEN WITH Key[I] DO
  237.                         BEGIN
  238.                           KEY_TO_STRING(WORK_REC[Offset], KEYLENGTH, KEYTYPE,
  239.                                         UPSHIFT);
  240.                           AddKey(INDEX_FILE, REC_REF, WORK_KEY);
  241.                           IF NOT OK THEN IOERROR := TRUE;
  242.                         END;
  243.                   END
  244.                 ELSE
  245.                   BEGIN
  246.                     WITH DATA_FILE DO
  247.                       BEGIN
  248.                         IF FLAG <> FirstFree THEN
  249.                           BEGIN
  250.                             FLAG := FirstFree;
  251.                             PutRec(DATA_FILE, REC_REF, WORK_REC);
  252.                             FirstFree := REC_REF;
  253.                           END;
  254.                         NumberFree := SUCC(NumberFree);
  255.                       END;
  256.                   END;
  257.                 REC_REF := SUCC(REC_REF);
  258.               END;
  259.           END;
  260.         REC_REF := 0;
  261.       END;
  262.   END;
  263.  
  264.   PROCEDURE DELETE_RECORD(VAR F : File_Type; VAR R);
  265.     {Delete the last record retrieved from the database and all its keys.
  266.     IOERROR indicates no valid last record retrieved.}
  267.   VAR
  268.     K : INTEGER;
  269.   BEGIN
  270.     WITH F DO IF REC_REF <> 0 THEN
  271.       BEGIN
  272.         GetRec(DATA_FILE, REC_REF, WORK_REC);
  273.         DeleteRec(DATA_FILE, REC_REF);
  274.         FOR K := 1 TO FileSpec.Number_of_keys DO WITH Key[K] DO
  275.           BEGIN
  276.             KEY_TO_STRING(WORK_REC[Offset], KEYLENGTH, KEYTYPE, UPSHIFT);
  277.             DeleteKey(INDEX_FILE, REC_REF, WORK_KEY);
  278.           END;
  279.         IOERROR := FALSE;
  280.         REC_REF := 0;
  281.       END
  282.       ELSE
  283.         IOERROR := TRUE;
  284.   END;
  285.  
  286.   PROCEDURE READ_RECORD(VAR F : File_Type; K : INTEGER; VAR R);
  287.     {Read a record from the database with a key equal to or higher than that
  288.     indicated by key field K in record R. IOERROR indicates search key was
  289.     higher than any in the index.}
  290.   VAR
  291.     REC : ARRAY[0..MaxDataRecSize] OF CHAR ABSOLUTE R;
  292.     REF : LONGINT;
  293.   BEGIN
  294.     WITH F DO
  295.       BEGIN
  296.         IF (K > FileSpec.Number_of_keys) OR (K < 1) THEN
  297.           BEGIN
  298.             WRITELN('Key ', K, ' Referenced, Keys 1.. ', FileSpec.Number_of_keys,
  299.                     ' Defined in file ', Name);
  300.             HALT;
  301.           END;
  302.         WITH Key[K] DO
  303.           BEGIN
  304.             REF := 0;
  305.             KEY_TO_STRING(REC[Offset], KEYLENGTH, KEYTYPE, UPSHIFT);
  306.             SearchKey(INDEX_FILE, REF, WORK_KEY);
  307.             IF OK THEN GetRec(DATA_FILE, REF, REC);
  308.             IF OK THEN REC_REF := REF;
  309.             IOERROR := NOT OK;
  310.           END;
  311.       END;
  312.   END;
  313.  
  314.   PROCEDURE NEXT_RECORD(VAR F : File_Type; K : INTEGER; VAR R);
  315.     {Read the next record by key K from the database. IOERROR indicates end of
  316.     file by key K.}
  317.   VAR
  318.     REC : ARRAY[0..MaxDataRecSize] OF CHAR ABSOLUTE R;
  319.     REF : LONGINT;
  320.   BEGIN
  321.     WITH F DO
  322.       BEGIN
  323.         IF (K > FileSpec.Number_of_keys) OR (K < 1) THEN
  324.           BEGIN
  325.             WRITELN('Key ', K, ' Referenced, Keys 1.. ', FileSpec.Number_of_keys,
  326.                     ' Defined in file ', Name);
  327.             HALT;
  328.           END;
  329.         WITH Key[K] DO
  330.           BEGIN
  331.             NextKey(INDEX_FILE, REF, WORK_KEY);
  332.             IF OK THEN GetRec(DATA_FILE, REF, REC);
  333.             IF OK THEN REC_REF := REF;
  334.             IOERROR := NOT OK;
  335.             IF NOT OK THEN
  336.               BEGIN
  337.                 NextKey(INDEX_FILE, REF, WORK_KEY);
  338.                 IF OK THEN GetRec(DATA_FILE, REF, REC);
  339.                 IF OK THEN REC_REF := REF;
  340.               END;
  341.           END;
  342.       END;
  343.   END;
  344.  
  345.   PROCEDURE PREVIOUS_RECORD(VAR F : File_Type; K : INTEGER; VAR R);
  346.     {Read the previous record by key K from the database. IOERROR indicates start
  347.     of file by key K.}
  348.   VAR
  349.     REC : ARRAY[0..MaxDataRecSize] OF CHAR ABSOLUTE R;
  350.     REF : LONGINT;
  351.   BEGIN
  352.     WITH F DO
  353.       BEGIN
  354.         IF (K > FileSpec.Number_of_keys) OR (K < 1) THEN
  355.           BEGIN
  356.             WRITELN('Key ', K, ' Referenced, Keys 1.. ', FileSpec.Number_of_keys,
  357.                     ' Defined in file', Name);
  358.             HALT;
  359.           END;
  360.         WITH Key[K] DO
  361.           BEGIN
  362.             PrevKey(INDEX_FILE, REF, WORK_KEY);
  363.             IF OK THEN GetRec(DATA_FILE, REF, REC);
  364.             IF OK THEN REC_REF := REF;
  365.             IOERROR := NOT OK;
  366.             IF NOT OK THEN
  367.               BEGIN
  368.                 PrevKey(INDEX_FILE, REF, WORK_KEY);
  369.                 IF OK THEN GetRec(DATA_FILE, REF, REC);
  370.                 IF OK THEN REC_REF := REF;
  371.               END;
  372.           END;
  373.       END;
  374.   END;
  375.  
  376.  
  377.   PROCEDURE ADD_RECORD(VAR F : File_Type; VAR R);
  378.     {
  379.     Add record R to the database and update all index files. IOERROR usually
  380.     indicates a duplicate key in a unique key index.
  381.     }
  382.   LABEL
  383.     DemoExit;
  384.   VAR
  385.     Ch : CHAR;
  386.     REC : ARRAY[0..MaxDataRecSize] OF CHAR ABSOLUTE R;
  387.     FLAG : INTEGER ABSOLUTE R;
  388.     REF : LONGINT;
  389.     K : INTEGER;
  390.   BEGIN
  391.     WITH F DO
  392.       BEGIN
  393.         IOERROR := FALSE;
  394.         FLAG := 0;
  395.         {$IFDEF DEMO}
  396.         IF UsedRecs(DATA_FILE) > 11 THEN
  397.           BEGIN
  398.             GoToXY(1, 1);
  399.             WRITELN('Only 10 records allowed in demo version');
  400.             Ch := ReadKey;
  401.             GOTO DemoExit;
  402.           END;
  403.         {$ENDIF}
  404.         AddRec(DATA_FILE, REF, REC);
  405.         FlushFile(DATA_FILE);
  406.         K := 1;
  407.         WHILE (K <= FileSpec.Number_of_keys) AND NOT IOERROR DO
  408.           BEGIN
  409.             WITH Key[K] DO
  410.               BEGIN
  411.                 KEY_TO_STRING(REC[Offset], KEYLENGTH, KEYTYPE, UPSHIFT);
  412.                 AddKey(INDEX_FILE, REF, WORK_KEY);
  413.                 FlushIndex(INDEX_FILE);
  414.                 IOERROR := NOT OK;
  415.               END;
  416.             K := SUCC(K);
  417.           END;
  418.         IF IOERROR THEN
  419.           BEGIN
  420.             K := PRED(PRED(K));
  421.             WHILE K > 0 DO
  422.               BEGIN
  423.                 WITH Key[K] DO
  424.                   BEGIN
  425.                     KEY_TO_STRING(REC[Offset], KEYLENGTH, KEYTYPE, UPSHIFT);
  426.                     DeleteKey(INDEX_FILE, REF, WORK_KEY);
  427.                   END;
  428.                 K := PRED(K);
  429.               END;
  430.             DeleteRec(DATA_FILE, REF);
  431.           END
  432.         ELSE
  433.           REC_REF := REF;
  434. DemoExit:
  435.       END;
  436.   END;
  437.  
  438.   PROCEDURE UPDATE_RECORD(VAR F : File_Type; VAR R);
  439.     {
  440.     Update the last retrieved record with data from record R and update any
  441.     index files whose keys were changed. IOERROR usually indicates a duplicate
  442.     key in a unique key index.
  443.     }
  444.   VAR
  445.     REC : ARRAY[0..MaxDataRecSize] OF CHAR ABSOLUTE R;
  446.     FLAG : INTEGER ABSOLUTE R;
  447.     S : STRING[MaxKeyLen];
  448.     K : INTEGER;
  449.   BEGIN
  450.     WITH F DO
  451.       BEGIN
  452.         IOERROR := FALSE;
  453.         IF REC_REF <> 0 THEN
  454.           BEGIN
  455.             FLAG := 0;
  456.             GetRec(DATA_FILE, REC_REF, WORK_REC);
  457.             K := 1;
  458.             WHILE (K <= FileSpec.Number_of_keys) AND NOT IOERROR DO
  459.               BEGIN
  460.                 WITH Key[K] DO
  461.                   BEGIN
  462.                     KEY_TO_STRING(WORK_REC[Offset], KEYLENGTH, KEYTYPE, UPSHIFT);
  463.                     S := WORK_KEY;
  464.                     KEY_TO_STRING(REC[Offset], KEYLENGTH, KEYTYPE, UPSHIFT);
  465.                     IF S <> WORK_KEY THEN
  466.                       BEGIN
  467.                         DeleteKey(INDEX_FILE, REC_REF, S);
  468.                         AddKey(INDEX_FILE, REC_REF, WORK_KEY);
  469.                         FlushIndex(INDEX_FILE);
  470.                         IOERROR := NOT OK;
  471.                         IF IOERROR THEN AddKey(INDEX_FILE, REC_REF, S);
  472.                       END;
  473.                     K := SUCC(K);
  474.                   END;
  475.               END;
  476.             IF IOERROR THEN
  477.               BEGIN
  478.                 K := PRED(PRED(K));
  479.                 WHILE K > 0 DO
  480.                   BEGIN
  481.                     WITH Key[K] DO
  482.                       BEGIN
  483.                         KEY_TO_STRING(WORK_REC[Offset], KEYLENGTH, KEYTYPE,
  484.                                       UPSHIFT);
  485.                         S := WORK_KEY;
  486.                         KEY_TO_STRING(REC[Offset], KEYLENGTH, KEYTYPE, UPSHIFT);
  487.                         IF S <> WORK_KEY THEN
  488.                           BEGIN
  489.                             DeleteKey(INDEX_FILE, REC_REF, WORK_KEY);
  490.                             AddKey(INDEX_FILE, REC_REF, S);
  491.                           END;
  492.                       END;
  493.                     K := PRED(K);
  494.                   END;
  495.               END
  496.             ELSE
  497.               BEGIN
  498.                 PutRec(DATA_FILE, REC_REF, REC);
  499.                 FlushFile(DATA_FILE);
  500.               END;
  501.           END
  502.         ELSE
  503.           IOERROR := TRUE;
  504.       END;
  505.   END;
  506.   {End of MULTIKEY routines}
  507. END.
  508.