/****************************************************************************/
/*                                                                          */
/*                                                                          */
/*      db_idx.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_idx  -  Add record to an index file
 */

void db_add_idx(ds, user_data)
 DATA_SET ds;
 char *user_data;
{
        FILE_HDR    fh;
        INDEX_HDR ihdr;
        INDEX_REC irec;
        BUFFER     buf;
        char     *rbuf, *src, *dst;
        ushort     rec;
        int        cnt;

        db_error     = 0;

        fh   = (FILE_HDR) ds->ds_fhdr->buf_data;
        buf  = ds->ds_buf;
        ihdr = (INDEX_HDR) buf->buf_data;
        rbuf = buf->buf_data + sizeof(struct db_index_hdr);

        if (!fh->fh_root_ptr)
        {       db_get_next_avail(ds, buf);
                if (db_error) return;
        }
        else
        {       db_find_insert_idx(ds, user_data, fh->fh_key_size);
                if (db_match_blk)
                        if (!(fh->fh_file_stat & DB_DUP_ALLOWED))
                        {       db_error = DB_DUP_NOT_ALLOWED;
                                return;
                        }
        }
                 
        ihdr->idx_rec_cnt++;
        rec = buf->buf_rec_inx;

        db_add_blk = buf->buf_cur_blk;
        db_add_rec = buf->buf_rec_inx;

        if (rec <= ihdr->idx_rec_cnt)
        {       src = rbuf + (rec - 1) * fh->fh_rec_size;
                dst = src + fh->fh_rec_size;
                cnt = (ihdr->idx_rec_cnt - rec + 1) * fh->fh_rec_size;
                memcpy(dst, src, cnt);
        }                             
        
        irec = (INDEX_REC) src;
        irec->idx_idx_ptr = 0;
        dst = (char *) irec + fh->fh_ctl_size;
        memcpy(dst, user_data, fh->fh_data_size);

        fh->fh_rec_cnt++;

        db_split_blk_idx(ds);
        if (db_error) return;

        db_put_blk(ds, ds->ds_fhdr);
}
      
/*
 *      db_find_insert_idx  -  Find Insert Point in an index File
 */

void db_find_insert_idx(ds, key, key_size)
 DATA_SET ds;
 char *key;                         
 int   key_size;
{
        FILE_HDR    fh;
        INDEX_HDR ihdr;
        INDEX_REC irec;
        BUFFER     buf;
        char     *rbuf, *ikey;
        ulong      blk;
        ushort     rec;
        int          x;
                       

        db_error     = 0;
        db_match_blk = 0;
        db_match_rec = 0;

        fh   = (FILE_HDR) ds->ds_fhdr->buf_data;
        buf  = ds->ds_buf;
        ihdr = (INDEX_HDR) buf->buf_data;
        blk  = fh->fh_root_ptr;

        if (!key_size) key_size = fh->fh_key_size;

        while (blk)
        {       db_get_blk(ds, blk, buf);
                if (db_error) return;

                rbuf = buf->buf_data + sizeof(struct db_index_hdr);

                for (rec=1; rec <= ihdr->idx_rec_cnt; rec++)
                {       irec = (INDEX_REC) rbuf;
                        ikey = rbuf + fh->fh_ctl_size;

                        x = memcmp(key, ikey, key_size);
        
                        if (x == 0)
                        {       db_match_blk = blk;
                                db_match_rec = rec;
                                blk = irec->idx_idx_ptr;
                                break;
                        }

                        if (x < 0)
                        {       blk = irec->idx_idx_ptr;
                                break;
                        }

                        rbuf += fh->fh_rec_size;

                        if (rec == ihdr->idx_rec_cnt)
                        {       irec = (INDEX_REC) rbuf;
                                blk  = irec->idx_idx_ptr;
                        }
                }        
        }
        buf->buf_rec_inx = rec;
}

/*
 *      db_split_blk_idx  -  Check for a block split
 */

