/****************************************************************************/
/*                                                                          */
/*                                                                          */
/*      db_main.c     (c) 1987  Ken Harris                                  */
/*                                                                          */
/*                                                                          */
/****************************************************************************/
/*                                                                          */
/*      This software is made available on an AS-IS basis. Unrestricted     */
/*      use is granted provided that the copywrite notice remains intack.   */
/*      The author makes no warranties expressed or implied.                */
/*                                                                          */
/****************************************************************************/

#include <stdio.h>
#ifdef MSC
#include <fcntl.h>
#include <sys\types.h>
#include <sys\stat.h>
#endif
#include "db.h"

static char  *copyright = "db V1.2 (c) 1987 Ken Harris";

int    db_error     = 0;
ulong  db_match_blk = 0;
ushort db_match_rec = 0;
ulong  db_add_blk   = 0;
ushort db_add_rec   = 0;

/*
 *      db_create  -  Create a New Data Set
 */

DATA_SET db_create(path, fname, fhdr)
 char *path, *fname;               
 FILE_HDR fhdr;
{
        BUFFER db_alloc_buf();
        DATA_SET ds;
        char  *fname_dflts(), *calloc();

        db_error = 0;

        db_fhdr_create(fhdr);
        if (db_error) return(NULL);

        ds = (DATA_SET) calloc(1, sizeof(struct db_data_set));

        strcpy(ds->ds_fname, fname_dflts(fname,path));

#ifdef MSC
	ds->ds_fd = open(ds->ds_fname, O_CREAT|O_TRUNC|O_RDWR|O_BINARY, S_IREAD|S_IWRITE);
#endif
#ifdef CI86
        ds->ds_fd = creat(ds->ds_fname, BUPDATE);
#endif    
        if (ds->ds_fd < 0)
        {       db_error = DB_FILE_NOT_CREATED;
                db_free_ds(ds);
                return(NULL);
        }                       

        ds->ds_stat = DB_OPEN;

        ds->ds_fhdr               = db_alloc_buf(0);
        ds->ds_fhdr->buf_size     = sizeof(struct db_file_hdr);
        ds->ds_fhdr->buf_data     = (char*) fhdr;
        ds->ds_fhdr->buf_cur_size = sizeof(struct db_file_hdr);

        if (fhdr->fh_file_type == DB_INDEX)
        {    ds->ds_buf = db_alloc_buf(fhdr->fh_block_size + 2 * fhdr->fh_rec_size);
             ds->ds_tmp = db_alloc_buf(fhdr->fh_block_size + 2 * fhdr->fh_rec_size);
             ds->ds_aux = db_alloc_buf(fhdr->fh_block_size + 2 * fhdr->fh_rec_size);
             ds->ds_buf->buf_size = fhdr->fh_block_size;
             ds->ds_tmp->buf_size = fhdr->fh_block_size;
             ds->ds_aux->buf_size = fhdr->fh_block_size;
        }
        else
        if (fhdr->fh_file_type == DB_RANDOM)
        {    ds->ds_buf = db_alloc_buf(fhdr->fh_block_size);
             ds->ds_tmp = db_alloc_buf(fhdr->fh_block_size);
             while (fhdr->fh_last_block < fhdr->fh_base_size)
             {  db_extend(ds, ds->ds_buf);
		if (db_error) return(NULL);

                db_put_blk(ds, ds->ds_buf);
             }
        }
        else
             ds->ds_buf = db_alloc_buf(fhdr->fh_block_size);

        db_put_blk(ds, ds->ds_fhdr);
        if (db_error)
        {       db_free_ds(ds);
                return(NULL);
        }

        return(ds);
}

/*
 *      db_fhdr_create - Check file header data on create
 */

