/* f4opt.c   (c)Copyright Sequiter Software Inc., 1988-1996.  All rights reserved. */
/* file-level optimizations */

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

#ifndef S4OPTIMIZE_OFF
#ifdef E4ANALYZE_ALL
#ifndef S4UNIX
   #include <sys\stat.h>
   #include <share.h>
#endif
#include <fcntl.h>

int file4PartLenSet( FILE4 *file, long newLen )
{
   int h1, rc ;
   #ifdef S4WIN32
      DWORD res ;
   #endif

   rc = 0 ;
   #ifdef S4WIN32
      h1 = (int)CreateFile( file->dupName, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 ) ;
   #else
      h1 = sopen( file->dupName, (int)(O_BINARY | O_RDWR), SH_DENYRW, 0 ) ;
   #endif
   if ( h1 != 0 )
   {
      #ifdef S4WIN32
         if ( SetFilePointer( (HANDLE)h1, newLen, NULL, FILE_BEGIN ) == (DWORD)-1 )
         {
            CloseHandle( (HANDLE)h1 ) ;
            return error4describe( file->codeBase, e4lenSet, E90606, file->dupName, 0, 0 ) ;
         }
         if ( SetEndOfFile( (HANDLE)h1 ) )
            rc = 0 ;
         else
         {
            res = GetLastError() ;
            rc = -1 ;
         }
      #else
         #ifdef __SC__
            rc = 0 ;
            dosFlush.x.ax = 0x4200;
            dosFlush.x.bx = h1 ;
            memcpy((void *)&dosFlush.x.dx,(void *)&newLen,2);
            memcpy((void *)&dosFlush.x.cx,((char *)&newLen)+2,2);
            intdos( &dosFlush, &dosFlush ) ;
            if ( dosFlush.x.cflag != 0 )
            {
               close( h1 ) ;
               return error4( file->codeBase, e4lenSet, E90606 ) ;
            }
            dosFlush.h.ah = 0x40;
            dosFlush.x.bx = h1 ;
            dosFlush.x.cx = 0x00;
            intdos( &dosFlush, &dosFlush ) ;
            if ( dosFlush.x.cflag != 0 )
            {
               close( h1 ) ;
               return error4( file->codeBase, e4lenSet, E90606 ) ;
            }
            rc = 0
         #else
            #ifndef S4NO_CHSIZE
               rc = chsize( h1, newLen ) ;
            #else
               rc = ftruncate( h1, newLen ) ;
            #endif
         #endif
      #endif
   }

   if ( h1 > 0 )
   {
      #ifdef S4WIN32
         CloseHandle( (HANDLE)h1 ) ;
      #else
         close( h1 ) ;
      #endif
   }

   return rc ;
}

int file4writePart( const char *buf, FILE4 *file, long pos, unsigned len )
{
   int h1 ;
   unsigned rc ;
   long rc1, fileLen, bufWriteLen ;
   char emptyBuf[512] ;

   if ( file->inUse == 1 )
      return 0 ;
   file->inUse = 1 ;

   rc = h1 = -1 ;

   for ( ;; )
   {
      #ifdef S4WIN32
         h1 = (int)CreateFile( file->dupName, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 ) ;
         if ( h1 == (int)INVALID_HANDLE_VALUE )
            h1 = -1 ;
      #else
         h1 = sopen( file->dupName, (int)(O_BINARY | O_RDWR), SH_DENYRW, 0 ) ;
      #endif
      if ( h1 < 0 )
         break ;

      fileLen = u4filelength( h1 ) ;
      while( fileLen < pos )
      {
         memset( emptyBuf, 0, sizeof( emptyBuf ) ) ;
         bufWriteLen = pos - fileLen ;
         if ( bufWriteLen > (long)sizeof( emptyBuf ) )
            bufWriteLen = (long)sizeof( emptyBuf ) ;
         #ifdef S4WIN32
            rc1 = SetFilePointer( (HANDLE)h1, fileLen, NULL, FILE_BEGIN ) ;
            if ( rc1 != (DWORD)-1 )
               rc1 = fileLen ;
         #else
            rc1 = (long)lseek( h1, fileLen, 0 ) ;
         #endif
         if ( rc1 != fileLen )
            break ;
         #ifdef S4WIN32
            WriteFile( (HANDLE)h1, emptyBuf, (int)bufWriteLen, (unsigned long *)&rc1, NULL ) ;
         #else
            rc1 = (long)write( h1, emptyBuf, (int)bufWriteLen ) ;
         #endif
         if ( rc1 != (long)len )
            break;
         fileLen = u4filelength( h1 ) ;
      }
      #ifdef S4WIN32
         rc1 = SetFilePointer( (HANDLE)h1, pos, NULL, FILE_BEGIN ) ;
         if ( rc1 != (DWORD)-1 )
            rc1 = pos ;
      #else
         rc1 = (long)lseek( h1, pos, 0 ) ;
      #endif
      if ( rc1 != pos )
         break ;

      #ifdef S4WIN32
         WriteFile( (HANDLE)h1, buf, (int)len, (unsigned long *)&rc1, NULL ) ;
      #else
         rc1 = (long)write( h1, buf, len ) ;
      #endif
      if ( rc1 != (long)len )
         break;

      rc = 0 ;
      break;
   }

   if ( h1 > 0 )
   {
      #ifdef S4WIN32
         CloseHandle( (HANDLE)h1 ) ;
      #else
         close( h1 ) ;
      #endif
   }

   file->inUse = 0 ;
   return rc ;
}