void db_split_blk_idx(ds)
 DATA_SET ds;
{
        FILE_HDR   fh;
        BUFFER     buf,  tmp,  aux;
        INDEX_HDR ihdr,  thdr, ahdr;
        INDEX_REC  idx;
        char     *hold, *rbuf, *src,  *dst;
        ushort     cnt,  cnt1,  cnt2;
        ushort     rec,  left, right;

        db_error = 0;

        fh  = (FILE_HDR) ds->ds_fhdr->buf_data;
        buf = ds->ds_buf;
        tmp = ds->ds_tmp;

        ihdr = (INDEX_HDR) buf->buf_data;

        if (ihdr->idx_rec_cnt <= fh->fh_recs_per_blk)
        {       db_put_blk(ds, buf);
                return;
        }

        buf        = ds->ds_tmp;
        tmp        = ds->ds_buf;
        ds->ds_buf = buf;
        ds->ds_tmp = tmp;
        aux        = ds->ds_aux;

        db_get_next_avail(ds, buf);
        if (db_error) return;

        ihdr = (INDEX_HDR) buf->buf_data;
        thdr = (INDEX_HDR) tmp->buf_data;
        ahdr = (INDEX_HDR) aux->buf_data;

        left   = tmp->buf_cur_blk;
        right  = buf->buf_cur_blk;

        cnt1 = thdr->idx_rec_cnt / 2;
        cnt2 = thdr->idx_rec_cnt - (cnt1 + 1);

        ihdr->idx_parent  = thdr->idx_parent;
        ihdr->idx_rec_cnt = cnt2;         

        src = tmp->buf_data + sizeof(struct db_index_hdr)
                                                + (cnt1 + 1) * fh->fh_rec_size;
        dst = buf->buf_data + sizeof(struct db_index_hdr);
        cnt = (cnt2 + 1) * fh->fh_rec_size;
        memcpy(dst, src, cnt);

        db_put_blk(ds, buf);
        if (db_error) return;

        if (db_add_blk == tmp->buf_cur_blk)
                if (db_add_rec > cnt1 + 1)
                {       db_add_blk  =  buf->buf_cur_blk;
                        db_add_rec -= cnt1 + 1;         
                }

        rbuf = buf->buf_data + sizeof(struct db_index_hdr);
        if (!thdr->idx_parent)
        {       if (tmp->buf_cur_blk != fh->fh_root_ptr)
                {       db_error = DB_INVALID_INDEX;
                        return;
                }
                db_get_next_avail(ds, buf);
                thdr->idx_parent = buf->buf_cur_blk;
                fh->fh_root_ptr  = buf->buf_cur_blk;
                idx              = (INDEX_REC) rbuf;
                rec              = 1;
        }
        else
        {       db_get_blk(ds, (long)thdr->idx_parent, buf);
                if (db_error) return;

                for (rec=1; rec <= ihdr->idx_rec_cnt+1; rec++)
                {       idx = (INDEX_REC) rbuf;

                        if (idx->idx_idx_ptr == left)
                                break;

                        rbuf += fh->fh_rec_size;
                }

                if (idx->idx_idx_ptr != left)
                {       db_error = DB_INVALID_INDEX;
                        return;
                }
        }

        idx->idx_idx_ptr = right;

        if (db_add_blk == tmp->buf_cur_blk)
                if (db_add_rec == cnt1 + 1)
                {       db_add_blk =  buf->buf_cur_blk;
                        db_add_rec =  rec;
                }

        src = rbuf;
        dst = rbuf + fh->fh_rec_size;
        cnt = ((ihdr->idx_rec_cnt + 1) - rec + 1) * fh->fh_rec_size;
        memcpy(dst, src, cnt);

        src = tmp->buf_data + sizeof(struct db_index_hdr)
                                         + cnt1 * fh->fh_rec_size;
        dst = rbuf;
        cnt = fh->fh_rec_size;
        memcpy(dst, src, cnt);

        idx->idx_idx_ptr = left;
        ihdr->idx_rec_cnt++;

        thdr->idx_rec_cnt = cnt1;
        db_put_blk(ds, tmp);
        if (db_error) return;

        db_get_blk(ds, (long) right, tmp);
        if (db_error) return;

        if (thdr->idx_parent != buf->buf_cur_blk)
        {       thdr->idx_parent = buf->buf_cur_blk;
                db_put_blk(ds, tmp);
                if (db_error) return;
        }

        rbuf = tmp->buf_data + sizeof(struct db_index_hdr);
        for (cnt=1; cnt <= thdr->idx_rec_cnt + 1; cnt++)
        {       idx = (INDEX_REC) rbuf;

                if (idx->idx_idx_ptr)
                {       db_get_blk(ds, (long)idx->idx_idx_ptr, aux);
                        if (db_error) return;

                        ahdr->idx_parent = tmp->buf_cur_blk;
                        db_put_blk(ds, aux);
                        if (db_error) return;
                }

                rbuf += fh->fh_rec_size;
        }

        db_split_blk_idx(ds);
}