void db_fhdr_create(fh)
 FILE_HDR fh;
{       short hdr_size;

        db_error = 0;

        fh->fh_db_version = DB_VERSION;

        if (fh->fh_file_type != DB_SEQ    &&
            fh->fh_file_type != DB_RANDOM &&
            fh->fh_file_type != DB_INDEX)
        {       db_error = DB_INVALID_FHDR;
                return;
        }

	fh->fh_file_stat &= ~DB_OPEN;
        fh->fh_last_block = 0;

        if (!fh->fh_block_size) fh->fh_block_size = 512;

        switch (fh->fh_file_type)
        {       case DB_SEQ:
                     hdr_size = 0;
                     if (!fh->fh_ctl_size) fh->fh_ctl_size = 1;
                     break;

                case DB_RANDOM:
                     hdr_size = sizeof(struct db_random_hdr);
                     if (!fh->fh_base_size)
                     {       db_error = DB_INVALID_FHDR;
                             return;
                     }
                     if (!fh->fh_ctl_size) fh->fh_ctl_size = 1;
                     break;

                case DB_INDEX:
                     hdr_size = sizeof(struct db_index_hdr);
                     if (!fh->fh_ctl_size) fh->fh_ctl_size = 2;
                     break;

                default:
                     hdr_size = 0;
                     break;
        }

        fh->fh_rec_size     = fh->fh_ctl_size + fh->fh_data_size;

	if (fh->fh_block_size < fh->fh_rec_size + hdr_size)
		fh->fh_block_size = fh->fh_rec_size + hdr_size;

	fh->fh_block_size = ((fh->fh_block_size + 511) / 512) * 512;

        fh->fh_rec_cnt      = 0;
        fh->fh_recs_per_blk = (fh->fh_block_size - hdr_size) / fh->fh_rec_size;
        fh->fh_root_ptr     = 0;
        fh->fh_next_avail   = 0;

        if (fh->fh_key_size > fh->fh_data_size)
        {       db_error = DB_INVALID_FHDR;
                return;
        }
}

/*
 *      db_open  -  Open a Data Set
 */

DATA_SET db_open(path,fname)
 char *path, *fname;
{
        BUFFER   db_alloc_buf();
        DATA_SET ds;
        FILE_HDR fhdr;
        char    *fname_dflts(), *calloc();
        int      cnt;

        db_error = 0;
        ds = (DATA_SET) calloc(1, sizeof(struct db_data_set));
        strcpy(ds->ds_fname, fname_dflts(fname,path));

#ifdef MSC
        ds->ds_fd = open(ds->ds_fname, O_RDWR|O_BINARY);
#endif
#ifdef CI86
        ds->ds_fd = open(ds->ds_fname, BUPDATE);
#endif
        if (ds->ds_fd < 0)
        {       db_error = DB_FILE_NOT_FOUND;
                db_free_ds(ds);
                return(NULL);
        }
                
        ds->ds_stat = DB_OPEN;

        ds->ds_fhdr = db_alloc_buf(sizeof(struct db_file_hdr));
        fhdr = (FILE_HDR) ds->ds_fhdr->buf_data;
            
        db_get_blk(ds, 0L, ds->ds_fhdr);
        if (db_error)
        {       db_free_ds(ds);
                return(NULL);
        }
        
        db_fhdr_open(fhdr);
        if (db_error) return(NULL);

        if (fhdr->fh_file_type == DB_INDEX)
        {    ds->ds_buf = db_alloc_buf(fhdr->fh_block_size + 2 * fhdr->fh_rec_size);
             ds->ds_tmp = db_alloc_buf(fhdr->fh_block_size + 2 * fhdr->fh_rec_size);
             ds->ds_aux = db_alloc_buf(fhdr->fh_block_size + 2 * fhdr->fh_rec_size);
             ds->ds_buf->buf_size = fhdr->fh_block_size;
             ds->ds_tmp->buf_size = fhdr->fh_block_size;
             ds->ds_aux->buf_size = fhdr->fh_block_size;
        }
        else
        if (fhdr->fh_file_type == DB_RANDOM)
        {    ds->ds_buf = db_alloc_buf(fhdr->fh_block_size);
             ds->ds_tmp = db_alloc_buf(fhdr->fh_block_size);
        }
        else
             ds->ds_buf = db_alloc_buf(fhdr->fh_block_size);

        return(ds);
}

/*
 *      db_fhdr_open - Check file header data on open
 */

void db_fhdr_open(fh)
 FILE_HDR fh;
{
        db_error = 0;

        if (fh->fh_db_version != DB_VERSION)
        {       db_error = DB_VERSION_ERROR;
                return;
        }

        if (fh->fh_file_type != DB_SEQ    &&
            fh->fh_file_type != DB_RANDOM &&
            fh->fh_file_type != DB_INDEX)
        {       db_error = DB_INVALID_FHDR;
                return;
        }

        if (fh->fh_block_size   == 0 ||
            fh->fh_rec_size     == 0 ||
            fh->fh_recs_per_blk == 0)
        {       db_error = DB_INVALID_FHDR;
                return;
        }

        if (fh->fh_rec_size != fh->fh_ctl_size + fh->fh_data_size)
        {       db_error = DB_INVALID_FHDR;
                return;
        }

        if (fh->fh_key_size > fh->fh_data_size)
        {       db_error = DB_INVALID_FHDR;
                return;
        }

        if (fh->fh_file_type == DB_RANDOM &&
            fh->fh_base_size == 0)
        {       db_error = DB_INVALID_FHDR;
                return;
        }
}

