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