/*
 *      db_read_first_idx  -  Read First Record in an Index File
 */

void db_read_first_idx(ds, blk, user_data)                           
 DATA_SET ds;
 ushort blk;
 char *user_data;
{
        FILE_HDR    fh;
        BUFFER     buf;
        INDEX_HDR ihdr;
        INDEX_REC  idx;
        char      *src;

                    
        db_error = 0;

        if (!blk)
        {       db_error = DB_END_OF_FILE;
                return;
        }

        fh   = (FILE_HDR) ds->ds_fhdr->buf_data;
        buf  = ds->ds_buf;
        ihdr = (INDEX_HDR)  buf->buf_data;
        idx  = (INDEX_REC) (buf->buf_data + sizeof(struct db_index_hdr));

        while (blk)
        {       db_get_blk(ds, (long) blk, buf);
                if (db_error) return;
         

                if (!ihdr->idx_rec_cnt)
                {       db_error = DB_INVALID_INDEX;
                        return;
                }

                blk = idx->idx_idx_ptr;
        }                              

        src = buf->buf_data + sizeof(struct db_index_hdr) + fh->fh_ctl_size;
        buf->buf_rec_inx = 1;
        memcpy(user_data, src, fh->fh_data_size);
}

/*
 *      db_read_next_idx  -  Read Next Record in an Index File
 */

void db_read_next_idx(ds, user_data)
 DATA_SET ds;
 char *user_data;
{
        FILE_HDR    fh;
        BUFFER     buf;
        INDEX_HDR ihdr;
        INDEX_REC  idx;
        char      *src;

                    
        db_error = 0;

        fh   = (FILE_HDR) ds->ds_fhdr->buf_data;
        buf  = ds->ds_buf;
        ihdr = (INDEX_HDR)  buf->buf_data;
                                               
        db_get_blk(ds, ds->ds_prev_blk, buf);
        if (db_error) return;

        buf->buf_rec_inx = ds->ds_prev_rec;

        if (buf->buf_rec_inx > ihdr->idx_rec_cnt)
        {       while (buf->buf_rec_inx > ihdr->idx_rec_cnt)
                {       db_get_parent_idx(ds);
                        if (db_error) return;
                }

                src = buf->buf_data + sizeof(struct db_index_hdr)
                    + (buf->buf_rec_inx - 1) * fh->fh_rec_size
                    + fh->fh_ctl_size;

                memcpy(user_data, src, fh->fh_data_size);
                return;
        }

        buf->buf_rec_inx++;

        idx = (INDEX_REC) (buf->buf_data + sizeof(struct db_index_hdr)
            + (buf->buf_rec_inx - 1) * fh->fh_rec_size);

        if (idx->idx_idx_ptr)
        {       db_read_first_idx(ds, idx->idx_idx_ptr, user_data);
                return;
        }

        if (buf->buf_rec_inx > ihdr->idx_rec_cnt)
        {       ds->ds_prev_rec = buf->buf_rec_inx;
                db_read_next_idx(ds, user_data);
                return;
        }

        src = ((char *) idx) + fh->fh_ctl_size;
        memcpy(user_data, src, fh->fh_data_size);
}

/*
 *	db_read_last_idx  -  Read Last Record in an Index File
 */

