/* i4index.c   (c)Copyright Sequiter Software Inc., 1990-1994.  All rights reserved. */

#include "d4all.h"
#ifndef S4UNIX
#ifdef __TURBOC__
#pragma hdrstop
#endif
#endif

#ifndef S4INDEX_OFF
#ifndef N4OTHER

int S4FUNCTION i4close( INDEX4 *i4 )
{
  int final_rc, save_attempts ;
  CODE4 *c4 ;
  TAG4 *tag_on ;

#ifdef S4VBASIC
  if ( c4parm_check( i4, 0, E4_I4CLOSE ) )
    return -1 ;
#endif

  if ( i4 == 0 )
    return -1 ;

  c4 = i4->code_base ;

  final_rc = c4->error_code ;
#ifndef S4SINGLE
  save_attempts = c4->lock_attempts ;
  c4->lock_attempts = -1 ;
#endif

#ifndef S4OFF_WRITE
  if ( i4->data )
    if ( d4update( i4->data ) < 0 )
      final_rc = e4set( c4, 0 ) ;
#endif

  if ( i4update( i4 ) < 0 )
    final_rc = e4set( c4, 0 ) ;

#ifndef S4SINGLE
  if ( i4unlock( i4 ) < 0 )
    final_rc = e4set( c4, 0 ) ;
#endif

#ifdef S4FOX
  if ( i4->tag_index )
    if ( i4->tag_index->header.type_code >= 64 )  /* compound index */
#endif

      for( ;; )
      {
        tag_on = (TAG4 *)l4pop( &i4->tags ) ;
        if ( tag_on == 0 )
          break ;
        if ( t4free_all( tag_on ) < 0 )
        {
          final_rc = e4set( c4, 0 ) ;
          break ;
        }
        expr4free( tag_on->expr ) ;
        expr4free( tag_on->filter ) ;
        mem4free( c4->tag_memory, tag_on ) ;
      }

#ifdef S4FOX
  if ( i4->tag_index != 0 )
  {
    if ( t4free_all( i4->tag_index ) < 0 )
      final_rc = e4set( c4, 0 ) ;
    else
    {
      expr4free( i4->tag_index->expr ) ;
      expr4free( i4->tag_index->filter ) ;
      mem4free( c4->tag_memory, i4->tag_index ) ;
    }
  }
#endif

  mem4release( i4->block_memory ) ;

  if ( file4open_test( &i4->file ) )
  {
    if ( i4->data )
      l4remove( &i4->data->indexes, i4 ) ;
    if ( file4close( &i4->file ) < 0 )
      final_rc = e4set( c4, 0 ) ;
  }

  mem4free( c4->index_memory, i4 ) ;
#ifndef S4SINGLE
  c4->lock_attempts = save_attempts ;
#endif
  e4set( c4, final_rc ) ;
  return final_rc ;
}

#ifndef S4OFF_WRITE
long S4FUNCTION i4extend( INDEX4 *i4 )
{
  long old_eof ;
  unsigned len ;

#ifdef S4DEBUG
  if ( i4 == 0 )
    e4severe( e4parm, E4_I4EXTEND ) ;
#endif

  if ( i4->code_base->error_code < 0 )
    return -1 ;

#ifndef S4FOX
  old_eof = i4->header.free_list ;

  if( i4->header.free_list == 0L )  /* case where no free list */
  {
    old_eof = i4->header.eof ;
    i4->header.eof = i4->header.eof + i4->header.block_rw/I4MULTIPLY ;
  }
  else
  {
    len = file4read( &i4->file, i4->header.free_list*I4MULTIPLY + sizeof(S4LONG),
                    (char *)&i4->header.free_list, sizeof(i4->header.free_list)) ;

#ifdef S4BYTE_SWAP
    i4->header.free_list = x4reverse_long( (void *)&i4->header.free_list ) ;
#endif

    if ( i4->code_base->error_code < 0 )  return -1 ;

    switch( len )
    {
    case 0:
#ifdef S4DEBUG
      e4severe( e4info, E4_I4EXTEND ) ;
#endif

      /* else fix up */
      i4->header.free_list = 0L ;
      old_eof = i4->header.eof ;
      i4->header.eof = i4->header.eof + i4->header.block_rw/I4MULTIPLY ;
      break ;

    case sizeof(i4->header.free_list):
      break ;

    default:
      return file4read_error( &i4->file ) ;
    }
  }
#endif

#ifdef S4FOX
#ifdef S4DEBUG
  if ( i4->tag_index->header.version == i4->version_old )
    e4severe( e4info, E4_I4EXTEND ) ;
#endif

  old_eof = i4->tag_index->header.free_list ;

  if( old_eof == 0L )  /* case where no free list */
  {
    old_eof = i4->eof ;
    i4->eof += B4BLOCK_SIZE ;
  }
  else
  {
    len = file4read( &i4->file, i4->tag_index->header.free_list*I4MULTIPLY,
                    (char *)&i4->tag_index->header.free_list, sizeof(i4->tag_index->header.free_list)) ;

#ifdef S4BYTE_SWAP
    i4->tag_index->header.free_list = x4reverse_long( (void *)&i4->tag_index->header.free_list ) ;
#endif

    if ( i4->code_base->error_code < 0 )  return -1 ;

    switch( len )
    {
    case 0:
#ifdef S4DEBUG
      e4severe( e4info, E4_I4EXTEND ) ;
#endif

      /* else fix up */
      i4->tag_index->header.free_list = 0L ;
      old_eof = i4->eof ;
      i4->eof += B4BLOCK_SIZE ;
      break ;

    case sizeof(i4->tag_index->header.free_list):
      break ;

    default:
      return file4read_error( &i4->file ) ;
    }
  }
#endif

  return old_eof ;
}

