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

  1. (******************************************************************************
  2.  dBASE DBF file access via Turbo Pascal
  3.  
  4. -------------------------------------------------------------------------------
  5.  Updated to Turbo Pascal 4.0/5.0 by Winthrop Chan, January 1989
  6.                                     producer@cscwam.umd.edu
  7.  
  8.  comments : I originally downloaded this program from Turbo City (209-599-7435)
  9.             but found it was severely outdated and was written for Turbo
  10.             Pascal 3.0. Since I lack the dBase technical reference manual, I
  11.             was not able to expand on the functions that are currently
  12.             available. I made the code more readable (which it wasn't when I
  13.             got hold of it) and updated some of the functions to take advantage
  14.             of Turbo 4.0/5.0's new features. If I get some time someday, I
  15.             would like to add index file access for both the dBase and Foxbase
  16.             index format. As usual, please backup your data before using this
  17.             unit since I may have not found all the bugs.
  18. -------------------------------------------------------------------------------
  19.  
  20. DBF.PAS version 1.3
  21.  
  22. Copyright (C) 1986 By James Troutman
  23. CompuServe PPN 74746,1567
  24. Permission is granted to use these routines for non-commercial purposes.
  25. For commercial use, please request permission via EasyPlex.
  26.  
  27. Revision history
  28.  1.1  - 5/6/86 - update header when modifying the .DBF file; write the
  29.  End Of File marker; simplify use.
  30.  
  31.  1.2  - 5/27/86 - removed (some of) the absurdities from the code;
  32.  allocate the current record buffer on the heap rather than in the data
  33.  segment; symbol names changed to avoid conflicts; some error checking
  34.  added.
  35.  
  36.  1.3  - 6/5/86 - added support for dBASE II files; new procedure CreateDbf.
  37.  
  38.                   !!!!ATTENTION!!!!
  39. If you have downloaded an earlier version of this file, please note that
  40. several of the TYPEs and VARs have been changed.  You may have to make
  41. some adjustments to any existing programs you have that use these routines.
  42.  
  43. The routines in this file present some tools for accessing dBASE II, III, and
  44. III Plus files from within a Turbo Pascal program.  There is MUCH
  45. room for improvement: the error checking is simplistic, there is no support
  46. for memo files, no buffering of data, no support for index files,
  47. etc. The main routines are:
  48.  
  49.        PROCEDURE OpenDbf(VAR D : dbfRecord;) : Integer;
  50.        PROCEDURE CloseDbf(VAR D : dbfRecord) : Integer;
  51.        PROCEDURE GetDbfRecord(VAR D : dbfRecord; RecNum : Real);
  52.        PROCEDURE PutDbfRecord(VAR D : dbfRecord; RecNum : Real);
  53.        PROCEDURE AppendDbf(VAR D : dbfRecord);
  54.        PROCEDURE CreateDbf(VAR D : dbfRecord; fn : _Str64; n : Integer;
  55.                            flds : _dFields);
  56.  
  57. After calling one of the procedures, check the status of the Boolean variable
  58. dbfOK to determine the success or failure of the operation.  If it failed,
  59. dbfError will contain a value corresponding to the IOResult value or
  60. to a specially assigned value for several special conditions.  Notice in
  61. particular that an unsuccessful call to CloseDbf will leave the file status
  62. unchanged and the memory still allocated.  It is your program's
  63. responsibility to take appropriate action.
  64.  
  65. A skeletal program might go something like:
  66.   {$I Dbf.PAS}
  67.   VAR
  68.     D : dbfRecord; { declare your dBASE file variable }
  69.   BEGIN
  70.   D.FileName := 'MyFile.DBF'; { get filename of .dbf file into FileName field
  71.                                 of D variable ...  }
  72.   OpenDbf(D);        { to open the file }
  73.   IF NOT dbfOK THEN { check dbfError and process error };
  74.   {... the rest of your program including calls to
  75.    GetDbfRecord, PutDbfRecord, AppendDbf as needed,
  76.    always remembering to interrogate the two global status
  77.    variables after each procedure call   }
  78.   CloseDbf(D);      { to close the file  }
  79.   IF NOT dbfOK THEN { check dbfError and process error };
  80.   END.
  81.  
  82. Upon exit from the GetDbfRecord Procedure, the CurRecord of the
  83. dbfRecord variable points to the current record contents.  Each field
  84. can be accessed using its offset into the CurRecord^ with the variable
  85. Off in the Fields^ array.
  86. Upon entry to the PutDbfRecord Procedure, the CurRecord^ should contain
  87. the data that you want to write.
  88. AppendDbf automatically adds a record to the end of the file (the
  89. CurRecord^ should contain the data that you want to write).
  90.  
  91. Notice that the OpenDbf routine does allocate a buffer on the heap for
  92. the current record.  You can, of course, override this by pointing
  93. CurRecord to any data structure that you wish; HOWEVER, since CloseDbf
  94. deallocates the buffer, you must repoint CurRecord to its original buffer
  95. before calling CloseDbf.
  96.  
  97. See the demo program for some examples.
  98. If you have any problems with these routines, please
  99. let me know.  Suggestions for improvements gratefully accepted.
  100.  
  101.  
  102.  
  103. dBASE III Database File Structure
  104. The structure of a dBASE III database file is composed of a
  105. header and data records.  The layout is given below.
  106. dBASE III DATABASE FILE HEADER:
  107. ┌─────────┬───────────────────┬─────────────────────────────────┐
  108. │  BYTE   │     CONTENTS      │          MEANING                │
  109. ├─────────┼───────────────────┼─────────────────────────────────┤
  110. │  0      │  1 byte           │ dBASE III version number        │
  111. │         │                   │  (03H without a .DBT file)      │
  112. │         │                   │  (83H with a .DBT file)         │
  113. ├─────────┼───────────────────┼─────────────────────────────────┤
  114. │  1-3    │  3 bytes          │ date of last update             │
  115. │         │                   │  (YY MM DD) in binary format    │
  116. ├─────────┼───────────────────┼─────────────────────────────────┤
  117. │  4-7    │  32 bit number    │ number of records in data file  │
  118. ├─────────┼───────────────────┼─────────────────────────────────┤
  119. │  8-9    │  16 bit number    │ length of header structure      │
  120. ├─────────┼───────────────────┼─────────────────────────────────┤
  121. │  10-11  │  16 bit number    │ length of the record            │
  122. ├─────────┼───────────────────┼─────────────────────────────────┤
  123. │  12-31  │  20 bytes         │ reserved bytes (version 1.00)   │
  124. ├─────────┼───────────────────┼─────────────────────────────────┤
  125. │  32-n   │  32 bytes each    │ field descriptor array          │
  126. │         │                   │  (see below)                    ├───┐
  127. ├─────────┼───────────────────┼─────────────────────────────────┤   │
  128. │  n+1    │  1 byte           │ 0DH as the field terminator     │   │
  129. └─────────┴───────────────────┴─────────────────────────────────┘   │
  130.                                                                     │
  131.                                                                     │
  132. A FIELD DESCRIPTOR:      <──────────────────────────────────────────┘
  133. ┌─────────┬───────────────────┬─────────────────────────────────┐
  134. │  BYTE   │     CONTENTS      │          MEANING                │
  135. ├─────────┼───────────────────┼─────────────────────────────────┤
  136. │  0-10   │  11 bytes         │ field name in ASCII zero-filled │
  137. ├─────────┼───────────────────┼─────────────────────────────────┤
  138. │  11     │  1 byte           │ field type in ASCII             │
  139. │         │                   │  (C N L D or M)                 │
  140. ├─────────┼───────────────────┼─────────────────────────────────┤
  141. │  12-15  │  32 bit number    │ field data address              │
  142. │         │                   │  (address is set in memory)     │
  143. ├─────────┼───────────────────┼─────────────────────────────────┤
  144. │  16     │  1 byte           │ field length in binary          │
  145. ├─────────┼───────────────────┼─────────────────────────────────┤
  146. │  17     │  1 byte           │ field decimal count in binary   │
  147. ├─────────┼───────────────────┼─────────────────────────────────┤
  148. │  18-31  │  14 bytes         │ reserved bytes (version 1.00)   │
  149. └─────────┴───────────────────┴─────────────────────────────────┘
  150.  
  151. The data records are layed out as follows:
  152. 1. Data records are preceeded by one byte that is a
  153.    space (20H) if the record is not deleted and an
  154.    asterisk (2AH) if it is deleted.
  155. 2. Data fields are packed into records with no field
  156.    separators or record terminators.
  157. 3. Data types are stored in ASCII format as follows:
  158.  
  159. DATA TYPE      DATA RECORD STORAGE
  160. ---------      --------------------------------------------
  161. Character      (ASCII characters)
  162. Numeric        - . 0 1 2 3 4 5 6 7 8 9
  163. Logical        ? Y y N n T t F f  (? when not initialized)
  164. Memo           (10 digits representing a .DBT block number)
  165. Date           (8 digits in YYYYMMDD format, such as
  166. 19840704 for July 4, 1984)
  167.  
  168. This information came directly from the Ashton-Tate Forum.
  169. It can also be found in the Advanced Programmer's Guide available
  170. from Ashton-Tate.
  171.  
  172. One slight difference occurs between files created by dBASE III and those
  173. created by dBASE III Plus.  In the earlier files, there is an ASCII NUL
  174. character between the $0D end of header indicator and the start of the data.
  175. This NUL is no longer present in Plus, making a Plus header one byte smaller
  176. than an identically structured III file.
  177.  
  178. ******************************************************************************)
  179.  
  180. unit dbf;
  181.  
  182. {$M 16384,0,4096}
  183. {$I-}
  184. interface
  185. uses dos;
  186.  
  187. CONST
  188.   DB2File = 2;
  189.   DB3File = 3;
  190.   DB3WithMemo = $83;
  191.   ValidTypes : SET OF Char = ['C', 'N', 'L', 'M', 'D'];
  192.   MAX_HEADER = 4129;          { = maximum length of dBASE III header }
  193.   MAX_BYTES_IN_RECORD = 4000; { dBASE III record limit }
  194.   MAX_FIELDS_IN_RECORD = 128; { dBASE III field limit  }
  195.   BYTES_IN_MEMO_RECORD = 512; { dBASE III memo field record size }
  196.  
  197.   { Special Error codes for .DBF files }
  198.   NOT_DB_FILE   = $80; { first byte was not a $3 or $83 or a $2 (dBASE II)}
  199.   INVALID_FIELD = $81; { invalid field type was found }
  200.   REC_TOO_HIGH  = $82; { tried to read a record beyond the correct range }
  201.   PARTIAL_READ  = $83; { only a partial record was read }
  202.  
  203.   (*
  204.   Although there are some declarations for memo files, the routines to access
  205.   them have not yet been implemented.
  206.   *)
  207.  
  208. TYPE
  209.   _HeaderType       = ARRAY[0..MAX_HEADER] OF Byte;
  210.   _HeaderPrologType = ARRAY[0..31] OF Byte;
  211.   _FieldDescType    = ARRAY[0..31] OF Byte;
  212.   _dRec             = ^_DataRecord;
  213.   _DataRecord       = ARRAY[0..MAX_BYTES_IN_RECORD] OF Byte;
  214.   _Str255           = STRING[255];
  215.   _Str80            = STRING[80];
  216.   _Str64            = STRING[64];
  217.   _Str10            = STRING[10];
  218.   _Str8             = STRING[8];
  219.   _Str2             = STRING[2];
  220.   _dbfFile          = FILE;
  221.   _FieldRecord      = RECORD
  222.                         Name : _Str10;
  223.                         Typ  : Char;
  224.                         Len  : Byte;
  225.                         Dec  : Byte;
  226.                         Off  : Integer;
  227.                       END;
  228.   _FieldArray = ARRAY[1..MAX_FIELDS_IN_RECORD] OF _FieldRecord;
  229.   _dFields    = ^_FieldArray;
  230.   _MemoRecord = ARRAY[1..BYTES_IN_MEMO_RECORD] OF Byte;
  231.   _MemoFile   = FILE OF _MemoRecord;
  232.   _StatusType = (NotOpen, NotUpdated, Updated);
  233.   dbfRecord   = RECORD
  234.                   FileName     : _Str64;
  235.                   dFile        : _dbfFile;
  236.                   HeadProlog   : _HeaderPrologType;
  237.                   dStatus      : _StatusType;
  238.                   WithMemo     : Boolean;
  239.                   DateOfUpdate : _Str8;
  240.                   NumRecs      : longint;
  241.                   HeadLen      : word;
  242.                   RecLen       : word;
  243.                   NumFields    : Integer;
  244.                   Fields       : _dFields;
  245.                   CurRecord    : _dRec;
  246.                 END;
  247.  
  248. VAR
  249.   dbfError : Integer;
  250.   dbfOK    : Boolean;
  251.  
  252. PROCEDURE GetDbfRecord(VAR D : dbfRecord; RecNum : longint);
  253. PROCEDURE PutDbfRecord(VAR D : dbfRecord; RecNum : longint);
  254. PROCEDURE AppendDbf(VAR D : dbfRecord);
  255. PROCEDURE CloseDbf(VAR D : dbfRecord);
  256. PROCEDURE OpenDbf(VAR D : dbfRecord);
  257. PROCEDURE CreateDbf(VAR D : dbfRecord; fn : _Str64; n : Integer;
  258.                      flds : _dFields);
  259.  
  260. implementation
  261.  
  262.  
  263.  
  264. FUNCTION MakeLongInt(VAR b) : longint;
  265. VAR
  266.   r : longint ABSOLUTE b;
  267. BEGIN
  268.   MakeLongInt := r;
  269. END;
  270.  
  271. FUNCTION MakeWord(VAR b) : word;
  272. VAR
  273.   r : word ABSOLUTE b;
  274. BEGIN
  275.   MakeWord := r;
  276. END;
  277.  
  278. FUNCTION MakeInt (VAR b) : Integer;
  279. VAR
  280.   i : Integer ABSOLUTE b;
  281. BEGIN
  282.   MakeInt := i;
  283. END;
  284.  
  285. FUNCTION MakeStr(b : Byte) : _Str2;
  286. VAR
  287.   i : Integer;
  288.   s : _Str2;
  289. BEGIN
  290.   i := b;
  291.   Str(i:2, s);
  292.   IF s[1] = ' ' THEN s[1] := '0';
  293.   MakeStr := s;
  294. END;
  295.  
  296. PROCEDURE GetDbfRecord(VAR D : dbfRecord; RecNum : longint);
  297. VAR
  298.   Result : Integer;
  299. BEGIN
  300.   IF RecNum > D.NumRecs THEN
  301.   BEGIN
  302.     dbfError := REC_TOO_HIGH;
  303.     dbfOK := FALSE;
  304.     Exit;
  305.   END;
  306.   seek(D.dFile, D.HeadLen+(RecNum-1)*D.RecLen);
  307.   dbfError := IOResult;
  308.   IF dbfError = 0 THEN
  309.   BEGIN
  310.     BlockRead(D.dFile, D.CurRecord^, D.RecLen, Result);
  311.     dbfError := IOResult;
  312.     IF (dbfError = 0) AND (Result < D.RecLen) THEN dbfError := PARTIAL_READ;
  313.   END;
  314.   dbfOK := (dbfError = 0);
  315. END;
  316.  
  317. PROCEDURE PutDbfRecord(VAR D : dbfRecord; RecNum : longint);
  318. VAR
  319.   Result : Integer;
  320. BEGIN
  321.   IF RecNum > D.NumRecs THEN
  322.   BEGIN
  323.     RecNum := D.NumRecs+1;
  324.     D.NumRecs := RecNum;
  325.   END;
  326.   seek(D.dFile, D.HeadLen+(RecNum-1)*D.RecLen);
  327.   dbfError := IOResult;
  328.   IF dbfError = 0 THEN
  329.   BEGIN
  330.     BlockWrite(D.dFile, D.CurRecord^, D.RecLen, Result);
  331.     dbfError := IOResult;
  332.   END;
  333.   IF dbfError = 0 THEN D.dStatus := Updated;
  334.   dbfOK := (dbfError = 0);
  335. END;                        {PutDbfRecord}
  336.  
  337. PROCEDURE AppendDbf(VAR D : dbfRecord);
  338. BEGIN
  339.   PutDbfRecord(D, D.NumRecs+1);
  340. END;
  341.  
  342. PROCEDURE CloseDbf(VAR D : dbfRecord);
  343. CONST EofMark : Byte = $1A;
  344.   PROCEDURE UpdateHeader(VAR D : dbfRecord);
  345.   VAR
  346.     Reg : Registers;
  347.     r   : longint;
  348.   BEGIN
  349.     r := D.NumRecs;
  350.     Reg.AX := $2A00;
  351.     Intr($21, Reg);
  352.     IF D.HeadProlog[0] = DB2File THEN
  353.     BEGIN
  354.       D.HeadProlog[5] := Reg.CX-1900; {Year}
  355.       D.HeadProlog[3] := Reg.DH; {Month}
  356.       D.HeadProlog[4] := Reg.DL; {Day}
  357.       D.HeadProlog[2] := r div 256;
  358.       r := r - (D.HeadProlog[5]*256);
  359.       D.HeadProlog[1] := r;
  360.     END ELSE
  361.     BEGIN
  362.       D.HeadProlog[1] := Reg.CX-1900; {Year}
  363.       D.HeadProlog[2] := Reg.DH; {Month}
  364.       D.HeadProlog[3] := Reg.DL; {Day}
  365.       D.HeadProlog[7] := r div 16777216;
  366.       r := r - (D.HeadProlog[7]*16777216);
  367.       D.HeadProlog[6] := r div 65536;
  368.       r := r - (D.HeadProlog[6]*65536);
  369.       D.HeadProlog[5] := r div 256;
  370.       r := r - (D.HeadProlog[5]*256);
  371.       D.HeadProlog[4] := r;
  372.     END;
  373.     seek(D.dFile, 0);
  374.     dbfError := IOResult;
  375.     IF dbfError = 0 THEN
  376.     BEGIN
  377.       BlockWrite(D.dFile, D.HeadProlog, 8);
  378.       dbfError := IOResult;
  379.     END;
  380.     dbfOK := (dbfError = 0);
  381.   END;
  382.  
  383. BEGIN                       { CloseDbf }
  384.   dbfError := 0;
  385.   IF D.dStatus = Updated THEN
  386.   BEGIN
  387.     UpdateHeader(D);
  388.     IF dbfError = 0 THEN
  389.     BEGIN
  390.       seek(D.dFile, D.HeadLen+D.NumRecs*D.RecLen);
  391.       dbfError := IOResult;
  392.     END;
  393.     IF dbfError = 0 THEN
  394.     BEGIN
  395.       BlockWrite(D.dFile, EofMark, 1); {Put EOF marker }
  396.       dbfError := IOResult;
  397.     END;
  398.   END;   { IF Updated }
  399.   IF dbfError = 0 THEN
  400.   BEGIN
  401.     Close(D.dFile);
  402.     dbfError := IOResult;
  403.   END;
  404.   IF dbfError = 0 THEN
  405.   BEGIN
  406.     D.dStatus := NotOpen;
  407.     FreeMem(D.CurRecord, D.RecLen);
  408.     FreeMem(D.Fields, D.NumFields*SizeOf(_FieldRecord));
  409.   END;
  410.   dbfOK := (dbfError = 0)
  411. END;                        { CloseDbf }
  412.  
  413. PROCEDURE OpenDbf(VAR D : dbfRecord);
  414.   PROCEDURE ProcessHeader(VAR Header : _HeaderType; NumBytes : Integer);
  415.   VAR
  416.     o, i       : Integer;
  417.     tempFields : _FieldArray;
  418.  
  419.     PROCEDURE GetOneFieldDesc(VAR F; VAR Field : _FieldRecord;
  420.                               VAR Offset : Integer);
  421.     VAR
  422.       i  : Integer;
  423.       FD : _FieldDescType ABSOLUTE F;
  424.     BEGIN                   { GetOneFieldDesc }
  425.       i := 0;
  426.       Field.Name := '';
  427.       REPEAT
  428.         Field.Name[Succ(i)] := Chr(FD[i]);
  429.         i := Succ(i);
  430.       UNTIL FD[i] = 0;
  431.       Field.Name[0] := Chr(i);
  432.       Field.Typ := Char(FD[11]);
  433.       IF D.HeadProlog[0] = DB2File THEN
  434.       BEGIN
  435.         Field.Len := FD[12];
  436.         Field.Dec := FD[15];
  437.       END ELSE
  438.       BEGIN
  439.         Field.Len := FD[16];
  440.         Field.Dec := FD[17];
  441.       END;
  442.       Field.Off := Offset;
  443.       Offset := Offset+Field.Len;
  444.       IF NOT(Field.Typ IN ValidTypes) THEN dbfError := INVALID_FIELD;
  445.     END;                    { GetOneFieldDesc }
  446.  
  447.     PROCEDURE ProcessDB2Header;
  448.     VAR
  449.       o, i, tFieldsLen : Integer;
  450.       tempFields       : _FieldArray;
  451.     BEGIN   { ProcessDB2Header }
  452.       D.DateOfUpdate := MakeStr(Header[3])+'/'+
  453.                         MakeStr(Header[4])+'/'+MakeStr(Header[5]);
  454.       D.NumRecs := MakeWord(Header[1]);
  455.       D.HeadLen := 521;
  456.       IF NumBytes < D.HeadLen THEN
  457.       BEGIN
  458.         dbfError := NOT_DB_FILE;
  459.         Close(D.dFile);
  460.         Exit;
  461.       END;
  462.       D.RecLen := MakeInt(Header[6]); { Includes the Deleted Record Flag }
  463.       GetMem(D.CurRecord, D.RecLen); { Allocate some memory for a buffer  }
  464.       D.dStatus := NotUpdated;
  465.       D.NumFields := 0;
  466.       Move(Header, D.HeadProlog, SizeOf(D.HeadProlog));
  467.       o := 1;                   {Offset within dbf record of current field }
  468.       i := 8;                   {Index for Header }
  469.       WHILE Header[i] <> $0D DO
  470.       BEGIN
  471.         D.NumFields := Succ(D.NumFields);
  472.         GetOneFieldDesc(Header[i], tempFields[D.NumFields], o);
  473.         IF dbfError <> 0 THEN
  474.         BEGIN
  475.           Close(D.dFile);
  476.           Exit;
  477.         END;
  478.         i := i+16;
  479.       END;                    { While Header[i] <> $0D }
  480.       tFieldsLen := D.NumFields*SizeOf(_FieldRecord);
  481.       GetMem(D.Fields, tFieldsLen);
  482.       Move(tempFields, D.Fields^, tFieldsLen);
  483.       D.WithMemo := FALSE;
  484.     END;                      {ProcessDB2Header}
  485.  
  486.   BEGIN                     {ProcessHeader}
  487.     CASE Header[0] OF
  488.       DB2File     : BEGIN
  489.                       ProcessDB2Header;
  490.                       Exit;
  491.                     END;
  492.       DB3File     : D.WithMemo := False;
  493.       DB3WithMemo : D.WithMemo := True;
  494.     ELSE
  495.       BEGIN
  496.         dbfError := NOT_DB_FILE;
  497.         Close(D.dFile);
  498.         Exit;
  499.       END;
  500.     END;                      {CASE}
  501.     D.DateOfUpdate := MakeStr(Header[2])+'/'+
  502.                       MakeStr(Header[3])+'/'+MakeStr(Header[1]);
  503.     D.NumRecs := MakeLongInt(Header[4]);
  504.     D.HeadLen := MakeInt(Header[8]);
  505.     IF NumBytes < D.HeadLen THEN
  506.     BEGIN
  507.       dbfError := NOT_DB_FILE;
  508.       Close(D.dFile);
  509.       Exit;
  510.     END;
  511.     D.RecLen := MakeInt(Header[10]); { Includes the Deleted Record Flag }
  512.     GetMem(D.CurRecord, D.RecLen); { Allocate some memory for a buffer  }
  513.     D.dStatus := NotUpdated;
  514.     D.NumFields := 0;
  515.     Move(Header, D.HeadProlog, SizeOf(D.HeadProlog));
  516.     o := 1;                   {Offset within dbf record of current field }
  517.     i := 32;                  {Index for Header }
  518.     WHILE Header[i] <> $0D DO
  519.     BEGIN
  520.       D.NumFields := Succ(D.NumFields);
  521.       GetOneFieldDesc(Header[i], tempFields[D.NumFields], o);
  522.       IF dbfError <> 0 THEN
  523.       BEGIN
  524.         Close(D.dFile);
  525.         Exit;
  526.       END;
  527.       i := i+32;
  528.     END;                    { While Header[i] <> $0D }
  529.     i := D.NumFields*SizeOf(_FieldRecord);
  530.     GetMem(D.Fields,i) ;
  531.     Move(tempFields, D.Fields^, i);
  532.   END;                      {ProcessHeader}
  533.  
  534.   PROCEDURE GetHeader;
  535.   VAR
  536.     Result : Integer;
  537.     H      : _HeaderType;
  538.   BEGIN                     { GetHeader }
  539.     BlockRead(D.dFile, H, MAX_HEADER, Result);
  540.     dbfError := IOResult;
  541.     IF dbfError = 0 THEN ProcessHeader(H, Result);
  542.   END;                      { GetHeader }
  543.  
  544. BEGIN                       { OpenDbf }
  545.   Assign(D.dFile, D.FileName);
  546.   Reset(D.dFile, 1); {the '1' parameter sets the record size}
  547.   dbfError := IOResult;
  548.   IF dbfError = 0 THEN GetHeader;
  549.   dbfOK := (dbfError = 0);
  550. END;                        { OpenDbf }
  551.  
  552. PROCEDURE CreateDbf(VAR D : dbfRecord; fn : _Str64; n : Integer;
  553.                     flds : _dFields);
  554.   {
  555.   Call this procedure with the full pathname of the file that you want
  556.   to create (fn), the number of fields in a record (n), and a pointer
  557.   to an array of _FieldRecord (flds).  The procedure will initialize all
  558.   the data structures in the dbfRecord (D).
  559.   }
  560.  
  561. VAR
  562.   tHeader : _HeaderType;
  563.  
  564.   PROCEDURE MakeFieldDescs;
  565.   VAR
  566.     i : Integer;
  567.     PROCEDURE MakeOneFieldDesc(VAR F; VAR Field : _FieldRecord);
  568.     VAR
  569.       FD : _FieldDescType ABSOLUTE F;
  570.     BEGIN                   { MakeOneFieldDesc }
  571.       Move(Field.Name[1],FD,Ord(Field.Name[0]));
  572.       FD[11] := Ord(Field.Typ);
  573.       FD[16] := Field.Len;
  574.       IF Field.Typ <> 'N' THEN Field.Dec := 0;
  575.       FD[17] := Field.Dec;
  576.       Field.Off := D.RecLen;
  577.       D.RecLen := D.RecLen+Field.Len;
  578.       IF NOT(Field.Typ IN ValidTypes) THEN dbfError := INVALID_FIELD;
  579.       IF Field.Typ = 'M' THEN D.WithMemo := TRUE;
  580.     END;                    { MakeOneFieldDesc }
  581.  
  582.   BEGIN                     {MakeFieldDescs}
  583.     D.RecLen := 1;
  584.     FOR i := 1 TO D.NumFields DO
  585.     BEGIN
  586.       MakeOneFieldDesc(tHeader[i*32],flds^[i]);
  587.       IF dbfError <> 0 THEN Exit;
  588.     END;
  589.   END;                      {MakeFieldDescs}
  590.  
  591.   PROCEDURE MakeHeader;
  592.   VAR
  593.     Result : Integer;
  594.   BEGIN                     { MakeHeader }
  595.     FillChar(tHeader,SizeOf(tHeader),#0);
  596.     D.WithMemo := FALSE;
  597.     D.HeadLen := Succ(D.NumFields) * 32;
  598.     tHeader[D.HeadLen] := $0D;
  599.     D.HeadLen := Succ(D.HeadLen);
  600.     tHeader[8] := Lo(D.HeadLen);
  601.     tHeader[9] := Hi(D.HeadLen);
  602.     MakeFieldDescs;
  603.     IF D.WithMemo THEN
  604.       tHeader[0] := DB3WithMemo
  605.     ELSE
  606.       tHeader[0] := DB3File;
  607.     tHeader[10] := Lo(D.RecLen);
  608.     tHeader[11] := Hi(D.RecLen);
  609.   END;                      { MakeHeader }
  610.  
  611. VAR
  612.   i : Integer;
  613. BEGIN            { CreateDbf }
  614.   D.NumFields := n;
  615.   MakeHeader;
  616.   D.FileName := fn;
  617.   Assign(D.dFile, D.FileName);
  618.   Rewrite(D.dFile, 1); {Will overwrite if file exists!}
  619.   dbfError := IOResult;
  620.   IF dbfError = 0 THEN
  621.   BEGIN
  622.     BlockWrite(D.dFile,tHeader,Succ(D.HeadLen));
  623.     dbfError := IOResult;
  624.   END;
  625.   IF dbfError = 0 THEN
  626.   BEGIN
  627.     D.dStatus := Updated;
  628.     D.NumRecs := 0;
  629.     Move(tHeader,D.HeadProlog,SizeOf(D.HeadProlog));
  630.     D.DateOfUpdate := '  /  /  ';
  631.     GetMem(D.CurRecord, D.RecLen); { Allocate some memory for a buffer  }
  632.     FillChar(D.CurRecord^,D.RecLen,' ');
  633.     i := D.NumFields*SizeOf(_FieldRecord);
  634.     GetMem(D.Fields,i);
  635.     Move(flds, D.Fields^,i);
  636.   END;
  637.   dbfOK := (dbfError = 0)
  638. END;                        { CreateDbf }
  639.  
  640. begin
  641. end.
  642.