filters

dbase.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2002 by Thomas Franke and Andreas Pietzowski <andreas@pietzowski.de>
00003                          Ariya Hidayat <ariyahidayat@yahoo.de>
00004 
00005    This library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Library General Public
00007    License as published by the Free Software Foundation; either
00008    version 2 of the License, or (at your option) any later version.
00009 
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018  * Boston, MA 02110-1301, USA.
00019 */
00020 
00021 
00022 #include <qdatetime.h>
00023 #include <qdatastream.h>
00024 #include <qfile.h>
00025 #include <qstring.h>
00026 #include <qstringlist.h>
00027 #include <qptrlist.h>
00028 
00029 #include <dbase.h>
00030 
00031 DBase::DBase(): m_recordCount( 0 )
00032 {
00033   fields.setAutoDelete( true );
00034 }
00035 
00036 DBase::~DBase()
00037 {
00038   fields.clear();
00039   close();
00040 }
00041 
00042   // Headerdefinition in dBASE
00043   //
00044   //  Type                            char   Content
00045   //
00046   //  unsigned char version           0      dBASE-Version (3)
00047   //  unsigned char last_update[3]    1-3    Date of last update
00048   //  unsigned long records           4-7    Number of records
00049   //  unsigned short header_length    8-9    headerlength
00050   //  unsigned short record_length    10-11  recordlength
00051   //  unsigned char reserved[20]      12-31  reserverd info from dBase
00052   //
00053 
00054 bool DBase::load( const QString& filename )
00055 {
00056 
00057   m_file.setName( filename );
00058   if( !m_file.open(IO_ReadOnly) )
00059     return false;
00060 
00061   m_stream.setDevice( &m_file );
00062   m_stream.setByteOrder( QDataStream::LittleEndian );
00063 
00064   unsigned filesize = m_file.size();
00065 
00066   // read dBASE version
00067   Q_UINT8 ver;
00068   m_stream >> ver;
00069   m_version = ver & 0x7f; // bit 7: has memo ?
00070 
00071   // only dBASE V.3 is supported
00072   if ( m_version != 3 )
00073      return false;
00074 
00075   // date of last update
00076   Q_UINT8 y, m, d;
00077   m_stream >> y >> m >> d;
00078   // because dBASE saves 102 instead of 2002 (very Y2K-save ;-)
00079   m_lastUpdate.setYMD( y+1900, m, d );
00080 
00081   // check for valid date
00082   if( !m_lastUpdate.isValid() ) return false;
00083 
00084   // number of records
00085   Q_UINT32 norec;
00086   m_stream >> norec;
00087   m_recordCount = norec;
00088 
00089   // header-length
00090   Q_UINT16 header_length;
00091   m_stream >> header_length;
00092   m_headerLength = header_length;
00093 
00094   // record-length
00095   Q_UINT16 record_length;
00096   m_stream >> record_length;
00097   m_recordLength = record_length;
00098 
00099   // read the remaining chars
00100   Q_UINT8 dummy;
00101   for (int foo = 0; foo < 20; ++foo)
00102     m_stream >> dummy;
00103 
00104   // size of file must match
00105   if( filesize < m_headerLength + m_recordLength * m_recordCount )
00106     return false;
00107 
00108   // Now read the headers of the columns and their type
00109 
00110   // Type                              char     Content
00111   //
00112   // unsigned char field_name[11]      0-10     Fieldname
00113   // unsigned char field_type          11       Fieldtype
00114   // unsigned long field_address       12-15    Fielddataaddress
00115   // unsigned char field_length        16       Fieldlength
00116   // unsigned char field_decimals      17       decimals
00117   // unsigned char reserved[14]        18-31    reserved for internal dBASE-stuff
00118 
00119   fields.clear();
00120   for( unsigned i = 1; i < m_headerLength/32; ++i )
00121   {
00122     DBaseField* field = new DBaseField;
00123 
00124     // columnn-name
00125     Q_UINT8 colname[12];
00126     for ( int j = 0; j < 11; ++j)
00127        m_stream >> colname[j];
00128     colname[11] = '\0';
00129     field->name = QString( (const char*) &colname[0] );
00130       
00131     // type of column
00132     Q_UINT8 coltype;
00133     m_stream >> coltype;
00134     switch( coltype )
00135     {
00136       case 'C': field->type = DBaseField::Character; break;
00137       case 'N': field->type = DBaseField::Numeric; break;
00138       case 'D': field->type = DBaseField::Date; break;
00139       case 'M': field->type = DBaseField::Memo; break;
00140       case 'L': field->type = DBaseField::Logical; break;
00141       default: field->type = DBaseField::Unknown; break;
00142     }
00143       
00144     // fileddataaddress
00145     Q_UINT32 addr;
00146     m_stream >> addr;
00147 
00148     // columnlength
00149     Q_UINT8 colsize;
00150     m_stream >> colsize;
00151     field->length = colsize; 
00152 
00153     // decimals
00154     Q_UINT8 decimals;
00155     m_stream >> decimals;
00156     field->decimals = decimals;
00157 
00158     // read remaining chars
00159     Q_UINT8 dummy;
00160     for ( int foo = 0; foo < 14; ++foo )
00161       m_stream >> dummy;
00162 
00163     // now append 
00164     fields.append( field );
00165   }
00166 
00167   // set the index to the first record
00168   m_stream.device()->at( m_headerLength );
00169 
00170   return true;
00171 }
00172 
00173 QStringList DBase::readRecord( unsigned recno )
00174 {
00175   QStringList result;
00176 
00177   // out of range ? return empty strings
00178   if( recno >= m_recordCount )
00179   {
00180     for( unsigned i=0; i<fields.count(); i++)
00181       result.append( "" );
00182     return result;
00183   }
00184 
00185   // seek to where the record is
00186   unsigned filepos = m_headerLength + recno * m_recordLength;
00187   m_stream.device()->at( filepos );
00188 
00189   // first char == '*' means the record is deleted
00190   // so we just skip it
00191   Q_UINT8 delmarker;
00192   m_stream >> delmarker;
00193   if( delmarker == 0x2a )
00194    return result;
00195 
00196   // load it
00197   for( unsigned i=0; i<fields.count(); i++ )
00198     switch( fields.at(i)->type )
00199     {
00200       // Numeric or Character
00201       case DBaseField::Numeric:
00202       case DBaseField::Character:
00203       {
00204         QString str;
00205         Q_UINT8 ch;
00206         for( unsigned j=0; j<fields.at(i)->length; j++ )
00207         {  m_stream >> ch; str += QChar(ch); }
00208         result.append( str );
00209       }  break;
00210 
00211       // Logical
00212       case DBaseField::Logical:
00213       {
00214         Q_UINT8 ch;
00215         m_stream >> ch;
00216         switch( ch )
00217         {
00218           case 'Y': case 'y': case 'T': case 't': result.append( "True" ); break;
00219           case 'N': case 'n': case 'F': case 'f': result.append( "False" ); break;
00220           default: result.append( "" ); break;
00221         }
00222       } break;
00223 
00224       // Date, stored as YYYYMMDD
00225       // Note: convert it to YYYY-MM-DD
00226       case DBaseField::Date:
00227       {
00228         QString str;
00229         Q_UINT8 ch;
00230         for( unsigned j=0; j<fields.at(i)->length; j++ )
00231         {  m_stream >> ch; str += QChar(ch); }
00232         str.insert( 6, '-' );
00233         str.insert( 4, '-' );
00234         result.append( str );
00235       } break;
00236 
00237       // Unknown/Unimplemented
00238       case DBaseField::Unknown:
00239       case DBaseField::Memo:
00240       default:
00241         result.append( "" ); // unknown
00242         break;
00243     }
00244 
00245   return result;
00246 }
00247 
00248 void DBase::close()
00249 {
00250   if( m_file.isOpen() ) m_file.close();
00251 }
KDE Home | KDE Accessibility Home | Description of Access Keys