/* d4seek.c   (c)Copyright Sequiter Software Inc., 1988-1996.  All rights reserved. */

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

#ifndef S4INDEX_OFF
#ifndef S4CLIENT

#ifdef S4FOX
/* because of possibilities of null, key may need to have a character
   indicater added */
static int tfile4stok( TAG4FILE *t4, char *buf, const char *str, int len )
{
   char *ptr = buf ;
   int hasNull = 0 ;

   if ( expr4nullLow( t4->expr, 0 ) )
   {
      if ( len == 0 )   /* seek for null */
      {
         *ptr = 0 ;
         return 1 ;
      }

      *ptr = (char)0x80 ;
      ptr++ ;
      hasNull = 1 ;
   }

   #ifdef S4VFP_KEY
      if ( tfile4type( t4 ) == r4str && tfile4vfpKey( t4 ) )
      {
         if ( len*2 > t4->header.keyLen )
         {
            #ifdef E4ANALYZE
               if ( (t4->header.keyLen-hasNull)%2 == 1 )
                  return error4( 0, e4index, E82107 ) ;
            #endif
               len = (t4->header.keyLen-hasNull) / 2 ;
         }
         len = t4strToVFPKey( ptr, str, len, t4->header.keyLen, &t4->vfpInfo ) ;
      }
      else
         (*t4->stok)( ptr, str, len ) ;
   #else
      (*t4->stok)( ptr, str, len ) ;
   #endif

   return len + hasNull ;
}

static void tfile4dtok( TAG4FILE *t4, char *buf, const double dkey )
{
   if ( expr4nullLow( t4->expr, 0 ) )
   {
      buf[0] = (char)0x80 ;

      (*t4->dtok)( buf+1, dkey ) ;
      return ;
   }
   else
   {
      (*t4->dtok)( buf, dkey ) ;
      return ;
   }
}
#else
   #define tfile4stok( t4, buf, str, len ) ((*((t4)->stok))( (buf), (str), (len) ), len)
   #define tfile4dtok( t4, buf, str ) (*((t4)->dtok))( (buf), (str) )
#endif

/* function to ensure the integrity of the seeks return value */
/* in single user mode, exclusive, or if file is locked, the data is assumed up to date, and not checked */
#ifdef P4ARGS_USED
   #pragma argsused
#endif
static int d4seekCheck( DATA4 *data, TAG4FILE *tag, const int rc, const char *buf, const int l )
{
   int rc2, len ;
   #ifndef S4SINGLE
      int skipped ;
      unsigned char *dbfKey ;
   #endif

   #ifndef S4SINGLE
      if ( rc == r4locked )
         return r4locked ;
      skipped = 0 ;
   #endif

   len = l ;

   if ( tfile4eof( tag ) )
      return d4goEof( data ) ;

   if ( len > tag->header.keyLen )
      len = tag->header.keyLen ;

   if ( d4recCountLessEq( data, tfile4recNo( tag ) ) == 0 )  /* past eof */
      while ( d4recCountLessEq( data, tfile4recNo( tag ) ) == 0 )  /* past eof */
      {
         if ( tfile4skip( tag, 1L ) == 0 )  /* eof */
            return d4goEof( data ) ;
         #ifndef S4SINGLE
            skipped = 1 ;
         #endif
      }

   #ifndef S4SINGLE
      #ifdef S4SERVER
         if ( dfile4lockTestFile( data->dataFile, data4clientId( data ), data4serverId( data ) ) )
      #else
         if ( d4lockTestFile( data ) )
      #endif
      {
   #endif
      #ifndef S4OFF_OPTIMIZE
         data->dataFile->hiPrio = 1 ;
      #endif
      rc2 = d4go( data, tfile4recNo( tag ) ) ;
      #ifndef S4OFF_OPTIMIZE
         data->dataFile->hiPrio = 0 ;
      #endif
      if ( rc2 )
         return rc2 ;
      return rc ;
   #ifndef S4SINGLE
      }
      for( ;; )
      {
         if ( d4recCountLessEq( data, tfile4recNo( tag ) ) != 0 )  /* valid spot */
         {
            #ifndef S4OFF_OPTIMIZE
               data->dataFile->hiPrio = 1 ;
            #endif
            rc2 = d4go( data, tfile4recNo( tag ) ) ;
            #ifndef S4OFF_OPTIMIZE
               data->dataFile->hiPrio = 0 ;
            #endif
            if ( rc2 )
               return rc2 ;

            if ( expr4context( tag->expr, data ) < 0 )
               return -1 ;
            if ( tfile4exprKey( tag, &dbfKey ) < 0 )
               return -1 ;
            #ifdef S4FOX
               if ( !u4keycmp( tfile4key( tag ), dbfKey, (unsigned)expr4keyLen( tag->expr ), (unsigned)tag->header.keyLen, 0, &tag->vfpInfo ) )   /* matched */
            #else
               if ( !(*tag->cmp)( tfile4key( tag ), dbfKey, (unsigned)expr4keyLen( tag->expr ) ) )  /* matched */
            #endif
            {
               if ( skipped )
               {
                  #ifdef S4FOX
                     rc2 = u4keycmp( dbfKey, buf, (unsigned)len, (unsigned)expr4keyLen( tag->expr ), 0, &tag->vfpInfo ) ;
                  #else
                     rc2 = (*tag->cmp)( dbfKey, buf, (unsigned)expr4keyLen( tag->expr ) ) ;
                  #endif
                  if ( rc2 == 0 )   /* worked */
                     return rc2 ;
                  if ( rc2 > 0 )
                     return r4after ;
                  /* other wise, if < 0, can't return r4after, so go down and skip next */
               }
               else
                  return rc ;
            }
         }

         /* try next record */
         if ( tfile4skip( tag, 1L ) == 0 )  /* eof */
            return d4goEof( data ) ;
         if ( error4code( data->codeBase ) < 0 )
            return -1 ;
         skipped = 1 ;
      }
   #endif
}
#endif