int S4FUNCTION i4flush( INDEX4 *i4 )
{
  int rc ;

#ifdef S4DEBUG
  if ( i4 == 0 )
    e4severe( e4parm, E4_I4FLUSH ) ;
#endif

  rc = i4update( i4 ) ;
  if ( file4flush( &i4->file ) < 0 )
    rc = -1 ;

  return rc ;
}

int S4FUNCTION i4update( INDEX4 *i4 )
{
  TAG4 *tag_on ;

#ifdef S4DEBUG
  if ( i4 == 0 )
    e4severe( e4parm, E4_I4UPDATE ) ;
#endif

  if ( i4->code_base->error_code < 0 )
    return -1 ;

#ifndef S4SINGLE
  if ( i4lock_test( i4 ) )
  {
#endif

    if ( i4update_header( i4 ) < 0 )
      return -1 ;
#ifdef S4FOX
    if ( t4update(i4->tag_index) < 0 )
      return -1 ;
    if ( i4->tag_index->header.type_code >= 64 )  /* compound index */
#endif

      for ( tag_on = 0 ;; )
      {
        tag_on = (TAG4 *)l4next( &i4->tags, tag_on ) ;
        if ( tag_on == 0 )
          break ;
        if ( t4update( tag_on ) < 0 )
          return -1 ;
        tag_on->header.root = -1L ;
      }

#ifndef S4SINGLE
  }
#endif

  return 0 ;
}
#endif

