/****************************************************************************/
/*                                                                          */
/*                                                                          */
/*      db_ran.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"

/*
 *      db_add_ran  -  Add a record to a random file
 */                                                 

void db_add_ran(ds, user_data)
 DATA_SET ds;
 char    *user_data;
{
        FILE_HDR   fh;
        RANDOM_HDR rhdr, thdr;
        RANDOM_REC rrec;
        BUFFER     buf, tmp;    
        short      hval;
        char      *rbuf;

        db_error = 0;

        fh   = (FILE_HDR) ds->ds_fhdr->buf_data;
        buf  = ds->ds_buf;
        tmp  = ds->ds_tmp;
        rhdr = (RANDOM_HDR) buf->buf_data;
        thdr = (RANDOM_HDR) tmp->buf_data;

        hval = db_hash_ran(ds, user_data);

        db_get_blk(ds, (long) hval, buf);
        if (db_error) return;

        if (rhdr->ran_stat != DB_INUSE)
        {       db_error = DB_INVALID_RANDOM;
                return;
        }

        if (db_search_blk_ran(ds, user_data, buf))
        {       db_error = DB_DUP_NOT_ALLOWED;
                return;
        }

        while (rhdr->ran_next)
        {       db_get_blk(ds, (long) rhdr->ran_next, buf);
                if (db_error) return;

                if (db_search_blk_ran(ds, user_data, buf))
                {       db_error = DB_DUP_NOT_ALLOWED;
                        return;
                }
        }

        tmp->buf_cur_blk = 0;
        if (rhdr->ran_rec_cnt >= fh->fh_recs_per_blk)
        {       buf        = ds->ds_tmp;
                tmp        = ds->ds_buf;
                ds->ds_buf = buf;
                ds->ds_tmp = tmp;
                rhdr       = (RANDOM_HDR) buf->buf_data;
                thdr       = (RANDOM_HDR) tmp->buf_data;

                db_get_next_avail(ds, buf);
                if (db_error) return;

                rhdr->ran_next = 0;
                thdr->ran_next = buf->buf_cur_blk;

        }

        rbuf = buf->buf_data + sizeof(struct db_random_hdr)
             + rhdr->ran_rec_cnt * fh->fh_rec_size;
        rrec = (RANDOM_REC) rbuf;

        if (rrec->ran_stat != DB_FREE)
        {       db_error = DB_INVALID_FREE;
                return;
        }
        memcpy(rbuf+fh->fh_ctl_size, user_data, fh->fh_data_size);
        rrec->ran_stat = DB_INUSE;

        rhdr->ran_rec_cnt += 1;
        db_add_blk = buf->buf_cur_blk;
        db_add_rec = rhdr->ran_rec_cnt;

        fh->fh_rec_cnt += 1;

        db_put_blk(ds, buf);
        if (db_error) return;

        if (tmp->buf_cur_blk) 
        {       db_put_blk(ds, tmp);
                if (db_error) return;
        }

        db_put_blk(ds, ds->ds_fhdr);
}

/*
 *      db_find_ran  -  Find a Random record
 */

void db_find_ran(ds, user_data, key)
 DATA_SET  ds;
 char     *user_data, *key;
{
        RANDOM_HDR rhdr;
        RANDOM_REC rrec;
        FILE_HDR   fh;
        BUFFER     buf;
        short      hval, rec;
        char      *rbuf;

        db_error = 0;

        fh   = (FILE_HDR) ds->ds_fhdr->buf_data;
        buf  = ds->ds_buf;
        rhdr = (RANDOM_HDR) buf->buf_data;

        hval = db_hash_ran(ds, key);

        while (hval)
        {       db_get_blk(ds, (long)hval, buf);
                if (db_error) return;

                if (rec = db_search_blk_ran(ds, key, buf))
                {       buf->buf_rec_inx = rec;
                        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_INVALID_RANDOM;
                                return;
                        }
                        memcpy(user_data, rbuf+fh->fh_ctl_size, fh->fh_data_size);
                        return;
                }
                hval = rhdr->ran_next;
        }
        db_error = DB_REC_NOT_FOUND;
        return;
}

/*
 *      db_read_first_ran - Read first record from a random file
 */

