Newer
Older
Microsoft / bplus / CPLUS.C
@tundra tundra on 24 May 2012 39 KB Initial revision
/********************************************************************/
/*                                                                  */
/*             BPLUS file indexing program - Version 1.1            */
/*                                                                  */
/*                      A "shareware program"                       */
/*                                                                  */
/*                                                                  */
/*                      Copyright (C) 1987 by                       */
/*                                                                  */
/*                      Hunter and Associates                       */
/*                      7050 NW Zinfandel Lane                      */
/*                      Corvallis, Oregon  97330                    */
/*                      (503) 745 - 7186                            */
/*                                                                  */
/********************************************************************/


#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#include <sys\types.h>            /*  delete this line for Turbo C  */
#include <sys\stat.h>
#include <string.h>
#include "bplus.h"


/*  macros, constants, data types  */

#define  NULLREC      (-1L)  /* special value for RECPOS variable */
#define  FREE_BLOCK   (-2)   /* designates a free block in index file */
        /* the address of an entry in a block */
#define  ENT_ADR(pb,off)  ((ENTRY*)((char*)((pb)->entries) + off))
        /* the size of an entry */
#define  ENT_SIZE(pe)     strlen((pe)->key) + 1 + 2 * sizeof(RECPOS)
        /* the cache changed indicator */
#define  BUFDIRTY(j)      (mci->cache[j].dirty)
        /* the index file handle for memblock j */
#define  BUFHANDLE(j)     (mci->cache[j].handle)
        /* memory cache block j */
#define  BUFBLOCK(j)      (mci->cache[j].mb)
        /* number of times cache blk j is referenced */
#define  BUFCOUNT(j)      (mci->cache[j].count)
        /* address of current block for level j */
#define  CB(j)            (pci->pos[j].cblock)
        /* offset of current block for level j */
#define  CO(j)            (pci->pos[j].coffset)


/*  declare some global variables  */

IX_DESC      *pci;                       /* pointer to index descriptor   */
IX_BUFFER    bt_buffer;                  /* memory cache for index blocks */
IX_BUFFER    *mci = &bt_buffer;          /* pointer to cache index blocks */
BLOCK        *block_ptr;                 /* pointer to index record block */
BLOCK        *spare_block;               /* pointer to spare index block  */
int          cache_ptr = 0;              /* index to cache memory pool    */
int          cache_init = 0;             /* 1 when cache is initilized    */
int          split_size = IXB_SPACE;     /* split block when greater than */
int          comb_size  = (IXB_SPACE/2); /* combine blocks when less than */

/* #define memmove     memcpy */    /* Use this macro for MicroSoft C 4.0 */

/* list all function prototypes */
void pascal error(int, long);
void pascal read_if(long, char *, int);
void pascal write_if(int, long, char *, int);
int  pascal creat_if(char *);
int  pascal open_if(char *);
void pascal close_if(int);
void pascal update_block(void);
void pascal init_cache(void);
int  pascal find_cache(RECPOS);
int  pascal new_cache(void);
void pascal load_cache(RECPOS);
void pascal get_cache(RECPOS);
void pascal retrieve_block(int, RECPOS);
int  pascal prev_entry(int);
int  pascal next_entry(int);
void pascal copy_entry(ENTRY *, ENTRY *);
int  pascal scan_blk(int);
int  pascal last_entry(void);
void pascal write_free( RECPOS, BLOCK *);
RECPOS pascal get_free(void);
int  pascal find_block(ENTRY *, int *);
void pascal movedown(BLOCK *, int, int);
void pascal moveup(BLOCK *, int, int);
void pascal ins_block(BLOCK *, ENTRY *, int);
void pascal del_block(BLOCK *, int);
void pascal split(int, ENTRY *, ENTRY *);
void pascal ins_level(int, ENTRY *);
int  pascal insert_ix(ENTRY *, IX_DESC *);
int  pascal find_ix(ENTRY *, IX_DESC *, int);
int  pascal combineblk(RECPOS, int);
void pascal replace_entry(ENTRY *);
void print_blk(BLOCK *);


/*  file I/O for B-PLUS module  */

void pascal error(j, l)               /* print file error messages */
  int j;                              /* error number */
  long l;                             /* current file position */
  {
    static char *msg[3] = {"ERROR - CANNOT OPEN/CLOSE FILE",
                           "ERROR WHILE READING FILE",
                           "ERROR WHILE WRITING FILE"};
    printf("\n  %s - Record Number %ld\n", msg[j], l);
    exit(1);                  /* delete this line to not halt program */
                              /* and call your error handlng routine */
  } /* error */