/*
 *      db_close  -  close a data set
 */

DATA_SET db_close(ds)
 DATA_SET ds;
{
        db_error = 0;

        db_check_ds(ds);
	if (db_error) return(NULL);

        db_put_blk(ds, ds->ds_fhdr);
	if (db_error) return(NULL);

        close(ds->ds_fd);

        db_free_ds(ds);

        return(NULL);
}

/*
 *      db_add  -  Add a new record to a data set
 */

ulong db_add(ds, user_data)
 DATA_SET ds;
 char *user_data;
{
        FILE_HDR fh;
        BUFFER  buf;
        ulong   rec_no;

        db_error = 0;

        db_check_ds(ds);
        if (db_error) return(0);

        fh  = (FILE_HDR) ds->ds_fhdr->buf_data;
        buf = (BUFFER) ds->ds_buf;

        switch (fh->fh_file_type)
        {       case DB_SEQ:
                        db_add_seq(ds, user_data);
                        break;

                case DB_INDEX:
                        db_add_idx(ds, user_data);
                        break;

                case DB_RANDOM:
                        db_add_ran(ds, user_data);
                        break;

                default:
                        db_error = DB_INVALID_REQUEST;
                        break;
        }

        if (db_error) return(0);

        rec_no = (db_add_blk - 1) * fh->fh_recs_per_blk + db_add_rec;

        buf->buf_cur_blk = 0;
        buf->buf_rec_inx = 0;

        return(rec_no);
}

/*
 *      db_read_first  -  Read First Record in a data set
 */

void db_read_first(ds, user_data)
 DATA_SET ds;
 char *user_data;
{
        FILE_HDR  fh;
        BUFFER   buf;

        db_error = 0;

        db_check_ds(ds);
        if (db_error) return;

        fh  = (FILE_HDR) ds->ds_fhdr->buf_data;
        buf = (BUFFER) ds->ds_buf;

        if (!fh->fh_rec_cnt)
        {       db_error = DB_END_OF_FILE;
                return;
        }

        switch (fh->fh_file_type)
        {       case DB_SEQ:
                        db_read_first_seq(ds, user_data);
                        break;

                case DB_INDEX:
                        db_read_first_idx(ds, fh->fh_root_ptr, user_data);
                        break;

                case DB_RANDOM:
                        db_read_first_ran(ds, user_data);
                        break;

                default:
                        db_error = DB_INVALID_REQUEST;
                        break;
        }

        if (db_error)
        {       buf->buf_cur_blk = 0;
                buf->buf_rec_inx = 0;
        }

        ds->ds_prev_blk = buf->buf_cur_blk;
        ds->ds_prev_rec = buf->buf_rec_inx;

	return;
}

/*
 *      db_read_next  -  Read next record from a data set
 */

void db_read_next(ds, user_data)
 DATA_SET ds;
 char *user_data;
{
        FILE_HDR  fh;
        BUFFER   buf;

        db_error = 0;

        db_check_ds(ds);
        if (db_error) return;

        fh  = (FILE_HDR) ds->ds_fhdr->buf_data;
        buf = (BUFFER) ds->ds_buf;

        if (!fh->fh_rec_cnt)
        {       db_error = DB_END_OF_FILE;
                return;
        }

        if (!ds->ds_prev_blk)
        {       db_error = DB_NO_CURRENT_REC;
                return;
        }

        switch (fh->fh_file_type)
        {       case DB_SEQ:
                        db_read_next_seq(ds, user_data);
                        break;

                case DB_INDEX:
                        db_read_next_idx(ds, user_data);
                        break;
                              
                case DB_RANDOM:
                        db_read_next_ran(ds, user_data);
                        break;

                default:
                        db_error = DB_INVALID_REQUEST;
                        break;
        }

        if (db_error)
        {       buf->buf_cur_blk = 0;
                buf->buf_rec_inx = 0;
        }

        ds->ds_prev_blk = buf->buf_cur_blk;
        ds->ds_prev_rec = buf->buf_rec_inx;

	return;
}
 
/*
 *	db_read_last  -  Read Last Record in a data set
 */

