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

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

#ifdef S4TEMP
#include "t4test.h"
#endif

#include <time.h>

#ifndef S4UNIX
#ifndef S4IBMOS2
#ifndef __TURBOC__
#include <sys\locking.h>
#define S4LOCKING
#endif
#ifdef __ZTC__
extern int  errno ;
#endif
#ifdef _MSC_VER
#include <sys\types.h>
#include <sys\locking.h>
#endif
#ifdef __TURBOC__
/*      extern int cdecl errno ; */
#endif
#endif

#include <sys\stat.h>
#include <share.h>
#endif

#include <fcntl.h>
#include <errno.h>

#ifdef __SC__
#include <dos.h>
#endif

#ifdef S4ERRNO
extern int errno ;
#endif


#ifdef S4NO_FILELENGTH
#ifdef S4MACINTOSH
long filelength( int hand )
{
  long filelen ;

  if ( GetEOF(hand, &filelen) != 0 )
    e4severe( e4result, "filelength(): GetEOF()" ) ;

  return filelen ;
}
#else
#ifdef S4WIN32
long filelength( int hand )
{
  long rc ;

  rc = (long)GetFileSize( (HANDLE)hand, NULL ) ;

  if ( rc == -1L )
    e4severe( e4result, "filelength()" ) ;

  return rc ;
}
#else
#include  <sys/types.h>
#include  <sys/stat.h>

long filelength( hand )
  int hand  ;
{
  struct stat   str_stat ;

  if (fstat( hand, &str_stat ) )
    e4severe( e4result, "filelength()" ) ;

  return( (long) str_stat.st_size ) ;
}
#endif
#endif
#endif

#ifdef S4MACINTOSH
long MAClseek(int hand, long offset, int fromwhere, int extend )
{
  long filelen ;
  if ( offset != 0 )
  {
    filelen = filelength( hand ) ;

    if (extend)
    {
      if ( filelen < offset )
        SetEOF( hand, offset ) ;
    }
    else
      if ( filelen < offset )
      {
        if ( SetFPos(hand, fsFromStart, filelen ) == 0 )
          return offset ;
        else
          return -1L ;
      }
  }

  if ( SetFPos(hand, fsFromStart, offset ) == 0 )
    return offset ;
  else
    return -1L ;
}
#endif

#ifdef S4LSEEK
/* if extend is set, file is extended, else lseek to EOF */
long f4lseek(FILE4 *file, long offset, int fromwhere, int extend )
{
  long filelen ;
  if ( offset != 0 )
  {
    filelen = filelength( file->hand ) ;

    if (extend)
    {
      if ( filelen < offset )
        file4change_size( file, offset ) ;
    }
    else
      if ( filelen < offset )
      {
        if ( lseek( file->hand, filelen, 0 ) )
          return offset ;
        else
          return -1L ;
      }
  }
  return lseek( file->hand, offset, fromwhere ) ;
}
#endif

#ifdef S4NO_CHSIZE

#define E4MAXLINE 129   /* maximum file path length */