int file4cmpPart( CODE4 *c4, char *buf, FILE4 *file, long pos, unsigned len )
{
   int h1, rc ;
   char buf1[512] ;
   long lenHold, rc1 ;

   if ( file->inUse == 1 )
      return 0 ;
   file->inUse = 1 ;

   lenHold = len ;

   if ( error4code( c4 ) != 0 )
      return -1 ;

   h1 = rc = -1 ;

   #ifdef S4WIN32
      h1 = (int)CreateFile( file->dupName, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 ) ;
      if ( h1 == (int)INVALID_HANDLE_VALUE )
         h1 = -1 ;
   #else
      h1 = sopen( file->dupName, (int)(O_BINARY | O_RDONLY), SH_DENYRW, 0 ) ;
   #endif

   if ( h1 > 0 )
      for( ;; )
      {
         #ifdef S4WIN32
            rc1 = SetFilePointer( (HANDLE)h1, pos, NULL, FILE_BEGIN ) ;
            if ( rc1 != (DWORD)-1 )
               rc1 = pos ;
         #else
            rc1 = lseek( h1, pos, 0 ) ;
         #endif
         if ( rc1 != pos )
            break ;

         #ifdef S4WIN32
            ReadFile( (HANDLE)h1, buf1, sizeof( buf1 ) > len ? len : sizeof( buf1 ), (unsigned long *)&rc1, NULL ) ;
         #else
            rc1 = (unsigned)read( h1, buf1, sizeof( buf1 ) > len ? len : sizeof( buf1 ) ) ;
         #endif
         if ( rc1 != (long)sizeof( buf1 ) && rc1 != (long)len )
            break ;

         if ( c4memcmp( buf + ( lenHold - len ), buf1, (unsigned)rc1 ) != 0 )
            break ;

         len -= (unsigned)rc1 ;
         if ( len == 0 )
         {
            rc = 0 ;
            break ;
         }

         pos += rc1 ;
      }

   if ( h1 > 0 )
   {
      #ifdef S4WIN32
         CloseHandle( (HANDLE)h1 ) ;
      #else
         close( h1 ) ;
      #endif
   }

   file->inUse = 0 ;
   if ( rc < 0 )
      return rc ;
   return 0 ;
}

int file4cmp( FILE4 *f1 )
{
   int h1, rc ;
   unsigned rc2 ;
   char buf1[512], buf2[512] ;
   long rct, p1, rc1 ;

   if ( f1->inUse == 1 )
      return 0 ;
   f1->inUse = 1 ;

   if ( error4code( f1->codeBase ) != 0 || sizeof( buf1 ) != sizeof( buf2 ) )
      return -1 ;

   p1 = 0L ;
   h1 = rc = -1 ;

   #ifdef S4WIN32
      h1 = (int)CreateFile( f1->dupName, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 ) ;
      if ( h1 == (int)INVALID_HANDLE_VALUE )
         h1 = -1 ;
   #else
      h1 = sopen( f1->dupName, (int)(O_BINARY | O_RDONLY), SH_DENYRW, 0 ) ;
   #endif

   if ( h1 > 0 )
      for( ;; )
      {
         #ifdef S4WIN32
            rct = SetFilePointer( (HANDLE)h1, p1, NULL, FILE_BEGIN ) ;
            if ( rct != (DWORD)-1 )
               rct = p1 ;
         #else
            rct = lseek( h1, p1, 0 ) ;
         #endif
         if ( rct != p1 )
            break ;

         #ifdef S4WIN32
            ReadFile( (HANDLE)h1, buf1, sizeof( buf1 ), (unsigned long *)&rc1, NULL ) ;
         #else
            rc1 = (unsigned)read( h1, buf1, sizeof( buf1 ) ) ;
         #endif
         f1->hasDup = 0 ;
         rc2 = file4read( f1, p1, buf2, sizeof( buf2 ) ) ;
         f1->hasDup = 1 ;

         if ( rc1 != (long)rc2 )
            break ;

         if ( c4memcmp( buf1, buf2, (unsigned)rc1 ) != 0 )
            break ;

         if ( rc1 < sizeof( buf1 ) )
         {
            rc = 0 ;
            break ;
         }

         p1 += sizeof( buf1 ) ;
      }

   if ( h1 > 0 )
   {
      #ifdef S4WIN32
         CloseHandle( (HANDLE)h1 ) ;
      #else
         close( h1 ) ;
      #endif
   }

   error4set( f1->codeBase, 0 ) ;

   f1->inUse = 0 ;
   return rc ;
}