void pascal read_if(start, buf, nwrt)    /* read pci index file */
  long start;                            /* file read position */
  char *buf;                             /* data holding buffer */
  int nwrt;                              /* number bytes to read */
  {
    long err;
          /* seek to read position in current index file */
    err = start - lseek(pci->ixfile, start, SEEK_SET);
         /* if no error read an index file block */
    if (err == 0) err = nwrt - read(pci->ixfile, buf, nwrt);
         /* call error routine if number bytes read != nwrt */
    if (err != 0) error(1, start);
  } /* read_if */


void pascal write_if(handle, start, buf, nwrt)   /* write index record */
  int handle;                        /* write to this file handle */
  long start;                        /* write to this position in file */
  char *buf;                         /* write data from this buffer */
  int nwrt;                          /* write this many bytes of data */
  {
    long err;
         /* seek to file write position */
    err = start - lseek(handle, start, SEEK_SET);
         /* if no error write index block block */
    if (err == 0) err = nwrt - write(handle, buf, nwrt);
         /* call error routine if number bytes written != nwrt */
    if (err != 0) error(2, start);
  } /* write_if */


int pascal creat_if(fn)               /* make a new index file */
  char *fn;                           /* name and path of file */
  {
    int   ret;
    ret = open(fn,O_RDWR|O_CREAT|O_TRUNC|O_BINARY,S_IWRITE);
    if (ret  < 0) error(0,0L);        /* there was an error if ret < 0 */
    return (ret);
  } /* creat_if */


int pascal open_if(fn)                /* open an existing index file */
  char *fn;                           /* path and name of index file */
  {
    int  ret;
    ret = open(fn,O_RDWR|O_BINARY);
    if (ret < 1) error(0,0L);         /* there was an error is ret < 1 */
    return (ret);
  } /* open_if */


void pascal close_if(handle)          /* close an open index file */
  int handle;                         /* with this file handle    */
  {
    if(close(handle) < 0)  error(2,0L);
  } /*  close_if */


int cdecl open_index(name, pix, dup)  /* open and initilize index file */
  char *name;                         /* path and name of index file */
  IX_DESC *pix;                       /* pointer to index descriptor */
  int dup;                            /* allow duplicate keys if != 0 */
  {
    pci = pix;                        /* pci is global index descriptor */
    pci->ixfile = open_if(name);      /* file handle */
    pci->duplicate = dup;             /* set duplicate keys flag */
         /* read root descriptor for index */
    read_if(0L,(char *)&(pix->root), (sizeof(BLOCK) + sizeof(IX_DISK)));
    if (!cache_init)                   /* if cache not initilized */
      {
        init_cache();                  /* initilize cache memory */
        cache_init = 1;                /* but only once */
      }
    first_key(pix);                    /* position to first index key */
    return ( IX_OK );
  } /* open_index */


int cdecl close_index(pix)            /* close an open index file */
  IX_DESC *pix;
  {
    int i;
         /* write out the root block for the index descriptor */
    write_if(pix->ixfile, 0L,(char *)&(pix->root),
               (sizeof(BLOCK) + sizeof(IX_DISK)));
         /* save all memory blocks for index that have been changed */
    for (i = 0; i < NUM_BUFS; i++)      /* check all of cache */
      if (BUFHANDLE(i) == pix->ixfile)
        {
          if (BUFDIRTY(i))
            {
              write_if(BUFHANDLE(i),     /* if changed, write to disk */
                       BUFBLOCK(i).brec,
                       (char *) &BUFBLOCK(i),
                       sizeof(BLOCK));
              BUFDIRTY(i) = 0;
            }
          BUFBLOCK(i).brec = NULLREC;    /* same handle can be used again */
        }
    close_if(pix->ixfile);               /* close the index file */
    return( IX_OK );
  } /* close_index */


int cdecl make_index(name, pix, dup)       /* create a new index file */
  char *name;                        /* pointer to path and file name */
  IX_DESC *pix;                      /* pointer to index descriptor */
  int dup;                           /* duplicate keys allow is != 0 */
  {
    pci = pix;             /* set global pci to this index descriptor */
    pci->ixfile = creat_if(name);
    pci->duplicate = dup;
    pci->dx.nl = 1;               /* the only block is the root */
    pci->dx.ff = NULLREC;         /* there are no free index blocks */
    pci->level = 0;               /* the root is level 0 */
    CO(0) = -1;                   /* the current block offset is -1 */
    CB(0) = 0L;                   /* the current block address 0L */
    pci->root.brec = 0L;          /* root block address is 0L */
    pci->root.bend = 0;           /* no entries yet so block end is 0 */
    pci->root.p0 = NULLREC;       /* p0 points to next index level */

         /* write the root block of the index descriptor */
    write_if(pci->ixfile, 0L,(char *)&(pix->root),
               (sizeof(BLOCK) + sizeof(IX_DISK)));
    if (!cache_init)
      {                        /* initiate memory cache but only once */
        init_cache();
        cache_init = 1;
      }
    first_key(pix);            /* initialize to first key in index */
    return ( IX_OK );
  } /* make_index */