void db_read_first_ran(ds, user_data)
 DATA_SET  ds;
 char     *user_data;
{
        RANDOM_HDR  rhdr;
        RANDOM_REC  rrec;
        FILE_HDR    fh;
        BUFFER      buf;
        ulong       blk;
        char       *rbuf;

        db_error = 0;

        fh   = (FILE_HDR) ds->ds_fhdr->buf_data;
        buf  = ds->ds_buf;
        rhdr = (RANDOM_HDR) buf->buf_data;

        for (blk=1; blk <= fh->fh_last_block; blk++)
        {       db_get_blk(ds, blk, buf);
                if (db_error) return;

                if (rhdr->ran_stat == DB_INUSE && rhdr->ran_rec_cnt > 0)
                {       buf->buf_rec_inx = 1;
                        rbuf = buf->buf_data + sizeof(struct db_random_hdr);

                        rrec = (RANDOM_REC) rbuf;
                        if (rrec->ran_stat != DB_INUSE)
                        {       db_error = DB_INVALID_RANDOM;
                                return;
                        }
                        memcpy(user_data, rbuf+fh->fh_ctl_size, fh->fh_data_size);
                        return;
                }
        }
        db_error = DB_END_OF_FILE;
        return;
}

/*
 *      db_read_next_ran - Read next record from a random file
 */

void db_read_next_ran(ds, user_data)
 DATA_SET  ds;
 char     *user_data;
{
        RANDOM_HDR  rhdr;
        RANDOM_REC  rrec;
        FILE_HDR    fh;
        BUFFER      buf;
        ulong       blk;
        ushort      rec;
        char       *rbuf;

        db_error = 0;

        fh   = (FILE_HDR) ds->ds_fhdr->buf_data;
        buf  = ds->ds_buf;
        rhdr = (RANDOM_HDR) buf->buf_data;

        if (ds->ds_prev_rec < fh->fh_recs_per_blk)
        {       blk = ds->ds_prev_blk;
                rec = ds->ds_prev_rec;
        }
        else
        {       blk = ds->ds_prev_blk + 1;
                rec = 0;
        }

        for (; blk <= fh->fh_last_block; blk++, rec=0)
        {       db_get_blk(ds, blk, buf);
                if (db_error) return;
                                                   
                if (rhdr->ran_stat != DB_INUSE) continue;
                if (rhdr->ran_rec_cnt <= rec) continue;


                buf->buf_rec_inx = rec + 1;
                rbuf = buf->buf_data + sizeof(struct db_random_hdr)
                     + (buf->buf_rec_inx - 1) * fh->fh_rec_size;

                rrec = (RANDOM_REC) rbuf;
                if (rrec->ran_stat != DB_INUSE)
                {       db_error = DB_INVALID_RANDOM;
                        return;
                }
                memcpy(user_data, rbuf+fh->fh_ctl_size, fh->fh_data_size);
                return;
        }
        db_error = DB_END_OF_FILE;
        return;
}

/*
 *	db_read_last_ran - Read last record from a random file
 */

void db_read_last_ran(ds, user_data)
 DATA_SET  ds;
 char     *user_data;
{
        RANDOM_HDR  rhdr;
        RANDOM_REC  rrec;
        FILE_HDR    fh;
        BUFFER      buf;
        ulong       blk;
        char       *rbuf;

        db_error = 0;

        fh   = (FILE_HDR) ds->ds_fhdr->buf_data;
        buf  = ds->ds_buf;
        rhdr = (RANDOM_HDR) buf->buf_data;

	for (blk=fh->fh_last_block; blk >= 1; blk--)
        {       db_get_blk(ds, blk, buf);
                if (db_error) return;

                if (rhdr->ran_stat == DB_INUSE && rhdr->ran_rec_cnt > 0)
		{	buf->buf_rec_inx = rhdr->ran_rec_cnt;
			rbuf = buf->buf_data + sizeof(struct db_random_hdr)
			     + (rhdr->ran_rec_cnt - 1) * fh->fh_rec_size;

                        rrec = (RANDOM_REC) rbuf;
                        if (rrec->ran_stat != DB_INUSE)
                        {       db_error = DB_INVALID_RANDOM;
                                return;
                        }
                        memcpy(user_data, rbuf+fh->fh_ctl_size, fh->fh_data_size);
                        return;
                }
        }
        db_error = DB_END_OF_FILE;
        return;
}

/*
 *	db_read_prev_ran - Read next record from a random file
 */