int file4copyx( CODE4 *c4, FILE4 *f1, char *f2 )
{
   int h1, h2, rc ;
   long rct ;
   unsigned rc1, rc2 ;
   char buf[1024] ;
   long p1 ;
   #ifdef S4WIN32
      unsigned urc ;
   #endif

   if ( error4code( c4 ) != 0 )
      return -1 ;

   h2 = -1 ;
   rc = -1 ;
   p1 = 0L ;

   #ifdef S4WIN32
      h2 = (int) CreateFile( f2, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 ) ;
      if ( h2 == (int)INVALID_HANDLE_VALUE )
         h2 = -1 ;
   #else
      h2 = sopen( f2, (int)(O_BINARY | O_RDWR), SH_DENYRW, 0 ) ;
   #endif
   h1 = f1->hand ;

   if ( h1 > 0 && h2 > 0 )
      for( ;; )
      {
         #ifdef S4WIN32
            rct = SetFilePointer( (HANDLE)h1, p1, NULL, FILE_BEGIN ) ;
            if ( rct != (DWORD)-1 )
               rct = p1 ;
            rct = SetFilePointer( (HANDLE)h2, p1, NULL, FILE_BEGIN ) ;
            if ( rct != (DWORD)-1 )
               rct = p1 ;
            ReadFile( (HANDLE)h1, buf, sizeof( buf ), (unsigned long *)&urc, NULL ) ;
            rc1 = urc ;
            WriteFile( (HANDLE)h2, buf, rc1, (unsigned long *)&rc1, NULL ) ;
         #else
            rct = lseek( h1, p1, 0 ) ;
            if ( rct != p1 )
               break ;
            rct = lseek( h2, p1, 0 ) ;
            if ( rct != p1 )
               break ;
            rc1 = (unsigned)read( h1, buf, sizeof( buf ) ) ;
            rc2 = (unsigned)write( h2, buf, rc1 ) ;
         #endif

         if ( rc1 != rc2 )
            break ;

         if ( rc1 < sizeof( buf ) )
         {
            rc = 0 ;
            break ;
         }

         p1 += sizeof( buf ) ;
      }

   if ( h2 > 0 )
   {
      #ifdef S4WIN32
         CloseHandle( (HANDLE)h2 ) ;
      #else
         close( h2 ) ;
      #endif
   }

   return rc ;
}
#endif
#endif

