/* f4file.c (c)Copyright Sequiter Software Inc., 1988-1996. All rights reserved. */ #include "d4all.h" #ifdef __TURBOC__ #pragma hdrstop #endif #ifdef S4TEMP #include "t4test.h" #endif #ifdef _MSC_VER #ifdef S4WINDOWS #include #endif #endif #ifdef S4WINTEL #ifndef S4IBMOS2 #ifndef __TURBOC__ #include #define S4LOCKING #endif #ifdef __ZTC__ #endif #ifdef _MSC_VER #include #include #endif #endif /* #include */ /* #include */ #endif /*#include */ #ifdef __SC__ #include #endif #ifdef S4NO_FILELENGTH /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ #ifdef S4WIN32 long u4filelength( int hand ) { long rc ; rc = (long)GetFileSize( (HANDLE)hand, NULL ) ; #ifdef E4PARM_LOW if ( rc == -1L ) error4( 0, e4result, E90603 ) ; #endif return rc ; } #else #include #include long u4filelength( int hand ) { struct stat strStat ; if (fstat( hand, &strStat ) ) return error4( 0, e4result, E90603 ) ; return( (long) strStat.st_size ) ; } #endif /* */ #endif /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ #ifdef S4LSEEK /* if extend is set, file is extended, else lseek to EOF */ long f4lseek(FILE4 *f4, long offset, int fromWhere, int extend ) { long fileLen ; if ( offset != 0 ) { fileLen = u4filelength( f4->hand ) ; if (extend) { if ( fileLen < offset ) file4changeSize( f4, offset ) ; } else if ( fileLen < offset ) { if ( lseek( f4->hand, fileLen, 0 ) ) return offset ; else return -1L ; } } return lseek( f4->hand, offset, fromWhere ) ; } #endif #ifdef S4NO_CHSIZE #define E4MAXLINE 129 /* maximum file path length */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ #ifdef _MSC_VER #ifdef S4WINDOWS int S4FUNCTION file4changeSize( FILE4 *f4, long size ) { unsigned int rc, num ; char a ; a = (char)0x00 ; _llseek( f4->hand, size, SEEK_SET ) ; rc = _dos_write( f4->hand, &a, 0, &num ) ; if ( num != 0 || rc != 0 ) return error4( f4->codeBase, e4lenSet, E90604 ) ; return 0 ; } #endif #else int S4FUNCTION file4changeSize( FILE4 *f4, long size ) { char *buffer ; char tempName[E4MAXLINE], fileName[E4MAXLINE] ; long bufferSize, fileLen, amountRead = 0, t ; CODE4 *c4 ; FILE4 temp ; FILE4SEQ_WRITE seqWrite ; FILE4SEQ_READ seqRead ; int len, shortLen, pos, i, rc ; int oldTemp, oldc4Temp, oldAccessMode, oldReadOnly, newAccessMode, newReadOnly; #ifdef E4PARM_LOW if ( f4 == 0 || size < 0L ) return error4( 0, e4parm_null, E90604 ) ; #endif #ifdef E4ANALYZE if ( f4->isReadOnly ) return error4( f4->codeBase, e4parm, E90604 ) ; #endif c4 = f4->codeBase ; fileLen = u4filelength( f4->hand ) ; if ( size == fileLen ) return 0 ; bufferSize = c4->memSizeBuffer ; buffer = (char *)u4allocFree( c4, bufferSize ) ; while ( !buffer ) { bufferSize -= 0x400 ; if ( bufferSize <= 0 ) return error4( c4, e4memory, E90604 ) ; buffer = (char *)u4allocFree( c4, bufferSize ) ; } if ( size > fileLen ) /* pad file to increase size */ { file4seqWriteInit( &seqWrite, f4, fileLen, buffer, bufferSize ) ; file4seqWriteRepeat( &seqWrite, size - fileLen, '\0' ) ; file4seqWriteFlush( &seqWrite ) ; u4free( buffer ) ; return 0 ; } c4 = f4->codeBase ; len = strlen( f4->name ) ; if ( len >= E4MAXLINE ) len = E4MAXLINE - 1 ; memcpy( fileName, f4->name, len ) ; fileName[len] = 0 ; oldTemp = f4->isTemp; newAccessMode = f4->lowAccessMode; newReadOnly = f4->isReadOnly; oldc4Temp = c4->createTemp; oldAccessMode = c4->accessMode; oldReadOnly = c4->readOnly; c4->createTemp = 0; c4->accessMode = newAccessMode; c4->readOnly = newReadOnly; u4namePiece(tempName, E4MAXLINE, f4->name, 0,0); shortLen = strlen(tempName); u4namePiece( tempName, E4MAXLINE, f4->name, 1, 0 ) ; pos = strlen(tempName) - shortLen; memcpy(tempName+pos, "TEMP",4); tempName[pos+9] = 0; for ( i = 0 ;; ) { if ( i >= 100 ) { rc = error4( c4, e4create, E80605 ) ; break ; } i++ ; u4delayHundredth( 50 ) ; time( &t ) ; t %= 10000L ; c4ltoa45( t, tempName + pos + 4, -4 ) ; u4nameExt( tempName, E4MAXLINE, "TMP", 1 ) ; rc = file4create( &temp, c4, tempName, 0 ) ; if ( rc <= 0 ) break; } c4->createTemp = oldc4Temp; c4->accessMode = oldAccessMode; c4->readOnly = oldReadOnly; if (rc < 0) { u4free( buffer ) ; return error4describe( c4, e4create, E90604, tempName, 0, 0 ) ; } /* use file4seq to avoid tracking position manually */ file4seqWriteInit( &seqWrite, &temp, 0, 0, 0 ) ; file4seqReadInit( &seqRead, f4, 0, 0, 0 ) ; /* copy the old file contents to the temp file */ while ( size > 0 ) { if ( size < bufferSize ) amountRead = file4seqRead( &seqRead, buffer, size ) ; else amountRead = file4seqRead( &seqRead, buffer, bufferSize ) ; file4seqWrite( &seqWrite, buffer, amountRead ) ; size -= amountRead ; } file4seqWriteFlush( &seqWrite ) ; u4free( buffer ) ; file4close( &temp ) ; f4->isTemp = 1; if ( file4close( f4 ) != 0) return error4describe( c4, e4info, E90604, fileName, 0, 0 ) ; if ( u4rename( tempName, fileName ) ) return error4describe( c4, e4rename, E90604, fileName, 0, 0 ) ; if ( file4open( f4, c4, fileName, 1 ) < 0 ) return error4describe( c4, e4open, E90604, fileName, 0, 0 ) ; f4->isTemp = oldTemp; #ifndef S4OFF_MULTI if ( file4lock( f4, L4LOCK_POS, L4LOCK_POS ) < 0 ) return error4describe( c4, e4lock, E90604, fileName, 0, 0 ) ; #endif return 0 ; } #endif /* MSC_VER */ /* */ #endif /* ifdef S4NO_CHSIZE */ long S4FUNCTION file4len( FILE4 *f4 ) { long lrc ; #ifdef E4PARM_HIGH if ( f4 == 0 ) return error4( 0, e4parm_null, E90605 ) ; #endif #ifdef E4ANALYZE if ( f4->hand < 0 ) return error4( f4->codeBase, e4parm, E90605 ) ; #endif #ifndef S4OFF_OPTIMIZE if ( f4->isTemp == 1 && f4->fileCreated == 0 ) { /* 04/24/96 AS fix for c/s t4commit.c */ if ( f4->len == -1 ) return 0 ; else return f4->len ; } if ( f4->doBuffer && f4->len >= 0 ) lrc = f4->len ; else #endif lrc = u4filelength( f4->hand ) ; if ( lrc < 0L ) error4describe( f4->codeBase, e4len, E90605, f4->name, 0, 0 ) ; return lrc ; } int S4FUNCTION file4lenSet( FILE4 *f4, const long newLen ) { int rc ; CODE4 *c4 ; #ifdef S4WRITE_DELAY FILE4WRITE_DELAY *writeDelay ; LINK4 *delayLink ; #endif #ifdef __SC__ union REGS dosFlush ; #endif #ifdef E4PARM_HIGH if ( f4 == 0 || newLen < 0 ) return error4( 0, e4parm_null, E90606 ) ; #endif c4 = f4->codeBase ; #ifdef E4ANALYZE if ( f4->hand < 0 || f4->codeBase == 0 ) return error4( c4, e4struct, E90606 ) ; if ( f4->isReadOnly ) return error4( c4, e4struct, E80601 ) ; #else if ( c4 == 0 ) return -1 ; #endif if ( error4code( c4 ) > 0 && error4code( c4 ) < 200 ) /* file error */ return -1 ; if ( f4->isReadOnly ) return error4( c4, e4parm, E80607 ) ; #ifndef S4OFF_OPTIMIZE if ( f4->doBuffer ) { /* if len is -1 but bufferWrites true, may still have buffered data which must be removed, so get disk file length for reference */ if ( f4->bufferWrites == 1 && f4->fileCreated != 0 ) f4->len = file4len( f4 ) ; if ( f4->len > newLen ) /* must do a partial delete of memory */ opt4fileDelete( f4, newLen, f4->len ) ; if ( f4->bufferWrites ) f4->len = newLen ; #ifdef E4ANALYZE else if ( f4->len != -1L ) error4( 0, e4result, E90606 ) ; #endif } #ifdef E4ANALYZE_ALL if ( f4->hasDup == 1 ) if ( f4->doBuffer == 1 || f4->link.n == 0 ) if ( file4PartLenSet( f4, newLen ) < 0 ) return error4( c4, e4opt, E80602 ) ; #endif #ifdef S4SAFE if ( f4->fileCreated != 0 ) /* don't need to safeguard temporary files */ #else /* E4ANALYZE must explicitly set file length for later verifications */ #ifndef E4ANALYZE if ( f4->doBuffer == 0 || f4->bufferWrites == 0 ) #endif #endif #endif { /* needed !!! */ #ifndef S4OFF_OPTIMIZE if ( f4->fileCreated == 1 ) #endif { /* */ /* */ /* */ #ifdef S4WIN32 #ifdef S4MULTI_THREAD EnterCriticalSection( &f4->critical4file ) ; #endif #ifdef S4WRITE_DELAY if ( l4numNodes( &f4->delayWriteFileList ) != 0 ) { for ( delayLink = (LINK4 *)l4first( &f4->delayWriteFileList ) ;; ) { if ( delayLink == 0 ) break ; writeDelay = (FILE4WRITE_DELAY *)(delayLink - 1 ) ; delayLink = (LINK4 *)l4next( &f4->delayWriteFileList, delayLink ) ; /* now, if the delay-write is without the boundaries of the len-set, then remove that part */ if ( ( writeDelay->pos + (long)writeDelay->len ) > newLen ) { /* maybe is being written to disk now, in which case must wait */ while ( writeDelay->usageFlag == r4inUse ) /* is being written to disk, just wait until it is done... */ Sleep( 0 ) ; if ( writeDelay->usageFlag == r4finished ) /* is written to disk, so can just ignore */ continue ; if ( writeDelay->pos > newLen ) /* just remove */ { writeDelay->status = 0 ; writeDelay->usageFlag = r4finished ; /* outside of critical section, to allow a wait for completion while keeping the critical section */ l4remove( &f4->codeBase->delayWriteList, writeDelay ) ; l4remove( &f4->delayWriteFileList, &writeDelay->fileLink ) ; writeDelay->completionRoutine( writeDelay ) ; mem4free( c4->delayWriteMemory, writeDelay ) ; } else /* just reduce the length */ writeDelay->len = newLen - writeDelay->pos ; } } } #endif if ( SetFilePointer( (HANDLE)f4->hand, newLen, NULL, FILE_BEGIN ) == (DWORD)-1 ) return error4describe( c4, e4lenSet, E90606, f4->name, 0, 0 ) ; if ( SetEndOfFile( (HANDLE)f4->hand ) ) rc = 0 ; else rc = -1 ; #ifdef S4MULTI_THREAD LeaveCriticalSection( &f4->critical4file ) ; #endif #else #ifdef __SC__ rc = 0 ; dosFlush.x.ax = 0x4200; dosFlush.x.bx = f4->hand ; 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 ) return error4( c4, e4lenSet, E90606 ) ; dosFlush.h.ah = 0x40; dosFlush.x.bx = f4->hand ; dosFlush.x.cx = 0x00; intdos( &dosFlush, &dosFlush ) ; if ( dosFlush.x.cflag != 0 ) return error4( c4, e4lenSet, E90606 ) ; rc = 0 #else #ifdef S4NO_CHSIZE rc = ftruncate( f4->hand, newLen ) ; #else rc = chsize( f4->hand, newLen ) ; #endif #endif #endif /* */ if ( rc < 0 ) return error4describe( c4, e4lenSet, E90606, f4->name, (char *)0, (char *)0 ) ; } } /* needed ! ! ! */ return 0 ; } static unsigned file4readLowDo( FILE4 *f4, const long pos, void *ptr, const unsigned len ) { long rc ; unsigned urc ; #ifndef S4OFF_OPTIMIZE #ifdef S4OPTIMIZE_STATS DATA4 *stat ; CODE4 *c4 ; #endif #endif #ifdef S4MULTI_THREAD EnterCriticalSection( &f4->critical4file ) ; #endif #ifndef S4OFF_OPTIMIZE #ifdef S4OPTIMIZE_STATS c4 = f4->codeBase ; stat = c4->statusDbf ; if ( stat != 0 ) /* track stats */ { if ( f4 != &stat->dataFile->file ) /* don't do for the stat file! */ { if ( d4appendStart( stat, 0 ) == 0 ) { f4assignChar( c4->typeFld, 'L' ) ; /* low-level */ f4assign( c4->fileNameFld, f4->name ) ; f4assignLong( c4->offsetFld, pos ) ; f4assignLong( c4->lengthFld, len ) ; d4append( stat ) ; } } } #endif #endif #ifdef S4WIN32 rc = SetFilePointer( (HANDLE)f4->hand, pos, NULL, FILE_BEGIN ) ; if ( rc != (DWORD)-1 ) rc = pos ; #else /* */ /* */ /* */ #ifdef S4WINDOWS rc = _llseek( f4->hand, pos, 0 ) ; #else #ifdef S4LSEEK rc = f4lseek( f4, pos, 0, 0 ) ; #else rc = lseek( f4->hand, pos, 0 ) ; #endif #endif /* */ #endif if ( rc != pos ) { #ifdef S4MULTI_THREAD LeaveCriticalSection( &f4->critical4file ) ; #endif file4readError( f4, pos, len, "file4readLow" ) ; return 0 ; } #ifdef S4WIN32 ReadFile( (HANDLE)f4->hand, ptr, len, (unsigned long *)&urc, NULL ) ; #else /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ #ifdef S4WINDOWS urc = (unsigned)_lread( f4->hand, (char *)ptr, len ) ; #else #ifdef S4LSEEK /* if reading past EOF */ if ( pos+len > u4filelength( f4->hand ) ) urc = (unsigned)read( f4->hand, ptr, u4filelength(f4->hand) - pos ) ; else urc = (unsigned)read( f4->hand, ptr, len ) ; #else urc = (unsigned)read( f4->hand, ptr, len ) ; #endif #endif /* */ #endif if ( urc > len ) { #ifdef S4MULTI_THREAD LeaveCriticalSection( &f4->critical4file ) ; #endif file4readError( f4, pos, len, "file4readLow" ) ; return 0 ; } #ifndef S4OFF_OPTIMIZE #ifdef E4ANALYZE_ALL if ( f4->hasDup == 1 ) if ( f4->doBuffer == 1 || f4->link.n == 0 ) if ( file4cmpPart( f4->codeBase, ptr, f4, pos, urc ) != 0 ) { #ifdef S4MULTI_THREAD LeaveCriticalSection( &f4->critical4file ) ; #endif error4( f4->codeBase, e4opt, E80602 ) ; return 0 ; } #endif #endif #ifdef S4MULTI_THREAD LeaveCriticalSection( &f4->critical4file ) ; #endif return urc ; } /* this function also includes advance-read/delay-write checking */ unsigned file4readLow( FILE4 *f4, const long pos, void *ptr, const unsigned len ) { unsigned urc ; #ifndef S4OFF_OPTIMIZE #ifdef S4OPTIMIZE_STATS DATA4 *stat ; CODE4 *c4 ; #endif #endif #ifdef S4WRITE_DELAY FILE4WRITE_DELAY *writeDelay ; LINK4 *delayLink ; long beforeLen, afterPos, afterLen ; unsigned copyLen, copyPos ; #endif #ifdef S4ADVANCE_READ long offset ; #endif #ifdef S4ADVANCE_READ /* check the special advance-read-buffer for the file first, if it fits in entirely, then copy from there instead of performing read */ if ( f4->advanceReadBufStatus != AR4EMPTY ) /* if it is set put still in area, then wait for the advance-read to finish */ { offset = pos - f4->advanceReadBufPos ; if ( ( offset >= 0 ) && ( ( pos + len ) <= ( f4->advanceReadBufPos + f4->advanceReadBufLen ) ) ) { while( f4->advanceReadBufStatus == AR4SET ) Sleep( 0 ) ; if ( f4->advanceReadBufStatus == AR4FULL ) /* successful read */ { memcpy( (char *)ptr, f4->advanceReadBuf + offset, len ) ; return len ; } } } #endif #ifdef S4MULTI_THREAD EnterCriticalSection( &f4->critical4file ) ; #endif #ifdef S4WRITE_DELAY /* make sure that the data to read isn't in memory */ urc = 0 ; if ( l4numNodes( &f4->delayWriteFileList ) != 0 ) /* check for pieces already in memory */ { for ( delayLink = 0 ;; ) { delayLink = (LINK4 *)l4next( &f4->delayWriteFileList, delayLink ) ; if ( delayLink == 0 ) break ; writeDelay = (FILE4WRITE_DELAY *)(delayLink - 1 ) ; /* now, if the delay piece belongs in the buffer, then read all the info before the delay piece, copy the delay piece over, and read all the info after the delay piece */ if ( pos >= ( writeDelay->pos + (long)writeDelay->len ) ) /* outside of block */ continue ; if ( ( pos + (long)len ) <= writeDelay->pos ) /* outside of block */ continue ; beforeLen = writeDelay->pos - pos ; if ( beforeLen < 0 ) beforeLen = 0 ; if ( beforeLen == 0 ) { copyPos = pos - writeDelay->pos ; copyLen = len ; } else { copyPos = 0 ; copyLen = len - beforeLen ; } if ( copyLen > ( writeDelay->len - copyPos ) ) copyLen = writeDelay->len - copyPos ; afterPos = beforeLen + pos + copyLen ; afterLen = pos + len - writeDelay->pos - writeDelay->len ; if ( beforeLen != 0 ) urc = file4readLow( f4, pos, ptr, beforeLen ) ; if ( urc == (unsigned int)beforeLen ) { memcpy( (char *)ptr + beforeLen, writeDelay->data + copyPos, copyLen ) ; urc += copyLen ; if ( afterLen > 0 ) /* is negative if read ends within block */ urc += file4readLow( f4, afterPos, (char *)ptr + beforeLen + copyLen, afterLen ) ; } LeaveCriticalSection( &f4->critical4file ) ; return urc ; } } #endif urc = file4readLowDo( f4, pos, ptr, len ) ; #ifdef S4MULTI_THREAD LeaveCriticalSection( &f4->critical4file ) ; #endif #ifdef S4ADVANCE_READ file4advanceReadWriteOver( f4, pos, len, ptr, 0 ) ; #endif return urc ; } unsigned S4FUNCTION file4read( FILE4 *f4, const long pos, void *ptr, const unsigned len ) { #ifndef S4OFF_OPTIMIZE unsigned urc ; #ifdef S4OPTIMIZE_STATS DATA4 *stat ; CODE4 *c4 ; #endif #endif #ifdef E4PARM_HIGH if ( f4 == 0 || pos < 0 || ptr == 0 ) { error4( 0, e4parm_null, E90607 ) ; return 0 ; } #endif #ifdef E4ANALYZE if ( f4->hand < 0 ) { error4( f4->codeBase, e4parm, E90607 ) ; return 0 ; } #endif if ( error4code( f4->codeBase ) < 0 ) return 0 ; if ( len == 0 ) return 0 ; #ifndef S4OFF_OPTIMIZE if ( f4->doBuffer ) { #ifdef S4OPTIMIZE_STATS c4 = f4->codeBase ; stat = c4->statusDbf ; if ( stat != 0 ) /* track stats */ { if ( f4 != &stat->dataFile->file ) /* don't do for the stat file! */ { if ( d4appendStart( stat, 0 ) == 0 ) { f4assignChar( c4->typeFld, 'H' ) ; /* high-level */ f4assign( c4->fileNameFld, f4->name ) ; f4assignLong( c4->offsetFld, pos ) ; f4assignLong( c4->lengthFld, len ) ; d4append( stat ) ; } } } #endif urc = (unsigned)opt4fileRead( f4, pos, ptr, len ) ; if ( urc > len ) { file4readError( f4, pos, len, "file4read" ) ; return 0 ; } return urc ; } else { if ( f4->fileCreated == 0 ) /* cannot read from non-existant file */ return 0 ; #endif return file4readLow( f4, pos, ptr, len ) ; #ifndef S4OFF_OPTIMIZE } #endif } int S4FUNCTION file4readAll( FILE4 *f4, const long pos, void *ptr, const unsigned len ) { unsigned urc ; #ifdef E4PARM_HIGH if ( f4 == 0 || pos < 0 || ptr == 0 ) return error4( 0, e4parm_null, E90608 ) ; #endif #ifdef E4ANALYZE if ( f4->hand < 0 ) return error4( f4->codeBase, e4parm, E90608 ) ; #endif if ( error4code( f4->codeBase ) < 0 ) return -1 ; if ( len == 0 ) return 0 ; #ifndef S4OFF_OPTIMIZE if ( f4->doBuffer ) { urc = opt4fileRead( f4, pos, ptr, len ) ; if ( urc != len ) return file4readError( f4, pos, len, "file4readAll" ) ; return 0 ; } else { if ( f4->fileCreated == 0 ) /* cannot read from non-existant file */ return error4( f4->codeBase, e4opt, E90607 ) ; #endif urc = file4readLow( f4, pos, ptr, len ) ; if ( urc != len ) return file4readError( f4, pos, len, "file4readAllLow" ) ; #ifndef S4OFF_OPTIMIZE } #endif return 0 ; } int S4FUNCTION file4readError( FILE4 *f4, const long pos, const unsigned int len, const char *location ) { char posBuf[40] ; memset( posBuf, 0, sizeof( posBuf ) ) ; memset( posBuf, ' ', sizeof( posBuf ) - 1 ) ; c4ltoa45( pos, posBuf, 19 ) ; c4ltoa45( (long)len, posBuf + 20, 19 ) ; return error4describe( f4->codeBase, e4read, E90621, f4->name, posBuf, location ) ; } int S4FUNCTION file4replace( FILE4 *keep, FILE4 *from ) { FILE4 tmp ; int rc ; char fromName[LEN4PATH] ; #ifndef S4SINGLE char *buf ; unsigned bufSize ; #ifdef S4LOW_MEMORY #ifndef S4OFF_OPTIMIZE int hasOpt ; #endif #endif long pos, fLen ; #endif #ifdef E4PARM_LOW if ( keep == 0 || from == 0 ) return error4( 0, e4parm_null, E90609 ) ; #endif rc = 0 ; #ifdef E4ANALYZE if ( from->isReadOnly || keep->isReadOnly ) return error4( from->codeBase, e4parm, E90601 ) ; #endif #ifndef S4SINGLE if ( keep->lowAccessMode == OPEN4DENY_RW ) { #endif memcpy( (void *)&tmp, (void *)keep, sizeof ( FILE4 ) ) ; /* remember settings */ /* 05/09/96 AS first, must flush the files to disk to avoid delay-writes which are based on the FILE4 pointer which is maintained, instead of the physical handle, which is not --> or at least unoptimize it */ file4optimize( from, OPT4OFF, OPT4OTHER ) ; file4optimize( keep, OPT4OFF, OPT4OTHER ) ; file4flush( from ) ; file4flush( keep ) ; keep->hand = from->hand ; from->hand = tmp.hand ; keep->doAllocFree = 0 ; strncpy( fromName, from->name, sizeof( fromName ) ) ; from->name = keep->name ; from->isTemp = 1 ; if ( file4close ( from ) ) return -1 ; if ( file4close ( keep ) ) return -1 ; if ( u4rename( fromName, tmp.name ) < 0 ) return -1 ; if ( file4open ( keep, tmp.codeBase, tmp.name, 0 ) ) return -1 ; keep->isTemp = tmp.isTemp ; keep->doAllocFree = tmp.doAllocFree ; #ifndef S4OFF_OPTIMIZE if ( tmp.link.n != 0 ) /* file was optimized... */ file4optimizeLow( keep, tmp.codeBase->optimize, tmp.type, tmp.expectedReadSize, tmp.ownerPtr ) ; #endif #ifndef S4SINGLE } else /* can't lose the file handle if other user's have the file open, so just do a copy */ { file4lenSet( keep, 0L ) ; bufSize = from->codeBase->memSizeBuffer ; #ifdef S4LOW_MEMORY #ifndef S4OFF_OPTIMIZE hasOpt = from->codeBase->hasOpt && from->codeBase->opt.numBuffers ; code4optSuspend( from->codeBase ) ; #endif #endif for ( ;; bufSize -= 0x800 ) { if ( bufSize < 0x800 ) /* make one last try */ { bufSize = 100 ; buf = (char *)u4allocEr( from->codeBase, (long)bufSize ) ; if ( buf == 0 ) return -1 ; } buf = (char *)u4alloc( (long)bufSize ) ; if ( buf ) break ; } pos = 0 ; for( fLen = file4len( from ) ; fLen > 0 ; fLen -= bufSize ) { bufSize = ((long)bufSize > fLen) ? (unsigned)fLen : bufSize ; if ( file4readAll( from, pos, buf, (unsigned)bufSize ) < 0 ) { rc = -1 ; break ; } if ( file4write( keep, pos, buf, (unsigned)bufSize ) < 0 ) { rc = -1 ; break ; } pos += bufSize ; } from->isTemp = 1 ; file4close( from ) ; u4free( buf ) ; #ifdef S4LOW_MEMORY #ifndef S4OFF_OPTIMIZE if ( hasOpt ) code4optRestart( keep->codeBase ) ; #endif #endif } #endif return rc ; } #ifdef S4NO_ECVT #define S4NO_FCVT #endif #ifdef S4NO_FCVT #define MAXIMUM 30 #define PRECISION 17 static char valueStr[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 *decPtr, int *signPtr ) { int dptr, count, j, k ; char *vPtr ; if ( numdigits < 0 ) numdigits = 0 ; else if ( numdigits > MAXIMUM ) numdigits = MAXIMUM ; if ( value < 0.0 ) { value = -value ; *signPtr = 1 ; } else *signPtr = 0 ; if ( value == 0.0 ) { memset( valueStr, '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 ( vPtr = &valueStr[0]; vPtr <= &valueStr[numdigits]; vPtr++ ) { if ( vPtr >= &valueStr[PRECISION] ) *vPtr = '0' ; else { j = value ; *vPtr = j + '0' ; value = ( value - j + 1.0e-15 ) * 10.0 ; } } --vPtr ; if ( *vPtr >= '5' ) { while (1) { if ( vPtr == &valueStr[0] ) { dptr++ ; valueStr[0] = '1' ; break ; } *vPtr = 0 ; --vPtr ; if ( *vPtr != '9' ) { (*vPtr)++ ; break ; } } } } *decPtr = dptr ; valueStr[numdigits] = 0 ; return valueStr ; } #endif char *f4fcvt( double value, int numdigits, int *decPtr, int *signPtr ) { int dptr, count, j, k ; char *vPtr ; if ( numdigits < 0 ) numdigits = 0 ; else if ( numdigits > MAXIMUM ) numdigits = MAXIMUM ; if ( value < 0.0 ) { value = -value ; *signPtr = 1 ; } else *signPtr = 0 ; if ( value == 0.0 ) { memset( valueStr, '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 ( vPtr = &valueStr[0]; vPtr <= &valueStr[numdigits]; vPtr++ ) { if ( vPtr >= &valueStr[PRECISION] ) *vPtr = '0' ; else { j = value ; *vPtr = j + '0' ; value = ( value - j + 1.0e-15 ) * 10.0 ; } } --vPtr ; if ( *vPtr >= '5' ) { while (1) { if ( vPtr == &valueStr[0] ) { numdigits++ ; dptr++ ; valueStr[0] = '1' ; break ; } *vPtr = 0 ; --vPtr ; if ( *vPtr != '9' ) { (*vPtr)++ ; break ; } } } } *decPtr = dptr ; valueStr[numdigits] = 0 ; return valueStr ; } #endif /* f4aread.c (c)Copyright Sequiter Software Inc., 1988-1996. All rights reserved. */ #include "d4all.h" #ifdef __TURBOC__ #pragma hdrstop #endif #ifdef S4READ_ADVANCE #define MEM4ADVANCE_START 10 #define MEM4ADVANCE_EXPAND 10 int S4FUNCTION file4advanceRead( FILE4 *f4, const long pos, void *data, const unsigned int len, S4ADVANCE_FUNCTION *completionRoutine, void *completionData ) { FILE4ADVANCE_READ *advanceRead ; CODE4 *c4 ; c4 = f4->codeBase ; if ( c4->advanceReadsEnabled == 0 ) /* not enabled */ return 0 ; if ( c4->advanceReadMemory == 0 ) advanceRead = (FILE4ADVANCE_READ *)mem4createAlloc( c4, &c4->advanceReadMemory, MEM4ADVANCE_START, sizeof( FILE4ADVANCE_READ ), MEM4ADVANCE_EXPAND, 0 ) ; else advanceRead = (FILE4ADVANCE_READ *)mem4alloc( c4->advanceReadMemory ) ; if ( advanceRead == 0 ) return error4( c4, e4memory, E90624 ) ; advanceRead->file = f4 ; advanceRead->data = (char *)data ; advanceRead->len = len ; advanceRead->pos = pos ; advanceRead->usageFlag = r4queued ; advanceRead->completionRoutine = completionRoutine ; advanceRead->completionData = completionData ; EnterCriticalSection( &c4->critical4advanceReadList ) ; l4add( &c4->advanceReadList, advanceRead ) ; l4add( &f4->advanceReadFileList, &advanceRead->fileLink ) ; LeaveCriticalSection( &c4->critical4advanceReadList ) ; SetEvent( c4->pendingReadEvent ) ; /* notify the write thread */ Sleep( 0 ) ; return 0 ; } /* cancels all advance-reads for the given file */ int file4advanceCancel( FILE4 *f4 ) { FILE4ADVANCE_READ *advanceRead ; LINK4 *advanceReadLink, *saved ; CODE4 *c4 ; c4 = f4->codeBase ; EnterCriticalSection( &c4->critical4advanceReadList ) ; for ( advanceReadLink = (LINK4 *)l4first( &f4->advanceReadFileList ) ;; ) { if ( advanceReadLink == 0 ) break ; advanceRead = (FILE4ADVANCE_READ *)(advanceReadLink - 1) ; saved = (LINK4 *)l4next( &f4->advanceReadFileList, advanceReadLink ) ; if ( advanceRead->usageFlag == r4queued ) /* do ourselves */ { l4remove( &f4->advanceReadFileList, advanceReadLink ) ; l4remove( &c4->advanceReadList, advanceRead ) ; advanceRead->status = 0 ; advanceRead->usageFlag = r4finished ; mem4free( c4->advanceReadMemory, advanceRead ) ; } advanceReadLink = saved ; } LeaveCriticalSection( &c4->critical4advanceReadList ) ; for ( ;; ) { /* now verify that the checkInUse read gets completed */ if ( l4numNodes( &f4->advanceReadFileList ) == 0 ) break ; #ifdef E4ANALYZE if ( l4numNodes( &f4->advanceReadFileList ) > 1 ) /* in theory impossible, it means delay-write has 2 files writing at same time */ return error4( c4, e4struct, E90624 ) ; #endif SetEvent( c4->pendingReadEvent ) ; /* notify the write thread */ Sleep( 0 ) ; /* give up our time slice to get the delay-write going */ } #ifndef S4OFF_OPTIMIZE if ( c4->opt.advanceReadFile == f4 ) { c4->opt.advanceLargeBufferAvail = AR4EMPTY ; c4->opt.advanceReadFile = 0 ; } #endif return 0 ; } #ifdef S4USE_INT_DELAY int file4advanceReadMain( void *data ) #else void file4advanceReadMain( void *data ) #endif { CODE4 *c4 ; FILE4ADVANCE_READ *advanceRead ; c4 = (CODE4 *)data ; c4->advanceReadsEnabled = 1 ; for ( ;; ) { if ( l4numNodes( &c4->advanceReadList ) == 0 ) { if ( c4->uninitializeAdvanceRead == 1 ) /* shutdown */ { SetEvent( c4->initUndoAdvanceRead ) ; #ifdef S4USE_INT_DELAY return 0 ; #else return ; #endif } else { WaitForSingleObject( c4->pendingReadEvent, INFINITE ) ; ResetEvent( c4->pendingReadEvent ) ; } } else /* perform a read on the first available block */ { EnterCriticalSection( &c4->critical4advanceReadList ) ; advanceRead = (FILE4ADVANCE_READ *)l4first( &c4->advanceReadList ) ; if ( advanceRead == 0 ) /* maybe got removed by main thread, so none to read... */ { LeaveCriticalSection( &c4->critical4advanceReadList ) ; Sleep( 0 ) ; continue ; } advanceRead->usageFlag = r4inUse ; LeaveCriticalSection( &c4->critical4advanceReadList ) ; advanceRead->status = file4readLowDo( advanceRead->file, advanceRead->pos, advanceRead->data, advanceRead->len ) ; advanceRead->usageFlag = r4finished ; /* outside of critical section, to allow a wait for completion while keeping the critical section */ EnterCriticalSection( &c4->critical4advanceReadList ) ; l4remove( &c4->advanceReadList, advanceRead ) ; l4remove( &advanceRead->file->advanceReadFileList, &advanceRead->fileLink ) ; /* the completion routine may get reset by another routine which needed to call it */ if ( advanceRead->completionRoutine != 0 ) advanceRead->completionRoutine( advanceRead ) ; /* for reading, the critical section includes the completion routine because it checks the status flag before modifying it */ LeaveCriticalSection( &c4->critical4advanceReadList ) ; mem4free( c4->advanceReadMemory, advanceRead ) ; } } } /* this function takes a write request, and ensures that any advance-read information which overlaps the write request is updated to reflect the changes */ /* if doCancel is true, it means that advance-reads which overlap the positions partially will be canceled since they are out of date (i.e. based on a write overlap request, not a re-request which is also serviced here) */ void file4advanceReadWriteOver( FILE4 *f4, const long pos, const unsigned len, const void *data, const int doCancel ) { FILE4ADVANCE_READ *advanceRead ; LINK4 *advanceLink ; CODE4 *c4 ; #ifndef S4OFF_OPTIMIZE OPT4 *opt ; #endif c4 = f4->codeBase ; #ifndef S4OFF_OPTIMIZE if ( f4->fileCreated == 0 ) /* ensure file created, else no critical section, can't be advance-reads */ return ; #endif /* first lock out operations on the advance-read list */ EnterCriticalSection( &c4->critical4advanceReadList ) ; /* first take care of the outstanding advance-reads */ if ( l4numNodes( &f4->advanceReadFileList ) != 0 ) { for ( advanceLink = (LINK4 *)l4first( &f4->advanceReadFileList ) ;; ) { if ( advanceLink == 0 ) break ; advanceRead = (FILE4ADVANCE_READ *)(advanceLink - 1 ) ; advanceLink = (LINK4 *)l4next( &f4->advanceReadFileList, advanceLink ) ; if ( pos >= ( advanceRead->pos + (long)advanceRead->len ) ) /* outside of block */ continue ; if ( ( pos + (long)len ) <= advanceRead->pos ) /* outside of block */ continue ; /* if the status is in-use, then just wait for it to finish */ while ( advanceRead->usageFlag == r4inUse ) Sleep( 0 ) ; /* in this case, the piece is finished, so take care of the completion routine ourselves */ /* once that is finished, it is assumed that the completion routine will make it noticed in the handling code below for after-read advance-read data */ if ( advanceRead->usageFlag == r4finished ) { if ( advanceRead->completionRoutine != 0 ) advanceRead->completionRoutine( advanceRead ) ; advanceRead->completionRoutine = 0 ; continue ; } /* now, if the advance piece belongs in the buffer, then read all the info before the delay piece, copy the delay piece over, and read all the info after the delay piece */ /* if the entire block is within the range, then can just copy to it, otherwise cancel it */ if ( ( advanceRead->pos >= pos ) && ( pos + len >= advanceRead->pos + advanceRead->len ) ) /* copy it */ { advanceRead->status = 0 ; memcpy( advanceRead->data, (const char *)data + (advanceRead->pos - pos ), advanceRead->len ) ; advanceRead->usageFlag = r4finished ; /* outside of critical section, to allow a wait for completion while keeping the critical section */ } else { if ( doCancel ) advanceRead->usageFlag = r4canceled ; /* outside of critical section, to allow a wait for completion while keeping the critical section */ else continue ; } l4remove( &f4->codeBase->advanceReadList, advanceRead ) ; l4remove( &f4->advanceReadFileList, &advanceRead->fileLink ) ; if ( advanceRead->completionRoutine != 0 ) advanceRead->completionRoutine( advanceRead ) ; mem4free( f4->codeBase->advanceReadMemory, advanceRead ) ; } } /* now take care of the special-advance-read buffer */ /* if it is not full, then either it is empty or else we serviced it already in the advance-read list */ if ( f4->advanceReadBufStatus == AR4FULL ) { if ( pos < ( f4->advanceReadBufPos + (long)f4->advanceReadBufLen ) ) /* outside of block */ if ( ( pos + (long)len ) > f4->advanceReadBufPos ) /* outside of block */ { if ( ( f4->advanceReadBufPos >= pos ) && ( pos + len >= f4->advanceReadBufPos + f4->advanceReadBufLen ) ) /* copy it */ memcpy( f4->advanceReadBuf, (const char *)data + (f4->advanceReadBufPos - pos ), f4->advanceReadBufLen ) ; else if ( doCancel ) f4->advanceReadBufStatus = AR4EMPTY ; } } /* now take care of the optimized advance-read buffer */ #ifndef S4OFF_OPTIMIZE opt = &c4->opt ; /* if it is not full, then either it is empty or else we serviced it already in the advance-read list */ if ( opt->advanceLargeBufferAvail == AR4FULL ) { if ( pos < ( opt->advanceLargePos + (long)opt->advanceLargeLen ) ) /* outside of block */ if ( ( pos + (long)len ) > opt->advanceLargePos ) /* outside of block */ { if ( ( opt->advanceLargePos >= pos ) && ( pos + len >= opt->advanceLargePos + opt->advanceLargeLen ) ) /* copy it */ memcpy( opt->advanceLargeBuffer, (const char *)data + (opt->advanceLargePos - pos ), opt->advanceLargeLen ) ; else if ( doCancel ) opt->advanceLargeBufferAvail = AR4EMPTY ; } } #endif LeaveCriticalSection( &c4->critical4advanceReadList ) ; } void S4CALL file4advanceReadBufCompletionRoutine( void *advance ) { FILE4ADVANCE_READ *advanceRead ; int *arFlag ; /* to verify safety, use critical section on the arFlag (from CODE4) */ advanceRead = (FILE4ADVANCE_READ *)advance ; arFlag = (int *)(advanceRead->completionData) ; if ( advanceRead->usageFlag == r4canceled ) *arFlag = AR4EMPTY ; else if ( *arFlag == AR4SET ) /* if reset to empty it means the read was cancelled by the main thread, so leave as empty */ { /* verify that all was read */ if ( advanceRead->file->advanceReadBufLen == advanceRead->status ) *arFlag = AR4FULL ; else *arFlag = AR4EMPTY ; } } void opt4advanceReadBuf( FILE4 *f4, long pos, unsigned len ) { CODE4 *c4 ; LINK4 *advanceLink ; FILE4ADVANCE_READ *advanceRead ; #ifndef S4OFF_OPTIMIZE long hashVal, adjustedPos ; unsigned int extraRead ; OPT4 *opt ; #endif c4 = f4->codeBase ; /* if optimization is enabled, then first ensure that the block is not already in memory */ #ifndef S4OFF_OPTIMIZE opt = &f4->codeBase->opt ; if ( len > opt->blockSize ) /* don't do multi-block reads */ return ; extraRead = (unsigned) ((unsigned long)((unsigned long)pos << opt->numShift ) >> opt->numShift ) ; adjustedPos = pos - extraRead ; hashVal = opt4fileHash( opt, f4, (unsigned long)adjustedPos ) ; if ( opt4fileReturnBlock( f4, pos, hashVal ) != 0 ) return ; #endif if ( f4->advanceReadBuf == 0 ) { f4->advanceReadBuf = (char *)u4alloc( len ) ; if ( f4->advanceReadBuf == 0 ) return ; f4->advanceReadBufStatus = AR4EMPTY ; f4->advanceReadBufLen = len ; } else { if ( len > f4->advanceReadBufLen ) /* too large, don't bother */ return ; if ( f4->advanceReadBufStatus == AR4SET || f4->advanceReadBufStatus == AR4FULL ) if ( f4->advanceReadBufPos == pos ) /* already advanced on this read */ return ; } #ifndef S4OFF_OPTIMIZE if ( f4->advanceReadBufStatus == AR4FULL ) /* finished read, so place into optimization */ { /* first ensure that it is not already bufferred */ extraRead = (unsigned) ((unsigned long)((unsigned long)f4->advanceReadBufPos << opt->numShift ) >> opt->numShift ) ; adjustedPos = f4->advanceReadBufPos - extraRead ; hashVal = opt4fileHash( opt, f4, (unsigned long)adjustedPos ) ; if ( opt4fileReturnBlock( f4, adjustedPos, hashVal ) == 0 ) { /* the opt4fileWrite function can be used to place data into memory without marking as changed based on last paramater... */ opt4fileWrite( f4, f4->advanceReadBufPos, f4->advanceReadBufLen, f4->advanceReadBuf, 0 ) ; } f4->advanceReadBufStatus = AR4EMPTY ; } #endif EnterCriticalSection( &c4->critical4advanceReadList ) ; if ( f4->advanceReadBufStatus == AR4SET ) /* must remove from list */ { for ( advanceLink = (LINK4 *)l4first( &f4->advanceReadFileList ) ;; ) { if ( advanceLink == 0 ) break ; advanceRead = (FILE4ADVANCE_READ *)(advanceLink - 1 ) ; advanceLink = (LINK4 *)l4next( &f4->advanceReadFileList, advanceLink ) ; if ( advanceRead->completionRoutine == file4advanceReadBufCompletionRoutine ) /* spec buf */ { if ( advanceRead->usageFlag != r4queued ) /* must just wait */ { LeaveCriticalSection( &c4->critical4advanceReadList ) ; while ( f4->advanceReadBufStatus == AR4SET ) Sleep( 0 ) ; EnterCriticalSection( &c4->critical4advanceReadList ) ; break ; } /* is queued, so can just remove */ advanceRead->status = 0 ; advanceRead->usageFlag = r4finished ; l4remove( &f4->codeBase->advanceReadList, advanceRead ) ; l4remove( &f4->advanceReadFileList, &advanceRead->fileLink ) ; mem4free( f4->codeBase->advanceReadMemory, advanceRead ) ; } } } #ifndef S4OFF_OPTIMIZE /* in optimized case, make sure the read is done on a block boundary */ extraRead = (unsigned) ((unsigned long)((unsigned long)pos << opt->numShift ) >> opt->numShift ) ; adjustedPos = pos - extraRead ; len = f4->advanceReadBufLen ; f4->advanceReadBufStatus = AR4SET ; f4->advanceReadBufPos = adjustedPos ; LeaveCriticalSection( &c4->critical4advanceReadList ) ; file4advanceRead( f4, pos, f4->advanceReadBuf, len, file4advanceReadBufCompletionRoutine, &f4->advanceReadBufStatus ) ; #else f4->advanceReadBufStatus = AR4SET ; f4->advanceReadBufPos = pos ; LeaveCriticalSection( &c4->critical4advanceReadList ) ; file4advanceRead( f4, pos, f4->advanceReadBuf, len, file4advanceReadBufCompletionRoutine, &f4->advanceReadBufStatus ) ; #endif } #endif /* S4READ_ADVANCE */