home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / DBF4PAS.ZIP / DBF-TP3.PAS next >
Encoding:
Pascal/Delphi Source File  |  1993-01-04  |  27.3 KB  |  858 lines

  1. uses crt,dos;
  2.  
  3.  
  4.  
  5.   (*
  6.   DBF.PAS version 1.3
  7.   Copyright (C) 1986 By James Troutman
  8.   CompuServe PPN 74746,1567
  9.   Permission is granted to use these routines for non-commercial purposes.
  10.   For commercial use, please request permission via EasyPlex.
  11.  
  12.   Revision history
  13.    1.1  - 5/6/86 - update header when modifying the .DBF file; write the
  14.    End Of File marker; simplify use.
  15.  
  16.    1.2  - 5/27/86 - removed (some of) the absurdities from the code;
  17.    allocate the current record buffer on the heap rather than in the data
  18.    segment; symbol names changed to avoid conflicts; some error checking
  19.    added.
  20.  
  21.    1.3  - 6/5/86 - added support for dBASE II files; new procedure CreateDbf.
  22.  
  23.                     !!!!ATTENTION!!!!
  24.   If you have downloaded an earlier version of this file, please note that
  25.   several of the TYPEs and VARs have been changed.  You may have to make
  26.   some adjustments to any existing programs you have that use these routines.
  27.  
  28.   The routines in this file present some tools for accessing dBASE II, III, and
  29.   III Plus files from within a Turbo Pascal program.  There is MUCH
  30.   room for improvement: the error checking is simplistic, there is no support
  31.   for memo files, no buffering of data, no support for index files,
  32.   etc. The main routines are:
  33.  
  34.          PROCEDURE OpenDbf(VAR D : dbfRecord;) : Integer;
  35.          PROCEDURE CloseDbf(VAR D : dbfRecord) : Integer;
  36.          PROCEDURE GetDbfRecord(VAR D : dbfRecord; RecNum : Real);
  37.          PROCEDURE PutDbfRecord(VAR D : dbfRecord; RecNum : Real);
  38.          PROCEDURE AppendDbf(VAR D : dbfRecord);
  39.          PROCEDURE CreateDbf(VAR D : dbfRecord; fn : _Str64; n : Integer;
  40.                              flds : _dFields);
  41.  
  42.   After calling one of the procedures, check the status of the Boolean variable
  43.   dbfOK to determine the success or failure of the operation.  If it failed,
  44.   dbfError will contain a value corresponding to the IOResult value or
  45.   to a specially assigned value for several special conditions.  Notice in
  46.   particular that an unsuccessful call to CloseDbf will leave the file status
  47.   unchanged and the memory still allocated.  It is your program's
  48.   responsibility to take appropriate action.
  49.  
  50.   A skeletal program might go something like:
  51.     {$I Dbf.PAS}
  52.     VAR
  53.       D : dbfRecord; { declare your dBASE file variable }
  54.     BEGIN
  55.     D.FileName := 'MyFile.DBF'; { get filename of .dbf file into FileName field
  56.                                   of D variable ...  }
  57.     OpenDbf(D);        { to open the file }
  58.     IF NOT dbfOK THEN { check dbfError and process error };
  59.     {... the rest of your program including calls to
  60.      GetDbfRecord, PutDbfRecord, AppendDbf as needed,
  61.      always remembering to interrogate the two global status
  62.      variables after each procedure call   }
  63.     CloseDbf(D);      { to close the file  }
  64.     IF NOT dbfOK THEN { check dbfError and process error };
  65.     END.
  66.  
  67.   Upon exit from the GetDbfRecord Procedure, the CurRecord of the
  68.   dbfRecord variable points to the current record contents.  Each field
  69.   can be accessed using its offset into the CurRecord^ with the variable
  70.   Off in the Fields^ array.
  71.   Upon entry to the PutDbfRecord Procedure, the CurRecord^ should contain
  72.   the data that you want to write.
  73.   AppendDbf automatically adds a record to the end of the file (the
  74.   CurRecord^ should contain the data that you want to write).
  75.  
  76.   Notice that the OpenDbf routine does allocate a buffer on the heap for
  77.   the current record.  You can, of course, override this by pointing
  78.   CurRecord to any data structure that you wish; HOWEVER, since CloseDbf
  79.   deallocates the buffer, you must repoint CurRecord to its original buffer
  80.   before calling CloseDbf.
  81.  
  82.   See the demo program for some examples.
  83.   If you have any problems with these routines, please
  84.   let me know.  Suggestions for improvements gratefully accepted.
  85. *)
  86.  
  87. (*
  88. dBASE III Database File Structure
  89. The structure of a dBASE III database file is composed of a
  90. header and data records.  The layout is given below.
  91. dBASE III DATABASE FILE HEADER:
  92. +---------+-------------------+---------------------------------+
  93. |  BYTE   |     CONTENTS      |          MEANING                |
  94. +---------+-------------------+---------------------------------+
  95. |  0      |  1 byte           | dBASE III version number        |
  96. |         |                   |  (03H without a .DBT file)      |
  97. |         |                   |  (83H with a .DBT file)         |
  98. +---------+-------------------+---------------------------------+
  99. |  1-3    |  3 bytes          | date of last update             |
  100. |         |                   |  (YY MM DD) in binary format    |
  101. +---------+-------------------+---------------------------------+
  102. |  4-7    |  32 bit number    | number of records in data file  |
  103. +---------+-------------------+---------------------------------+
  104. |  8-9    |  16 bit number    | length of header structure      |
  105. +---------+-------------------+---------------------------------+
  106. |  10-11  |  16 bit number    | length of the record            |
  107. +---------+-------------------+---------------------------------+
  108. |  12-31  |  20 bytes         | reserved bytes (version 1.00)   |
  109. +---------+-------------------+---------------------------------+
  110. |  32-n   |  32 bytes each    | field descriptor array          |
  111. |         |                   |  (see below)                    | --+
  112. +---------+-------------------+---------------------------------+   |
  113. |  n+1    |  1 byte           | 0DH as the field terminator     |   |
  114. +---------+-------------------+---------------------------------+   |
  115. |
  116. |
  117. A FIELD DESCRIPTOR:      <------------------------------------------+
  118. +---------+-------------------+---------------------------------+
  119. |  BYTE   |     CONTENTS      |          MEANING                |
  120. +---------+-------------------+---------------------------------+
  121. |  0-10   |  11 bytes         | field name in ASCII zero-filled |
  122. +---------+-------------------+---------------------------------+
  123. |  11     |  1 byte           | field type in ASCII             |
  124. |         |                   |  (C N L D or M)                 |
  125. +---------+-------------------+---------------------------------+
  126. |  12-15  |  32 bit number    | field data address              |
  127. |         |                   |  (address is set in memory)     |
  128. +---------+-------------------+---------------------------------+
  129. |  16     |  1 byte           | field length in binary          |
  130. +---------+-------------------+---------------------------------+
  131. |  17     |  1 byte           | field decimal count in binary   |
  132. +---------+-------------------+--------------------------------
  133. |  18-31  |  14 bytes         | reserved bytes (version 1.00)   |
  134. +---------+-------------------+---------------------------------+
  135.  
  136. The data records are layed out as follows:
  137. 1. Data records are preceeded by one byte that is a
  138.    space (20H) if the record is not deleted and an
  139.    asterisk (2AH) if it is deleted.
  140. 2. Data fields are packed into records with no field
  141.    separators or record terminators.
  142. 3. Data types are stored in ASCII format as follows:
  143.  
  144. DATA TYPE      DATA RECORD STORAGE
  145. ---------      --------------------------------------------
  146. Character      (ASCII characters)
  147. Numeric        - . 0 1 2 3 4 5 6 7 8 9
  148. Logical        ? Y y N n T t F f  (? when not initialized)
  149. Memo           (10 digits representing a .DBT block number)
  150. Date           (8 digits in YYYYMMDD format, such as
  151. 19840704 for July 4, 1984)
  152.  
  153. This information came directly from the Ashton-Tate Forum.
  154. It can also be found in the Advanced Programmer's Guide available
  155. from Ashton-Tate.
  156.  
  157. One slight difference occurs between files created by dBASE III and those
  158. created by dBASE III Plus.  In the earlier files, there is an ASCII NUL
  159. character between the $0D end of header indicator and the start of the data.
  160. This NUL is no longer present in Plus, making a Plus header one byte smaller
  161. than an identically structured III file.
  162. *)
  163.  
  164. CONST
  165.   DB2File = 2;
  166.   DB3File = 3;
  167.   DB3WithMemo = $83;
  168.   ValidTypes : SET OF Char = ['C', 'N', 'L', 'M', 'D'];
  169.   MAX_HEADER = 4129;          { = maximum length of dBASE III header }
  170.   MAX_BYTES_IN_RECORD = 4000; { dBASE III record limit }
  171.   MAX_FIELDS_IN_RECORD = 128; { dBASE III field limit  }
  172.   BYTES_IN_MEMO_RECORD = 512; { dBASE III memo field record size }
  173.  
  174.   { Special Error codes for .DBF files }
  175.   NOT_DB_FILE = $80;  { first byte was not a $3 or $83 or a $2 (dBASE II)}
  176.   INVALID_FIELD = $81;{ invalid field type was found }
  177.   REC_TOO_HIGH = $82; { tried to read a record beyond the correct range }
  178.   PARTIAL_READ = $83; { only a partial record was read }
  179.  
  180.   (*
  181.   Although there are some declarations for memo files, the routines to access
  182.   them have not yet been implemented.
  183.   *)
  184.  
  185. TYPE
  186.   _HeaderType = ARRAY[0..MAX_HEADER] OF Byte;
  187.   _HeaderPrologType = ARRAY[0..31] OF Byte;
  188.   _FieldDescType = ARRAY[0..31] OF Byte;
  189.   _dRec = ^_DataRecord;
  190.   _DataRecord = ARRAY[0..MAX_BYTES_IN_RECORD] OF Byte;
  191.   _Str255 = STRING[255];
  192.   _Str80 = STRING[80];
  193.   _Str64 = STRING[64];
  194.   _Str10 = STRING[10];
  195.   _Str8 = STRING[8];
  196.   _Str2 = STRING[2];
  197.   _dbfFile = FILE;
  198.   _FieldRecord = RECORD
  199.                    Name : _Str10;
  200.                    Typ : Char;
  201.                    Len : Byte;
  202.                    Dec : Byte;
  203.                    Off : Integer;
  204.                  END;
  205.   _FieldArray = ARRAY[1..MAX_FIELDS_IN_RECORD] OF _FieldRecord;
  206.   _dFields = ^_FieldArray;
  207.   _MemoRecord = ARRAY[1..BYTES_IN_MEMO_RECORD] OF Byte;
  208.   _MemoFile = FILE OF _MemoRecord;
  209.   _StatusType = (NotOpen, NotUpdated, Updated);
  210.   dbfRecord = RECORD
  211.                 FileName : _Str64;
  212.                 dFile : _dbfFile;
  213.                 HeadProlog : _HeaderPrologType;
  214.                 dStatus : _StatusType;
  215.                 WithMemo : Boolean;
  216.                 DateOfUpdate : _Str8;
  217.                 NumRecs : longint;
  218.                 HeadLen : word;
  219.                 RecLen : word;
  220.                 NumFields : Integer;
  221.                 Fields : _dFields;
  222.                 CurRecord : _dRec;
  223.               END;
  224.  
  225. VAR
  226.   dbfError : Integer; { global error indicators }
  227.   dbfOK  : Boolean;
  228.  
  229. FUNCTION MakeReal(VAR b) : longint;
  230.   { takes a long 32-bit integer and converts it to a longint }
  231. VAR
  232.   r : longint ABSOLUTE b;
  233. BEGIN
  234.   MakeReal := r;
  235. END;
  236.  
  237. FUNCTION MakeUnsignedReal(VAR b) : word;
  238.   { takes an unsigned 16-bit integer and converts it to a longint }
  239. VAR
  240.   r : word ABSOLUTE b;
  241. BEGIN
  242.   MakeUnsignedReal := r;
  243. END;
  244.  
  245. FUNCTION MakeInt (VAR b) : Integer;
  246. VAR
  247.   i : Integer ABSOLUTE b;
  248. BEGIN
  249.   MakeInt := i;
  250. END;
  251.  
  252. function MakeLongInt (var b:real) : longint;
  253. begin
  254.   MakeLongInt := round(b)
  255. end;
  256.  
  257. FUNCTION MakeStr(b : Byte) : _Str2;
  258. VAR
  259.   i : Integer;
  260.   s : _Str2;
  261. BEGIN
  262.   i := b;
  263.   Str(i:2, s);
  264.   IF s[1] = ' ' THEN s[1] := '0';
  265.   MakeStr := s;
  266. END;
  267.  
  268. PROCEDURE GetDbfRecord(VAR D : dbfRecord; RecNum : longint);
  269. VAR
  270.   Result : Integer;
  271. BEGIN
  272.   IF RecNum > D.NumRecs THEN
  273.   BEGIN
  274.     dbfError := REC_TOO_HIGH;
  275.     dbfOK := FALSE;
  276.     Exit;
  277.   END;
  278.   {$I-}
  279.   seek(D.dFile, D.HeadLen+(RecNum-1)*D.RecLen);
  280.   {$I+}
  281.   dbfError := IOResult;
  282.   IF dbfError = 0 THEN
  283.   BEGIN
  284.     {$I-}
  285.     BlockRead(D.dFile, D.CurRecord^, D.RecLen, Result);
  286.     {$I+}
  287.     dbfError := IOResult;
  288.     IF (dbfError = 0) AND (Result < D.RecLen) THEN
  289.       dbfError := PARTIAL_READ;
  290.   END;
  291.   dbfOK := (dbfError = 0);
  292. END;
  293.  
  294. PROCEDURE PutDbfRecord(VAR D : dbfRecord; RecNum : longint);
  295. VAR
  296.   Result : Integer;
  297.  
  298. BEGIN
  299. IF RecNum > D.NumRecs THEN
  300. BEGIN
  301.   RecNum := D.NumRecs+1;
  302.   D.NumRecs := RecNum;
  303. END;
  304. {$I-}
  305. seek(D.dFile, D.HeadLen+(RecNum-1)*D.RecLen);
  306. {$I+}
  307. dbfError := IOResult;
  308. IF dbfError = 0 THEN
  309.   BEGIN
  310.   {$I-} BlockWrite(D.dFile, D.CurRecord^, D.RecLen, Result); {$I+}
  311.   dbfError := IOResult;
  312.   END;
  313. IF dbfError = 0 THEN D.dStatus := Updated;
  314. dbfOK := (dbfError = 0);
  315. END;                        {PutDbfRecord}
  316.  
  317. PROCEDURE AppendDbf(VAR D : dbfRecord);
  318. BEGIN
  319.   PutDbfRecord(D, D.NumRecs+1);
  320. END;
  321.  
  322. PROCEDURE CloseDbf(VAR D : dbfRecord);
  323.  
  324.   PROCEDURE UpdateHeader(VAR D : dbfRecord);
  325.   VAR
  326.     Reg : Registers;
  327.     r : real;
  328.   BEGIN                     { UpdateHeader }
  329.   r := D.NumRecs;
  330.   Reg.AX := $2A00;          { Get DOS Date }
  331.   Intr($21, Reg);
  332.   IF D.HeadProlog[0] = DB2File THEN
  333.     BEGIN
  334.     D.HeadProlog[5] := Reg.CX-1900; {Year}
  335.     D.HeadProlog[3] := Reg.DH; {Month}
  336.     D.HeadProlog[4] := Reg.DL; {Day}
  337.     D.HeadProlog[2] := Trunc(r/256.0);
  338.     r := r-(D.HeadProlog[5]*256);
  339.     D.HeadProlog[1] := Trunc(r);
  340.     END
  341.   ELSE
  342.     BEGIN
  343.     D.HeadProlog[1] := Reg.CX-1900; {Year}
  344.     D.HeadProlog[2] := Reg.DH; {Month}
  345.     D.HeadProlog[3] := Reg.DL; {Day}
  346.     D.HeadProlog[7] := Trunc(r/16777216.0);
  347.     r := r-(D.HeadProlog[7]*16777216.0);
  348.     D.HeadProlog[6] := Trunc(r/65536.0);
  349.     r := r-(D.HeadProlog[6]*65536.0);
  350.     D.HeadProlog[5] := Trunc(r/256);
  351.     r := r-(D.HeadProlog[5]*256);
  352.     D.HeadProlog[4] := Trunc(r);
  353.     END;
  354.   {$I-}seek(D.dFile, 0);{$I+}
  355.   dbfError := IOResult;
  356.   IF dbfError = 0 THEN
  357.     BEGIN
  358.     {$I-} BlockWrite(D.dFile, D.HeadProlog, 8); {$I+}
  359.     dbfError := IOResult;
  360.     END;
  361.   dbfOK := (dbfError = 0);
  362.   END;                      { UpdateHeader }
  363.  
  364. CONST
  365.   EofMark : Byte = $1A;
  366.  
  367. BEGIN                       { CloseDbf }
  368. dbfError := 0;
  369. IF D.dStatus = Updated THEN
  370.   BEGIN
  371.   UpdateHeader(D);
  372.   IF dbfError = 0 THEN
  373.     BEGIN
  374.  
  375.     {$I-} seek(D.dFile, D.HeadLen+D.NumRecs*D.RecLen); {$I+}
  376.     dbfError := IOResult;
  377.     END;
  378.   IF dbfError = 0 THEN
  379.     BEGIN
  380.     {$I-} BlockWrite(D.dFile, EofMark, 1); {$I+} {Put EOF marker }
  381.     dbfError := IOResult;
  382.     END;
  383.   END;   { IF Updated }
  384. IF dbfError = 0 THEN
  385.   BEGIN
  386.   {$I-} Close(D.dFile);     {$I+}
  387.   dbfError := IOResult;
  388.   END;
  389. IF dbfError = 0 THEN
  390.   BEGIN
  391.     D.dStatus := NotOpen;
  392.     FreeMem(D.CurRecord, D.RecLen);
  393.     FreeMem(D.Fields, D.NumFields*SizeOf(_FieldRecord));
  394.   END;
  395. dbfOK := (dbfError = 0);
  396. END;                        { CloseDbf }
  397.  
  398. PROCEDURE OpenDbf(VAR D : dbfRecord);
  399.  
  400.   PROCEDURE ProcessHeader(VAR Header : _HeaderType; NumBytes : Integer);
  401.  
  402.     PROCEDURE GetOneFieldDesc(VAR F; VAR Field : _FieldRecord;
  403.                               VAR Offset : Integer);
  404.  
  405.     VAR
  406.       i : Integer;
  407.       FD : _FieldDescType ABSOLUTE F;
  408.  
  409.     BEGIN                   { GetOneFieldDesc }
  410.     i := 0;
  411.     Field.Name := '';
  412.     REPEAT
  413.       Field.Name[Succ(i)] := Chr(FD[i]);
  414.       i := Succ(i);
  415.     UNTIL FD[i] = 0;
  416.     Field.Name[0] := Chr(i);
  417.     Field.Typ := Char(FD[11]);
  418.     IF D.HeadProlog[0] = DB2File THEN
  419.       BEGIN
  420.       Field.Len := FD[12];
  421.       Field.Dec := FD[15];
  422.       END
  423.     ELSE
  424.       BEGIN
  425.       Field.Len := FD[16];
  426.       Field.Dec := FD[17];
  427.       END;
  428.     Field.Off := Offset;
  429.     Offset := Offset+Field.Len;
  430.     IF NOT(Field.Typ IN ValidTypes) THEN
  431.       dbfError := INVALID_FIELD;
  432.     END;                    { GetOneFieldDesc }
  433.  
  434.     PROCEDURE ProcessDB2Header;
  435.  
  436.     VAR
  437.       o, i, tFieldsLen : Integer;
  438.       tempFields : _FieldArray;
  439.  
  440.     BEGIN   { ProcessDB2Header }
  441.     D.DateOfUpdate := MakeStr(Header[3])+'/'+MakeStr(Header[4])+'/'+MakeStr(Header[5]);
  442.     D.NumRecs := MakeUnsignedReal(Header[1]);
  443.     D.HeadLen := 521;
  444.     IF NumBytes < D.HeadLen THEN
  445.       BEGIN
  446.       dbfError := NOT_DB_FILE;
  447.       Close(D.dFile);
  448.       Exit;
  449.       END;
  450.     D.RecLen := MakeInt(Header[6]); { Includes the Deleted Record Flag }
  451.     GetMem(D.CurRecord, D.RecLen); { Allocate some memory for a buffer  }
  452.     D.dStatus := NotUpdated;
  453.     D.NumFields := 0;
  454.     Move(Header, D.HeadProlog, SizeOf(D.HeadProlog));
  455.     o := 1;                   {Offset within dbf record of current field }
  456.     i := 8;                   {Index for Header }
  457.     WHILE Header[i] <> $0D DO
  458.       BEGIN
  459.       D.NumFields := Succ(D.NumFields);
  460.       GetOneFieldDesc(Header[i], tempFields[D.NumFields], o);
  461.       IF dbfError <> 0 THEN
  462.         BEGIN
  463.         Close(D.dFile);
  464.         Exit;
  465.         END;
  466.       i := i+16;
  467.       END;                    { While Header[i] <> $0D }
  468.     tFieldsLen := D.NumFields*SizeOf(_FieldRecord);
  469.     GetMem(D.Fields, tFieldsLen);
  470.     Move(tempFields, D.Fields^, tFieldsLen);
  471.     D.WithMemo := FALSE;
  472.     END;                      {ProcessDB2Header}
  473.  
  474.   VAR
  475.     o, i : Integer;
  476.     tempFields : _FieldArray;
  477.  
  478.   BEGIN                     {ProcessHeader}
  479.   CASE Header[0] OF
  480.     DB2File : BEGIN
  481.                 ProcessDB2Header;
  482.                 Exit;
  483.               END;
  484.     DB3File : D.WithMemo := False;
  485.     DB3WithMemo : D.WithMemo := True;
  486.     ELSE
  487.       BEGIN
  488.       dbfError := NOT_DB_FILE;
  489.       Close(D.dFile);
  490.       Exit;
  491.       END;
  492.     END;                      {CASE}
  493.   D.DateOfUpdate := MakeStr(Header[2])+'/'+MakeStr(Header[3])+'/'+MakeStr(Header[1]);
  494.   D.NumRecs := MakeReal(Header[4]);
  495.   D.HeadLen := MakeInt(Header[8]);
  496.   IF NumBytes < D.HeadLen THEN
  497.     BEGIN
  498.     dbfError := NOT_DB_FILE;
  499.     Close(D.dFile);
  500.     Exit;
  501.     END;
  502.   D.RecLen := MakeInt(Header[10]); { Includes the Deleted Record Flag }
  503.   GetMem(D.CurRecord, D.RecLen); { Allocate some memory for a buffer  }
  504.   D.dStatus := NotUpdated;
  505.   D.NumFields := 0;
  506.   Move(Header, D.HeadProlog, SizeOf(D.HeadProlog));
  507.   o := 1;                   {Offset within dbf record of current field }
  508.   i := 32;                  {Index for Header }
  509.   WHILE Header[i] <> $0D DO
  510.     BEGIN
  511.     D.NumFields := Succ(D.NumFields);
  512.     GetOneFieldDesc(Header[i], tempFields[D.NumFields], o);
  513.     IF dbfError <> 0 THEN
  514.       BEGIN
  515.       Close(D.dFile);
  516.       Exit;
  517.       END;
  518.     i := i+32;
  519.     END;                    { While Header[i] <> $0D }
  520.   i := D.NumFields*SizeOf(_FieldRecord);
  521.   GetMem(D.Fields,i) ;
  522.   Move(tempFields, D.Fields^, i);
  523.   END;                      {ProcessHeader}
  524.  
  525.   PROCEDURE GetHeader;
  526.  
  527.   VAR
  528.     Result : Integer;
  529.     H : _HeaderType;
  530.  
  531.   BEGIN                     { GetHeader }
  532.   {$I-} BlockRead(D.dFile, H, MAX_HEADER, Result); {$I+}
  533.   dbfError := IOResult;
  534.   IF dbfError = 0 THEN ProcessHeader(H, Result);
  535.   END;                      { GetHeader }
  536.  
  537. BEGIN                       { OpenDbf }
  538.   Assign(D.dFile, D.FileName);
  539.   {$I-} Reset(D.dFile, 1); {$I+} {the '1' parameter sets the record size}
  540.   dbfError := IOResult;
  541.   IF dbfError = 0 THEN GetHeader;
  542.   dbfOK := (dbfError = 0);
  543. END;                        { OpenDbf }
  544.  
  545. PROCEDURE CreateDbf(VAR D : dbfRecord; fn : _Str64; n : Integer;
  546.                     flds : _dFields);
  547.   {
  548.   Call this procedure with the full pathname of the file that you want
  549.   to create (fn), the number of fields in a record (n), and a pointer
  550.   to an array of _FieldRecord (flds).  The procedure will initialize all
  551.   the data structures in the dbfRecord (D).
  552.   }
  553.  
  554. VAR
  555.   tHeader : _HeaderType;
  556.  
  557.   PROCEDURE MakeFieldDescs;
  558.  
  559.     PROCEDURE MakeOneFieldDesc(VAR F; VAR Field : _FieldRecord);
  560.     VAR
  561.       FD : _FieldDescType ABSOLUTE F;
  562.     BEGIN                   { MakeOneFieldDesc }
  563.       Move(Field.Name[1],FD,Ord(Field.Name[0]));
  564.       FD[11] := Ord(Field.Typ);
  565.       FD[16] := Field.Len;
  566.       IF Field.Typ <> 'N' THEN Field.Dec := 0;
  567.       FD[17] := Field.Dec;
  568.       Field.Off := D.RecLen;
  569.       D.RecLen := D.RecLen+Field.Len;
  570.       IF NOT(Field.Typ IN ValidTypes) THEN dbfError := INVALID_FIELD;
  571.       IF Field.Typ = 'M' THEN D.WithMemo := TRUE;
  572.     END;                    { MakeOneFieldDesc }
  573.  
  574.   VAR
  575.     i : Integer;
  576.   BEGIN                     {MakeFieldDescs}
  577.     D.RecLen := 1;
  578.     FOR i := 1 TO D.NumFields DO
  579.     BEGIN
  580.       MakeOneFieldDesc(tHeader[i*32],flds^[i]);
  581.       IF dbfError <> 0 THEN Exit;
  582.     END;
  583.   END;                      {MakeFieldDescs}
  584.  
  585.   PROCEDURE MakeHeader;
  586.   VAR
  587.     Result : Integer;
  588.   BEGIN                     { MakeHeader }
  589.     FillChar(tHeader,SizeOf(tHeader),#0);
  590.     D.WithMemo := FALSE;
  591.     D.HeadLen := Succ(D.NumFields) * 32;
  592.     tHeader[D.HeadLen] := $0D;
  593.     D.HeadLen := Succ(D.HeadLen);
  594.     tHeader[8] := Lo(D.HeadLen);
  595.     tHeader[9] := Hi(D.HeadLen);
  596.     MakeFieldDescs;
  597.     IF D.WithMemo THEN
  598.       tHeader[0] := DB3WithMemo
  599.     ELSE
  600.       tHeader[0] := DB3File;
  601.     tHeader[10] := Lo(D.RecLen);
  602.     tHeader[11] := Hi(D.RecLen);
  603.   END;                      { MakeHeader }
  604.  
  605. VAR
  606.   i : Integer;
  607. BEGIN            { CreateDbf }
  608.   D.NumFields := n;
  609.   MakeHeader;
  610.   D.FileName := fn;
  611.   Assign(D.dFile, D.FileName);
  612.   {$I-} Rewrite(D.dFile, 1); {$I+} {Will overwrite if file exists!}
  613.   dbfError := IOResult;
  614.   IF dbfError = 0 THEN
  615.     BEGIN
  616.     {$I-} BlockWrite(D.dFile,tHeader,Succ(D.HeadLen));{$I+}
  617.     dbfError := IOResult;
  618.     END;
  619.   IF dbfError = 0 THEN
  620.     BEGIN
  621.     D.dStatus := Updated;
  622.     D.NumRecs := 0;
  623.     Move(tHeader,D.HeadProlog,SizeOf(D.HeadProlog));
  624.     D.DateOfUpdate := '  /  /  ';
  625.     GetMem(D.CurRecord, D.RecLen); { Allocate some memory for a buffer  }
  626.     FillChar(D.CurRecord^,D.RecLen,' ');
  627.     i := D.NumFields*SizeOf(_FieldRecord);
  628.     GetMem(D.Fields,i);
  629.     Move(flds, D.Fields^,i);
  630.     END;
  631.   dbfOK := (dbfError = 0);
  632. END;                        { CreateDbf }
  633.  
  634. (* To enable the Demo program, delete the next line. *)
  635.  
  636.  
  637. (*
  638. PROCEDURE ErrorHalt(errorCode : Integer);
  639.   { a VERY crude error handler }
  640. VAR
  641.   errorMsg : _Str80;
  642. BEGIN
  643.   CASE errorCode OF
  644.      00 : Exit;                { no error occurred }
  645.     $01 : errorMsg := 'Not found';
  646.     $02 : errorMsg := 'Not open for input';
  647.     $03 : errorMsg := 'Not open for output';
  648.     $04 : errorMsg := 'Just not open';
  649.     $91 : errorMsg := 'Seek beyond EOF';
  650.     $99 : errorMsg := 'Unexpected EOF';
  651.     $F0 : errorMsg := 'Disk write error';
  652.     $F1 : errorMsg := 'Directory full';
  653.     $F3 : errorMsg := 'Too many files';
  654.     $FF : errorMsg := 'Where did that file go?';
  655.     NOT_DB_FILE : errorMsg := 'Not a dBASE data file';
  656.     INVALID_FIELD : errorMsg := 'Invalid field type encountered';
  657.     REC_TOO_HIGH  : errorMsg := 'Requested record beyond range';
  658.     PARTIAL_READ  : errorMsg := 'Tried to read beyon EOF';
  659.     ELSE
  660.      errorMsg := 'Undefined error';
  661.   END;
  662.   WriteLn;
  663.   WriteLn(errorCode:3, ': ',errorMsg);
  664.   Halt(1);
  665. END;
  666.  
  667. TYPE
  668.   PseudoStr = ARRAY[1..255] OF Char;
  669. VAR
  670.   Demo : dbfRecord;
  671.   j, i : Integer;
  672.   blanks : _Str255;
  673.   SizeOfFile, r : longint;
  674.   fn : _Str64;
  675.  
  676.   PROCEDURE Wait;
  677.   VAR
  678.     c : Char;
  679.   BEGIN
  680.     Write('Press any key to continue . . .');
  681.     Read(Kbd, c);
  682.   END;
  683.  
  684.  
  685.   PROCEDURE List(VAR D : dbfRecord);
  686.  
  687.     PROCEDURE ShowField(VAR a; VAR F : _FieldRecord);
  688.  
  689.     VAR
  690.       Data : PseudoStr ABSOLUTE a;
  691.  
  692.     BEGIN
  693.       WITH F DO
  694.       BEGIN
  695.         CASE Typ OF
  696.           'C', 'N', 'L' : Write(Copy(Data, 1, Len));
  697.           'M' : Write('Memo      ');
  698.           'D' : Write(Copy(Data, 5, 2), '/',
  699.                 Copy(Data, 7, 2), '/',
  700.                 Copy(Data, 1, 2));
  701.         END;                    {CASE}
  702.         IF Len <= Length(Name) THEN
  703.           Write(Copy(blanks, 1, Length(Name)-Pred(Len)))
  704.         ELSE
  705.           Write(' ');
  706.         END;                    {WITH F}
  707.       END;                      {ShowField}
  708.  
  709.       BEGIN                       {List}
  710.       WriteLn;
  711.       Write('Rec Num  ');
  712.       WITH D DO
  713.         BEGIN
  714.           FOR i := 1 TO NumFields DO
  715.             WITH Fields^[i] DO
  716.               IF Len >= Length(Name) THEN
  717.                 Write(Name, Copy(blanks, 1, Succ(Len-Length(Name))))
  718.               ELSE
  719.                 Write(Name, ' ');
  720.           WriteLn;
  721.           r := 1;
  722.           WHILE r <= NumRecs DO
  723.             BEGIN
  724.               GetDbfRecord(Demo, r);
  725.               IF NOT dbfOK THEN ErrorHalt(dbfError);
  726.               WriteLn;
  727.               Write(r:7:0, ' ');
  728.               Write(Chr(CurRecord^[0])); { the 'deleted' indicator }
  729.               FOR i := 1 TO NumFields DO
  730.                 ShowField(CurRecord^[Fields^[i].Off], Fields^[i]);
  731.               r := r+1;
  732.             END;                    {WHILE r }
  733.         END;                      {WITH D }
  734.     END;                        {List}
  735.  
  736.   PROCEDURE DisplayStructure(VAR D : dbfRecord);
  737.  
  738.   VAR
  739.     i : Integer;
  740.  
  741.   BEGIN
  742.   WITH D DO
  743.     BEGIN
  744.     ClrScr;
  745.     Write(' #  Field Name   Type  Length  Decimal');
  746.     FOR i := 1 TO NumFields DO
  747.       BEGIN
  748.       WITH Fields^[i] DO
  749.         BEGIN
  750.         IF i MOD 20 = 0 THEN
  751.           BEGIN
  752.           WriteLn;
  753.           Wait;
  754.           ClrScr;
  755.           Write(' #  Field Name   Type  Length  Decimal');
  756.           END;
  757.         GoToXY(1, Succ(WhereY));
  758.         Write(i:2, Name:12, Typ:5, Len:9);
  759.         IF Typ = 'N' THEN Write(Dec:5);
  760.         END;                  {WITH Fields^}
  761.       END;                    {FOR}
  762.     WriteLn;
  763.     Wait;
  764.     END;                      {WITH D}
  765.   END;                        { DisplayStructure }
  766.  
  767.   PROCEDURE CopyDbf(fnDB2,fnDB3 : _Str64);
  768.     {
  769.     Copies a .DBF file to another file.  The SOURCE file may be a
  770.     II, III, or III Plus file.  The DESTINATION file will be a III Plus
  771.     file (although III will be able to use it with no problems).
  772.     }
  773.  
  774.   VAR
  775.     dOrg,dDest : dbfRecord;
  776.     recCount : longint;
  777.     x,y : Integer;
  778.     dummyPtr : _dRec;
  779.  
  780.   BEGIN             { CopyDbf }
  781.   dOrg.FileName := fnDB2;
  782.   OpenDbf(dOrg);
  783.   IF NOT dbfOK THEN ErrorHalt(dbfError);
  784.   CreateDbf(dDest,fnDB3,dOrg.NumFields,dOrg.Fields);
  785.   IF NOT dbfOK THEN ErrorHalt(dbfError);
  786.   dummyPtr := dDest.CurRecord;
  787.   dDest.CurRecord := dOrg.CurRecord;     { a dirty trick! }
  788.   recCount := 0;
  789.   WriteLn;
  790.   x := WhereX; y := WhereY;
  791.   Write(recCount:8:0,' Records Converted.');
  792.   WHILE recCount < dOrg.NumRecs DO
  793.     BEGIN
  794.     recCount := recCount + 1;
  795.     GetDbfRecord(dOrg,recCount);
  796.     IF NOT dbfOK THEN ErrorHalt(dbfError);
  797.     AppendDbf(dDest);     { go right into the append because both CurRecords }
  798.                           {  point to the same place }
  799.     IF NOT dbfOK THEN ErrorHalt(dbfError);
  800.     GotoXY(x,y);
  801.     Write(recCount:8:0);
  802.     END;
  803.   WriteLn;
  804.   CloseDbf(dOrg);
  805.   IF NOT dbfOK THEN ErrorHalt(dbfError);
  806.   dDest.CurRecord := dummyPtr;      { It is important to undo the dirty work! }
  807.   CloseDbf(dDest);
  808.   IF NOT dbfOK THEN ErrorHalt(dbfError);
  809.   END;              { CopyDbf }
  810.  
  811. VAR
  812.   fn1,fn2,p : _Str64;
  813.  
  814. BEGIN                         {Demonstration of DBF routines}
  815. WITH Demo DO
  816.   BEGIN
  817.   FillChar(blanks, SizeOf(blanks), $20);
  818.   blanks[0] := Chr(255);
  819.   ClrScr;
  820.   GoToXY(10, 10);
  821.   Write('Name of dBASE file (.DBF assumed): ');
  822.   Read(FileName);
  823.   IF Pos('.', FileName) = 0 THEN FileName := FileName+'.DBF';
  824.   OpenDbf(Demo);
  825.   IF NOT dbfOK THEN ErrorHalt(dbfError);
  826.   ClrScr;
  827.   SizeOfFile := LongFileSize(dFile);
  828.   WriteLn('File Name: ', FileName);
  829.   WriteLn('Date Of Last Update: ', DateOfUpdate);
  830.   WriteLn('Number of Records: ', NumRecs:10:0);
  831.   WriteLn('Size of File: ', SizeOfFile:15:0);
  832.   WriteLn('Length of Header: ', HeadLen:11);
  833.   WriteLn('Length of One Record: ', RecLen:7);
  834.   IF WithMemo THEN WriteLn('This file contains Memo fields.');
  835.   IF HeadProlog[0] = DB2File THEN WriteLn('dBASE 2.4 file');
  836.   Wait;
  837.   ClrScr;
  838.   DisplayStructure(Demo);
  839.   ClrScr;
  840.   List(Demo);
  841.   WriteLn;
  842.   Wait;
  843.   CloseDbf(Demo);
  844.   IF NOT dbfOK THEN ErrorHalt(dbfError);
  845.   END;                        {WITH}
  846. ClrScr;
  847. WriteLn('Enter the name of a dBASE file (II, III, or III +) to copy.');
  848. Write('Enter a blank name to exit: ');
  849. ReadLn(fn1);
  850. IF fn1 = '' THEN Halt;
  851. IF Pos('.', fn1) = 0 THEN fn1 := fn1+'.DBF';
  852. Write('Enter destination file name: ');
  853. ReadLn(fn2);
  854. IF Pos('.', fn2) = 0 THEN fn2 := fn2+'.DBF';
  855. CopyDbf(fn1,fn2);
  856. END.                          {of Demo program }
  857.  *)
  858.