diff options
author | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2014-08-02 08:43:31 +0200 |
---|---|---|
committer | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2014-08-02 08:43:31 +0200 |
commit | daf17154bf13139d9375f48525d19d6aaba08155 (patch) | |
tree | e3c08b6c49dc8a8e83f03327591310546675b43d /xbase64/xbdbf.cpp |
Imported Upstream version 3.1.2upstream/3.1.2
Diffstat (limited to 'xbase64/xbdbf.cpp')
-rwxr-xr-x | xbase64/xbdbf.cpp | 2671 |
1 files changed, 2671 insertions, 0 deletions
diff --git a/xbase64/xbdbf.cpp b/xbase64/xbdbf.cpp new file mode 100755 index 0000000..d3790cb --- /dev/null +++ b/xbase64/xbdbf.cpp @@ -0,0 +1,2671 @@ +/* xbdbf.cpp + + Xbase64 project source code + + This file contains the basic Xbase routines for reading and writing + Xbase .DBF files. + + Copyright (C) 1997,2003 Gary A Kunkel + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + Contact: + + Email: + + xdb-devel@lists.sourceforge.net + xdb-users@lists.sourceforge.net + + + Regular Mail: + + XBase Support + 149C South Main St + Keller Texas, 76248 + USA + +*/ + +#ifdef __GNU LesserG__ + #pragma implementation "xbdbf.h" +#endif + +#ifdef __WIN32__ +#include <xbase64/xbwincfg.h> +#else +#include <xbase64/xbconfig.h> +#endif + +#include <xbase64/xbase64.h> + +#ifdef HAVE_IO_H +#include <io.h> +#endif +#include <errno.h> + +/*! \file xbdbf.cpp +*/ + +/************************************************************************/ +//! Constructor +/*! + \param x pointer to the global xbXbase class +*/ +xbDbf::xbDbf( xbXBase * x ) +{ + xbase = x; + InitVars(); +} +/************************************************************************/ +//! Destructor +/*! +*/ +xbDbf::~xbDbf() +{ + CloseDatabase(true); +} +/************************************************************************/ +//! Initialize private data members. +/*! + Internal use only. +*/ +void xbDbf::InitVars() +{ + SetFileName(NULL); + NoOfFields = 0; + DbfStatus = XB_CLOSED; + fp = NULL; + CurRec = 0L; + SchemaPtr = NULL; + RecBuf = NULL; + RecBuf2 = NULL; + Version = 0x00; + UpdateYY = 0x00; + UpdateMM = 0x00; + UpdateDD = 0x00; + NoOfRecs = 0L; + HeaderLen = 0x00; + RecordLen = 0x00; + NdxList = NULL; + FreeIxList = NULL; + XFV = 3; /* Xbase file version */ + +#ifdef XB_LOCKING_ON + xblfh = NULL; /* lock file for XB_XBASE_LOCK_MODE */ + LockMode = xbase->GetLockMode(); + TableLockCnt = 0; + IndexLockCnt = 0; +#ifdef XB_MEMO_FIELDS + MemoLockCnt = 0; +#endif + + AutoLock = 1; + CurLockType = -1; + CurLockCount = 0; + CurLockedRecNo = 0L; + CurRecLockType = -1; + CurRecLockCount = 0; + CurMemoLockType = -1; + CurMemoLockCount = 0; +#else + AutoLock = 0; +#endif + +#ifdef XB_MEMO_FIELDS + MemofileName = ""; + MemoHeader.BlockSize = XB_DBT_BLOCK_SIZE; + MemoHeader.Version = 0x03; + mfp = NULL; + mbb = NULL; + CurMemoBlockNo = -1; + mfield1 = 0; + MStartPos = 0; + MFieldLen = 0; + NextFreeBlock = 0L; + FreeBlockCnt = 0L; + MNextBlockNo = 0L; + MNoOfFreeBlocks = 0L; +#endif + +//#ifdef XB_REAL_DELETE + RealDelete = 0; + FirstFreeRec = 0L; + RealNumRecs = 0L; +//#endif +} +/************************************************************************/ +//! Set dbase version for the dbf file. +/*! + Set dbase version. Should only be used before creating a database with + xbDbf::CreateDatabase(). + + \param v version, either 3 or 4. +*/ +xbShort xbDbf::SetVersion(xbShort v) { + if (v == 0) + return XFV; + else + if(v == 3) { + XFV = 3; +#ifdef XB_MEMO_FIELDS + MemoHeader.Version = 0x03; +#endif + return XFV; + } else + if (v == 4) { + XFV = 4; +#ifdef XB_MEMO_FIELDS + MemoHeader.Version = 0x00; +#endif + return XFV; + } + + return XB_INVALID_OPTION; +} +/************************************************************************/ +//! Write the dbf header +/*! + Internal use only. + + \param PositionOption flag that indicates whether file postition should + be moved. non-zero if so, zero if not. +*/ +xbShort xbDbf::WriteHeader( xbShort PositionOption ) +{ + char buf[32]; + memset(buf, 0, 32); + if(PositionOption) + rewind(fp); + + memcpy(&buf[0], &Version, 4); + xbase->PutLong(&buf[4], NoOfRecs); + xbase->PutShort(&buf[8], HeaderLen ); + xbase->PutShort(&buf[10], RecordLen ); + +#ifdef XB_REAL_DELETE + if(RealDelete){ + xbase->PutULong(&buf[12], FirstFreeRec); + xbase->PutULong(&buf[16], RealNumRecs); + } +#endif + if(fwrite(buf, 32, 1, fp) != 1) + return XB_WRITE_ERROR; + + return XB_NO_ERROR; +} +/************************************************************************/ +//! Read the dbf header. +/*! + Internal use only. + + \param PositionOption +*/ +xbShort xbDbf::ReadHeader( xbShort PositionOption ) +{ +#if 0 + char buf[4]; + if (PositionOption) + rewind(fp); + if (fread(&Version, 4, 1, fp) != 1) + xb_error(XB_READ_ERROR); + + if (fread(buf, 4, 1, fp ) != 1) + xb_error(XB_READ_ERROR); + + NoOfRecs = xbase->GetLong( buf ); + if(fread(buf, 2, 1, fp) != 1) + xb_error(XB_READ_ERROR); + + HeaderLen = xbase->GetShort( buf ); + if(fread(buf, 2, 1, fp) != 1) + xb_error(XB_READ_ERROR); + + RecordLen = xbase->GetShort(buf); + +#ifdef XB_REAL_DELETE + if(RealDelete) + { + if (fread(buf, 4, 1, fp ) != 1) + xb_error(XB_READ_ERROR); + FirstFreeRec = xbase->GetULong( buf ); + + if (fread(buf, 4, 1, fp ) != 1) + xb_error(XB_READ_ERROR); + RealNumRecs = xbase->GetULong( buf ); + } +#endif +#else + char buf[32]; + + if(PositionOption) + rewind(fp); + + if(fread(buf, 32, 1, fp) != 1) + return XB_READ_ERROR; + + memcpy(&Version, buf, 4); + NoOfRecs = xbase->GetLong(&buf[4]); + HeaderLen = xbase->GetShort(&buf[8]); + RecordLen = xbase->GetShort(&buf[10]); + +#ifdef XB_REAL_DELETE + if(RealDelete) + { + FirstFreeRec = xbase->GetULong(&buf[12]); + RealNumRecs = xbase->GetULong(&buf[16]); + } +#endif +#endif + + return XB_NO_ERROR; +} +/************************************************************************/ +//! Determine if file name suffix is missing +/*! + Internal use only. +*/ +xbShort xbDbf::NameSuffixMissing( xbShort type, const char * name ) +{ + /* type 1 is DBF check + type 2 is NDX check + type 3 is MDX check + type 4 is NTX check + + Returns 0 if suffix found + 1 if suffix not found, lower case + 2 is suffix not found, upper, case +*/ + + xbShort len; + + len = strlen( name ); + if( len <= 4 ) + if( name[len-1] >= 'A' && name[len-1] <= 'Z' ) + return 2; + else + return 1; + + if( type == 1 && name[len-4] == '.' && + ( name[len-3] == 'd' || name[len-3] == 'D' ) && + ( name[len-2] == 'b' || name[len-2] == 'B' ) && + ( name[len-1] == 'f' || name[len-1] == 'F' ) + ) + return 0; + + if( type == 2 && name[len-4] == '.' && + ( name[len-3] == 'n' || name[len-3] == 'N' ) && + ( name[len-2] == 'd' || name[len-2] == 'D' ) && + ( name[len-1] == 'x' || name[len-1] == 'X' ) + ) + return 0; + + if( type == 4 && name[len-4] == '.' && + ( name[len-3] == 'n' || name[len-3] == 'N' ) && + ( name[len-2] == 't' || name[len-2] == 'T' ) && + ( name[len-1] == 'x' || name[len-1] == 'X' ) + ) + return 0; + + if( name[len-5] >= 'A' && name[len-5] <= 'Z' ) + return 2; + else + return 1; +} +/************************************************************************/ +//! Create the dbf file. +/*! + This method attempts to create the DBF file with the specified + name (TableName) and schema (xbSchema s). The OverLay switch is used to determine + if an existing file should be overwritten or an error flagged if the + file already exists. The record buffer is blanked (set to spaces). + + \param TableName name of the table + \param s xbSchema + \param Overlay One of the following: + \htmlonly + <p> + <table border=2><tr><th>OverLay</th><th>Description</th></tr> + <tr><td>XB_OVERLAY</td><td>Overwrite existing file if it exists</td></tr> + <tr><td>XB_DONTOVERLAY</td><td>Report an error if file exists</td></tr> + </table> + \endhtmlonly + \latexonly + \\ + \\ + \begin{tabular}{|l|l|} \hline + \textbf{OverLay} & \textbf{Description} \\ \hline \hline + XB\_OVERLAY & Overwrite existing file if it exists \\ \hline + XB\_DONTOVERLAY & Report an error if file exists \\ \hline + \end{tabular} + \endlatexonly + \returns One of the following return codes: + \htmlonly + <p> + <table border=2><tr><th>Return Code</th><th>Description</th></tr> + <tr><td>XB_NO_ERROR</td><td>No error</td></tr> + <tr><td>XB_FILE_EXISTS</td><td>If the file exists and OverLay is XB_DONTOVERLAY</td></tr> + <tr><td>XB_OPEN_ERROR</td><td>Couldn't open the file</td></tr> <tr><td>XB_NO_MEMORY</td><td>Memory allocation error</td></tr> + <tr><td>XB_WRITE_ERROR</td><td>Couldn't write to disk</td><tr> + </table> + \endhtmlonly + \latexonly + \\ + \\ + \begin{tabular}{|l|l|} \hline + \textbf{Return Code} & \textbf{Description} \\ \hline \hline + XB\_NO\_ERROR & No Error \\ \hline + XB\_FILE\_EXISTS & If the file exists and OverLay is XB\_DONTOVERAY \\ \hline + XB\_OPEN\_ERROR & Couldn't open the file \\ \hline + XB\_WRITE\_ERROR & Couldn't write to disk \\ \hline + \end{tabular} + \endlatexonly +*/ +xbShort xbDbf::CreateDatabase( const char * TableName, xbSchema * s, + const xbShort Overlay ) +{ + xbShort i, j, k, k2, rc; /* , count; */ + +#ifdef XB_MEMO_FIELDS + xbShort MemoSw = 0; +#endif + + DbfStatus = XB_CLOSED; + SetFileName( TableName ); + + /* check if the file already exists */ + if((( fp = fopen( GetFileName(), "r" )) != NULL ) && !Overlay ){ + fclose( fp ); + return XB_FILE_EXISTS; + } + else if( fp ) fclose( fp ); + + if(( fp = fopen( GetFileName(), "w+b" )) == NULL ) + return XB_OPEN_ERROR; + +#ifdef XB_LOCKING_ON + /* no buffering in multi user mode */ + setbuf( fp, NULL ); + + /* open the lock file if XB_XBASE_LOCK_FLAVOR */ +// if( LockMode == XB_XBASE_LOCK_MODE ) +// if(( rc = OpenXbLockFile()) != XB_NO_ERROR ) +// return rc; +#endif + + /* count the number of fields and check paramaters */ + i = 0; + while( s[i].Type != 0 ){ + NoOfFields++; + +#ifdef XB_MEMO_FIELDS + if(s[i].Type == 'M'){ + s[i].FieldLen = 10; + s[i].NoOfDecs = 0; + } +#endif /* XB_MEMO_FIELDS */ + + if(s[i].Type == 'D'){ + s[i].FieldLen = 8; + s[i].NoOfDecs = 0; + } + + if(s[i].Type == 'C') + s[i].NoOfDecs = 0; + + RecordLen += s[i].FieldLen; + + if( s[i].Type != 'C' && + s[i].Type != 'N' && + s[i].Type != 'F' && + s[i].Type != 'D' && +#ifdef XB_MEMO_FIELDS + s[i].Type != 'M' && +#endif /* XB_MEMO_FIELDS */ + s[i].Type != 'L' ) + { + fclose( fp ); + InitVars(); + return XB_UNKNOWN_FIELD_TYPE; + } + +#ifdef XB_MEMO_FIELDS +// 8/18/03 types B and O dont exist yet - gkunkel +// if( !MemoSw && ( s[i].Type=='M' || s[i].Type=='B' || s[i].Type=='O')) + if( !MemoSw && ( s[i].Type=='M' )) + MemoSw++; +#endif + +// check for numeric fields which are too long + if((s[i].Type == 'N' || s[i].Type == 'F') && s[i].FieldLen > 19 ){ + fclose( fp ); + InitVars(); + return XB_INVALID_FIELD_LEN; + } + i++; + } + RecordLen++; /* add one byte for 0x0D */ + + if(( RecBuf = (char *) malloc( RecordLen )) == NULL ){ + fclose( fp ); + InitVars(); + return XB_NO_MEMORY; + } + + if(( RecBuf2 = (char *) malloc( RecordLen )) == NULL ){ + free( RecBuf ); + fclose( fp ); + InitVars(); + return XB_NO_MEMORY; + } + + /* BlankRecord(); */ + memset( RecBuf, 0x20, RecordLen ); + memset( RecBuf2, 0x20, RecordLen ); + + /* set class variables */ + Version = XFV & 0x7; // file version - bit 0-2 +#ifdef XB_MEMO_FIELDS + if(MemoSw){ + if((XFV & 0x7) == 3) + Version |= 0x80; // memo presence - bit 7 + else + Version = (char) 0x8b; + } +#endif + + CurRec = 0L; + HeaderLen = 33 + NoOfFields * 32; + xbDate d; + UpdateYY = (d.YearOf() - 1900); + if((XFV & 0x7) == 3) + UpdateYY %= 100; // dBASE III seems to do this, but IV does not. DTB + + UpdateMM = d.MonthOf(); + UpdateDD = d.DayOf( XB_FMT_MONTH ); + + /* write the header prolog */ + if(( rc = WriteHeader( 0 )) != XB_NO_ERROR ){ + free( RecBuf ); + free( RecBuf2 ); + fclose( fp ); + InitVars(); + return XB_WRITE_ERROR; + } + + if((SchemaPtr=(xbSchemaRec *)malloc(NoOfFields*sizeof(xbSchemaRec)))==NULL){ + free( RecBuf ); + free( RecBuf2 ); + fclose( fp ); + InitVars(); + return XB_NO_MEMORY; + } + memset( SchemaPtr, 0x00, ( NoOfFields * sizeof(xbSchemaRec))); + + /* write the field information into the header */ + for( i = 0, k = 1; i < NoOfFields; i++ ){ + memset( SchemaPtr[i].FieldName, 0x00, 11 ); + strncpy( SchemaPtr[i].FieldName, s[i].FieldName, 10 ); + + SchemaPtr[i].Type = s[i].Type; + SchemaPtr[i].FieldLen = s[i].FieldLen; + SchemaPtr[i].NoOfDecs = s[i].NoOfDecs; + + if( SchemaPtr[i].NoOfDecs > SchemaPtr[i].FieldLen ) { + fclose( fp ); + free( SchemaPtr ); + free( RecBuf ); + free( RecBuf2 ); + InitVars(); + return XB_INVALID_SCHEMA; + } + + k2 = k; + k += SchemaPtr[i].FieldLen; + + if(( fwrite( &SchemaPtr[i], 1, 18, fp )) != 18 ) { + fclose( fp ); + free( SchemaPtr ); + free( RecBuf ); + free( RecBuf2 ); + InitVars(); + return XB_WRITE_ERROR; + } + + for( j = 0; j < 14; j++ ) { + if(( fwrite( "\x00", 1, 1, fp )) != 1 ) { + free( SchemaPtr ); + free( RecBuf ); + free( RecBuf2 ); + fclose( fp ); + InitVars(); + return XB_WRITE_ERROR; + } + } + SchemaPtr[i].Address = RecBuf + k2; + SchemaPtr[i].Address2 = RecBuf2 + k2; + } + + /* write the header terminator */ + if(( fputc( XB_CHARHDR, fp )) != XB_CHARHDR ){ + fclose( fp ); + free( SchemaPtr ); + free( RecBuf ); + free( RecBuf2 ); + InitVars(); + return XB_WRITE_ERROR; + } + +#ifdef XB_MEMO_FIELDS + if( MemoSw ) + if((rc = CreateMemoFile()) != XB_NO_ERROR){ + fclose(fp); + free(RecBuf); + free(RecBuf2); + InitVars(); + return rc; + } +#endif + + DbfStatus = XB_OPEN; + return xbase->AddDbfToDbfList(this, GetFileName()); +} +/************************************************************************/ +//! Close the dbf file. +/*! + This method attempts to close the DBF file which was previously + opened with either CreateDatabase() or OpenDatabase(). Deletes any + memory allocated. Automatically closes any open indexes associated + with this data file. + + \param deleteIndexes if TRUE, the indexes (xbIndex instances) will also + be deleted (index files will not be deleted) + \returns One of the following: + \htmlonly + <p> + <table border=2><tr><th>Return Code</th><th>Description</th></tr> + <tr><td>XB_NO_ERROR</td><td>No error</td></tr> + <tr><td>XB_NOT_OPEN</td><td>File was not open</td></tr> + </table> + \endhtmlonly + \latexonly + \\ + \\ + \begin{tabular}{|l|l|} \hline + \textbf{Return Code} & \textbf{Description} \\ \hline \hline + XB\_NO\_ERROR & No Error \\ \hline + XB\_NOT\_OPEN\_ERROR & File was not open \\ \hline + \end{tabular} + \endlatexonly +*/ +xbShort xbDbf::CloseDatabase( xbBool deleteIndexes ) +{ +#if defined(XB_INDEX_ANY) + xbIxList *i, *ti; +#endif + + if(DbfStatus == XB_CLOSED) + return XB_NO_ERROR; + + +#if defined(XB_INDEX_ANY) + i = NdxList; + while (i){ + i->index->CloseIndex(); + if(deleteIndexes) + delete i->index; + i = NdxList; + } +/* free up unused nodes */ + i = FreeIxList; + while( i ) { + ti = i; + i = i->NextIx; + free(ti); + } +#endif + + if(SchemaPtr){ + for( int j = 0; j < NoOfFields; j++ ) + if( SchemaPtr[j].fp ) delete SchemaPtr[j].fp; + free( SchemaPtr ); + } + if(RecBuf) + free( RecBuf ); + if(RecBuf2) + free( RecBuf2 ); + +#ifdef XB_MEMO_FIELDS + if( mbb ) + free( mbb ); /* memo block buffer */ + if( mfp ) + fclose( mfp ); /* memo file pointer */ +#endif + +#ifdef XB_LOCKING_ON + if( xblfh ){ + fclose( xblfh ); + xblfh = NULL; + } +#endif + + xbase->RemoveDbfFromDbfList( this ); + if(fp) + fclose( fp ); + InitVars(); + return XB_NO_ERROR; +} +/************************************************************************/ +/* options 1 = Print header only + 2 = Field data only + 3 = Header and Field data */ + +//! Dump header information. +/*! + \param Option One of the following: + \htmlonly + <p> + <table border=2><tr><th>Option</th><th>Description</th></tr> + <tr><td>1</td><td>Print header only</td></tr> + <tr><td>2</td><td>Field data only</td></tr> + <tr><td>3</td><td>Header and field data</td></tr> + </table> + \endhtmlonly + \latexonly + \\ + \\ + \begin{tabular}{|l|l|} \hline + \textbf{Option} & \textbf{Description} \\ \hline \hline + 1 & Header only \\ \hline + 2 & Field data only \\ \hline + 3 & Header and field data \\ \hline + \end{tabular} + \endlatexonly +*/ +#ifdef XBASE_DEBUG +xbShort xbDbf::DumpHeader( xbShort Option ) +{ + int i; + + if( Option < 1 || Option > 3 ) + return XB_INVALID_OPTION; + + if( DbfStatus == XB_CLOSED ) + return XB_NOT_OPEN; + + std::cout << "\nDatabase file " << GetFileName() << std::endl << std::endl; + + if( Option != 2 ){ + std::cout << "File header data:" << std::endl; + if( Version == 3 ) + std::cout << "Dbase III file" << std::endl; + else if ( Version == 83 ) + std::cout << "Dbase III file with memo fields" << std::endl << std::endl; + + std::cout << "Last update date = " + << (int) UpdateMM << "/" << (int) UpdateDD << "/" << (int) UpdateYY % 100 << std::endl; + + std::cout << "Header length = " << HeaderLen << std::endl; + std::cout << "Record length = " << RecordLen << std::endl; + std::cout << "Records in file = " << NoOfRecs << std::endl << std::endl; +#ifdef XB_REAL_DELETE + std::cout << "First Free Rec = " << FirstFreeRec << std::endl << std::endl; +#endif + } + if( Option != 1 ){ + std::cout << "Field Name Type Length Decimals" << std::endl; + std::cout << "---------- ---- ------ --------" << std::endl; + for( i = 0; i <NoOfFields; i++ ){ + if( SchemaPtr[i].Type == 'C' && SchemaPtr[i].NoOfDecs > 0 ) + printf( "%10s %1c %4d %4d\n", SchemaPtr[i].FieldName, + SchemaPtr[i].Type, SchemaPtr[i].FieldLen, 0 ); + else + printf( "%10s %1c %4d %4d\n", SchemaPtr[i].FieldName, + SchemaPtr[i].Type, SchemaPtr[i].FieldLen, SchemaPtr[i].NoOfDecs ); + } + } + std::cout << std::endl; + return XB_NO_ERROR; +} +#endif +/************************************************************************/ +//! Open the DBF file. +/*! + This method attempts to open the DBF file with the specified + name (TableName). This method does not position to any particular + record in the file. The record buffer is blanked (set to spaces). + + \param TableName Name of table to open + \returns One of the following: + \htmlonly + <p> + <table border=2><tr><th>Return Code</th><th>Description</th></tr> + <tr><td>XB_NO_ERROR</td><td>No error</td></tr> + <tr><td>XB_OPEN_ERROR</td><td>Couldn't open file</td></tr> + <tr><td>XB_NO_MEMORY</td><td>Memory allocation error</td></tr> + <tr><td>XB_NOT_XBASE</td><td>Not an DBF file</td></tr> + </table> + \endhtmlonly + \latexonly + \\ + \\ + \begin{tabular}{|l|l|} \hline + \textbf{Return Code} & \textbf{Description} \\ \hline \hline + XB\_NO\_ERROR & No error \\ \hline + XB\_OPEN\_ERROR & Couldn't open file \\ \hline + XB\_NO\_MEMORY & Memory allocation error \\ \hline + XB\_NOT\_XBASE & Not an DBF file \\ \hline + \end{tabular} + \endlatexonly +*/ +xbShort xbDbf::OpenDatabase( const char * TableName ) +{ + xbShort i, j, rc; + char buf[33]; + char *p; + +#ifdef XB_MEMO_FIELDS + xbShort MemoSw = 0; +#endif + + /* verify the file is not already open */ + if( DbfStatus != XB_CLOSED ) + return XB_ALREADY_OPEN; + + /* copy the file name to the class variable */ + SetFileName( TableName ); + + /* open the file */ + if(( fp = fopen(GetFileName(), "r+b")) == NULL ){ + // + // Try to open read only if failed to open read/write + // + if(( fp = fopen(GetFileName(), "rb")) == NULL ) + return XB_OPEN_ERROR; + } + +#ifdef XB_LOCKING_ON + /* no buffering in multi user mode - may not see what others have updated */ + setbuf( fp, NULL ); + + /* open the lock file if XB_XBASE_LOCK_MODE */ +// if( LockMode == XB_XBASE_LOCK_MODE ) +// if(( rc = OpenXbLockFile()) != XB_NO_ERROR ) +// return rc; +#endif + +#ifdef XB_LOCKING_ON +// if( AutoLock ) +// if(( rc = LockDatabase( XB_LOCK, 0L )) != XB_NO_ERROR) +// return rc; +#endif + + /* copy the header into memory */ + if(( rc = ReadHeader( 1 )) != XB_NO_ERROR ){ + InitVars(); + return rc; + } + + /* check the version */ + if( Version == 3 || Version == (char)0x83 ){ /* dBASE III+ */ + XFV = 3; +#ifdef XB_MEMO_FIELDS + MemoHeader.Version = 0x03; +#endif + } + else if( Version == 4 || Version == (char)0x8B ){ /* dBASE IV */ + XFV = 4; +#ifdef XB_MEMO_FIELDS + MemoHeader.Version = 0x00; +#endif + } + else if( Version == (char)0xf5 ){ /* FoxPro */ + XFV = 4; +#ifdef XB_MEMO_FIELDS + MemoHeader.Version = 0x00; +#endif + } + else if( Version == (char)0x30 ){ /* Visual Foxpro */ + XFV = 4; +#ifdef XB_MEMO_FIELDS + MemoHeader.Version = 0x00; +#endif + } else { + InitVars(); + return XB_NOT_XBASE; + } + + // it would seem that dBASE III+ generates an UpdateYY value + // of 0 for 2000 and dBASE IV uses 100, so I have removed the + // check for UpdateYY being 0 (which might be valid). DTB + + // Not all flavors of database tools use these fields + // Found a month set to 0 in valid dbf file + // Commented out this check 2/11/06 - GAK + + // if( UpdateMM == 0 || UpdateDD == 0 ){ + // InitVars(); + // return XB_NOT_XBASE; + // } + + /* calculate the number of fields */ + if( Version == (char)0x30 ) { + NoOfFields = ( HeaderLen - 296 ) / 32 ; + } else { + NoOfFields = ( HeaderLen - 33 ) / 32; + } + + if(( RecBuf = (char *) malloc( RecordLen )) == NULL ) { + fclose( fp ); + InitVars(); + return XB_NO_MEMORY; + } + if(( RecBuf2 = (char *) malloc( RecordLen )) == NULL ) { + fclose( fp ); + free( RecBuf ); + InitVars(); + return XB_NO_MEMORY; + } + + if((SchemaPtr=(xbSchemaRec *)malloc(NoOfFields*sizeof(xbSchemaRec)))==NULL){ + free( RecBuf ); + free( RecBuf2 ); + fclose( fp ); + InitVars(); + return XB_NO_MEMORY; + } + memset( SchemaPtr, 0x00, ( NoOfFields * sizeof(xbSchemaRec))); + + /* copy field info into memory */ + for( i = 0, j = 1; i < NoOfFields; i++ ){ + _fseek( fp,((xbOffT)i*32+32), 0 ); + fread( &buf, 1, 32, fp ); + p = buf; + strncpy( SchemaPtr[i].FieldName, p, 10 ); + p += 11; + SchemaPtr[i].Type = *p++; + + SchemaPtr[i].Address = RecBuf + j; + SchemaPtr[i].Address2 = RecBuf2 + j; + + SchemaPtr[i].FieldLen = *( p + 4 ); + SchemaPtr[i].NoOfDecs = *( p + 5 ); + + if( SchemaPtr[i].Type == 'C' && SchemaPtr[i].NoOfDecs > 0 ){ + SchemaPtr[i].LongFieldLen = xbase->GetShort( p + 4 ); + j += SchemaPtr[i].LongFieldLen; + } + else + j += SchemaPtr[i].FieldLen; +#ifdef XB_MEMO_FIELDS + if( !MemoSw && (SchemaPtr[i].Type == 'M' || + SchemaPtr[i].Type == 'B' || SchemaPtr[i].Type == 'O' )) + MemoSw++; +#endif + } + CurRec = 0L; + BlankRecord(); + DbfStatus = XB_OPEN; + +#ifdef XB_MEMO_FIELDS + if( MemoSw ) /* does this table have memo fields ? */ + if(( rc = OpenMemoFile()) != XB_NO_ERROR ){ + free( RecBuf ); + free( RecBuf2 ); + free( SchemaPtr ); + fclose( fp ); + InitVars(); + return rc; + } +#endif + +#ifdef XB_LOCKING_ON +// if( AutoLock ) +// LockDatabase( XB_UNLOCK, 0L ); +#endif /* XB_LOCKING_ON */ + + return xbase->AddDbfToDbfList( this, GetFileName() ); +} +/************************************************************************/ +//! Blank the record buffer. +/*! + Sets the record to spaces. +*/ +xbShort xbDbf::BlankRecord() +{ + if( DbfStatus == XB_CLOSED ) + return XB_NOT_OPEN; + + if( DbfStatus != XB_UPDATED ){ + DbfStatus = XB_UPDATED; + memcpy( RecBuf2, RecBuf, RecordLen ); + } + + memset( RecBuf, 0x20, RecordLen ); + return XB_NO_ERROR; +} +/************************************************************************/ +//! Append the current record to the data file +/*! + This method attempts to append the contents of the current record buffer + to the end of the DBF file and updates the file date and number of + records in the file. Also updates any open indices associated with + this data file. + + \returns One of the following: + \htmlonly + <p> + <table border=2><tr><th>Return Code</th><th>Description</th></tr> + <tr><td>XB_NO_ERROR</td><td>No error</td></tr> + <tr><td>XB_LOCK_FAILED</td><td>Couldn't lock file</td></tr> + <tr><td>XB_WRITE_ERROR</td><td>Error writing to file</td></tr> + </table> + \endhtmlonly + \latexonly + \\ + \\ + \begin{tabular}{|l|l|} \hline + \textbf{Return Code} & \textbf{Description} \\ \hline \hline + XB\_NO\_ERROR & No error \\ \hline + XB\_LOCK\_FAILED & Couldn't lock file \\ \hline + XB\_WRITE\_ERROR & Error writing to file \\ \hline + \end{tabular} + \endlatexonly +*/ +xbShort xbDbf::AppendRecord() +{ + xbShort rc; + xbULong nextRecNo; + +#if defined(XB_INDEX_ANY) + xbIxList *i; +#endif + +/* lock the database */ +#ifdef XB_LOCKING_ON +// if( AutoLock ) +// if(( rc = LockDatabase( XB_LOCK, 0L )) != XB_NO_ERROR) +// return rc; + + rc = ReadHeader(1); + +// if(AutoLock) +// LockDatabase( XB_UNLOCK, 0L ); + + if( rc ) + return rc; + +#endif + +/* lock any indexes */ +#if defined(XB_INDEX_ANY) +#ifdef XB_LOCKING_ON + i = NdxList; + while( i && AutoLock ){ +// if(( rc = i->index->LockIndex( XB_LOCK )) != XB_NO_ERROR ) +// return rc; + i = i->NextIx; + } +#endif /* XB_LOCKING_ON */ +#endif + +// if there are no duplicates, and no records set the CurRec to the +// last record + 1. This is for EXP::RECNO() + +/* check for any duplicate keys */ +#if defined(XB_INDEX_ANY) + i = NdxList; + while( i ){ + if( i->index->UniqueIndex() ){ + i->index->CreateKey( 0, 0 ); + if( i->index->FindKey() == XB_FOUND ) + return XB_KEY_NOT_UNIQUE; + } + i = i->NextIx; + } +#endif + +#ifdef XB_REAL_DELETE + if(RealDelete && FirstFreeRec) + nextRecNo = FirstFreeRec; + else + nextRecNo = NoOfRecs + 1; +#else + nextRecNo = NoOfRecs + 1; +#endif + + CurRec = NoOfRecs + 1; + +#if defined(XB_INDEX_ANY) +/* update the indexes */ + i = NdxList; + while( i ){ + if( !i->index->UniqueIndex() ) /* if we didn't prepare the key */ + if(( rc = i->index->CreateKey( 0, 0 )) != XB_NO_ERROR ) /* then do it before the add */ + return rc; + if(( rc = i->index->AddKey(nextRecNo)) != XB_NO_ERROR ) + return rc; + i->index->TouchIndex(); + i = i->NextIx; + } +#endif /* XB_INDEX_ANY */ + +#ifdef XB_REAL_DELETE + char buf[4]; + + if(RealDelete && FirstFreeRec){ + /* + ** Grab the next free rec no and put it in FirstFreeRec + */ + if(_fseek(fp, (HeaderLen+(((xbOffT)FirstFreeRec-1)*RecordLen)+1), 0) != 0) + return XB_SEEK_ERROR; + + if(fread(buf, 4, 1, fp) != 1) + return XB_READ_ERROR; + + FirstFreeRec = xbase->GetULong(buf); + } + + /* + ** Okay, seek and write the record out + */ + if(_fseek(fp, (HeaderLen+(((xbOffT)nextRecNo-1)*RecordLen)), 0) != 0) + return XB_SEEK_ERROR; + + if(fwrite( RecBuf, RecordLen, 1, fp) != 1) + return XB_WRITE_ERROR; + + /* + ** If we just appended the record to the file, then write the EOF char + */ + if(nextRecNo == NoOfRecs + 1){ + if( fputc( XB_CHAREOF, fp ) != XB_CHAREOF ) + return XB_WRITE_ERROR; + } +#else + /* write the last record */ + if( _fseek( fp,(HeaderLen+((xbOffT)NoOfRecs*RecordLen)), 0 ) != 0 ) + return XB_SEEK_ERROR; + + if( fwrite( RecBuf, RecordLen, 1, fp ) != 1 ) + return XB_WRITE_ERROR; + + /* write the end of file marker */ + if( fputc( XB_CHAREOF, fp ) != XB_CHAREOF ) + return XB_WRITE_ERROR; +#endif + + /* calculate the latest header information */ + xbDate d; + UpdateYY = d.YearOf() - 1900; + if(XFV == 3) + UpdateYY %= 100; // dBASE III seems to do this, IV does not. DTB + UpdateMM = d.MonthOf(); + UpdateDD = d.DayOf( XB_FMT_MONTH ); +#ifndef XB_REAL_DELETE + NoOfRecs++; +#else + if(RealDelete){ + if(nextRecNo == NoOfRecs + 1) + NoOfRecs++; + RealNumRecs++; + } + else + NoOfRecs++; +#endif + CurRec = nextRecNo; + + /* rewrite the header record */ + if(( rc = WriteHeader( 1 )) != XB_NO_ERROR ) + return rc; + +#ifdef XB_LOCKING_ON +// if( AutoLock ) +// LockDatabase( XB_UNLOCK, 0L ); + +#if defined(XB_INDEX_ANY) + i = NdxList; + while( i && AutoLock ){ +// i->index->LockIndex( XB_UNLOCK ); + i = i->NextIx; + } +#endif /* XB_INDEX_ANY */ +#endif /* XB_LOCKING_ON */ + + DbfStatus = XB_OPEN; + return XB_NO_ERROR; +} +/************************************************************************/ +//! Get a record from the data file +/*! + This method attempts to retrieve the record specified by RecNo from the + data file into the record buffer. + + \param RecNo Record number to retrieve + \returns One of the following: + \htmlonly + <p> + <table border=2><tr><th>Return Code</th><th>Description</th></tr> + <tr><td>XB_NO_ERROR</td><td>No error</td></tr> + <tr><td>XB_LOCK_FAILED</td><td>Couldn't lock file</td></tr> + <tr><td>XB_NOT_OPEN</td><td>File is not open</td></tr> + <tr><td>XB_INVALID_RECORD</td><td>Invalid record number</td></tr> + <tr><td>XB_WRITE_ERROR</td><td>Error writing to file</td></tr> + </table> + \endhtmlonly + \latexonly + \\ + \\ + \begin{tabular}{|l|l|} \hline + \textbf{Return Code} & \textbf{Description} \\ \hline \hline + XB\_NO\_ERROR & No error \\ \hline + XB\_LOCK\_FAILED & Couldn't lock file \\ \hline + XB\_NOT\_OPEN & File is not open \\ \hline + XB\_INVALID\_RECORD & Invalid record number \\ \hline + XB\_WRITE\_ERROR & Error writing to file \\ \hline + \end{tabular} + \endlatexonly +*/ +xbShort xbDbf::GetRecord( xbULong RecNo ) +{ + xbShort rc; + if( DbfStatus == XB_CLOSED ) + return XB_NOT_OPEN; + +#ifdef XB_LOCKING_ON +// if( AutoLock ) +// if(( rc = LockDatabase( XB_LOCK, RecNo )) != 0 ) return rc; + + rc = ReadHeader(1); + +// if(AutoLock) +// LockDatabase( XB_UNLOCK, RecNo ); + + if( rc ) + return rc; + +#endif + + if( RecNo > NoOfRecs || RecNo == 0L ) + return XB_INVALID_RECORD; + + if( _fseek( fp, (HeaderLen+(((xbOffT)RecNo-1L)*RecordLen)), SEEK_SET )){ +#ifdef XB_LOCKING_ON +// LockDatabase( XB_UNLOCK, RecNo ); +#endif + return XB_SEEK_ERROR; + } + + if( fread( RecBuf, RecordLen, 1, fp ) != 1 ){ +#ifdef XB_LOCKING_ON +// LockDatabase( XB_UNLOCK, RecNo ); +#endif + return XB_READ_ERROR; + } + +#ifdef XB_LOCKING_ON +// if( AutoLock ) +// LockDatabase( XB_LOCK, RecNo ); +#endif + + DbfStatus = XB_OPEN; + CurRec = RecNo; + return XB_NO_ERROR; +} +/************************************************************************/ +//! Get the first physical record in the data file +/*! + Attempts to retrieve the first physical record from the data file into + the record buffer. + + \returns One of the following: + \htmlonly + <p> + <table border=2><tr><th>Return Code</th><th>Description</th></tr> + <tr><td>XB_NO_ERROR</td><td>No error</td></tr> + <tr><td>XB_LOCK_FAILED</td><td>Couldn't lock file</td></tr> + <tr><td>XB_NOT_OPEN</td><td>File is not open</td></tr> + <tr><td>XB_INVALID_RECORD</td><td>Invalid record number</td></tr> + <tr><td>XB_SEEK_ERROR</td><td>Error seeking file</td></tr> + <tr><td>XB_WRITE_ERROR</td><td>Error writing to file</td></tr> + </table> + \endhtmlonly + \latexonly + \\ + \\ + \begin{tabular}{|l|l|} \hline + \textbf{Return Code} & \textbf{Description} \\ \hline \hline + XB\_NO\_ERROR & No error \\ \hline + XB\_LOCK\_FAILED & Couldn't lock file \\ \hline + XB\_NOT\_OPEN & File is not open \\ \hline + XB\_INVALID\_RECORD & Invalid record number \\ \hline + XB\_SEEK\_ERROR & Error seeking file \\ \hline + XB\_WRITE\_ERROR & Error writing to file \\ \hline + \end{tabular} + \endlatexonly +*/ +xbShort xbDbf::GetFirstRecord() +{ + xbShort rc; + if( NoOfRecs == 0 ) + return XB_INVALID_RECORD; + + rc = GetRecord( 1L ); +#ifdef XB_REAL_DELETE + if(!rc && RealDelete && RecordDeleted()) + rc = GetNextRecord(); +#endif + + return rc; +} +/************************************************************************/ +//! Get the last phyiscal record in the data file +/*! + Attempts to retrieve the last physical record from the data file into + the record buffer. + + \returns One of the following: + \htmlonly + <p> + <table border=2><tr><th>Return Code</th><th>Description</th></tr> + <tr><td>XB_NO_ERROR</td><td>No error</td></tr> + <tr><td>XB_LOCK_FAILED</td><td>Couldn't lock file</td></tr> + <tr><td>XB_EOF</td><td>At end of file</td></tr> + <tr><td>XB_NOT_OPEN</td><td>File is not open</td></tr> + <tr><td>XB_INVALID_RECORD</td><td>Invalid record number</td></tr> + <tr><td>XB_SEEK_ERROR</td><td>Error seeking file</td></tr> + <tr><td>XB_WRITE_ERROR</td><td>Error writing to file</td></tr> + </table> + \endhtmlonly + \latexonly + \\ + \\ + \begin{tabular}{|l|l|} \hline + \textbf{Return Code} & \textbf{Description} \\ \hline \hline + XB\_NO\_ERROR & No error \\ \hline + XB\_LOCK\_FAILED & Couldn't lock file \\ \hline + XB\_EOF & At end of file \\ \hline + XB\_NOT\_OPEN & File is not open \\ \hline + XB\_INVALID\_RECORD & Invalid record number \\ \hline + XB\_SEEK\_ERROR & Error seeking file \\ \hline + XB\_WRITE\_ERROR & Error writing to file \\ \hline + \end{tabular} + \endlatexonly +*/ +xbShort xbDbf::GetLastRecord() +{ + xbShort rc; + if( NoOfRecs == 0 ) + return XB_INVALID_RECORD; + + rc = GetRecord( NoOfRecs ); +#ifdef XB_REAL_DELETE + if(!rc && RealDelete && RecordDeleted()) + rc = GetPrevRecord(); +#endif + + return rc; +} +/************************************************************************/ +//! Get the next physical record in the data file +/*! + Attempts to retrieve the next physical record from the data file into + the record buffer. + + \returns One of the following: + \htmlonly + <p> + <table border=2><tr><th>Return Code</th><th>Description</th></tr> + <tr><td>XB_NO_ERROR</td><td>No error</td></tr> + <tr><td>XB_LOCK_FAILED</td><td>Couldn't lock file</td></tr> + <tr><td>XB_EOF</td><td>At end of file</td></tr> + <tr><td>XB_NOT_OPEN</td><td>File is not open</td></tr> + <tr><td>XB_INVALID_RECORD</td><td>Invalid record number</td></tr> + <tr><td>XB_SEEK_ERROR</td><td>Error seeking file</td></tr> + <tr><td>XB_WRITE_ERROR</td><td>Error writing to file</td></tr> + </table> + \endhtmlonly + \latexonly + \\ + \\ + \begin{tabular}{|l|l|} \hline + \textbf{Return Code} & \textbf{Description} \\ \hline \hline + XB\_NO\_ERROR & No error \\ \hline + XB\_LOCK\_FAILED & Couldn't lock file \\ \hline + XB\_EOF & At end of file \\ \hline + XB\_NOT\_OPEN & File is not open \\ \hline + XB\_INVALID\_RECORD & Invalid record number \\ \hline + XB\_SEEK\_ERROR & Error seeking file \\ \hline + XB\_WRITE\_ERROR & Error writing to file \\ \hline + \end{tabular} + \endlatexonly +*/ +xbShort xbDbf::GetNextRecord() +{ + xbShort rc; + if( NoOfRecs == 0 ) + return XB_INVALID_RECORD; + else if( CurRec >= NoOfRecs ) + return XB_EOF; + + rc = GetRecord( ++CurRec ); + +#ifdef XB_REAL_DELETE + while(!rc && RealDelete && RecordDeleted()) + rc = GetRecord(++CurRec); +#endif + + return rc; +} +/************************************************************************/ +//! Get the previous physical record in the data file +/*! + Attempts to retrieve the previous physical record from the data file into + the record buffer. + + \returns One of the following: + \htmlonly + <p> + <table border=2><tr><th>Return Code</th><th>Description</th></tr> + <tr><td>XB_NO_ERROR</td><td>No error</td></tr> + <tr><td>XB_LOCK_FAILED</td><td>Couldn't lock file</td></tr> + <tr><td>XB_BOF</td><td>At beginning of file</td></tr> + <tr><td>XB_NOT_OPEN</td><td>File is not open</td></tr> + <tr><td>XB_INVALID_RECORD</td><td>Invalid record number</td></tr> + <tr><td>XB_SEEK_ERROR</td><td>Error seeking file</td></tr> + <tr><td>XB_WRITE_ERROR</td><td>Error writing to file</td></tr> + </table> + \endhtmlonly + \latexonly + \\ + \\ + \begin{tabular}{|l|l|} \hline + \textbf{Return Code} & \textbf{Description} \\ \hline \hline + XB\_NO\_ERROR & No error \\ \hline + XB\_LOCK\_FAILED & Couldn't lock file \\ \hline + XB\_BOF & At beginning of file \\ \hline + XB\_NOT\_OPEN & File is not open \\ \hline + XB\_INVALID\_RECORD & Invalid record number \\ \hline + XB\_SEEK\_ERROR & Error seeking file \\ \hline + XB\_WRITE\_ERROR & Error writing to file \\ \hline + \end{tabular} + \endlatexonly +*/ +xbShort xbDbf::GetPrevRecord() +{ + xbShort rc; + if( NoOfRecs == 0 ) + return XB_INVALID_RECORD; + else if( CurRec <= 1L ) + return XB_EOF; + + rc = GetRecord( --CurRec ); + +#ifdef XB_REAL_DELETE + while(!rc && RealDelete && RecordDeleted()) + rc = GetRecord(--CurRec); +#endif + + return rc; +} +/************************************************************************/ +//! Dump record +/*! + Dump the contents of the specified record to stdout. + + \param RecNo Record number of record to be dumped. + \returns An error code (same as GetRecord()). +*/ +xbShort xbDbf::DumpRecord( xbULong RecNo ) +{ + int i, rc; + char buf[4096]; + + if( RecNo == 0 || RecNo > NoOfRecs ) + return XB_INVALID_RECORD; + + rc = GetRecord( RecNo ); + if( rc != XB_NO_ERROR ) + return rc; + + std::cout << "\nREC NUMBER " << RecNo << "\n"; + + if( RecordDeleted() ) + std::cout << "\nRecord deleted...\n"; + + for( i = 0; i < NoOfFields; i++ ){ +#ifdef XB_MEMO_FIELDS + if(SchemaPtr[i].Type == 'M'){ + if( MemoFieldExists( i )){ + std::cout << SchemaPtr[i].Type << " " << SchemaPtr[i].FieldName + << " len = " << GetMemoFieldLen( i ) << std::endl; + memset( buf, 0x00, 4095 ); + rc = GetMemoField(i, 4095, buf, 0); + if(rc != XB_NO_ERROR) + return rc; + } else { + buf[0] = 0x00; + } + } + else + GetField( i, buf ); + std::cout << SchemaPtr[i].Type << " " << SchemaPtr[i].FieldName << " = '" << buf << "'\n"; +#else + GetField( i, buf ); + std::cout << SchemaPtr[i].FieldName << " = '" << buf << "'\n"; +#endif + } + std::cout << std::endl; + return XB_NO_ERROR; +} +/************************************************************************/ +//! Write the current record buffer to the current record in the data file. +/*! + Attempts to write the contents of the record buffer to the current + record in the data file. Updates any open indexes. + + \sa PutRecord(xbULong RecNo) + \returns One of the following: + \htmlonly + <p> + <table border=2><tr><th>Return Code</th><th>Description</th></tr> + <tr><td>XB_NO_ERROR</td><td>No error</td></tr> + <tr><td>XB_LOCK_FAILED</td><td>Couldn't lock file</td></tr> + <tr><td>XB_NOT_OPEN</td><td>File is not open</td></tr> + <tr><td>XB_INVALID_RECORD</td><td>Invalid record number</td></tr> + <tr><td>XB_SEEK_ERROR</td><td>Error seeking file</td></tr> + <tr><td>XB_WRITE_ERROR</td><td>Error writing to file</td></tr> + </table> + \endhtmlonly + \latexonly + \\ + \\ + \begin{tabular}{|l|l|} \hline + \textbf{Return Code} & \textbf{Description} \\ \hline \hline + XB\_NO\_ERROR & No error \\ \hline + XB\_LOCK\_FAILED & Couldn't lock file \\ \hline + XB\_NOT\_OPEN & File is not open \\ \hline + XB\_INVALID\_RECORD & Invalid record number \\ \hline + XB\_SEEK\_ERROR & Error seeking file \\ \hline + XB\_WRITE\_ERROR & Error writing to file \\ \hline + \end{tabular} + \endlatexonly +*/ + +/************************************************************************/ +xbShort xbDbf::PutRecord() { + return PutRecord(CurRec); +} + +//! Write the current record buffer to the specified record in the data file. +/*! + Attempts to write the contents of the record buffer to the record specified + by RecNo. Updates any open indexes. + + \param RecNo Record number to which data should be written + \returns One of the following: + \htmlonly + <p> + <table border=2><tr><th>Return Code</th><th>Description</th></tr> + <tr><td>XB_NO_ERROR</td><td>No error</td></tr> + <tr><td>XB_LOCK_FAILED</td><td>Couldn't lock file</td></tr> + <tr><td>XB_NOT_OPEN</td><td>File is not open</td></tr> + <tr><td>XB_INVALID_RECORD</td><td>Invalid record number</td></tr> + <tr><td>XB_SEEK_ERROR</td><td>Error seeking file</td></tr> + <tr><td>XB_WRITE_ERROR</td><td>Error writing to file</td></tr> + </table> + \endhtmlonly + \latexonly + \\ + \\ + \begin{tabular}{|l|l|} \hline + \textbf{Return Code} & \textbf{Description} \\ \hline \hline + XB\_NO\_ERROR & No error \\ \hline + XB\_LOCK\_FAILED & Couldn't lock file \\ \hline + XB\_NOT\_OPEN & File is not open \\ \hline + XB\_INVALID\_RECORD & Invalid record number \\ \hline + XB\_SEEK\_ERROR & Error seeking file \\ \hline + XB\_WRITE\_ERROR & Error writing to file \\ \hline + \end{tabular} + \endlatexonly +*/ +xbShort xbDbf::PutRecord(xbULong RecNo) +{ + xbShort rc; + +#if defined(XB_INDEX_ANY) + xbIxList *i; +#endif + + if( DbfStatus == XB_CLOSED ) + return XB_NOT_OPEN; + +/* lock the database */ +#ifdef XB_LOCKING_ON + if( AutoLock ){ +// if(( rc = LockDatabase( XB_LOCK, RecNo )) != XB_NO_ERROR ) +// return rc; +// if(( rc = LockDatabase( XB_LOCK, 0L )) != XB_NO_ERROR ){ +// LockDatabase( XB_UNLOCK, RecNo ); +// return rc; +// } + + if((rc = ReadHeader(1)) != XB_NO_ERROR){ +// if(AutoLock){ +// LockDatabase( XB_UNLOCK, RecNo ); +// LockDatabase( XB_UNLOCK, 0L ); +// } + return rc; + } + } +#endif + + if( RecNo > NoOfRecs || RecNo == 0L ) + return XB_INVALID_RECORD; + +/* lock the indexes */ +#if defined(XB_INDEX_ANY) +#ifdef XB_LOCKING_ON + i = NdxList; + while( i && AutoLock ){ +// if(( rc = i->index->LockIndex( XB_LOCK )) != XB_NO_ERROR ) +// return rc; + i = i->NextIx; + } +#endif /* XB_LOCKING_ON */ +#endif + +#if defined(XB_INDEX_ANY) + /* for any unique indexes that were updated, verify no unique keys exist */ + i = NdxList; + while( i ){ + if( i->index->UniqueIndex() ){ + if(( i->KeyUpdated = i->index->KeyWasChanged()) == 1 ){ + i->index->CreateKey(0, 0); + if( i->index->FindKey() == XB_FOUND && i->index->GetCurDbfRec() != RecNo) + return XB_KEY_NOT_UNIQUE; + } + } + i = i->NextIx; + } +#endif + +#if defined(XB_INDEX_ANY) + /* loop through deleting old index keys and adding new index keys */ + i = NdxList; + while( i ){ + if( !i->index->UniqueIndex() ) + i->KeyUpdated = i->index->KeyWasChanged(); + if( i->KeyUpdated ){ + i->index->CreateKey( 1, 0 ); /* load key buf w/ old values */ + if((rc = i->index->DeleteKey( CurRec )) != XB_NO_ERROR){ +#ifdef XB_LOCKING_ON +// if( AutoLock ){ +// LockDatabase( XB_UNLOCK, RecNo ); +// LockDatabase( XB_UNLOCK, 0L ); +// } +#if defined(XB_INDEX_ANY) + i = NdxList; + while( i && AutoLock ){ +// i->index->LockIndex( XB_UNLOCK ); + i = i->NextIx; + } +#endif /* XB_INDEX_ANY */ +#endif /* XB_LOCKING_ON */ + return rc; + } + + i->index->CreateKey( 0, 0 ); + if(( rc = i->index->AddKey(CurRec)) != XB_NO_ERROR ){ +#ifdef XB_LOCKING_ON +// if( AutoLock ){ +// LockDatabase( XB_UNLOCK, RecNo ); +// LockDatabase( XB_UNLOCK, 0L ); +// } +#if defined(XB_INDEX_ANY) + i = NdxList; + while( i && AutoLock ){ +// i->index->LockIndex( XB_UNLOCK ); + i = i->NextIx; + } +#endif /* XB_INDEX_ANY */ +#endif /* XB_LOCKING_ON */ + return rc; + } + i->index->TouchIndex(); + } + i = i->NextIx; + } +#endif /* XB_INDEX_ANY */ + + if( _fseek( fp, (HeaderLen+(((xbOffT)RecNo-1L)*RecordLen)),0 )) + return XB_SEEK_ERROR; + + if( fwrite( RecBuf, RecordLen, 1, fp ) != 1 ) + return XB_WRITE_ERROR; + + /* calculate the latest header information */ + xbDate d; + UpdateYY = d.YearOf() - 1900; + if(XFV == 3) + UpdateYY %= 100; // dBASE III seems to do this, IV does not. DTB + UpdateMM = d.MonthOf(); + UpdateDD = d.DayOf( XB_FMT_MONTH ); + + /* rewrite the header record */ + if(( rc = WriteHeader( 1 )) != XB_NO_ERROR ) + return rc; + +#ifdef XB_LOCKING_ON +// if( AutoLock ){ +// LockDatabase( XB_UNLOCK, RecNo ); +// LockDatabase( XB_UNLOCK, 0L ); +// } + +#if defined(XB_INDEX_ANY) + i = NdxList; + while( i && AutoLock ){ +// i->index->LockIndex( XB_UNLOCK ); + i = i->NextIx; + } +#endif /* XB_INDEX_ANY */ +#endif /* XB_LOCKING_ON */ + + CurRec = RecNo; + DbfStatus = XB_OPEN; + return XB_NO_ERROR; +} +/************************************************************************/ +//! Delete the current record +/*! + Marks the current record as deleted or if "real" deletes are turned + on (xbDbf::RealDeleteOn()) will delete the record and add it to the + free record list. Normal dBase behavior is to simply mark the record + as deleted; the record will actually be deleted when the the DBF file + "packed" (xbDbf::PackDatabase()). If "real" deletes are not on, a + record may be undeleted using xbDbf::UndeleteRecord(). + + \returns One of the following: + \htmlonly + <p> + <table border=2><tr><th>Return Code</th><th>Description</th></tr> + <tr><td>XB_NO_ERROR</td><td>No error</td></tr> + <tr><td>XB_INVALID_RECORD</td><td>Invalid record number</td></tr> + </table> + \endhtmlonly + \latexonly + \\ + \\ + \begin{tabular}{|l|l|} \hline + \textbf{Return Code} & \textbf{Description} \\ \hline \hline + XB\_NO\_ERROR & No error \\ \hline + XB\_INVALID\_RECORD & Invalid record number \\ \hline + \end{tabular} + \endlatexonly +*/ +xbShort xbDbf::DeleteRecord() +{ + xbULong newCurRec = 0; + xbShort rc = XB_NO_ERROR; + +#if defined(XB_INDEX_ANY) + xbIxList *i; +#endif + + if(!RecBuf) + return XB_INVALID_RECORD; + + if(CurRec < 1 || CurRec > NoOfRecs) + return XB_INVALID_RECORD; + +/* lock the database */ +#ifdef XB_LOCKING_ON + + if( AutoLock ){ +/* + if(( rc = LockDatabase( XB_LOCK, CurRec )) != XB_NO_ERROR ) + return rc; + if(( rc = LockDatabase( XB_LOCK, 0L )) != XB_NO_ERROR ){ + LockDatabase( XB_UNLOCK, CurRec ); + return rc; + } + */ + if((rc = ReadHeader(1)) != XB_NO_ERROR){ +// if(AutoLock){ +// LockDatabase( XB_UNLOCK, CurRec ); +// LockDatabase( XB_UNLOCK, 0L ); +// } + return rc; + } + } +#endif + +/* lock the indexes */ +#if defined(XB_INDEX_ANY) && defined(XB_LOCKING_ON) && defined(XB_REAL_DELETE) + i = NdxList; + while( i && AutoLock ){ +// if(( rc = i->index->LockIndex( XB_LOCK )) != XB_NO_ERROR ) +// return rc; + i = i->NextIx; + } +#endif + +/* remove keys from indexes */ +#if defined(XB_REAL_DELETE) && defined(XB_INDEX_ANY) + + if(RealDelete){ + i = NdxList; + while(i){ + i->index->CreateKey(0, 0); /* load key buf */ + if(i->index->GetCurDbfRec() == (xbULong)CurRec){ + i->index->DeleteKey(CurRec); + newCurRec = i->index->GetCurDbfRec(); + } + else + i->index->DeleteKey(CurRec); + i->index->TouchIndex(); + i = i->NextIx; + } + } + +#endif + + RecBuf[0] = 0x2a; + + +#ifdef XB_REAL_DELETE + if(RealDelete){ +#ifdef XB_MEMO_FIELDS + // + // Delete memo data for memo fields. + // + for(int f = 0; f < NoOfFields; f++ ) + if(GetFieldType(f) == 'M' && MemoFieldExists(f)) + UpdateMemoData(f, 0, 0, XB_LOCK); +#endif + xbase->PutULong(&RecBuf[1], FirstFreeRec); + FirstFreeRec = CurRec; + RealNumRecs--; + WriteHeader(1); + } +#endif + + if(!RealDelete){ + if( DbfStatus != XB_UPDATED ){ + DbfStatus = XB_UPDATED; + memcpy( RecBuf2, RecBuf, RecordLen ); + } + rc = PutRecord( CurRec ); + } + else + { + if(_fseek( fp, (HeaderLen+(((xbOffT)CurRec-1L)*RecordLen)), 0)) + return XB_SEEK_ERROR; + if(fwrite( RecBuf, RecordLen, 1, fp ) != 1 ) + return XB_WRITE_ERROR; + + // + // Attempt to read in the record for the current location + // in the active index. + // + CurRec = newCurRec; + if(CurRec) + rc = GetRecord(CurRec); + else + BlankRecord(); + } + +#ifdef XB_LOCKING_ON +// if(AutoLock){ +// LockDatabase( XB_UNLOCK, CurRec ); +// LockDatabase( XB_UNLOCK, 0L ); +// } + +#if defined(XB_INDEX_ANY) && defined(XB_REAL_DELETE) + i = NdxList; + while( i && AutoLock ){ +// i->index->LockIndex( XB_UNLOCK ); + i = i->NextIx; + } +#endif /* XB_INDEX_ANY */ +#endif /* XB_LOCKING_ON */ + + return rc; +} +/************************************************************************/ +//! Undelete the current record +/*! + Marks the currect record as not deleted (i.e. removes the flag indicating + the record is deleted). This method may not be used (and will return + an error code) if "real" deletes are on. + + \returns One of the following: + \htmlonly + <p> + <table border=2><tr><th>Return Code</th><th>Description</th></tr> + <tr><td>XB_NO_ERROR</td><td>No error</td></tr> + <tr><td>XB_INVALID_RECORD</td><td>Invalid record number</td></tr> + </table> + \endhtmlonly + \latexonly + \\ + \\ + \begin{tabular}{|l|l|} \hline + \textbf{Return Code} & \textbf{Description} \\ \hline \hline + XB\_NO\_ERROR & No error \\ \hline + XB\_INVALID\_RECORD & Invalid record number \\ \hline + \end{tabular} + \endlatexonly +*/ +xbShort xbDbf::UndeleteRecord() +{ + xbShort rc; + +#ifdef XB_REAL_DELETE + if(RealDelete) + return XB_INVALID_RECORD; +#endif + if( RecBuf ){ + if( DbfStatus != XB_UPDATED ){ + DbfStatus = XB_UPDATED; + memcpy( RecBuf2, RecBuf, RecordLen ); + } + + RecBuf[0] = 0x20; + if(( rc = PutRecord( CurRec )) != 0 ) + return rc; + } + else + return XB_INVALID_RECORD; + + return 0; +} +/************************************************************************/ +//! Determine if current record is deleted +/*! + \returns TRUE (1) if the current record is marked as deleted or FALSE + (0) if not. +*/ +xbShort xbDbf::RecordDeleted() +{ + if( RecBuf && RecBuf[0] == 0x2a ) + return 1; + else + return 0; +} +/************************************************************************/ +//! Create a unique file name +/*! +*/ +xbShort xbDbf::CreateUniqueDbfName( xbString & sDbfn, xbString & sDbtn ) +{ + xbShort dnf; /* directory in name flag */ + xbShort unique = 0; + xbLong l = 1; + char dbfn[13]; + char dbtn[13]; + + dnf = xbase->DirectoryExistsInName( GetFileName() ); + sprintf( dbfn, "xb%06d.dbf", l ); + sprintf( dbtn, "xb%06d.dbt", l++ ); + + if( dnf ){ + sDbfn.assign( GetFileName(), 0, dnf ); + sDbfn += dbfn; + sDbtn.assign( GetFileName(), 0, dnf ); + sDbtn += dbtn; + } else { + sDbfn = dbfn; + sDbtn = dbtn; + } + + while( !unique ){ + if( access( sDbfn.getData(), 0 ) == -1 && + access( sDbtn.getData(), 0 ) == -1 ) + unique++; + else{ + sprintf( dbfn, "xb%06d.dbf", l ); + sprintf( dbtn, "xb%06d.dbt", l++ ); + + if( dnf ){ + sDbfn.assign( GetFileName(), 0, dnf ); + sDbfn += dbfn; + sDbtn.assign( GetFileName(), 0, dnf ); + sDbtn += dbtn; + } else { + sDbfn = dbfn; + sDbtn = dbtn; + } + } + } + return 0; +} + +/************************************************************************/ +//! Pack data file +/*! +*/ +xbShort xbDbf::PackDatafiles(void (*statusFunc)(xbLong itemNum, xbLong numItems)) +{ + xbShort rc, i; + FILE *t; + xbLong l; + char *target, *source; + xbString TempDbfName; + xbString TempDbtName; + char * Buf = 0; + +#ifdef XB_MEMO_FIELDS + char tbuf[4]; +#endif + +#ifdef XB_MEMO_FIELDS + xbLong len, BufSize; + xbShort MemoFields; +#endif /* XB_MEMO_FIELDS */ + + xbDbf Temp( xbase ); + CreateUniqueDbfName( TempDbfName, TempDbtName ); + + if(( t = fopen( TempDbfName, "w+b" )) == NULL ) + return XB_OPEN_ERROR; + + /* copy file header */ + if(( rc = _fseek( fp, 0, SEEK_SET )) != 0 ) + return XB_SEEK_ERROR; + + for( i = 0; i < HeaderLen; i++ ) + fputc( fgetc( fp ), t ); + fputc( 0x1a, t ); + + if( fclose( t ) != 0 ) + return XB_CLOSE_ERROR; + +#ifdef XB_MEMO_FIELDS + if(( MemoFields = MemoFieldsPresent()) > 0 ){ + + if((t = fopen( TempDbtName, "w+b" )) == NULL) + return XB_OPEN_ERROR; + + l = 1L; + memset( tbuf, 0x00, 4 ); + xbase->PutLong( tbuf, l ); + + if((fwrite(&tbuf, 4, 1, t)) != 1) + return XB_WRITE_ERROR; + + if( MemoHeader.Version == 0x03 ){ + for( i = 0; i < 12; i++ ) fputc( 0x00, t ); + fputc( 0x03, t ); + for( i = 0; i < 495; i++ ) fputc( 0x00, t ); + } else { + for( i = 0; i < 4; i++ ) fputc( 0x00, t ); + if ((fwrite(&MemoHeader.FileName, 8, 1, t)) != 1) + return XB_WRITE_ERROR; + for( i = 0; i < 4; i++ ) fputc( 0x00, t ); + memset( tbuf, 0x00, 2 ); + xbase->PutShort( tbuf, MemoHeader.BlockSize ); + if ((fwrite(&tbuf, 2, 1, t)) != 1) + return XB_WRITE_ERROR; + + for( i = 22; i < MemoHeader.BlockSize; i++ ) fputc( 0x00, t ); + } + + if( fclose( t ) != 0 ) + return XB_CLOSE_ERROR; + } +#endif /* XB_MEMO_FIELDS */ + + /* reopen as database */ + if(( rc = Temp.OpenDatabase( TempDbfName )) != XB_NO_ERROR ) + return rc; + +#ifdef XB_REAL_DELETE + if(RealDelete) + Temp.RealDeleteOn(); + Temp.FirstFreeRec = 0; + Temp.RealNumRecs = 0; +#endif + Temp.ResetNoOfRecs(); + Temp.WriteHeader(2); // flush NoOfRecs=0 to disk + target = Temp.GetRecordBuf(); + source = GetRecordBuf(); + + for( l = 1; l <= PhysicalNoOfRecords(); l++ ){ + if(statusFunc && (l == 1 || !(l % 100) || l == PhysicalNoOfRecords())) + statusFunc(l, PhysicalNoOfRecords()); + + if(( rc = GetRecord( l )) != XB_NO_ERROR ) + return rc; + + if( !RecordDeleted() ){ + memcpy( target, source, GetRecordLen()); + +#ifdef XB_MEMO_FIELDS + BufSize = 0L; +// Buf = NULL; Already set to 0, this statement flags as memory leak + + for( i = 0; i < NoOfFields; i++ ){ + if( GetFieldType( i ) == 'M' && MemoFieldExists( i )){ + Temp.PutLongField(i, 0L); + len = GetMemoFieldLen( i ); + if( len > BufSize ){ + if( Buf ) + free( Buf ); + if((Buf = (char *)malloc(len)) == NULL) + return XB_NO_MEMORY; + BufSize = len; + } + GetMemoField( i, len, Buf, -1 ); + Temp.UpdateMemoData( i, len, Buf, -1 ); + } + } +#endif + if(( rc = Temp.AppendRecord()) != XB_NO_ERROR ){ + if(Buf) free(Buf); + return rc; + } + } + } + if( Buf ) free( Buf ); + Temp.CloseDatabase(); + + if(fclose(fp) != 0) + return XB_CLOSE_ERROR; + + if(remove(GetFileName()) != 0) + return XB_WRITE_ERROR; + + if(rename(TempDbfName, GetFileName()) != 0) + return XB_WRITE_ERROR; + +#ifdef XB_MEMO_FIELDS + if( MemoFields ){ + if(fclose(mfp) != 0) + return XB_CLOSE_ERROR; + + if(remove(MemofileName) != 0) + return XB_WRITE_ERROR; + if( rename( TempDbtName, MemofileName ) != 0 ) + return XB_WRITE_ERROR; + if(( mfp = fopen( MemofileName, "r+b" )) == NULL ) + return XB_OPEN_ERROR; + if(( rc = GetDbtHeader(1)) != 0 ){ + fclose( mfp ); + return rc; + } +#ifdef XB_LOCKING_ON + /* no buffering in multi user mode */ + setbuf( mfp, NULL ); +#endif + } + +#endif /* XB_MEMO_FIELDS */ + + if(( fp = fopen( GetFileName(), "r+b" )) == NULL ) + return XB_OPEN_ERROR; + +#ifdef XB_LOCKING_ON + /* no buffering in multi user mode */ + setbuf( fp, NULL ); +#endif + + return XB_NO_ERROR; +} +/************************************************************************/ +//! Pack the database +/*! + This method removes all records marked for deletion from an Xbase (.DBF) + file, reindexes any open index files, and also reorganizes any memo fields + stored in a .DBT memo file. + + \param packStatusFunc status function + \param indexStatusFunc index status function + + \param LockWaitOption One of the following: + \htmlonly + <p> + <table border=2><tr><th>LockWaitOption</th><th>Description</th></tr> + <tr><td>F_SETLK</td><td>Return immediately if the DBF file cannot be locked</td></tr> + <tr><td>XB_LOCK</td><td>Wait for lock on DBF file to succeed</td></tr> + </table> + \endhtmlonly + \latexonly + \\ + \\ + \begin{tabular}{|l|l|} \hline + \textbf{LockWaitOption} & \textbf{Description} \\ \hline \hline + F\_SETLK & Return immediately if DBF file cannot be locked \\ \hline + F\_SETLKW & Wait for lock on DBF file to succeed \\ \hline + \end{tabular} + \endlatexonly + + \returns One of the following return codes: + \htmlonly + <p> + <table border=2><tr><th>Return Code</th><th>Description</th></tr> + <tr><td>XB_NO_ERROR</td><td>No error</td></tr> + <tr><td>XB_CLOSE_ERROR</td><td>Unable to close intermediate work file</td></tr> + <tr><td>XB_OPEN_ERROR</td><td>Could not open file</td></tr> + <tr><td>XB_NO_MEMORY</td><td>Memory allocation error</td></tr> + <tr><td>XB_WRITE_ERROR</td><td>Couldn't write to disk</td></tr> + <tr><td>XB_SEEK_ERROR</td><td>Error seeking file</td></tr> + <tr><td>XB_LOCK_FAILED</td><td>Unable to lock file or index</td></tr> + </table> + \endhtmlonly + \latexonly + \\ + \\ + \begin{tabular}{|l|l|} \hline + \textbf{Return Code} & \textbf{Description} \\ \hline \hline + XB\_NO\_ERROR & No Error \\ \hline + XB\_CLOSE\_ERROR & Unable to close intermediate work file \\ \hline + XB\_OPEN\_ERROR & Couldn't open the file \\ \hline + XB\_NO\_MEMORY & Memory allocation error \\ \hline + XB\_WRITE\_ERROR & Couldn't write to disk \\ \hline + XB\_SEEK\_ERROR & Error seeking file \\ \hline + XB\_LOCK\_FAILED & Unable to lock file or index \\ \hline + \end{tabular} + \endlatexonly +*/ +xbShort xbDbf::PackDatabase(xbShort LockWaitOption, + void (*packStatusFunc)(xbLong itemNum, xbLong numItems), + void (*indexStatusFunc)(xbLong itemNum, xbLong numItems)) +{ + xbShort rc; + + /* lock all open files and indexes */ +// if(( rc = ExclusiveLock( LockWaitOption )) != XB_NO_ERROR ) return rc; + + if(( rc = PackDatafiles(packStatusFunc)) != XB_NO_ERROR ){ +// ExclusiveUnlock(); + return rc; + } + + /* refresh file header */ + if(( rc = ReadHeader(1)) != XB_NO_ERROR ) + return rc; + + if(( rc = RebuildAllIndices(indexStatusFunc)) != XB_NO_ERROR ) + return rc; + +// ExclusiveUnlock(); + return XB_NO_ERROR; +} +/************************************************************************/ +//! Copy DBF structure +/*! +*/ +xbShort xbDbf::CopyDbfStructure(const char *NewFileName, xbShort Overlay) { + + xbShort rc, i; + xbString ndfn; /* new dbf file name */ + char ch; + +#ifdef XB_MEMO_FIELDS + char buf[9]; + xbShort ct, NameLen; + xbString MemoName; +#endif + FILE *t; + + /* build the new file name */ + rc = NameSuffixMissing( 1, NewFileName ); + ndfn = NewFileName; + if( rc == 1 ) + ndfn += ".dbf"; + else if( rc == 2 ) + ndfn += ".DBF"; + + /* check if the file exists and Overlay is on */ + if(((t = fopen( ndfn, "r" )) != NULL ) && !Overlay) { + fclose(t); + return XB_FILE_EXISTS; + } + + /* open new file */ + if((t = fopen(ndfn, "w+b")) == NULL) + return XB_OPEN_ERROR; + + /* copy the file header */ + if(( rc = _fseek( fp, 0, SEEK_SET )) != 0 ) + return XB_SEEK_ERROR; + + fputc( fgetc( fp ), t ); + + /* do the date */ + xbDate d; + ch = d.YearOf() - 1900; + if(XFV == 3) + ch %= 100; // dBASE III+ does this, dBASE IV does not. + fputc( ch, t ); + ch = d.MonthOf(); + fputc( ch, t ); + ch = d.DayOf( XB_FMT_MONTH ); + fputc( ch, t ); + + /* record count */ + for( i = 0; i < 4; i++ ) fputc( 0x00, t ); + + if((rc = _fseek(fp, 7, SEEK_CUR)) != 0) { + fclose( t ); + return XB_SEEK_ERROR; + } + for( i = 0; i < 4; i++ ) + fputc( fgetc( fp ), t ); + + for( i = 0; i < 17; i++ ) + fputc( 0x00, t ); + + if((rc = _fseek( fp, 17, SEEK_CUR )) != 0) { + fclose( t ); + return XB_SEEK_ERROR; + } + + for( i = 29; i < HeaderLen; i++ ) + fputc( fgetc( fp ), t ); + + fputc( 0x1a, t ); + fclose( t ); + +#ifdef XB_MEMO_FIELDS + if( MemoFieldsPresent()){ + MemoName = ndfn; + + NameLen = MemoName.len(); + NameLen--; + if( MemoName.getCharacter( NameLen ) == 'F' ) + MemoName.putAt(NameLen, 'T'); + else + MemoName.putAt(NameLen, 't'); + + if(( t = fopen( MemoName, "w+b" )) == NULL ) + return XB_OPEN_ERROR; + + memset( buf, 0x00, 4 ); + xbase->PutLong( buf, 1L ); + if(( fwrite( &buf, 4, 1, t )) != 1 ){ + fclose( t ); + return XB_WRITE_ERROR; + } + if( MemoHeader.Version == 0x03 ){ + for( i = 0; i < 12; i++ ) fputc( 0x00, t ); + fputc( 0x03, t ); + for( i = 0; i < 495; i++ ) fputc( 0x00, t ); + } + else + { + for( i = 0; i < 4; i++ ) fputc( 0x00, t ); // put 4 bytes 0x00 + memset( buf, 0x00, 9 ); + NameLen = ndfn.len(); + for( i = 0, ct = 0; i < NameLen; i++ ) + if( ndfn.getCharacter( i ) == PATH_SEPARATOR ){ + ct = i; + ct++; + } + + for( i = 0; i < 8 && ndfn[i+ct] != '.'; i++ ) + buf[i] = ndfn[i+ct]; + + fwrite( &buf, 8, 1, t ); + for( i = 0; i < 4; i++ ) fputc( 0x00, t ); + memset( buf, 0x00, 2 ); + xbase->PutShort( buf, MemoHeader.BlockSize ); + if(( fwrite( &buf, 2, 1, t )) != 1 ){ + fclose(t); + return XB_WRITE_ERROR; + } + for( i = 22; i < MemoHeader.BlockSize; i++ ) fputc( 0x00, t ); + } + } + fclose( t ); +#endif // XB_MEMO_FIELDS + return XB_NO_ERROR; +} +/************************************************************************/ +//! Add index to list +/*! + Adds the specified index to the list of indexes maintained by the + dbf. + + \param n index to add + \param IndexName name of index +*/ +#if defined(XB_INDEX_ANY) +xbShort xbDbf::AddIndexToIxList(xbIndex * n, const char *IndexName) +{ + xbIxList *i, *s, *t; + + if( !FreeIxList ){ + if((i = (xbIxList *) malloc(sizeof(xbIxList))) == NULL) + return XB_NO_MEMORY; + } + else + { + i = FreeIxList; + FreeIxList = i->NextIx; + } + memset(i, 0x00, sizeof(xbIxList)); + + i->IxName = IndexName; + i->index = n; + + s = NULL; + t = NdxList; + while( t && strcmp( t->IxName, IndexName ) < 0 ){ + s = t; + t = t->NextIx; + } + i->NextIx = t; + if( s == NULL ) + NdxList = i; + else + s->NextIx = i; + return 0; +} +#endif +/************************************************************************/ +//! Rebuild all index files +/*! +*/ +xbShort xbDbf::RebuildAllIndices(void (*statusFunc)(xbLong itemNum, xbLong numItems)) +{ +#if defined(XB_INDEX_ANY) + xbShort rc; + xbIxList *n; + + n = NdxList; + while( n ){ + if(( rc = n->index->ReIndex(statusFunc)) != XB_NO_ERROR ){ +// ExclusiveUnlock(); + return rc; + } + n = n->NextIx; + } +#endif + return XB_NO_ERROR; +} +/************************************************************************/ +//! Delete all records +/*! +*/ +xbShort xbDbf::DeleteAll( xbShort Option ) +{ + xbShort rc; + + if(( NoOfRecords()) == 0 ) + return XB_NO_ERROR; + if(( rc = GetFirstRecord()) != XB_NO_ERROR ) + return rc; + + if( Option == 0 ){ /* delete all option */ + while( 1 ){ + if( !RecordDeleted()) + if(( rc = DeleteRecord()) != XB_NO_ERROR ) + return rc; + if(( rc = GetNextRecord()) != XB_NO_ERROR ) + break; + } + } + else /* undelete all option */ + { + while( 1 ){ + if( RecordDeleted()) + if(( rc = UndeleteRecord()) != XB_NO_ERROR ) + return rc; + if(( rc = GetNextRecord()) != XB_NO_ERROR ) + break; + } + } + if( rc == XB_EOF ) + return XB_NO_ERROR; + else + return rc; +} +/************************************************************************/ +//! Delete all records and pack data file +/*! +*/ +xbShort xbDbf::Zap( xbShort WaitOption ) +{ + xbShort rc; + xbString TempDbfName, TempDbtName; + + CreateUniqueDbfName( TempDbfName, TempDbtName ); + if(( rc = CopyDbfStructure( TempDbfName, 1 )) != XB_NO_ERROR) { + return rc; + } + + if( fp ){ + fclose( fp ); + fp = 0; + } + + if(( rc = remove( GetFileName() )) != 0 ) + return XB_WRITE_ERROR; + + if(( rc = rename( TempDbfName, GetFileName() )) != 0 ) + return XB_WRITE_ERROR; + + if((fp = fopen( GetFileName(), "r+b" )) == NULL) + return XB_OPEN_ERROR; + +#ifdef XB_LOCKING_ON + setbuf( fp, NULL ); +#endif + ReadHeader( 1 ); + +#ifdef XB_MEMO_FIELDS + if( MemoFieldsPresent() ){ + fclose( mfp ); + + if(( rc = remove( MemofileName )) != 0 ) + return XB_WRITE_ERROR; + + if(( rc = rename( TempDbtName, MemofileName )) != 0 ) + return XB_WRITE_ERROR; + + if(( mfp = fopen( MemofileName, "r+b" )) == NULL) + return XB_OPEN_ERROR; + + } +#endif // XB_MEMO_FIELDS + + if(( rc = RebuildAllIndices()) != XB_NO_ERROR ) + return rc; + + return XB_NO_ERROR; +} +/************************************************************************/ +//! Remove an index from the list +/*! +*/ +#if defined(XB_INDEX_ANY) +xbShort xbDbf::RemoveIndexFromIxList(xbIndex * n) { + xbIxList *i, *s; + + i = NdxList; + s = NULL; + while( i ){ + if( i->index == n ){ + /* remove it from current chain */ + if( s ) + s->NextIx = i->NextIx; + else + NdxList = i->NextIx; + + /* add i to the current free chain */ + i->NextIx = FreeIxList; + FreeIxList = i; + FreeIxList->IxName = (const char *)NULL; + FreeIxList->index = NULL; + break; + } + else + { + s = i; + i = i->NextIx; + } + } + return XB_NO_ERROR; +} +#endif + +/************************************************************************/ +//! Gets the number of records in the data file +/*! +*/ +xbLong xbDbf::NoOfRecords() +{ + xbLong numRecs; + +/* lock the database */ +#ifdef XB_LOCKING_ON + xbShort rc; + + if( AutoLock ){ +// if(( rc = LockDatabase( XB_LOCK, 0L )) != XB_NO_ERROR ) +// return rc; + + if((rc = ReadHeader(1)) != XB_NO_ERROR){ +// if(AutoLock) +// LockDatabase( XB_UNLOCK, 0L ); + return rc; + } + } +#endif + +#ifndef XB_REAL_DELETE + numRecs = NoOfRecs; +#else + numRecs = RealDelete ? RealNumRecs : NoOfRecs; +#endif + +#ifdef XB_LOCKING_ON +// if(AutoLock) +// LockDatabase( XB_UNLOCK, 0L ); +#endif + + return numRecs; +} +/************************************************************************/ +//! Get the physical number of records in the data file +/*! +*/ +xbLong xbDbf::PhysicalNoOfRecords() +{ + xbShort rc; + +/* lock the database */ +#ifdef XB_LOCKING_ON +// if( AutoLock ) +// if(( rc = LockDatabase( XB_LOCK, 0L )) != XB_NO_ERROR ) +// return rc; +#endif + + rc = ReadHeader(1); + +#ifdef XB_LOCKING_ON +// if(AutoLock) +// if(( rc = LockDatabase( XB_UNLOCK, 0L )) != XB_NO_ERROR ) +// return rc; +#endif + + if( rc ) + return rc; + + return NoOfRecs; +} + +/************************************************************************/ +#if defined(XB_INDEX_ANY) +//! Get the number of currently open indexes for data file +/*! +*/ +xbShort xbDbf::IndexCount() +{ + xbShort count; + xbIxList *i; + + for(count = 0, i = NdxList; i; i = i->NextIx, count++) ; + + return count; +} +/************************************************************************/ +//! Get a specific index +/*! +*/ +xbIndex * xbDbf::GetIndex(xbShort indexNum) +{ + xbIxList *i; + + i = NdxList; + while(indexNum && i){ + indexNum--; + i = i->NextIx; + } + + if(i) + return i->index; + + return 0; +} + +#endif // XB_INDEX_ANY +/************************************************************************/ +void xbDbf::Flush() +{ + if(fp) + fflush(fp); + +#ifdef XB_MEMO_FIELDS + if(mfp) + fflush(mfp); +#endif + +#if defined(XB_INDEX_ANY) + xbIxList + *i; + + i = NdxList; + while(i) { + i->index->Flush(); + i = i->NextIx; + } +#endif +} +/************************************************************************/ +#ifdef XB_LOCKING_ON +xbShort xbDbf::SetLockMode( xbShort nlm ) +{ +/* + xbShort rc; + if( LockMode != XB_XBASE_LOCK_MODE && + nlm == XB_XBASE_LOCK_MODE && + !xblfh ){ + rc = OpenXbLockFile(); + if( rc ) + return rc; + } +*/ + LockMode = nlm; + return XB_NO_ERROR; +} +#endif +/************************************************************************/ +const char * xbDbf::GetExtWithDot( bool lower ) +{ + return lower ? ".dbf" : ".DBF"; +} |