INDEX4 *S4FUNCTION i4open( DATA4 *d4, char *file_name )
{
  INDEX4 *i4 ;
  CODE4  *c4 ;
  char   buf[258] ;
  int    rc ;
  TAG4 *tag_ptr ;
#ifdef S4FOX
  B4BLOCK *b4 ;
  int old_file_lock ;
#else
  T4DESC tag_info[47] ;
  int i_tag ;
#ifdef S4BYTE_SWAP
  T4DESC swap_tag ;
#endif
#endif

#ifdef S4DEBUG
  INDEX4 *i4_ptr ;
  if ( d4 == 0 )
    e4severe( e4parm, E4_I4OPEN ) ;

  if ( file_name )
    u4name_piece( buf, sizeof( buf ), file_name, 0, 0 ) ;
  else
    u4name_piece( buf, sizeof( buf ), d4->file.name, 0, 0 ) ;
  if ( d4index( d4, buf ) )
  {
    e4( d4->code_base, e4info, E4_INFO_IAO ) ;
    return 0 ;
  }
#endif

#ifdef S4VBASIC
  if ( c4parm_check( d4, 2, E4_I4OPEN ) )
    return 0 ;
#endif

  c4 = d4->code_base ;
  if ( c4->error_code < 0 )
    return 0 ;

  if ( c4->index_memory == 0 )
    c4->index_memory = mem4create( c4, c4->mem_start_index, sizeof(INDEX4),
                                  c4->mem_expand_index, 0 ) ;

  if ( c4->index_memory == 0 )
    return 0 ;

  if ( c4->tag_memory == 0 )
  {
    c4->tag_memory = mem4create( c4, c4->mem_start_tag, sizeof(TAG4),
                                c4->mem_expand_tag, 0 ) ;
    if ( c4->tag_memory == 0 )
      return 0 ;
  }

  i4 = (INDEX4 *)mem4alloc( c4->index_memory ) ;
  if ( i4 == 0 )
  {
    e4( c4, e4memory, 0 ) ;
    return 0 ;
  }

  i4->data = d4 ;
  i4->code_base = c4 ;

#ifdef S4FOX
  if ( file_name == 0 )
  {
    u4ncpy( buf, d4->file.name, sizeof(buf) ) ;
#ifdef S4UNIX
    u4name_ext( buf, sizeof(buf), "cdx", 1 ) ;
#else
    u4name_ext( buf, sizeof(buf), "CDX", 1 ) ;
#endif
  }
  else
  {
    u4ncpy( buf, file_name, sizeof(buf) ) ;
#ifdef S4UNIX
    u4name_ext( buf, sizeof(buf), "cdx", 0 ) ;
#else
    u4name_ext( buf, sizeof(buf), "CDX", 0 ) ;
#endif
  }

#ifdef S4DEBUG
  for ( i4_ptr = 0 ;; )
  {
    i4_ptr = (INDEX4 *)l4next( &d4->indexes, i4_ptr ) ;
    if ( i4_ptr == 0 )
      break ;
    if ( !memcmp(i4_ptr->file.name, buf, (size_t)strlen( buf ) ) )
      e4severe( e4parm, E4_PARM_IND ) ;
  }
#endif

  rc = file4open( &i4->file, c4, buf, 1 ) ;
  if ( rc )
    return 0 ;
  l4add( &d4->indexes, i4 ) ;

  i4->eof = file4len( &i4->file ) ;

  i4->tag_index = (TAG4 *)mem4alloc( c4->tag_memory ) ;
  if ( i4->tag_index == 0 )
  {
    file4close( &i4->file ) ;
    e4( c4, e4memory, 0 ) ;
    return 0 ;
  }

  if ( file_name == 0 )
  {
    if ( t4init( i4->tag_index, i4, 0L, "") < 0 )
    {
      i4close( i4 ) ;
      return 0 ;
    }
  }
  else
  {
    u4name_piece( buf, 258, file_name, 0, 0 ) ;  /* get the tag_name based on the file_name */
    if ( t4init( i4->tag_index, i4, 0L, buf) < 0 )
    {
      i4close( i4 ) ;
      return 0 ;
    }
  }

  /* Perform some checks */
  if ( i4->tag_index->header.root <= 0L ||
      i4->tag_index->header.type_code < 32 )
  {
#ifdef S4DEBUG_DEV
    e4describe( c4, e4index, buf, "i4open()", "Root <= 0L or type_code < 32" ) ;
#endif
    i4close( i4 ) ;
    e4( c4, e4index, buf ) ;
    return 0 ;
  }

  i4->tag_index->header.version = x4reverse_long( (void *)&i4->tag_index->header.version ) ;
  i4->version_old = i4->tag_index->header.version ;
  i4->block_memory = mem4create( c4, c4->mem_start_block,
                                sizeof(B4BLOCK) + B4BLOCK_SIZE - sizeof(B4STD_HEADER) - sizeof(B4NODE_HEADER),
                                c4->mem_expand_block, 0 ) ;
  if ( i4->block_memory == 0 )
  {
    i4close(i4) ;
    return 0 ;
  }

  /* do an initial block allocation to make sure minimal is allocated while optimization is suspended */
  b4 = (B4BLOCK *)mem4alloc2( i4->block_memory, c4 ) ;
  if ( b4 == 0 )
  {
    i4close( i4 ) ;
    e4( c4, e4memory, E4_I4OPEN ) ;
    return 0 ;
  }
  else
    mem4free( i4->block_memory, b4 ) ;

#ifndef S4SINGLE
  /* disable locking */
  old_file_lock = i4->file_locked ;
  i4->file_locked = 1 ;
#endif

  t4top( i4->tag_index ) ;

  /* if we have a compound index, then load the tags, otherwise this is the only tag */
  if ( i4->tag_index->header.type_code >= 64 )
  {
    if ( t4block( i4->tag_index )->header.n_keys )
      do
      {
        tag_ptr = (TAG4 *)mem4alloc( c4->tag_memory ) ;
        if ( tag_ptr == 0 )
        {
          i4close( i4 ) ;
          e4( c4, e4memory, 0 ) ;
#ifndef S4SINGLE
          i4->file_locked = old_file_lock ;
#endif
          return 0 ;
        }

        if ( t4init( tag_ptr, i4, b4recno( t4block(i4->tag_index), t4block(i4->tag_index)->key_on ), t4key_data( i4->tag_index )->value ) < 0 )
        {
#ifndef S4SINGLE
          i4->file_locked = old_file_lock ;
#endif
          i4close( i4 ) ;
          return 0 ;
        }

        l4add( &i4->tags, tag_ptr ) ;
      } while ( t4skip( i4->tag_index, 1L ) == 1L ) ;
  }
  else
  {
#ifdef S4DEBUG
    if ( file_name == 0 )
    {
      i4close( i4 ) ;
#ifdef S4DEBUG_DEV
      e4describe( c4, e4index, E4_INDEX_EXP, "i4open()", "file_name == 0 invalid" ) ;
#endif
      e4( c4, e4index, E4_INDEX_EXP ) ;
    }
#endif
    l4add( &i4->tags, i4->tag_index ) ;   /* if an .idx, add single tag */
  }
#ifndef S4SINGLE
  i4->file_locked = old_file_lock ;
#endif
#endif

#ifndef S4FOX
  if ( file_name == 0 )
  {
    u4ncpy( buf, d4->file.name, sizeof(buf) ) ;
#ifdef S4UNIX
    u4name_ext( buf, sizeof(buf), "mdx", 1 ) ;
#else
    u4name_ext( buf, sizeof(buf), "MDX", 1 ) ;
#endif
  }
  else
  {
    u4ncpy( buf, file_name, sizeof(buf) ) ;
#ifdef S4UNIX
    u4name_ext( buf, sizeof(buf), "mdx", 0 ) ;
#else
    u4name_ext( buf, sizeof(buf), "MDX", 0 ) ;
#endif
  }

#ifdef S4DEBUG
  for ( i4_ptr = 0 ;; )
  {
    i4_ptr = (INDEX4 *)l4next(&d4->indexes, i4_ptr) ;
    if ( i4_ptr == 0 )
      break ;
    if ( !memcmp(i4_ptr->file.name, buf, (size_t) strlen(buf) ) )
      e4severe( e4parm, E4_PARM_IND ) ;
  }
#endif

  rc = file4open( &i4->file, c4, buf, 1 ) ;
  if ( rc )
    return 0 ;
  l4add( &d4->indexes, i4 ) ;

  if ( file4read_all( &i4->file, 0L, &i4->header, sizeof(I4HEADER) ) < 0 )
  {
    file4close( &i4->file ) ;
    return 0 ;
  }

#ifdef S4BYTE_SWAP
  i4->header.block_chunks = x4reverse_short( (void *)&i4->header.block_chunks ) ;
  i4->header.block_rw = x4reverse_short( (void *)&i4->header.block_rw ) ;
  i4->header.slot_size = x4reverse_short( (void *)&i4->header.slot_size ) ;
  i4->header.num_tags = x4reverse_short( (void *)&i4->header.num_tags ) ;
  i4->header.eof = x4reverse_long( (void *)&i4->header.eof ) ;
  i4->header.free_list = x4reverse_long( (void *)&i4->header.free_list ) ;
#endif

  /* Perform some checks */
  if ( i4->header.block_rw != i4->header.block_chunks*512  ||
      i4->header.block_chunks <= 0 ||
      i4->header.block_chunks > 63 ||
      i4->header.num_tags < 0  || i4->header.num_tags > 47 ||
      i4->header.eof <= 0L )
  {
#ifdef S4DEBUG_DEV
    e4describe( c4, e4index, buf, "i4open()", "checks failed" ) ;
#endif
    i4close( i4 ) ;
    e4( c4, e4index, buf ) ;
    return 0 ;
  }

  if ( file4read_all( &i4->file, 544L, tag_info, sizeof(tag_info)) < 0 )
  {
    i4close( i4 ) ;
    return 0 ;
  }

  for ( i_tag = 0; i_tag < (int) i4->header.num_tags; i_tag++ )
  {
    tag_ptr = (TAG4 *)mem4alloc( c4->tag_memory ) ;
    if ( tag_ptr == 0 )
    {
      i4close( i4 ) ;
      e4( c4, e4memory, 0 ) ;
      return 0 ;
    }

#ifdef S4BYTE_SWAP
    tag_info[i_tag].header_pos = x4reverse_long( (void *)&tag_info[i_tag].header_pos ) ;
    tag_info[i_tag].x1000 = 0x1000 ;
#endif

    if ( t4init( tag_ptr, i4, tag_info + i_tag ) < 0 )
    {
      i4close( i4 ) ;
      return 0 ;
    }

    l4add( &i4->tags, tag_ptr ) ;
  }

  i4->block_memory = mem4create( c4, c4->mem_start_block,
                                sizeof(B4BLOCK) + i4->header.block_rw -
                                sizeof(B4KEY_DATA) - sizeof(short) - sizeof(char[6]),
                                c4->mem_expand_block, 0 ) ;
  if ( i4->block_memory == 0 )
  {
    i4close(i4) ;
    return 0 ;
  }
#endif

#ifndef S4OPTIMIZE_OFF
  file4optimize( &i4->file, c4->optimize, OPT4INDEX ) ;
#endif

  return i4 ;
}

