/*
 * INXRTNS.C - index file support routines
 *
 *                      Copyright (c) 1987, Jim Mischel
 * Modifications:
 *
 * 08/13/87 - jim - original coding
 * 08/21/87 - jim - flush the buffer after every write (iwrite_dat and iwrite_inx)
 * 08/23/87 - jim - move global ierrno to this module
 * 11/04/87 - jim - remove default comparison routines
 * 02/02/88 - jim - check for deleted records in isearch() routine
 */
#define __INDEX__

#include "inxdefs.h"

int ierrno;		/* global external */
/************************************************************************/
/*                                                                      */
/*                       Internal support routines                      */
/*                                                                      */
/************************************************************************/

/*
 * iget_next() - read the next index record in sequence.
 * If successful, '*irec' contains the next index record and the function
 * returns 0.
 * If unsuccessful, '*irec' is undefined and the function returns EOF.
 */
int iget_next(df_rec *db_control, inx_rec *irec)
{
  /* see if we've already reached the end of the file */
  if (irec->if_flags & ETHRD) {
    db_control->df_flags |= DF_EOF;
    return(EOF);
  }

  if (irec->if_flags & RTHRD) {         /* Right node is a thread pointer */
    if (iread_inx(db_control,irec->if_right_node))
      return(EOF);
  }
  else {                                /* Right node is a tree pointer */

  /*
   * Read the right node, then follow the left nodes until we come to a leaf.
   * We'll know a leaf when the left node pointer is a thread pointer.
   */
    if (iread_inx(db_control,irec->if_right_node))
      return(EOF);
    while (!(db_control->df_inx_buff.if_flags & LTHRD))
      if (iread_inx(db_control,db_control->df_inx_buff.if_left_node))
        return(EOF);
  } /* else */

  return(0);
} /* iget_next */

/*
 * iget_prev() - read the previous index record in sequence.
 * If successful, '*irec' contains the previous index   record and the function
 * returns 0.
 * If unsuccessful, '*irec' is undefined and the function returns EOF.
 */
int iget_prev(df_rec *db_control, inx_rec *irec)
{
  if (irec->if_flags & BTHRD) {
    db_control->df_flags |= DF_TOF;
    return(EOF);
  }

  if (irec->if_flags & LTHRD) {         /* Left node is a thread pointer */
    if (iread_inx(db_control,irec->if_left_node))
      return(EOF);
  }
  else {                                /* Left node is a tree pointer */

  /*
   * Read the left node, then follow the right nodes until we come to a leaf.
   * We'll know a leaf when the right node pointer is a thread pointer.
   */
    if (iread_inx(db_control,irec->if_left_node))
      return(EOF);
    while (!(db_control->df_inx_buff.if_flags & RTHRD))
      if (iread_inx(db_control,db_control->df_inx_buff.if_right_node))
        return(EOF);
  } /* else */

  return(0);
} /* iget_prev */

/*
 * isearch() - searches for key value '*key'    in data file using a simple
 * binary tree search.
 * The function returns:
 *   -1 : The specified key was not found.  '*db_control->df_inx_buff'
 *        would be the parent if '*key' was added to    the file.
 *        '*db_control->df_dat_buff' contains the data record last read.
 *    0 : The specified key was found.  '*db_control->df_inx_buff' contains
 *        the index record and '*db_control->df_dat_buff' contains the
 *        data record.
 *    1 : The specified key was not found.  '*db_control->df_inx_buff'
 *        would be the parent if '*key' was added to    the file.
 *        '*db_control->df_dat_buff' contains the data record last read.
 *    2 : An error was encountered reading either the index or the data file.
 *        '*db_control->df_inx_buff' and '*db_control->df_dat_buff' are
 *        undefined.
 */
int isearch(df_rec *db_control, char *key)
{
  if (iget_root(db_control))
    return(ierrno);

  /* Binary tree search */
  while (1) {

    /* read the data record */
    if (iread_dat(db_control,db_control->df_inx_buff.if_dat_ptr))
      return(2);                        /* data file read error */

    switch ((*db_control->df_cmp)(key,db_control->df_key_ptr)) {
      case 0    :
        if (!(db_control->df_inx_buff.if_flags & DELETED_REC))
          return(0);      /* found */
      case 1    :                       /* key is greater than data record key */
        if (db_control->df_inx_buff.if_flags & RTHRD)
          return(1);                    /* right thread (not found) */
        if (iread_inx(db_control,db_control->df_inx_buff.if_right_node))
          return(2);                    /* inx file read error */
        break;
      case -1        :                  /* key is less than data record key */
        if (db_control->df_inx_buff.if_flags & LTHRD)
          return(-1);                   /* thread pointer (not found) */
        if (iread_inx(db_control,db_control->df_inx_buff.if_left_node))
          return(2);                    /* index file read error */
        break;
      default   :
        puts("invalid return value from comparison routine");
        return(2);
    } /* switch */
  } /* while */
} /* isearch */