#ifdef S4MACINTOSH
int S4FUNCTION file4change_size( FILE4 *f4, long size )
{
  if ( SetEOF( f4->hand, size ) != 0 )
    e4severe( e4result, "file4change_size(): SetEOF" ) ;

  return 0 ;
}
#else
int S4FUNCTION file4change_size( FILE4 *f4, long size )
{
  char *buffer ;
  char temp_name[E4MAXLINE], file_name[E4MAXLINE] ;
  long buffer_size, filelen, amount_read = 0, extra_size = 0 ;
  CODE4 *c4 ;
  FILE4 temp ;
  FILE4SEQ_WRITE seq_write ;
  FILE4SEQ_READ seq_read ;

  if ( f4->is_read_only )
    return e4( f4->code_base, e4parm, E4_PARM_SIZ ) ;

  filelen = filelength( f4->hand ) ;
  if ( size == filelen )
    return 0 ;

  if ( size > filelen )    /* pad file to increase size */
  {
    file4seq_write_init( &seq_write, f4, filelen, 0, 0 ) ;
    file4seq_write_repeat( &seq_write, size - filelen, '\0' ) ;
    return 0 ;
  }

  c4 = f4->code_base ;

  memcpy( file_name, f4->name, E4MAXLINE ) ;
  u4name_piece( temp_name, E4MAXLINE, f4->name, 1, 0 ) ;
  u4name_ext( temp_name, E4MAXLINE, "TMP", 1 ) ;

  if ( file4create( &temp, c4, temp_name, 0 ) < 0 )
  {
    e4( c4, e4create, temp_name ) ;
    return -1 ;
  }

  buffer_size = c4->mem_size_buffer ;
  buffer = (char *)u4alloc_free( c4, buffer_size ) ;

  while ( !buffer )
  {
    buffer_size -= 0x400 ;

    if ( buffer_size <= 0 )
    {
      e4( c4, e4memory, 0 ) ;
      return -1 ;
    }

    buffer = (char *)u4alloc_free( c4, buffer_size ) ;
  }

  file4seq_write_init( &seq_write, &temp, 0, 0, buffer_size ) ;
  file4seq_read_init( &seq_read, f4, 0, 0, buffer_size ) ;

  /* copy the old file contents to the temp file */
  while ( size > 0 )
  {
    if ( size < buffer_size )
      amount_read = file4seq_read( &seq_read, buffer, size ) ;
    else
      amount_read = file4seq_read( &seq_read, buffer, buffer_size ) ;

    file4seq_write( &seq_write, buffer, amount_read ) ;
    size -= amount_read ;
  }

  file4seq_write_flush( &seq_write ) ;

  u4free( buffer ) ;

  file4close( f4 ) ;
  file4close( &temp ) ;

  if ( u4remove( file_name ) )
  {
    e4( c4, e4info, file_name ) ;
    return -1 ;
  }

  if ( u4rename( temp_name, file_name ) )
  {
    e4( c4, e4rename, temp_name ) ;
    return -1 ;
  }

  if ( file4open( f4, c4, file_name, 1 ) < 0 )
  {
    e4( c4, e4open, file_name ) ;
    return -1 ;
  }

  if ( file4lock( f4, L4LOCK_POS, L4LOCK_POS ) < 0 )
  {
    e4( c4, e4lock, file_name ) ;
    return -1 ;
  }

  return 0 ;
}
#endif  /* ifdef S4MACINTOSH */
#endif   /* ifdef S4NO_CHSIZE */

long S4FUNCTION file4len( FILE4 *file )
{
  long lrc ;

#ifdef S4DEBUG
  if ( file == 0 )
    e4severe( e4parm, E4_FILE4LEN ) ;
  if ( file->hand < 0 && file->file_created != 0 )
    e4severe( e4parm, E4_FILE4LEN ) ;
#endif

#ifndef S4OPTIMIZE_OFF
  if ( file->is_temp == 1 && file->file_created == 0 && file->len == -1 )
    return 0 ;
  if ( file->do_buffer && file->len >= 0 )
    lrc = file->len ;
  else
#endif
    lrc = filelength( file->hand ) ;
  if ( lrc < 0L )
    e4( file->code_base, e4len, file->name ) ;
  return lrc ;
}