#ifdef S4CLIENT
static int d4seekServer( DATA4 *data, const void *searchData, const int len, const int fromCurrentPos, const int seekType )
{
   CONNECTION4 *connection ;
   CONNECTION4GO_INFO_OUT *out ;
   CONNECTION4SEEK_INFO_IN infoIn ;
   int rc, saveRc ;
   TAG4 *tag ;
   CODE4 *c4 ;
   #ifdef E4ANALYZE
      int e4anLen ;
   #endif

   #ifdef E4PARM_HIGH
      if ( data == 0 || searchData == 0 || len < 0 || fromCurrentPos < 0 || fromCurrentPos > 1 ||
         ( seekType != CON4SEEK && seekType != CON4SEEK_DBL ) )
         return error4( 0, e4parm, E92906 ) ;
   #endif

   c4 = data->codeBase ;

   tag = d4tagDefault( data ) ;
   if ( tag == 0 )
      return r4noTag ;

   #ifndef S4OFF_WRITE
      rc = d4updateRecord( data, 0  ) ; /* was 1 */
      if ( rc )
         return rc ;
   #endif

   connection = data->dataFile->connection ;
   if ( connection == 0 )
      return error4stack( c4, rc, E92906 ) ;

   connection4assign( connection, seekType, data4clientId( data ), data4serverId( data ) ) ;
   #ifdef E4ANALYZE
      e4anLen = sizeof( infoIn.tagName ) ;
      if (  e4anLen != even4up( LEN4TAG_ALIAS + 1 ) || e4anLen != even4up( sizeof( tag->tagFile->alias ) ) )
         return error4( c4, e4struct, E92906 ) ;
   #endif
   memcpy( infoIn.tagName, tag->tagFile->alias, LEN4TAG_ALIAS ) ;
   infoIn.keyLen = len ;
   infoIn.fromCurrentPos = fromCurrentPos ;
   if ( fromCurrentPos == 1 )   /* verify at valid position, first */
   {
      if ( d4eof( data ) || d4bof( data ) )
         infoIn.startPos = 0 ;
      else
         infoIn.startPos = d4recNo( data ) ;   /* for seek next */
   }
   connection4addData( connection, &infoIn, sizeof(CONNECTION4SEEK_INFO_IN), 0 ) ;
   connection4addData( connection, searchData, len, 0 ) ;
   connection4addData( connection, "", 1, 0 ) ;  /* null end for character double seeks */
   saveRc = connection4repeat( connection, -2, -1, -1, data ) ;
   if ( saveRc < 0 )
      return connection4errorDescribe( connection, c4, saveRc, E92906, tag->tagFile->alias, 0, 0 ) ;
   if ( saveRc == r4locked )
      return r4locked ;
   if ( saveRc == r4eof )
   {
      rc = d4goEof( data ) ;
      if ( rc == 0 )
         rc = r4eof ;
      return rc ;
   }
   if ( connection4len( connection ) != (long)sizeof( CONNECTION4GO_INFO_OUT ) + (long)d4recWidth( data ) )
      return error4( c4, e4packetLen, E92906 ) ;
   out = (CONNECTION4GO_INFO_OUT *)connection4data( connection ) ;

   if ( saveRc == r4entry )
      rc = r4entry ;
   else rc = 0 ;
   rc = d4goVirtual( data, out->recNo, rc, out, connection ) ;
   if ( rc != 0 )
      return rc ;
   return saveRc ;
}
#endif