void db_read_last(ds, user_data)
 DATA_SET ds;
 char *user_data;
{
        FILE_HDR  fh;
        BUFFER   buf;

        db_error = 0;

        db_check_ds(ds);
        if (db_error) return;

        fh  = (FILE_HDR) ds->ds_fhdr->buf_data;
        buf = (BUFFER) ds->ds_buf;

        if (!fh->fh_rec_cnt)
        {       db_error = DB_END_OF_FILE;
                return;
        }

        switch (fh->fh_file_type)
        {       case DB_SEQ:
			db_read_last_seq(ds, user_data);
                        break;

                case DB_INDEX:
			db_read_last_idx(ds, fh->fh_root_ptr, user_data);
                        break;

                case DB_RANDOM:
			db_read_last_ran(ds, user_data);
                        break;

                default:
                        db_error = DB_INVALID_REQUEST;
                        break;
        }

        if (db_error)
        {       buf->buf_cur_blk = 0;
                buf->buf_rec_inx = 0;
        }

        ds->ds_prev_blk = buf->buf_cur_blk;
        ds->ds_prev_rec = buf->buf_rec_inx;

	return;
}

/*
 *	db_read_prev  -  Read prev record from a data set
 */

void db_read_prev(ds, user_data)
 DATA_SET ds;
 char *user_data;
{
        FILE_HDR  fh;
        BUFFER   buf;

        db_error = 0;

        db_check_ds(ds);
        if (db_error) return;

        fh  = (FILE_HDR) ds->ds_fhdr->buf_data;
        buf = (BUFFER) ds->ds_buf;

        if (!fh->fh_rec_cnt)
        {       db_error = DB_END_OF_FILE;
                return;
        }

        if (!ds->ds_prev_blk)
        {       db_error = DB_NO_CURRENT_REC;
                return;
        }

        switch (fh->fh_file_type)
        {       case DB_SEQ:
			db_read_prev_seq(ds, user_data);
                        break;

                case DB_INDEX:
			db_read_prev_idx(ds, user_data);
                        break;
                              
                case DB_RANDOM:
			db_read_prev_ran(ds, user_data);
                        break;

                default:
                        db_error = DB_INVALID_REQUEST;
                        break;
        }

        if (db_error)
        {       buf->buf_cur_blk = 0;
                buf->buf_rec_inx = 0;
        }

        ds->ds_prev_blk = buf->buf_cur_blk;
        ds->ds_prev_rec = buf->buf_rec_inx;

	return;
}

/*
 *      db_find  -  Find a Record
 */

void db_find(ds, user_data, key, key_size)
 DATA_SET ds;
 char *user_data;
 char *key;
 int   key_size;
{
        FILE_HDR  fh;
        BUFFER   buf;

        db_error = 0;

        db_check_ds(ds);
        if (db_error) return;

        fh  = (FILE_HDR) ds->ds_fhdr->buf_data;
        buf = (BUFFER) ds->ds_buf;

        if (!fh->fh_rec_cnt)
        {       db_error = DB_REC_NOT_FOUND;
                return;
        }

        if (!key_size) key_size = fh->fh_key_size;

        switch (fh->fh_file_type)
        {       case DB_INDEX:
                        db_find_first_idx(ds, user_data, key, key_size);
                        break;

                case DB_RANDOM:
                        db_find_ran(ds, user_data, key);
                        break;

                default:
                        db_error = DB_INVALID_REQUEST;
                        break;
        }

        if (db_error)
        {       buf->buf_cur_blk = 0;
                buf->buf_rec_inx = 0;
        }

        ds->ds_prev_blk = buf->buf_cur_blk;
        ds->ds_prev_rec = buf->buf_rec_inx;

	return;
}

/*
 *      db_update  -  Update a record from a data set
 */

void db_update(ds, user_data)
 DATA_SET ds;
 char *user_data;
{
        FILE_HDR  fh;
        BUFFER   buf;

        db_error = 0;

        db_check_ds(ds);
        if (db_error) return;

        fh  = (FILE_HDR) ds->ds_fhdr->buf_data;
        buf = (BUFFER) ds->ds_buf;

        if (!buf->buf_cur_blk || !buf->buf_rec_inx)
        {       db_error = DB_NO_CURRENT_REC;
                return;
        }

        switch (fh->fh_file_type)
        {       case DB_SEQ:
                        db_update_seq(ds, user_data);
                        break;

                case DB_RANDOM:
                        db_update_ran(ds, user_data);
                        break;

                case DB_INDEX:
                        db_update_idx(ds, user_data);
                        break;

                default:
                        db_error = DB_INVALID_REQUEST;
                        break;
        }

        buf->buf_cur_blk = 0;
        buf->buf_rec_inx = 0;

	return;
}

/*
 *      db_delete  -  Delete a record from a data set
 */