int S4FUNCTION file4len_set( FILE4 *file, long new_len )
{
  int rc ;
#ifdef __SC__
  union REGS dos_flush ;
#endif

#ifdef S4DEBUG
  if ( file == 0 || new_len < 0 )
    e4severe( e4parm, E4_FILE4LEN_SET ) ;
  if ( file->hand < 0 && file->file_created != 0 )
    e4severe( e4parm, E4_FILE4LEN_SET ) ;
#endif

  if ( file->code_base->error_code > 0 && file->code_base->error_code < 200 )  /* file error */
    return -1 ;

  if ( file->is_read_only )
    return e4( file->code_base, e4parm, E4_PARM_SIZ ) ;

#ifndef S4OPTIMIZE_OFF
  if ( file->do_buffer )
  {
    if ( file->len > new_len )   /* must do a partial delete of memory */
      opt4file_delete( file, new_len, file->len ) ;
    if ( file->buffer_writes )
      file->len = new_len ;
#ifdef S4DEBUG
    else
      if ( file->len != -1L )
        e4severe( e4opt, E4_FILE4LEN_SET ) ;
#endif
  }
#ifndef S4SAFE
  if ( file->do_buffer == 0 || file->buffer_writes == 0 )
#endif
#endif

#ifdef S4NO_CHSIZE
    rc = file4change_size( file, new_len ) ;
#else
#ifdef S4WIN32
  if ( SetFilePointer( (HANDLE)file->hand, new_len, NULL, FILE_BEGIN ) == (DWORD)-1 )
    return e4( file->code_base, e4len_set, file->name ) ;
  if ( SetEndOfFile( (HANDLE)file->hand ) )
    rc = 0 ;
  else
    rc = -1 ;
#else
#ifdef __SC__
  dos_flush.x.ax = 0x4200;
  dos_flush.x.bx = file->hand ;
  memcpy((void *)&dos_flush.x.dx,(void *)&new_len,2);
  memcpy((void *)&dos_flush.x.cx,((char *)&new_len)+2,2);
  intdos( &dos_flush, &dos_flush ) ;
  if ( dos_flush.x.cflag != 0 )
    return e4( file->code_base, e4len_set, 0 ) ;
  dos_flush.h.ah = 0x40;
  dos_flush.x.bx = file->hand ;
  dos_flush.x.cx = 0x00;
  intdos( &dos_flush, &dos_flush ) ;
  if ( dos_flush.x.cflag != 0 )
    return e4( file->code_base, e4len_set, 0 ) ;
#else
  rc = chsize( file->hand, new_len ) ;
#endif
#endif
#endif
  if ( rc < 0 )
  {
    e4describe( file->code_base, e4len_set, E4_CREATE_FIL, file->name, (char *)0 ) ;
    return -1 ;
  }

  return 0 ;
}

unsigned S4FUNCTION file4read( FILE4 *file, long pos, void *ptr, unsigned ptr_len )
{
  long rc ;
  unsigned urc ;

#ifdef S4DEBUG
  if ( file == 0 || pos < 0 || ptr == 0  )
    e4severe( e4parm, E4_F4READ ) ;
  if ( file->hand < 0 && file->file_created != 0 )
    e4severe( e4parm, E4_F4READ ) ;
#endif

  if ( file->code_base->error_code < 0 )
    return 0 ;

#ifndef S4OPTIMIZE_OFF
  if ( file->do_buffer )
    urc = (unsigned)opt4file_read( file, pos, ptr, ptr_len )  ;
  else
  {
#endif

#ifdef S4WINDOWS
    rc = _llseek( file->hand, pos, 0 ) ;
#else
#ifdef S4MACINTOSH
    rc = MAClseek( file->hand, pos, 0, 0 ) ;
#else
#ifdef S4WIN32
    if ( rc = SetFilePointer( (HANDLE)file->hand, pos, NULL, FILE_BEGIN ) != (DWORD)-1 )
      rc = pos ;
#else
#ifdef S4LSEEK
    rc = f4lseek( file, pos, 0, 0 ) ;
#else
    rc = lseek( file->hand, pos, 0 ) ;
#endif
#endif
#endif
#endif

    if ( rc != pos )
    {
      file4read_error( file ) ;
      return 0 ;
    }

#ifdef S4WINDOWS
    urc = (unsigned)_lread( file->hand, (char *)ptr, ptr_len ) ;
#else
#ifdef S4MACINTOSH
    rc = (long) ptr_len ;
    urc = FSRead( file->hand, &rc, ptr ) ;
    if ( urc == 0 ) urc = ptr_len ;
    if ( urc == eofErr ) urc = rc ;  /* attempt to read past EOF OK */
#else
#ifdef S4WIN32
    rc = ReadFile( (HANDLE)file->hand, ptr, ptr_len, (unsigned long *)&urc, NULL ) ;
    if ( !rc )
      rc = GetLastError() ;
#else
#ifdef S4LSEEK
    /* if reading past EOF */
    if ( pos+ptr_len > filelength( file->hand ) )
      urc = (unsigned)read( file->hand, ptr, filelength(file->hand) - pos ) ;
    else
      urc = (unsigned)read( file->hand, ptr, ptr_len ) ;
#else
    urc = (unsigned)read( file->hand, ptr, ptr_len ) ;
#endif
#endif
#endif
#endif

#ifndef S4OPTIMIZE_OFF
  }
#endif

  if ( urc != ptr_len )
    if ( urc > ptr_len )
    {
      file4read_error( file ) ;
      return 0 ;
    }

#ifndef S4OPTIMIZE_OFF
#ifdef S4DEBUG_DEV
  if ( file->has_dup == 1 )
    if ( file->do_buffer == 1 || file->link.n == 0 )
      if ( file4cmp_part( file->code_base, ptr, file, pos, urc ) != 0 )
        e4severe( e4opt, "file4read() - file corruption" ) ;
#endif
#endif

  return urc ;
}

