/* m4file.c (c)Copyright Sequiter Software Inc., 1988-1996. All rights reserved. */ #include "d4all.h" #ifndef S4UNIX #ifdef __TURBOC__ #pragma hdrstop #endif #endif #ifndef S4CLIENT #ifndef S4MEMO_OFF #ifndef S4OFF_MULTI /* ndx/clipper versions have no free chain--do not lock memo file */ #ifndef N4OTHER /* the lock is forced since a memo file lock only lasts if the .dbf file is locked */ int memo4fileLock( MEMO4FILE *f4memo ) { int rc, oldAttempts ; if ( f4memo->fileLock == 1 ) return 0 ; if ( f4memo->file.hand == -1 ) return -1 ; oldAttempts = f4memo->file.codeBase->lockAttempts ; f4memo->file.codeBase->lockAttempts = WAIT4EVER ; #ifdef S4MDX rc = file4lock( &f4memo->file, L4LOCK_POS - 1L, 2L ) ; #endif #ifdef S4FOX rc = file4lock( &f4memo->file, L4LOCK_POS_OLD, 1L ) ; #endif f4memo->file.codeBase->lockAttempts = oldAttempts ; if ( rc == 0 ) f4memo->fileLock = 1 ; #ifndef S4OPTIMIZE_OFF file4refresh( &f4memo->file ) ; /* make sure all up to date */ #endif return rc ; } int memo4fileUnlock( MEMO4FILE *f4memo ) { int rc ; if ( f4memo->fileLock == 0 ) return 0 ; #ifdef S4MDX rc = file4unlock( &f4memo->file, L4LOCK_POS - 1L, 2L ) ; #endif #ifdef S4FOX rc = file4unlock( &f4memo->file, L4LOCK_POS_OLD, 1L ) ; #endif if ( rc == 0 ) f4memo->fileLock = 0 ; return rc ; } #endif #endif int memo4fileOpen( MEMO4FILE *f4memo, DATA4FILE *d4, char *name ) { MEMO4HEADER header ; int rc ; f4memo->data = d4 ; if ( file4open( &f4memo->file, d4->c4, name, 1 ) ) return -1 ; #ifndef S4OPTIMIZE_OFF file4optimize( &f4memo->file, d4->c4->optimize, OPT4OTHER ) ; #endif if ( (rc = file4readAll(&f4memo->file, 0L, &header, sizeof(header))) < 0 ) return -1 ; #ifdef S4BYTE_SWAP header.nextBlock = x4reverseLong( (void *)&header.nextBlock ) ; #ifndef S4MNDX #ifndef S4MFOX header.x102 = 0x201 ; #endif header.blockSize = x4reverseShort( (void *)&header.blockSize ) ; #endif #endif #ifdef S4MFOX f4memo->blockSize = x4reverseShort( (void *)&header.blockSize ) ; #else #ifdef S4MNDX f4memo->blockSize = MEMO4SIZE ; #else f4memo->blockSize = header.blockSize ; #endif #endif return rc ; } #ifdef S4MFOX /* offset is # bytes from start of memo that reading should begin, readMax is the maximum possible that can be read (limited to an unsigned int, so is 16-bit/32-bit compiler dependent. */ int memo4fileReadPart( MEMO4FILE *f4memo, long memoId, char **ptrPtr, unsigned *lenPtr, unsigned long offset, const unsigned readMax, long *type ) { unsigned long pos, avail ; MEMO4BLOCK memoBlock ; if ( memoId <= 0L ) { *lenPtr = 0 ; return 0 ; } pos = (unsigned long)memoId * f4memo->blockSize ; if ( file4readAll( &f4memo->file, pos, &memoBlock, sizeof( MEMO4BLOCK ) ) < 0) return -1 ; #ifdef S4BYTE_SWAP #ifdef S4MFOX memoBlock.type = x4reverseLong( (void *)&memoBlock.type ) ; #else memoBlock.startPos = x4reverseShort( (void *)&memoBlock.startPos ) ; #endif memoBlock.numChars = x4reverseLong( (void *)&memoBlock.numChars ) ; #endif memoBlock.numChars = x4reverseLong( (void *)&memoBlock.numChars ) ; avail = memoBlock.numChars - offset ; if ( avail > (unsigned long)readMax ) avail = readMax ; if ( avail > (unsigned long)*lenPtr ) { if ( *lenPtr > 0 ) u4free( *ptrPtr ) ; *ptrPtr = (char *)u4allocEr( f4memo->file.codeBase, avail + 1 ) ; if ( *ptrPtr == 0 ) return e4memory ; } *lenPtr = (unsigned)avail ; *type = x4reverseLong( &memoBlock.type ) ; return (int)file4readAll( &f4memo->file, offset + pos + (long)(2*sizeof(S4LONG)), *ptrPtr, *lenPtr ) ; } #endif #ifdef S4MNDX extern char f4memoNullChar ; #endif #ifdef S4MFOX int memo4fileRead( MEMO4FILE *f4memo, long memoId, char **ptrPtr, unsigned int *ptrLen, long *mType ) #else int memo4fileRead( MEMO4FILE *f4memo, long memoId, char **ptrPtr, unsigned int *ptrLen ) #endif { #ifdef S4MFOX return memo4fileReadPart( f4memo, memoId, ptrPtr, ptrLen, 0L, UINT_MAX -100, mType ) ; #else long pos ; CODE4 *c4 ; #ifdef S4MNDX unsigned int amtRead, lenRead, loop ; char *tPtr ; #else MEMO4BLOCK memoBlock ; unsigned finalLen ; #endif c4 = f4memo->file.codeBase ; #ifdef S4MNDX if ( memoId <= 0L ) { if ( *ptrPtr != &f4memoNullChar ) u4free( *ptrPtr ) ; *ptrPtr = 0 ; *ptrLen = 0 ; return 0 ; } pos = memoId * f4memo->blockSize ; amtRead = 0 ; if ( c4->memoUseBuffer == 0 ) { c4->memoUseBuffer = (char*)u4allocEr( c4, MEMO4SIZE ) ; if ( c4->memoUseBuffer == 0 ) return e4memory ; } for( amtRead = 0 ;; ) { lenRead = file4read( &f4memo->file, pos + amtRead, c4->memoUseBuffer, MEMO4SIZE ) ; if ( lenRead <= 0 ) return -1 ; for ( loop = 0 ; lenRead > 0 ; loop++, lenRead-- ) { if ( c4->memoUseBuffer[loop] == 0x1A ) /* if done */ { if ( loop + amtRead > 0 ) { if ( *ptrLen < amtRead + loop ) { tPtr = (char *)u4allocEr( c4, amtRead + loop + 1 ) ; if ( tPtr == 0 ) return e4memory ; memcpy( tPtr, *ptrPtr, (int)amtRead ) ; if ( *ptrPtr != &f4memoNullChar ) u4free( *ptrPtr ) ; *ptrPtr = tPtr ; } *ptrLen = amtRead + loop ; memcpy( *ptrPtr + amtRead, c4->memoUseBuffer, loop ) ; (*ptrPtr)[amtRead+loop] = 0 ; return 0 ; } else { tPtr = 0 ; if ( *ptrPtr != &f4memoNullChar ) u4free( *ptrPtr ) ; *ptrPtr = 0 ; *ptrLen = 0 ; } return 0 ; } } lenRead = loop ; if ( *ptrLen < amtRead + lenRead ) { tPtr = (char *)u4allocEr( c4, amtRead + lenRead + 1 ) ; if ( tPtr == 0 ) return e4memory ; if ( *ptrLen > 0 ) { memcpy( tPtr, *ptrPtr, *ptrLen ) ; u4free( *ptrPtr ) ; } *ptrPtr = tPtr ; *ptrLen = (unsigned)( amtRead + lenRead ) ; } memcpy( *ptrPtr + amtRead, c4->memoUseBuffer, lenRead ) ; amtRead += lenRead ; } #else if ( memoId <= 0L ) { *ptrLen = 0 ; return 0 ; } pos = memoId * f4memo->blockSize ; if ( file4readAll( &f4memo->file, pos, &memoBlock, sizeof( MEMO4BLOCK ) ) < 0) return -1 ; #ifdef S4BYTE_SWAP memoBlock.startPos = x4reverseShort( (void *)&memoBlock.startPos ) ; memoBlock.numChars = x4reverseLong( (void *)&memoBlock.numChars ) ; #endif if ( memoBlock.minusOne != -1 ) /* must be an invalid entry, so return an empty entry */ { *ptrLen = 0 ; return 0 ; } else { if ( memoBlock.numChars >= UINT_MAX ) return error4( c4, e4info, E95210 ) ; finalLen = (unsigned)memoBlock.numChars - 2 * ( sizeof(short) ) - ( sizeof(S4LONG) ) ; if ( finalLen > *ptrLen ) { if ( *ptrLen > 0 ) u4free( *ptrPtr ) ; *ptrPtr = (char *)u4allocEr( c4, finalLen + 1 ) ; if ( *ptrPtr == 0 ) return e4memory ; } *ptrLen = finalLen ; return file4readAll( &f4memo->file, pos+ memoBlock.startPos, *ptrPtr, finalLen ) ; } #endif #endif } #ifndef S4OFF_WRITE #ifdef S4MFOX /* Writes partial data to a memo record. Usage rules: Must call this function with an offset == 0 to write 1st part of block before any additional writing. In addition, the memoLen must be accurately set during the first call in order to reserve the correct amount of memo space ahead of time. Later calls just fill in data to the reserved disk space. lenWrite is the amount of data to write, offset is the number of bytes from the beginning of the memo in which to write the data Secondary calls to this function assume that everything has been previously set up, and merely performs a file write to the reserved space. The space is not checked to see whether or not it actually is in the bounds specified, so use with care. */ int memo4fileWritePart( MEMO4FILE *f4memo, long *memoIdPtr, const char *ptr, const long memoLen, const long offset, const unsigned lenWrite, const long type ) { int strNumBlocks ; long pos ; #ifndef S4OFF_MULTI #ifndef N4OTHER int rc, lockCond ; #endif #endif MEMO4BLOCK oldMemoBlock ; MEMO4HEADER mh ; unsigned lenRead ; long blockNo ; unsigned nEntryBlks = 0 ; #ifdef E4PARM_LOW if ( memoIdPtr == 0 ) return error4( 0, e4parm_null, E95208 ) ; if ( f4memo->file.hand == -1 ) /* file closed! */ return error4( 0, e4parm, E95208 ) ; #endif if ( offset == 0 ) /* must do the set-up work */ { if ( memoLen == 0 ) { *memoIdPtr = 0L ; return 0 ; } #ifdef E4MISC if ( f4memo->blockSize <= 1 ) return error4( f4memo->data->c4, e4info, E85202 ) ; #endif strNumBlocks = (int) ((memoLen + sizeof(MEMO4BLOCK) + f4memo->blockSize-1) / f4memo->blockSize) ; if ( *memoIdPtr <= 0L ) blockNo = 0L ; else { blockNo = *memoIdPtr ; pos = blockNo * f4memo->blockSize ; file4readAll( &f4memo->file, pos, (char *)&oldMemoBlock, sizeof(oldMemoBlock) ) ; #ifdef S4BYTE_SWAP #ifdef S4MFOX oldMemoBlock.type = x4reverseLong( (void *)&oldMemoBlock.type ) ; #else oldMemoBlock.startPos = x4reverseShort( (void *)&oldMemoBlock.startPos ) ; #endif oldMemoBlock.numChars = x4reverseLong( (void *)&oldMemoBlock.numChars ) ; #endif oldMemoBlock.numChars = x4reverseLong( (void *)&oldMemoBlock.numChars ) ; nEntryBlks = (unsigned) ((oldMemoBlock.numChars + f4memo->blockSize-1)/ f4memo->blockSize ) ; } if ( nEntryBlks >= ((unsigned)strNumBlocks) && blockNo ) /* write to existing position */ *memoIdPtr = blockNo ; else /* read in header record */ { #ifndef S4OFF_MULTI #ifndef N4OTHER lockCond = f4memo->data->memoFile.fileLock ; rc = memo4fileLock( &f4memo->data->memoFile ) ; if ( rc ) return rc ; #endif #endif lenRead = file4read( &f4memo->file, 0L, &mh, sizeof( mh ) ) ; #ifdef S4BYTE_SWAP mh.nextBlock = x4reverseLong( (void *)&mh.nextBlock ) ; mh.blockSize = x4reverseShort( (void *)&mh.blockSize ) ; #endif if ( error4code( f4memo->data->c4 ) < 0 ) { #ifndef S4OFF_MULTI #ifndef N4OTHER if ( !lockCond ) memo4fileUnlock( &f4memo->data->memoFile ) ; #endif #endif return -1 ; } if ( lenRead != sizeof( mh ) ) { #ifndef S4OFF_MULTI #ifndef N4OTHER if ( !lockCond ) memo4fileUnlock( &f4memo->data->memoFile ) ; #endif #endif return file4readError( &f4memo->file, 0L, sizeof( mh ), "memo4fileWritePart" ) ; } *memoIdPtr = x4reverseLong( (void *)&mh.nextBlock ) ; mh.nextBlock = *memoIdPtr + strNumBlocks ; mh.nextBlock = x4reverseLong( (void *)&mh.nextBlock ) ; #ifdef S4BYTE_SWAP mh.nextBlock = x4reverseLong( (void *)&mh.nextBlock ) ; mh.blockSize = x4reverseShort( (void *)&mh.blockSize ) ; #endif file4write( &f4memo->file, 0L, &mh, sizeof( mh ) ) ; #ifndef S4OFF_MULTI #ifndef N4OTHER if ( !lockCond ) memo4fileUnlock( &f4memo->data->memoFile ) ; #endif #endif } if ( memo4fileDump( f4memo, *memoIdPtr, ptr, lenWrite, memoLen, type ) < 0 ) return -1 ; } else return file4write( &f4memo->file, *memoIdPtr * f4memo->blockSize + offset + sizeof( oldMemoBlock ), ptr, lenWrite ) ; return 0 ; } #endif /* S4MFOX */ int memo4fileWrite( MEMO4FILE *f4memo, long *memoIdPtr, const char *ptr, const unsigned ptrLen ) { #ifdef S4MFOX return memo4fileWritePart( f4memo, memoIdPtr, ptr, (long)ptrLen, 0L, ptrLen, 1L ) ; #else int strNumBlocks ; long pos ; #ifndef S4MDX #ifndef S4OFF_MULTI #ifndef N4OTHER int rc, lockCond ; #endif #endif #endif #ifdef S4MNDX MEMO4HEADER mh ; long lenRead ; char buf[MEMO4SIZE] ; int readSize, i ; #else MEMO4BLOCK oldMemoBlock ; MEMO4CHAIN_ENTRY newEntry, cur, prev ; int strWritten ; long prevPrevEntry, prevPrevNum ; long fileLen, extraLen ; #endif #ifdef E4PARM_LOW if ( memoIdPtr == 0 ) return error4( 0, e4parm_null, E95209 ) ; #endif #ifdef S4MNDX if ( ptrLen == 0 ) { *memoIdPtr = 0L ; return 0 ; } strNumBlocks = (int)(((long)ptrLen + f4memo->blockSize-1) / f4memo->blockSize) ; if ( *memoIdPtr <= 0L ) *memoIdPtr = 0L ; else /* read in old record to see if new entry can fit */ { readSize = 0 ; pos = *memoIdPtr * f4memo->blockSize ; do { readSize += MEMO4SIZE ; lenRead = file4read( &f4memo->file, pos, buf, MEMO4SIZE ) ; if ( lenRead <= 0 ) return file4readError( &f4memo->file, pos, MEMO4SIZE, "memo4fileWrite()" ) ; for ( i=0 ; ((long) i) < lenRead ; i++ ) if ( buf[i] == (char)0x1A ) break ; #ifdef E4MISC if ( buf[i] != (char)0x1A && lenRead != MEMO4SIZE ) return error4( f4memo->file.codeBase, e4info, E85203 ) ; #endif pos += MEMO4SIZE ; } while ( i >= MEMO4SIZE && buf[i] != (char) 0x1A ) ; /* Continue if Esc is not located */ if ( ((unsigned)readSize) <= ptrLen ) /* there is not room */ *memoIdPtr = 0 ; } if ( *memoIdPtr == 0 ) /* add entry at eof */ { #ifndef S4OFF_MULTI #ifndef N4OTHER lockCond = f4memo->data->memoFile.fileLock ; rc = memo4fileLock( &f4memo->data->memoFile ) ; if ( rc ) return rc ; #endif #endif lenRead = file4read( &f4memo->file, 0, &mh, sizeof( mh ) ) ; #ifdef S4BYTE_SWAP mh.nextBlock = x4reverseLong( (void *)&mh.nextBlock ) ; #endif if ( error4code( f4memo->data->c4 ) < 0 ) { #ifndef S4OFF_MULTI #ifndef N4OTHER if ( !lockCond ) memo4fileUnlock( &f4memo->data->memoFile ) ; #endif #endif return -1 ; } if ( lenRead != sizeof( mh ) ) { #ifndef S4OFF_MULTI #ifndef N4OTHER if ( !lockCond ) memo4fileUnlock( &f4memo->data->memoFile ) ; #endif #endif return file4readError( &f4memo->file, 0L, sizeof( mh ), "memo4fileWrite()" ) ; } *memoIdPtr = mh.nextBlock ; mh.nextBlock = *memoIdPtr + strNumBlocks ; #ifdef S4BYTE_SWAP mh.nextBlock = x4reverseLong( (void *)&mh.nextBlock ) ; #endif file4write( &f4memo->file, 0, &mh, sizeof( mh ) ) ; #ifndef S4OFF_MULTI #ifndef N4OTHER if ( !lockCond ) memo4fileUnlock( &f4memo->data->memoFile ) ; #endif #endif #ifdef S4BYTE_SWAP mh.nextBlock = x4reverseLong( (void *)&mh.nextBlock ) ; #endif } if ( memo4fileDump( f4memo, *memoIdPtr, ptr, ptrLen, ptrLen ) < 0 ) return -1 ; return 0 ; #else /* S4MMDX */ memset( (void *)&newEntry, 0, sizeof(newEntry) ) ; newEntry.blockNo = *memoIdPtr ; strWritten = 0 ; if ( ptrLen == 0 ) { strWritten = 1 ; *memoIdPtr = 0 ; } /* Initialize information about the old memo entry */ if ( newEntry.blockNo <= 0L ) { if ( strWritten ) { *memoIdPtr = 0L ; return 0 ; } newEntry.num = 0 ; } else { pos = newEntry.blockNo * f4memo->blockSize ; file4readAll( &f4memo->file, pos, (char *)&oldMemoBlock, sizeof(oldMemoBlock) ) ; #ifdef S4BYTE_SWAP oldMemoBlock.startPos = x4reverseShort( (void *)&oldMemoBlock.startPos ) ; oldMemoBlock.numChars = x4reverseLong( (void *)&oldMemoBlock.numChars ) ; #endif newEntry.num = (unsigned)(((long)oldMemoBlock.numChars + (long)f4memo->blockSize-1)/ f4memo->blockSize ) ; } strNumBlocks = (int)(((long)ptrLen+2*(sizeof(short))+(sizeof(S4LONG))+ f4memo->blockSize-1) / f4memo->blockSize ) ; if ( newEntry.num >= strNumBlocks && !strWritten ) { *memoIdPtr = newEntry.blockNo + newEntry.num - strNumBlocks ; if ( memo4fileDump( f4memo, *memoIdPtr, ptr, ptrLen, ptrLen ) < 0 ) return -1 ; strWritten = 1 ; if ( newEntry.num == strNumBlocks ) return 0 ; newEntry.num -= strNumBlocks ; } /* Initialize 'chain' */ memset( (void *)&cur, 0, sizeof(cur) ) ; memset( (void *)&prev, 0, sizeof(prev) ) ; for(;;) { if ( error4code( f4memo->data->c4 ) < 0 ) return -1 ; memo4fileChainFlush( f4memo, &prev ) ; prevPrevEntry = prev.blockNo ; prevPrevNum = prev.num ; memcpy( (void *)&prev, (void *)&cur, sizeof(prev) ) ; if ( newEntry.blockNo > 0 && prev.next > newEntry.blockNo ) { /* See if the new entry fits in here */ memcpy( (void *)&cur, (void *)&newEntry, sizeof(cur) ) ; newEntry.blockNo = 0 ; cur.next = prev.next ; prev.next = cur.blockNo ; cur.toDisk = prev.toDisk = 1 ; } else memo4fileChainSkip( f4memo, &cur ) ; /* See if the entries can be combined. */ if ( prev.blockNo + prev.num == cur.blockNo && prev.num ) { /* 'cur' becomes the combined groups. */ prev.toDisk = 0 ; cur.toDisk = 1 ; cur.blockNo = prev.blockNo ; if ( cur.num >= 0 ) cur.num += prev.num ; prev.blockNo = prevPrevEntry ; prev.num = prevPrevNum ; } if ( strWritten ) { if ( newEntry.blockNo == 0 ) { memo4fileChainFlush( f4memo, &prev ) ; memo4fileChainFlush( f4memo, &cur ) ; return 0 ; } } else /* 'str' is not yet written, try the current entry */ { if ( cur.next == -1 ) /* End of file */ cur.num = strNumBlocks ; if ( cur.num >= strNumBlocks ) { cur.num -= strNumBlocks ; *memoIdPtr = cur.blockNo + cur.num ; memo4fileDump( f4memo, *memoIdPtr, ptr, ptrLen, ptrLen ) ; if ( cur.next == -1 ) /* if end of file */ { /* For dBASE IV compatibility */ fileLen = file4len( &f4memo->file ) ; extraLen = f4memo->blockSize - fileLen % f4memo->blockSize ; if ( extraLen != f4memo->blockSize ) file4lenSet( &f4memo->file, fileLen+extraLen ) ; } strWritten = 1 ; if ( cur.num == 0 ) { if ( cur.next == -1 ) /* End of file */ prev.next = cur.blockNo + strNumBlocks ; else prev.next = cur.next ; prev.toDisk = 1 ; cur.toDisk = 0 ; } else cur.toDisk = 1 ; } } } #endif #endif } #endif /* S4OFF_WRITE */ #endif /* S4MEMO_OFF */ #endif /* S4CLIENT */