filters

dbase.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2002 by Thomas Franke and Andreas Pietzowski <andreas@pietzowski.de>
3 Ariya Hidayat <ariyahidayat@yahoo.de>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19*/
20
21
22#include <tqdatetime.h>
23#include <tqdatastream.h>
24#include <tqfile.h>
25#include <tqstring.h>
26#include <tqstringlist.h>
27#include <tqptrlist.h>
28
29#include <dbase.h>
30
31DBase::DBase(): m_recordCount( 0 )
32{
33 fields.setAutoDelete( true );
34}
35
36DBase::~DBase()
37{
38 fields.clear();
39 close();
40}
41
42 // Headerdefinition in dBASE
43 //
44 // Type char Content
45 //
46 // unsigned char version 0 dBASE-Version (3)
47 // unsigned char last_update[3] 1-3 Date of last update
48 // unsigned long records 4-7 Number of records
49 // unsigned short header_length 8-9 headerlength
50 // unsigned short record_length 10-11 recordlength
51 // unsigned char reserved[20] 12-31 reserverd info from dBase
52 //
53
54bool DBase::load( const TQString& filename )
55{
56
57 m_file.setName( filename );
58 if( !m_file.open(IO_ReadOnly) )
59 return false;
60
61 m_stream.setDevice( &m_file );
62 m_stream.setByteOrder( TQDataStream::LittleEndian );
63
64 unsigned filesize = m_file.size();
65
66 // read dBASE version
67 TQ_UINT8 ver;
68 m_stream >> ver;
69 m_version = ver & 0x7f; // bit 7: has memo ?
70
71 // only dBASE V.3 is supported
72 if ( m_version != 3 )
73 return false;
74
75 // date of last update
76 TQ_UINT8 y, m, d;
77 m_stream >> y >> m >> d;
78 // because dBASE saves 102 instead of 2002 (very Y2K-save ;-)
79 m_lastUpdate.setYMD( y+1900, m, d );
80
81 // check for valid date
82 if( !m_lastUpdate.isValid() ) return false;
83
84 // number of records
85 TQ_UINT32 norec;
86 m_stream >> norec;
87 m_recordCount = norec;
88
89 // header-length
90 TQ_UINT16 header_length;
91 m_stream >> header_length;
92 m_headerLength = header_length;
93
94 // record-length
95 TQ_UINT16 record_length;
96 m_stream >> record_length;
97 m_recordLength = record_length;
98
99 // read the remaining chars
100 TQ_UINT8 dummy;
101 for (int foo = 0; foo < 20; ++foo)
102 m_stream >> dummy;
103
104 // size of file must match
105 if( filesize < m_headerLength + m_recordLength * m_recordCount )
106 return false;
107
108 // Now read the headers of the columns and their type
109
110 // Type char Content
111 //
112 // unsigned char field_name[11] 0-10 Fieldname
113 // unsigned char field_type 11 Fieldtype
114 // unsigned long field_address 12-15 Fielddataaddress
115 // unsigned char field_length 16 Fieldlength
116 // unsigned char field_decimals 17 decimals
117 // unsigned char reserved[14] 18-31 reserved for internal dBASE-stuff
118
119 fields.clear();
120 for( unsigned i = 1; i < m_headerLength/32; ++i )
121 {
122 DBaseField* field = new DBaseField;
123
124 // columnn-name
125 TQ_UINT8 colname[12];
126 for ( int j = 0; j < 11; ++j)
127 m_stream >> colname[j];
128 colname[11] = '\0';
129 field->name = TQString( (const char*) &colname[0] );
130
131 // type of column
132 TQ_UINT8 coltype;
133 m_stream >> coltype;
134 switch( coltype )
135 {
136 case 'C': field->type = DBaseField::Character; break;
137 case 'N': field->type = DBaseField::Numeric; break;
138 case 'D': field->type = DBaseField::Date; break;
139 case 'M': field->type = DBaseField::Memo; break;
140 case 'L': field->type = DBaseField::Logical; break;
141 default: field->type = DBaseField::Unknown; break;
142 }
143
144 // fileddataaddress
145 TQ_UINT32 addr;
146 m_stream >> addr;
147
148 // columnlength
149 TQ_UINT8 colsize;
150 m_stream >> colsize;
151 field->length = colsize;
152
153 // decimals
154 TQ_UINT8 decimals;
155 m_stream >> decimals;
156 field->decimals = decimals;
157
158 // read remaining chars
159 TQ_UINT8 dummy;
160 for ( int foo = 0; foo < 14; ++foo )
161 m_stream >> dummy;
162
163 // now append
164 fields.append( field );
165 }
166
167 // set the index to the first record
168 m_stream.device()->at( m_headerLength );
169
170 return true;
171}
172
173TQStringList DBase::readRecord( unsigned recno )
174{
175 TQStringList result;
176
177 // out of range ? return empty strings
178 if( recno >= m_recordCount )
179 {
180 for( unsigned i=0; i<fields.count(); i++)
181 result.append( "" );
182 return result;
183 }
184
185 // seek to where the record is
186 unsigned filepos = m_headerLength + recno * m_recordLength;
187 m_stream.device()->at( filepos );
188
189 // first char == '*' means the record is deleted
190 // so we just skip it
191 TQ_UINT8 delmarker;
192 m_stream >> delmarker;
193 if( delmarker == 0x2a )
194 return result;
195
196 // load it
197 for( unsigned i=0; i<fields.count(); i++ )
198 switch( fields.at(i)->type )
199 {
200 // Numeric or Character
201 case DBaseField::Numeric:
202 case DBaseField::Character:
203 {
204 TQString str;
205 TQ_UINT8 ch;
206 for( unsigned j=0; j<fields.at(i)->length; j++ )
207 { m_stream >> ch; str += TQChar(ch); }
208 result.append( str );
209 } break;
210
211 // Logical
212 case DBaseField::Logical:
213 {
214 TQ_UINT8 ch;
215 m_stream >> ch;
216 switch( ch )
217 {
218 case 'Y': case 'y': case 'T': case 't': result.append( "True" ); break;
219 case 'N': case 'n': case 'F': case 'f': result.append( "False" ); break;
220 default: result.append( "" ); break;
221 }
222 } break;
223
224 // Date, stored as YYYYMMDD
225 // Note: convert it to YYYY-MM-DD
226 case DBaseField::Date:
227 {
228 TQString str;
229 TQ_UINT8 ch;
230 for( unsigned j=0; j<fields.at(i)->length; j++ )
231 { m_stream >> ch; str += TQChar(ch); }
232 str.insert( 6, '-' );
233 str.insert( 4, '-' );
234 result.append( str );
235 } break;
236
237 // Unknown/Unimplemented
238 case DBaseField::Unknown:
239 case DBaseField::Memo:
240 default:
241 result.append( "" ); // unknown
242 break;
243 }
244
245 return result;
246}
247
248void DBase::close()
249{
250 if( m_file.isOpen() ) m_file.close();
251}