void db_read_last_idx(ds, blk, user_data)
 DATA_SET ds;
 ushort blk;
 char *user_data;
{
        FILE_HDR    fh;
        BUFFER     buf;
        INDEX_HDR ihdr;
        INDEX_REC  idx;
        char      *src;

                    
        db_error = 0;

        if (!blk)
        {       db_error = DB_END_OF_FILE;
                return;
        }

        fh   = (FILE_HDR) ds->ds_fhdr->buf_data;
        buf  = ds->ds_buf;
        ihdr = (INDEX_HDR)  buf->buf_data;

        while (blk)
        {       db_get_blk(ds, (long) blk, buf);
                if (db_error) return;
         

                if (!ihdr->idx_rec_cnt)
                {       db_error = DB_INVALID_INDEX;
                        return;
                }

		idx  = (INDEX_REC) (buf->buf_data + sizeof(struct db_index_hdr)
				   + ihdr->idx_rec_cnt * fh->fh_rec_size);

                blk = idx->idx_idx_ptr;
        }                              

	src = buf->buf_data + sizeof(struct db_index_hdr)
	    + (ihdr->idx_rec_cnt - 1) * fh->fh_rec_size + fh->fh_ctl_size;

	buf->buf_rec_inx = ihdr->idx_rec_cnt;
        memcpy(user_data, src, fh->fh_data_size);
}

/*
 *	db_read_prev_idx  -  Read Prev Record in an Index File
 */

void db_read_prev_idx(ds, user_data)
 DATA_SET ds;
 char *user_data;
{
        FILE_HDR    fh;
        BUFFER     buf;
        INDEX_HDR ihdr;
        INDEX_REC  idx;
        char      *src;

                    
        db_error = 0;

        fh   = (FILE_HDR) ds->ds_fhdr->buf_data;
        buf  = ds->ds_buf;
        ihdr = (INDEX_HDR)  buf->buf_data;
                                               
        db_get_blk(ds, ds->ds_prev_blk, buf);
        if (db_error) return;

        buf->buf_rec_inx = ds->ds_prev_rec;

        idx = (INDEX_REC) (buf->buf_data + sizeof(struct db_index_hdr)
            + (buf->buf_rec_inx - 1) * fh->fh_rec_size);

	if (buf->buf_rec_inx == 1 && idx->idx_idx_ptr == 0)
	{	while (buf->buf_rec_inx == 1)
                {       db_get_parent_idx(ds);
                        if (db_error) return;
                }
					    
		buf->buf_rec_inx--;

                src = buf->buf_data + sizeof(struct db_index_hdr)
                    + (buf->buf_rec_inx - 1) * fh->fh_rec_size
                    + fh->fh_ctl_size;

                memcpy(user_data, src, fh->fh_data_size);
                return;
        }

        if (idx->idx_idx_ptr)
	{	db_read_last_idx(ds, idx->idx_idx_ptr, user_data);
                return;
        }

	buf->buf_rec_inx--;

        idx = (INDEX_REC) (buf->buf_data + sizeof(struct db_index_hdr)
            + (buf->buf_rec_inx - 1) * fh->fh_rec_size);

        src = ((char *) idx) + fh->fh_ctl_size;
        memcpy(user_data, src, fh->fh_data_size);
}


/*
 *      db_get_parent_idx  -  Get Parent Block
 */

void db_get_parent_idx(ds)
 DATA_SET ds;        
{
        FILE_HDR     fh;
        BUFFER      buf;
        INDEX_HDR  ihdr;
        INDEX_REC   idx;
        char      *rbuf;
        ushort hold_blk;
        int         rec;

                    
        db_error = 0;

        fh   = (FILE_HDR) ds->ds_fhdr->buf_data;
        buf  = ds->ds_buf;
        rbuf = buf->buf_data + sizeof(struct db_index_hdr);
        ihdr = (INDEX_HDR)  buf->buf_data;                 

        hold_blk = buf->buf_cur_blk;
        if (!ihdr->idx_parent)
        {       db_error = DB_END_OF_FILE;
                return;
        }

        db_get_blk(ds, (long)ihdr->idx_parent, buf);
        if (db_error) return;

        for (rec=1; rec <= ihdr->idx_rec_cnt + 1; rec++)
        {       idx = (INDEX_REC) rbuf;

                if (idx->idx_idx_ptr == hold_blk)
                        break;

                rbuf += fh->fh_rec_size;
        }

        if (idx->idx_idx_ptr != hold_blk)
        {       db_error = DB_INVALID_INDEX;
                return;
        }

        buf->buf_rec_inx = rec;
}

