/* xbfile.cpp XBase64 Software Library Copyright (c) 1997,2003,2014,2022 Gary A Kunkel The xb64 software library is covered under the terms of the GPL Version 3, 2007 license. Email Contact: XDB-devel@lists.sourceforge.net XDB-users@lists.sourceforge.net This module handles all the low level file I/O and is the base class for the table, memo and index classes */ #include "xbase.h" namespace xb{ /************************************************************************/ //! @brief Class Constructor. xbFile::xbFile( xbXBase * x ){ fp = NULL; bFileOpen = xbFalse; ulBlockSize = 0; iFileNo = 0; xbase = x; if( GetMultiUser() == xbTrue ) iShareMode = XB_MULTI_USER; else iShareMode = XB_SINGLE_USER; iOpenMode = 0; #ifdef XB_LOCKING_SUPPORT iLockRetries = -1; #endif #ifdef HAVE_SETENDOFFILE_F fHandle = NULL; #endif } /************************************************************************/ //! @brief Class Destructor. xbFile::~xbFile(){ if( bFileOpen ) xbFclose(); } /************************************************************************/ //! @brief Create a unique file name. /*! Given a directory and file extension as inputs, create a unique file name. \param sDirIn Directory \param sExtIn File Extension \param sFqnOut A fully qualifed unique file name as output \returns Return Codes */ xbInt16 xbFile::CreateUniqueFileName( const xbString & sDirIn, const xbString & sExtIn, xbString &sFqnOut ){ return CreateUniqueFileName( sDirIn, sExtIn, sFqnOut, 0 ); } /************************************************************************/ //! @brief Create a unique file name. /*! Given a directory and file extension as inputs, create a unique file name. \param sDirIn Directory \param sExtIn File Extension \param iOption 0 - look only for one file for a given directory and extension
1 - if file name extension is "dbf" or "DBF", verify unique dbt or DBT (memo) file is also available
\param sFqnOut A fully qualifed unique file name as output \returns Return Codes */ xbInt16 xbFile::CreateUniqueFileName( const xbString & sDirIn, const xbString & sExtIn, xbString &sFqnOut, xbInt16 iOption ){ xbBool bUniqueFileNameFound = xbFalse; xbFile f( xbase); xbInt32 l = 1; xbString sMemoFileName; while( !bUniqueFileNameFound ){ sFqnOut.Sprintf( "%sxbTmp%03d.%s", sDirIn.Str(), l, sExtIn.Str()); if( iOption == 1 && sExtIn == "DBF" ){ sMemoFileName.Sprintf( "%sxbTmp%03d.DBT", sDirIn.Str(), l ); } else if( iOption == 1 && sExtIn == "dbf" ){ sMemoFileName.Sprintf( "%sxbTmp%03d.dbt", sDirIn.Str(), l ); } if( f.FileExists( sFqnOut ) || ( sMemoFileName.Len() > 0 && f.FileExists( sMemoFileName ))){ l++; } else { bUniqueFileNameFound = xbTrue; } if( l > 999 ) return XB_FILE_EXISTS; } return XB_NO_ERROR; } /*************************************************************************/ //! @brief Determine which version the memo (dbt) file is. /*! This routine uses the first byte in the dbf file to determine which memo file version is in use. The main difference between version 3 and 4 is that version 4 will reuse blocks if they become available. Version 3 does not. \param cFileTypeByte is an output field and is one of:

0 - none
3 - Dbase III+
4 - Dbase IV
\returns Return Codes */ xbInt16 xbFile::DetermineXbaseMemoVersion( unsigned char cFileTypeByte ) const { if( BitSet( cFileTypeByte, 3 ) && BitSet( cFileTypeByte, 7 )) return 4; else if( BitSet( cFileTypeByte, 7 )) return 3; return 0; } /*************************************************************************/ //! @brief Determine xbase dbf version. /*! This routine is used to determine which version of the Xbase classes can be used for a given DBF file.
It attempts to use the highest version compiled into the library.
References:
This routine uses the first byte from the dbf file.
Per DBase documentation:
Valid dBASE for Windows table file, bits 0-2 indicate version number: 3 for dBASE Level 5, 4 for dBASE Level 7.
Bit 3 and bit 7 indicate presence of a dBASE IV or dBASE for Windows memo file;
Bits 4-6 indicate the presence of a dBASE IV SQL table;
Bit 7 indicates the presence of any .DBT memo file (either a dBASE III PLUS type or a dBASE IV or dBASE for Windows memo file).

Bachmann spec (used extensively in library build), page 7 - does not match DBase documentation

returns
0 - unknown
3 - Dbase level 3
4 - Dbase level 4
5 - Dbase Level 5 (future)
7 - Dbase Level 7 (future)

1x - Clipper files (future)
2x - Foxbase files (future)