void db_read_prev_ran(ds, user_data)
 DATA_SET  ds;
 char     *user_data;
{
        RANDOM_HDR  rhdr;
        RANDOM_REC  rrec;
        FILE_HDR    fh;
        BUFFER      buf;
        ulong       blk;
        ushort      rec;
        char       *rbuf;

        db_error = 0;

        fh   = (FILE_HDR) ds->ds_fhdr->buf_data;
        buf  = ds->ds_buf;
        rhdr = (RANDOM_HDR) buf->buf_data;

	if (ds->ds_prev_rec > 1)
        {       blk = ds->ds_prev_blk;
		rec = ds->ds_prev_rec - 1;
        }
        else
	{	blk = ds->ds_prev_blk - 1;
                rec = 0;
        }

	for (; blk >= 1; blk--, rec=0)
        {       db_get_blk(ds, blk, buf);
                if (db_error) return;
                                                   
                if (rhdr->ran_stat != DB_INUSE) continue;
		if (!rhdr->ran_rec_cnt) continue;

		if (rec)
			buf->buf_rec_inx = rec;
		else
			buf->buf_rec_inx = rhdr->ran_rec_cnt;

                rbuf = buf->buf_data + sizeof(struct db_random_hdr)
                     + (buf->buf_rec_inx - 1) * fh->fh_rec_size;

                rrec = (RANDOM_REC) rbuf;
                if (rrec->ran_stat != DB_INUSE)
                {       db_error = DB_INVALID_RANDOM;
                        return;
                }
                memcpy(user_data, rbuf+fh->fh_ctl_size, fh->fh_data_size);
                return;
        }
        db_error = DB_END_OF_FILE;
        return;
}


/*
 *      db_update_ran - Update a random record
 */

void db_update_ran(ds, user_data)
 DATA_SET  ds;
 char     *user_data;
{
        RANDOM_HDR  rhdr;
        RANDOM_REC  rrec;
        FILE_HDR    fh;
        BUFFER      buf;
        char       *rbuf;

        db_error = 0;

        fh   = (FILE_HDR) ds->ds_fhdr->buf_data;
        buf  = ds->ds_buf;
        rhdr = (RANDOM_HDR) buf->buf_data;

        if (rhdr->ran_rec_cnt < buf->buf_rec_inx)
        {       db_error = DB_DELETED_REC;
                return;
        }

        rbuf = buf->buf_data + sizeof(struct db_random_hdr)
             + (buf->buf_rec_inx - 1) * fh->fh_rec_size;

        rrec = (RANDOM_REC) rbuf;
        if (rrec->ran_stat != DB_INUSE)
        {       db_error = DB_INVALID_RANDOM;
                return;
        }
        memcpy(rbuf+fh->fh_ctl_size, user_data, fh->fh_data_size);
        db_put_blk(ds, buf);
        return;
}

/*
 *      db_delete_ran - Delete a random record
 */

void db_delete_ran(ds)
 DATA_SET ds;
{
        FILE_HDR    fh;
        RANDOM_HDR  rhdr,  thdr;
        RANDOM_REC  rrec,  trec;
        BUFFER      buf,   tmp;
        char       *rbuf, *tbuf;
        ulong       hval,  next;

        db_error = 0;

        fh   = (FILE_HDR) ds->ds_fhdr->buf_data;
        buf  = ds->ds_buf;
        tmp  = ds->ds_tmp;
        rhdr = (RANDOM_HDR) buf->buf_data;
        thdr = (RANDOM_HDR) tmp->buf_data;

        if (buf->buf_rec_inx > rhdr->ran_rec_cnt)
        {       db_error = DB_DELETED_REC;
                return;
        }

        rbuf = buf->buf_data + sizeof(struct db_random_hdr) + fh->fh_ctl_size;
        hval = db_hash_ran(ds, rbuf);

        ds->ds_prev_blk = buf->buf_cur_blk;
        ds->ds_prev_rec = buf->buf_rec_inx - 1;

        if (!rhdr->ran_next)
        {       if (buf->buf_rec_inx < rhdr->ran_rec_cnt)
                {       rbuf = buf->buf_data + sizeof(struct db_random_hdr)
                             + (buf->buf_rec_inx - 1) * fh->fh_rec_size;

                        tbuf = buf->buf_data + sizeof(struct db_random_hdr)
                             + (rhdr->ran_rec_cnt - 1) * fh->fh_rec_size;

                        rrec = (RANDOM_REC) rbuf;
                        if (rrec->ran_stat != DB_INUSE)
                        {       db_error = DB_INVALID_RANDOM;
                                return;
                        }
                        memcpy(rbuf, tbuf, fh->fh_rec_size);
                        memset(tbuf, 0, fh->fh_rec_size);

                        trec = (RANDOM_REC) tbuf;
                        trec->ran_stat = DB_FREE;
                }
                else
                if (buf->buf_rec_inx == rhdr->ran_rec_cnt)
                {       rbuf = buf->buf_data + sizeof(struct db_random_hdr)
                             + (buf->buf_rec_inx - 1) * fh->fh_rec_size;

                        rrec = (RANDOM_REC) rbuf;
                        if (rrec->ran_stat != DB_INUSE)
                        {       db_error = DB_INVALID_RANDOM;
                                return;
                        }

                        memset(rbuf, 0, fh->fh_rec_size);

                        rrec = (RANDOM_REC) rbuf;
                        rrec->ran_stat = DB_FREE;
                }

                rhdr->ran_rec_cnt--;
                db_put_blk(ds, buf);
                if (db_error) return;

                if (!rhdr->ran_rec_cnt)
                {       db_delete_blk_ran(ds, hval);
                        return;
                }
                return;
        }

        if (rhdr->ran_next)
        {       next = rhdr->ran_next;
                while (next)
                {       db_get_blk(ds, next, tmp);
                        if (db_error) return;

                        next = thdr->ran_next;
                }
                                      
                if (!thdr->ran_rec_cnt)
                {       db_error = DB_INVALID_RANDOM;
                        return;
                }

                rbuf = buf->buf_data + sizeof(struct db_random_hdr)
                     + (buf->buf_rec_inx - 1) * fh->fh_rec_size;

                tbuf = tmp->buf_data + sizeof(struct db_random_hdr)
                     + (thdr->ran_rec_cnt - 1) * fh->fh_rec_size;

                rrec = (RANDOM_REC) rbuf;
                if (rrec->ran_stat != DB_INUSE)
                {       db_error = DB_INVALID_RANDOM;
                        return;
                }
                memcpy(rbuf, tbuf, fh->fh_rec_size);
                memset(tbuf, 0, fh->fh_rec_size);

                trec = (RANDOM_REC) tbuf;
                trec->ran_stat = DB_FREE;

                thdr->ran_rec_cnt--;
                db_put_blk(ds, buf);
                if (db_error) return;

                db_put_blk(ds, tmp);
                if (db_error) return;

                if (!thdr->ran_rec_cnt)
                {       db_delete_blk_ran(ds, hval);
                        return;
                }
                return;
        }
}