/*  cache I/O for BPLUS  */

void pascal update_block()
   /* set flag that a memory block has changed     */
   /* no action necessary if current block is root */
  {
    if (block_ptr != &(pci->root))
       BUFDIRTY(cache_ptr) = 1;
  } /* update_block */


void pascal init_cache()       /* initialize the cache memory buffers */
  {
    register int  j;
    for (j = 0; j < NUM_BUFS; j++)
      {  BUFDIRTY(j) = 0;          /* the buffer has not been changed */
         BUFCOUNT(j) = 0;                /* number of references is 0 */
         BUFBLOCK(j).brec = NULLREC;    /* each memory block is empty */
      }
  } /* init_cache */


int pascal find_cache(r)          /* find a block in the cache memory */
  RECPOS r;
  {
    register int  j;
    for (j = 0; j < NUM_BUFS; j++)    /* repeat for each index buffer */
      {
             /* check handle and index address for a match */
        if((BUFBLOCK(j).brec == r) && (BUFHANDLE(j) == pci->ixfile))
         {  cache_ptr = j;            /* if match, set cache_ptr */
            return (1);               /* and return true */
      }  }
    return (-1);               /* return false if not in cache memory */
  } /* find_cache */


int pascal new_cache()             /* assign a block in cache memory */
  {
    register int  i;
    i = (cache_ptr + 1) % NUM_BUFS;    /* assign memory buffer */
         /* if it has been changed, save it to disk */
    if (BUFDIRTY(i)) write_if(BUFHANDLE(i),
                              BUFBLOCK(i).brec,
                              (char *) &BUFBLOCK(i),
                              sizeof(BLOCK));
    BUFHANDLE(i) = pci->ixfile;        /* save index file handle */
    BUFDIRTY(i) = 0;                   /* buffer change flag is false */
    return (i);                        /* return memory buffer pointer */
  } /* new_cache */


void pascal load_cache(r)         /* load index block in cache memory */
  RECPOS r;
  {
    cache_ptr = new_cache();        /* get a block in cache memory */
                                    /* and then load the index block */
    read_if(r, (char *)&BUFBLOCK(cache_ptr), sizeof(BLOCK));
  } /* load_cache */


void pascal get_cache(r)            /* load an index block into cache */
  RECPOS r;
  {
    if (find_cache(r) < 0)         /* if block is not in cache memory */
       load_cache(r);              /* load the block in memory */
                                   /* and set block point to this block */
    block_ptr = &BUFBLOCK(cache_ptr);
  } /* get_cache */


void pascal retrieve_block(j, r)    /* load an index block */
  int j;
  RECPOS r;
  {
    if (j == 0)                     /* if the block wanted is the root */
       block_ptr = &(pci->root);    /* then point to the root */
    else  get_cache(r);             /* else get from cache memory */
    CB(j) = block_ptr->brec;        /* store index block address */
  } /* retrieve_block */


/*  low level functions of BPLUS  */

int pascal prev_entry(off)        /* back up one entry in current block */
  int off;
  {
     if (off <= 0)                /* if off <= can not back up */
       {
         off = -1;                /* set to beginning of block */
         CO(pci->level) = off;
       }
     else
       off = scan_blk(off);       /* find previous entry */
     return(off);
  } /* prev_entry */


int pascal next_entry(off)        /* find next entry in current block */
  int off;
  {
     if (off == -1)               /* at beginning of the block */
       off = 0;
     else                         /* move to next entry if not at end */
       {
         if (off < block_ptr->bend)
            off += ENT_SIZE(ENT_ADR(block_ptr,off));
       }
     CO(pci->level) = off;       /* save the offset position in block */
     return (off);
  } /* next_entry */


void pascal copy_entry(to, from)  /* copy an entry */
  ENTRY *to;                     /* to here */
  ENTRY *from;                   /* from here */
  {
    int me;
    me = ENT_SIZE(from);         /* get the entry's size */
    memmove(to, from, me);        /* and copy */
  } /* copy_entry */


int pascal scan_blk(n)           /* find the offset of last entry in */
int n;                           /* current block before postion n */
  {
     register int off, last;
     off = 0;
     last = -1;
     while (off < n )            /* repeat until position >= n */
       {  last = off;
          off += ENT_SIZE(ENT_ADR(block_ptr,off));
       }
     CO(pci->level) = last;      /* save new block offset positioon */
     return (last);
  } /* scan_blk */


