filters

kword/starwriter/pole.cpp
1/* POLE - Portable library to access OLE Storage
2 Copyright (C) 2002-2003 Ariya Hidayat <ariya@kde.org>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA
18*/
19
20#include <fstream>
21#include <string>
22#include <list>
23#include <iostream>
24#include <vector>
25
26#include "pole.h"
27
28namespace POLE
29{
30
31class Entry
32{
33 public:
34 Entry* parent;
35 std::string name;
36 unsigned long size;
37 unsigned start;
38 bool dir;
39 std::vector<Entry*> children;
40 Entry();
41 ~Entry();
42 private:
43 Entry( const Entry& );
44 Entry& operator=( const Entry& );
45};
46
47class AllocTable
48{
49 public:
50
51 AllocTable();
52
53 ~AllocTable();
54
55 bool dirty;
56
57 void clear();
58
59 unsigned long size();
60
61 void resize( unsigned long newsize );
62
63 void set( unsigned long index, unsigned long val );
64
65 std::vector<unsigned long> follow( unsigned long start );
66
67 unsigned long operator[](unsigned long index );
68
69 private:
70
71 std::vector<unsigned long> data;
72
73 AllocTable( const AllocTable& );
74
75 AllocTable& operator=( const AllocTable& );
76};
77
78class StorageIO
79{
80 public:
81
83 int result;
84
86 Storage* doc;
87
89 std::string filename;
90
92 int mode;
93
95 std::fstream file;
96
98 unsigned long filesize;
99
101 unsigned char header[512];
102
104 unsigned char magic[8];
105
107 unsigned threshold;
108
110 unsigned bb_size;
111
113 unsigned sb_size;
114
116 AllocTable bb;
117
119 AllocTable sb;
120
122 unsigned sbat_start;
123
125 std::vector<unsigned long> sb_blocks;
126
128 unsigned mbat_start;
129
131 unsigned dirent_start;
132
134 Entry* root;
135
137 Entry* current_dir;
138
140 StorageIO( Storage* storage, const char* fileName, int mode );
141
143 ~StorageIO();
144
145 void flush();
146
147 unsigned long loadBigBlocks( std::vector<unsigned long> blocks, unsigned char* buffer, unsigned long maxlen );
148
149 unsigned long loadBigBlock( unsigned long block, unsigned char* buffer, unsigned long maxlen );
150
151 unsigned long loadSmallBlocks( std::vector<unsigned long> blocks, unsigned char* buffer, unsigned long maxlen );
152
153 unsigned long loadSmallBlock( unsigned long block, unsigned char* buffer, unsigned long maxlen );
154
156 Entry* buildTree( Entry* parent, int index, const unsigned char* dirent );
157
158 std::string fullName( Entry* e );
159
161 Entry* entry( const std::string& name );
162
163
164 private:
165
166 void load();
167
168 void create();
169
170 // no copy or assign
171 StorageIO( const StorageIO& );
172 StorageIO& operator=( const StorageIO& );
173
174};
175
176class StreamIO
177{
178 public:
179
180 StreamIO( StorageIO* io, Entry* entry );
181
182 ~StreamIO();
183
184 unsigned long size();
185
186 void seek( unsigned long pos );
187
188 unsigned long tell();
189
190 int getch();
191
192 unsigned long read( unsigned char* data, unsigned long maxlen );
193
194 unsigned long read( unsigned long pos, unsigned char* data, unsigned long maxlen );
195
196 StorageIO* io;
197
198 Entry* entry;
199
200
201 private:
202
203 std::vector<unsigned long> blocks;
204
205 // no copy or assign
206 StreamIO( const StreamIO& );
207 StreamIO& operator=( const StreamIO& );
208
209 // pointer for read
210 unsigned long m_pos;
211
212 // simple cache system to speed-up getch()
213 unsigned char* cache_data;
214 unsigned long cache_size;
215 unsigned long cache_pos;
216 void updateCache();
217
218};
219
220}; // namespace POLE
221
222using namespace POLE;
223
224static inline unsigned long readU16( const unsigned char* ptr )
225{
226 return ptr[0]+(ptr[1]<<8);
227}
228
229static inline unsigned long readU32( const unsigned char* ptr )
230{
231 return ptr[0]+(ptr[1]<<8)+(ptr[2]<<16)+(ptr[3]<<24);
232}
233
234static inline void writeU16( unsigned char* ptr, unsigned long data )
235{
236 ptr[0] = data & 0xff;
237 ptr[1] = (data >> 8) & 0xff;
238}
239
240static inline void writeU32( unsigned char* ptr, unsigned long data )
241{
242 ptr[0] = data & 0xff;
243 ptr[1] = (data >> 8) & 0xff;
244 ptr[2] = (data >> 16) & 0xff;
245 ptr[3] = (data >> 24) & 0xff;
246}
247
248static const unsigned char pole_magic[] =
249 { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 };
250
251Entry::Entry()
252{
253 name = "Unnamed";
254 size = 0;
255 dir = false;
256}
257
258Entry::~Entry()
259{
260 for( unsigned i=0; i<children.size(); i++ )
261 {
262 Entry* entry = children[ i ];
263 delete entry;
264 }
265}
266
267AllocTable::AllocTable()
268{
269 dirty = false;
270}
271
272AllocTable::~AllocTable()
273{
274}
275
276unsigned long AllocTable::size()
277{
278 return data.size();
279}
280
281void AllocTable::resize( unsigned long newsize )
282{
283 data.resize( newsize );
284}
285
286unsigned long AllocTable::operator[]( unsigned long index )
287{
288 unsigned long result;
289 result = data[index];
290 return result;
291}
292
293void AllocTable::set( unsigned long index, unsigned long value )
294{
295 if( index >= size() ) resize( index + 1);
296 data[ index ] = value;
297}
298
299std::vector<unsigned long> AllocTable::follow( unsigned long start )
300{
301 std::vector<unsigned long> chain;
302
303 if( start >= size() ) return chain;
304
305 unsigned long p = start;
306 while( p < size() )
307{
308 if( p >= (unsigned long)0xfffe ) break;
309 if( p >= size() ) break;
310 chain.push_back( p );
311 if( data[p] >= size() ) break;
312 p = data[ p ];
313}
314
315 return chain;
316}
317
318// =========== StorageIO ==========
319
320StorageIO::StorageIO( Storage* storage, const char* fname, int m ):
321 doc( storage ), filename( fname), mode( m )
322{
323 // initialization
324 result = Storage::Ok;
325 root = (Entry*) 0L;
326 current_dir = (Entry*) 0L;
327 bb_size = 512;
328 sb_size = 64;
329 threshold = 4096;
330
331 // prepare input stream
332 int om = std::ios::in;
333 if( mode == Storage::WriteOnly ) om = std::ios::out;
334 if( mode == Storage::ReadWrite) om = std::ios::app;
335 om |= std::ios::binary;
336 //file.open( filename.c_str(), om );
337 file.open( filename.c_str(), std::ios::binary | std::ios::in );
338
339 // check for error
340 if( !file.good() )
341 {
342 std::cerr << "Can't open " << filename << "\n\n";
343 result = Storage::OpenFailed;
344 return;
345 }
346
347 // assume we'll be just fine
348 result = Storage::Ok;
349
350 if( mode == Storage::WriteOnly ) create();
351 else load();
352
353}
354
355StorageIO::~StorageIO()
356{
357 filename = std::string();
358 flush();
359 file.close();
360}
361
362void StorageIO::load()
363{
364 unsigned bb_shift; // should be 9 (2^9 = 1024)
365 unsigned sb_shift; // should be 6 (2^6 = 64)
366 unsigned num_bat; // blocks for big-BAT
367 unsigned num_sbat; // blocks for small-BAT
368 unsigned num_mbat; // blocks for meta-BAT
369
370 // find size of input file
371 file.seekg( 0, std::ios::end );
372 filesize = file.tellg();
373
374 // load header
375 file.seekg( 0 );
376 file.read( (char*)header, sizeof( header ) );
377 if( file.gcount() != sizeof( header ) )
378 {
379 result = Storage::NotOLE;
380 return;
381 }
382
383 // check OLE magic id
384 for( unsigned i=0; i<8; i++ )
385 {
386 magic[i] = header[i];
387 if( magic[i] != pole_magic[i] )
388 {
389 result = Storage::NotOLE;
390 return;
391 }
392 }
393
394 // load important variables
395 bb_shift = readU16( header + 0x1e );
396 sb_shift = readU16( header + 0x20 );
397 num_bat = readU32( header + 0x2c );
398 dirent_start = readU32( header + 0x30 );
399 threshold = readU32( header + 0x38 );
400 sbat_start = readU32( header + 0x3c );
401 num_sbat = readU32( header + 0x40 );
402 mbat_start = readU32( header + 0x44 );
403 num_mbat = readU32( header + 0x48 );
404
405 // sanity checks !!
406 if( ( threshold != 4096 ) || ( num_bat == 0 ) ||
407 ( sb_shift > bb_shift ) || ( bb_shift <= 6 ) || ( bb_shift >=31 ) )
408 {
409 result = Storage::BadOLE;
410 return;
411 }
412
413 // important block size
414 bb_size = 1 << bb_shift;
415 sb_size = 1 << sb_shift;
416
417 std::vector<unsigned long> chain;
418 unsigned char buffer[bb_size];
419
420 // load blocks in meta BAT
421 std::vector<unsigned long> metabat( num_mbat * bb_size / 4 );
422 unsigned q = 0;
423 for( unsigned r=0; r<num_mbat; r++ )
424 {
425 unsigned char metabuf[ bb_size ];
426 loadBigBlock( mbat_start+r, metabuf, sizeof( metabuf ) );
427 for( unsigned s=0; s< bb_size/4; s++ )
428 metabat[q++] = readU32( metabuf + s*4 );
429 }
430
431 // load big BAT, keep it in memory
432 // each entry in alloc table takes 4 bytes
433 bb.resize( num_bat * bb_size / 4 );
434 for( unsigned long i=0; i< num_bat; i++ )
435 {
436 // note that first 109 blocks are defined in header
437 // the rest are from meta BAT
438 unsigned long block = (i<109) ? readU32( header + 0x4C+i*4 ) : metabat[i-109];
439
440 loadBigBlock( block, buffer, bb_size );
441
442 for( unsigned m=0; m<bb_size/4; m++)
443 bb.set( i*bb_size/4 + m, readU32( buffer + m*4 ) );
444 }
445
446 // load small BAT, also keep in memory
447 // to get blocks for small BAT, follow chain in big BAT
448 chain = bb.follow( sbat_start );
449 //sb.resize( chain.size() * bb_size / 4 );
450 for( unsigned long i=0; i < chain.size(); i++ )
451 {
452 unsigned long block = chain[i];
453 loadBigBlock( block, buffer, bb_size );
454 for( unsigned m=0; m<bb_size/4; m++ )
455 sb.set( i*bb_size/4 + m, readU32( buffer + m*4 ) );
456 }
457
458 // construct root directory tree
459 unsigned char* dirent;
460 chain = bb.follow( dirent_start );
461 unsigned bufsize = chain.size() * bb_size;
462 dirent = new unsigned char[bufsize];
463 loadBigBlocks( chain, dirent, bufsize );
464 root = buildTree( (Entry*)0L, 0, dirent );
465
466 // fetch block chain as data for small-files
467 sb_blocks = bb.follow( readU32( dirent + 0x74 ) );
468
469 delete [] dirent;
470
471 // start with root directory
472 current_dir = root;
473
474 // done, without error
475 result = Storage::Ok;
476}
477
478// unsupported yet
479void StorageIO::create()
480{
481 unsigned bb_shift = 9;
482 unsigned sb_shift = 6;
483 bb_size = 1 << bb_shift;
484 sb_size = 1 << sb_shift;
485 threshold = 4096;
486
487 // construct blank header
488 // only few important parts, the rest will be fixed-up in flush()
489 for( int i=0; i<8; i++ ) header[i] = pole_magic[i];
490 writeU32( header + 8, 0 );
491 writeU32( header + 12, 0 );
492 writeU32( header + 16, 0 );
493 writeU16( header + 24, 0x3B00 ); // revision ?
494 writeU16( header + 26, 3 ); // version ?
495 writeU16( header + 28, 0xfffe ); // unknown
496 writeU16( header + 30, bb_shift );
497 writeU16( header + 32, sb_shift );
498 writeU16( header + 34, 0 );
499 writeU32( header + 36, 0 );
500 writeU32( header + 40, 0 );
501 writeU32( header + 52, 0 );
502 for( int j=0x4C; j<512; j++ ) header[j] = 0xff;
503
504 // done, without error
505 result = Storage::Ok;
506}
507
508void StorageIO::flush()
509{
510 if( mode == Storage::ReadOnly ) return;
511
512 // header fix-up
513 writeU32( header + 0x30, dirent_start );
514 writeU32( header + 0x38, threshold );
515 writeU32( header + 0x3c, sbat_start );
516 writeU32( header + 0x44, mbat_start );
517
518 // write the header
519
520
521
522 // dirty ?
523}
524
525unsigned long StorageIO::loadBigBlocks( std::vector<unsigned long> blocks,
526 unsigned char* data, unsigned long maxlen )
527{
528 // sentinel
529 if( !data ) return 0;
530 if( !file.good() ) return 0;
531 if( blocks.size() < 1 ) return 0;
532 if( maxlen == 0 ) return 0;
533
534 // read block one by one, seems fast enough
535 unsigned long bytes = 0;
536 for( unsigned long i=0; (i < blocks.size() ) & ( bytes<maxlen ); i++ )
537 {
538 unsigned long block = blocks[i];
539 if( block < 0 ) continue;
540 unsigned long pos = bb_size * ( block+1 );
541 unsigned long p = (bb_size < maxlen-bytes) ? bb_size : maxlen-bytes;
542 if( pos + p > filesize ) p = filesize - pos;
543 file.seekg( pos );
544 file.read( (char*)data + bytes, p );
545 bytes += p;
546 }
547
548 return bytes;
549}
550
551unsigned long StorageIO::loadBigBlock( unsigned long block,
552 unsigned char* data, unsigned long maxlen )
553{
554 // sentinel
555 if( !data ) return 0;
556 if( !file.good() ) return 0;
557 if( block < 0 ) return 0;
558
559 // wraps call for loadBigBlocks
560 std::vector<unsigned long> blocks;
561 blocks.resize( 1 );
562 blocks[ 0 ] = block;
563
564 return loadBigBlocks( blocks, data, maxlen );
565}
566
567// return number of bytes which has been read
568unsigned long StorageIO::loadSmallBlocks( std::vector<unsigned long> blocks,
569 unsigned char* data, unsigned long maxlen )
570{
571 // sentinel
572 if( !data ) return 0;
573 if( !file.good() ) return 0;
574 if( blocks.size() < 1 ) return 0;
575 if( maxlen == 0 ) return 0;
576
577 // our own local buffer
578 unsigned char buf[ bb_size ];
579
580 // read small block one by one
581 unsigned long bytes = 0;
582 for( unsigned long i=0; ( i<blocks.size() ) & ( bytes<maxlen ); i++ )
583 {
584 unsigned long block = blocks[i];
585 if( block < 0 ) continue;
586
587 // find where the small-block exactly is
588 unsigned long pos = block * sb_size;
589 unsigned long bbindex = pos / bb_size;
590 if( bbindex >= sb_blocks.size() ) break;
591
592 loadBigBlock( sb_blocks[ bbindex ], buf, bb_size );
593
594 // copy the data
595 unsigned offset = pos % bb_size;
596 unsigned long p = (maxlen-bytes < bb_size-offset ) ? maxlen-bytes : bb_size-offset;
597 p = (sb_size<p ) ? sb_size : p;
598 memcpy( data + bytes, buf + offset, p );
599 bytes += p;
600 }
601
602 return bytes;
603}
604
605unsigned long StorageIO::loadSmallBlock( unsigned long block,
606 unsigned char* data, unsigned long maxlen )
607{
608 // sentinel
609 if( !data ) return 0;
610 if( !file.good() ) return 0;
611 if( block < 0 ) return 0;
612
613 // wraps call for loadSmallBlocks
614 std::vector<unsigned long> blocks;
615 blocks.resize( 1 );
616 blocks.assign( 1, block );
617
618 return loadSmallBlocks( blocks, data, maxlen );
619}
620
621// recursive function to construct directory tree
622Entry* StorageIO::buildTree( Entry* parent, int index, const unsigned char* dirent )
623{
624 Entry* entry = (Entry*) 0L;
625
626 // find where to start
627 unsigned p = index * 128;
628
629 // would be < 32 if first char in the name isn't printable
630 unsigned prefix = 32;
631
632 // parse name of this entry, which stored as Unicode 16-bit
633 std::string name;
634 int name_len = readU16( dirent + 64+p );
635 for( int j=0; ( dirent[j+p]) && (j<name_len); j+= 2 )
636 name.append( 1, dirent[j+p] );
637
638 // emtpy name ?
639 if( !name.length() ) return entry;
640
641 // first char isn't printable ? remove it...
642 if( dirent[p] < 32 ){ prefix = dirent[0]; name.erase( 0,1 ); }
643
644 // type of this entry will decide which Entry should be created
645 // 2 = file (aka stream), 1 = directory (aka storage), 5 = root
646 unsigned type = dirent[ 0x42 + p];
647 if( ( type == 2 ) || ( type == 1 ) || ( type == 5 ) ) entry = new Entry();
648 if( entry ) entry->dir = ( ( type = 1 ) || ( type == 5 ) );
649
650 // barf on error
651 if( !entry ) return entry;
652
653 // fetch important data
654 entry->name = name;
655 entry->start = readU32( dirent + 0x74+p );
656 entry->size = readU32( dirent + 0x78+p );
657
658 // append as another child
659 entry->parent = parent;
660 if( parent ) parent->children.push_back( entry );
661
662 // check previous
663 int prev = readU32( dirent + 0x44+p );
664 if( prev >= 0 ) buildTree( parent, prev, dirent );
665
666 // traverse to sub
667 int dir = readU32( dirent + 0x4C+p );
668 if( entry->dir && (dir > 0 ))
669 buildTree( entry, dir, dirent );
670
671 // check next
672 int next = readU32( dirent + 0x48+p );
673 if( next >= 0 ) buildTree( parent, next, dirent );
674 return entry;
675}
676
677// given an entry, find a complete path from root
678std::string StorageIO::fullName( Entry* e )
679{
680 if( !e ) return (const char*) 0L;
681
682 std::string result;
683
684 while( e->parent )
685 {
686 result.insert( 0, e->name );
687 result.insert( 0, "/" );
688 e = e->parent;
689 }
690
691 // don't use specified root name (like "Root Entry")
692 if( !result.length() ) result = "/";
693
694 return result;
695}
696
697// given a fullname (e.g "/ObjectPool/_1020961869"), find the entry
698Entry* StorageIO::entry( const std::string& name )
699{
700 Entry* entry = (Entry*) 0L;
701
702 // sanity check
703 if( !root ) return (Entry*) 0L;
704 if( !name.length() ) return (Entry*) 0L;
705
706 // start from root when name is absolute
707 // or current directory when name is relative
708 entry = (name[0] == '/' ) ? root : current_dir;
709
710 // split the names, e.g "/ObjectPool/_1020961869" will become:
711 // "ObjectPool" and "_1020961869"
712 std::list<std::string> names;
713 std::string::size_type start = 0, end = 0;
714 while( start < name.length() )
715 {
716 end = name.find_first_of( '/', start );
717 if( end == std::string::npos ) end = name.length();
718 names.push_back( name.substr( start, end-start ) );
719 start = end+1;
720 }
721
722 std::list<std::string>::iterator it;
723 for( it = names.begin(); it != names.end(); ++it )
724 {
725 std::string entryname = *it;
726 Entry *child = (Entry*) 0L;
727 if( entry->dir )
728 for( unsigned j=0; j < entry->children.size(); j++ )
729 if( entry->children[j]->name == entryname )
730 child = entry->children[j];
731 if( !child ) return (Entry*) 0L;
732 entry = child;
733 }
734
735 return entry;
736}
737
738// =========== StreamIO ==========
739
740StreamIO::StreamIO( StorageIO* _io, Entry* _entry ):
741 io( _io ), entry( _entry ), m_pos( 0 ),
742 cache_data( 0 ), cache_size( 0 ), cache_pos( 0 )
743{
744 blocks = ( entry->size >= io->threshold ) ? io->bb.follow( entry->start ) :
745 io->sb.follow( entry->start );
746
747 // prepare cache
748 cache_size = 4096; // optimal ?
749 cache_data = new unsigned char[cache_size];
750 updateCache();
751}
752
753// FIXME tell parent we're gone
754StreamIO::~StreamIO()
755{
756 delete[] cache_data;
757}
758
759void StreamIO::seek( unsigned long pos )
760{
761 m_pos = pos;
762}
763
764unsigned long StreamIO::tell()
765{
766 return m_pos;
767}
768
769int StreamIO::getch()
770{
771 // past end-of-file ?
772 if( m_pos > entry->size ) return -1;
773
774 // need to update cache ?
775 if( !cache_size || ( m_pos < cache_pos ) ||
776 ( m_pos >= cache_pos + cache_size ) )
777 updateCache();
778
779 // something bad if we don't get good cache
780 if( !cache_size ) return -1;
781
782 int data = cache_data[m_pos - cache_pos];
783 m_pos++;
784
785 return data;
786}
787
788unsigned long StreamIO::read( unsigned long pos, unsigned char* data, unsigned long maxlen )
789{
790 // sanity checks
791 if( !data ) return 0;
792 if( maxlen == 0 ) return 0;
793
794 unsigned long totalbytes = 0;
795
796 if ( entry->size < io->threshold )
797 {
798 // small file
799 unsigned long index = pos / io->sb_size;
800
801 if( index >= blocks.size() ) return 0;
802
803 unsigned char buf[ io->sb_size ];
804 unsigned long offset = pos % io->sb_size;
805 while( totalbytes < maxlen )
806 {
807 if( index >= blocks.size() ) break;
808 io->loadSmallBlock( blocks[index], buf, io->bb_size );
809 unsigned long count = io->sb_size - offset;
810 if( count > maxlen-totalbytes ) count = maxlen-totalbytes;
811 memcpy( data+totalbytes, buf + offset, count );
812 totalbytes += count;
813 offset = 0;
814 index++;
815 }
816
817 }
818 else
819 {
820 // big file
821 unsigned long index = pos / io->bb_size;
822
823 if( index >= blocks.size() ) return 0;
824
825 unsigned char buf[ io->bb_size ];
826 unsigned long offset = pos % io->bb_size;
827 while( totalbytes < maxlen )
828 {
829 if( index >= blocks.size() ) break;
830 io->loadBigBlock( blocks[index], buf, io->bb_size );
831 unsigned long count = io->bb_size - offset;
832 if( count > maxlen-totalbytes ) count = maxlen-totalbytes;
833 memcpy( data+totalbytes, buf + offset, count );
834 totalbytes += count;
835 index++;
836 offset = 0;
837 }
838
839 }
840
841 return totalbytes;
842}
843
844unsigned long StreamIO::read( unsigned char* data, unsigned long maxlen )
845{
846 unsigned long bytes = read( tell(), data, maxlen );
847 m_pos += bytes;
848 return bytes;
849}
850
851void StreamIO::updateCache()
852{
853 // sanity check
854 if( !cache_data ) return;
855
856 cache_pos = m_pos - ( m_pos % cache_size );
857 unsigned long bytes = cache_size;
858 if( cache_pos + bytes > entry->size ) bytes = entry->size - cache_pos;
859 cache_size = read( cache_pos, cache_data, bytes );
860}
861
862
863// =========== Storage ==========
864
865Storage::Storage()
866{
867 io = (StorageIO*) 0L;
868 result = Storage::Ok;
869}
870
871Storage::~Storage()
872{
873 close();
874 delete io;
875}
876
877bool Storage::open( const char* fileName, int m )
878{
879 // only a few modes accepted
880 if( ( m != ReadOnly ) && ( m != WriteOnly ) && ( m != ReadWrite ) )
881 {
882 result = UnknownError;
883 return false;
884 }
885
886 io = new StorageIO( this, fileName, m );
887
888 result = io->result;
889
890 return result == Storage::Ok;
891}
892
893void Storage::flush()
894{
895 if( io ) io->flush();
896}
897
898void Storage::close()
899{
900 flush();
901}
902
903// list all files and subdirs in current path
904std::list<std::string> Storage::listDirectory()
905{
906 std::list<std::string> entries;
907
908 // sanity check
909 if( !io ) return entries;
910 if( !io->current_dir ) return entries;
911
912 // sentinel: do nothing if not a directory
913 if( !io->current_dir->dir ) return entries;
914
915 // find all children belongs to this directory
916 for( unsigned i = 0; i<io->current_dir->children.size(); i++ )
917 {
918 Entry* e = io->current_dir->children[i];
919 if( e ) entries.push_back( e->name );
920 }
921
922 return entries;
923}
924
925// enters a sub-directory, returns false if not a directory or not found
926bool Storage::enterDirectory( const std::string& directory )
927{
928 // sanity check
929 if( !io ) return false;
930 if( !io->current_dir ) return false;
931
932 // look for the specified sub-dir
933 for( unsigned i = 0; i<io->current_dir->children.size(); i++ )
934 {
935 Entry* e = io->current_dir->children[i];
936 if( e ) if( e->name == directory )
937 if ( e->dir )
938 {
939 io->current_dir = e;
940 return true;
941 }
942 }
943
944 return false;
945}
946
947// goes up one level (like cd ..)
948void Storage::leaveDirectory()
949{
950 // sanity check
951 if( !io ) return;
952 if( !io->current_dir ) return;
953
954 Entry* parent = io->current_dir->parent;
955 if( parent ) if( parent->dir )
956 io->current_dir = parent;
957}
958
959// note: without trailing "/"
960std::string Storage::path()
961{
962 // sanity check
963 if( !io ) return std::string();
964 if( !io->current_dir ) return std::string();
965
966 return io->fullName( io->current_dir );
967}
968
969Stream* Storage::stream( const std::string& name )
970{
971 // sanity check
972 if( !name.length() ) return (Stream*) 0L;
973 if( !io ) return (Stream*) 0L;
974
975 // make absolute if necesary
976 std::string fullName = name;
977 if( name[0] != '/' ) fullName.insert( 0, path() + "/" );
978
979 // find to which entry this stream associates
980 Entry* entry = io->entry( name );
981 if( !entry ) return (Stream*) 0L;
982
983 StreamIO* sio = new StreamIO( io, entry );
984 Stream* s = new Stream( sio );
985
986 return s;
987}
988
989// =========== Stream ==========
990
991Stream::Stream( StreamIO* _io ):
992 io( _io )
993{
994}
995
996// FIXME tell parent we're gone
997Stream::~Stream()
998{
999 delete io;
1000}
1001
1002unsigned long Stream::tell()
1003{
1004 return io ? io->tell() : 0;
1005}
1006
1007void Stream::seek( unsigned long newpos )
1008{
1009 if( io ) io->seek( newpos );
1010}
1011
1012unsigned long Stream::size()
1013{
1014 return io ? io->entry->size : 0;
1015}
1016
1017int Stream::getch()
1018{
1019 return io ? io->getch() : -1;
1020}
1021
1022unsigned long Stream::read( unsigned char* data, unsigned long maxlen )
1023{
1024 return io ? io->read( data, maxlen ) : 0;
1025}
1026