/*
 * Copyright (c) 1991, 1992, 1993 Brad Eacker,
 *              (Music, Intuition, Software, and Computers)
 * All Rights Reserved
 */

#include <stdio.h>
#include <fcntl.h>

#include "dbf.h"

/*
 * get the header info from the file
 *	basic header info & field descriptions
 */
dbhead_t *
get_dbf_head(fd)
int fd;
{
	dbhead_t *dbh;
	struct dbf_dhead  dbhead;
	dbfield_t *dbf, *cur_f;
	int ret, nfields, offset;

	if ((dbh = (dbhead_t *)malloc(sizeof(dbhead_t))) == NULL)
		return NULL;
	if (lseek(fd, 0, 0) < 0)
		return NULL;
	if ((ret = read(fd, &dbhead, sizeof(dbhead))) < 0)
		return NULL;

	/* build in core info */
	dbh->db_fd = fd;
	dbh->db_dbt = dbhead.dbh_dbt;
	dbh->db_records = get_long(dbhead.dbh_records);
	dbh->db_hlen = get_short(dbhead.dbh_hlen);
	dbh->db_rlen = get_short(dbhead.dbh_rlen);

	db_set_date(dbh->db_date, dbhead.dbh_date[DBH_DATE_YEAR] + 1900,
		dbhead.dbh_date[DBH_DATE_MONTH],
		dbhead.dbh_date[DBH_DATE_DAY]);

	dbh->db_nfields = nfields = (dbh->db_hlen - sizeof(struct dbf_dhead)) /
				sizeof(struct dbf_dfield);

	/* get all the field info */
	dbf = (dbfield_t *)malloc(sizeof(dbfield_t) * nfields);

	offset = 1;
	for (cur_f = dbf; cur_f < &dbf[nfields] ; cur_f++) {
		if (get_dbf_field(dbh, cur_f) < 0) {
			free_dbf_head(dbh);
			return NULL;
		}
		cur_f->db_foffset = offset;
		offset += cur_f->db_flen;
	}
	dbh->db_fields = dbf;

	return dbh;
}

/*
 * free up the header info built above
 */
free_dbf_head(dbh)
dbhead_t *dbh;
{
	dbfield_t *dbf, *cur_f;
	int nfields;
	u_char *fp;

	dbf = dbh->db_fields;
	nfields = dbh->db_nfields;
	for (cur_f = dbf; cur_f < &dbf[nfields]; cur_f++) {
		if (cur_f->db_format) {
			free(cur_f->db_format);
		}
	}

	free(dbf);
	free(dbh);
}

/*
 * put out the header info
 */
put_dbf_head(dbh)
dbhead_t *dbh;
{
	int fd = dbh->db_fd;
	struct dbf_dhead  dbhead;
	int	ret;

	bzero(&dbhead, sizeof dbhead);

	/* build on disk info */
	dbhead.dbh_dbt = dbh->db_dbt;
	put_long(dbhead.dbh_records, dbh->db_records);
	put_short(dbhead.dbh_hlen, dbh->db_hlen);
	put_short(dbhead.dbh_rlen, dbh->db_rlen);

	/* put the date spec'd into the on disk header */
	dbhead.dbh_date[DBH_DATE_YEAR] =(u_char)(db_date_year(dbh->db_date) -
						1900);
	dbhead.dbh_date[DBH_DATE_MONTH]=(u_char)(db_date_month(dbh->db_date));
	dbhead.dbh_date[DBH_DATE_DAY] =(u_char)(db_date_day(dbh->db_date));

	if (lseek(fd, 0, 0) < 0)
		return -1;
	if ((ret = write(fd, &dbhead, sizeof(dbhead))) < 0)
		return -1;
}

/*
 * get a field off the disk from the current file offset
 */
get_dbf_field(dbh, dbf)
dbhead_t *dbh;
dbfield_t *dbf;
{
	struct dbf_dfield	dbfield;
	unsigned char	*scp, *dcp;
	unsigned char	format[100];
	int ret;

	if ((ret = read(dbh->db_fd, &dbfield, sizeof(dbfield))) < 0) {
		return ret;
	}

	/* build the field name */
	copy_crimp(dbf->db_fname, dbfield.dbf_name, DBF_NAMELEN);

	dbf->db_type = dbfield.dbf_type;
	switch (dbf->db_type) {
	    case 'N':
		dbf->db_flen = dbfield.dbf_flen[0];
		dbf->db_fdc = dbfield.dbf_flen[1];
		break;
	    default:
	    	dbf->db_flen = get_short(dbfield.dbf_flen);
	}

	if ((dbf->db_format = get_dbf_f_fmt(dbf)) == NULL) {
		return 1;
	}

	return 0;
}

/*
 * put a field out on the disk at the current file offset
 */
put_dbf_field(dbh, dbf)
dbhead_t *dbh;
dbfield_t *dbf;
{
	struct dbf_dfield	dbfield;
	unsigned char		*scp, *dcp;
	int			c_left = DBF_NAMELEN;
	int			ret;

	bzero(&dbfield, sizeof dbfield);

	/* build the on disk field info */
	scp = dbf->db_fname; dcp = dbfield.dbf_name;

	strncpy(dbfield.dbf_name, dbf->db_fname, DBF_NAMELEN);

	dbfield.dbf_type = dbf->db_type;
	switch (dbf->db_type) {
	    case 'N':		
		dbfield.dbf_flen[0] = dbf->db_flen;
		dbfield.dbf_flen[1] = dbf->db_fdc;
		break;
	    default:
	    	put_short(dbfield.dbf_flen, dbf->db_flen);
	}

	/* now write it out to disk */
	if ((ret = write(dbh->db_fd, &dbfield, sizeof(dbfield))) < 0) {
		return ret;
	}
	return 1;
}

/*
 * put out all the info at the top of the file...
 */
static char end_stuff[2] = {0x0d, 0};

put_dbf_info(dbh)
dbhead_t *dbh;
{
	dbfield_t	*dbf, *cur_f;
	int		fcnt;

	put_dbf_head(dbh);
	dbf = dbh->db_fields;
	for (fcnt = dbh->db_nfields; fcnt > 0; fcnt--, dbf++)
		put_dbf_field(dbh, dbf);
	write(dbh->db_fd, end_stuff, 2);
}

u_char *
get_dbf_f_fmt(dbf)
dbfield_t *dbf;
{
	u_char format[100];

	/* build the field format for printf */
	switch (dbf->db_type) {
	   case 'C':
		sprintf(format, "%%-%ds", dbf->db_flen);
		break;
	   case 'N':
	   case 'L':
	   case 'D':
		sprintf(format, "%%%ds", dbf->db_flen);
		break;
	   case 'M':
		strcpy(format, "%s");
		break;
	}
	return (u_char *)strdup(format);
}