void db_delete(ds)
 DATA_SET ds;
{
        FILE_HDR  fh;
        BUFFER   buf;

        db_error = 0;

        db_check_ds(ds);
        if (db_error) return;

        fh  = (FILE_HDR) ds->ds_fhdr->buf_data;
        buf = (BUFFER) ds->ds_buf;

        if (!buf->buf_cur_blk || !buf->buf_rec_inx)
        {       db_error = DB_NO_CURRENT_REC;
                return;
        }

        switch (fh->fh_file_type)
        {       case DB_SEQ:
                        db_free_rec(ds, buf);
                        if (db_error) return;

                        if (fh->fh_rec_cnt > 0) fh->fh_rec_cnt--;
                        db_put_blk(ds, ds->ds_fhdr);
                        break;

                case DB_INDEX:
                        db_delete_idx(ds);
                        break;

                case DB_RANDOM:
                        db_delete_ran(ds);
                        break;

                default:
                        db_error = DB_INVALID_REQUEST;
                        break;
        }

        buf->buf_cur_blk = 0;
        buf->buf_rec_inx = 0;

	return;
}

/*
 *      db_get_rec_no  -  Get relative record number
 */

ulong db_get_rec_no(ds)
 DATA_SET ds;
{
        FILE_HDR  fh;
        BUFFER   buf;

        db_error = 0;

        db_check_ds(ds);
        if (db_error) return(0);

        fh  = (FILE_HDR) ds->ds_fhdr->buf_data;
        buf = ds->ds_buf;

        if (!buf->buf_cur_blk || !buf->buf_rec_inx)
        {       db_error = DB_NO_CURRENT_REC;
                return(0);
        }
 
        return((long)(buf->buf_cur_blk - 1) * fh->fh_recs_per_blk
                                                         + buf->buf_rec_inx);
}

/*
 *      db_read_direct  -  Read a record by number
 */                                               

void db_read_direct(ds, rec_no, user_data)
 DATA_SET     ds;
 ulong    rec_no;
 char  *user_data;
{
        FILE_HDR    fh;
        BUFFER      buf;
        INDEX_HDR   ihdr;
        RANDOM_HDR  rhdr;
        RANDOM_REC  rrec;
        SEQ_REC     seq;
        ulong       blk;
        ushort      rec;
        char       *rbuf;

        db_error = 0;
                                       
        db_check_ds(ds);
	if (db_error) return;

        fh  = (FILE_HDR) ds->ds_fhdr->buf_data;
        buf = ds->ds_buf;

        buf->buf_cur_blk = 0;
        buf->buf_rec_inx = 0;

        blk = rec_no / fh->fh_recs_per_blk;
        rec = rec_no % fh->fh_recs_per_blk;

        if (rec == 0)
                rec = fh->fh_recs_per_blk;
        else
                blk++;

        db_get_blk(ds, blk, buf);
	if (db_error) return;

        switch (fh->fh_file_type)
        {       case DB_SEQ:
                        rbuf = buf->buf_data + (rec-1) * fh->fh_rec_size;
                        seq  = (SEQ_REC) rbuf;
                        
                        if (seq->seq_stat != DB_INUSE)
                        {       db_error = DB_DELETED_REC;
				return;
                        }
                        break;

                case DB_INDEX:
                        ihdr = (INDEX_HDR) buf->buf_data;
                        
                        if (ihdr->idx_stat != DB_INUSE)
                        {       db_error = DB_DELETED_REC;
				return;
                        }

                        if (rec > ihdr->idx_rec_cnt)
                        {       db_error = DB_DELETED_REC;
				return;
                        }

                        rbuf = buf->buf_data + sizeof(struct db_index_hdr)
                                                + (rec - 1) * fh->fh_rec_size;
                        break;

                case DB_RANDOM:
                        rhdr = (RANDOM_HDR) buf->buf_data;
                        
                        if (rhdr->ran_stat != DB_INUSE)
                        {       db_error = DB_DELETED_REC;
				return;
                        }

                        if (rec > rhdr->ran_rec_cnt)
                        {       db_error = DB_DELETED_REC;
				return;
                        }

                        rbuf = buf->buf_data + sizeof(struct db_random_hdr)
                                                + (rec - 1) * fh->fh_rec_size;

                        rrec = (RANDOM_REC) rbuf;
                        if (rrec->ran_stat != DB_INUSE)
                        {       db_error = DB_DELETED_REC;
				return;
                        }

                        break;

                default:
                        db_error = DB_INVALID_REQUEST;
			return;
        }
        memcpy(user_data, rbuf + fh->fh_ctl_size, fh->fh_data_size);

        ds->ds_prev_blk = buf->buf_cur_blk = blk;
        ds->ds_prev_rec = buf->buf_rec_inx = rec;

	return;
}

/*
 *      db_read_atr  - Read Data Set Attribute Block
 */