#ifndef S4OFF_WRITE
int S4FUNCTION i4shrink( INDEX4 *i4, long block_no )
{
#ifdef S4DEBUG
  if ( i4 == 0 || block_no < 0 )
    e4severe( e4parm, E4_I4SHRINK ) ;
#endif

  if ( i4->code_base->error_code < 0 )
    return -1 ;

#ifdef S4FOX
#ifdef S4BYTE_SWAP
  i4->tag_index->header.free_list = x4reverse_long( (void *)&i4->tag_index->header.free_list ) ;
#endif
  if ( file4write( &i4->file, block_no, (char *)&i4->tag_index->header.free_list,
                  sizeof(i4->tag_index->header.free_list)) < 0) return -1 ;
  i4->tag_index->header.free_list = block_no ;
#else
#ifdef S4BYTE_SWAP
  i4->header.free_list = x4reverse_long( (void *)&i4->header.free_list ) ;
#endif
  if ( file4write( &i4->file, block_no*I4MULTIPLY + sizeof(S4LONG),
                  (char *)&i4->header.free_list, sizeof(i4->header.free_list)) < 0) return -1 ;
  i4->header.free_list = block_no ;
#endif

  return 0 ;
}
#endif

TAG4 *S4FUNCTION i4tag( INDEX4 *i4, char *tag_name )
{
  char tag_lookup[11] ;
  TAG4 *tag_on ;

#ifdef S4VBASIC
  if ( c4parm_check( i4, 0, E4_I4TAG ) )
    return 0 ;
#endif

#ifdef S4DEBUG
  if ( i4 == 0 || tag_name == 0 )
    e4severe( e4parm, E4_I4TAG ) ;
#endif

  u4ncpy( tag_lookup, tag_name, sizeof( tag_lookup ) ) ;
  c4upper( tag_lookup ) ;

  for( tag_on = 0 ;; )
  {
    tag_on = (TAG4 *)l4next( &i4->tags, tag_on ) ;
    if ( tag_on == 0 )
      break ;
    if ( strcmp( tag_on->alias, tag_lookup ) == 0 )
      return tag_on ;
  }

  if ( i4->code_base->tag_name_error )
    e4( i4->code_base, e4tag_name, tag_name ) ;
  return 0 ;
}