int S4FUNCTION file4optimizeLow( FILE4 *file, const int optFlagIn, const int fileType, const long expectedReadSize, const void *ownerPtr )
{
   #ifdef S4OPTIMIZE_OFF
      return 0 ;
   #else
      OPT4 *opt ;
      int rc, optFlag ;
      double hitCountAdd ;
      #ifdef E4ANALYZE_ALL
         #ifndef S4OPTIMIZE_OFF
            int hand ;
         #endif
      #endif

      #ifdef E4PARM_HIGH
         if ( file == 0 || fileType < 0 || fileType > 3 || optFlagIn < -1 || optFlagIn > 1 )
            return error4( 0, e4parm, E90616 ) ;
      #endif

      optFlag = optFlagIn ;
      rc = 0 ;

      opt = &file->codeBase->opt ;
      file->expectedReadSize = 0 ;
      file->ownerPtr = ownerPtr ;

      if ( optFlag == -1 )
      #ifdef S4OFF_MULTI
         optFlag = 1 ;
      #else
         optFlag = ( file->lowAccessMode != OPEN4DENY_NONE || file->isReadOnly ) ? 1 : 0 ;
      #endif

      if ( optFlag == 1 )
      {
         if ( file->doBuffer != 0 && file->type != OPT4NONE )  /* already optimized */
            return 0 ;
         if ( opt->numBuffers > 0 )
         {
            file->len = -1 ;
            file->hashInit = opt->hashTrail * opt->blockSize ;
            #ifdef E4ANALYZE
               if ( file4len( file ) < 0 || opt->blockSize == 0 )
                  return error4( file->codeBase, e4info, E90616 ) ;
            #endif
            opt->hashTrail = (opt->hashTrail + (unsigned)file4len( file ) / opt->blockSize) % opt->numBlocks ;
            file->doBuffer = 1 ;
         }
         else
            file->hashInit = - 1 ;

         if ( file->type == OPT4NONE )   /* add to list... */
            l4add( &opt->optFiles, file ) ;
         file->type = (char)fileType ;
         rc = file4optimizeWrite( file, file->codeBase->optimizeWrite ) ;

         switch( fileType )
         {
            case OPT4DBF:
               if ( opt->blockSize == 0 ) /* probably not initialized, take CODE4 setting */
                  hitCountAdd = (double)expectedReadSize / (double)file->codeBase->memSizeBlock ;
               else
                  hitCountAdd = (double)expectedReadSize / (double)opt->blockSize ;
               if ( hitCountAdd > 1.0 )
                   file->hitCountAdd = 1.0 ;
                else
                   file->hitCountAdd = hitCountAdd ;
               break ;
         }

         #ifdef E4ANALYZE_ALL
            #ifndef S4OPTIMIZE_OFF
               tmpnam( file->dupName ) ;
               hand = sopen( file->dupName, O_CREAT | O_TRUNC | O_RDWR, SH_DENYWR, S_IREAD  | S_IWRITE ) ;

               if ( hand < 0 )
                  file->hasDup = 0 ;
               else
               {
                  close( hand ) ;
                  if ( file4len( file ) > 0 )
                  {
                     if ( file4copyx( file->codeBase, file, file->dupName ) < 0 )
                        file->hasDup = 0 ;
                     else
                        file->hasDup = 1 ;
                  }
                  else
                     file->hasDup = 1 ;
               }
            #endif
         #endif
      }
      else  /* 0 */
      {
         if ( file->type == OPT4NONE )   /* not optimized */
            return 0 ;

         #ifdef E4ANALYZE_ALL
            if ( file->hasDup == 1 )
            {
               /* first do a final verification */
               if ( file->doBuffer == 1 || file->link.n == 0 )
                  if ( file4cmp( file ) != 0 )
                     return error4( file->codeBase, e4opt, E80602 ) ;
               u4remove( file->dupName ) ;
               file->hasDup = 0 ;
            }
         #endif

         rc = file4optimizeWrite( file, 0 ) ;
         if ( rc == 0 )
         {
            if ( opt4fileFlush( file, 1 ) < 0 )
               return error4( file->codeBase, e4optFlush, E90616 ) ;
            l4remove( &opt->optFiles, file ) ;
            file->type = OPT4NONE ;
            file->doBuffer = 0 ;
         }
      }

      return rc ;
   #endif
}

/* optFlag has the same definitions as C4CODE.optimizeWrite */
int S4FUNCTION file4optimizeWrite( FILE4 *file, const int optFlag )
{
   #ifdef S4OPTIMIZE_OFF
      return 0 ;
   #else
      int rc ;

      rc = 0 ;

      #ifdef E4PARM_HIGH
         if( file == 0 || optFlag < -1 || optFlag > 1 )
            return error4( 0, e4parm, E90617 ) ;
      #endif

      if ( optFlag == file->writeBuffer )
         return rc ;

      switch ( optFlag )
      {
         case 0:
            if ( file->doBuffer )
               rc = opt4fileFlush( file, 1 ) ;
            file->writeBuffer = 0 ;
            break ;
         case  -1 :
            #ifdef S4OFF_MULTI
               if ( file->doBuffer )
                  file->bufferWrites = 1 ;
               file->writeBuffer = 1 ;
            #else
               if ( file->lowAccessMode != OPEN4DENY_RW )
               {
                  if ( file->doBuffer )
                  {
                     rc = opt4fileFlush( file, 1 ) ;
                     file->bufferWrites = 0 ;
                  }
                  file->writeBuffer = 0 ;
               }
               else
               {
                  if ( file->doBuffer )
                     file->bufferWrites = 1 ;
                  file->writeBuffer = 1 ;
               }
            #endif
            break ;
         case 1:
            #ifndef S4OFF_MULTI
               if ( file->lowAccessMode == OPEN4DENY_RW )
            #endif
            if ( file->doBuffer )
               file->bufferWrites = 1 ;
            file->writeBuffer = 1 ;
            break ;
         default:
            return 0 ;
      }

      return rc ;
   #endif
}

/* tries to actually turn on/off the write bufferring when locking/unlocking a file */
void file4setWriteOpt( FILE4 *f4, int setOpt )
{
   #ifndef S4OPTIMIZE_OFF
      if ( setOpt == f4->bufferWrites )
         return ;
      if ( setOpt == 1 && f4->writeBuffer == 1 )
         f4->bufferWrites = 1 ;
      if ( setOpt == 0 && f4->writeBuffer == 1 )
         f4->bufferWrites = 0 ;
   #endif
}