/*
 *      db_delete_blk_ran - Delete empty overflow buckets
 *                          There should be at most one at the end of the chain.
 */

void db_delete_blk_ran(ds, base_blk)
 DATA_SET  ds;
 ulong     base_blk;
{
        FILE_HDR    fh;
        RANDOM_HDR  rhdr, thdr;
        BUFFER      buf,  tmp;
        ulong       next;

        db_error = 0;

        fh   = (FILE_HDR) ds->ds_fhdr->buf_data;
        buf  = ds->ds_buf;
        tmp  = ds->ds_tmp;
        rhdr = (RANDOM_HDR) buf->buf_data;
        thdr = (RANDOM_HDR) tmp->buf_data;

        buf->buf_cur_blk = 0;
        tmp->buf_cur_blk = 0;

        next = base_blk;
        while (next)
        {       db_get_blk(ds, next, tmp);
                if (db_error) return;

                next = thdr->ran_next;

                if (!thdr->ran_rec_cnt)
                {       if (buf->buf_cur_blk)
                        {       rhdr->ran_next = next;
                                db_put_blk(ds, buf);
                                if (db_error) return;
                        }
                        if (tmp->buf_cur_blk > fh->fh_base_size)
                        {       db_free_rec(ds, tmp);
                                continue;
                        }
                }

                buf        = ds->ds_tmp;
                tmp        = ds->ds_buf;
                ds->ds_buf = buf;
                ds->ds_tmp = tmp;
                rhdr = (RANDOM_HDR) buf->buf_data;
                thdr = (RANDOM_HDR) tmp->buf_data;
        }
 }

/*
 *      db_hash_ran  -  Hash a random key
 */

short db_hash_ran(ds, key)
 DATA_SET  ds;
 char     *key;
{
        FILE_HDR  fh;
        ulong     hval = 0l;                               
        short     i;

        fh = (FILE_HDR) ds->ds_fhdr->buf_data;

        for (i=0; i < fh->fh_key_size; i++) hval += key[i];

        hval = (hval % fh->fh_base_size) + 1;

        return((short) hval);
}

/*
 *      db_search_blk_ran  - Search a random block for matching record
 */

short db_search_blk_ran(ds, key, buf)
 DATA_SET  ds;
 char     *key;
 BUFFER    buf;
{                            
        RANDOM_HDR rhdr;
        RANDOM_REC rrec;
        FILE_HDR   fh;
        char      *rec;
        short      r_cnt;

        fh   = (FILE_HDR) ds->ds_fhdr->buf_data;
        rhdr = (RANDOM_HDR) buf->buf_data;
        rec  = buf->buf_data + sizeof(struct db_random_hdr);

        for (r_cnt = 1; r_cnt <= rhdr->ran_rec_cnt; r_cnt++)
        {       rrec = (RANDOM_REC) rec;
                if (rrec->ran_stat != DB_INUSE)
                {       db_error = DB_INVALID_RANDOM;
                        return(0);
                }

                if (memcmp(key, rec+fh->fh_ctl_size, fh->fh_key_size)==0) 
                        return(r_cnt);

                rec += fh->fh_rec_size;
        }

        return(0);
}