int  S4FUNCTION file4read_all( FILE4 *file, long pos, void *ptr, unsigned ptr_len )
{
  long rc ;
  unsigned urc ;

#ifdef S4DEBUG
  if ( file == 0 || pos < 0 || ptr == 0  )
    e4severe( e4parm, E4_F4READ_ALL ) ;
  if ( file->hand < 0 && file->file_created != 0 )
    e4severe( e4parm, E4_F4READ_ALL ) ;
#endif

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

#ifndef S4OPTIMIZE_OFF
  if ( file->do_buffer )
    urc = (unsigned) opt4file_read( file, pos, ptr, ptr_len )  ;
  else
  {
#endif

#ifdef S4WINDOWS
    rc = _llseek( file->hand, pos, 0 ) ;
#else
#ifdef S4MACINTOSH
    rc = MAClseek( file->hand, pos, 0, 0 ) ;
#else
#ifdef S4WIN32
    if ( SetFilePointer( (HANDLE)file->hand, pos, NULL, FILE_BEGIN ) != (DWORD)-1 )
      rc = pos ;
#else
#ifdef S4LSEEK
    rc = f4lseek( file, pos, 0, 0 ) ;
#else
    rc = lseek( file->hand, pos, 0 ) ;
#endif
#endif
#endif
#endif
    if ( rc != pos )
      return file4read_error( file ) ;

#ifdef S4WINDOWS
    urc = (unsigned)_lread( file->hand, (char *)ptr, ptr_len ) ;
#else
#ifdef S4MACINTOSH
    rc = (long) ptr_len ;
    urc = FSRead( file->hand, &rc, ptr ) ;
    if ( urc == 0 )
      urc = ptr_len ;
#else
#ifdef S4WIN32
    rc = ReadFile( (HANDLE)file->hand, ptr, ptr_len, (unsigned long *)&urc, NULL ) ;
    if ( !rc )
      rc = GetLastError() ;
#else
    urc = (unsigned)read( file->hand, ptr, ptr_len ) ;
#endif
#endif
#endif

#ifndef S4OPTIMIZE_OFF
  }
#endif

  if ( urc != ptr_len )
    return file4read_error( file ) ;

#ifndef S4OPTIMIZE_OFF
#ifdef S4DEBUG_DEV
  if ( file->has_dup == 1 )
    if ( file->do_buffer == 1 || file->link.n == 0 )
      if ( file4cmp_part( file->code_base, ptr, file, pos, urc ) != 0 )
        e4severe( e4opt, "file4read_all() - file corruption" ) ;
#endif
#endif

  return 0 ;
}

int S4FUNCTION file4read_error( FILE4 *file )
{
  return e4describe( file->code_base, e4read, E4_CREATE_FIL, file->name, (char *) 0 ) ;
}