#ifndef S4OFF_WRITE
/* Updates the header if the version has changed */
int S4FUNCTION i4update_header( INDEX4 *i4 )
{
#ifdef S4MDX
  TAG4 *tag_on ;
#ifdef S4BYTE_SWAP
  I4HEADER swap ;
#endif
#endif

#ifdef S4DEBUG
  if ( i4 == 0 )
    e4severe( e4parm, E4_I4UPDATE_HDR ) ;
#endif

  if ( i4->code_base->error_code < 0 )
    return -1 ;

#ifdef S4FOX
  /*
     #ifndef S4SINGLE
     i4->tag_index->header.version = i4->version_old + 2 ;
     #endif
     */
  if ( i4->version_old != i4->tag_index->header.version )
  {
#ifdef S4BYTE_SWAP
    i4->tag_index->header.root = x4reverse_long( (void *)&i4->tag_index->header.root ) ;
    i4->tag_index->header.free_list = x4reverse_long( (void *)&i4->tag_index->header.free_list ) ;
    i4->tag_index->header.key_len = x4reverse_short( (void *)&i4->tag_index->header.key_len ) ;
#else
    i4->tag_index->header.version = x4reverse_long( (void *)&i4->tag_index->header.version ) ;
#endif

    if (file4write(&i4->file,0L, (char *)&i4->tag_index->header, T4HEADER_WR_LEN) < 0) return -1;

#ifdef S4BYTE_SWAP
    i4->tag_index->header.root = x4reverse_long( (void *)&i4->tag_index->header.root ) ;
    i4->tag_index->header.free_list = x4reverse_long( (void *)&i4->tag_index->header.free_list ) ;
    i4->tag_index->header.key_len = x4reverse_short( (void *)&i4->tag_index->header.key_len ) ;
#else
    i4->tag_index->header.version = x4reverse_long( (void *)&i4->tag_index->header.version ) ;
#endif
    i4->version_old = i4->tag_index->header.version ;
  }
#else
  /*
     #ifndef S4SINGLE
     i4->changed = 1 ;
     #endif
     */
  if ( i4->changed )
  {
#ifdef S4BYTE_SWAP
    memcpy( (void *)&swap, (void *)&i4->header, sizeof(I4HEADER) ) ;

    swap.block_chunks = x4reverse_short( (void *)&swap.block_chunks ) ;
    swap.block_rw = x4reverse_short( (void *)&swap.block_rw ) ;
    swap.slot_size = x4reverse_short( (void *)&swap.slot_size ) ;
    swap.num_tags = x4reverse_short( (void *)&swap.num_tags ) ;
    swap.eof = x4reverse_long( (void *)&swap.eof ) ;
    swap.free_list = x4reverse_long( (void *)&swap.free_list ) ;

    if ( file4write(&i4->file,0L, (char *)&swap, sizeof(I4HEADER)) < 0 )
      return -1;
#else
    if ( file4write(&i4->file,0L, (char *)&i4->header, sizeof(I4HEADER) ) < 0)
      return -1;
#endif
    i4->changed = 0 ;
    for( tag_on = 0 ;; )
    {
      tag_on = (TAG4 *)l4next( &i4->tags, tag_on ) ;
      if ( tag_on == 0 )
        break ;
      if ( tag_on->changed == 1 )
      {
        tag_on->header.version++ ;
        tag_on->changed = 0 ;
        if ( file4write( &i4->file, tag_on->header_offset + 20L, &tag_on->header.version, sizeof(char) ) < 0)
          return -1;
        if ( tag_on->had_keys != tag_on->has_keys )
        {
          if ( file4write( &i4->file, tag_on->header_offset + 222 + sizeof( T4HEADER ), &tag_on->has_keys, sizeof(char) ) < 0)
            return -1;
          tag_on->had_keys = tag_on->has_keys ;
        }
      }
    }
  }
#endif

  return 0 ;
}
#endif

int S4FUNCTION t4version_check( TAG4 *t4, int do_seek, int update_version )
{
#ifndef S4OPTIMIZE_OFF
  if ( t4->index->file.do_buffer == 0 )
    return i4version_check( t4->index, do_seek, update_version ) ;
  else
    return 0 ;
#else
  return i4version_check( t4->index, do_seek, update_version ) ;
#endif
}

