mirror of
				https://github.com/postgres/postgres.git
				synced 2025-10-29 22:49:41 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			519 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			519 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* 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 "postgres_fe.h"
 | |
| 
 | |
| #include <fcntl.h>
 | |
| #include <ctype.h>
 | |
| #include <time.h>
 | |
| #include <unistd.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)) != sizeof(dbf_header))
 | |
| 		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)) != sizeof(dbf_field))
 | |
| 			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 && !isprint(*end))
 | |
| 			{
 | |
| 				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 && !isprint(*end))
 | |
| 			{
 | |
| 				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, because 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);
 | |
| 					snprintf(format, 32, "%%.%df", rec[t].db_dec);
 | |
| 					snprintf(foo, 128, format, fl);
 | |
| 				}
 | |
| 				else
 | |
| 					strncpy(foo, rec[t].db_contents, 128);
 | |
| 				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) != dbh->db_rlen)
 | |
| 		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;
 | |
| }
 |