int pascal last_entry()          /* find offset of last entry in block */
  {
     return( scan_blk(block_ptr->bend) );
  } /* last_entry */


/*  maintain list of free index blocks  */

void pascal write_free(r, pb)    /* update list of free index blocks */
  RECPOS r;                      /* free index position */
  BLOCK *pb;                     /* free block */
  {
    pb->p0 = FREE_BLOCK;         /* mark as free */
    pb->brec = pci->dx.ff;       /* keep old first free address */
    write_if(pci->ixfile, r, (char *) pb, sizeof(BLOCK));
    pci->dx.ff = r;              /* set first free address to r */
  } /* write_free */


RECPOS pascal get_free()         /* get address of free index block */
  {
    RECPOS  r, rt;

    r = pci->dx.ff;              /* use block address ff if free */
    if ( r != NULLREC )
      {  read_if(r, (char *)&rt, sizeof( RECPOS ));
         pci->dx.ff = rt;        /* save next free index block */
      }
    else                         /* else add to end of index file */
      r = filelength (pci->ixfile);
    return (r);                  /* return index block address */
  } /* get_free */


/*  general BPLUS block level functions  */

int pascal find_block(pe, poff)    /* find a key with current block */
  ENTRY *pe;                       /* use this entry */
  int *poff;                       /* return offset within block */
  {
    register int pos, nextpos, ret;
    pos = -1;
    nextpos = 0;
    ret = 1;
    while ( nextpos < block_ptr->bend)   /* repeat until end of block */
      {
        ret = strcmp((char *)(pe->key),
                     (char *)(ENT_ADR(block_ptr, nextpos)->key));
        if (ret <= 0)              /* if the entry key >= search key */
          {
             if (ret == 0) pos = nextpos;   /* move to matching key  */
             break;                         /* and break loop */
          }
        pos = nextpos;                      /* save current offset */
        nextpos = next_entry(pos);          /* get next entry position */
      }
    CO(pci->level) = pos;         /* save offset within current block */
    *poff = pos;                  /* store offset position */
    return (ret);
  } /* find_block */


void pascal movedown(pb, off, n)       /* move part of block downward */
  BLOCK *pb;                           /* block to move down */
  int off;                             /* start move here */
  int n;                               /* move this far */
  {
    memmove(ENT_ADR(pb, off),
           ENT_ADR(pb, off + n),
           pb -> bend - (off + n));
  } /* movedown */


void pascal moveup(pb, off, n)        /* move part of a block upward */
  BLOCK *pb;                          /* the block */
  int off;                            /* start move here */
  int n;                              /* move up n bytes */
  {
    memmove(ENT_ADR(pb, off + n),
            ENT_ADR(pb, off),
            pb->bend - off);
  } /* moveup */


void pascal ins_block(pb, pe, off)      /* insert entry into a block */
  BLOCK *pb;                            /* add to this block */
  ENTRY *pe;                            /* add this entry */
  int off;                              /* at this position */
  {
    int size;
    size = ENT_SIZE(pe);                /* size of new entry */
    moveup(pb,off,size);                /* move entries to make room */
    copy_entry(ENT_ADR(pb,off),pe);     /* copy new entry */
    pb->bend += size;                   /* adjust block size */
  } /* ins_block */


void pascal del_block(pb, off)          /* delete entry in a block */
  BLOCK *pb;                            /* this block */
  int off;                              /* delete entry at this position */
  {
    int ne;
    ne = ENT_SIZE(ENT_ADR(pb, off));   /* size of deleted entry */
    movedown(pb, off, ne);             /* move entries down */
    pb->bend -= ne;                    /* adjust block size */
  } /* del_block */


/*  position at start/end of index  */

int cdecl first_key(pix)              /* position to first key */
  IX_DESC *pix;                       /* in this index file */
  {
    pci = pix;                        /* set global index descriptor */
    block_ptr = &(pci->root);         /* start with the root */
    CB(0) = 0L;                       /* root address is 0L */
    CO(0) = -1;                       /* offset is -1 */
    pci->level = 0;                   /* 0 level in index file */
    while(block_ptr->p0 != NULLREC)   /* repeat for all levels */
      {                               /* get index block for next level */
        retrieve_block(++(pci->level), block_ptr->p0);
        CO(pci->level) = -1;          /* position to start of block */
      }
    return ( IX_OK );
  } /* first_key */