*/ xbInt16 xbFile::DetermineXbaseTableVersion( unsigned char cFileTypeByte ) const { xbInt16 iMemoVersion = DetermineXbaseMemoVersion(cFileTypeByte); char cDbfLevel = cFileTypeByte & 0x07; #ifdef XB_DBF4_SUPPORT if( cDbfLevel == 3 && iMemoVersion != 3 ) return 4; #endif #ifdef XB_DBF3_SUPPORT if( cDbfLevel == 3 && iMemoVersion != 4 ) return 3; #endif return 0; } /*************************************************************************/ //! @brief Get a portable double value. /*! This routine returns a double value from an 8 byte character stream, accounting for endian type. Converts a double (64 bit floating point) value stored at p from a portable format to the machine format. \param p pointer to memory containing the portable double value \returns the double value. */ xbDouble xbFile::eGetDouble( const char *p ) const { xbDouble d; const char *sp; char *tp; xbInt16 i; tp = (char *) &d; sp = p; if( iEndianType == 'L' ) for( i = 0; i < 8; i++ ) *tp++ = *sp++; else { sp+=7; for( i = 0; i < 8; i++ ) *tp++ = *sp--; } return d; } /*************************************************************************/ //! @brief Get a portable long value. /*! This routine returns a long int value from a 4 byte character stream, accounting for endian type. \param p pointer to memory containing the portable long value \returns the long value. */ xbInt32 xbFile::eGetInt32( const char *p ) const { xbInt32 l; char *tp; xbInt16 i; tp = (char *) &l; if( iEndianType == 'L' ) for( i = 0; i < 4; i++ ) *tp++ = *p++; else { p+=3; for( i = 0; i < 4; i++ ) *tp++ = *p--; } return l; } /*************************************************************************/ //! @brief Get a portable unsigned long value. /*! This routine returns an unsigned long int value from a 4 byte character stream, accounting for endian type. \param p pointer to memory containing the portable long value \returns the unsigned long value. */ xbUInt32 xbFile::eGetUInt32( const char *p ) const { xbUInt32 ul; xbInt16 i; char *tp; tp = (char *) &ul; if( iEndianType == 'L' ) for( i = 0; i < 4; i++ ) *tp++ = *p++; else{ p+=3; for( i = 0; i < 4; i++ ) *tp++ = *p--; } return ul; } /*************************************************************************/ //! @brief Get a portable short value. /*! This routine returns a short int value from a 2 byte character stream, accounting for endian type. \param p pointer to memory containing the portable long value \returns the short value. */ xbInt16 xbFile::eGetInt16(const char *p) const { xbInt16 s, i; char *tp; tp = (char *) &s; if( iEndianType == 'L' ) for( i = 0; i < 2; i++ ) *tp++ = *p++; else{ p++; for( i = 0; i < 2; i++ ) *tp++ = *p--; } return s; } /*************************************************************************/ //! @brief Get a portable unsigned short value. /*! This routine returns a short unsigned int value from a 2 byte character stream, accounting for endian type. \param p pointer to memory containing the portable long value \returns the short value. */ xbUInt16 xbFile::eGetUInt16(const char *p) const { xbInt16 i; xbUInt16 uI; char *tp; tp = (char *) &uI; if( iEndianType == 'L' ) for( i = 0; i < 2; i++ ) *tp++ = *p++; else{ p++; for( i = 0; i < 2; i++ ) *tp++ = *p--; } return uI; } /*************************************************************************/ //! @brief Write a portable double value to memory location. /*! Converts a double (64 bit floating point) value from machine format to a portable format and stores the converted value in the memory referenced by c. This routine puts a double value to an 8 byte character stream \param c Pointer to memory to hold converted value \param d Input value to be converted */ void xbFile::ePutDouble( char *c, xbDouble d ){ const char *sp; char *tp; xbInt16 i; tp = c; sp = (const char *) &d; if( iEndianType == 'L' ) for( i = 0; i < 8; i++ ) *tp++ = *sp++; else { sp+=7; for( i = 0; i < 8; i++ ) *tp++ = *sp--; } return; } /*************************************************************************/ //! @brief Write a portable short value to memory location. /*! Converts a short (16 bit integer) value from machine format to a portable format and stores the converted value in the memory referenced by c. This routine puts a short value to a 2 byte character stream \param c Pointer to memory to hold converted value \param s Input value to be converted */ void xbFile::ePutInt16( char * c, xbInt16 s ){ const char *sp; char *tp; xbInt16 i; tp = c; sp = (const char *) &s; if( iEndianType == 'L' ) for( i = 0; i < 2; i++ ) *tp++ = *sp++; else{ /* big endian */ sp++; for( i = 0; i < 2; i++ ) *tp++ = *sp--; } return; } /*************************************************************************/ //! @brief Write a portable unsigned short value to memory location. /*! Converts an unsigned short (16 bit integer) value from machine format to a portable format and stores the converted value in the memory referenced by c. This routine puts an unsigned short value to a 2 byte character stream \param c Pointer to memory to hold converted value \param s Input value to be converted */ void xbFile::ePutUInt16( char * c, xbUInt16 s ){ const char *sp; char *tp; xbInt16 i; tp = c; sp = (const char *) &s; if( iEndianType == 'L' ) for( i = 0; i < 2; i++ ) *tp++ = *sp++; else{ /* big endian */ sp++; for( i = 0; i < 2; i++ ) *tp++ = *sp--; } return; } /*************************************************************************/ //! @brief Write a portable long value to memory location. /*! Converts a long (32 bit integer) value from machine format to a portable format and stores the converted value in the memory referenced by c. This routine puts a long value to a 4 byte character stream. \param c Pointer to memory to hold converted value \param l Input value to be converted */ void xbFile::ePutInt32( char * c, xbInt32 l ) { const char *sp; char *tp; xbInt16 i; tp = c; sp = (const char *) &l; if( iEndianType == 'L' ) for( i = 0; i < 4; i++ ) *tp++ = *sp++; else { sp+=3; for( i = 0; i < 4; i++ ) *tp++ = *sp--; } return; } /*************************************************************************/ //! @brief Write a portable unsigned long value to memory location. /*! Converts an unsigned long (32 bit integer) value from machine format to a portable format and stores the converted value in the memory referenced by c. This routine puts an unsigned long value to a 4 byte character stream. \param c Pointer to memory to hold converted value \param ul Input value to be converted */ void xbFile::ePutUInt32( char * c, xbUInt32 ul ) { const char *sp; char *tp; xbInt16 i; tp = c; sp = (const char *) &ul; if( iEndianType == 'L' ) for( i = 0; i < 4; i++ ) *tp++ = *sp++; else { sp+=3; for( i = 0; i < 4; i++ ) *tp++ = *sp--; } return; } /************************************************************************/ //! @brief Determines if a file exists. /*! \returns xbTrue if file exists
xbFalse if file does not exist */ xbBool xbFile::FileExists() const { return FileExists( sFqFileName, 0 ); } /************************************************************************/ //! @brief Determines if a file exists. /*! \param iOption if 1, assume this is a request for a dbf file and check for the a dbt memo file also, returns true if both files are found \returns xbTrue if both files exist
xbFalse if file does not exist */ xbBool xbFile::FileExists( xbInt16 iOption ) const { return FileExists( sFqFileName, iOption ); } /************************************************************************/ //! @brief Determines if a file exists. /*! \param sFileName - file name to check for \returns xbTrue if file exists
xbFalse if file does not exist */ xbBool xbFile::FileExists(const xbString &sFileName ) const { return FileExists( sFileName, 0 ); } /************************************************************************/ //! @brief Determines if a file exists. /*! \param sFileName - file name to check for \param iOption if 1, assume this is a request for a dbf file and check for the a dbt memo file also, returns true if both files are found \returns xbTrue if both dbf and dbt files exist
xbFalse if file does not exist */ xbBool xbFile::FileExists( const xbString & sFileName, xbInt16 iOption ) const { struct stat buffer; if(( stat( sFileName.Str(), &buffer ) != 0 )){ return xbFalse; } #ifdef XB_MEMO_SUPPORT if( iOption == 1 ){ xbString sFileName2 = sFileName; if( sFileName2[sFileName2.Len()] == 'F' ) sFileName2.PutAt( sFileName2.Len(), 'T' ); else sFileName2.PutAt( sFileName2.Len(), 't' ); if(( stat( sFileName2.Str(), &buffer) != 0 )) return xbFalse; } #endif return xbTrue; } /************************************************************************/ //! @brief Determines if file is open. /*! \returns xbTrue if file is open
xbFalse if file is not open */ xbBool xbFile::FileIsOpen() const { return bFileOpen; } /************************************************************************/ //! @brief Get the block size. /*! \returns Block Size */ xbUInt32 xbFile::GetBlockSize() const { return ulBlockSize; } /************************************************************************/ //! @brief Get the directory name. /*! \returns the directory name of the file */ const xbString & xbFile::GetDirectory() const { return sDirectory; } /************************************************************************/ //! @brief Get the directory part of the file name. /*! \param sFileDirPartOut - the returned directory name \returns Return Codes */ xbInt16 xbFile::GetFileDirPart( xbString & sFileDirPartOut ) const { return GetFileDirPart( sFqFileName, sFileDirPartOut ); } /************************************************************************/ //! @brief Get the directory part of the file name. /*! \param sCompleteFileNameIn - a fully qualfied input file name \param sFileDirPartOut - the returned directory name part out of sCompleteFileNameIn \returns Return Codes */ xbInt16 xbFile::GetFileDirPart( const xbString & sCompleteFileNameIn, xbString & sFileDirPartOut ) const { sFileDirPartOut = sCompleteFileNameIn; sFileDirPartOut.SwapChars( '\\', '/' ); xbUInt32 iPos = sFileDirPartOut.GetLastPos( '/' ); if( iPos > 0 ){ xbString sTemp = sFileDirPartOut; sFileDirPartOut.Assign( sTemp, 1, iPos ); return XB_NO_ERROR; } return XB_INVALID_DATA; } /************************************************************************/ //! @brief Get the extension part of the file name. /*! \param sFileNameExtOut - the returned extension part out of sCompleteFileNameIn \returns Return Codes */ xbInt16 xbFile::GetFileExtPart( xbString & sFileNameExtOut ) const { return GetFileExtPart( sFqFileName, sFileNameExtOut ); } /************************************************************************/ //! @brief Get the extension part of the file name. /*! \param sCompleteFileNameIn - a fully qualfied input file name \param sFileExtPartOut - the returned directory name part out of sCompleteFileNameIn \returns Return Codes */ xbInt16 xbFile::GetFileExtPart( const xbString & sCompleteFileNameIn, xbString & sFileExtPartOut ) const { sFileExtPartOut = sCompleteFileNameIn; xbUInt32 iPos = sFileExtPartOut.GetLastPos( '.' ); if( iPos > 0 ){ /* get rid of the directory part of the name */ sFileExtPartOut.Ltrunc( iPos ); return XB_NO_ERROR; } return XB_INVALID_DATA; } /************************************************************************/ //! @brief Get the time of last file modification timestamp as reported by the OS. /*! \param mtime - returned time of last file modification \returns Return Codes */ xbInt16 xbFile::GetFileMtime( time_t &mtime ){ struct stat buffer; if( stat( sFqFileName.Str(), &buffer )) return XB_FILE_NOT_FOUND; else{ mtime = buffer.st_mtime; return XB_NO_ERROR; } } /************************************************************************/ //! @brief Get the file name. /*! \returns the file name portion of the file */ const xbString & xbFile::GetFileName() const { return sFileName; } /************************************************************************/ //! @brief Get the name part of the file name. /*! \param sFileNamePartOut - the returned file name part out of sCompleteFileNameIn \returns Return Codes */ xbInt16 xbFile::GetFileNamePart( xbString & sFileNamePartOut ) const { return GetFileNamePart( sFqFileName, sFileNamePartOut ); } /************************************************************************/ //! @brief Get the name part of the file name. /*! \param sCompleteFileNameIn - a fully qualified input file name \param sFileNamePartOut - the returned file name part out of sCompleteFileNameIn \returns Return Codes */ //*********** fixme should this be static????? xbInt16 xbFile::GetFileNamePart( const xbString & sCompleteFileNameIn, xbString & sFileNamePartOut ) const { /* extract the file name part out of the string */ sFileNamePartOut = sCompleteFileNameIn; sFileNamePartOut.SwapChars( '\\', '/' ); xbUInt32 iPos = sFileNamePartOut.GetLastPos( '/' ); if( iPos > 0 ) /* get rid of the directory part of the name */ sFileNamePartOut.Ltrunc( iPos ); iPos = sFileNamePartOut.Pos( '.' ); if( iPos > 0 ){ /* get rid of the extension part of the name */ xbString sTemp = sFileNamePartOut; sFileNamePartOut.Assign( sTemp, 1, iPos-1 ); } return XB_NO_ERROR; } /************************************************************************/ //! @brief Get the size of the file as reported by the OS. /*! \param ullFileSize - unsigned long long field as output \returns Return Codes */ xbInt16 xbFile::GetFileSize( xbUInt64 &ullFileSize ){ xbInt16 iRc = 0; xbInt16 iErrorStop = 0; try{ if(( iRc = xbFseek( 0, SEEK_END )) != XB_NO_ERROR ){ iErrorStop = 10; throw iRc; } ullFileSize = xbFtell(); } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbFile::GetFileSize() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /************************************************************************/ //! @brief Get the file type aka Capitalized file extension /*! \param sFileTypeOut - the returned extension part out of sCompleteFileNameIn \returns Return Codes */ xbInt16 xbFile::GetFileType( xbString & sFileTypeOut ) const { xbInt16 iRc = GetFileExtPart( sFqFileName, sFileTypeOut ); sFileTypeOut.ToUpperCase(); return iRc; } /************************************************************************/ //! @brief Get the fully qualified file name. /*! \returns the fully qualfied name of the file */ const xbString & xbFile::GetFqFileName() const { return sFqFileName; } /************************************************************************/ //! @brief Get the open mode of the file. /*! \returns XB_READ
XB_READ_WRITE
XB_WRITE
*/ xbInt16 xbFile::GetOpenMode() const { return iOpenMode; } /************************************************************************/ //! @brief Get the share mode of the file. /*! \returns XB_SINGLE_USER - (file buffering on>
XB_MULTI_USER - (file buffering off)
*/ xbInt16 xbFile::GetShareMode() const { return iShareMode; } /************************************************************************/ //! @brief Get the file type byte and version of the dbf file. /*! Pull the first bye off the DBF file for further inspection. First byte has various bits set to determine what the file format is. \param sFileName - Name of file to examine \param iVersion - Returned file version \returns Return Codes */ xbInt16 xbFile::GetXbaseFileTypeByte( const xbString &sFileName, xbInt16 &iVersion ) { unsigned char cFileTypeByte; return GetXbaseFileTypeByte( sFileName, cFileTypeByte, iVersion ); } /************************************************************************/ //! @brief Get the file type byte and version of the dbf file. /*! Pull the first bye off the DBF file for further inspection. First byte has various bits set to determine what the file format is. \param sFileName - Name of file to examine \param cFileTypeByte - Retruned first byte of dbf file \returns Return Codes */ xbInt16 xbFile::GetXbaseFileTypeByte( const xbString &sFileName, unsigned char &cFileTypeByte ) { xbInt16 iVersion; return GetXbaseFileTypeByte( sFileName, cFileTypeByte, iVersion ); } /************************************************************************/ //! @brief Get the file type byte and version of the dbf file. /*! Pull the first bye off the DBF file for further inspection. First byte has various bits set to determine what the file format is. \param sFileName - Name of file to examine \param cFileTypeByte - Returned first byte of dbf file \param iVersion - Returned file version \returns Return Codes */ xbInt16 xbFile::GetXbaseFileTypeByte( const xbString &sFileName, unsigned char &cFileTypeByte, xbInt16 &iVersion ){ xbInt16 iErrorStop = 0; xbInt16 iRc = XB_NO_ERROR; size_t stRc; FILE *tfp; try{ iVersion = 0; cFileTypeByte = 0x00; #ifdef HAVE__FSOPEN_F // 0x40 is SH_DENYNO or _SH_DENYNO if(( tfp = _fsopen( sFileName.Str(), "r", 0x40 )) == NULL ){ iErrorStop = 20; iRc = XB_OPEN_ERROR; throw iRc; } #else if(( tfp = fopen( sFileName.Str(), "r" )) == NULL ){ iErrorStop = 20; iRc = XB_OPEN_ERROR; throw iRc; } #endif #ifdef HAVE_FSEEKO_F iRc = fseeko( tfp, 0, SEEK_SET ); #else iRc = fseek( tfp, 0, SEEK_SET ); #endif if( iRc != 0 ){ iErrorStop = 30; iRc = XB_SEEK_ERROR; throw iRc; } stRc = fread( &cFileTypeByte, (size_t) 1, (size_t) 1, tfp ); if( stRc != (size_t) 1 ){ iErrorStop = 40; iRc = XB_READ_ERROR; throw iRc; } iRc = XB_NO_ERROR; fclose( tfp ); iVersion = DetermineXbaseTableVersion( cFileTypeByte ); } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbFile::GetXbaseFileType() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /************************************************************************/ //! @brief Determines status of file extension. /*! \param sFileName - Name of file to examine \param iOption - Inspection type
1 check for DBF
2 check for NDX
3 check for MDX
4 check for NTX
\returns 0 if suffix found
1 if suffix not found, lower case
2 is suffix not found, upper case
*/ xbInt16 xbFile::NameSuffixMissing( const xbString & sFileName, xbInt16 iOption ) const { xbUInt32 ulLen = sFileName.Len(); if( ulLen <= 4 ){ if( sFileName[ulLen] >= 'A' && sFileName[ulLen] <= 'Z' ) return 2; else return 1; } if( iOption == 1 && sFileName[ulLen-3] == '.' && ( sFileName[ulLen-2] == 'd' || sFileName[ulLen-2] == 'D' ) && ( sFileName[ulLen-1] == 'b' || sFileName[ulLen-1] == 'B' ) && ( sFileName[ulLen] == 'f' || sFileName[ulLen] == 'F' ) ) return 0; if( iOption == 2 && sFileName[ulLen-3] == '.' && ( sFileName[ulLen-2] == 'n' || sFileName[ulLen-2] == 'N' ) && ( sFileName[ulLen-1] == 'd' || sFileName[ulLen-1] == 'D' ) && ( sFileName[ulLen] == 'x' || sFileName[ulLen] == 'X' ) ) return 0; if( iOption == 3 && sFileName[ulLen-3] == '.' && ( sFileName[ulLen-2] == 'm' || sFileName[ulLen-2] == 'M' ) && ( sFileName[ulLen-1] == 'd' || sFileName[ulLen-1] == 'D' ) && ( sFileName[ulLen] == 'x' || sFileName[ulLen] == 'X' ) ) return 0; if( iOption == 4 && sFileName[ulLen-3] == '.' && ( sFileName[ulLen-2] == 'n' || sFileName[ulLen-2] == 'N' ) && ( sFileName[ulLen-1] == 't' || sFileName[ulLen-1] == 'T' ) && ( sFileName[ulLen] == 'x' || sFileName[ulLen] == 'X' ) ) return 0; // next line might be problematic if file naem has mixed case and extension is missing if( sFileName[ulLen-4] >= 'A' && sFileName[ulLen-4] <= 'Z' ) return 2; else return 1; } /***********************************************************************/ //! @brief Read a block of data from file. /*! \param ulBlockNo - block number to read \param lReadSize - size of data to read at block location, set to 0 to read blocksize \param *buf - pointer to buffer to write output data, assumed to be previosuly allocated and large enough to contain data \returns Return Codes */ xbInt16 xbFile::ReadBlock( xbUInt32 ulBlockNo, size_t lReadSize, void * buf ){ return ReadBlock( ulBlockNo, ulBlockSize, lReadSize, buf ); } /***********************************************************************/ //! @brief Read a block of data from file. /*! \param ulBlockNo - block number to read \param ulBlockSize - block size \param lReadSize - size of data to read at block location, set to 0 to read blocksize \param buf - pointer to buffer to write output data, assumed to be previosuly allocated and large enough to contain data \returns Return Codes */ xbInt16 xbFile::ReadBlock( xbUInt32 ulBlockNo, xbUInt32 ulBlockSize, size_t lReadSize, void * buf ){ xbInt16 iErrorStop = 0; xbInt16 iRc = XB_NO_ERROR; try{ if( ulBlockSize <= 0 ){ iErrorStop = 10; iRc = XB_INVALID_BLOCK_SIZE; throw iRc; } if(( iRc = xbFseek(((xbInt64) ulBlockNo*ulBlockSize ), SEEK_SET )) != XB_NO_ERROR ){ iErrorStop = 20; iRc = XB_SEEK_ERROR; throw iRc; } if( lReadSize <= 0 ) lReadSize = ulBlockSize; if(( iRc = xbFread( buf, lReadSize, 1 )) != XB_NO_ERROR ){ iErrorStop = 30; iRc = XB_READ_ERROR; throw iRc; } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbFile::ReadBlock() Exception Caught. Error Stop = [%d] iRc = [%d] BlkNo=[%ld] BlkSize=[%ld] ReadSize=[%ld]", iErrorStop, iRc, ulBlockNo, ulBlockSize, lReadSize ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /************************************************************************/ //! @brief Set the block size. /*! \param ulBlockSize - unsigned long block size, divisible by 512 \returns Return Codes */ xbInt16 xbFile::SetBlockSize( xbUInt32 ulBlockSize ){ if( ulBlockSize %512 != 0 ) return XB_INVALID_BLOCK_SIZE; this->ulBlockSize = ulBlockSize; return XB_NO_ERROR; } /************************************************************************/ //! @brief Set the directory. /*! \param sDirectory - Valid directory name */ void xbFile::SetDirectory( const xbString & sDirectory ){ this->sDirectory = sDirectory; char cLastChar = sDirectory[sDirectory.Len()]; if( cLastChar != '/' && cLastChar != '\\' ) sFqFileName.Sprintf( "%s/%s", sDirectory.Str(), sFileName.Str()); else sFqFileName.Sprintf( "%s%s", sDirectory.Str(), sFileName.Str()); #ifdef WIN32 sFqFileName.SwapChars( '/', '\\' ); #else sFqFileName.SwapChars( '\\', '/' ); #endif } /************************************************************************/ //! @brief Set the filename. /*! This routine builds out two internal variables from the input file name
sFileName - the file name part
sFqFileName - the fully qualified file name
\param sFileName - Input file name */ void xbFile::SetFileName( const xbString & sFileName ){ if( sFileName == "" ){ sFqFileName = ""; return; } char cPathSep = sFileName.GetPathSeparator(); if( cPathSep ){ xbString sName; xbString sExt; // GetFileDirPart( this->sDirectory ); GetFileNamePart( sFileName, sName ); GetFileExtPart( sFileName, sExt ); this->sFileName.Sprintf( "%s.%s", sName.Str(), sExt.Str()); sFqFileName = sFileName; } else { this->sFileName = sFileName; if( sDirectory.Len() == 0 ){ sDirectory = GetDataDirectory(); char cLastChar = sDirectory[sDirectory.Len()]; if( cLastChar != '/' && cLastChar != '\\' ) sFqFileName.Sprintf( "%s/%s", sDirectory.Str(), sFileName.Str() ); else sFqFileName = sDirectory + sFileName; } else{ char cLastChar = sDirectory[sDirectory.Len()]; if( cLastChar != '/' && cLastChar != '\\' ) sFqFileName.Sprintf( "%s/%s", sDirectory.Str(), sFileName.Str() ); else sFqFileName = sDirectory + sFileName; } } #ifdef WIN32 sFqFileName.SwapChars( '/', '\\' ); #else sFqFileName.SwapChars( '\\', '/' ); #endif } /************************************************************************/ //! @brief Set the fully qualifed filename. /*! \param sFqFileName - Fully qualifed input file name */ void xbFile::SetFqFileName( const xbString & sFqFileName ){ this->sFqFileName = sFqFileName; xbString sDir; xbString sName; xbString sExt; GetFileDirPart ( sFqFileName, sDir ); GetFileNamePart( sFqFileName, sName ); GetFileExtPart ( sFqFileName, sExt ); sDirectory = sDir; sFileName.Sprintf( "%s.%s", sName.Str(), sExt.Str() ); #ifdef WIN32 this->sDirectory.SwapChars ( '/', '\\' ); this->sFqFileName.SwapChars( '/', '\\' ); #else this->sDirectory.SwapChars ( '\\', '/' ); this->sFqFileName.SwapChars( '\\', '/' ); #endif } /************************************************************************/ //! @brief Write a block of data to file. /*! \param ulBlockNo - block number to write \param lWriteSize - size of data to write, set to 0 to write blocksize \param *buf - pointer to buffer of data to be written \returns Return Codes */ xbInt16 xbFile::WriteBlock( xbUInt32 ulBlockNo, size_t lWriteSize, void * buf ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; try{ if( ulBlockSize == 0 ){ iErrorStop = 10; iRc = XB_INVALID_BLOCK_SIZE; throw iRc; } if( lWriteSize <= 0 ) lWriteSize = ulBlockSize; if(( iRc = xbFseek(( (xbInt64) ulBlockNo*ulBlockSize), SEEK_SET )) != XB_NO_ERROR ){ iErrorStop = 20; throw iRc; } if(( iRc = xbFwrite( buf, lWriteSize, 1 )) != XB_NO_ERROR ){ iErrorStop = 30; throw iRc; } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbFile::WriteBlock() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /************************************************************************/ //! @brief Xbase wrapper for standard libary fclose. /*! \returns Return Codes */ xbInt16 xbFile::xbFclose(){ int iRc = 0; if( bFileOpen ){ iRc = fclose( fp ); if( iRc != 0 ){ return XB_CLOSE_ERROR; } else{ bFileOpen = xbFalse; } iFileNo = 0; } return XB_NO_ERROR; } /************************************************************************/ //! @brief Xbase wrapper for standard libary feof. /*! \returns non zero if end-of-file is set for the stream. */ xbInt16 xbFile::xbFeof(){ return feof( fp ); } /************************************************************************/ //! @brief Xbase wrapper for standard libary fflush. /*! \returns Return Codes */ xbInt16 xbFile::xbFflush() { if( fflush( fp ) ) return XB_WRITE_ERROR; else return XB_NO_ERROR; } /************************************************************************/ //! @brief Xbase wrapper for standard libary fgetc. /*! \param c - output integer returned by fgetc \returns Return Codes */ xbInt16 xbFile::xbFgetc( xbInt32 &c ) { int i; i = fgetc( fp ); if( i == EOF ) return XB_EOF; c = i; return XB_NO_ERROR; } /************************************************************************/ //! @brief Xbase wrapper for standard libary fgetc. /*! \param c - output character returned by fgetc \returns Return Codes */ xbInt16 xbFile::xbFgetc( char &c ) { int i; i = fgetc( fp ); if( i == EOF ) return XB_EOF; c = (char) i; return XB_NO_ERROR; } /************************************************************************/ //! @brief Xbase wrapper for standard libary fgets. /*! \param lSize - reads in at most, one character less than lSize \param s - an xbString containing data returned by fseek \returns Return Codes */ xbInt16 xbFile::xbFgets( size_t lSize, xbString &s ) { s = ""; if( feof( fp )) return XB_EOF; char *sBuf = (char *) malloc( lSize + 1 ); if( fgets( sBuf, (xbInt32) lSize, fp ) == NULL ){ free( sBuf ); return XB_EOF; } s.Set( sBuf ); free( sBuf ); return XB_NO_ERROR; } /************************************************************************/ //! @brief Xbase wrapper for standard libary fopen. /*! This routine supports all the standard C library open modes. The Xbase routines only use "r" and "r+b". \param sOpenMode
OpenModeDescription
rReading
r+Reading and Writing
wOpen for writing. Truncate to zero bytes if it exists
w+Open for reading and writing, truncate to zero bytes if it exists
aOpen for append
a+Open for reading and writing (at end).
The mode can also include the letter "b" for binary ie; "r+b". The "b" is ignored on POSIX compliant systems, but is included for cross platform compatibility. \param sFileName File name to open \param iShareMode XB_SINGLE_USER
XB_MULTI_USER
\returns Return Codes */ xbInt16 xbFile::xbFopen( const xbString &sOpenMode, const xbString &sFileName, xbInt16 iShareMode ) { if( sFileName == "" || sFqFileName == "" ) SetFileName( sFileName ); return xbFopen( sOpenMode, iShareMode ); } /************************************************************************/ //! @brief Xbase wrapper for standard libary fopen. /*! This routine supports all the standard C library open modes. The Xbase routines only use "r" and "r+". \param sOpenMode
OpenModeDescription
rReading
r+Reading and Writing
wOpen for writing. Truncate to zero bytes if it exists
w+Open for reading and writing, truncate to zero bytes if it exists
aOpen for append
a+Open for reading and writing (at end).
The mode can also include the letter "b" for binary ie; "r+b". The "b" is ignored on POSIX compliant systems, but is included for cross platform compatibility. \param iShareMode XB_SINGLE_USER
XB_MULTI_USER
\returns Return Codes */ xbInt16 xbFile::xbFopen( const xbString & sOpenMode, xbInt16 iShareMode ) { #ifdef HAVE__FSOPEN_F if(( fp = _fsopen( sFqFileName.Str(), sOpenMode.Str(), 0x40 )) != NULL ){ #else if(( fp = fopen( sFqFileName.Str(), sOpenMode.Str())) != NULL ){ #endif if( sOpenMode == "r" ) iOpenMode = XB_READ; else if( sOpenMode == "w" ) iOpenMode = XB_WRITE; else iOpenMode = XB_READ_WRITE; bFileOpen = xbTrue; this->iShareMode = iShareMode; #ifdef HAVE__FILENO_F iFileNo = _fileno( fp ); #else iFileNo = fileno( fp ); #endif #ifdef HAVE_SETENDOFFILE_F //used by visual studio, 32 bit fHandle = (HANDLE) _get_osfhandle( iFileNo ); #endif #ifdef XB_LOCKING_SUPPORT if( iShareMode ) xbFTurnOffFileBuffering(); #endif return XB_NO_ERROR; } else return XB_OPEN_ERROR; } /************************************************************************/ //! @brief Xbase wrapper for standard libary fopen. /*! \param iOpenMode XB_READ
XB_READ_WRITE
\param iShareMode XB_SINGLE_USER
XB_MULTI_USER
\returns Return Codes */ xbInt16 xbFile::xbFopen( xbInt16 iOpenMode, xbInt16 iShareMode ) { this->iOpenMode = iOpenMode; if( iOpenMode == XB_READ_WRITE ) return xbFopen( "r+b", iShareMode ); else if( iOpenMode == XB_READ ) return xbFopen( "r", iShareMode ); else return XB_INVALID_OPTION; } /************************************************************************/ //! @brief Xbase wrapper for standard libary fputc. /*! \param c Character to write \returns Return Codes */ xbInt16 xbFile::xbFputc( xbInt32 c ) { return xbFputc( c, 1 ); } /************************************************************************/ //! @brief Xbase wrapper for standard libary fputc. /*! \param c Character to write \param iNoOfTimes Number of times to write the character \returns Return Codes */ xbInt16 xbFile::xbFputc( xbInt32 c, xbInt32 iNoOfTimes ) { for( xbInt32 l = 0; l < iNoOfTimes; l++ ) if( fputc( c, fp ) != (int) c ) return XB_WRITE_ERROR; return XB_NO_ERROR; } /************************************************************************/ //! @brief Xbase wrapper for standard libary fputs. /*! \param s xbString to write to file \returns Return Codes */ xbInt16 xbFile::xbFputs( const xbString & s ){ if( fputs( s.Str(), fp ) < 0 ) return XB_WRITE_ERROR; else return XB_NO_ERROR; } /************************************************************************/ //! @brief Xbase wrapper for standard libary fread. /*! \param p Pointer to data to write \param size size of write \param nmemb Number of times to write it \returns Return Codes */ xbInt16 xbFile::xbFread( void *p, size_t size, size_t nmemb ) { size_t iRc; iRc = fread( p, size, nmemb, fp ); if( iRc == nmemb ) return XB_NO_ERROR; else return XB_READ_ERROR; } /************************************************************************/ //! @brief Xbase wrapper for standard libary fseek. /*! \param lOffset Position in file to seek to \param iWhence SEEK_SET - from beginning of file
SEEK_CUR - from current position
SEEK_END - from end of file
\returns Return Codes */ xbInt16 xbFile::xbFseek( xbInt64 lOffset, xbInt32 iWhence ) { xbInt32 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; try { #if defined(HAVE_FSEEKO_F) iRc = fseeko( fp, lOffset, iWhence ); if( iRc != 0 ){ iErrorStop = 100; throw iRc; } #elif defined(HAVE__FSEEKI64_F) iRc = _fseeki64( fp, lOffset, iWhence ); if( iRc != 0 ){ iErrorStop = 200; throw iRc; } #else #ifdef XB_PLATFORM_32 /* if request is larger than 2 gig,this is a part of a locking request, assuming offset is less than 4 gig, split the request into 2 fseek calls */ if( lOffset > 2147483647 && iWhence == SEEK_SET ){ /* move forward max amt - 2G */ if(( iRc = fseek( fp, 2147483647, SEEK_SET )) != 0 ){ iErrorStop = 300; throw iRc; } lOffset -= 2147483647; iWhence = SEEK_CUR; } #endif iRc = fseek( fp, (long) lOffset, iWhence ); if( iRc != 0 ){ iErrorStop = 310; throw iRc; } #endif } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbFile::xbFseek() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); iRc = XB_SEEK_ERROR; } return iRc; } /************************************************************************/ //! @brief Xbase wrapper for standard libary ftell. /*! Returns the current file position. \returns Current file position. */ size_t xbFile::xbFtell() { return (size_t) ftell( fp ); } /************************************************************************/ //! @brief Turn off file buffering. /*! Turns off file buffering. File buffering can't be used while in multi user mode. */ void xbFile::xbFTurnOffFileBuffering() { setvbuf( fp, NULL, _IONBF, 0 ); } /************************************************************************/ //! @brief Xbase wrapper for standard libary fwrite. /*! \param p Pointer to data buffer to write \param size Size of data to write \param nmemb Number of times to write data buffer \returns Return Codes */ xbInt16 xbFile::xbFwrite( const void *p, size_t size, size_t nmemb ) { size_t iRc; iRc = fwrite( p, size, nmemb, fp ); if( iRc == nmemb ) return XB_NO_ERROR; else return XB_READ_ERROR; } /************************************************************************/ //! @brief Read file until a particular character is encountered on input stream. /*! This routine will read until cDelim is encountered or eof, which ever occurs first. \param cDelim Delimiter to stop writing at. \param sOut Output xbString containing data read \returns Return Codes */ xbInt16 xbFile::xbReadUntil( const char cDelim, xbString &sOut ){ xbInt16 iRc = XB_NO_ERROR; xbInt16 iErrorStop = 0; char c; try{ sOut = ""; if(( iRc = xbFgetc( c )) != XB_NO_ERROR ){ iErrorStop = 10; throw iRc; } sOut = c; while( iRc == XB_NO_ERROR && c != cDelim ){ if(( iRc = xbFgetc( c )) != XB_NO_ERROR ){ iErrorStop = 10; throw iRc; } sOut += c; } } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbFile::xbReadUntil() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /************************************************************************/ //! @brief Delete file. /*! \returns Return Codes */ xbInt16 xbFile::xbRemove() { return xbRemove( sFqFileName.Str(), 0 ); } /************************************************************************/ //! @brief Delete file. /*! \param sFileNameIn Name of file to delete \returns Return Codes */ xbInt16 xbFile::xbRemove( const xbString & sFileNameIn ) { return xbRemove( sFileNameIn, 0 ); } /************************************************************************/ //! @brief Delete file. /*! \param sFileNameIn Name of file to delete \param iOption If Set to 1, assume this is a delete request for a dbf file, and should rename the dbt file also \returns Return Codes */ xbInt16 xbFile::xbRemove( const xbString & sFileNameIn, xbInt16 iOption ) { xbInt32 iRc = remove( sFileNameIn.Str()); if( iRc != 0 ) return XB_DELETE_FAILED; if( iOption == 1 ){ xbString sFileName2 = sFileNameIn; if( sFileName2[sFileName2.Len()] == 'F' ) sFileName2.PutAt( sFileName2.Len(), 'T' ); else sFileName2.PutAt( sFileName2.Len(), 't' ); iRc = remove( sFileName2.Str()); if( iRc != 0 ) return XB_DELETE_FAILED; } return XB_NO_ERROR; } /************************************************************************/ //! @brief Rename file. /*! \param sOldName Original file name \param sNewName New file name \returns Return Codes */ xbInt16 xbFile::xbRename( const xbString & sOldName, const xbString & sNewName ){ return xbRename( sOldName, sNewName, 0 ); } /************************************************************************/ //! @brief Rename file. /*! \param sOldName Original file name \param sNewName New file name \param iOption If Set to 1, assume this is a rename request for a dbf file, and should rename the dbt file also \returns Return Codes */ xbInt16 xbFile::xbRename( const xbString & sOldName, const xbString & sNewName, xbInt16 iOption ){ if( rename( sOldName.Str(), sNewName.Str())){ return XB_RENAME_ERROR; } if( iOption == 1 ){ xbString sOldName2 = sOldName; xbString sNewName2 = sNewName; if( sOldName2[sOldName2.Len()] == 'F' ) sOldName2.PutAt( sOldName2.Len(), 'T' ); else sOldName2.PutAt( sOldName2.Len(), 't' ); if( sNewName2[sNewName2.Len()] == 'F' ) sNewName2.PutAt( sNewName2.Len(), 'T' ); else sNewName2.PutAt( sNewName2.Len(), 't' ); if( rename( sOldName2.Str(), sNewName2.Str())) return XB_RENAME_ERROR; } return XB_NO_ERROR; } /************************************************************************/ //! @brief Xbase wrapper for rewind. /*! Set file pointer at beginning of file. */ void xbFile::xbRewind() { rewind( fp ); } /************************************************************************/ //! @brief Xbase wrapper for ftruncate. /*! Set file size to llSize \param llSize New file size. \returns Return Codes */ xbInt16 xbFile::xbTruncate( xbInt64 llSize ) { xbInt16 iRc = 0; xbInt16 iErrorStop = 0; try{ #ifdef HAVE_FTRUNCATE_F if(( iRc = ftruncate( iFileNo, llSize )) != 0 ){ iErrorStop = 10; iRc = XB_WRITE_ERROR; throw iRc; } #elif defined(HAVE_SETENDOFFILE_F) if(( iRc = xbFseek( llSize, SEEK_SET )) != XB_NO_ERROR ){ iErrorStop = 20; throw iRc; } if(( iRc = SetEndOfFile( fHandle )) == 0 ){ iErrorStop = 30; iRc = XB_WRITE_ERROR; throw iRc; } else { iRc = XB_NO_ERROR; } #else // check that cmake can find function SetEndOfFile - // cmake could not find for Borland 5.5 FATAL_COMPILE_ERROR CANT_LOCATE_FUNCTION_ftruncate_or_SetEndOfFile #endif } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbFile::xbTruncate() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } /***********************************************************************/ #ifdef XB_LOCKING_SUPPORT //! @brief Lock / unlock file. /*! \param iFunction XB_LOCK
XB_UNLOCK
\param lOffset Position in file to lock \param stLen Length to lock \returns Return Codes */ xbInt16 xbFile::xbLock( xbInt16 iFunction, xbInt64 lOffset, size_t stLen ){ xbInt16 iRc = 0; xbInt16 iErrorStop = 0; xbInt16 iTries = 0; try{ #ifdef HAVE_FCNTL_F /* Unix lock function */ if(( iRc = xbFseek( lOffset, SEEK_SET )) != XB_NO_ERROR ){ iErrorStop = 10; throw iRc; } struct flock fl; switch( iFunction ){ case( XB_LOCK ): fl.l_type = F_WRLCK; break; case( XB_UNLOCK ): fl.l_type = F_UNLCK; break; default: iErrorStop = 20; iRc = XB_INVALID_LOCK_OPTION; throw iRc; break; } fl.l_whence = SEEK_CUR; fl.l_start = 0; fl.l_len = (xbInt32) stLen; do{ iRc = fcntl( iFileNo, F_SETLK, &fl ); if( iRc && (errno == EACCES || errno == EAGAIN )){ iTries++; xbase->xbSleep( GetDefaultLockWait() ); } else if( iRc ){ iErrorStop = 30; iRc = XB_LOCK_FAILED; throw iRc; } } while( iRc && iTries < GetLockRetryCount()); if( iRc ) iRc = XB_LOCK_FAILED; // lock failed, don't log an exception #elif defined(HAVE_LOCKFILE_F) /* Windows 64 byte lock functions */ /* split a quad word into two double words */ typedef union{ size_t Qword; xbUInt32 Dword[2]; } Qsplit; Qsplit lPos; Qsplit lLen; lPos.Qword = lOffset; lLen.Qword = stLen; do{ if( iFunction == XB_LOCK ){ if(( iRc = LockFile( fHandle, lPos.Dword[0], lPos.Dword[1], lLen.Dword[0], lLen.Dword[1] )) == 0 ){ iTries++; xbase->xbSleep( GetDefaultLockWait() ); } } else if( iFunction == XB_UNLOCK ){ if(( iRc = UnlockFile( fHandle, lPos.Dword[0], lPos.Dword[1], lLen.Dword[0], lLen.Dword[1] )) == 0 ){ iTries++; xbase->xbSleep( GetDefaultLockWait() ); } } else { iErrorStop = 30; iRc = XB_INVALID_LOCK_OPTION; throw iRc; } } while( iRc == 0 && iTries < GetLockRetryCount()); if( iRc == 0 ) iRc = XB_LOCK_FAILED; // lock failed, don't log an exception else iRc = XB_NO_ERROR; #elif defined(HAVE_LOCKING_F) || defined(HAVE__LOCKING_F) /* older 32 bit locking functions */ xbInt32 iLockType; if( iFunction == XB_LOCK ){ iLockType = 2; } else if( iFunction == XB_UNLOCK ){ iLockType = 0; } else { iErrorStop = 50; iRc = XB_INVALID_LOCK_OPTION; throw iRc; } if(( iRc = xbFseek( lOffset, SEEK_SET )) != XB_NO_ERROR ){ iErrorStop = 60; iRc = XB_SEEK_ERROR; throw iRc; } do{ #ifdef HAVE__LOCKING_F if(( iRc = _locking( iFileNo, iLockType, stLen )) != 0 ){ #else if(( iRc = locking( iFileNo, iLockType, stLen )) != 0 ){ #endif iTries++; xbase->xbSleep( GetDefaultLockWait() ); } } while( iRc != 0 && iTries < GetLockRetryCount()); if( iRc != 0 ) iRc = XB_LOCK_FAILED; // lock failed, don't log an exception else iRc = XB_NO_ERROR; #else FATAL ERROR - CANT BUILD LIBRARY IN CURRENT CONFIG - MISSING - no file locking function defined in xbfile.cpp #endif } catch (xbInt16 iRc ){ xbString sMsg; sMsg.Sprintf( "xbFile::xbLock() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); } return iRc; } //! @brief Return the locking retry setting. /*! \returns The lock retry setting for this file or ths system default setting if the lock retry for the file has not been set. */ xbInt16 xbFile::GetLockRetryCount() const { if( iLockRetries == -1 ) return xbase->GetDefaultLockRetries(); else return iLockRetries; } //! @brief Set the lock retry countr for this specific file. /*! \param iLockRetries The number of retries to attempt before returning failure for this file */ void xbFile::SetLockRetryCount( xbInt16 iLockRetries ) { this->iLockRetries = iLockRetries; } #endif /***********************************************************************/ #ifdef XB_DEBUG_SUPPORT //! @brief Debugging routine - dump a block to the log file. /*! This routine dumps a block to a file in the log file. This is primarily used for debugging and analysis purposes. \param ulBlockNo Block number to write \param lBlxkSize Size of block \returns Return Codes */ xbInt16 xbFile::DumpBlockToDisk( xbUInt32 ulBlockNo, size_t lBlkSize ){ xbInt16 iRc = 0; xbInt16 iErrorStop = 0; xbUInt32 ulStartBlock; xbUInt32 ulEndBlock; char *p = 0x00; xbString sDir; xbString sFn; char *buf = NULL; FILE *fpd = NULL; try{ iErrorStop = 10; if( ulBlockNo == 0 ){ ulStartBlock = 0; xbUInt64 ullFileSizeulBlockNo; if(( iRc = GetFileSize( ullFileSizeulBlockNo )) != XB_NO_ERROR ){ iErrorStop = 20; throw iRc; } ulEndBlock = (xbUInt32) (ullFileSizeulBlockNo / lBlkSize); } else { ulStartBlock = ulBlockNo; ulEndBlock = ulBlockNo; } if(( buf = (char *) malloc( lBlkSize )) == NULL ){ iErrorStop = 30; iRc = XB_NO_MEMORY; throw iRc; } sDir = GetDefaultLogDirectory(); char cLastChar = sDir[sDir.Len()]; for( xbUInt32 l = ulStartBlock; l < ulEndBlock; l++ ){ if(( iRc = ReadBlock( l, lBlkSize, buf )) != XB_NO_ERROR ){ iErrorStop = 40; throw iRc; } // build logfile name if( cLastChar != '/' && cLastChar != '\\' ) sFn.Sprintf( "%s/Blockdump.B%ld", sDir.Str(), l); else sFn.Sprintf( "%sBlockDump.%ld", sDir.Str(), l); // open the dump file for append #ifdef HAVE__FSOPEN_F if(( fpd = _fsopen( sFn.Str(), "w+b", 0x40 )) == NULL ){ #else if(( fpd = fopen( sFn.Str(), "w+b")) == NULL ){ #endif iErrorStop = 50; iRc = XB_OPEN_ERROR; throw iRc; } // dump the block to the file p = buf; for( size_t l = 0; l < lBlkSize; l++ ){ //if( fputc( *p, fpd ) != *p ){ if( fputc( *p, fpd ) == EOF ){ iErrorStop = 60; iRc = XB_WRITE_ERROR; throw iRc; } p++; } // close the dump file fclose( fpd ); } // free the buffer if( buf ) free( buf ); } catch (xbInt16 iRc ){ xbString sMsg; if( iErrorStop == 60 ) sMsg.Sprintf( "xbFile::DumpBlockToDisk() Exception Caught. Error Stop = [%d] iRc = [%d] c=[%c][%x]", iErrorStop, iRc, *p, *p ); else sMsg.Sprintf( "xbFile::DumpBlockToDisk() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc ); xbase->WriteLogMessage( sMsg.Str() ); xbase->WriteLogMessage( GetErrorMessage( iRc )); if( buf ) free( buf ); if( fpd ) fclose( fpd ); } return iRc; } #endif /***********************************************************************/ } /* namespace xb */