File indexing completed on 2025-01-19 13:27:14
0001 /* This file is part of the KDE project 0002 Copyright (C) 2002 by Thomas Franke and Andreas Pietzowski <andreas@pietzowski.de> 0003 Ariya Hidayat <ariyahidayat@yahoo.de> 0004 0005 This library is free software; you can redistribute it and/or 0006 modify it under the terms of the GNU Library General Public 0007 License as published by the Free Software Foundation; either 0008 version 2 of the License, or (at your option) any later version. 0009 0010 This library is distributed in the hope that it will be useful, 0011 but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0013 Library General Public License for more details. 0014 0015 You should have received a copy of the GNU Library General Public License 0016 along with this library; see the file COPYING.LIB. If not, write to 0017 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0018 * Boston, MA 02110-1301, USA. 0019 */ 0020 0021 #include "dbase.h" 0022 0023 #include <QDateTime> 0024 #include <QDataStream> 0025 #include <QFile> 0026 #include <QString> 0027 #include <QStringList> 0028 0029 DBase::DBase(): m_recordCount(0) 0030 { 0031 } 0032 0033 DBase::~DBase() 0034 { 0035 while (!fields.isEmpty()) delete fields.takeFirst(); 0036 close(); 0037 } 0038 0039 // Headerdefinition in dBASE 0040 // 0041 // Type char Content 0042 // 0043 // unsigned char version 0 dBASE-Version (3) 0044 // unsigned char last_update[3] 1-3 Date of last update 0045 // unsigned long records 4-7 Number of records 0046 // unsigned short header_length 8-9 headerlength 0047 // unsigned short record_length 10-11 recordlength 0048 // unsigned char reserved[20] 12-31 reserved info from dBase 0049 // 0050 0051 bool DBase::load(const QString& filename) 0052 { 0053 0054 m_file.setFileName(filename); 0055 if (!m_file.open(QIODevice::ReadOnly)) 0056 return false; 0057 0058 m_stream.setDevice(&m_file); 0059 m_stream.setByteOrder(QDataStream::LittleEndian); 0060 0061 unsigned filesize = m_file.size(); 0062 0063 // read dBASE version 0064 quint8 ver; 0065 m_stream >> ver; 0066 m_version = ver & 0x7f; // bit 7: has memo ? 0067 0068 // only dBASE V.3 is supported 0069 if (m_version != 3) 0070 return false; 0071 0072 // date of last update 0073 quint8 y, m, d; 0074 m_stream >> y >> m >> d; 0075 // because dBASE saves 102 instead of 2002 (very Y2K-save ;-) 0076 m_lastUpdate.setYMD(y + 1900, m, d); 0077 0078 // check for valid date 0079 if (!m_lastUpdate.isValid()) return false; 0080 0081 // number of records 0082 quint32 norec; 0083 m_stream >> norec; 0084 m_recordCount = norec; 0085 0086 // header-length 0087 quint16 header_length; 0088 m_stream >> header_length; 0089 m_headerLength = header_length; 0090 0091 // record-length 0092 quint16 record_length; 0093 m_stream >> record_length; 0094 m_recordLength = record_length; 0095 0096 // read the remaining chars 0097 quint8 dummy; 0098 for (int foo = 0; foo < 20; ++foo) 0099 m_stream >> dummy; 0100 0101 // size of file must match 0102 if (filesize < m_headerLength + m_recordLength * m_recordCount) 0103 return false; 0104 0105 // Now read the headers of the columns and their type 0106 0107 // Type char Content 0108 // 0109 // unsigned char field_name[11] 0-10 Fieldname 0110 // unsigned char field_type 11 Fieldtype 0111 // unsigned long field_address 12-15 Fielddataaddress 0112 // unsigned char field_length 16 Fieldlength 0113 // unsigned char field_decimals 17 decimals 0114 // unsigned char reserved[14] 18-31 reserved for internal dBASE-stuff 0115 0116 while (!fields.isEmpty()) delete fields.takeFirst(); 0117 for (unsigned i = 1; i < m_headerLength / 32; ++i) { 0118 DBaseField* field = new DBaseField; 0119 0120 // column-name 0121 quint8 colname[12]; 0122 for (int j = 0; j < 11; ++j) 0123 m_stream >> colname[j]; 0124 colname[11] = '\0'; 0125 field->name = QString((const char*) & colname[0]); 0126 0127 // type of column 0128 quint8 coltype; 0129 m_stream >> coltype; 0130 switch (coltype) { 0131 case 'C': field->type = DBaseField::Character; break; 0132 case 'N': field->type = DBaseField::Numeric; break; 0133 case 'D': field->type = DBaseField::Date; break; 0134 case 'M': field->type = DBaseField::Memo; break; 0135 case 'L': field->type = DBaseField::Logical; break; 0136 default: field->type = DBaseField::Unknown; break; 0137 } 0138 0139 // fileddataaddress 0140 quint32 addr; 0141 m_stream >> addr; 0142 0143 // columnlength 0144 quint8 colsize; 0145 m_stream >> colsize; 0146 field->length = colsize; 0147 0148 // decimals 0149 quint8 decimals; 0150 m_stream >> decimals; 0151 field->decimals = decimals; 0152 0153 // read remaining chars 0154 quint8 dummy; 0155 for (int foo = 0; foo < 14; ++foo) 0156 m_stream >> dummy; 0157 0158 // now append 0159 fields.append(field); 0160 } 0161 0162 // set the index to the first record 0163 m_stream.device()->seek(m_headerLength); 0164 0165 return true; 0166 } 0167 0168 QStringList DBase::readRecord(unsigned recno) 0169 { 0170 QStringList result; 0171 0172 // out of range ? return empty strings 0173 if (recno >= m_recordCount) { 0174 for (int i = 0; i < fields.count(); i++) 0175 result.append(""); 0176 return result; 0177 } 0178 0179 // seek to where the record is 0180 qint64 filepos = m_headerLength + recno * m_recordLength; 0181 m_stream.device()->seek(filepos); 0182 0183 // first char == '*' means the record is deleted 0184 // so we just skip it 0185 quint8 delmarker; 0186 m_stream >> delmarker; 0187 if (delmarker == 0x2a) 0188 return result; 0189 0190 // load it 0191 for (int i = 0; i < fields.count(); ++i) 0192 switch (fields.at(i)->type) { 0193 // Numeric or Character 0194 case DBaseField::Numeric: 0195 case DBaseField::Character: { 0196 QString str; 0197 quint8 ch; 0198 for (unsigned j = 0; j < fields.at(i)->length; ++j) { 0199 m_stream >> ch; str += QChar(ch); 0200 } 0201 result.append(str); 0202 } 0203 break; 0204 0205 // Logical 0206 case DBaseField::Logical: { 0207 quint8 ch; 0208 m_stream >> ch; 0209 switch (ch) { 0210 case 'Y': case 'y': case 'T': case 't': result.append("True"); break; 0211 case 'N': case 'n': case 'F': case 'f': result.append("False"); break; 0212 default: result.append(""); break; 0213 } 0214 } 0215 break; 0216 0217 // Date, stored as YYYYMMDD 0218 // Note: convert it to YYYY-MM-DD 0219 case DBaseField::Date: { 0220 QString str; 0221 quint8 ch; 0222 for (unsigned j = 0; j < fields.at(i)->length; j++) { 0223 m_stream >> ch; str += QChar(ch); 0224 } 0225 str.insert(6, '-'); 0226 str.insert(4, '-'); 0227 result.append(str); 0228 } 0229 break; 0230 0231 // Unknown/Unimplemented 0232 case DBaseField::Unknown: 0233 case DBaseField::Memo: 0234 default: 0235 result.append(""); // unknown 0236 break; 0237 } 0238 0239 return result; 0240 } 0241 0242 void DBase::close() 0243 { 0244 if (m_file.isOpen()) m_file.close(); 0245 }