int cdecl last_key(pix)               /* position at last key */
  IX_DESC *pix;                       /* in this index file */
  {
    long  ads;
    pci = pix;
    block_ptr = &(pci->root);         /* start with the root */
    CB(0) = 0L;
    pci->level = 0;
    if(last_entry() >= 0)             /* repeat for all levels */
      {                               /* get block for next level */
        while ((ads = ENT_ADR(block_ptr,last_entry())->idxptr) != NULLREC)
             retrieve_block(++(pci->level), ads);
      }
    CO(pci->level) = block_ptr->bend; /* set offset position to the end */
    return ( IX_OK );
  } /* last_key */


/*  get next, previous entries  */

int cdecl next_key(pe, pix)           /* get next key */
  ENTRY *pe;                          /* and put it here */
  IX_DESC *pix;                       /* for this index */
  {
    RECPOS  address;
    pci = pix;
                                      /* get block for current level */
    retrieve_block(pci->level, CB(pci->level));
                                      /* set address for next level */
    if(CO(pci->level) == -1) address = block_ptr->p0;
    else
      {
        if (CO(pci->level) == block_ptr->bend) address = NULLREC;
        else address = ENT_ADR(block_ptr, CO(pci->level))->idxptr;
      }
    while (address != NULLREC)        /* repeat until at leaf level */
      {
         retrieve_block(++(pci->level), address);
         CO(pci->level) = -1;
         address = block_ptr->p0;
      }
    next_entry(CO(pci->level));       /* get next entry for leaf block */
    if (CO(pci->level) == block_ptr->bend)   /* check for end of block */
      {
        do
          { if(pci->level == 0)       /* if this is the root block */
              {
                last_key(pci);        /* go to end of root block */
                return (EOIX);        /* return end of index file */
              }
            --(pci->level);           /* level of ancestor block */
            retrieve_block(pci->level, CB(pci->level));
            next_entry(CO(pci->level));  /* next entry for ancestor */
          } while (CO(pci->level) == block_ptr->bend);
      }
                                      /* copy the next entry and return */
    copy_entry(pe, ENT_ADR(block_ptr, CO(pci->level)));
    return ( IX_OK );
  } /* next_key */


int cdecl prev_key(pe, pix)          /* get the previous key */
  ENTRY *pe;                         /* put it here */
  IX_DESC *pix;                      /* for this index */
  {
    RECPOS  address;
    pci = pix;
                                     /* get block for current level */
    retrieve_block(pci->level, CB(pci->level));
    prev_entry(CO(pci->level));      /* previous entry in this block */
    if (CO(pci->level) == -1)
      address = block_ptr->p0;
    else
      address = ENT_ADR(block_ptr, CO(pci->level))->idxptr;
    if (address != NULLREC)          /* go to the leaf level of index */
      { do
          {
            retrieve_block(++(pci->level), address);
            address = ENT_ADR(block_ptr, last_entry())->idxptr;
          } while (address != NULLREC);
      }
    if (CO(pci->level) == -1)        /* check if at beginning of block */
      { do
          {
            if(pci->level == 0)      /* if this is the root block */
              {
                first_key(pci);      /* get first key */
                return (EOIX);       /* and return end of index */
              }
            --(pci->level);          /* level for ancestor block */
          } while (CO(pci->level) == -1);   /* repeat if beginning */
        retrieve_block(pci->level, CB(pci->level));   /* get block  */
      }
                                     /* copy entry and return */
    copy_entry(pe, ENT_ADR(block_ptr, CO(pci->level)));
    return ( IX_OK );
  } /* prev_key */


/*  insert new entries into tree  */

void pascal split(l, pe, e)           /* split an index block */
  int l;                              /* level in tree */
  ENTRY *pe;                          /* entry to insert */
  ENTRY *e;                           /* entry to pass up to next level */
  {
    int  half, ins_pos, size;
    ins_pos = CO(pci->level);         /* save current offset */
                                      /* and divide block in half */
    half = scan_blk(block_ptr->bend / 2 + sizeof(RECPOS));
    if (half == ins_pos)              /* if inserting at half */
      *e = *pe;                       /* pass up entry pe */
    else                              /* else copy entry at half */
      {
         copy_entry(e, ENT_ADR(block_ptr, half));
         size = ENT_SIZE(e);
         movedown(block_ptr, half, size);   /* move block entries down */
         block_ptr->bend -= size;           /* and adjust the size */
      }
    spare_block = &BUFBLOCK(new_cache());   /* allocate a spare block */
    memmove(spare_block->entries,           /* and copy half the entries */
           ENT_ADR(block_ptr,half),
           block_ptr->bend - half);
    spare_block->brec = get_free();         /* index address of new block */
    spare_block->bend = block_ptr->bend - half;    /* set size of block */
    spare_block->p0 = e->idxptr;            /* set all the pointers */
    block_ptr->bend = half;
    e->idxptr = spare_block->brec;
    if (ins_pos < half)                     /* insert the new entry */
      ins_block(block_ptr,pe,ins_pos);      /* in current block */
    else if (ins_pos > half)                /* else insert the entry */
      {                                     /* in the spare block */
         ins_pos -= ENT_SIZE(e);
         ins_block(spare_block,pe,ins_pos - half);
         CB(l) = e->idxptr;                 /* set block address */
         CO(l) = CO(l) - half;              /* and offset */
      }
    write_if(pci->ixfile, spare_block->brec,   /* write to disk */
             (char *) spare_block, sizeof(BLOCK));
  } /* split */