/*
 *      db_find_first_idx  -  Find First Record
 */

void db_find_first_idx(ds, user_data, key, key_size)
 DATA_SET ds;
 char *user_data;
 char *key;
 int   key_size;
{
        FILE_HDR  fh;
        BUFFER   buf;
        char   *rbuf;

        db_error = 0;

        fh   = (FILE_HDR) ds->ds_fhdr->buf_data;
        buf  = ds->ds_buf;

        db_find_insert_idx(ds, key, key_size);
        if (db_error) return;

        if (!db_match_blk)
        {       db_error = DB_REC_NOT_FOUND;
                return;
        }

        db_get_blk(ds, db_match_blk, buf);
        if (db_error) return;

        buf->buf_rec_inx = db_match_rec;

        rbuf = buf->buf_data + sizeof(struct db_index_hdr)
             + (buf->buf_rec_inx - 1) * fh->fh_rec_size + fh->fh_ctl_size;

        memcpy(user_data, rbuf, fh->fh_data_size);
}

/*
 *      db_delete_idx  -  Delete an Index Record
 */

void db_delete_idx(ds)
 DATA_SET ds;
{
        FILE_HDR     fh;
        BUFFER      buf;
        INDEX_HDR  ihdr;
        char       *src, *dst;
        int         cnt, move_flag;

        db_error = 0;

        move_flag = db_move_to_leaf_idx(ds);
        if (db_error) return;       

        fh   = (FILE_HDR) ds->ds_fhdr->buf_data;
        buf  = ds->ds_buf;
        ihdr = (INDEX_HDR) buf->buf_data;

        dst = buf->buf_data + sizeof(struct db_index_hdr)
                            + (buf->buf_rec_inx - 1) * fh->fh_rec_size;
        src = dst + fh->fh_rec_size;
        cnt = (ihdr->idx_rec_cnt - buf->buf_rec_inx + 1) * fh->fh_rec_size;

        if (cnt) memcpy(dst, src, cnt);

        ds->ds_prev_blk = buf->buf_cur_blk;
        ds->ds_prev_rec = buf->buf_rec_inx - 1;

        ihdr->idx_rec_cnt--;
        if (fh->fh_rec_cnt) fh->fh_rec_cnt--;

        if (buf->buf_cur_blk == fh->fh_root_ptr  &&  ihdr->idx_rec_cnt == 0)
        {       fh->fh_root_ptr = 0;
                db_free_rec(ds, buf);
                if (db_error) return;
        }
        else
        {       db_balance_idx(ds);
                if (db_error) return;
        }

        db_put_blk(ds, ds->ds_fhdr);

        if (move_flag)
                db_read_next(ds, ds->ds_tmp->buf_data);
}

/*
 *      db_move_to_leaf_idx  -  Move index record to a leaf node 
 *                              before deleting it
 */

short db_move_to_leaf_idx(ds)
 DATA_SET ds;
{
        FILE_HDR     fh;
        BUFFER      buf, tmp;
        INDEX_HDR  ihdr, thdr;
        INDEX_REC   idx, tdx;
        ulong       blk;

        db_error = 0;

        fh  = (FILE_HDR) ds->ds_fhdr->buf_data;
        buf = ds->ds_buf;

        tdx = (INDEX_REC)(buf->buf_data + sizeof(struct db_index_hdr)
                                + (buf->buf_rec_inx - 1) * fh->fh_rec_size);

        if (!tdx->idx_idx_ptr) return(0);

        tmp        = ds->ds_buf;
        buf        = ds->ds_tmp;
        ds->ds_buf = buf;
        ds->ds_tmp = tmp;
                   
        ihdr = (INDEX_HDR) buf->buf_data;
        thdr = (INDEX_HDR) tmp->buf_data;

        blk = tdx->idx_idx_ptr;

        while (blk)
        {       db_get_blk(ds, blk, buf);
                if (db_error) return(0);

                idx = (INDEX_REC)(buf->buf_data + sizeof(struct db_index_hdr)
                                       + ihdr->idx_rec_cnt * fh->fh_rec_size);

                blk = idx->idx_idx_ptr;
        }

        idx = (INDEX_REC)((char *) idx - fh->fh_rec_size);

        idx->idx_idx_ptr = tdx->idx_idx_ptr;
        memcpy(tdx, idx, fh->fh_rec_size);
        idx->idx_idx_ptr = 0;

        db_put_blk(ds, tmp);
        if (db_error) return(0);

        buf->buf_rec_inx = ihdr->idx_rec_cnt;

        return(1);
}

