1 /*****************************************************************************
2 * dbf.c
3 *****************************************************************************
4 * Library to read information from dBASE files
5 * Author: Bjoern Berg, clergyman@gmx.de
6 * (C) Copyright 2004, Bj�rn Berg
7 *
8 *****************************************************************************
9 * Permission to use, copy, modify and distribute this software and its
10 * documentation for any purpose is hereby granted without fee, provided that
11 * the above copyright notice appear in all copies and that both that copyright
12 * notice and this permission notice appear in supporting documentation. The
13 * author makes no representations about the suitability of this software for
14 * any purpose. It is provided "as is" without express or implied warranty.
15 *
16 * $Id: dbf.c,v 1.8 2004/09/07 16:08:23 steinm Exp $
17 ****************************************************************************/
18
19
20 #include <time.h>
21 #include "../include/libdbf/libdbf.h"
22 #include "dbf.h"
23
24 /* get_db_version() {{{
25 * Convert version field of header into human readable string.
26 */
get_db_version(int version)27 const char *get_db_version(int version) {
28 static char name[31];
29
30 switch (version) {
31 case 0x02:
32 // without memo fields
33 return "FoxBase";
34 case 0x03:
35 // without memo fields
36 return "FoxBase+/dBASE III+";
37 case 0x04:
38 // without memo fields
39 return "dBASE IV";
40 case 0x05:
41 // without memo fields
42 return "dBASE 5.0";
43 case 0x83:
44 return "FoxBase+/dBASE III+";
45 case 0x8B:
46 return "dBASE IV";
47 case 0x30:
48 // without memo fields
49 return "Visual FoxPro";
50 case 0xF5:
51 // with memo fields
52 return "FoxPro 2.0";
53 default:
54 sprintf(name, _("Unknown (code 0x%.2X)"), version);
55 return name;
56 }
57 }
58 /* }}} */
59
60 /* static dbf_ReadHeaderInfo() {{{
61 * Reads header from file into struct
62 */
dbf_ReadHeaderInfo(P_DBF * p_dbf)63 static int dbf_ReadHeaderInfo(P_DBF *p_dbf)
64 {
65 DB_HEADER *header;
66 if(NULL == (header = malloc(sizeof(DB_HEADER)))) {
67 return -1;
68 }
69 if ((read( p_dbf->dbf_fh, header, sizeof(DB_HEADER))) == -1 ) {
70 return -1;
71 }
72
73 /* Endian Swapping */
74 header->header_length = rotate2b(header->header_length);
75 header->record_length = rotate2b(header->record_length);
76 header->records = rotate4b(header->records);
77 p_dbf->header = header;
78
79 return 0;
80 }
81 /* }}} */
82
83 /* static dbf_WriteHeaderInfo() {{{
84 * Write header into file
85 */
dbf_WriteHeaderInfo(P_DBF * p_dbf,DB_HEADER * header)86 static int dbf_WriteHeaderInfo(P_DBF *p_dbf, DB_HEADER *header)
87 {
88 time_t ps_calendar_time;
89 struct tm *ps_local_tm;
90
91 DB_HEADER *newheader = malloc(sizeof(DB_HEADER));
92 if(NULL == newheader) {
93 return -1;
94 }
95 memcpy(newheader, header, sizeof(DB_HEADER));
96
97 ps_calendar_time = time(NULL);
98 if(ps_calendar_time != (time_t)(-1)) {
99 ps_local_tm = localtime(&ps_calendar_time);
100 newheader->last_update[0] = ps_local_tm->tm_year;
101 newheader->last_update[1] = ps_local_tm->tm_mon+1;
102 newheader->last_update[2] = ps_local_tm->tm_mday;
103 }
104
105 newheader->header_length = rotate2b(newheader->header_length);
106 newheader->record_length = rotate2b(newheader->record_length);
107 newheader->records = rotate4b(newheader->records);
108
109 /* Make sure the header is written at the beginning of the file
110 * because this function is also called after each record has
111 * been written.
112 */
113 lseek(p_dbf->dbf_fh, 0, SEEK_SET);
114 if ((write( p_dbf->dbf_fh, newheader, sizeof(DB_HEADER))) == -1 ) {
115 free(newheader);
116 return -1;
117 }
118 free(newheader);
119 return 0;
120 }
121 /* }}} */
122
123 /* static dbf_ReadFieldInfo() {{{
124 * Sets p_dbf->fields to an array of DB_FIELD containing the specification
125 * for all columns.
126 */
dbf_ReadFieldInfo(P_DBF * p_dbf)127 static int dbf_ReadFieldInfo(P_DBF *p_dbf)
128 {
129 int columns, i, offset;
130 DB_FIELD *fields;
131
132 columns = dbf_NumCols(p_dbf);
133
134 if(NULL == (fields = malloc(columns * sizeof(DB_FIELD)))) {
135 return -1;
136 }
137
138 lseek(p_dbf->dbf_fh, sizeof(DB_HEADER), SEEK_SET);
139
140 if ((read( p_dbf->dbf_fh, fields, columns * sizeof(DB_FIELD))) == -1 ) {
141 perror(_("In function dbf_ReadFieldInfo(): "));
142 return -1;
143 }
144 p_dbf->fields = fields;
145 p_dbf->columns = columns;
146 /* The first byte of a record indicates whether it is deleted or not. */
147 offset = 1;
148 for(i = 0; i < columns; i++) {
149 fields[i].field_offset = offset;
150 offset += fields[i].field_length;
151 }
152
153 return 0;
154 }
155 /* }}} */
156
157 /* static dbf_WriteFieldInfo() {{{
158 * Writes the field specification into the output file
159 */
dbf_WriteFieldInfo(P_DBF * p_dbf,DB_FIELD * fields,int numfields)160 static int dbf_WriteFieldInfo(P_DBF *p_dbf, DB_FIELD *fields, int numfields)
161 {
162 lseek(p_dbf->dbf_fh, sizeof(DB_HEADER), SEEK_SET);
163
164 if ((write( p_dbf->dbf_fh, fields, numfields * sizeof(DB_FIELD))) == -1 ) {
165 perror(_("In function dbf_WriteFieldInfo(): "));
166 return -1;
167 }
168
169 write(p_dbf->dbf_fh, "\r\0", 2);
170
171 return 0;
172 }
173 /* }}} */
174
175 /* dbf_Open() {{{
176 * Open the a dbf file and returns file handler
177 */
dbf_Open(const char * file)178 P_DBF *dbf_Open(const char *file)
179 {
180 P_DBF *p_dbf;
181 if(NULL == (p_dbf = malloc(sizeof(P_DBF)))) {
182 return NULL;
183 }
184
185 if (file[0] == '-' && file[1] == '\0') {
186 p_dbf->dbf_fh = fileno(stdin);
187 } else if ((p_dbf->dbf_fh = open(file, O_RDONLY|O_BINARY)) == -1) {
188 free(p_dbf);
189 return NULL;
190 }
191
192 p_dbf->header = NULL;
193 if(0 > dbf_ReadHeaderInfo(p_dbf)) {
194 free(p_dbf);
195 return NULL;
196 }
197
198 p_dbf->fields = NULL;
199 if(0 > dbf_ReadFieldInfo(p_dbf)) {
200 free(p_dbf->header);
201 free(p_dbf);
202 return NULL;
203 }
204
205 p_dbf->cur_record = 0;
206
207 return p_dbf;
208 }
209 /* }}} */
210
211 /* dbf_CreateFH() {{{
212 * Create a new dbf file and returns file handler
213 */
dbf_CreateFH(int fh,DB_FIELD * fields,int numfields)214 P_DBF *dbf_CreateFH(int fh, DB_FIELD *fields, int numfields)
215 {
216 P_DBF *p_dbf;
217 DB_HEADER *header;
218 int reclen, i;
219
220 if(NULL == (p_dbf = malloc(sizeof(P_DBF)))) {
221 return NULL;
222 }
223
224 p_dbf->dbf_fh = fh;
225
226 if(NULL == (header = malloc(sizeof(DB_HEADER)))) {
227 return NULL;
228 }
229 reclen = 0;
230 for(i=0; i<numfields; i++) {
231 reclen += fields[i].field_length;
232 }
233 memset(header, 0, sizeof(DB_HEADER));
234 header->version = FoxBasePlus;
235 /* Add 1 to record length for deletion flog */
236 header->record_length = reclen+1;
237 header->header_length = sizeof(DB_HEADER) + numfields * sizeof(DB_FIELD) + 2;
238 if(0 > dbf_WriteHeaderInfo(p_dbf, header)) {
239 free(p_dbf);
240 return NULL;
241 }
242 p_dbf->header = header;
243
244 if(0 > dbf_WriteFieldInfo(p_dbf, fields, numfields)) {
245 free(p_dbf->header);
246 free(p_dbf);
247 return NULL;
248 }
249 p_dbf->fields = fields;
250
251 p_dbf->cur_record = 0;
252
253 return p_dbf;
254 }
255 /* }}} */
256
257 /* dbf_Create() {{{
258 * Create a new dbf file and returns file handler
259 */
dbf_Create(const char * file,DB_FIELD * fields,int numfields)260 P_DBF *dbf_Create(const char *file, DB_FIELD *fields, int numfields)
261 {
262 int fh;
263
264 if (file[0] == '-' && file[1] == '\0') {
265 fh = fileno(stdout);
266 } else if ((fh = open(file, O_WRONLY|O_BINARY)) == -1) {
267 return NULL;
268 }
269
270 return(dbf_CreateFH(fh, fields, numfields));
271 }
272 /* }}} */
273
274 /* dbf_Close() {{{
275 * Close the current open dbf file and free all memory
276 */
dbf_Close(P_DBF * p_dbf)277 int dbf_Close(P_DBF *p_dbf)
278 {
279 if(p_dbf->header)
280 free(p_dbf->header);
281
282 if(p_dbf->fields)
283 free(p_dbf->fields);
284
285 if ( p_dbf->dbf_fh == fileno(stdin) )
286 return 0;
287
288 if( (close(p_dbf->dbf_fh)) == -1 ) {
289 return -1;
290 }
291
292 free(p_dbf);
293
294 return 0;
295 }
296 /* }}} */
297
298 /******************************************************************************
299 Block with functions to get information about the amount of
300 - rows and
301 - columns
302 ******************************************************************************/
303
304 /* dbf_NumRows() {{{
305 * Returns the number of records.
306 */
dbf_NumRows(P_DBF * p_dbf)307 int dbf_NumRows(P_DBF *p_dbf)
308 {
309 if ( p_dbf->header->records > 0 ) {
310 return p_dbf->header->records;
311 } else {
312 perror(_("In function dbf_NumRows(): "));
313 return -1;
314 }
315
316 return 0;
317 }
318 /* }}} */
319
320 /* dbf_NumCols() {{{
321 * Returns the number of fields.
322 */
dbf_NumCols(P_DBF * p_dbf)323 int dbf_NumCols(P_DBF *p_dbf)
324 {
325 if ( p_dbf->header->header_length > 0) {
326 // TODO: Backlink muss noch eingerechnet werden
327 return ((p_dbf->header->header_length - sizeof(DB_HEADER) -1)
328 / sizeof(DB_FIELD));
329 } else {
330 perror(_("In function dbf_NumCols(): "));
331 return -1;
332 }
333
334 return 0;
335 }
336 /* }}} */
337
338 /******************************************************************************
339 Block with functions to get/set information about the columns
340 ******************************************************************************/
341
342 /* dbf_ColumnName() {{{
343 * Returns the name of a column. Column names cannot be longer than
344 * 11 characters.
345 */
dbf_ColumnName(P_DBF * p_dbf,int column)346 const char *dbf_ColumnName(P_DBF *p_dbf, int column)
347 {
348 if ( column >= p_dbf->columns ) {
349 return "invalid";
350 }
351
352 return p_dbf->fields[column].field_name;
353 }
354 /* }}} */
355
356 /* dbf_ColumnSize() {{{
357 */
dbf_ColumnSize(P_DBF * p_dbf,int column)358 int dbf_ColumnSize(P_DBF *p_dbf, int column)
359 {
360 if ( column >= p_dbf->columns ) {
361 return -1;
362 }
363
364 return (int) p_dbf->fields[column].field_length;
365 }
366 /* }}} */
367
368 /* dbf_ColumnType() {{{
369 */
dbf_ColumnType(P_DBF * p_dbf,int column)370 const char dbf_ColumnType(P_DBF *p_dbf, int column)
371 {
372 if ( column >= p_dbf->columns ) {
373 return -1;
374 }
375
376 return p_dbf->fields[column].field_type;
377 }
378 /* }}} */
379
380 /* dbf_ColumnDecimals() {{{
381 */
dbf_ColumnDecimals(P_DBF * p_dbf,int column)382 int dbf_ColumnDecimals(P_DBF *p_dbf, int column)
383 {
384 if ( column >= p_dbf->columns ) {
385 return -1;
386 }
387
388 return p_dbf->fields[column].field_decimals;
389 }
390 /* }}} */
391
392 /* dbf_ColumnAddress() {{{
393 */
dbf_ColumnAddress(P_DBF * p_dbf,int column)394 u_int32_t dbf_ColumnAddress(P_DBF *p_dbf, int column)
395 {
396 if ( column >= p_dbf->columns ) {
397 return -1;
398 }
399
400 return p_dbf->fields[column].field_address;
401 }
402 /* }}} */
403
404 /* dbf_SetField() {{{
405 */
dbf_SetField(DB_FIELD * field,int type,const char * name,int len,int dec)406 int dbf_SetField(DB_FIELD *field, int type, const char *name, int len, int dec)
407 {
408 memset(field, 0, sizeof(DB_FIELD));
409 field->field_type = type;
410 strncpy(field->field_name, name, 11);
411 field->field_length = len;
412 field->field_decimals = dec;
413 return 0;
414 }
415 /* }}} */
416
417 /******************************************************************************
418 Block with functions to read out special dBASE information, like
419 - date
420 - usage of memo
421 ******************************************************************************/
422
423 /* dbf_GetDate() {{{
424 * Returns the date of last modification as a human readable string.
425 */
dbf_GetDate(P_DBF * p_dbf)426 const char *dbf_GetDate(P_DBF *p_dbf)
427 {
428 static char date[10];
429
430 if ( p_dbf->header->last_update[0] ) {
431 sprintf(date, "%d-%02d-%02d",
432 1900 + p_dbf->header->last_update[0], p_dbf->header->last_update[1], p_dbf->header->last_update[2]);
433
434 return date;
435 } else {
436 perror("In function GetDate(): ");
437 return "";
438 }
439
440 return 0;
441 }
442 /* }}} */
443
444 /* dbf_HeaderSize() {{{
445 */
dbf_HeaderSize(P_DBF * p_dbf)446 int dbf_HeaderSize(P_DBF *p_dbf)
447 {
448 if ( p_dbf->header->header_length > 0 ) {
449 return p_dbf->header->header_length;
450 } else {
451 perror(_("In function dbf_HeaderSize(): "));
452 return -1;
453 }
454
455 return 0;
456 }
457 /* }}} */
458
459 /* dbf_RecordLength() {{{
460 * Returns the length of a record.
461 */
dbf_RecordLength(P_DBF * p_dbf)462 int dbf_RecordLength(P_DBF *p_dbf)
463 {
464 if (p_dbf->header->record_length > 0) {
465 return p_dbf->header->record_length;
466 } else {
467 perror(_("In function dbf_RecordLength(): "));
468 return -1;
469 }
470
471 return 0;
472 }
473 /* }}} */
474
475 /* dbf_GetStringVersion() {{{
476 * Returns the verion of the dbase file as a human readable string.
477 */
dbf_GetStringVersion(P_DBF * p_dbf)478 const char *dbf_GetStringVersion(P_DBF *p_dbf)
479 {
480 if ( p_dbf->header->version == 0 ) {
481 perror(_("In function dbf_GetStringVersion(): "));
482 return (char *)-1;
483 }
484
485 return get_db_version(p_dbf->header->version);
486 }
487 /* }}} */
488
489 /* dbf_GetVersion() {{{
490 * Returns the verion field as it is storedi in the header.
491 */
dbf_GetVersion(P_DBF * p_dbf)492 int dbf_GetVersion(P_DBF *p_dbf)
493 {
494 if ( p_dbf->header->version == 0 ) {
495 perror(_("In function dbf_GetVersion(): "));
496 return -1;
497 }
498
499 return p_dbf->header->version;
500 }
501 /* }}} */
502
503 /* dbf_IsMemo() {{{
504 */
dbf_IsMemo(P_DBF * p_dbf)505 int dbf_IsMemo(P_DBF *p_dbf)
506 {
507 int memo;
508
509 if ( p_dbf->header->version == 0 ) {
510 perror(_("In function dbf_IsMemo(): "));
511 return -1;
512 }
513
514 memo = (p_dbf->header->version & 128)==128 ? 1 : 0;
515
516 return memo;
517 }
518 /* }}} */
519
520 /******************************************************************************
521 Block with functions to read records
522 ******************************************************************************/
523
524 /* dbf_SetRecordOffset() {{{
525 */
dbf_SetRecordOffset(P_DBF * p_dbf,int offset)526 int dbf_SetRecordOffset(P_DBF *p_dbf, int offset) {
527 if(offset == 0)
528 return -3;
529 if(offset > (int) p_dbf->header->records)
530 return -1;
531 if((offset < 0) && (abs(offset) > p_dbf->header->records))
532 return -2;
533 if(offset < 0)
534 p_dbf->cur_record = (int) p_dbf->header->records + offset;
535 else
536 p_dbf->cur_record = offset-1;
537 return p_dbf->cur_record;
538 }
539 /* }}} */
540
541 /* dbf_ReadRecord() {{{
542 */
dbf_ReadRecord(P_DBF * p_dbf,char * record,int len)543 int dbf_ReadRecord(P_DBF *p_dbf, char *record, int len) {
544 off_t offset;
545
546 if(p_dbf->cur_record >= p_dbf->header->records)
547 return -1;
548
549 offset = lseek(p_dbf->dbf_fh, p_dbf->header->header_length + p_dbf->cur_record * (p_dbf->header->record_length), SEEK_SET);
550 // fprintf(stdout, "Offset = %d, Record length = %d\n", offset, p_dbf->header->record_length);
551 if (read( p_dbf->dbf_fh, record, p_dbf->header->record_length) == -1 ) {
552 return -1;
553 }
554 p_dbf->cur_record++;
555 return p_dbf->cur_record-1;
556 }
557 /* }}} */
558
559 /* dbf_WriteRecord() {{{
560 */
dbf_WriteRecord(P_DBF * p_dbf,char * record,int len)561 int dbf_WriteRecord(P_DBF *p_dbf, char *record, int len) {
562
563 if(len != p_dbf->header->record_length-1) {
564 fprintf(stderr, _("Length of record mismatches expected length (%d != %d)."), len, p_dbf->header->record_length);
565 fprintf(stderr, "\n");
566 return -1;
567 }
568 lseek(p_dbf->dbf_fh, 0, SEEK_END);
569 if (write( p_dbf->dbf_fh, " ", 1) == -1 ) {
570 return -1;
571 }
572 if (write( p_dbf->dbf_fh, record, p_dbf->header->record_length-1) == -1 ) {
573 return -1;
574 }
575 p_dbf->header->records++;
576 if(0 > dbf_WriteHeaderInfo(p_dbf, p_dbf->header)) {
577 return -1;
578 }
579 return p_dbf->header->records;
580 }
581 /* }}} */
582
583 /* dbf_GetRecordData() {{{
584 */
dbf_GetRecordData(P_DBF * p_dbf,char * record,int column)585 char *dbf_GetRecordData(P_DBF *p_dbf, char *record, int column) {
586 return(record + p_dbf->fields[column].field_offset);
587 }
588 /* }}} */
589
590 /*
591 * Local variables:
592 * tab-width: 4
593 * c-basic-offset: 4
594 * End:
595 * vim600: sw=4 ts=4 fdm=marker
596 * vim<600: sw=4 ts=4
597 */
598