int S4FUNCTION d4seek( DATA4 *data, const char *str )
{
   #ifdef E4PARM_HIGH
      if ( data == 0 || str == 0 )
         return error4( 0, e4parm_null, E92907 ) ;
   #endif

   #ifdef S4CLIENT
      return d4seekServer( data, str, strlen( str ), 0, CON4SEEK ) ;
   #else
      return d4seekN( data, str, strlen( str ) ) ;
   #endif
}

#ifndef S4CB51
int S4FUNCTION d4seekNext( DATA4 *data, const char *str )
{
   #ifdef S4CLIENT
      int oldErrGo, rc ;
      CODE4 *c4 ;
   #endif

   #ifdef E4PARM_HIGH
      if ( data == 0 || str == 0 )
         return error4( 0, e4parm_null, E92908 ) ;
   #endif

   #ifdef S4CLIENT
      c4 = data->codeBase ;
      oldErrGo = c4->errGo ;   /* avoid for r4entry */
      c4->errGo = 0 ;
      rc = d4seekServer( data, str, strlen( str ), 1, CON4SEEK ) ;
      c4->errGo = oldErrGo ;
      if ( rc < 0 && error4code( c4 ) == 0 )
         return error4( c4, rc, E92908 ) ;
      return rc ;
   #else
      return d4seekNextN( data, str, strlen( str ) ) ;
   #endif
}