int S4FUNCTION file4replace( FILE4 *keep, FILE4 *from )
{
#ifdef S4NO_RENAME
  return e4( from->code_base, e4not_rename, E4_F4REPLACE ) ;
#else
  FILE4 tmp ;
  int from_do_alloc_free, rc ;
  char *from_name ;
#ifndef S4SINGLE
  char *buf ;
  unsigned buf_size ;
#ifndef S4OPTIMIZE_OFF
  int has_opt ;
#endif
  long pos, f_len ;
#endif

#ifdef S4DEBUG
  if ( keep == 0 || from == 0  )
    e4severe( e4parm, E4_F4REPLACE ) ;
#endif

  rc = 0 ;
  if ( from->is_read_only || keep->is_read_only )
    return e4( from->code_base, e4parm, E4_PARM_REP ) ;

#ifndef S4SINGLE
  if ( keep->is_exclusive )
  {
#endif
    memcpy( (void *)&tmp, (void *)keep, sizeof ( FILE4 ) ) ;  /* remember settings */

    keep->hand = from->hand ;
    from->hand = tmp.hand ;
    from_do_alloc_free = from->do_alloc_free ;
    keep->do_alloc_free = 0 ;
    from->do_alloc_free = 0 ;
    from_name = from->name ;
    from->name = keep->name ;
    from->is_temp = 1 ;
    if ( file4close ( from ) )
      return -1 ;
    if ( file4close ( keep ) )
      return -1 ;

    if ( rename( from_name, tmp.name ) < 0 )
      return - 1 ;

    if ( from_do_alloc_free )
      u4free( from_name ) ;
    if ( file4open ( keep, tmp.code_base, tmp.name, 0 ) )
      return -1 ;
    keep->is_temp = tmp.is_temp ;
    keep->do_alloc_free = tmp.do_alloc_free ;
#ifndef S4OPTIMIZE_OFF
    if ( tmp.link.n != 0 )   /* file was optimized... */
      file4optimize( keep, tmp.code_base->optimize ,tmp.type ) ;
#endif
#ifndef S4SINGLE
  }
  else  /* can't lose the file handle if other user's have the file open, so just do a copy */
  {
    file4len_set( keep, 0 ) ;

    buf_size = from->code_base->mem_size_buffer ;

#ifndef S4OPTIMIZE_OFF
    has_opt = from->code_base->has_opt && from->code_base->opt.num_buffers ;
    d4opt_suspend( from->code_base ) ;
#endif

    for ( ;; buf_size -= 0x800 )
    {
      if ( buf_size < 0x800 )  /* make one last try */
      {
        buf_size = 100 ;
        buf = (char *)u4alloc_er( from->code_base, buf_size ) ;
        if ( buf == 0 )
          return -1 ;
      }
      buf = (char *)u4alloc( buf_size ) ;
      if ( buf )
        break ;
    }

    pos = 0 ;
    for( f_len = file4len( from ) ; f_len > 0 ; f_len -= buf_size )
    {
      buf_size = ((long)buf_size > f_len) ?  (unsigned)f_len : buf_size ;
      if ( file4read_all( from, pos, buf, (unsigned)buf_size ) < 0 )
      {
        rc = -1 ;
        break ;
      }
      if ( file4write( keep, pos, buf, (unsigned)buf_size ) < 0 )
      {
        rc = -1 ;
        break ;
      }
      pos += buf_size ;
    }
    from->is_temp = 1 ;
    file4close( from ) ;
    u4free( buf ) ;
#ifndef S4OPTIMIZE_OFF
    if ( has_opt )
      d4opt_restart( keep->code_base ) ;
#endif
  }
#endif
  return rc ;
#endif
}

#ifdef S4NO_ECVT
#define S4NO_FCVT
#endif

#ifdef S4NO_FCVT

#define MAXIMUM 30
#define PRECISION 17

static char value_str[32] ;

static double minus[] =
{1e-256,1e-128,1e-64,
 1e-32,1e-16,1e-8,
 1e-4,1e-2,1e-1,1.0} ;

 static double plus[] =
{1e+256,1e+128,1e+64,
 1e+32,1e+16,1e+8,
 1e+4,1e+2,1e+1} ;