/* Reads the header, checks the version to see if the blocks need to be freed. */
int S4FUNCTION i4version_check( INDEX4 *i4, int do_seek, int update_version )
{
#ifndef S4SINGLE
  TAG4 *tag_on, *save_tag ;
  int rc, need_seek ;
  B4BLOCK *b4 ;

#ifdef S4DEBUG
  if ( i4 == 0 )
    e4severe( e4parm, E4_I4VERSION_CHK ) ;
#endif

  if ( i4->code_base->error_code < 0 )
    return -1 ;

  if ( i4lock_test( i4 ) )
    return 0 ;

#ifndef S4OPTIMIZE_OFF
  /* make sure read from disk unless file locked, etc. */
  if ( i4->file.do_buffer )  /* also makes sure 'opt' should exist */
    i4->code_base->opt.force_current = 1 ;
#endif

#ifdef S4FOX
  rc = file4read_all( &i4->file, 0L, &i4->tag_index->header, T4HEADER_WR_LEN ) ;
#ifdef S4BYTE_SWAP
  i4->tag_index->header.root = x4reverse_long( (void *)&i4->tag_index->header.root ) ;
  i4->tag_index->header.free_list = x4reverse_long( (void *)&i4->tag_index->header.free_list ) ;
  i4->tag_index->header.version = x4reverse_long( (void *)&i4->tag_index->header.version ) ;
  i4->tag_index->header.key_len = x4reverse_short( (void *)&i4->tag_index->header.key_len ) ;
#endif
#ifndef S4OPTIMIZE_OFF
  if ( i4->file.do_buffer )
    i4->code_base->opt.force_current = 0 ;
#endif
  if ( rc < 0 )
    return rc ;
  i4->tag_index->header.version = x4reverse_long( (void *)&i4->tag_index->header.version ) ;
  if ( i4->tag_index->header.version == i4->version_old )
    return 0 ;

  if ( update_version == 1 )
    i4->version_old = i4->tag_index->header.version ;
  else
    i4->tag_index->header.version = i4->version_old ;
#else
  rc = file4read_all( &i4->file, 0L, &i4->header, sizeof(I4HEADER) ) ;
#ifndef S4OPTIMIZE_OFF
  if ( i4->file.do_buffer )
    i4->code_base->opt.force_current = 0 ;
#endif
  if ( rc < 0 )
    return rc ;

#ifdef S4BYTE_SWAP
  i4->header.block_chunks = x4reverse_short( (void *)&i4->header.block_chunks ) ;
  i4->header.block_rw = x4reverse_short( (void *)&i4->header.block_rw ) ;
  i4->header.slot_size = x4reverse_short( (void *)&i4->header.slot_size ) ;
  i4->header.num_tags = x4reverse_short( (void *)&i4->header.num_tags ) ;
  i4->header.eof = x4reverse_long( (void *)&i4->header.eof ) ;
  i4->header.free_list = x4reverse_long( (void *)&i4->header.free_list ) ;
#endif
#ifndef S4OPTIMIZE_OFF
  /* make sure read from disk unless file locked, etc. */
  if ( i4->file.do_buffer )  /* also makes sure 'opt' should exist */
    i4->code_base->opt.force_current = 1 ;
#endif
  for( tag_on = 0 ;; )
  {
    tag_on = (TAG4 *)l4next( &i4->tags, tag_on ) ;
    if ( tag_on == 0 )
      break ;
    if ( file4read_all( &i4->file, tag_on->header_offset + 20L, &tag_on->header.version, sizeof(char) ) < 0 )
    {
#ifndef S4OPTIMIZE_OFF
      if ( i4->file.do_buffer )
        i4->code_base->opt.force_current = 0 ;
#endif
      return -1;
    }
    if ( file4read_all( &i4->file, tag_on->header_offset + 222 + sizeof( T4HEADER ), &tag_on->has_keys, sizeof(char) ) < 0 )
    {
#ifndef S4OPTIMIZE_OFF
      if ( i4->file.do_buffer )
        i4->code_base->opt.force_current = 0 ;
#endif
      return -1;
    }
    tag_on->had_keys = tag_on->has_keys ;
    tag_on->header.root = -1 ;
  }

#ifndef S4OPTIMIZE_OFF
  if ( i4->file.do_buffer )
    i4->code_base->opt.force_current = 0 ;
#endif
#endif

  need_seek = 0 ;
  save_tag = d4tag_selected( i4->data ) ;
  if ( save_tag != 0 )
  {
    /* remember the old position */
    if ( do_seek )
    {
      b4 = (B4BLOCK *)save_tag->blocks.last_node ;
      if ( b4 != 0 )
      {
#ifdef S4FOX
        if ( b4->header.n_keys != 0 )
#else
          if ( b4->n_keys != 0 )
#endif
          {
            if ( t4eof( save_tag ) )
              need_seek = 2 ;
            else
#ifdef S4FOX
              if ( b4leaf( b4 ) && b4->header.n_keys != 0 )
#else
                if ( b4leaf( b4 ) && b4->n_keys != 0 && b4->key_on < b4->n_keys )
#endif
                {
#ifdef S4FOX
                  memcpy( save_tag->code_base->saved_key, (void *)(b4key( b4, b4->key_on )), save_tag->header.key_len + sizeof(long) ) ;
#else
                  memcpy( save_tag->code_base->saved_key, (void *)(b4key( b4, b4->key_on )), save_tag->header.key_len + 2 * sizeof(S4LONG) ) ;
#endif
                  need_seek = 1 ;
                }
          }
      }
    }
  }

  for ( tag_on = 0 ;; )
  {
    tag_on = (TAG4 *)l4next( &i4->tags, tag_on ) ;
    if ( tag_on == 0 )
      break ;
    if ( t4free_all(tag_on) < 0 )  /* Should be a memory operation only */
#ifdef S4DEBUG
      e4severe( e4result, E4_I4VERSION_CHK ) ;
#else
    return -1 ;
#endif
  }

  switch ( need_seek )
  {
  case 1:
    t4go( save_tag, ((B4KEY_DATA *)save_tag->code_base->saved_key)->value, ((B4KEY_DATA *)save_tag->code_base->saved_key)->num ) ;
    break ;
  case 2:
    t4go_eof( save_tag ) ;
    break ;
  }
#endif
  return 0 ;
}

#endif  /*  ifndef  N4OTHER  */


#ifdef N4OTHER

/* This function closes all the tags corresponding with the index */
int S4FUNCTION i4close( INDEX4 *i4 )
{
  int final_rc, save_attempts ;
  CODE4 *c4 ;
  TAG4 *tag_on ;

#ifdef S4VBASIC
  if ( c4parm_check( i4, 0, E4_I4CLOSE ) )
    return -1 ;
#endif

#ifdef S4DEBUG
  if ( i4 == 0 )
    e4severe( e4parm, E4_I4CLOSE ) ;
#endif

  c4 = i4->code_base ;

  final_rc = c4->error_code ;
#ifndef S4SINGLE
  save_attempts = c4->lock_attempts ;
#endif
  c4->lock_attempts = -1 ;

#ifndef S4OFF_WRITE
  if ( i4->data )
    if ( d4update( i4->data ) < 0 )
      final_rc = e4set( c4, 0 ) ;
#endif

#ifndef S4SINGLE
  if ( i4unlock(i4) < 0 )
    final_rc = e4set( c4, 0 ) ;
#endif

  for( ;; )
  {
    tag_on = (TAG4 *)l4pop( &i4->tags ) ;
    if ( tag_on == 0 )
      break ;
    t4close( tag_on ) ;
    if ( t4free_all(tag_on) < 0 )
    {
      final_rc = e4set( c4, 0) ;
      break ;
    }

    expr4free( tag_on->expr ) ;
    expr4free( tag_on->filter ) ;
    mem4free( c4->tag_memory, tag_on ) ;
  }

  mem4release( i4->block_memory ) ;
  i4->block_memory = 0 ;

  if ( i4->data )
    l4remove( &i4->data->indexes, i4 ) ;

  mem4free( c4->index_memory, i4 ) ;
#ifndef S4SINGLE
  c4->lock_attempts = save_attempts ;
#endif
  e4set( c4, final_rc ) ;
  return  final_rc ;
}