int S4FUNCTION d4seekNextN( DATA4 *data, const char *str, const int l )
{
   CODE4 *c4 ;
   int rc ;
   #ifndef S4SERVER
      #ifndef S4OFF_MULTI
         int oldErrGo ;
      #endif
   #endif
   #ifndef S4CLIENT
      int len, rc2, saveGo ;
      TAG4 *tag ;
      TAG4FILE *tfile ;
      char buf[I4MAX_KEY_SIZE] ;
      unsigned char *dbfKey ;
   #endif

   #ifdef S4VBASIC
      if ( c4parm_check( data, 2, E92905 ) )
         return 0 ;
   #endif

   #ifdef E4PARM_HIGH
      if ( data == 0 || str == 0 )
         return error4( 0, e4parm_null, E92905 ) ;
   #endif

   #ifdef S4CLIENT
      c4 = data->codeBase ;
      oldErrGo = c4->errGo ;   /* avoid for r4entry */
      c4->errGo = 0 ;
      rc = d4seekServer( data, str, l, 1, CON4SEEK ) ;
      c4->errGo = oldErrGo ;
      if ( rc < 0 && error4code( c4 ) == 0 )
         return error4( c4, rc, E92908 ) ;
      return rc ;
   #else
      len = l ;
      c4 = data->codeBase ;
      if ( c4 == 0 )
         return e4info ;
      if ( error4code( c4 ) < 0 )
         return -1 ;

      tag = d4tagDefault( data ) ;
      if ( tag == 0 )
         return r4noTag ;

      #ifndef S4OFF_WRITE
         rc = d4updateRecord( data, 0 ) ; /* was 1 */
         if ( rc )
            return rc ;
      #endif

      tfile = tag->tagFile ;

      #ifdef S4UNIX
         #ifdef S4MDX
            switch ( tfile->keyType )
            {
               case r4num:
                  c4bcdFromA( buf, str, len ) ;
                  break ;
               case r4date:
                  t4strToDateMdx( buf, str, len ) ;
                  break ;
               case r4str:
                  t4noChangeStr( buf, str, len ) ;
                  break ;
               default:
                  return error4( data->codeBase, e4index, E82901 ) ;
            }
         #endif
         #ifdef S4FOX
            switch ( tfile->keyType )
            {
               case r4num:
               case r4numDoub:
                  t4strToFox( buf, str, len ) ;
                  break ;
               case r4date:
               case r4dateDoub:
                  t4dtstrToFox( buf, str, len ) ;
                  break ;
               case r4str:
                  t4noChangeStr( buf, str, len ) ;
                  break ;
               case r4log:
                  t4strToLog( buf, str, len ) ;
                  break ;
               default:
                  return error4( data->codeBase, e4index, E82901 ) ;
            }
         #endif
         #ifdef S4CLIPPER
            switch ( tfile->keyType )
            {
               case r4num:
               case r4numDoub:
                  t4strToClip( buf, str, len ) ;
                  break ;
               case r4date:
               case r4dateDoub:
               case r4str:
                  t4noChangeStr( buf, str, len ) ;
                  break ;
               default:
                  return error4( data->codeBase, e4index, E82901 ) ;
            }
         #endif
         #ifdef S4NDX
            switch ( tfile->keyType )
            {
               case r4num:
               case r4numDoub:
                  t4strToDoub( buf, str, len ) ;
                  break ;
               case r4date:
               case r4dateDoub:
                  t4strToDateMdx( buf, str, len ) ;
                  break ;
               case r4str:
                  t4noChangeStr( buf, str, len ) ;
                  break ;
               default:
                  return error4( data->codeBase, e4index, E82901 ) ;
            }
         #endif
      #else
          len = tfile4stok( tag->tagFile, buf, str, len ) ;
      #endif

      if ( tfile4type( tfile ) != r4str )
         len = tfile->header.keyLen ;
      else
         if ( len <= 0 )
            len = strlen( str ) ;

      t4versionCheck( tag, 0, 0 ) ;

      if ( tfile4recNo( tfile ) != data->recNum )
      {
         #ifndef S4OFF_MULTI
            if ( d4lockTest( data, data->recNum ) == 0 )  /* ensure latest from disk */
            {
               #ifndef S4SERVER
                  oldErrGo = data->codeBase->errGo ;
                  data->codeBase->errGo = 0 ;
               #endif
               #ifndef S4OFF_OPTIMIZE
                  data->dataFile->hiPrio = 1 ;
               #endif
               saveGo = d4go( data, data->recNum ) ;
               #ifndef S4OFF_OPTIMIZE
                  data->dataFile->hiPrio = 0 ;
               #endif
               #ifndef S4SERVER
                  data->codeBase->errGo = oldErrGo ;
               #endif
               if ( saveGo < 0 )
                  return saveGo ;
            }
            else
         #endif
            saveGo = 0 ;

         if ( saveGo != r4entry )
         {
            if ( expr4context( tfile->expr, data ) < 0 )
               return -1 ;

            tfile4exprKey( tfile, &dbfKey ) ;

            rc = tfile4go( tfile, dbfKey, data->recNum, 0 ) ;
         }
      }
      else
      {
         saveGo = 0 ;
         if ( expr4context( tfile->expr, data ) < 0 )
            return -1 ;
         rc = tfile4exprKey( tfile, &dbfKey ) ;
      }

      if ( rc < 0 )
         return rc ;

      if ( len > tfile->header.keyLen )
         len = tfile->header.keyLen ;

      if ( saveGo == r4entry )  /* at eof or bof, so present record not match, so regular seek */
      {
         rc = tfile4seek( tfile, buf, len ) ;
         return d4seekCheck( data, tfile, rc, buf, len ) ;  /* return a valid value */
      }

      /* first check where the datafile currently is in relation to the
         seeked-for item */
      #ifdef S4FOX
         rc = u4keycmp( buf, dbfKey, len, (unsigned)expr4keyLen( tfile->expr ), 0, &tag->tagFile->vfpInfo ) ;
      #else
         rc = (*tfile->cmp)( buf, dbfKey, (unsigned)len ) ;
      #endif

      if ( rc != 0 )  /* present record not match, so regular seek */
      {
         rc = tfile4seek( tfile, buf, len ) ;
         return d4seekCheck( data, tfile, rc, buf, len ) ;  /* return a valid value */
      }

      rc = (int)tfile4dskip( tfile, 1L ) ;
      if ( rc == 0 )   /* on a valid entry, but it is last entry, so r4entry returned */
         return r4entry ;
      if ( rc < 0 )
         return rc ;
      else
         rc = 0 ;

      /* need to check the key against the seek key to see whether or not
         we have gone too far */
      #ifdef S4FOX
         /* fox version returns # matching bytes, so subtract to see if valid value */
         rc = u4keycmp( tfile4key( tfile ), buf, (unsigned)len, (unsigned)tfile->header.keyLen, 0, &tfile->vfpInfo ) ;
      #else
         rc = (*tfile->cmp)( buf, tfile4key( tfile ), (unsigned)len ) ;
      #endif
      rc2 = d4seekCheck( data, tfile, rc, buf, len ) ;  /* return a valid value */
      if ( rc != 0 )
         return r4entry ;
      return rc2 ;
   #endif
}
#endif /* S4CB51 */