/*
 *      db_balance_idx  -  Rebalance tree after a delete
 */

void db_balance_idx(ds)
 DATA_SET ds;
{
        FILE_HDR    fh;
        BUFFER     buf,  tmp,  aux;
        INDEX_HDR ihdr, thdr, ahdr;
        INDEX_REC  idx,  tdx,  adx;
        int    min_cnt, hold,  cnt,  op;
        char      *src, *dst;  

        db_error = 0;

        fh   = (FILE_HDR) ds->ds_fhdr->buf_data;
        buf  = ds->ds_buf;
        ihdr = (INDEX_HDR) buf->buf_data;

        if (buf->buf_cur_blk == fh->fh_root_ptr)
        {       db_put_blk(ds, buf);
                return;
        }

        min_cnt = (fh->fh_recs_per_blk + 1) / 2;
        if (ihdr->idx_rec_cnt >= min_cnt)
        {       db_put_blk(ds, buf);
                return;
        }

        tmp        = ds->ds_buf;
        buf        = ds->ds_tmp;
        ds->ds_buf = buf;
        ds->ds_tmp = tmp;
        aux        = ds->ds_aux;

        thdr = (INDEX_HDR) tmp->buf_data;
        ahdr = (INDEX_HDR) aux->buf_data;
        ihdr = (INDEX_HDR) buf->buf_data;

        buf->buf_cur_blk = tmp->buf_cur_blk;
        buf->buf_rec_inx = tmp->buf_rec_inx;
        ihdr->idx_parent = thdr->idx_parent;

        db_get_parent_idx(ds);
        if (db_error) return;

        idx  = (INDEX_REC)(buf->buf_data + sizeof(struct db_index_hdr)
                                + (buf->buf_rec_inx - 1) * fh->fh_rec_size);

        if (buf->buf_rec_inx == 1)
        {       adx = (INDEX_REC) ((char *) idx + fh->fh_rec_size);
                op  = 1;
        }
        else
        {       idx = adx = (INDEX_REC) ((char *) idx - fh->fh_rec_size);
                buf->buf_rec_inx--;
                op  = 2;          
        }                              

        db_get_blk(ds, (long)adx->idx_idx_ptr, aux);
        if (db_error) return;

        if (ahdr->idx_rec_cnt < min_cnt) op += 2;

        switch (op)
        {   case 1: /* Rotate Left */
                if (ds->ds_prev_blk == aux->buf_cur_blk &&
                    ds->ds_prev_rec == 0)
                {       ds->ds_prev_blk = buf->buf_cur_blk;
                        ds->ds_prev_rec = buf->buf_rec_inx;
                }

                if (ds->ds_prev_blk == buf->buf_cur_blk &&
                    ds->ds_prev_rec == buf->buf_rec_inx)
                {       ds->ds_prev_blk = tmp->buf_cur_blk;
                        ds->ds_prev_rec = thdr->idx_rec_cnt + 1;
                }                       
                else
                if (ds->ds_prev_blk == aux->buf_cur_blk &&
                    ds->ds_prev_rec == 1)
                {       ds->ds_prev_blk = buf->buf_cur_blk;
                        ds->ds_prev_rec = buf->buf_rec_inx;
                }
                else
                if (ds->ds_prev_blk == aux->buf_cur_blk)
                        ds->ds_prev_rec--;

                tdx = (INDEX_REC)(tmp->buf_data + sizeof(struct db_index_hdr)
                            + thdr->idx_rec_cnt * fh->fh_rec_size);
                adx = (INDEX_REC)(aux->buf_data + sizeof(struct db_index_hdr));

                hold = tdx->idx_idx_ptr;
                memcpy(tdx, idx, fh->fh_rec_size);
                tdx->idx_idx_ptr = hold;
                tdx = (INDEX_REC) ((char *) tdx + fh->fh_rec_size);
                tdx->idx_idx_ptr = adx->idx_idx_ptr;
                thdr->idx_rec_cnt++;
                db_put_blk(ds,tmp);
                if (db_error) return;

                if (tdx->idx_idx_ptr)
                {       hold = tmp->buf_cur_blk;
                        db_get_blk(ds, (long)tdx->idx_idx_ptr, tmp);
                        if (db_error) return;

                        thdr->idx_parent = hold;
                        db_put_blk(ds, tmp);
                        if (db_error) return;
                }

                hold = idx->idx_idx_ptr;
                memcpy(idx, adx, fh->fh_rec_size);
                idx->idx_idx_ptr = hold;
                db_put_blk(ds, buf);
                if (db_error) return;

                src = (char*) adx + fh->fh_rec_size;
                cnt = ahdr->idx_rec_cnt * fh->fh_rec_size;
                memcpy(adx, src, cnt);
                ahdr->idx_rec_cnt--;
                db_put_blk(ds, aux);
                if (db_error) return;
                return;

            case 2: /* Rotate Right */ 
                if (ds->ds_prev_blk == buf->buf_cur_blk &&
                    ds->ds_prev_rec == buf->buf_rec_inx)
                {       ds->ds_prev_blk = tmp->buf_cur_blk;
                        ds->ds_prev_rec = 1;
                }                       
                else
                if (ds->ds_prev_blk == aux->buf_cur_blk &&
                    ds->ds_prev_rec == ahdr->idx_rec_cnt)
                {       ds->ds_prev_blk = buf->buf_cur_blk;
                        ds->ds_prev_rec = buf->buf_rec_inx;
                }
                else
                if (ds->ds_prev_blk == tmp->buf_cur_blk)
                        ds->ds_prev_rec++;

                tdx = (INDEX_REC)(tmp->buf_data + sizeof(struct db_index_hdr));
                adx = (INDEX_REC)(aux->buf_data + sizeof(struct db_index_hdr)
                                       + ahdr->idx_rec_cnt * fh->fh_rec_size);

                dst = (char *) tdx + fh->fh_rec_size;
                cnt = (thdr->idx_rec_cnt + 1) * fh->fh_rec_size;
                memcpy(dst, tdx, cnt);
                memcpy(tdx, idx, fh->fh_rec_size);
                tdx->idx_idx_ptr = adx->idx_idx_ptr;
                thdr->idx_rec_cnt++;
                db_put_blk(ds, tmp);
                if (db_error) return;

                if (tdx->idx_idx_ptr)
                {       hold = tmp->buf_cur_blk;
                        db_get_blk(ds, (long)tdx->idx_idx_ptr, tmp);
                        if (db_error) return;

                        thdr->idx_parent = hold;
                        db_put_blk(ds, tmp);
                        if (db_error) return;
                }

                hold = idx->idx_idx_ptr;
                adx  = (INDEX_REC) ((char *) adx - fh->fh_rec_size);
                memcpy(idx, adx, fh->fh_rec_size);
                idx->idx_idx_ptr = hold;
                db_put_blk(ds, buf);
                if (db_error) return;

                ahdr->idx_rec_cnt--;
                db_put_blk(ds, aux);
                if (db_error) return;
                return;

            case 4: /* Merge Right */
                tmp        = ds->ds_aux;
                aux        = ds->ds_tmp;
                ds->ds_aux = aux;
                ds->ds_tmp = tmp;
                thdr       = (INDEX_HDR) tmp->buf_data;
                ahdr       = (INDEX_HDR) aux->buf_data;

            case 3: /* Merge Left */      
                if (ds->ds_prev_blk == buf->buf_cur_blk &&
                    ds->ds_prev_rec == buf->buf_rec_inx)
                {       ds->ds_prev_blk = tmp->buf_cur_blk;
                        ds->ds_prev_rec = thdr->idx_rec_cnt + 1;
                }                       
                else
                if (ds->ds_prev_blk == aux->buf_cur_blk)
                {       ds->ds_prev_blk  = tmp->buf_cur_blk;
                        ds->ds_prev_rec += thdr->idx_rec_cnt + 1;
                }                       

                tdx = (INDEX_REC)(tmp->buf_data + sizeof(struct db_index_hdr)
                            + thdr->idx_rec_cnt * fh->fh_rec_size);
                adx = (INDEX_REC)(aux->buf_data + sizeof(struct db_index_hdr));

                hold = tdx->idx_idx_ptr;
                memcpy(tdx, idx, fh->fh_rec_size);
                tdx->idx_idx_ptr = hold;
                tdx = (INDEX_REC) ((char *) tdx + fh->fh_rec_size);
                thdr->idx_rec_cnt++;

                cnt = ahdr->idx_rec_cnt * fh->fh_rec_size + fh->fh_ctl_size;
                memcpy(tdx, adx, cnt);
                thdr->idx_rec_cnt += ahdr->idx_rec_cnt;

                src = (char *) idx + fh->fh_rec_size;
                cnt = (ihdr->idx_rec_cnt - buf->buf_rec_inx)
                                       * fh->fh_rec_size + fh->fh_ctl_size;
                memcpy(idx, src, cnt);
                idx->idx_idx_ptr = tmp->buf_cur_blk;
                ihdr->idx_rec_cnt--;

                src = buf->buf_data + sizeof(struct db_index_hdr)
                    + ihdr->idx_rec_cnt * fh->fh_rec_size + fh->fh_ctl_size;
                cnt = fh->fh_rec_size;
                memset(src, 0, cnt);

                if (!ihdr->idx_rec_cnt)
                        thdr->idx_parent = 0;

                db_put_blk(ds, tmp);
                if (db_error) return;

                hold = ahdr->idx_rec_cnt + 1;
                db_free_rec(ds, aux);
                if (db_error) return;

                for (cnt=1; cnt <= hold; cnt++)
                {       if (tdx->idx_idx_ptr)
                        {       db_get_blk(ds, (long)tdx->idx_idx_ptr, aux);
                                if (db_error) return;

                                ahdr->idx_parent = tmp->buf_cur_blk;
                                db_put_blk(ds, aux);
                                if (db_error) return;
                        }
                        tdx = (INDEX_REC)((char *) tdx + fh->fh_rec_size);
                }

                if (buf->buf_cur_blk == fh->fh_root_ptr)
                {       if (!ihdr->idx_rec_cnt)
                        {       fh->fh_root_ptr = tmp->buf_cur_blk;
                                db_free_rec(ds, buf);
                                if (db_error) return;
                        }
                        else
                        {       db_put_blk(ds, buf);
                                if (db_error) return;
                        }
                }
                else
                {       db_balance_idx(ds);
                        if (db_error) return;
                }

                return;
        }
}

/*
 *      db_update_idx - Update an Index record
 */

void db_update_idx(ds, user_data)
 DATA_SET  ds;
 char     *user_data;
{
        INDEX_HDR   ihdr;
        FILE_HDR    fh;
        BUFFER      buf;
        char       *rbuf;

        db_error = 0;

        fh   = (FILE_HDR) ds->ds_fhdr->buf_data;
        buf  = ds->ds_buf;
        ihdr = (INDEX_HDR) buf->buf_data;

        if (ihdr->idx_rec_cnt < buf->buf_rec_inx)
        {       db_error = DB_DELETED_REC;
                return;
        }

        rbuf = buf->buf_data + sizeof(struct db_index_hdr)
             + (buf->buf_rec_inx - 1) * fh->fh_rec_size;

        memcpy(rbuf+fh->fh_ctl_size, user_data, fh->fh_data_size);
        db_put_blk(ds, buf);
        return;
}
