├── DBFEngine.pro ├── readme.md ├── LICENSE ├── main.cpp ├── dbf.h └── dbf.cpp /DBFEngine.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2013-01-30T15:01:46 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += core 8 | 9 | QT -= gui 10 | 11 | TARGET = DBFEngine 12 | CONFIG += console 13 | CONFIG -= app_bundle 14 | 15 | TEMPLATE = app 16 | 17 | 18 | SOURCES += main.cpp \ 19 | dbf.cpp 20 | 21 | HEADERS += \ 22 | dbf.h 23 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | This is a simple program that allows people to read and write FoxPro files with the .bdf file extension. 2 | This is NOT a complete implementation, it was done without ANY official specifications and was done by reverse engineering a file with a hex editor to a large degree. It currently supports numbers,integers, floats, doubles, and character strings and logicals. It will treat all unhandled data types as if they are character fields. 3 | 4 | I used the QtCreator development tool to build this project, but it is not dependent on Qt, it is just plain ansi c++, so any compiler should work fine. 5 | I only used the QtCreator because I prefer it as my c++ IDE. 6 | The purpose of the project is not to provide a compiled binary, but a c++ and h file to include in your own projects. 7 | 8 | see http://www.ostafichuk.com for more information 9 | 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2012 Ron Ostafichuk 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 4 | // (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, 5 | // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 6 | // furnished to do so, subject to the following conditions: 7 | // 8 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | // 10 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 11 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 12 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 13 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "dbf.h" 2 | 3 | using namespace std; 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | 8 | std::cout << "Test of DBFEngine Started"<< std::endl; 9 | 10 | // any param is assumed to be a filename to do a read test and dump to std output 11 | if( argc > 1 ) 12 | { 13 | string sFileReadTest = "test.dbf"; 14 | sFileReadTest = argv[1]; // do a read test on the given file 15 | 16 | DBF mydbf; 17 | std::cerr << "Read Test of " << sFileReadTest << std::endl; 18 | int nRet = mydbf.open(sFileReadTest); 19 | if( nRet ) 20 | { 21 | std::cerr << "Unable to Open File " << sFileReadTest << std::endl; 22 | } else 23 | { 24 | int nRecs = mydbf.GetNumRecords(); 25 | int nFields = mydbf.GetNumFields(); 26 | std::cout << "Open() found " << nRecs << " records, and " << nFields << " fields." << std::endl; 27 | mydbf.dumpAsCSV(); 28 | std::cout << "Done Read Test " << std::endl; 29 | 30 | mydbf.close(); 31 | } 32 | } else 33 | { 34 | // no param, means to run a test to create, read and delete a record in the dbf 35 | 36 | // now create a db 37 | std::cout << "Create file TestCreate.dbf from code" << std::endl; 38 | DBF newdbf; 39 | int nRet = newdbf.create("TestCreate.dbf",5); 40 | if( nRet == 0 ) 41 | { 42 | // create the 5 fields 43 | fieldDefinition fd; 44 | strncpy(fd.cFieldName,"ID",10); 45 | fd.cFieldType='I'; 46 | fd.FieldFlags=0; 47 | fd.uAutoIncrementStep=0; 48 | fd.uFieldOffset=0; 49 | fd.uLength=4; 50 | fd.uNumberOfDecimalPlaces=0; 51 | newdbf.assignField(fd,0); 52 | 53 | strncpy(fd.cFieldName,"FirstName",10); 54 | fd.cFieldType='C'; 55 | fd.FieldFlags=0; 56 | fd.uAutoIncrementStep=0; 57 | fd.uFieldOffset=0; 58 | fd.uLength=20; 59 | fd.uNumberOfDecimalPlaces=0; 60 | newdbf.assignField(fd,1); 61 | 62 | strncpy(fd.cFieldName,"Weight",10); 63 | fd.cFieldType='F'; 64 | fd.FieldFlags=0; 65 | fd.uAutoIncrementStep=0; 66 | fd.uFieldOffset=0; 67 | fd.uLength=12; 68 | fd.uNumberOfDecimalPlaces=0; 69 | newdbf.assignField(fd,2); 70 | 71 | strncpy(fd.cFieldName,"Age",10); 72 | fd.cFieldType='N'; 73 | fd.FieldFlags=0; 74 | fd.uAutoIncrementStep=0; 75 | fd.uFieldOffset=0; 76 | fd.uLength=3; 77 | fd.uNumberOfDecimalPlaces=0; 78 | newdbf.assignField(fd,3); 79 | 80 | strncpy(fd.cFieldName,"Married",10); 81 | fd.cFieldType='L'; 82 | fd.FieldFlags=0; 83 | fd.uAutoIncrementStep=0; 84 | fd.uFieldOffset=0; 85 | fd.uLength=1; 86 | fd.uNumberOfDecimalPlaces=0; 87 | newdbf.assignField(fd,4); 88 | 89 | 90 | // now create some records to test it! 91 | string s1[5]= {"1" ,"Ric G","210.123456789123456","43","T"}; 92 | newdbf.appendRecord(&s1[0],5); 93 | 94 | string s2[5]={"1000" ,"Paul F","196.2","33","T"}; 95 | newdbf.appendRecord(s2,5); 96 | 97 | string s3[5]={"20000" ,"Dean K","186.1","23","F"}; 98 | newdbf.appendRecord(s3,5); 99 | 100 | string s4[5]={"300000" ,"Gary\" Q","175.123456789","13","F"}; 101 | newdbf.appendRecord(s4,5); 102 | 103 | string s5[5]={"2000000" ,"Dan'e \"D, with comma and over sized field that will be truncated","65.2","6","F"}; 104 | newdbf.appendRecord(s5,5); 105 | 106 | // now add a huge pile of random records to see if it crashes 107 | int nID = 2000001; 108 | for( int i=0; i < 1000; i++) 109 | { 110 | stringstream ssName; 111 | ssName << "FirstName" << nID; 112 | 113 | float fWeight = (float) nID / 40000.0; 114 | int nAge = i % 120; 115 | 116 | 117 | stringstream ssID; 118 | ssID << nID; 119 | 120 | stringstream ssWeight; 121 | ssWeight << fWeight; 122 | 123 | stringstream ssAge; 124 | ssAge << nAge; 125 | 126 | string sMarried = "F"; 127 | if( nID % 2 == 0 ) 128 | sMarried = "T"; 129 | 130 | string sRec[5]={ssID.str(),ssName.str(),ssWeight.str(),ssAge.str(),sMarried}; 131 | newdbf.appendRecord(sRec,5); 132 | 133 | nID++; 134 | } 135 | 136 | 137 | 138 | newdbf.close(); 139 | 140 | std::cout << "Done Creating the DBF" << std::endl; 141 | 142 | std::cout << "Open the DBF for reading and writing" << std::endl; 143 | // now read the dbf back to test that it worked! 144 | DBF readTest; 145 | readTest.open("TestCreate.dbf",true); 146 | int nRecs = readTest.GetNumRecords(); 147 | int nFields = readTest.GetNumFields(); 148 | std::cout << "Open() found " << nRecs << " records, and " << nFields << " fields." << std::endl; 149 | readTest.dumpAsCSV(); 150 | std::cout << "Done Reading a freshly created DBF! " << std::endl; 151 | 152 | std::cout << "Test Delete Record 1 in DBF! " << std::endl; 153 | std::cout << "Test Delete Record 999 in DBF! " << std::endl; 154 | readTest.markAsDeleted(1); 155 | readTest.markAsDeleted(999); 156 | readTest.dumpAsCSV(); 157 | std::cout << "Done Test Delete Record DBF! " << std::endl; 158 | 159 | readTest.close(); 160 | } 161 | } 162 | return 0; 163 | } 164 | -------------------------------------------------------------------------------- /dbf.h: -------------------------------------------------------------------------------- 1 | #ifndef DBF_H 2 | #define DBF_H 3 | 4 | // Copyright (C) 2012 Ron Ostafichuk 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 7 | // (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, 8 | // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 14 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 15 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 16 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | using namespace std; 27 | 28 | typedef unsigned char uint8; 29 | typedef short int uint16; 30 | typedef int uint32; 31 | 32 | #define MAX_FIELDS 255 33 | #define DBF_DELETED_RECORD_FLAG '*' // found by reading with hex editor 34 | #define MAX_RECORD_SIZE 0xffff*50 // not idea if this is correct, but good enough for my needs 35 | 36 | struct fileHeader 37 | { 38 | uint8 u8FileType; 39 | uint8 u8LastUpdateYear; 40 | uint8 u8LastUpdateMonth; 41 | uint8 u8LastUpdateDay; 42 | uint32 uRecordsInFile; 43 | uint16 uPositionOfFirstRecord; 44 | uint16 uRecordLength; // includes the delete flag (byte) at start of record 45 | uint8 Reserved16[4*4]; // 16 bytes reserved, must always be set to zeros 46 | uint8 uTableFlags; 47 | uint8 uCodePage; 48 | uint8 Reserved2[2]; // 2 bytes reserved, must put zeros in all reserved fields 49 | }; 50 | 51 | // after the file header, we can have n field definition records, terminated by the byte 0x0D 52 | // must build as a union to make sure bytes are aligned correctly 53 | struct fieldDefinition 54 | { 55 | char cFieldName[11]; 56 | char cFieldType; 57 | uint32 uFieldOffset; // location of field from start of record 58 | uint8 uLength; // Length of Field in bytes 59 | uint8 uNumberOfDecimalPlaces; 60 | uint8 FieldFlags; 61 | uint8 uNextAutoIncrementValue[4]; // 32 bit int 62 | uint8 uAutoIncrementStep; 63 | uint8 Reserved8[8]; // should always be zero 64 | }; 65 | // terminated by the byte 0x0D then 263 bytes of 0x00 66 | // then the records start 67 | 68 | 69 | class DBF 70 | { 71 | public: 72 | DBF(); 73 | ~DBF(); 74 | 75 | int open(string sFileName,bool bAllowWrite=false); // open an existing dbf file 76 | int close(); 77 | 78 | int markAsDeleted(int nRecord); // mark this record as deleted 79 | int create(string sFileName,int nNumFields); // create a new dbf file with space for nNumFields 80 | int assignField(fieldDefinition myFieldDef,int nField); // used to assign the field info ONLY if num records in file = 0 !!! 81 | int appendRecord(string *sValues, int nNumValues); // used to append records to the end of the dbf file 82 | 83 | int getFieldIndex(string sFieldName); 84 | int loadRec(int nRecord); // load the record into memory 85 | bool isRecordDeleted(); // check if loaded record is deleted 86 | string readField(int nField); // read the request field as a string always from the loaded record! 87 | double readFieldAsDouble(int nField); // read the request field as a double to get higher performance for 'B' type fields only! 88 | 89 | void dumpAsCSV(); // output fields and records as csv to std output 90 | 91 | int GetNumRecords() 92 | { 93 | return m_FileHeader.uRecordsInFile; 94 | } 95 | int GetNumFields() 96 | { 97 | return m_nNumFields; 98 | } 99 | string GetFieldName(int nField) 100 | { 101 | return string(m_FieldDefinitions[nField].cFieldName); 102 | } 103 | 104 | string convertInt(int number) 105 | { 106 | stringstream ss;//create a stringstream 107 | ss << number;//add number to the stream 108 | return ss.str();//return a string with the contents of the stream 109 | } 110 | 111 | string convertNumber(uint8 *n, int nSize) 112 | { 113 | // convert any size of number (represented by n[] ) into a string 114 | long long nResult = 0; 115 | for( int i = 0 ; i < nSize ; i++ ) 116 | { 117 | nResult += (((unsigned long long) n[i]) << (i*8) ); 118 | } 119 | stringstream ss; //create a stringstream 120 | ss << nResult; //add number to the stream 121 | return ss.str(); //return a string with the contents of the stream 122 | } 123 | 124 | int ConvertStringToInt(string sInteger,int nSize, char *cRecord) 125 | { 126 | // convert the given string into an integer of nSize bytes (2 or 4 or 8 only) 127 | if( nSize == 2 ) 128 | { 129 | union { 130 | short int i; 131 | uint8 n[4]; 132 | } u; 133 | stringstream ss; 134 | ss << sInteger; 135 | ss >> u.i; 136 | 137 | for( int i = 0 ; i < nSize ; i++ ) 138 | cRecord[i] = u.n[i]; 139 | 140 | return 0; 141 | } 142 | else if( nSize == 4 ) 143 | { 144 | union { 145 | int i; 146 | uint8 n[4]; 147 | } u; 148 | stringstream ss; 149 | ss << sInteger; 150 | ss >> u.i; 151 | 152 | for( int i = 0 ; i < nSize ; i++ ) 153 | cRecord[i] = u.n[i]; 154 | 155 | return 0; 156 | } 157 | else if( nSize ==8 ) 158 | { 159 | union { 160 | long i; 161 | uint8 n[8]; 162 | } u; 163 | stringstream ss; 164 | ss << sInteger; 165 | ss >> u.i; 166 | 167 | for( int i = 0 ; i < nSize ; i++ ) 168 | cRecord[i] = u.n[i]; 169 | 170 | return 0; 171 | } 172 | 173 | // fail, clear the record 174 | for( int i = 0 ; i < nSize ; i++ ) 175 | cRecord[i] = 0; 176 | return 1; // fail 177 | } 178 | 179 | int ConvertStringToFloat(string sFloat,int nSize, char *cRecord) 180 | { 181 | // convert the given string into a float or a double 182 | if( nSize == 4 ) 183 | { 184 | union { 185 | float f; 186 | uint8 n[4]; 187 | } u; 188 | stringstream ss; 189 | ss.precision(8); // ensure string conversion maintains single precision 190 | ss << sFloat; 191 | ss >> u.f; 192 | 193 | for( int i = 0 ; i < nSize ; i++ ) 194 | cRecord[i] = u.n[i]; 195 | 196 | return 0; 197 | } else if( nSize == 8 ) 198 | { 199 | union { 200 | double d; 201 | uint8 n[8]; 202 | } u; 203 | stringstream ss; 204 | ss.precision(17); // ensure string conversion maintains double precision 205 | ss << sFloat; 206 | ss >> u.d; 207 | 208 | for( int i = 0 ; i < nSize ; i++ ) 209 | cRecord[i] = u.n[i]; 210 | return 0; 211 | } 212 | 213 | // fail, clear the record 214 | for( int i = 0 ; i < nSize ; i++ ) 215 | cRecord[i] = 0; 216 | return 1; // fail 217 | } 218 | 219 | private: 220 | FILE * m_pFileHandle; 221 | string m_sFileName; 222 | 223 | bool m_bStructSizesOK; // this must be true for engine to work! 224 | bool m_bAllowWrite; 225 | fileHeader m_FileHeader; 226 | fieldDefinition m_FieldDefinitions[MAX_FIELDS]; // allow a max of 255 fields 227 | int m_nNumFields; // number of fields in use 228 | 229 | int updateFileHeader(); 230 | 231 | char *m_pRecord; 232 | 233 | }; 234 | 235 | #endif // DBF_H 236 | -------------------------------------------------------------------------------- /dbf.cpp: -------------------------------------------------------------------------------- 1 | #include "dbf.h" 2 | 3 | // Copyright (C) 2012 Ron Ostafichuk 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 6 | // (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, 7 | // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | // 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 13 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 14 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 15 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | DBF::DBF() 18 | { 19 | m_pFileHandle = NULL; 20 | m_nNumFields = 0; 21 | m_bAllowWrite = false; 22 | m_bStructSizesOK = true; 23 | if( sizeof( fileHeader ) != 32 ) 24 | { 25 | std::cerr << __FUNCTION__ << " fileHeader Structure is padded and will not work! Must be 32, but is " << sizeof( fileHeader ) << std::endl; 26 | m_bStructSizesOK = false; 27 | } 28 | if( sizeof( fieldDefinition ) != 32 ) 29 | { 30 | std::cerr << __FUNCTION__ << " fieldDefinition Structure is padded and will not work! Must be 32, but is " << sizeof( fieldDefinition ) << std::endl; 31 | m_bStructSizesOK = false; 32 | } 33 | 34 | m_pRecord = new char[MAX_RECORD_SIZE]; 35 | } 36 | 37 | DBF::~DBF() 38 | { 39 | if( m_pFileHandle != NULL ) 40 | fclose(m_pFileHandle); 41 | 42 | m_pFileHandle = NULL; 43 | delete [] m_pRecord; 44 | } 45 | 46 | int DBF::open(string sFileName,bool bAllowWrite) 47 | { 48 | // open a dbf file for reading only 49 | m_sFileName = sFileName; 50 | if( bAllowWrite && !m_bStructSizesOK ) 51 | bAllowWrite = false; // DO NOT WRITE IF ENGINE IS NOT COMPILED PROPERLY! 52 | m_bAllowWrite = bAllowWrite; 53 | 54 | char cMode[10] = "rb"; // for windows we MUST open in binary mode ALWAYS!!! Linux does not care 55 | if( m_bAllowWrite ) 56 | strncpy(cMode,"rb+",3); // change to read write mode 57 | 58 | m_pFileHandle = fopen(sFileName.c_str(),cMode); 59 | if( m_pFileHandle == NULL ) 60 | { 61 | std::cerr << __FUNCTION__ << " Unable to open file " << sFileName << std::endl; 62 | return errno; 63 | } 64 | 65 | // open is ok, so read in the File Header 66 | 67 | int nBytesRead = fread (&m_FileHeader,1,32,m_pFileHandle); 68 | if( nBytesRead != 32 ) 69 | { 70 | std::cerr << __FUNCTION__ << " Bad read for Header, wanted 32, got " << nBytesRead << std::endl; 71 | return 1; // fail 72 | } 73 | 74 | std::cout << "Header: Type=" << m_FileHeader.u8FileType << std::endl 75 | << " Last Update=" << (int) m_FileHeader.u8LastUpdateDay << "/" << (int) m_FileHeader.u8LastUpdateMonth << "/" << (int) m_FileHeader.u8LastUpdateYear << std::endl 76 | << " Num Recs=" << m_FileHeader.uRecordsInFile << std::endl 77 | << " Rec0 position=" << m_FileHeader.uPositionOfFirstRecord << std::endl 78 | << " Rec length=" << m_FileHeader.uRecordLength << std::endl 79 | << " CodePage=" << (int) m_FileHeader.uCodePage << std::endl 80 | << " TableFlags=" << (int) m_FileHeader.uTableFlags << std::endl; 81 | 82 | m_nNumFields = 0; 83 | // now read in all the field definitions 84 | std::cout << "Fields: " << std::endl; 85 | do 86 | { 87 | int nBytesRead = fread(&(m_FieldDefinitions[m_nNumFields]),1,32,m_pFileHandle); 88 | if( nBytesRead != 32 ) 89 | { 90 | std::cerr << __FUNCTION__ << " Bad read for Field, wanted 32, got " << nBytesRead << std::endl; 91 | return 1; 92 | } 93 | 94 | if( m_FieldDefinitions[m_nNumFields].cFieldName[0] == 0x0D || strlen(m_FieldDefinitions[m_nNumFields].cFieldName) <= 1 ) 95 | { 96 | // end of fields 97 | break; 98 | } 99 | // show field in std out 100 | std::cout << " " << m_FieldDefinitions[m_nNumFields].cFieldName << ", Type=" << m_FieldDefinitions[m_nNumFields].cFieldType 101 | << ", Offset=" << (int) m_FieldDefinitions[m_nNumFields].uFieldOffset << ", len=" << (int) m_FieldDefinitions[m_nNumFields].uLength 102 | << ", Dec=" << (int) m_FieldDefinitions[m_nNumFields].uNumberOfDecimalPlaces << ", Flag=" << (int) m_FieldDefinitions[m_nNumFields].FieldFlags << std::endl; 103 | 104 | m_nNumFields++; 105 | }while(!feof(m_pFileHandle)); 106 | 107 | // recalculate field offsets, some Files can have incorrect offsets! 108 | uint32 uFieldOffset = 1; 109 | for( int i=0;itm_year % 100; // convert yr to 2 digits 386 | m_FileHeader.u8LastUpdateDay = timePtr->tm_mday; 387 | m_FileHeader.u8LastUpdateMonth = timePtr->tm_mon+1; 388 | m_FileHeader.u8LastUpdateYear = nYear; 389 | m_FileHeader.uCodePage = 0; // copied from another db, no idea what this corresponds to 390 | m_FileHeader.uPositionOfFirstRecord = 32+32*nNumFields+264; // calculated based on the file header size plus the n*FieldDef size + 1 term char + 263 zeros 391 | m_FileHeader.uRecordLength = 0; 392 | m_FileHeader.uRecordsInFile = 0; 393 | m_FileHeader.uTableFlags = 0; // bit fields, copied from another db , 0x01=has a .cdx?, 0x02=Has Memo Fields, 0x04=is a .dbc? 394 | 395 | // write the File Header for the first time! 396 | fwrite(&m_FileHeader,1,sizeof(m_FileHeader),m_pFileHandle); 397 | 398 | // now write dummy field definition records until the real ones can be specified 399 | for( int i = 0; i < nNumFields ; i++ ) 400 | { 401 | for( int j = 0; j < 11 ; j++ ) 402 | m_FieldDefinitions[i].cFieldName[j]=0; // clear entire name 403 | m_FieldDefinitions[i].cFieldType='C'; 404 | m_FieldDefinitions[i].FieldFlags=0; 405 | m_FieldDefinitions[i].uAutoIncrementStep=0; 406 | m_FieldDefinitions[i].uNextAutoIncrementValue[0]=0; 407 | m_FieldDefinitions[i].uNextAutoIncrementValue[1]=0; 408 | m_FieldDefinitions[i].uNextAutoIncrementValue[2]=0; 409 | m_FieldDefinitions[i].uNextAutoIncrementValue[3]=0; 410 | m_FieldDefinitions[i].uLength=0; 411 | m_FieldDefinitions[i].uNumberOfDecimalPlaces=0; 412 | m_FieldDefinitions[i].Reserved8[i]=0; 413 | 414 | // write the definitions 415 | fwrite(&m_FieldDefinitions[i],1,sizeof(fieldDefinition),m_pFileHandle); 416 | } 417 | // write the field definition termination character 418 | char FieldDefTermination[2]; 419 | FieldDefTermination[0] = 0x0D; 420 | FieldDefTermination[1] = 0; 421 | 422 | fwrite(FieldDefTermination,1,1,m_pFileHandle); 423 | // write the 263 bytes of 0 424 | char cZero[263]; 425 | for( int j=0; j<263;j++) 426 | cZero[j]=0; 427 | 428 | fwrite(&cZero[0],1,263,m_pFileHandle); 429 | // this is now the starting point for the first record 430 | // ready to assign the field definitions! 431 | 432 | // make sure change is made permanent, we are not looking for speed, just reliability and compatibility 433 | fflush(m_pFileHandle); 434 | 435 | return 0; 436 | } 437 | 438 | int DBF::updateFileHeader() 439 | { 440 | // move to file start 441 | int nRes = fseek(m_pFileHandle,0,SEEK_SET); 442 | if( nRes != 0) 443 | return 1; //fail 444 | 445 | // update the last modified date 446 | time_t t = time(NULL); 447 | tm* timePtr = localtime(&t); 448 | int nYear = timePtr->tm_year % 100; // convert yr to 2 digits 449 | m_FileHeader.u8LastUpdateDay = timePtr->tm_mday; 450 | m_FileHeader.u8LastUpdateMonth = timePtr->tm_mon+1; 451 | m_FileHeader.u8LastUpdateYear = nYear; 452 | 453 | // write the current header info 454 | int nBytesWritten = fwrite(&m_FileHeader,1,sizeof(m_FileHeader),m_pFileHandle); 455 | if( nBytesWritten != sizeof(m_FileHeader) ) 456 | { 457 | // error! 458 | std::cerr << __FUNCTION__ << " Failed to update header!" << std::endl; 459 | return 1; 460 | } 461 | return 0; 462 | } 463 | 464 | int DBF::assignField(fieldDefinition fd,int nField) 465 | { 466 | // used to assign the field info ONLY if num records in file = 0 !!! 467 | if( m_FileHeader.uRecordsInFile != 0) 468 | { 469 | std::cerr << __FUNCTION__ << " Failed to AssignField Can not change Fields once the File has records in it!" << std::endl; 470 | return 1; // fail 471 | } 472 | 473 | // set the unused characters for the field name to zero 474 | int nPos = strlen(fd.cFieldName); 475 | for( int i=nPos; i < 11 ; i++ ) 476 | fd.cFieldName[i]=0; 477 | 478 | // this engine does not support auto increment, set it to zero 479 | fd.uAutoIncrementStep=0; 480 | fd.uNextAutoIncrementValue[0]=0; 481 | fd.uNextAutoIncrementValue[1]=0; 482 | fd.uNextAutoIncrementValue[2]=0; 483 | fd.uNextAutoIncrementValue[3]=0; 484 | 485 | for( int i=0; i<8;i++) 486 | fd.Reserved8[i] = 0; // must always be set to zeros! 487 | 488 | // add some rules to prevent creation of invalid db. 489 | if( fd.cFieldType=='I' ) 490 | { 491 | fd.uLength = 4; 492 | }else if( fd.cFieldType=='B' ) 493 | { 494 | fd.uLength = 8; // actual double, not text! 495 | }else if( fd.cFieldType=='L' ) 496 | { 497 | fd.uLength = 1; 498 | } else 499 | { 500 | //default case 501 | if( fd.uLength < 1 ) 502 | fd.uLength=1; 503 | } 504 | 505 | // calculate the proper field offset based on corrected data 506 | if( nField == 0 ) 507 | fd.uFieldOffset = 1; 508 | else 509 | { 510 | fd.uFieldOffset = 1; 511 | for( int i=0;i 0 ) 574 | std::cerr << "Unable to convert '" << sFieldValue << "' to int " 575 | << m_FieldDefinitions[f].uLength << " bytes" << std::endl; 576 | } 577 | else if( cType== 'B' ) 578 | { 579 | // float or double 580 | int res = ConvertStringToFloat(sFieldValue,m_FieldDefinitions[f].uLength,&m_pRecord[m_FieldDefinitions[f].uFieldOffset]); 581 | if( res > 0 ) 582 | { 583 | std::cerr << "Unable to convert '" << sFieldValue << "' to float " 584 | << m_FieldDefinitions[f].uLength << " bytes" << std::endl; 585 | } 586 | } 587 | else if( cType== 'L' ) 588 | { 589 | // logical 590 | if( sFieldValue=="T" || sFieldValue=="TRUE" ) 591 | m_pRecord[m_FieldDefinitions[f].uFieldOffset] = 'T'; 592 | else if( sFieldValue=="?") 593 | m_pRecord[m_FieldDefinitions[f].uFieldOffset] = '?'; 594 | else 595 | m_pRecord[m_FieldDefinitions[f].uFieldOffset] = 'F'; 596 | } else 597 | { 598 | // default for character type fields (and all unhandled field types) 599 | for( unsigned int j=0;j 0 ; i-- ) 702 | { 703 | if( s[i] == ' ' ) 704 | s.erase(i,1); 705 | else 706 | break; // done 707 | } 708 | // trim left spaces 709 | for( int i = 0 ; i < s.length() ; i++ ) 710 | { 711 | if( s[i] == ' ' ) 712 | { 713 | s.erase(i,1); 714 | i--; 715 | } 716 | else 717 | break; // done 718 | } 719 | 720 | int nFind = s.find(","); 721 | if( nFind > -1 ) 722 | { 723 | // put string in double quotes! 724 | // need quotes (make sure string also does not have double quotes, NOT DONE!) 725 | nFind = s.find("\""); 726 | while( nFind > -1 ) 727 | { 728 | s[nFind] = '\''; // convert double quote(34) to single quote to prevent errors reading this csv 729 | nFind = s.find("\""); 730 | } 731 | std::cout << ",\"" << s << "\""; 732 | } 733 | else 734 | std::cout << "," << s; 735 | } 736 | std::cout << std::endl; 737 | } 738 | } 739 | --------------------------------------------------------------------------------