void db_read_atr(ds, user_data)
 DATA_SET ds;
 char  *user_data;
{        
        FILE_HDR fh;

        db_error = 0;

        db_check_ds(ds);
        if (db_error) return;

        fh = (FILE_HDR) ds->ds_fhdr->buf_data;

        memcpy(user_data, fh->fh_user_data, fh->fh_atr_size);

	return;
}

/*
 *      db_update_atr  - Update Data Set Attribute Block
 */

void db_update_atr(ds, user_data)
 DATA_SET ds;
 char  *user_data;
{
        FILE_HDR fh;

        db_error = 0;

        db_check_ds(ds);
        if (db_error) return;

        fh = (FILE_HDR) ds->ds_fhdr->buf_data;

        memcpy(fh->fh_user_data, user_data, fh->fh_atr_size);

        db_put_blk(ds, ds->ds_fhdr);

	return;
}

/*
 *      db_check_ds  -  Common checks on Data Set Validity
 */                                                       

void db_check_ds(ds)
 DATA_SET ds;
{
        FILE_HDR fh;

        db_error = 0;

        if (!ds)
        {       db_error = DB_FILE_NOT_OPEN;
                return;
        }

        if (!ds->ds_stat & DB_OPEN)
        {       db_error = DB_FILE_NOT_OPEN;
                return;
        }

        if (!ds->ds_fhdr || !ds->ds_buf)
        {       db_error = DB_BUFFER_ERROR;
                return;
        }

        fh = (FILE_HDR) ds->ds_fhdr->buf_data;

        if (fh->fh_file_type == DB_INDEX)
                if (!ds->ds_tmp || !ds->ds_aux)
                {       db_error = DB_BUFFER_ERROR;
                        return;
                }
}

/*
 *      db_extend  -  Extend a data set by 1 Block
 */

void db_extend(ds, buf)
 DATA_SET ds;
 BUFFER   buf;
{
        FILE_HDR  fhdr;
        long       rec;
        char        *c;
        FREE_REC   fre;
        RANDOM_HDR ran;
        SEQ_REC    seq;
        int        cnt;


        fhdr                = (FILE_HDR) ds->ds_fhdr->buf_data;
        buf                 = ds->ds_buf;
        fhdr->fh_last_block = fhdr->fh_last_block + 1;
        buf->buf_cur_blk    = fhdr->fh_last_block;
        buf->buf_cur_size   = fhdr->fh_block_size;

        memset(buf->buf_data, 0, buf->buf_cur_size);

        switch(fhdr->fh_file_type)
        {       case DB_SEQ:     
                        rec = buf->buf_cur_blk * fhdr->fh_recs_per_blk;
                        c   = buf->buf_data + (fhdr->fh_recs_per_blk - 1)
                                                        * fhdr->fh_rec_size;

                        for (cnt=0; cnt < fhdr->fh_recs_per_blk; cnt++)
                        {       fre                 = (FREE_REC) c;
                                fre->fre_stat       = DB_FREE;
                                fre->fre_next       = fhdr->fh_next_avail;
                                fhdr->fh_next_avail = rec--;
                                c                  -= fhdr->fh_rec_size;
                        }
                        break;

                case DB_RANDOM:
                        c = buf->buf_data + sizeof(struct db_random_hdr);
                        for (cnt=0; cnt < fhdr->fh_recs_per_blk; cnt++)
                        {       *c  = DB_FREE;
                                 c += fhdr->fh_rec_size;
                        }
                        if (buf->buf_cur_blk <= fhdr->fh_base_size)
                        {       ran = (RANDOM_HDR) buf->buf_data;
                                ran->ran_stat    = DB_INUSE;
                                ran->ran_next    = 0;
                                ran->ran_rec_cnt = 0;
                        }
                        else
                        {       fre = (FREE_REC) buf->buf_data;
                                fre->fre_stat = DB_FREE;
                                fre->fre_next = fhdr->fh_next_avail;
                                fhdr->fh_next_avail = buf->buf_cur_blk;
                        }
                        break;

                case DB_INDEX:
                        fre = (FREE_REC) buf->buf_data;
                        fre->fre_stat = DB_FREE;               
                        fre->fre_next = fhdr->fh_next_avail;
                        fhdr->fh_next_avail = buf->buf_cur_blk;
                        break;
        }

        db_put_blk(ds,ds->ds_buf);
        db_put_blk(ds,ds->ds_fhdr);
}

/*
 *      db_get_next_avail  -  Get Next Available Block
 */