#ifdef S4NO_ECVT
 char *f4ecvt( double value, int numdigits, int *dec_ptr, int *sign_ptr )
{
  int dptr, count, j, k ;
  char *v_ptr ;

  if ( numdigits < 0 )
    numdigits = 0 ;
  else
    if ( numdigits > MAXIMUM ) numdigits = MAXIMUM ;

  if ( value < 0.0 )
  {
    value = -value ;
    *sign_ptr = 1 ;
  }
  else
    *sign_ptr = 0 ;

  if ( value == 0.0 )
  {
    memset( value_str, '0', numdigits ) ;
    dptr = 0 ;
  }
  else
  {
    dptr = 1 ;
    k = 256 ;
    count = 0 ;
    while ( value < 1.0 )
    {
      while ( value < minus[count+1] )
      {
        value *= plus[count] ;
        dptr -= k ;
      }
      k /= 2 ;
      count++ ;
    }
    k = 256 ;
    count = 0 ;
    while ( value >= 10.0 )
    {
      while ( value >= plus[count] )
      {
        value *= minus[count] ;
        dptr += k ;
      }
      k /= 2 ;
      count++ ;
    }

    for ( v_ptr = &value_str[0]; v_ptr <= &value_str[numdigits]; v_ptr++ )
    {
      if ( v_ptr >= &value_str[PRECISION] )  *v_ptr = '0' ;
      else
      {
        j = value ;
        *v_ptr = j + '0' ;
        value = ( value - j + 1.0e-15 ) * 10.0 ;
      }
    }
    --v_ptr ;
    if ( *v_ptr >= '5' )
    {
      while (1)
      {
        if ( v_ptr == &value_str[0] )
        {
          dptr++ ;
          value_str[0] = '1' ;
          break ;
        }
        *v_ptr = 0 ;
        --v_ptr ;
        if ( *v_ptr != '9' )
        {
          (*v_ptr)++ ;
          break ;
        }
      }
    }
  }
  *dec_ptr = dptr ;
  value_str[numdigits] = 0 ;
  return value_str ;
}
#endif

char *f4fcvt( double value, int numdigits, int *dec_ptr, int *sign_ptr )
{
  int dptr, count, j, k ;
  char *v_ptr ;

  if ( numdigits < 0 )
    numdigits = 0 ;
  else
    if ( numdigits > MAXIMUM ) numdigits = MAXIMUM ;

  if ( value < 0.0 )
  {
    value = -value ;
    *sign_ptr = 1 ;
  }
  else
    *sign_ptr = 0 ;

  if ( value == 0.0 )
  {
    memset( value_str, '0', numdigits ) ;
    dptr = 0 ;
  }
  else
  {
    dptr = 1 ;
    k = 256 ;
    count = 0 ;
    while ( value < 1.0 )
    {
      while ( value < minus[count+1] )
      {
        value *= plus[count] ;
        dptr -= k ;
      }
      k /= 2 ;
      count++ ;
    }
    k = 256 ;
    count = 0 ;
    while ( value >= 10.0 )
    {
      while ( value >= plus[count] )
      {
        value *= minus[count] ;
        dptr += k ;
      }
      k /= 2 ;
      count++ ;
    }

    if ( ( numdigits += dptr ) < 0 )
      numdigits = 0 ;
    else
      if ( numdigits > MAXIMUM )  numdigits = MAXIMUM ;

    for ( v_ptr = &value_str[0]; v_ptr <= &value_str[numdigits]; v_ptr++ )
    {
      if ( v_ptr >= &value_str[PRECISION] )  *v_ptr = '0' ;
      else
      {
        j = value ;
        *v_ptr = j + '0' ;
        value = ( value - j + 1.0e-15 ) * 10.0 ;
      }
    }
    --v_ptr ;
    if ( *v_ptr >= '5' )
    {
      while (1)
      {
        if ( v_ptr == &value_str[0] )
        {
          numdigits++ ;
          dptr++ ;
          value_str[0] = '1' ;
          break ;
        }
        *v_ptr = 0 ;
        --v_ptr ;
        if ( *v_ptr != '9' )
        {
          (*v_ptr)++ ;
          break ;
        }
      }
    }
  }
  *dec_ptr = dptr ;
  value_str[numdigits] = 0 ;
  return value_str ;
}
#endif