int S4FUNCTION d4seekN( DATA4 *data, const char *str, const int l )
{
   #ifndef S4CLIENT
      TAG4 *tag ;
      TAG4FILE *tfile ;
      int rc, len ;
      CODE4 *c4 ;
      char buf[I4MAX_KEY_SIZE] ;
   #endif

   #ifdef S4VBASIC
      if ( c4parm_check( data, 2, E92903 ) )
         return 0 ;
   #endif

   #ifdef E4PARM_HIGH
      if ( data == 0 || str == 0 )
         return error4( 0, e4parm_null, E92903 ) ;
   #endif

   #ifdef S4CLIENT
      return d4seekServer( data, str, l, 0, CON4SEEK ) ;
   #else
      len = l ;
      c4 = data->codeBase ;
      if ( c4 == 0 )
         return e4info ;
      if ( error4code( c4 ) < 0 )
         return -1 ;

      tag = d4tagDefault( data ) ;
      if ( tag == 0 )
         return r4noTag ;

      #ifndef S4OFF_WRITE
         rc = d4updateRecord( data, 0 ) ;    /* was 1*/
         if ( rc )
            return rc ;
      #endif

      tfile = tag->tagFile ;

      #ifdef S4FOX
         if ( d4version( data ) == 0x30 && l == 0 )  /* means seek for .NULL. */
         {
            len = tfile->header.keyLen ;
            memset( buf, 0, len ) ;
         }
         else
      #endif
      {  /* for S4FOX */
         #ifdef S4UNIX
            #ifdef S4MDX
               switch ( tfile->keyType )
               {
                  case r4num:
                     c4bcdFromA( buf, str, len ) ;
                     break ;
                  case r4date:
                     t4strToDateMdx( buf, str, len ) ;
                     break ;
                  case r4str:
                     if ( len > tfile->header.keyLen )
                        len = tfile->header.keyLen ;
                     t4noChangeStr( buf, str, len ) ;
                     break ;
                  default:
                     return error4( data->codeBase, e4index, E82901 ) ;
               }
            #endif
            #ifdef S4FOX
               switch ( tfile->keyType )
               {
                  case r4num:
                  case r4numDoub:
                     t4strToFox( buf, str, len ) ;
                     break ;
                  case r4date:
                  case r4dateDoub:
                     t4dtstrToFox( buf, str, len ) ;
                     break ;
                  case r4str:
                     if ( len > tfile->header.keyLen )
                        len = tfile->header.keyLen ;
                     t4noChangeStr( buf, str, len ) ;
                     break ;
                  case r4log:
                     t4strToLog( buf, str, len ) ;
                     break ;
                  default:
                     return error4( data->codeBase, e4index, E82901 ) ;
               }
            #endif
            #ifdef S4CLIPPER
               switch ( tfile->keyType )
               {
                  case r4num:
                  case r4numDoub:
                     t4strToClip( buf, str, len ) ;
                     break ;
                  case r4date:
                  case r4dateDoub:
                  case r4str:
                     if ( len > tfile->header.keyLen )
                        len = tfile->header.keyLen ;
                     t4noChangeStr( buf, str, len ) ;
                     break ;
                  default:
                     return error4( data->codeBase, e4index, E82901 ) ;
               }
            #endif
            #ifdef S4NDX
               switch ( tfile->keyType )
               {
                  case r4num:
                  case r4numDoub:
                     t4strToDoub( buf, str, len ) ;
                     break ;
                  case r4date:
                  case r4dateDoub:
                     t4strToDateMdx( buf, str, len ) ;
                     break ;
                  case r4str:
                     if ( len > tfile->header.keyLen )
                        len = tfile->header.keyLen ;
                     t4noChangeStr( buf, str, len ) ;
                     break ;
                  default:
                     return error4( data->codeBase, e4index, E82901 ) ;
               }
            #endif
         #else
            #ifdef S4CLIPPER
               switch ( tfile4type( tfile ) )
               {
                  case r4num:
                  case r4numDoub:
                     if ( tfile4stok( tfile, buf, str, tfile->header.keyLen ) < 0 )
                        return -1 ;
                     break ;
                  default:
                     if ( tfile4stok( tfile, buf, str, len ) < 0 )
                        return -1 ;
                     break ;
               }
            #else
               len = tfile4stok( tfile, buf, str, len ) ;
            #endif
         #endif

         if ( tfile4type( tfile ) != r4str )
            len = tfile->header.keyLen ;
         else
            if ( len <= 0 )
               len = strlen( str ) ;
      }  /* for S4FOX */

      t4versionCheck( tag, 0, 0 ) ;
      rc = tfile4seek( tfile, buf, len ) ;

      return d4seekCheck( data, tfile, rc, buf, len ) ;  /* return a valid value */
   #endif
}