void pascal ins_level(l, e)        /* insert an entry e */
  int l;                           /* into block level l */
  ENTRY *e;
  {
    int  i;
    if ( l < 0)                    /* tree height has increased */
      {  for (i = 1; i < MAX_LEVELS; i++)   /* save offset and addresses */
           {  CO(MAX_LEVELS - i) = CO(MAX_LEVELS - i - 1);
              CB(MAX_LEVELS - i) = CB(MAX_LEVELS - i - 1);
           }
                                   /* copy old root to spare block */
         memmove(spare_block, &(pci->root), sizeof(BLOCK));
                                  /* get index address and write to disk */
         spare_block->brec = get_free();
         write_if(pci->ixfile, spare_block->brec,
                  (char *) spare_block, sizeof(BLOCK));
         pci->root.p0 = spare_block->brec;    /* set p0 pointer */
         copy_entry((ENTRY *) (pci->root.entries), e);  /* copy insert e */
         pci->root.bend = ENT_SIZE(e);        /* root contains only e */
         CO(0) = 0;                           /* root offset is 0 */
         pci->level = 0;                      /* set current level */
         (pci->dx.nl)++;                      /* increment no. of levels */
      }
    else ins_block(block_ptr,e,CO(l));        /* insert in current block */
  } /* ins_level */


int pascal insert_ix(pe, pix)         /* insert at current level */
  ENTRY *pe;                          /* insert entry pe */
  IX_DESC *pix;                       /* into this index */
  {
    ENTRY    e, ee;
    int h;
    h = 0;
    pci = pix;
    ee = *pe;
    do
      {
         if(CO(pci->level) >= 0)      /* set new offset */
           CO(pci->level) +=
                  ENT_SIZE(ENT_ADR(block_ptr, CO(pci->level)));
         else
           CO(pci->level) = 0;
         update_block();           /* we are going to change this block */
                                   /* if new block size < split size */
         if( (block_ptr->bend + ENT_SIZE(&ee)) <= split_size)
           {
             ins_level(pci->level, &ee);   /* insert into current block */
             break;                        /* and break */
           }
         else
           {
             h = 1;                        /* must reset index pointers */
             split(pci->level,&ee, &e);    /* split the current block */
             ee = e;                       /* this entry is passed up */
             pci->level--;                 /* to insert at this level */
             if (pci->level < 0)           /* increase tree height */
               {
                 ins_level(pci->level, &e);
                 break;
               }
                                           /* get block for next level */
             retrieve_block(pci->level, CB(pci->level));
           }
      }
    while (1);
    if (h) find_ix(pe, pix, 0);           /* reset pointers if necessary */
    return ( IX_OK );
  } /* insert_ix */


/*  BPLUS find and add key functions  */

int pascal find_ix(pe, pix, find)     /* search an index file */
  ENTRY *pe;                          /* for this entry */
  IX_DESC *pix;                       /* in this index */
  int find;                           /* 1 to find_key, 0 to locate_key */
  {
    int      level, off, ret;
    RECPOS   ads;
    pci = pix;
    ads = 0L;                            /* start at the root */
    level = ret = 0;
    while (ads != NULLREC)               /* repeat until done */
      {  pci->level = level;             /* set level */
         retrieve_block(level, ads);     /* get index block */
                                         /* and search for entry */
         if (find_block(pe, &off) == 0) ret = 1;     /* found? */
         if (ret && find) break;                     /* done? */
         if (off == -1)                  /* get next block address */
           ads = block_ptr->p0;
         else
           ads = ENT_ADR(block_ptr, off)->idxptr;
         CO(level++) = off;              /* increment level */
       }
     return ( ret );
   } /* find_ix */


int cdecl find_key(pe, pix)           /* find a key */
  ENTRY *pe;                          /* this entry */
  IX_DESC *pix;                       /* in this index */
  {
    int ret;
    ret = find_ix(pe, pix, 1);       /* find_ix does all the work */
                                     /* if found, copy the entry */
    if ( ret ) copy_entry(pe, ENT_ADR(block_ptr, CO(pci->level)));
    return ( ret );
  } /* find_key */