#ifndef S4OFF_WRITE
/* This function flushes all the tags corresponding with the index */
int S4FUNCTION i4flush( INDEX4 *i4 )
{
  TAG4 *tag_on ;

#ifdef S4DEBUG
  if ( i4 == 0 )
    e4severe( e4parm, E4_I4FLUSH ) ;
#endif

#ifndef S4SINGLE
  if ( i4lock_test( i4 ) )
#endif
    for ( tag_on = 0 ;; )
    {
      tag_on = (TAG4 *)l4next( &i4->tags, tag_on ) ;
      if ( tag_on == 0 )
        break ;
      if ( t4flush(tag_on) < 0 )
        return -1 ;
      tag_on->header.root = -1L ;
    }
  return 0 ;
}

int S4FUNCTION i4update( INDEX4 *i4 )
{
  TAG4 *tag_on ;

#ifdef S4DEBUG
  if ( i4 == 0 )
    e4severe( e4parm, E4_I4UPDATE ) ;
#endif

  if ( i4->code_base->error_code < 0 )
    return -1 ;

#ifndef S4SINGLE
  if ( i4lock_test( i4 ) )
#endif
    for ( tag_on = 0 ;; )
    {
      tag_on = (TAG4 *)l4next( &i4->tags, tag_on ) ;
      if ( tag_on == 0 )
        break ;
      if ( t4update( tag_on ) < 0 )
        return -1 ;
      tag_on->header.root = -1L ;
    }
  return 0 ;
}
#endif

INDEX4 *S4FUNCTION i4open( DATA4 *d4, char *file_name )
{
  INDEX4 *i4 ;
  CODE4 *c4 ;
  TAG4 *tag ;
  int len, rc, i ;
  char buf[258], tag_buf[258], ext[3] ;
  int num_files ;
  FILE4SEQ_READ seqread ;
  char buffer[1024], t_names[258], first_byte ;
  int pos, i_pos, save_len, temp_len, hold_len ;

#ifdef S4VBASIC
  if ( c4parm_check( d4, 2, E4_I4OPEN ) )
    return 0 ;
#endif

#ifdef S4DEBUG
  if ( d4 == 0 )
    e4severe( e4parm, E4_I4OPEN ) ;

  if ( file_name == 0 )
    u4name_piece( buf, sizeof( buf ), d4->file.name, 0, 0 ) ;
  else
    u4name_piece( buf, sizeof( buf ), file_name, 0, 0 ) ;
  if ( d4index( d4, buf ) )
  {
    e4( d4->code_base, e4info, E4_INFO_IAO ) ;
    return 0 ;
  }
#endif

  c4 = d4->code_base ;
  if ( c4->error_code < 0 )
    return 0 ;

  if ( file_name == 0 )
  {
    u4ncpy( buf, d4->file.name, sizeof( buf ) ) ;
    u4name_ext( buf, sizeof( buf ), "CGP", 1 ) ;
  }
  else
  {
    u4ncpy( buf, file_name, sizeof(buf) ) ;
    c4upper( buf ) ;
    rc = u4name_ret_ext( ext, 3, buf ) ;
    if ( rc )  /* extension provided */
    {
      if ( rc == 3 )  /* check for .ndx or .cdx */
#ifdef S4NDX
#ifdef S4UNIX
        if ( memcmp( ext, "ndx", 3 ) == 0 )
#else
          if ( memcmp( ext, "NDX", 3 ) == 0 )
#endif
#else
#ifdef S4UNIX
            if ( memcmp( ext, "ntx", 3 ) == 0 )
#else
              if ( memcmp( ext, "NTX", 3 ) == 0 )
#endif
#endif
              {
                tag = t4open( d4, (INDEX4 *)0, file_name ) ;
                if ( tag == 0 )
                  return 0 ;
                return tag->index ;
              }
    }
    else
      u4name_ext( buf, sizeof(buf), "CGP", 0 ) ;
  }

  if ( c4->index_memory == 0 )
  {
    c4->index_memory = mem4create( c4, c4->mem_start_index, sizeof( INDEX4 ), c4->mem_expand_index, 0 ) ;
    if ( c4->index_memory == 0 )
      return 0 ;
  }

  if ( c4->tag_memory == 0 )
  {
    c4->tag_memory = mem4create( c4, c4->mem_start_tag, sizeof( TAG4 ), c4->mem_expand_tag, 0 ) ;
    if ( c4->tag_memory == 0 )
      return 0 ;
  }

  i4 = (INDEX4 *)mem4alloc( c4->index_memory ) ;
  if ( i4 == 0 )
    return 0 ;

  i4->code_base = c4 ;
  u4name_piece( i4->alias, sizeof( i4->alias ), buf, 0, 0 ) ;

  if ( file4open( &i4->file, c4, buf, 1 ) )
  {
    i4close( i4 ) ;
    if ( c4->open_error )
      e4( c4, e4open, E4_INFO_SET ) ;
    return 0 ;
  }

  file4seq_read_init( &seqread, &i4->file, 0, buffer, sizeof(buffer) ) ;

  pos = 0L ;
  rc = file4seq_read_all( &seqread, &first_byte, sizeof( first_byte ) ) ;
  if ( rc )
  {
    if ( c4->open_error )
      e4( c4, e4info, E4_INFO_REA ) ;
    return 0 ;
  }

  if ( first_byte < 65 )   /* old format - potential problem if >= 65 files in an old format file. */
  {
    num_files = first_byte ;
    if ( file4seq_read_all( &seqread, &first_byte, sizeof( first_byte ) ) )
    {
      if ( c4->open_error )
        e4( c4, e4info, E4_INFO_REA ) ;
      return 0 ;
    }

    for ( i = 0 ; i < num_files ; i++ )
    {
      if ( file4seq_read_all( &seqread, &len, sizeof( len ) ) )
      {
        file4close( &i4->file ) ;
        i4close( i4 ) ;
        if ( c4->open_error )
          e4( c4, e4info, E4_INFO_REA ) ;
        return 0 ;
      }
      pos += sizeof( len ) ;
      rc = u4name_path( tag_buf, sizeof( tag_buf ), buf ) ;
      tag_buf[rc+len] = '\0' ;
      if ( sizeof( tag_buf ) > rc + len ) /* make sure room to read */
        rc = file4seq_read_all( &seqread, tag_buf+rc, len ) ;
      else
        rc = -1 ;

      if ( rc )
      {
        if ( c4->open_error )
          e4( c4, e4info, E4_INFO_REA ) ;
        file4close( &i4->file ) ;
        i4close( i4 ) ;
        return 0 ;
      }

      pos += len ;
      if  ( t4open( d4, i4, tag_buf ) == 0 )
      {
        file4close( &i4->file ) ;
        i4close( i4 ) ;
        return 0 ;
      }
    }

    file4close( &i4->file ) ;
  }
  else
  {
    file4seq_read_init( &seqread, &i4->file, 0, buffer, sizeof(buffer) ) ;
    save_len = 0 ;

    for( len = sizeof( t_names ) ; len == sizeof( t_names ) ; )
    {
      len = file4seq_read( &seqread, t_names, sizeof( t_names )) ;
      if ( len < sizeof( t_names ) ) /* case where all read in now - free up this file handle for use */
      {
        if ( file4close ( &i4->file ) )
        {
          i4close( i4 ) ;
          if ( c4->open_error )
            e4( c4, e4info, E4_INFO_CLO ) ;
          return 0 ;
        }
        if ( len == 0 )
          break ;
      }
      for( i_pos = 0, pos = 0 ; pos < len ; )
      {
        switch( t_names[pos] )
        {
          /* cases where the values are ignored, or found name */
        case ' ':
        case '\r':
        case '\n':
        case '\t':
        case '\x1A':
          if ( i_pos < pos )  /* try to open the file */
          {
            temp_len = pos - i_pos ;
            if ( save_len == 0 )
            {
              rc = u4name_path( tag_buf, sizeof( tag_buf ), buf ) ;
              tag_buf[rc+temp_len] = '\0' ;
            }
            else
              rc = save_len ;
            memcpy( tag_buf + rc, &t_names[i_pos], temp_len ) ;

            if  ( t4open( d4, i4, tag_buf ) == 0 )
            {
              file4close( &i4->file ) ;
              i4close( i4 ) ;
              return 0 ;
            }
          }
          i_pos = ++pos ;
          break ;

          /* case where a name is attempted to be read in */
        default:
          pos++ ;
        }
      }
      temp_len = pos - i_pos ;
      rc = u4name_path( tag_buf, sizeof( tag_buf ), buf ) ;
      tag_buf[rc+temp_len] = '\0' ;
      memcpy( tag_buf + rc, &t_names[i_pos], temp_len ) ;
      save_len = rc + temp_len ;
    }

    if ( ( save_len - rc ) > 0 )  /* try to open the file */
    {
      if  ( t4open( d4, i4, tag_buf ) == 0 )
      {
        file4close( &i4->file ) ;
        i4close( i4 ) ;
        return 0 ;
      }
    }
  }

  l4add( &d4->indexes, i4 ) ;
  i4->data = d4 ;
  return i4 ;
}