int S4FUNCTION d4seekDouble( DATA4 *data, const double dkey )
{
   #ifndef S4CLIENT
      TAG4 *tag ;
      TAG4FILE *tfile ;
      CODE4 *c4 ;
      int rc ;
      char buf[I4MAX_KEY_SIZE] ;
      #ifdef S4CLIPPER
         int len ;
      #endif
   #endif

   #ifdef S4VBASIC
      if ( c4parm_check( data, 2, E92903 ) )
         return 0 ;
   #endif

   #ifdef E4PARM_HIGH
      if ( data == 0 )
         return error4( 0, e4parm_null, E92904 ) ;
   #endif

   #ifdef S4CLIENT
      return d4seekServer( data, &dkey, sizeof( dkey ), 0, CON4SEEK_DBL ) ;
   #else
      c4 = data->codeBase ;
      if ( c4 == 0 )
         return e4info ;
      if ( error4code( c4 ) < 0 )
         return -1 ;

      tag = d4tagDefault( data ) ;
      if ( tag == 0 )
         return r4noTag ;

      #ifndef S4OFF_WRITE
         rc = d4updateRecord( data, 0 ) ;   /* was 1 */
         if ( rc )
            return rc ;
      #endif

      tfile = tag->tagFile ;

      #ifdef S4CLIPPER
         if ( tfile->dtok == 0 )
         {
            len = tfile->header.keyLen ;
            c4dtoa45( dkey, buf, len, tfile->header.keyDec ) ;
            if ( buf[0] == '*' )  /* unknown overflow result */
               return -1 ;
            c4clip( buf, len ) ;
         }
         else
      #else
         if ( tfile->dtok == 0 )
            return error4( data->codeBase, e4seek, E82902 ) ;
      #endif

      #ifdef S4UNIX
         #ifdef S4MDX
            switch ( tfile->keyType )
            {
               case r4num:
                  c4bcdFromD( buf, dkey ) ;
                  break ;
               case r4date:
                  t4noChangeDouble( buf, dkey ) ;
                  break ;
               case r4str:
                  break ;
               default:
                  return error4( data->codeBase, e4index, E82901 ) ;
            }
         #endif
         #ifdef S4FOX
            switch ( tfile->keyType )
            {
               case r4num:
               case r4numDoub:
               case r4date:
               case r4dateDoub:
                  t4dblToFox( buf, dkey ) ;
                  break ;
               case r4str:
               case r4log:
                  break ;
               default:
                  return error4( data->codeBase, e4index, E82901 ) ;
            }
         #endif
         #ifdef S4CLIPPER
            switch ( tfile->keyType )
            {
               case r4num:
               case r4numDoub:
               case r4str:
                  break ;
               case r4date:
               case r4dateDoub:
                  t4dateDoubToStr( buf, dkey ) ;
                  break ;
               default:
                  return error4( data->codeBase, e4index, E82901 ) ;
            }
         #endif
         #ifdef S4NDX
            switch ( tfile->keyType )
            {
               case r4num:
               case r4numDoub:
               case r4date:
               case r4dateDoub:
                  t4noChangeDouble( buf, dkey ) ;
                  break ;
               case r4str:
                  break ;
               default:
                  return error4( data->codeBase, e4index, E82901 ) ;
            }
         #endif
      #else
         tfile4dtok( tfile, buf, dkey ) ;
      #endif

      t4versionCheck( tag, 0, 0 ) ;
      rc = tfile4seek( tfile, buf, tfile->header.keyLen ) ;

      return d4seekCheck( data, tfile, rc, buf, tfile->header.keyLen ) ;  /* return a valid value */
   #endif
}