int cdecl add_key(pe, pix)            /* add a new key */
  ENTRY *pe;                          /* this entry */
  IX_DESC *pix;                       /* this index file */
  {
    int ret;
    ret = find_ix(pe, pix, 0);        /* see if key is already in index */
                                      /* if found, are dupicates are OK? */
    if ( ret && (pci->duplicate == 0)) return ( IX_FAIL );
    pe->idxptr = NULLREC;             /* add new key on leaf level */
    return (insert_ix(pe, pix));      /* insert_ix does the work */
  } /* add_key */


int cdecl locate_key(pe, pix)         /* locate first key */
  ENTRY *pe;                          /* <= this entry */
  IX_DESC *pix;                       /* in this index */
  {
    int ret;
    ret = find_ix(pe, pix, 1);        /* search index for entry */
                                      /* if found, copy it to pe */
    if (ret) copy_entry(pe, ENT_ADR(block_ptr, CO(pci->level)));
                                      /* else get the next key */
    else if (next_key(pe,pix) == EOIX) ret = EOIX;
    return ( ret );
  } /* locate_key */


int cdecl find_exact(pe, pix)         /* find an exact match */
  ENTRY *pe;                          /* for this entry */
  IX_DESC * pix;                      /* in this index */
  {
    int  ret;
    ENTRY e;
    copy_entry(&e, pe);               /* make a copy of the entry */
    ret = find_key(&e, pix);          /* is it in the index? */
    if ( ret && pci->duplicate)       /* if duplicate key are allowed */
      {                               /* then search for recptr match */
        do
          {
            ret = (e.recptr == pe->recptr);
            if( !ret )  ret = next_key(&e, pci);
            if (ret) ret = (strcmp(e.key, pe->key) == 0);
            if ( !ret ) return ( 0 );     /* no match was found */
          } while ( !ret );
      }
    copy_entry(pe, &e);              /* if found, data is contained in e */
    return ( ret );
  } /* find_exact */


/* BPLUS delete key functions */

int cdecl delete_key(pe, pix)         /* delete a key */
  ENTRY *pe;                          /* this entry */
  IX_DESC *pix;                       /* in this index */
  {
     ENTRY   e;
     RECPOS  ads;
     int     h, leveli, levelf;
                                      /* search index for exact match */
     if (!find_exact(pe, pix))  return( IX_FAIL );
     h = 1;
     if ((ads = pe->idxptr) != NULLREC)    /* if not the leaf level */
       {
          leveli = pci->level;        /* save current level */
          do                          /* go to leaf level of index */
            {
               retrieve_block(++(pci->level), ads);
               CO(pci->level) = -1;
            }
          while ((ads = block_ptr->p0) != NULLREC);
          CO(pci->level) = 0;
          copy_entry(&e, ENT_ADR(block_ptr, CO(pci->level)));
          levelf = pci->level;        /* save leaf level */
          pci->level = leveli;        /* reset starting level */
          replace_entry(&e);          /* replace with entry from leaf */
          pci->level = levelf;        /* leaf level */
       }
     while ( h )
       {
                             /* get block and delete current entry */
          retrieve_block(pci->level, CB(pci->level));
          del_block(block_ptr, CO(pci->level));
          update_block();             /* block has been changed */
          if ( (pci->level == 0) && (block_ptr->bend == 0))
          /* tree was reduced in height */
            {
              if (pci->root.p0 != NULLREC)    /* replace root block */
                {
                  retrieve_block(++pci->level, pci->root.p0);
                  memmove(&(pci->root), block_ptr, sizeof(BLOCK));
                  (pci->dx.nl)--;           /* decrement number of levels */
                  write_free(block_ptr->brec, block_ptr);  /* reuse space */
                  BUFDIRTY(cache_ptr) = 0;       /* block saved on disk */
                  BUFHANDLE(cache_ptr) = 0;
                }
              break;
            }
          /* see if we can combine index blocks */
          h = (block_ptr->bend < comb_size) && (pci->level > 0);
          if ( h )
              h = combineblk(CB(pci->level), block_ptr->bend);
       }
    find_ix(pe, pix, 0);         /* restore CO and CB for each level */
    return( IX_OK );
  } /* delete_key */