/*
 * iget_root() - reads the root of the binary tree into the index buffer.
 * Returns 0 if successful, error status if unsuccessful.  If the file
 * is empty, EOF is returned.  If unsuccessful, the contents of the index
 * buffer is undefined.
 */
int iget_root(df_rec *db_control)
{
  if (iread_inx(db_control,0L))
    return(ierrno);
  if (db_control->df_inx_buff.if_left_node == 0L)
    return(ierror(EOF));                /* empty file */
  if (iread_inx(db_control,db_control->df_inx_buff.if_left_node))
    return(ierrno);
  return(0);
} /* iget_root */


/************************************************************************/
/*                                                                      */
/*                      File I/O primitives                             */
/*                                                                      */
/*  These are the only routines that actually access the files.         */
/*                                                                      */
/************************************************************************/

/*
 * iread_inx() - reads the index record at seek_pos into the index buffer.
 * Returns 0 if successful, I_INXRD if unsuccessful.  If unsuccessful,
 * the contents of the index buffer is undefined.
 */
int iread_inx(df_rec *db_control, long seek_pos)
{
  if (fseek(db_control->df_inx_file,seek_pos,SEEK_SET))
    return(ierror(I_INXRD));
  db_control->df_inx_ptr = seek_pos;
  if (fread(&db_control->df_inx_buff,sizeof(inx_rec),1,
            db_control->df_inx_file) != 1)
    return(ierror(I_INXRD));
  return(ierror(0));
} /* iread_inx */

/*
 * iwrite_inx() - write the record from 'irec' to the index file at position
 * 'seek_pos'.  Returns 0 if successful,        I_INXWT if unsuccessful.
 */
int iwrite_inx(df_rec *db_control, inx_rec *irec, long seek_pos)
{
  if (fseek(db_control->df_inx_file,seek_pos,SEEK_SET))
    return(ierror(I_INXWT));
  db_control->df_inx_ptr = seek_pos;
  if (fwrite(irec,sizeof(inx_rec),1,db_control->df_inx_file) != 1)
    return(ierror(I_INXWT));
  if (fflush(db_control->df_inx_file))
    return(ierror(I_INXWT));
  return(ierror(0));
} /* iwrite_inx */

/*
 * iread_dat() - reads the data record at seek_pos into the data buffer.
 * Returns 0 if successful, I_DATRD if unsuccessful.  If unsuccessful,
 * the contents of the data buffer is undefined.
 */
int iread_dat(df_rec *db_control, long seek_pos)
{
  if (fseek(db_control->df_dat_file,seek_pos,SEEK_SET))
    return(ierror(I_DATRD));
  db_control->df_dat_ptr = seek_pos;
  if (fread(db_control->df_dat_buff,db_control->df_rec_size,1,
            db_control->df_dat_file) != 1)
    return(ierror(I_DATRD));
  return(ierror(0));
} /* iread_dat */

/*
 * iwrite_dat() - write the record from 'datrec' to the data file at position
 * 'seek_pos'.  Returns 0 if successful,        I_DATWT if unsuccessful.
 */
int iwrite_dat(df_rec *db_control, void *datrec, long seek_pos)
{
  if (fseek(db_control->df_dat_file,seek_pos,SEEK_SET))
    return(ierror(I_DATWT));
  db_control->df_dat_ptr = seek_pos;
  if (fwrite(datrec,db_control->df_rec_size,1,db_control->df_dat_file) != 1)
    return(ierror(I_DATWT));
  if (fflush(db_control->df_dat_file))
    return(ierror(I_DATWT));
  return(ierror(0));
} /* iwrite_dat */

/*
 * ierror() - set the global variable 'ierrno'  to the value supplied in 'e'
 * and return the error number.
 */
int ierror(int e)
{
  ierrno = e;
  return(ierrno);
}