void db_get_next_avail(ds, buf)
 DATA_SET ds;
 BUFFER  buf;
{
        FILE_HDR     fh;
        FREE_REC    fre;
        RANDOM_HDR rhdr;
        INDEX_HDR  ihdr;
        ulong       blk;
        ushort      rec;
        char      *rbuf;

        db_error = 0;

        fh  = (FILE_HDR) ds->ds_fhdr->buf_data;

        if (!fh->fh_next_avail)
        {       db_extend(ds, buf);
                if (db_error) return;
        }

        if (fh->fh_file_type == DB_SEQ)
        {       blk = fh->fh_next_avail / fh->fh_recs_per_blk;
                rec = fh->fh_next_avail % fh->fh_recs_per_blk;

                if (rec==0)
                        rec = fh->fh_recs_per_blk;
                else
                        blk++;
        }
        else
        {       blk = fh->fh_next_avail;
                rec = 1;
        }

        db_get_blk(ds, blk, buf);
        if (db_error) return;

        buf->buf_rec_inx = rec;
        rbuf = buf->buf_data + (rec - 1) * fh->fh_rec_size;

        fre = (FREE_REC) rbuf;
        if (fre->fre_stat != DB_FREE)
        {       db_error = DB_INVALID_FREE;
                return;
        }

        fre->fre_stat     = DB_INUSE;
        fh->fh_next_avail = fre->fre_next;

        if (fh->fh_file_type == DB_INDEX)
                if (!fh->fh_root_ptr)
                        fh->fh_root_ptr = blk;

        switch (fh->fh_file_type)
        {       case DB_SEQ:
                        break;

                case DB_RANDOM:
                        rhdr = (RANDOM_HDR) rbuf;
                        rhdr->ran_next = 0;
                        rhdr->ran_rec_cnt = 0;
                        break;

                case DB_INDEX:
                        ihdr = (INDEX_HDR) rbuf;
                        ihdr->idx_parent = 0;
                        ihdr->idx_rec_cnt = 0;
                        break;
        }
}

/*
 *      db_free_rec  -  Free a deleted record
 */

void db_free_rec(ds, buf)
 DATA_SET ds;
 BUFFER   buf;
{
        FILE_HDR  fh;
        FREE_REC fre;

        db_error = 0;

        fh = (FILE_HDR) ds->ds_fhdr->buf_data;

        switch (fh->fh_file_type)
        {       case DB_SEQ:
                        fre = (FREE_REC) (buf->buf_data 
                                + (buf->buf_rec_inx - 1) * fh->fh_rec_size);
                        memset(fre, 0, fh->fh_rec_size);
                        fre->fre_stat     = DB_FREE;
                        fre->fre_next     = fh->fh_next_avail;
                        fh->fh_next_avail = (long) (buf->buf_cur_blk - 1)
                                  * fh->fh_recs_per_blk + buf->buf_rec_inx;
                        break;

                case DB_RANDOM:
                        fre = (FREE_REC) buf->buf_data;
                        fre->fre_stat     = DB_FREE;
                        fre->fre_next     = fh->fh_next_avail;
                        fh->fh_next_avail = buf->buf_cur_blk;
                        break;                               

                case DB_INDEX:
                        fre = (FREE_REC) buf->buf_data;
                        memset(fre, 0, fh->fh_block_size);
                        fre->fre_stat     = DB_FREE;
                        fre->fre_next     = fh->fh_next_avail;
                        fh->fh_next_avail = buf->buf_cur_blk;
                        break;                               
        }
        db_put_blk(ds, buf);
        if (db_error) return;
}

/*
 *      db_get_blk  -  Get a Block from a Data Set
 */

void db_get_blk(ds,blk,buf)
 DATA_SET ds;         
 long     blk;
 BUFFER   buf;
{
        FILE_HDR  fhdr;
        long psn, lseek();
        int cnt;          
                                
        db_error = 0;

        if (!ds)
        {       db_error = DB_FILE_NOT_OPEN;
                return;
        }

        if (!ds->ds_stat & DB_OPEN)
        {       db_error = DB_FILE_NOT_OPEN;
                return;
        }
        
        if (!buf)
        {       db_error = DB_BUFFER_ERROR;
                return;
        }

        fhdr = (FILE_HDR) ds->ds_fhdr->buf_data;
                      
        if (blk == 0)
                psn = 0;
        else
                psn = sizeof(struct db_file_hdr) 
                    + (blk-1) * fhdr->fh_block_size;

        buf->buf_cur_blk  = 0;
        buf->buf_cur_size = 0;

        if (lseek(ds->ds_fd, psn, 0) < 0)
        {       db_error = DB_INVALID_BLOCK;
                return;
        }

        cnt = read(ds->ds_fd, buf->buf_data, buf->buf_size);
        if (cnt < 0)
        {       db_error = DB_READ_ERROR;
                return;
        }

        if (cnt == 0)
        {       db_error = DB_END_OF_FILE;
                return;
        }            

        buf->buf_cur_blk  = blk;
        buf->buf_cur_size = cnt;
}