int pascal combineblk(ads, size)      /* combine index blocks */
  RECPOS ads;                         /* block at this address */
  int size;                           /* and is this size */
  {
    ENTRY  e;
    RECPOS address;
    int    esize, off, ret, saveoff, ibuff;
    ret = 0;
                                   /* ancestor level and save offset */
    saveoff = CO(--(pci->level));
                                   /* retrieve ancestor index block */
    retrieve_block(pci->level, CB(pci->level));
    if ((off = next_entry( saveoff )) < block_ptr->bend)
      /* combine with page on right */
      {
        if ( (ENT_SIZE(ENT_ADR(block_ptr, off)) + size) < split_size)
          /* okay to combine */
          {
            copy_entry(&e, ENT_ADR(block_ptr, off));   /* save entry */
            address = ENT_ADR(block_ptr, CO(pci->level))->idxptr;
            retrieve_block(++pci->level, address);
            ibuff = cache_ptr;           /* save cache pointer */
            spare_block = block_ptr;     /* use as spare block */
            retrieve_block(pci->level, ads);
            esize = ENT_SIZE(&e);
            if(((block_ptr->bend + spare_block->bend + esize) >= split_size)
                 && (spare_block->bend <= block_ptr->bend + esize))
               return( ret );
            e.idxptr = spare_block->p0;
            ins_block(block_ptr, &e, block_ptr->bend);
            update_block();
            if ((block_ptr->bend + spare_block->bend) < split_size)
            /* combine the blocks */
              {
                memmove(ENT_ADR(block_ptr, block_ptr->bend),
                       ENT_ADR(spare_block, 0),
                       spare_block->bend);
                /* set block length and free spare block space */
                block_ptr->bend += spare_block->bend;
                write_free(spare_block->brec, spare_block);
                BUFDIRTY(ibuff) = 0;
                BUFHANDLE(ibuff) = 0;
                --pci->level;
                ret = 1;
              }
            else
            /* move an entry up to replace the one moved */
              {
                copy_entry(&e, ENT_ADR(spare_block, 0));
                esize = ENT_SIZE(&e);
                /* fixup spare block and pointers */
                movedown(spare_block, 0, esize);
                spare_block->bend -= esize;
                spare_block->p0 = e.idxptr;
                BUFDIRTY(ibuff) = 1;
                --(pci->level);
                replace_entry(&e);
              }
          }
      }
    else
      /* move from page on left */
      {
        if ( (ENT_SIZE(ENT_ADR(block_ptr, CO(pci->level))) + size)
                 < split_size)
          /* okay to proceed */
          {
            copy_entry(&e, ENT_ADR(block_ptr, saveoff));  /* save entry */
            /* get page which is on the left */
            off = prev_entry(saveoff);
            if (CO(pci->level) == -1) address = block_ptr->p0;
            else address = ENT_ADR(block_ptr, CO(pci->level))->idxptr;
            retrieve_block(++pci->level, address);
            off = last_entry();
            ibuff = cache_ptr;
            /* set spare block to left page */
            spare_block = block_ptr;
            /* get current block */
            retrieve_block(pci->level, ads);
            esize = ENT_SIZE(&e);
            if(((block_ptr->bend + spare_block->bend + esize) >= split_size)
                 && (spare_block->bend <= block_ptr->bend + esize))
               return( ret );
            BUFDIRTY(ibuff) = 1;           /* we have changed things */
            CO(pci->level) = 0;
            e.idxptr = block_ptr->p0;
            ins_block(block_ptr, &e, 0);
            if ((block_ptr->bend + spare_block->bend) < split_size)
            /* combine the blocks */
              {
                memmove(ENT_ADR(spare_block, spare_block->bend),
                       ENT_ADR(block_ptr, 0),
                       block_ptr->bend);
                /* set block length and freeup block */
                spare_block->bend += block_ptr->bend;
                write_free(block_ptr->brec, block_ptr);
                BUFDIRTY(cache_ptr) = 0;
                BUFHANDLE(cache_ptr) = 0;
                CO(--(pci->level)) = saveoff;
                ret = 1;
              }
            else
            /* move an entry up to replace the one moved */
              {
                 block_ptr->p0 = ENT_ADR(spare_block,off)->idxptr;
                 copy_entry(&e, ENT_ADR(spare_block, off));
                 spare_block->bend = off;
                 update_block();
                 CO(--(pci->level)) = saveoff;
                 replace_entry(&e);
              }
          }
      }
    return ( ret );
  } /* combineblk */


void pascal replace_entry(pe)     /* replace entry at current position */
  ENTRY *pe;                      /* with this entry */
  {
    retrieve_block(pci->level, CB(pci->level));   /* get current block */
    /* set address for the replacement entry */
    pe->idxptr = ENT_ADR(block_ptr, CO(pci->level))->idxptr;
    del_block(block_ptr, CO(pci->level));      /* now delete the entry */
    prev_entry(CO(pci->level));                /* backup one entry */
    insert_ix(pe, pci);                        /* and insert new entry */
  } /* replace_entry */