diff options
| author | Bruce Momjian | 2001-05-10 14:41:23 +0000 |
|---|---|---|
| committer | Bruce Momjian | 2001-05-10 14:41:23 +0000 |
| commit | c2a062b7fe55a0d29a8b2f7dffd4f6dd1202af31 (patch) | |
| tree | b9785ea644f509b6816b5dd482c1f5f3395a092f /contrib/dbase/dbf.c | |
| parent | 72c8af51fd8080c9659c2b54264aa4fd78f2e151 (diff) | |
Add dbase conversion utility to /contrib.
Diffstat (limited to 'contrib/dbase/dbf.c')
| -rw-r--r-- | contrib/dbase/dbf.c | 474 |
1 files changed, 474 insertions, 0 deletions
diff --git a/contrib/dbase/dbf.c b/contrib/dbase/dbf.c new file mode 100644 index 00000000000..94f0b165cac --- /dev/null +++ b/contrib/dbase/dbf.c @@ -0,0 +1,474 @@ +/* Routines to read and write xBase-files (.dbf) + + By Maarten Boekhold, 29th of oktober 1995 + + Modified by Frank Koormann (fkoorman@usf.uni-osnabrueck.de), Jun 10 1996 + prepare dataarea with memset + get systemtime and set filedate + set formatstring for real numbers +*/ + +#include <sys/types.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <ctype.h> +#include <time.h> + +#include "dbf.h" + +/* open a dbf-file, get it's field-info and store this information */ + +dbhead *dbf_open(u_char *file, int flags) { + int file_no; + dbhead *dbh; + f_descr *fields; + dbf_header *head; + dbf_field *fieldc; + int t; + + if ((dbh = (dbhead *)malloc(sizeof(dbhead))) == NULL) { + return (dbhead *)DBF_ERROR; + } + + if ((head = (dbf_header *)malloc(sizeof(dbf_header))) == NULL) { + free(dbh); + return (dbhead *)DBF_ERROR; + } + + if ((fieldc = (dbf_field *)malloc(sizeof(dbf_field))) == NULL) { + free(head); + free(dbh); + return (dbhead *)DBF_ERROR; + } + + if ((file_no = open(file, flags)) == -1) { + free(fieldc); + free(head); + free(dbh); + return (dbhead *)DBF_ERROR; + } + +/* read in the disk-header */ + + if (read(file_no, head, sizeof(dbf_header)) == -1) { + close(file_no); + free(fieldc); + free(head); + free(dbh); + return (dbhead *)DBF_ERROR; + } + + if (!(head->dbh_dbt & DBH_NORMAL)) { + close(file_no); + free(fieldc); + free(head); + free(dbh); + return (dbhead *)DBF_ERROR; + } + + dbh->db_fd = file_no; + if (head->dbh_dbt & DBH_MEMO) { + dbh->db_memo = 1; + } else { + dbh->db_memo = 0; + } + dbh->db_year = head->dbh_year; + dbh->db_month = head->dbh_month; + dbh->db_day = head->dbh_day; + dbh->db_hlen = get_short((u_char *)&head->dbh_hlen); + dbh->db_records = get_long((u_char *)&head->dbh_records); + dbh->db_currec = 0; + dbh->db_rlen = get_short((u_char *)&head->dbh_rlen); + dbh->db_nfields = (dbh->db_hlen - sizeof(dbf_header)) / sizeof(dbf_field); + + /* dbh->db_hlen - sizeof(dbf_header) isn't the + correct size, cos dbh->hlen is in fact + a little more cos of the 0x0D (and + possibly another byte, 0x4E, I have + seen this somewhere). Because of rounding + everything turns out right :) */ + + if ((fields = (f_descr *)calloc(dbh->db_nfields, sizeof(f_descr))) + == NULL) { + close(file_no); + free(fieldc); + free(head); + free(dbh); + return (dbhead *)DBF_ERROR; + } + + for (t = 0; t < dbh->db_nfields; t++) { +/* Maybe I have calculated the number of fields incorrectly. This can happen + when programs reserve lots of space at the end of the header for future + expansion. This will catch this situation */ + if (fields[t].db_name[0] == 0x0D) { + dbh->db_nfields = t; + break; + } + read(file_no, fieldc, sizeof(dbf_field)); + strncpy(fields[t].db_name, fieldc->dbf_name, DBF_NAMELEN); + fields[t].db_type = fieldc->dbf_type; + fields[t].db_flen = fieldc->dbf_flen; + fields[t].db_dec = fieldc->dbf_dec; + } + + dbh->db_offset = dbh->db_hlen; + dbh->db_fields = fields; + + if ((dbh->db_buff = (u_char *)malloc(dbh->db_rlen)) == NULL) { + return (dbhead *)DBF_ERROR; + } + + free(fieldc); + free(head); + + return dbh; +} + +int dbf_write_head(dbhead *dbh) { + dbf_header head; + time_t now; + struct tm *dbf_time; + + if (lseek(dbh->db_fd, 0, SEEK_SET) == -1) { + return DBF_ERROR; + } + +/* fill up the diskheader */ + +/* Set dataarea of head to '\0' */ + memset(&head,'\0',sizeof(dbf_header)); + + head.dbh_dbt = DBH_NORMAL; + if (dbh->db_memo) head.dbh_dbt = DBH_MEMO; + + now = time((time_t *)NULL); + dbf_time = localtime(&now); + head.dbh_year = dbf_time->tm_year; + head.dbh_month = dbf_time->tm_mon + 1; /* Months since January + 1 */ + head.dbh_day = dbf_time->tm_mday; + + put_long(head.dbh_records, dbh->db_records); + put_short(head.dbh_hlen, dbh->db_hlen); + put_short(head.dbh_rlen, dbh->db_rlen); + + if (write(dbh->db_fd, &head, sizeof(dbf_header)) == -1 ) { + return DBF_ERROR; + } + + return 0; +} + +int dbf_put_fields(dbhead *dbh) { + dbf_field field; + u_long t; + u_char end = 0x0D; + + if (lseek(dbh->db_fd, sizeof(dbf_header), SEEK_SET) == -1) { + return DBF_ERROR; + } + +/* Set dataarea of field to '\0' */ + memset(&field,'\0',sizeof(dbf_field)); + + for (t = 0; t < dbh->db_nfields; t++) { + strncpy(field.dbf_name, dbh->db_fields[t].db_name, DBF_NAMELEN - 1); + field.dbf_type = dbh->db_fields[t].db_type; + field.dbf_flen = dbh->db_fields[t].db_flen; + field.dbf_dec = dbh->db_fields[t].db_dec; + + if (write(dbh->db_fd, &field, sizeof(dbf_field)) == -1) { + return DBF_ERROR; + } + } + + if (write(dbh->db_fd, &end, 1) == -1) { + return DBF_ERROR; + } + + return 0; +} + +int dbf_add_field(dbhead *dbh, u_char *name, u_char type, + u_char length, u_char dec) { +f_descr *ptr; +u_char *foo; +u_long size, field_no; + + size = (dbh->db_nfields + 1) * sizeof(f_descr); + if (!(ptr = (f_descr *) realloc(dbh->db_fields, size))) { + return DBF_ERROR; + } + dbh->db_fields = ptr; + + field_no = dbh->db_nfields; + strncpy(dbh->db_fields[field_no].db_name, name, DBF_NAMELEN); + dbh->db_fields[field_no].db_type = type; + dbh->db_fields[field_no].db_flen = length; + dbh->db_fields[field_no].db_dec = dec; + + dbh->db_nfields++; + dbh->db_hlen += sizeof(dbf_field); + dbh->db_rlen += length; + + if (!(foo = (u_char *) realloc(dbh->db_buff, dbh->db_rlen))) { + return DBF_ERROR; + } + + dbh->db_buff = foo; + + return 0; +} + +dbhead *dbf_open_new(u_char *name, int flags) { +dbhead *dbh; + + if (!(dbh = (dbhead *)malloc(sizeof(dbhead)))) { + return (dbhead *)DBF_ERROR; + } + + if (flags & O_CREAT) { + if ((dbh->db_fd = open(name, flags, DBF_FILE_MODE)) == -1) { + free(dbh); + return (dbhead *)DBF_ERROR; + } + } else { + if ((dbh->db_fd = open(name, flags)) == -1) { + free(dbh); + return (dbhead *)DBF_ERROR; + } + } + + + dbh->db_offset = 0; + dbh->db_memo = 0; + dbh->db_year = 0; + dbh->db_month = 0; + dbh->db_day = 0; + dbh->db_hlen = sizeof(dbf_header) + 1; + dbh->db_records = 0; + dbh->db_currec = 0; + dbh->db_rlen = 1; + dbh->db_nfields = 0; + dbh->db_buff = NULL; + dbh->db_fields = (f_descr *)NULL; + + return dbh; +} + +void dbf_close(dbhead *dbh) { + int t; + + close(dbh->db_fd); + + for (t = 0; t < dbh->db_nfields; t++) { + free(&dbh->db_fields[t]); + } + + if (dbh->db_buff != NULL) { + free(dbh->db_buff); + } + + free(dbh); +} + +int dbf_get_record(dbhead *dbh, field *fields, u_long rec) { + u_char *data; + int t, i, offset; + u_char *dbffield, *end; + +/* calculate at which offset we have to read. *DON'T* forget the + 0x0D which seperates field-descriptions from records! + + Note (april 5 1996): This turns out to be included in db_hlen +*/ + offset = dbh->db_hlen + (rec * dbh->db_rlen); + + if (lseek(dbh->db_fd, offset, SEEK_SET) == -1) { + lseek(dbh->db_fd, 0, SEEK_SET); + dbh->db_offset = 0; + return DBF_ERROR; + } + + dbh->db_offset = offset; + dbh->db_currec = rec; + data = dbh->db_buff; + + read(dbh->db_fd, data, dbh->db_rlen); + + if (data[0] == DBF_DELETED) { + return DBF_DELETED; + } + + dbffield = &data[1]; + for (t = 0; t < dbh->db_nfields; t++) { + strncpy(fields[t].db_name, dbh->db_fields[t].db_name, DBF_NAMELEN); + fields[t].db_type = dbh->db_fields[t].db_type; + fields[t].db_flen = dbh->db_fields[t].db_flen; + fields[t].db_dec = dbh->db_fields[t].db_dec; + + if (fields[t].db_type == 'C') { + end = &dbffield[fields[t].db_flen - 1 ]; + i = fields[t].db_flen; + while (( i > 0) && ((*end < 0x21) || (*end > 0x7E))) { + end--; + i--; + } + strncpy(fields[t].db_contents, dbffield, i); + fields[t].db_contents[i] = '\0'; + } else { + end = dbffield; + i = fields[t].db_flen; + while (( i > 0) && ((*end < 0x21) || (*end > 0x7E))) { + end++; + i--; + } + strncpy(fields[t].db_contents, end, i); + fields[t].db_contents[i] = '\0'; + } + + dbffield += fields[t].db_flen; + } + + dbh->db_offset += dbh->db_rlen; + + return DBF_VALID; +} + +field *dbf_build_record(dbhead *dbh) { + int t; + field *fields; + + if (!(fields = (field *)calloc(dbh->db_nfields, sizeof(field)))) { + return (field *)DBF_ERROR; + } + + for ( t = 0; t < dbh->db_nfields; t++) { + if (!(fields[t].db_contents = + (u_char *)malloc(dbh->db_fields[t].db_flen + 1))) { + for (t = 0; t < dbh->db_nfields; t++) { + if (fields[t].db_contents != 0) { + free(fields[t].db_contents); + free(fields); + } + return (field *)DBF_ERROR; + } + } + strncpy(fields[t].db_name, dbh->db_fields[t].db_name, DBF_NAMELEN); + fields[t].db_type = dbh->db_fields[t].db_type; + fields[t].db_flen = dbh->db_fields[t].db_flen; + fields[t].db_dec = dbh->db_fields[t].db_dec; + } + + return fields; +} + +void dbf_free_record(dbhead *dbh, field *rec) { + int t; + + for ( t = 0; t < dbh->db_nfields; t++) { + free(rec[t].db_contents); + } + + free(rec); +} + +int dbf_put_record(dbhead *dbh, field *rec, u_long where) { + u_long offset, new, idx, t, h, length; + u_char *data, end = 0x1a; + double fl; + u_char foo[128], format[32]; + +/* offset: offset in file for this record + new: real offset after lseek + idx: index to which place we are inside the 'hardcore'-data for this + record + t: field-counter + data: the hardcore-data that is put on disk + h: index into the field-part in the hardcore-data + length: length of the data to copy + fl: a float used to get the right precision with real numbers + foo: copy of db_contents when field is not 'C' + format: sprintf format-string to get the right precision with real numbers + + NOTE: this declaration of 'foo' can cause overflow when the contents-field + is longer the 127 chars (which is highly unlikely, cos it is not used + in text-fields). +*/ +/* REMEMBER THAT THERE'S A 0x1A AT THE END OF THE FILE, SO DON'T + DO A SEEK_END WITH 0!!!!!! USE -1 !!!!!!!!!! +*/ + + if (where > dbh->db_records) { + if ((new = lseek(dbh->db_fd, -1, SEEK_END)) == -1) { + return DBF_ERROR; + } + dbh->db_records++; + } else { + offset = dbh->db_hlen + (where * dbh->db_rlen); + if ((new = lseek(dbh->db_fd, offset, SEEK_SET)) == -1) { + return DBF_ERROR; + } + } + + dbh->db_offset = new; + + data = dbh->db_buff; + +/* Set dataarea of data to ' ' (space) */ + memset(data,' ',dbh->db_rlen); + +/* data[0] = DBF_VALID; */ + + idx = 1; + for (t = 0; t < dbh->db_nfields; t++) { +/* if field is empty, don't do a thing */ + if (rec[t].db_contents[0] != '\0') { +/* Handle text */ + if (rec[t].db_type == 'C') { + if (strlen(rec[t].db_contents) > rec[t].db_flen) { + length = rec[t].db_flen; + } else { + length = strlen(rec[t].db_contents); + } + strncpy(data+idx, rec[t].db_contents, length); + } else { +/* Handle the rest */ +/* Numeric is special, because of real numbers */ + if ((rec[t].db_type == 'N') && (rec[t].db_dec != 0)) { + fl = atof(rec[t].db_contents); + sprintf(format, "%%.%df", rec[t].db_dec); + sprintf(foo, format, fl); + } else { + strcpy(foo, rec[t].db_contents); + } + if (strlen(foo) > rec[t].db_flen) { + length = rec[t].db_flen; + } else { + length = strlen(foo); + } + h = rec[t].db_flen - length; + strncpy(data+idx+h, foo, length); + } + } + idx += rec[t].db_flen; + } + + if (write(dbh->db_fd, data, dbh->db_rlen) == -1) { + return DBF_ERROR; + } + +/* There's a 0x1A at the end of a dbf-file */ + if (where == dbh->db_records) { + if (write(dbh->db_fd, &end, 1) == -1) { + return DBF_ERROR; + } + } + + dbh->db_offset += dbh->db_rlen; + + return 0; +} |