TAG4 *S4FUNCTION i4tag( INDEX4 *i4, char *tag_name )
{
  char tag_lookup[11] ;
  TAG4 *tag_on ;

#ifdef S4VBASIC
  if ( c4parm_check( i4, 0, E4_I4TAG ) )
    return 0 ;
#endif

  if ( i4 == 0 || tag_name == 0 )
    return 0 ;

  u4ncpy( tag_lookup, tag_name, sizeof(tag_lookup) ) ;
  c4upper( tag_lookup ) ;

  for( tag_on = 0 ;; )
  {
    tag_on = (TAG4 *)l4next( &i4->tags, tag_on) ;
    if ( tag_on == 0 )
      break ;
    if ( strcmp( tag_on->alias, tag_lookup) == 0 )
      return tag_on ;
  }

  if ( i4->code_base->tag_name_error )
    e4( i4->code_base, e4tag_name, tag_name ) ;
  return 0 ;
}
#endif   /*  ifdef N4OTHER  */


int S4FUNCTION i4is_production( INDEX4 *i4 )
{
#ifdef N4OTHER
  return 0 ;
#else
#ifdef S4MDX
  return i4->header.is_production ;
#endif
#ifdef S4FOX
  int l1, l2 ;

  if ( i4->data->has_mdx )
  {
    l1 = strlen( i4->file.name ) - 4 ;  /* remove extenstion */
    l2 = strlen( i4->data->alias ) ;
    if ( l1 == l2 )
      return( memcmp( i4->file.name, i4->data->alias, l1 ) ? 0 : 1 ) ;
  }
  return 0 ;
#endif
#endif
}

#endif  /* S4INDEX_OFF */


#ifdef S4VB_DOS

INDEX4 * i4open_v( DATA4 *d4, char *name )
{
  char *name_ptr ;

  name_ptr = c4str(name) ;

  if (name_ptr[0] == '\0' )
    return i4open( d4, 0 ) ;

  return i4open( d4, name_ptr ) ;
}


TAG4 *i4tag_v( INDEX4 *ind, char *tag_name )
{
  return i4tag( ind, c4str(tag_name) ) ;
}

#endif
