filters

pole.cpp

00001 /* POLE - Portable library to access OLE Storage
00002    Copyright (C) 2002-2003 Ariya Hidayat <ariya@kde.org>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA
00018 */
00019 
00020 #include <fstream>
00021 #include <string>
00022 #include <list>
00023 #include <iostream>
00024 #include <vector>
00025 
00026 #include "pole.h"
00027 
00028 namespace POLE
00029 {
00030 
00031 class Entry
00032 {
00033   public:
00034     Entry* parent;
00035     std::string name;
00036     unsigned long size;
00037     unsigned start;
00038     bool dir;
00039     std::vector<Entry*> children;
00040     Entry();
00041     ~Entry();
00042   private:
00043     Entry( const Entry& );
00044     Entry& operator=( const Entry& );
00045 };
00046 
00047 class AllocTable
00048 {
00049   public:
00050 
00051     AllocTable();
00052 
00053     ~AllocTable();
00054 
00055     bool dirty;
00056 
00057     void clear();
00058 
00059     unsigned long size();
00060 
00061     void resize( unsigned long newsize );
00062 
00063     void set( unsigned long index, unsigned long val );
00064 
00065     std::vector<unsigned long> follow( unsigned long start );
00066 
00067     unsigned long operator[](unsigned long index );
00068 
00069   private:
00070 
00071     std::vector<unsigned long> data;
00072 
00073     AllocTable( const AllocTable& );
00074 
00075     AllocTable& operator=( const AllocTable& );
00076 };
00077 
00078 class StorageIO
00079 {
00080   public:
00081 
00083     int result;
00084 
00086     Storage* doc;
00087 
00089     std::string filename;
00090 
00092     int mode;
00093 
00095     std::fstream file;
00096 
00098     unsigned long filesize;
00099 
00101     unsigned char header[512];
00102 
00104     unsigned char magic[8];
00105 
00107     unsigned threshold;
00108 
00110     unsigned bb_size;
00111 
00113     unsigned sb_size;
00114 
00116     AllocTable bb;
00117     
00119     AllocTable sb;
00120 
00122     unsigned sbat_start;
00123 
00125     std::vector<unsigned long> sb_blocks;
00126 
00128     unsigned mbat_start;
00129     
00131     unsigned dirent_start;
00132 
00134     Entry* root;
00135     
00137     Entry* current_dir;
00138 
00140     StorageIO( Storage* storage, const char* fileName, int mode );
00141 
00143     ~StorageIO();
00144 
00145     void flush();
00146 
00147     unsigned long loadBigBlocks( std::vector<unsigned long> blocks, unsigned char* buffer, unsigned long maxlen );
00148 
00149     unsigned long loadBigBlock( unsigned long block, unsigned char* buffer, unsigned long maxlen );
00150 
00151     unsigned long loadSmallBlocks( std::vector<unsigned long> blocks, unsigned char* buffer, unsigned long maxlen );
00152 
00153     unsigned long loadSmallBlock( unsigned long block, unsigned char* buffer, unsigned long maxlen );
00154 
00156     Entry* buildTree( Entry* parent, int index, const unsigned char* dirent );
00157 
00158     std::string fullName( Entry* e );
00159 
00161     Entry* entry( const std::string& name );
00162 
00163 
00164   private:
00165 
00166     void load();
00167 
00168     void create();
00169 
00170     // no copy or assign
00171     StorageIO( const StorageIO& );
00172     StorageIO& operator=( const StorageIO& );
00173 
00174 };
00175 
00176 class StreamIO
00177 {
00178   public:
00179 
00180     StreamIO( StorageIO* io, Entry* entry );
00181 
00182     ~StreamIO();
00183 
00184     unsigned long size();
00185 
00186     void seek( unsigned long pos );
00187 
00188     unsigned long tell();
00189 
00190     int getch();
00191 
00192     unsigned long read( unsigned char* data, unsigned long maxlen );
00193 
00194     unsigned long read( unsigned long pos, unsigned char* data, unsigned long maxlen );
00195 
00196     StorageIO* io;
00197 
00198     Entry* entry;
00199 
00200 
00201   private:
00202 
00203     std::vector<unsigned long> blocks;
00204 
00205     // no copy or assign
00206     StreamIO( const StreamIO& );
00207     StreamIO& operator=( const StreamIO& );
00208 
00209     // pointer for read
00210     unsigned long m_pos;
00211 
00212     // simple cache system to speed-up getch()
00213     unsigned char* cache_data;
00214     unsigned long cache_size;
00215     unsigned long cache_pos;
00216     void updateCache();
00217 
00218 };
00219 
00220 }; // namespace POLE
00221 
00222 using namespace POLE;
00223 
00224 static inline unsigned long readU16( const unsigned char* ptr )
00225 {
00226   return ptr[0]+(ptr[1]<<8);
00227 }
00228 
00229 static inline unsigned long readU32( const unsigned char* ptr )
00230 {
00231   return ptr[0]+(ptr[1]<<8)+(ptr[2]<<16)+(ptr[3]<<24);
00232 }
00233 
00234 static inline void writeU16( unsigned char* ptr, unsigned long data )
00235 {
00236   ptr[0] = data & 0xff;
00237   ptr[1] = (data >> 8) & 0xff;
00238 }
00239 
00240 static inline void writeU32( unsigned char* ptr, unsigned long data )
00241 {
00242   ptr[0] = data & 0xff;
00243   ptr[1] = (data >> 8) & 0xff;
00244   ptr[2] = (data >> 16) & 0xff;
00245   ptr[3] = (data >> 24) & 0xff;
00246 }
00247 
00248 static const unsigned char pole_magic[] = 
00249  { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 };
00250  
00251 Entry::Entry()
00252 {
00253   name = "Unnamed";
00254   size = 0;
00255   dir = false;
00256 }
00257 
00258 Entry::~Entry()
00259 { 
00260   for( unsigned i=0; i<children.size(); i++ )
00261   {
00262     Entry* entry = children[ i ];
00263     delete entry;
00264   }
00265 }
00266 
00267 AllocTable::AllocTable()
00268 {
00269   dirty = false;
00270 }
00271 
00272 AllocTable::~AllocTable()
00273 {
00274 }
00275 
00276 unsigned long AllocTable::size()
00277 {
00278   return data.size();
00279 }
00280 
00281 void AllocTable::resize( unsigned long newsize )
00282 {
00283   data.resize( newsize );
00284 }
00285 
00286 unsigned long AllocTable::operator[]( unsigned long index )
00287 {
00288   unsigned long result;
00289   result = data[index];
00290   return result;
00291 }
00292 
00293 void AllocTable::set( unsigned long index, unsigned long value )
00294 {
00295   if( index >= size() ) resize( index + 1);
00296   data[ index ] = value;
00297 }
00298 
00299 std::vector<unsigned long> AllocTable::follow( unsigned long start )
00300 {
00301   std::vector<unsigned long> chain;
00302 
00303   if( start >= size() ) return chain; 
00304 
00305   unsigned long p = start;
00306   while( p < size() )
00307 {
00308     if( p >= (unsigned long)0xfffe ) break;
00309     if( p >= size() ) break;
00310     chain.push_back( p );
00311     if( data[p] >= size() ) break;
00312     p = data[ p ];
00313 }
00314 
00315   return chain;
00316 }
00317 
00318 // =========== StorageIO ==========
00319 
00320 StorageIO::StorageIO( Storage* storage, const char* fname, int m ):
00321   doc( storage ), filename( fname), mode( m )
00322 {
00323   // initialization
00324   result = Storage::Ok; 
00325   root = (Entry*) 0L;
00326   current_dir = (Entry*) 0L;
00327   bb_size = 512;
00328   sb_size = 64;
00329   threshold = 4096;
00330 
00331   // prepare input stream
00332   int om = std::ios::in;
00333   if( mode == Storage::WriteOnly ) om = std::ios::out;
00334   if( mode == Storage::ReadWrite) om = std::ios::app;
00335   om |= std::ios::binary;
00336   //file.open( filename.c_str(), om );
00337   file.open( filename.c_str(), std::ios::binary | std::ios::in );
00338 
00339   // check for error
00340   if( !file.good() )
00341   {
00342     std::cerr << "Can't open " << filename << "\n\n";
00343     result = Storage::OpenFailed;
00344     return;
00345   }
00346 
00347   // assume we'll be just fine
00348   result = Storage::Ok;
00349 
00350   if( mode == Storage::WriteOnly ) create();
00351   else load();
00352 
00353 }
00354 
00355 StorageIO::~StorageIO()
00356 {
00357   filename = std::string();
00358   flush();
00359   file.close();
00360 }
00361 
00362 void StorageIO::load()
00363 {
00364   unsigned bb_shift;  // should be 9 (2^9 = 1024)
00365   unsigned sb_shift;  // should be 6 (2^6 = 64)
00366   unsigned num_bat;   // blocks for big-BAT
00367   unsigned num_sbat;  // blocks for small-BAT
00368   unsigned num_mbat;  // blocks for meta-BAT
00369 
00370   // find size of input file
00371   file.seekg( 0, std::ios::end );
00372   filesize = file.tellg();
00373 
00374   // load header
00375   file.seekg( 0 ); 
00376   file.read( (char*)header, sizeof( header ) );
00377   if( file.gcount() != sizeof( header ) )
00378   {
00379     result = Storage::NotOLE;
00380     return;
00381   }
00382 
00383   // check OLE magic id
00384   for( unsigned i=0; i<8; i++ )
00385   {
00386     magic[i] = header[i];
00387     if( magic[i] != pole_magic[i] )
00388     {
00389       result = Storage::NotOLE;
00390       return;
00391     }
00392   }
00393 
00394   // load important variables
00395   bb_shift = readU16( header + 0x1e );
00396   sb_shift = readU16( header + 0x20 );
00397   num_bat = readU32( header + 0x2c );
00398   dirent_start = readU32( header + 0x30 );
00399   threshold = readU32( header + 0x38 );
00400   sbat_start = readU32( header + 0x3c );
00401   num_sbat = readU32( header + 0x40 );
00402   mbat_start = readU32( header + 0x44 );
00403   num_mbat = readU32( header + 0x48 );
00404 
00405   // sanity checks !!
00406   if( ( threshold != 4096 ) || ( num_bat == 0 ) ||
00407       ( sb_shift > bb_shift ) || ( bb_shift <= 6 ) || ( bb_shift >=31 ) )
00408   {
00409     result = Storage::BadOLE;
00410     return;
00411   }
00412   
00413   // important block size
00414   bb_size = 1 << bb_shift;
00415   sb_size = 1 << sb_shift;
00416 
00417   std::vector<unsigned long> chain;
00418   unsigned char buffer[bb_size];
00419 
00420   // load blocks in meta BAT
00421   std::vector<unsigned long> metabat( num_mbat * bb_size / 4 );
00422   unsigned q = 0;
00423   for( unsigned r=0; r<num_mbat; r++ )
00424   {
00425     unsigned char metabuf[ bb_size ];
00426     loadBigBlock( mbat_start+r, metabuf, sizeof( metabuf ) );
00427     for( unsigned s=0; s< bb_size/4; s++ )
00428       metabat[q++] = readU32( metabuf + s*4 ); 
00429   }
00430 
00431   // load big BAT, keep it in memory
00432   // each entry in alloc table takes 4 bytes
00433   bb.resize( num_bat * bb_size / 4 );
00434   for( unsigned long i=0; i< num_bat; i++ )
00435   {
00436     // note that first 109 blocks are defined in header
00437     // the rest are from meta BAT
00438     unsigned long block = (i<109) ? readU32( header + 0x4C+i*4 ) : metabat[i-109];
00439 
00440     loadBigBlock( block, buffer, bb_size );
00441 
00442     for( unsigned m=0; m<bb_size/4; m++)
00443       bb.set( i*bb_size/4 + m, readU32( buffer + m*4 ) );
00444   }
00445 
00446   // load small BAT, also keep in memory
00447   // to get blocks for small BAT, follow chain in big BAT
00448   chain = bb.follow( sbat_start );
00449   //sb.resize( chain.size() * bb_size / 4 );
00450   for( unsigned long i=0; i < chain.size(); i++ )
00451   {
00452     unsigned long block = chain[i];
00453     loadBigBlock( block, buffer, bb_size );
00454     for( unsigned m=0; m<bb_size/4; m++ )
00455       sb.set( i*bb_size/4 + m, readU32( buffer + m*4 ) ); 
00456   }
00457 
00458   // construct root directory tree
00459   unsigned char* dirent;
00460   chain = bb.follow( dirent_start );
00461   unsigned bufsize = chain.size() * bb_size;
00462   dirent = new unsigned char[bufsize];
00463   loadBigBlocks( chain, dirent, bufsize );
00464   root = buildTree( (Entry*)0L, 0, dirent );
00465   
00466   // fetch block chain as data for small-files
00467   sb_blocks = bb.follow( readU32( dirent + 0x74 ) );
00468 
00469   delete [] dirent;
00470   
00471   // start with root directory
00472   current_dir = root;
00473 
00474   // done, without error
00475   result = Storage::Ok;
00476 }
00477 
00478 // unsupported yet
00479 void StorageIO::create()
00480 {
00481   unsigned bb_shift = 9;
00482   unsigned sb_shift = 6;
00483   bb_size = 1 << bb_shift;
00484   sb_size = 1 << sb_shift;
00485   threshold = 4096;
00486 
00487   // construct blank header
00488   // only few important parts, the rest will be fixed-up in flush()
00489   for( int i=0; i<8; i++ ) header[i] = pole_magic[i];
00490   writeU32( header + 8, 0 );
00491   writeU32( header + 12, 0 );
00492   writeU32( header + 16, 0 );
00493   writeU16( header + 24, 0x3B00 );  // revision ?
00494   writeU16( header + 26, 3 );       // version ?
00495   writeU16( header + 28, 0xfffe );  // unknown
00496   writeU16( header + 30, bb_shift );
00497   writeU16( header + 32, sb_shift );
00498   writeU16( header + 34, 0 );
00499   writeU32( header + 36, 0 );
00500   writeU32( header + 40, 0 );
00501   writeU32( header + 52, 0 );
00502   for( int j=0x4C; j<512; j++ ) header[j] = 0xff;
00503 
00504   // done, without error
00505   result = Storage::Ok;
00506 }
00507 
00508 void StorageIO::flush()
00509 {
00510   if( mode == Storage::ReadOnly ) return;
00511 
00512   // header fix-up
00513   writeU32( header + 0x30, dirent_start );
00514   writeU32( header + 0x38, threshold );
00515   writeU32( header + 0x3c, sbat_start );
00516   writeU32( header + 0x44, mbat_start );
00517 
00518   // write the header
00519 
00520 
00521 
00522   // dirty ?
00523 }
00524 
00525 unsigned long StorageIO::loadBigBlocks( std::vector<unsigned long> blocks,
00526   unsigned char* data, unsigned long maxlen )
00527 {
00528   // sentinel
00529   if( !data ) return 0;
00530   if( !file.good() ) return 0;
00531   if( blocks.size() < 1 ) return 0;
00532   if( maxlen == 0 ) return 0;
00533 
00534   // read block one by one, seems fast enough
00535   unsigned long bytes = 0;
00536   for( unsigned long i=0; (i < blocks.size() ) & ( bytes<maxlen ); i++ )
00537   {
00538     unsigned long block = blocks[i];
00539     if( block < 0 ) continue;
00540     unsigned long pos =  bb_size * ( block+1 );
00541     unsigned long p = (bb_size < maxlen-bytes) ? bb_size : maxlen-bytes;
00542     if( pos + p > filesize ) p = filesize - pos;
00543     file.seekg( pos );
00544     file.read( (char*)data + bytes, p );
00545     bytes += p;
00546   }
00547 
00548   return bytes;
00549 }
00550 
00551 unsigned long StorageIO::loadBigBlock( unsigned long block,
00552   unsigned char* data, unsigned long maxlen )
00553 {
00554   // sentinel
00555   if( !data ) return 0;
00556   if( !file.good() ) return 0;
00557   if( block < 0 ) return 0;
00558 
00559   // wraps call for loadBigBlocks
00560   std::vector<unsigned long> blocks;
00561   blocks.resize( 1 );
00562   blocks[ 0 ] = block;
00563 
00564   return loadBigBlocks( blocks, data, maxlen );
00565 }
00566 
00567 // return number of bytes which has been read
00568 unsigned long StorageIO::loadSmallBlocks( std::vector<unsigned long> blocks,
00569   unsigned char* data, unsigned long maxlen )
00570 {
00571   // sentinel
00572   if( !data ) return 0;
00573   if( !file.good() ) return 0;
00574   if( blocks.size() < 1 ) return 0;
00575   if( maxlen == 0 ) return 0;
00576 
00577   // our own local buffer
00578   unsigned char buf[ bb_size ];
00579 
00580   // read small block one by one
00581   unsigned long bytes = 0;
00582   for( unsigned long i=0; ( i<blocks.size() ) & ( bytes<maxlen ); i++ )
00583   {
00584     unsigned long block = blocks[i];
00585     if( block < 0 ) continue;
00586 
00587     // find where the small-block exactly is
00588     unsigned long pos = block * sb_size;
00589     unsigned long bbindex = pos / bb_size;
00590     if( bbindex >= sb_blocks.size() ) break;
00591 
00592     loadBigBlock( sb_blocks[ bbindex ], buf, bb_size );
00593 
00594     // copy the data
00595     unsigned offset = pos % bb_size;
00596     unsigned long p = (maxlen-bytes < bb_size-offset ) ? maxlen-bytes :  bb_size-offset;
00597     p = (sb_size<p ) ? sb_size : p;
00598     memcpy( data + bytes, buf + offset, p );
00599     bytes += p;
00600   }
00601 
00602   return bytes;
00603 }
00604 
00605 unsigned long StorageIO::loadSmallBlock( unsigned long block,
00606   unsigned char* data, unsigned long maxlen )
00607 {
00608   // sentinel
00609   if( !data ) return 0;
00610   if( !file.good() ) return 0;
00611   if( block < 0 ) return 0;
00612 
00613   // wraps call for loadSmallBlocks
00614   std::vector<unsigned long> blocks;
00615   blocks.resize( 1 );
00616   blocks.assign( 1, block );
00617 
00618   return loadSmallBlocks( blocks, data, maxlen );
00619 }
00620 
00621 // recursive function to construct directory tree
00622 Entry* StorageIO::buildTree( Entry* parent, int index, const unsigned char* dirent )
00623 {
00624   Entry* entry = (Entry*) 0L;
00625 
00626   // find where to start
00627   unsigned p = index * 128;
00628 
00629   // would be < 32 if first char in the name isn't printable
00630   unsigned prefix = 32;
00631 
00632   // parse name of this entry, which stored as Unicode 16-bit
00633   std::string name;
00634   int name_len = readU16( dirent + 64+p );
00635   for( int j=0; ( dirent[j+p]) && (j<name_len); j+= 2 )
00636      name.append( 1, dirent[j+p] );
00637 
00638   // emtpy name ?
00639   if( !name.length() ) return entry;
00640 
00641   // first char isn't printable ? remove it...
00642   if( dirent[p] < 32 ){ prefix = dirent[0]; name.erase( 0,1 ); }
00643 
00644   // type of this entry will decide which Entry should be created
00645   // 2 = file (aka stream), 1 = directory (aka storage), 5 = root
00646   unsigned type  = dirent[ 0x42 + p];
00647   if( ( type == 2 ) || ( type == 1 ) || ( type == 5 ) ) entry = new Entry();
00648   if( entry ) entry->dir = ( ( type = 1 ) || ( type == 5 ) );
00649 
00650   // barf on error
00651   if( !entry ) return entry;
00652 
00653   // fetch important data
00654   entry->name = name;
00655   entry->start = readU32( dirent + 0x74+p );
00656   entry->size  = readU32( dirent + 0x78+p );
00657 
00658   // append as another child
00659   entry->parent = parent;
00660   if( parent ) parent->children.push_back( entry );
00661 
00662   // check previous
00663   int prev  = readU32( dirent + 0x44+p );
00664   if( prev >= 0 ) buildTree( parent, prev, dirent );
00665   
00666   // traverse to sub
00667   int dir   = readU32( dirent + 0x4C+p );
00668   if( entry->dir && (dir > 0 ))
00669        buildTree( entry, dir, dirent );
00670 
00671   // check next
00672   int next  = readU32( dirent + 0x48+p );
00673   if( next >= 0 ) buildTree( parent, next, dirent );
00674   return entry;
00675 }
00676 
00677 // given an entry, find a complete path from root
00678 std::string StorageIO::fullName( Entry* e )
00679 {
00680   if( !e ) return (const char*) 0L;
00681 
00682   std::string result;
00683 
00684   while( e->parent )
00685   {
00686      result.insert( 0, e->name );
00687      result.insert( 0,  "/" );
00688      e = e->parent;
00689   }
00690 
00691   // don't use specified root name (like "Root Entry")
00692   if( !result.length() ) result = "/";
00693 
00694   return result;
00695 }
00696 
00697 // given a fullname (e.g "/ObjectPool/_1020961869"), find the entry
00698 Entry* StorageIO::entry( const std::string& name )
00699 {
00700    Entry* entry = (Entry*) 0L;
00701 
00702    // sanity check
00703    if( !root ) return (Entry*) 0L;
00704    if( !name.length() ) return (Entry*) 0L;
00705    
00706    // start from root when name is absolute
00707    // or current directory when name is relative
00708    entry = (name[0] == '/' ) ? root : current_dir;
00709 
00710    // split the names, e.g  "/ObjectPool/_1020961869" will become:
00711    // "ObjectPool" and "_1020961869" 
00712    std::list<std::string> names;
00713    std::string::size_type start = 0, end = 0;
00714    while( start < name.length() )
00715    {
00716      end = name.find_first_of( '/', start );
00717      if( end == std::string::npos ) end = name.length();
00718      names.push_back( name.substr( start, end-start ) );
00719      start = end+1;
00720    }
00721   
00722    std::list<std::string>::iterator it; 
00723    for( it = names.begin(); it != names.end(); ++it )
00724    {
00725      std::string entryname = *it;
00726      Entry *child = (Entry*) 0L;
00727      if( entry->dir )
00728        for( unsigned j=0; j < entry->children.size(); j++ )
00729          if( entry->children[j]->name == entryname )
00730            child = entry->children[j];
00731      if( !child ) return (Entry*) 0L;
00732       entry = child;
00733    }
00734 
00735    return entry;
00736 }
00737 
00738 // =========== StreamIO ==========
00739 
00740 StreamIO::StreamIO( StorageIO* _io, Entry* _entry ):
00741   io( _io ), entry( _entry ), m_pos( 0 ),
00742   cache_data( 0 ), cache_size( 0 ), cache_pos( 0 )
00743 {
00744   blocks = ( entry->size >= io->threshold ) ? io->bb.follow( entry->start ) :
00745      io->sb.follow( entry->start );
00746 
00747   // prepare cache
00748   cache_size = 4096; // optimal ?
00749   cache_data = new unsigned char[cache_size];
00750   updateCache();
00751 }
00752 
00753 // FIXME tell parent we're gone
00754 StreamIO::~StreamIO()
00755 {
00756   delete[] cache_data;  
00757 }
00758 
00759 void StreamIO::seek( unsigned long pos )
00760 {
00761   m_pos = pos;
00762 }
00763 
00764 unsigned long StreamIO::tell()
00765 {
00766   return m_pos;
00767 }
00768 
00769 int StreamIO::getch()
00770 {
00771   // past end-of-file ?
00772   if( m_pos > entry->size ) return -1;
00773 
00774   // need to update cache ?
00775   if( !cache_size || ( m_pos < cache_pos ) ||
00776     ( m_pos >= cache_pos + cache_size ) )
00777       updateCache();
00778 
00779   // something bad if we don't get good cache
00780   if( !cache_size ) return -1;
00781 
00782   int data = cache_data[m_pos - cache_pos];
00783   m_pos++;
00784 
00785   return data;
00786 }
00787 
00788 unsigned long StreamIO::read( unsigned long pos, unsigned char* data, unsigned long maxlen )
00789 {
00790   // sanity checks
00791   if( !data ) return 0;
00792   if( maxlen == 0 ) return 0;
00793 
00794   unsigned long totalbytes = 0;
00795   
00796   if ( entry->size < io->threshold )
00797   {
00798     // small file
00799     unsigned long index = pos / io->sb_size;
00800 
00801     if( index >= blocks.size() ) return 0;
00802 
00803     unsigned char buf[ io->sb_size ];
00804     unsigned long offset = pos % io->sb_size;
00805     while( totalbytes < maxlen )
00806     {
00807       if( index >= blocks.size() ) break;
00808       io->loadSmallBlock( blocks[index], buf, io->bb_size );
00809       unsigned long count = io->sb_size - offset;
00810       if( count > maxlen-totalbytes ) count = maxlen-totalbytes;
00811       memcpy( data+totalbytes, buf + offset, count );
00812       totalbytes += count;
00813       offset = 0;
00814       index++;
00815     }
00816 
00817   }
00818   else
00819   {
00820     // big file
00821     unsigned long index = pos / io->bb_size;
00822     
00823     if( index >= blocks.size() ) return 0;
00824     
00825     unsigned char buf[ io->bb_size ];
00826     unsigned long offset = pos % io->bb_size;
00827     while( totalbytes < maxlen )
00828     {
00829       if( index >= blocks.size() ) break;
00830       io->loadBigBlock( blocks[index], buf, io->bb_size );
00831       unsigned long count = io->bb_size - offset;
00832       if( count > maxlen-totalbytes ) count = maxlen-totalbytes;
00833       memcpy( data+totalbytes, buf + offset, count );
00834       totalbytes += count;
00835       index++;
00836       offset = 0;
00837     }
00838 
00839   }
00840 
00841   return totalbytes;
00842 }
00843 
00844 unsigned long StreamIO::read( unsigned char* data, unsigned long maxlen )
00845 {
00846   unsigned long bytes = read( tell(), data, maxlen );
00847   m_pos += bytes;
00848   return bytes;
00849 }
00850 
00851 void StreamIO::updateCache()
00852 {
00853   // sanity check
00854   if( !cache_data ) return;
00855 
00856   cache_pos = m_pos - ( m_pos % cache_size );
00857   unsigned long bytes = cache_size;
00858   if( cache_pos + bytes > entry->size ) bytes = entry->size - cache_pos;
00859   cache_size = read( cache_pos, cache_data, bytes );
00860 }
00861 
00862 
00863 // =========== Storage ==========
00864 
00865 Storage::Storage()
00866 {
00867   io = (StorageIO*) 0L;
00868   result = Storage::Ok;
00869 }
00870 
00871 Storage::~Storage()
00872 {
00873   close();
00874   delete io;
00875 }
00876 
00877 bool Storage::open( const char* fileName, int m )
00878 {
00879   // only a few modes accepted
00880   if( ( m != ReadOnly ) && ( m != WriteOnly ) && ( m != ReadWrite ) )
00881   {
00882     result = UnknownError;
00883     return false;
00884   }
00885 
00886   io = new StorageIO( this, fileName, m );
00887 
00888   result = io->result;
00889   
00890   return result == Storage::Ok;
00891 }
00892 
00893 void Storage::flush()
00894 {
00895   if( io ) io->flush();
00896 }
00897 
00898 void Storage::close()
00899 {
00900   flush();
00901 }
00902 
00903 // list all files and subdirs in current path
00904 std::list<std::string> Storage::listDirectory()
00905 {
00906   std::list<std::string> entries;
00907 
00908   // sanity check
00909   if( !io ) return entries;
00910   if( !io->current_dir ) return entries;
00911 
00912   // sentinel: do nothing if not a directory
00913   if( !io->current_dir->dir ) return entries;
00914 
00915   // find all children belongs to this directory
00916   for( unsigned i = 0; i<io->current_dir->children.size(); i++ )
00917   {
00918     Entry* e = io->current_dir->children[i];
00919     if( e ) entries.push_back( e->name );
00920   }
00921 
00922   return entries;
00923 }
00924 
00925 // enters a sub-directory, returns false if not a directory or not found
00926 bool Storage::enterDirectory( const std::string& directory )
00927 {
00928   // sanity check
00929   if( !io ) return false;
00930   if( !io->current_dir ) return false;
00931 
00932   // look for the specified sub-dir
00933   for( unsigned i = 0; i<io->current_dir->children.size(); i++ )
00934   {
00935     Entry* e = io->current_dir->children[i];
00936     if( e ) if( e->name == directory ) 
00937       if ( e->dir )
00938       {
00939         io->current_dir = e;
00940         return true;
00941       }
00942   }
00943 
00944   return false;
00945 }
00946 
00947 // goes up one level (like cd ..)
00948 void Storage::leaveDirectory()
00949 {
00950   // sanity check
00951   if( !io ) return;
00952   if( !io->current_dir ) return;
00953 
00954   Entry* parent = io->current_dir->parent;
00955   if( parent ) if( parent->dir ) 
00956     io->current_dir = parent;
00957 }
00958 
00959 // note: without trailing "/"
00960 std::string Storage::path()
00961 {
00962   // sanity check
00963   if( !io ) return std::string();
00964   if( !io->current_dir ) return std::string();
00965 
00966   return io->fullName( io->current_dir );
00967 }
00968 
00969 Stream* Storage::stream( const std::string& name )
00970 {
00971   // sanity check
00972   if( !name.length() ) return (Stream*) 0L;
00973   if( !io ) return (Stream*) 0L;
00974 
00975   // make absolute if necesary
00976   std::string fullName = name;
00977   if( name[0] != '/' ) fullName.insert( 0, path() + "/" );
00978 
00979   // find to which entry this stream associates
00980   Entry* entry =  io->entry( name );
00981   if( !entry ) return (Stream*) 0L;
00982 
00983   StreamIO* sio = new StreamIO( io, entry );
00984   Stream* s = new Stream( sio );
00985 
00986   return s;
00987 }
00988 
00989 // =========== Stream ==========
00990 
00991 Stream::Stream( StreamIO* _io ):
00992   io( _io )
00993 {
00994 }
00995 
00996 // FIXME tell parent we're gone
00997 Stream::~Stream()
00998 {
00999   delete io;
01000 }
01001 
01002 unsigned long Stream::tell()
01003 {
01004   return io ? io->tell() : 0;
01005 }
01006 
01007 void Stream::seek( unsigned long newpos )
01008 {
01009   if( io ) io->seek( newpos );
01010 }
01011 
01012 unsigned long Stream::size()
01013 {
01014   return io ? io->entry->size : 0;
01015 }
01016 
01017 int Stream::getch()
01018 {
01019   return io ? io->getch() : -1;
01020 }
01021 
01022 unsigned long Stream::read( unsigned char* data, unsigned long maxlen )
01023 {
01024   return io ? io->read( data, maxlen ) : 0;
01025 }
01026 
KDE Home | KDE Accessibility Home | Description of Access Keys