/*
 *      db_put_blk  -  Put a Block out to a Data Set
 */

void db_put_blk(ds,buf)
 DATA_SET ds;         
 BUFFER   buf;
{
        FILE_HDR  fhdr;
        long psn, lseek();
        int cnt;          
                                
        db_error = 0;

        if (!ds)
        {       db_error = DB_FILE_NOT_OPEN;
                return;
        }

        if (!ds->ds_stat & DB_OPEN)
        {       db_error = DB_FILE_NOT_OPEN;
                return;
        }
        
        if (!buf)
        {       db_error = DB_BUFFER_ERROR;
                return;
        }

        if (buf->buf_cur_size == 0)
        {       db_error = DB_INVALID_BLK_SIZE;
                return;
        }

        fhdr = (FILE_HDR) ds->ds_fhdr->buf_data;

        if (buf->buf_cur_blk == 0)
                psn = 0;
        else
                psn  = sizeof(struct db_file_hdr)
                     + (buf->buf_cur_blk - 1) * fhdr->fh_block_size;

        if (lseek(ds->ds_fd, psn, 0) < 0)
        {       db_error = DB_INVALID_BLOCK;
                return;
        }

        cnt = write(ds->ds_fd, buf->buf_data, buf->buf_cur_size);
        if (cnt != buf->buf_cur_size)
        {       db_error = DB_WRITE_ERROR;
                return;
        }
}

/*
 *      db_alloc_buf  -  Get a Buffer
 */

BUFFER db_alloc_buf(size)
 int size;
{
        BUFFER  buf;
        char   *calloc();

        buf = (BUFFER) calloc(1, sizeof(struct db_data_buf));

        if (size)
        {       buf->buf_size  = size;
                buf->buf_data  = (char *) calloc(1, size);
        }

        return(buf);
}



/*
 *      db_free_buf  -  Free a Buffer
 */

BUFFER db_free_buf(buf)
 BUFFER buf;
{      
        BUFFER prev,next;

	if (!buf) return(NULL);

	if (!buf->buf_data) return(NULL);
                    
        prev = buf->buf_prev;
        next = buf->buf_next;

        if (prev) prev->buf_next = buf->buf_next;

        if (next) next->buf_prev = buf->buf_prev;

        free(buf->buf_data);
        free(buf);
        
        return(next);
}

/*
 *      db_free_ds  -  Free all dynamic structures for a Data Set
 */

void db_free_ds(ds)
 DATA_SET ds;
{
        if (!ds) return;

        if (ds->ds_fhdr)
                db_free_buf(ds->ds_fhdr);

        while (ds->ds_buf)
                ds->ds_buf = db_free_buf(ds->ds_buf);

        if (ds->ds_tmp)
                db_free_buf(ds->ds_tmp);

        if (ds->ds_aux)
                db_free_buf(ds->ds_aux);

	return;
}

/*
 *      db_error_msg  -  Return pointer to error message text
 */

static char *db_error_msgs[] =
{"",
 "DB-Data Set Not Found",    /*  1 */
 "DB-Read Error",            /*  2 */
 "DB-End Of File",           /*  3 */
 "DB-Write Error",           /*  4 */
 "DB-Data Set Not Created",  /*  5 */
 "DB-Data Set Not Open",     /*  6 */
 "DB-Invalid Block",         /*  7 */
 "DB-Buffer Error",          /*  8 */
 "DB-No Current Record",     /*  9 */
 "DB-Record Deleted",        /* 10 */
 "DB-Free List Error",       /* 11 */
 "DB-Invalid Block Size",    /* 12 */
 "DB-Index File Corrupted",  /* 13 */
 "DB-Record Not Found",      /* 14 */
 "DB-Duplicate Key",         /* 15 */
 "DB-Invalid Request",       /* 16 */
 "DB-Random File Corrupted", /* 17 */
 "DB-Invalid File Header",   /* 18 */
 "DB-File Version Mismatch", /* 19 */
 ""};

char *db_error_msg(error)
 int error;
{
        return(db_error_msgs[error]);
}

/*
 *		memcpy - copy a block of memory taking care not to overlap
 */

memcpy(dst, src, cnt)
 char *dst, *src;
 int   cnt;
{
	char *s, *d;

	if (dst > src  &&  dst < src+cnt)
	{	s = src + cnt - 1;
		d = dst + cnt - 1;
		while (cnt--) *d-- = *s--;
	}
	else
		while (cnt--) *dst++ = *src++;
}
				 
