You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
252 lines
6.5 KiB
252 lines
6.5 KiB
/* This file is part of the KDE project
|
|
Copyright (C) 2002 by Thomas Franke and Andreas Pietzowski <andreas@pietzowski.de>
|
|
Ariya Hidayat <ariyahidayat@yahoo.de>
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This library 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
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
|
|
#include <qdatetime.h>
|
|
#include <qdatastream.h>
|
|
#include <qfile.h>
|
|
#include <qstring.h>
|
|
#include <qstringlist.h>
|
|
#include <qptrlist.h>
|
|
|
|
#include <dbase.h>
|
|
|
|
DBase::DBase(): m_recordCount( 0 )
|
|
{
|
|
fields.setAutoDelete( true );
|
|
}
|
|
|
|
DBase::~DBase()
|
|
{
|
|
fields.clear();
|
|
close();
|
|
}
|
|
|
|
// Headerdefinition in dBASE
|
|
//
|
|
// Type char Content
|
|
//
|
|
// unsigned char version 0 dBASE-Version (3)
|
|
// unsigned char last_update[3] 1-3 Date of last update
|
|
// unsigned long records 4-7 Number of records
|
|
// unsigned short header_length 8-9 headerlength
|
|
// unsigned short record_length 10-11 recordlength
|
|
// unsigned char reserved[20] 12-31 reserverd info from dBase
|
|
//
|
|
|
|
bool DBase::load( const QString& filename )
|
|
{
|
|
|
|
m_file.setName( filename );
|
|
if( !m_file.open(IO_ReadOnly) )
|
|
return false;
|
|
|
|
m_stream.setDevice( &m_file );
|
|
m_stream.setByteOrder( QDataStream::LittleEndian );
|
|
|
|
unsigned filesize = m_file.size();
|
|
|
|
// read dBASE version
|
|
Q_UINT8 ver;
|
|
m_stream >> ver;
|
|
m_version = ver & 0x7f; // bit 7: has memo ?
|
|
|
|
// only dBASE V.3 is supported
|
|
if ( m_version != 3 )
|
|
return false;
|
|
|
|
// date of last update
|
|
Q_UINT8 y, m, d;
|
|
m_stream >> y >> m >> d;
|
|
// because dBASE saves 102 instead of 2002 (very Y2K-save ;-)
|
|
m_lastUpdate.setYMD( y+1900, m, d );
|
|
|
|
// check for valid date
|
|
if( !m_lastUpdate.isValid() ) return false;
|
|
|
|
// number of records
|
|
Q_UINT32 norec;
|
|
m_stream >> norec;
|
|
m_recordCount = norec;
|
|
|
|
// header-length
|
|
Q_UINT16 header_length;
|
|
m_stream >> header_length;
|
|
m_headerLength = header_length;
|
|
|
|
// record-length
|
|
Q_UINT16 record_length;
|
|
m_stream >> record_length;
|
|
m_recordLength = record_length;
|
|
|
|
// read the remaining chars
|
|
Q_UINT8 dummy;
|
|
for (int foo = 0; foo < 20; ++foo)
|
|
m_stream >> dummy;
|
|
|
|
// size of file must match
|
|
if( filesize < m_headerLength + m_recordLength * m_recordCount )
|
|
return false;
|
|
|
|
// Now read the headers of the columns and their type
|
|
|
|
// Type char Content
|
|
//
|
|
// unsigned char field_name[11] 0-10 Fieldname
|
|
// unsigned char field_type 11 Fieldtype
|
|
// unsigned long field_address 12-15 Fielddataaddress
|
|
// unsigned char field_length 16 Fieldlength
|
|
// unsigned char field_decimals 17 decimals
|
|
// unsigned char reserved[14] 18-31 reserved for internal dBASE-stuff
|
|
|
|
fields.clear();
|
|
for( unsigned i = 1; i < m_headerLength/32; ++i )
|
|
{
|
|
DBaseField* field = new DBaseField;
|
|
|
|
// columnn-name
|
|
Q_UINT8 colname[12];
|
|
for ( int j = 0; j < 11; ++j)
|
|
m_stream >> colname[j];
|
|
colname[11] = '\0';
|
|
field->name = QString( (const char*) &colname[0] );
|
|
|
|
// type of column
|
|
Q_UINT8 coltype;
|
|
m_stream >> coltype;
|
|
switch( coltype )
|
|
{
|
|
case 'C': field->type = DBaseField::Character; break;
|
|
case 'N': field->type = DBaseField::Numeric; break;
|
|
case 'D': field->type = DBaseField::Date; break;
|
|
case 'M': field->type = DBaseField::Memo; break;
|
|
case 'L': field->type = DBaseField::Logical; break;
|
|
default: field->type = DBaseField::Unknown; break;
|
|
}
|
|
|
|
// fileddataaddress
|
|
Q_UINT32 addr;
|
|
m_stream >> addr;
|
|
|
|
// columnlength
|
|
Q_UINT8 colsize;
|
|
m_stream >> colsize;
|
|
field->length = colsize;
|
|
|
|
// decimals
|
|
Q_UINT8 decimals;
|
|
m_stream >> decimals;
|
|
field->decimals = decimals;
|
|
|
|
// read remaining chars
|
|
Q_UINT8 dummy;
|
|
for ( int foo = 0; foo < 14; ++foo )
|
|
m_stream >> dummy;
|
|
|
|
// now append
|
|
fields.append( field );
|
|
}
|
|
|
|
// set the index to the first record
|
|
m_stream.device()->at( m_headerLength );
|
|
|
|
return true;
|
|
}
|
|
|
|
QStringList DBase::readRecord( unsigned recno )
|
|
{
|
|
QStringList result;
|
|
|
|
// out of range ? return empty strings
|
|
if( recno >= m_recordCount )
|
|
{
|
|
for( unsigned i=0; i<fields.count(); i++)
|
|
result.append( "" );
|
|
return result;
|
|
}
|
|
|
|
// seek to where the record is
|
|
unsigned filepos = m_headerLength + recno * m_recordLength;
|
|
m_stream.device()->at( filepos );
|
|
|
|
// first char == '*' means the record is deleted
|
|
// so we just skip it
|
|
Q_UINT8 delmarker;
|
|
m_stream >> delmarker;
|
|
if( delmarker == 0x2a )
|
|
return result;
|
|
|
|
// load it
|
|
for( unsigned i=0; i<fields.count(); i++ )
|
|
switch( fields.at(i)->type )
|
|
{
|
|
// Numeric or Character
|
|
case DBaseField::Numeric:
|
|
case DBaseField::Character:
|
|
{
|
|
QString str;
|
|
Q_UINT8 ch;
|
|
for( unsigned j=0; j<fields.at(i)->length; j++ )
|
|
{ m_stream >> ch; str += QChar(ch); }
|
|
result.append( str );
|
|
} break;
|
|
|
|
// Logical
|
|
case DBaseField::Logical:
|
|
{
|
|
Q_UINT8 ch;
|
|
m_stream >> ch;
|
|
switch( ch )
|
|
{
|
|
case 'Y': case 'y': case 'T': case 't': result.append( "True" ); break;
|
|
case 'N': case 'n': case 'F': case 'f': result.append( "False" ); break;
|
|
default: result.append( "" ); break;
|
|
}
|
|
} break;
|
|
|
|
// Date, stored as YYYYMMDD
|
|
// Note: convert it to YYYY-MM-DD
|
|
case DBaseField::Date:
|
|
{
|
|
QString str;
|
|
Q_UINT8 ch;
|
|
for( unsigned j=0; j<fields.at(i)->length; j++ )
|
|
{ m_stream >> ch; str += QChar(ch); }
|
|
str.insert( 6, '-' );
|
|
str.insert( 4, '-' );
|
|
result.append( str );
|
|
} break;
|
|
|
|
// Unknown/Unimplemented
|
|
case DBaseField::Unknown:
|
|
case DBaseField::Memo:
|
|
default:
|
|
result.append( "" ); // unknown
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void DBase::close()
|
|
{
|
|
if( m_file.isOpen() ) m_file.close();
|
|
}
|