#ifndef S4CB51
int S4FUNCTION d4seekNextDouble( DATA4 *data, const double dkey )
{
   CODE4 *c4 ;
   int rc ;
   #ifndef S4SERVER
      #ifndef S4OFF_MULTI
         int oldErrGo ;
      #endif
   #endif
   #ifndef S4CLIENT
      int rc2, saveGo ;
      TAG4 *tag ;
      TAG4FILE *tfile ;
      char buf[I4MAX_KEY_SIZE] ;
      unsigned char *dbfKey ;
      #ifdef S4CLIPPER
         int len ;
      #endif
   #endif

   #ifdef S4VBASIC
      if ( c4parm_check( data, 2, E92909 ) )
         return 0 ;
   #endif

   #ifdef E4PARM_HIGH
      if ( data == 0 )
         return error4( 0, e4parm_null, E92909 ) ;
   #endif

   #ifdef S4CLIENT
      c4 = data->codeBase ;
      oldErrGo = c4->errGo ;   /* avoid for r4entry */
      c4->errGo = 0 ;
      rc = d4seekServer( data, &dkey, sizeof( dkey ), 1, CON4SEEK_DBL ) ;
      c4->errGo = oldErrGo ;
      if ( rc < 0 && error4code( c4 ) == 0 )
         return error4( c4, rc, E92908 ) ;
      return rc ;
   #else
      c4 = data->codeBase ;
      if ( c4 == 0 )
         return e4info ;
      if ( error4code( c4 ) < 0 )
         return -1 ;

      tag = d4tagDefault( data ) ;
      if ( tag == 0 )
         return r4noTag ;

      #ifndef S4OFF_WRITE
         rc = d4updateRecord( data, 0 ) ;      /* was 1 */
         if ( rc )
            return rc ;
      #endif

      tfile = tag->tagFile ;
      #ifdef S4CLIPPER
         if ( tfile->dtok == 0 )
         {
            len = tfile->header.keyLen ;
            c4dtoa45( dkey, buf, len, tfile->header.keyDec ) ;
            if ( buf[0] == '*' )  /* unknown overflow result */
               return -1 ;
            c4clip( buf, len ) ;
         }
         else
      #else
         if ( tfile->dtok == 0 )
            return error4( data->codeBase, e4seek, E82902 ) ;
      #endif

      #ifdef S4UNIX
         #ifdef S4MDX
            switch ( tfile->keyType )
            {
               case r4num:
                  c4bcdFromD( buf, dkey ) ;
                  break ;
               case r4date:
                  t4noChangeDouble( buf, dkey ) ;
                  break ;
               case r4str:
                  break ;
               default:
                  return error4( data->codeBase, e4index, E82901 ) ;
            }
         #endif
         #ifdef S4FOX
            switch ( tfile->keyType )
            {
               case r4num:
               case r4numDoub:
               case r4date:
               case r4dateDoub:
                  t4dblToFox( buf, dkey ) ;
                  break ;
               case r4str:
               case r4log:
                  break ;
               default:
                  return error4( data->codeBase, e4index, E82901 ) ;
            }
         #endif
         #ifdef S4CLIPPER
            switch ( tfile->keyType )
            {
               case r4num:
               case r4numDoub:
               case r4str:
                  break ;
               case r4date:
               case r4dateDoub:
                  t4dateDoubToStr( buf, dkey ) ;
                  break ;
               default:
                  return error4( data->codeBase, e4index, E82901 ) ;
            }
         #endif
         #ifdef S4NDX
            switch ( tfile->keyType )
            {
               case r4num:
               case r4numDoub:
               case r4date:
               case r4dateDoub:
                  t4noChangeDouble( buf, dkey ) ;
                  break ;
               case r4str:
                  break ;
               default:
                  return error4( data->codeBase, e4index, E82901 ) ;
            }
         #endif
      #else
         tfile4dtok( tfile, buf, dkey ) ;
      #endif

      t4versionCheck( tag, 0, 0 ) ;

      if ( tfile4recNo( tfile ) != data->recNum )
      {
         #ifndef S4OFF_MULTI
            if ( d4lockTest( data, data->recNum ) == 0 )  /* ensure latest from disk */
            {
               #ifndef S4SERVER
                  oldErrGo = data->codeBase->errGo ;
                  data->codeBase->errGo = 0 ;
               #endif
               #ifndef S4OFF_OPTIMIZE
                  data->dataFile->hiPrio = 1 ;
               #endif
               saveGo = d4go( data, data->recNum ) ;
               #ifndef S4OFF_OPTIMIZE
                  data->dataFile->hiPrio = 0 ;
               #endif
               #ifndef S4SERVER
                  data->codeBase->errGo = oldErrGo ;
               #endif
               if ( saveGo < 0 )
                  return saveGo ;
            }
            else
         #endif
            saveGo = 0 ;

         if ( saveGo != r4entry )
         {
            if ( expr4context( tfile->expr, data ) < 0 )
               return -1 ;
            rc = tfile4exprKey( tfile, &dbfKey ) ;
            if ( rc < 0 )
               return rc ;

            rc = tfile4go( tfile, dbfKey, data->recNum, 0 ) ;
         }
      }
      else
      {
         saveGo = 0 ;
         if ( expr4context( tfile->expr, data ) < 0 )
            return -1 ;
         rc = tfile4exprKey( tfile, &dbfKey ) ;
      }

      if ( rc < 0 )
         return rc ;

      if ( saveGo == r4entry )  /* at eof or bof, so present record not match, so regular seek */
      {
         rc = tfile4seek( tfile, buf, tfile->header.keyLen ) ;
         return d4seekCheck( data, tfile, rc, buf, tfile->header.keyLen ) ;  /* return a valid value */
      }

      /* first check where the datafile currently is in relation to the
         seeked-for item */
      #ifdef S4FOX
         rc = u4keycmp( dbfKey, buf, (unsigned)tfile->header.keyLen, (unsigned)expr4keyLen( tfile->expr ), 0, &tfile->vfpInfo ) ;
      #else
         rc = (*tfile->cmp)( buf, dbfKey, (unsigned)tfile->header.keyLen ) ;
      #endif

      if ( rc != 0 )  /* present record not match, so regular seek */
      {
         rc = tfile4seek( tfile, buf, tfile->header.keyLen ) ;
         return d4seekCheck( data, tfile, rc, buf, tfile->header.keyLen ) ;  /* return a valid value */
      }

      rc = (int)tfile4dskip( tfile, 1L ) ;
      if ( rc == 0 )
         return r4after ;
      if ( rc < 0 )
         return rc ;
      else
         rc = 0 ;

      /* need to check the key against the seek key to see whether or not
         we have gone too far */
      #ifdef S4FOX
         rc = u4keycmp( tfile4key( tfile ), buf, tfile->header.keyLen, (unsigned)tfile->header.keyLen, 0, &tfile->vfpInfo ) ;
      #else
         rc = (*tfile->cmp)( buf, tfile4key( tfile ), tfile->header.keyLen ) ;
      #endif
      rc2 = d4seekCheck( data, tfile, rc, buf, tfile->header.keyLen ) ;  /* return a valid value */
      if ( rc != 0 )
         return r4entry ;
      return rc2 ;
   #endif
}
#endif  /* S4CB51 */

#endif  /* S4INDEX_OFF */

#ifdef S4VB_DOS

int d4seek_v( DATA4 *data, const char *seek )
{
   return d4seek( data, c4str(seek) ) ;
}

#endif