diff --git a/.gitattributes b/.gitattributes index 3da33d55b8..b4f7088ac6 100644 --- a/.gitattributes +++ b/.gitattributes @@ -317,6 +317,8 @@ Common/ASCDocxFormat/Lib/Release/Common.lib svn_mime_002dtype=application%2Focte Common/ASCDocxFormat/Lib/Release/DocxFormat.lib svn_mime_002dtype=application%2Foctet-stream Common/ASCDocxFormat/Lib/Release/Utility.lib svn_mime_002dtype=application%2Foctet-stream Common/ASCDocxFormat/Lib/Release/XML.lib svn_mime_002dtype=application%2Foctet-stream +Common/DocxFormat/Source/CompoundDocument svnc_tsvn_003alogminsize=5 +Common/DocxFormat/Source/CompoundDocument/detail svnc_tsvn_003alogminsize=5 Common/DocxFormat/Source/SystemUtility/Solution/FileSystemTest/app.ico svn_mime_002dtype=application%2Foctet-stream Common/DocxFormat/Source/XML/libxml2/LIBXML2_LIB_TEST svnc_tsvn_003alogminsize=5 Common/DocxFormat/Source/XML/libxml2/XML/doc/DOM.gif svn_mime_002dtype=application%2Foctet-stream diff --git a/Common/DocxFormat/Source/CompoundDocument/detail/alloctable.hpp b/Common/DocxFormat/Source/CompoundDocument/detail/alloctable.hpp new file mode 100644 index 0000000000..7a0a075bdc --- /dev/null +++ b/Common/DocxFormat/Source/CompoundDocument/detail/alloctable.hpp @@ -0,0 +1,205 @@ +/* POLE - Portable C++ library to access OLE Storage + Copyright (C) 2005-2006 Jorge Lodos Vigil + Copyright (C) 2002-2005 Ariya Hidayat + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of the authors nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// AllocTableT header +#pragma once + +#include +#include +#include "util.hpp" + +namespace POLE +{ + +template +class AllocTableT +{ +public: + static const ULONG32 Eof; + static const ULONG32 Avail; + static const ULONG32 Bat; + static const ULONG32 MetaBat; + +// Construction/destruction +public: + AllocTableT(ULONG32 block_size = 4096) { set_block_size(block_size); } + +// Attributes +public: + size_t count() const { return _data.size(); } // number of blocks + ULONG32 block_size() const { return _block_size; } // block size + ULONG32 operator[]( size_t index ) const { return _data[index]; } + bool follow( ULONG32 start, std::vector& chain ) const; + +// Operations +public: + void set_block_size(ULONG32 size) { _block_size = size; _data.clear(); resize( 128 ); } + void set_chain( const std::vector& chain ); + + bool load( const unsigned char* buffer, size_t len ); + bool save( unsigned char* buffer, size_t len ); +#ifndef NDEBUG + void debug() const; +#endif + +// Implementation +private: + void resize( size_t newsize ) { _data.resize( newsize, Avail); } + size_t unused(); + void preserve( size_t n ) { unused(); } + void set( size_t index, ULONG32 val ); + + std::vector _data; + ULONG32 _block_size; + + AllocTableT( const AllocTableT& ); // No copy construction + AllocTableT& operator=( const AllocTableT& ); // No copy operator +}; + +typedef AllocTableT AllocTable; + +// =========== AllocTableT Implementation ========== + +template +const ULONG32 AllocTableT<_>::Avail = 0xffffffff; +template +const ULONG32 AllocTableT<_>::Eof = 0xfffffffe; +template +const ULONG32 AllocTableT<_>::Bat = 0xfffffffd; +template +const ULONG32 AllocTableT<_>::MetaBat = 0xfffffffc; + + +template +void AllocTableT<_>::set( size_t index, ULONG32 value ) +{ + if ( index >= count() ) + resize( index + 1); + _data[ index ] = value; +} + +template +void AllocTableT<_>::set_chain( const std::vector& chain ) +{ + size_t chain_size = chain.size(); + if ( chain_size ) + { + for( size_t i = 0; i +bool AllocTableT<_>::follow( ULONG32 start, std::vector& chain ) const +{ + //assert(chain.size() == 0); + + size_t blocks = count(); + if( start >= blocks ) + return false; + + ULONG32 p = start; + chain.reserve(blocks); + for (size_t loop_control = 0; p < blocks; ++loop_control) + { + if (loop_control >= blocks) + return false; + chain.push_back( p ); + p = _data[ p ]; + } + + return true; +} + +template +size_t AllocTableT<_>::unused() +{ + // find first available block + size_t blocks = count(); + for( size_t i = 0; i < blocks; i++ ) + if( _data[i] == Avail ) + return i; + + // completely full, so enlarge the table + resize( blocks + 10 ); + return count(); +} + +template +bool AllocTableT<_>::load( const unsigned char* buffer, size_t len ) +{ + if (len%4 || !buffer) + return false; + + resize( len / 4 ); + size_t blocks = count(); + for( size_t i = 0; i < blocks; i++ ) + _data[ i ] = readU32( buffer + i*4 ); + + return true; +} + +template +bool AllocTableT<_>::save( unsigned char* buffer, size_t len ) +{ + if (len < 4*count() || !buffer) + return false; + + size_t blocks = count(); + for( size_t i = 0; i < blocks; i++ ) + writeU32( buffer + i*4, _data[i] ); + + return true; +} + +#ifndef NDEBUG +template +void AllocTableT<_>::debug() const +{ + std::cout << "block size " << _data.size() << std::endl; + for( size_t i=0; i< _data.size(); i++ ) + { + if( _data[i] == Avail ) + continue; + std::cout << i << ": "; + if( _data[i] == Eof ) + std::cout << "[eof]"; + else if( _data[i] == Bat ) + std::cout << "[bat]"; + else if( _data[i] == MetaBat ) + std::cout << "[metabat]"; + else std::cout << _data[i]; + std::cout << std::endl; + } +} +#endif + +} // namespace POLE diff --git a/Common/DocxFormat/Source/CompoundDocument/detail/dirtree.hpp b/Common/DocxFormat/Source/CompoundDocument/detail/dirtree.hpp new file mode 100644 index 0000000000..d5cf590971 --- /dev/null +++ b/Common/DocxFormat/Source/CompoundDocument/detail/dirtree.hpp @@ -0,0 +1,791 @@ +/* POLE - Portable C++ library to access OLE Storage + Copyright (C) 2005-2006 Jorge Lodos Vigil + Copyright (C) 2002-2005 Ariya Hidayat + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of the authors nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// dirtree header +#pragma once + +#include +#include +#include +#include "util.hpp" + +namespace POLE +{ + +template +class DirEntryT +{ +public: + static const unsigned End; + +// Construction/destruction +public: + DirEntryT(): + _name("Root Entry"), + _type(5), + _size(0), + _start(End), + _prev(End), + _next(End), + _child(End), + _index(0), + _parent(0) + {} + DirEntryT(const std::string& name, ULONG8 type, ULONG32 size, ULONG32 start, ULONG32 prev, ULONG32 next, ULONG32 child, ULONG32 index, ULONG32 parent) + { + set(name, type, size, start, prev, next, child, index, parent); + } + +// Attributes +public: + bool valid() const; + const std::string& name() const { return _name; } + ULONG8 type() const { return _type; } + bool root() const { return (_type == 5); } + bool dir() const { return ((_type == 1) || (_type == 5)); } + bool file() const { return (_type == 2); } + ULONG32 size() const { return _size; } + ULONG32 start() const { return _start; } + ULONG32 prev() const { return _prev; } + ULONG32 next() const { return _next; } + ULONG32 child() const { return _child; } + ULONG32 index() const { return _index; } + ULONG32 parent() const { return _parent; } + +// Operations +public: + void set(const std::string& name, ULONG8 type, ULONG32 size, ULONG32 start, ULONG32 prev, ULONG32 next, ULONG32 child, ULONG32 index, ULONG32 parent) + { + _name = name; + _type = type; + _size = size; + _start = start; + _prev = prev; + _next = next; + _child = child; + _index = index; + _parent = parent; + } + void set_prev(ULONG32 prev) { _prev = prev; } + void set_next(ULONG32 next) { _next = next; } + void set_child(ULONG32 child) { _child = child; } + void set_parent(ULONG32 parent) { _parent = parent; } +#ifndef NDEBUG + void debug() const; +#endif + +// Implementation +private: + std::string _name; // the name, not in unicode anymore + ULONG8 _type; // true if directory + ULONG32 _size; // size (not valid if directory) + ULONG32 _start; // starting block + ULONG32 _prev; // previous sibling + ULONG32 _next; // next sibling + ULONG32 _child; // first child + ULONG32 _index; // index of the entry in the directory + ULONG32 _parent; // parent in the directory structure. Must be a folder. +}; + +typedef DirEntryT DirEntry; + +template +class DirTreeT +{ +// Construction/destruction +public: + DirTreeT() { clear(); } + +// Attributes +public: + size_t entryCount() const { return _entries.size(); } + void listAll(std::vector&) const; + ULONG32 parent( ULONG32 index ) const; + ULONG32 parentDirectory( ULONG32 index ) const; + void fullName( ULONG32 index, std::string& ) const; + void current_path( std::string& result) const { fullName( _current, result ); } + const DirEntry* root_entry() const { return entry( 0 ); } + const DirEntry* current_entry() const { return entry( _current ); } + void children( ULONG32 index, std::vector& ) const; + void listDirectory(std::vector&) const; + const DirEntry* entry( ULONG32 index ) const; + +// Operations +public: + void clear(); + const DirEntry* entry( const std::string& name, bool create=false ) { return _entry(name, create); } + bool enterDirectory( const std::string& dir ); + void leaveDirectory(); + + bool delete_entry(const std::string& path) { return delete_entry(path, 0); } + ULONG32 search_prev_link( ULONG32 entry ); + ULONG32 find_rightmost_sibling(ULONG32 left_sib); + bool set_prev_link(ULONG32 prev_link, ULONG32 entry, ULONG32 value); + + bool load( unsigned char* buffer, size_t len ); + bool save( unsigned char* buffer, size_t len ); +#ifndef NDEBUG + void debug() const; +#endif + +// Implementation +private: + DirEntry* entry( ULONG32 index ); + DirEntry* _entry( const std::string& name, bool create = false ); + void find_siblings( std::vector& result, ULONG32 index ) const; + bool delete_entry(const std::string& path, int level); + void set_parents(); + void set_parents(DirEntry * cur_entry, ULONG32 cur_parent); + + ULONG32 _current; + std::vector _entries; + + DirTreeT( const DirTreeT<_>& ); + DirTreeT<_>& operator=( const DirTreeT<_>& ); +}; + +typedef DirTreeT DirTree; + +// =========== DirEntryT ========== + +template +const unsigned DirEntryT<_>::End = 0xffffffff; + +template +bool DirEntryT<_>::valid() const +{ + //if ((_type == 1 ) && (_size != 0 )) + // return false; + if ((_type != 1 ) && (_type != 2 ) && (_type != 5 )) + return false; + if (_name.empty()) + return false; + + return true; +} + +#ifndef NDEBUG +template +void DirEntryT<_>::debug() const +{ + if( !valid() ) std::cout << "INVALID "; + std::cout << name() << " "; + if( valid() ) std::cout << "(Valid) "; + else std::cout << "(Invalid) "; + if( dir() ) std::cout << "(Dir) "; + else std::cout << "(File) "; + std::cout << index() << " "; + std::cout << size() << " "; + std::cout << "s:" << start() << " "; + std::cout << "("; + if( child() == DirEntry::End ) std::cout << "-"; else std::cout << child(); + std::cout << " "; + if( prev() == DirEntry::End ) std::cout << "-"; else std::cout << prev(); + std::cout << ":"; + if( next() == DirEntry::End ) std::cout << "-"; else std::cout << next(); + std::cout << ") "; + std::cout << parent(); + std::cout << std::endl; +} +#endif + +// =========== DirTree ========== + +template +void DirTreeT<_>::clear() +{ + // leave only the root entry + _entries.resize( 1 ); + _current = 0; +} + +template +const DirEntry* DirTreeT<_>::entry( ULONG32 index ) const +{ + if( index >= entryCount() ) return NULL; + return &_entries[ index ]; +} + +template +DirEntry* DirTreeT<_>::entry( ULONG32 index ) +{ + if( index >= entryCount() ) return NULL; + return &_entries[ index ]; +} + +template +ULONG32 DirTreeT<_>::parentDirectory( ULONG32 index ) const +{ + const DirEntry * e = entry(index); + if (!e) return DirEntry::End; + assert(e->valid()); + +#ifndef NDEBUG + const DirEntry * test = entry(e->parent()); + assert(test); + assert(test->valid()); + assert(test->dir()); +#endif + + return e->parent(); +} + +template +ULONG32 DirTreeT<_>::parent( ULONG32 index ) const +{ + // brute-force, basically we iterate for all entries, find its children + // and check if one of the children is 'index' + std::vector::const_iterator it; + for (it = _entries.begin(); it != _entries.end(); ++it) + if (it->valid() && it->child() == index) + return it->index(); + + return DirEntry::End; +} + +template +void DirTreeT<_>::fullName( ULONG32 index, std::string& result ) const +{ + const DirEntry* e = entry( index ); + if( !e ) + return; + + // don't use root name ("Root Entry"), just give "/" + if( index == 0 ) + { + result = "/"; + return; + } + + result = e->name(); + result.insert( 0, "/" ); + ULONG32 p = parentDirectory( index ); + if (p == -1) + return; + const DirEntry* _entry = 0; + while( p > 0 ) + { + _entry = entry( p ); + if (_entry && _entry->dir() && _entry->valid()) + { + result.insert( 0, _entry->name()); + result.insert( 0, "/" ); + } + p = parentDirectory(p); + if (p == DirEntry::End) + break; + } +} + +// Given a fullname (e.g "/ObjectPool/_1020961869"), find the entry. +// If not found and create is false, return 0. +// If create is true, and name ends with '/' a new dir entry is returned. +// If create is true, and name does not end with '/' a new file entry is returned. +template +DirEntry* DirTreeT<_>::_entry( const std::string& name, bool create ) +{ + if( name.empty() ) + return NULL; + + // quick check for "/" (that's root) + if( name == "/" ) return &_entries[0]; + + // get the parent and child, e.g "/ObjectPool/_1020961869" will become: + // "/ObjectPool" and "_1020961869". "/Data" will become "/" and "Data". + + std::string parent; + std::string child; + std::string::size_type name_pos = name.rfind('/'); + if ( name_pos == name.length()-1) + name_pos = name.rfind('/', name_pos); + if (name_pos != std::string::npos && name_pos != 0) + { + parent = name.substr( 0, name_pos ); + child = name.substr(name_pos+1); + } + else if (name_pos == 0) + { + parent = "/"; + child = name.substr(1); + } + else + child = name; + if (child[child.length()-1] == '/') + child.erase(child.length()-1); + + // start from root when name is absolute + // or current directory when name is relative + ULONG32 index = (name[0] == '/' ) ? 0 : _current; + + const DirEntry* pe = (parent.empty()) ? entry( index ) : entry( parent, create ); + if (!pe) return NULL; + if (!pe->valid()) return NULL; + assert(pe->dir()); + + // search entries with the same parent + std::vector::iterator it; + for (it = _entries.begin(); it != _entries.end(); ++it) + if (pe->index() == it->parent() && it->valid()) + if (it->name().length() == child.length()) + { + size_t j; + for (j = 0; jname().length(); ++j) + if( tolower(child[j]) != tolower(it->name()[j]) ) + break; + if (j == it->name().length()) + return &(*it); // found + } + + if (!create) + return NULL; // not found + + // create a new entry + assert(parent.empty()); + DirEntry* ep = &_entries[index]; + ULONG8 type = (name[name.length()-1] == '/') ? 1 : 2; + index = (ULONG32)entryCount(); + DirEntry ne(child, type, 0, 0, DirEntry::End, DirEntry::End, ep->child(), index, ep->index()); + _entries.push_back( ne ); + ep->set_child(index); + return entry( index ); +} + +template +void DirTreeT<_>::children( ULONG32 index, std::vector& result ) const +{ + const DirEntry* e = entry( index ); + if( e && e->valid() && e->dir() ) + find_siblings( result, e->child() ); +} + +template +void DirTreeT<_>::listAll(std::vector& result) const +{ + size_t size = _entries.size(); + result.reserve(size); + for( size_t i = 0; i < size; i++ ) + if (_entries[i].valid()) + result.push_back( &_entries[i] ); +} + +template +void DirTreeT<_>::listDirectory(std::vector& result) const +{ + std::vector chi; + children( _current, chi ); + size_t size = chi.size(); + result.reserve(size); + for( size_t i = 0; i < size; i++ ) + result.push_back( entry( chi[i] ) ); +} + +template +bool DirTreeT<_>::enterDirectory( const std::string& dir ) +{ + const DirEntry* e = entry( dir ); + if( !e ) + return false; + if( !e->valid() ) + return false; + if( !e->dir() ) + return false; + + ULONG32 index = e->index(); + if( index == DirEntry::End) return false; + + _current = index; + return true; +} + +template +void DirTreeT<_>::leaveDirectory() +{ + // already at root ? + if( _current == 0 ) + return; + + ULONG32 p = parentDirectory( _current ); + if( p != DirEntry::End) + _current = p; +#ifndef NDEBUG + else + assert(false); +#endif + } + +template +bool DirTreeT<_>::load( unsigned char* buffer, size_t size ) +{ + _entries.clear(); + _current = 0; + size_t init_count = size / 128; + + _entries.reserve(init_count); + for( unsigned i = 0; i < init_count; i++ ) + { + unsigned p = i * 128; + + // Name's length + int name_len = readU16( buffer + 0x40+p ); + // parse name of this entry, which stored as Unicode 16-bit + std::string name; + name.reserve(name_len); + // Read name char by char + if( name_len > 64 ) name_len = 64; + for( int j=0; ( buffer[j + p]) && (j < name_len); j+= 2 ) + name.append( 1, buffer[j + p] ); + + // 1 = directory (aka storage), 2 = file (aka stream), 5 = root + ULONG8 type = buffer[ 0x42 + p]; + ULONG32 start = readU32( buffer + 0x74+p ); + ULONG32 size = readU32( buffer + 0x78+p ); + ULONG32 prev = readU32( buffer + 0x44+p ); + ULONG32 next = readU32( buffer + 0x48+p ); + ULONG32 child = readU32( buffer + 0x4C+p ); + + DirEntry e(name, type, size, start, prev, next, child, i, 0); + _entries.push_back( e ); + } + set_parents(); + + return true; +} + +template +bool DirTreeT<_>::save( unsigned char* buffer, size_t len ) +{ + size_t size = 128*entryCount(); + if (len < size) + return false; + memset( buffer, 0, size ); + + for( unsigned i = 0; i < entryCount(); i++ ) + { + const DirEntry* e = entry( i ); + if( !e ) return false; + + // max length for name is 32 chars + std::string name = e->name(); + if( name.length() > 32 ) + name.erase( 32, name.length() ); + + // write name as Unicode 16-bit + for( unsigned j = 0; j < name.length(); j++ ) + buffer[ i*128 + j*2 ] = name[j]; + + writeU16( buffer + i*128 + 0x40, (ULONG16)(name.length()*2 + 2) ); + writeU32( buffer + i*128 + 0x74, e->start() ); + writeU32( buffer + i*128 + 0x78, e->size() ); + writeU32( buffer + i*128 + 0x44, e->prev() ); + writeU32( buffer + i*128 + 0x48, e->next() ); + writeU32( buffer + i*128 + 0x4c, e->child() ); + buffer[ i*128 + 0x42 ] = e->type(); + buffer[ i*128 + 0x43 ] = 1; // always black + } + return true; +} + +#ifndef NDEBUG +template +void DirTreeT<_>::debug() const +{ + for( unsigned i = 0; i < entryCount(); i++ ) + { + const DirEntry* e = entry( i ); + if( !e ) continue; + std::cout << i << ": "; + e->debug(); + } + std::vector res; + children(0, res); + std::cout << std::endl << std::endl << "--------------------------" << std::endl; + for (unsigned int i = 0; i < res.size(); ++i) + { + std::cout << res[i] << std::endl; + } +} +#endif + +// helper function: recursively find siblings of index +template +void DirTreeT<_>::find_siblings( std::vector& result, ULONG32 index ) const +{ + const DirEntry* e = entry( index ); + if( !e ) return; + if( !e->valid() ) return; + + // search entries with the same parent + result.reserve(_entries.size()); // for efficiency + std::vector::const_iterator it; + for (it = _entries.begin(); it != _entries.end(); ++it) + if (e->parent() == it->parent() && it->valid() && !it->root()) + { + result.push_back(it->index()); + } +} + +template +bool DirTreeT<_>::delete_entry(const std::string& path, int level) +{ + // Deletion is not posible over Root Entry + if (path == "/") + return false; + DirEntry *e = _entry(path); + // Hack: Should an invalid path throw and exception? + if (!e) + return false; + // Is a Storage? + if (e->type() == 1) + { + // Has Child? + if (e->child() != DirEntry::End) + { + DirEntry *child = entry(e->child()); + std::string _child_path = path + ((path[path.size()] == '/') ? child->name() : std::string("/") + child->name()); + level++; + if (!delete_entry(_child_path, level)) + return false; + e->set_child(DirEntry::End); + level--; + } + } + // This is not the first call. Searching for brothers + if (level) + { + // Has right sibling? + if (e->prev() != DirEntry::End) + { + // Find the previous entry + DirEntry *pe = entry(e->prev()); + if (!pe) + return false; + std::string _child_path; + std::string::size_type _pos = path.find_last_of("/"); + + if (_pos != std::string::npos) + _child_path = path.substr(0, path.size() - ((path.size() - _pos) - 1)) + pe->name(); + else + return false; + + level++; + if (!delete_entry(_child_path, level)) + return false; + e->set_prev(DirEntry::End); + level--; + } + // Has left sibling? + if (e->next() != DirEntry::End) + { + // Find the previous entry + DirEntry *pe = entry(e->next()); + if (!pe) + return false; + std::string _child_path; + std::string::size_type _pos = path.find_last_of("/"); + + if (_pos != std::string::npos) + _child_path = path.substr(0, path.size() - ((path.size() - _pos) - 1)) + pe->name(); + else + return false; + + level++; + if (!delete_entry(_child_path, level)) + return false; + e->set_next(DirEntry::End); + level--; + } + } + + // Delete the entry + if (level == 0) + { + // This is not an recursive call + // Find the entry that points to the entry is being deleted + size_t prev_link = search_prev_link(e->index()); + if (prev_link == -1) + return false; + // Last entry? + if (e->next() == DirEntry::End && + e->prev() == DirEntry::End) + { + // Set the end of chain mark in the entry that points to the entry + // being deleted + if (!set_prev_link(prev_link, e->index(), DirEntry::End)) + return false; + } + else + { + // It is not the last one, but has a sibling + if(e->next() == DirEntry::End || + e->prev() == DirEntry::End) + { + // If hasn't a previous sibling, set previous link to point + // to the next field of the entry being deleted + if (e->prev() == DirEntry::End) + { + if (!set_prev_link(prev_link, e->index(), e->next())) + return false; + } + else + // If hasn't a next sibling, set previous link to point + // to the prev field of the entry being deleted + if (!set_prev_link(prev_link, e->index(), e->prev())) + return false; + } + else + { + // If has a previous and a next sibling, find the right most sibling + // pointed by the next field of the entry being deleted, set this entry's previous field + // to point to the prev field of the entry being deleted. + // Then set previous link to point to the next field of the entry being deleted. + size_t right_most = find_rightmost_sibling(e->next()); + if (right_most == -1) + return false; + DirEntry *_right = entry(right_most); + if (!_right) return false; + _right->set_prev(e->prev()); + + if (!set_prev_link(prev_link, e->index(), e->next())) + return false; + } + } + } + e->set("", 0, 0, 0, 0, DirEntry::End, DirEntry::End, DirEntry::End, 0); + + return true; +} + +template +ULONG32 DirTreeT<_>::search_prev_link( ULONG32 _entry ) +{ + // Find parent + size_t par_index = parent(_entry); + if (par_index == -1) + return false; + if (_entries[par_index].child() == _entry) + return par_index; + else + { + std::vector brothers; + children(par_index, brothers); + if (brothers.size() == 0) + return false; + for (size_t ndx = 0; ndx < brothers.size(); ++ndx) + { + if (_entries[brothers[ndx]].next() == _entry || + _entries[brothers[ndx]].prev() == _entry) + { + return brothers[ndx]; + } + } + } + + return DirEntry::End; +} + +template +bool DirTreeT<_>::set_prev_link(ULONG32 prev_link, ULONG32 index, ULONG32 value) +{ + DirEntry * pl = entry(prev_link); + if (!pl) return false; + + if (pl->prev() == index) + pl->set_prev(value); + if (pl->next() == index) + pl->set_next(value); + if (pl->child() == index) + pl->set_child(value); + + return true; +} + +template +ULONG32 DirTreeT<_>::find_rightmost_sibling(ULONG32 sib) +{ + DirEntry * _ent = entry(sib); + if (!_ent) + return DirEntry::End; + if (_ent->prev() == DirEntry::End) + return _ent->index(); + return find_rightmost_sibling(_ent->prev()); +} + +template +void DirTreeT<_>::set_parents() +{ + if (_entries.size()) + { + // The parent is already set to 0 + if (_entries[0].valid()) + { + DirEntry * _child = entry(_entries[0].child()); + if (_child && _child->valid()) + { + _child->set_parent(0); + set_parents(_child, 0); + } + } + } +} + +template +void DirTreeT<_>::set_parents(DirEntry * cur_entry, ULONG32 cur_parent) +{ + assert(cur_entry); + assert(cur_entry->valid()); + + if (cur_entry->dir()) + { + DirEntry * _child = entry(cur_entry->child()); + // the last condition prevents loops + if (_child && _child->valid() && _child->parent() == 0) + { + _child->set_parent(cur_entry->index()); + set_parents(_child, cur_entry->index()); + } + } + + DirEntry * _prev = entry(cur_entry->prev()); + // the last condition prevents loops + if (_prev && _prev->valid() && _prev->parent() == 0) + { + _prev->set_parent(cur_parent); + set_parents(_prev, cur_parent); + } + + DirEntry * _next = entry(cur_entry->next()); + // the last condition prevents loops + if (_next && _next->valid() && _next->parent() == 0) + { + _next->set_parent(cur_parent); + set_parents(_next, cur_parent); + } +} + +} diff --git a/Common/DocxFormat/Source/CompoundDocument/detail/header.hpp b/Common/DocxFormat/Source/CompoundDocument/detail/header.hpp new file mode 100644 index 0000000000..14f4172fe0 --- /dev/null +++ b/Common/DocxFormat/Source/CompoundDocument/detail/header.hpp @@ -0,0 +1,213 @@ +/* POLE - Portable C++ library to access OLE Storage + Copyright (C) 2005-2006 Jorge Lodos Vigil + Copyright (C) 2002-2005 Ariya Hidayat + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of the authors nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// header.hpp +#pragma once +#include "alloctable.hpp" + +namespace POLE +{ + +template +class HeaderT +{ +public: + static const unsigned char pole_magic[]; + +// Construction/destruction +public: + HeaderT(); + +// Attributes +public: + const unsigned char* id() const { return _id; } + unsigned b_shift() const { return _b_shift; } + unsigned s_shift() const { return _s_shift; } + unsigned num_bat() const { return _num_bat; } + unsigned dirent_start() const { return _dirent_start; } + unsigned threshold() const { return _threshold; } + unsigned sbat_start() const { return _sbat_start; } + unsigned num_sbat() const { return _num_sbat; } + unsigned mbat_start() const { return _mbat_start; } + unsigned num_mbat() const { return _num_mbat; } + const ULONG32* bb_blocks() const { return _bb_blocks; } + bool valid() const; + bool is_ole() const; + +// Operations +public: + bool load( const unsigned char* buffer, size_t len ); + bool save( unsigned char* buffer, size_t len ); +#ifndef NDEBUG + void debug() const; +#endif + +// Implementation +private: + unsigned char _id[8]; // signature, or magic identifier + unsigned _b_shift; // bbat->blockSize = 1 << b_shift [_uSectorShift] + unsigned _s_shift; // sbat->blockSize = 1 << s_shift [_uMiniSectorShift] + unsigned _num_bat; // blocks allocated for big bat [_csectFat] + unsigned _dirent_start; // starting block for directory info [_secDirStart] + unsigned _threshold; // switch from small to big file (usually 4K) [_ulMiniSectorCutoff] + unsigned _sbat_start; // starting block index to store small bat [_sectMiniFatStart] + unsigned _num_sbat; // blocks allocated for small bat [_csectMiniFat] + unsigned _mbat_start; // starting block to store meta bat [_sectDifStart] + unsigned _num_mbat; // blocks allocated for meta bat [_csectDif] + ULONG32 _bb_blocks[109]; // [_sectFat] +}; + +typedef HeaderT Header; + +// =========== HeaderT ========== + +template +const unsigned char HeaderT<_>::pole_magic[] = { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 }; + +template +HeaderT<_>::HeaderT() +{ + _b_shift = 9; + _s_shift = 6; + _num_bat = 0; + _dirent_start = 0; + _threshold = 4096; + _sbat_start = 0; + _num_sbat = 0; + _mbat_start = 0; + _num_mbat = 0; + + unsigned i = 0; + for(; i < 8; i++ ) + _id[i] = pole_magic[i]; + for( i=0; i<109; i++ ) + _bb_blocks[i] = AllocTable::Avail; +} + +template +bool HeaderT<_>::valid() const +{ + if( _threshold != 4096 ) return false; + if( _num_bat == 0 ) return false; //ok + if( (_num_bat > 109) && (_num_bat > ((_num_mbat * 127) + 109))) return false; //ok + if( (_num_bat < 109) && (_num_mbat != 0) ) return false; //ok + if( _s_shift > _b_shift ) return false; + if( _b_shift <= 6 ) return false; + if( _b_shift >=31 ) return false; + return true; +} + +template +bool HeaderT<_>::is_ole() const +{ + for( unsigned i=0; i<8; i++ ) + if( _id[i] != pole_magic[i] ) + return false; + return true; +} + +template +bool HeaderT<_>::load( const unsigned char* buffer, size_t len ) +{ + if (len < 0x4C+109 * 4 || !buffer) + return false; + + _b_shift = readU16( buffer + 0x1e ); + _s_shift = readU16( buffer + 0x20 ); + _num_bat = readU32( buffer + 0x2c ); + _dirent_start = readU32( buffer + 0x30 ); + _threshold = readU32( buffer + 0x38 ); + _sbat_start = readU32( buffer + 0x3c ); + _num_sbat = readU32( buffer + 0x40 ); + _mbat_start = readU32( buffer + 0x44 ); + _num_mbat = readU32( buffer + 0x48 ); + + unsigned int i = 0; + for( ; i < 8; i++ ) + _id[i] = buffer[i]; + for( i=0; i<109; i++ ) + _bb_blocks[i] = readU32( buffer + 0x4C+i*4 ); + + return true; +} + +template +bool HeaderT<_>::save( unsigned char* buffer, size_t len ) +{ + if (len<0x4C+109*4 || !buffer) + return false; + + memset( buffer, 0, 0x4c ); + memcpy( buffer, pole_magic, 8 ); // ole signature + writeU32( buffer + 8, 0 ); // unknown + writeU32( buffer + 12, 0 ); // unknown + writeU32( buffer + 16, 0 ); // unknown + writeU16( buffer + 24, 0x003e ); // revision ? + writeU16( buffer + 26, 3 ); // version ? + writeU16( buffer + 28, 0xfffe ); // unknown + writeU16( buffer + 0x1e, _b_shift ); + writeU16( buffer + 0x20, _s_shift ); + writeU32( buffer + 0x2c, _num_bat ); + writeU32( buffer + 0x30, _dirent_start ); + writeU32( buffer + 0x38, _threshold ); + writeU32( buffer + 0x3c, _sbat_start ); + writeU32( buffer + 0x40, _num_sbat ); + writeU32( buffer + 0x44, _mbat_start ); + writeU32( buffer + 0x48, _num_mbat ); + + for( unsigned i=0; i<109; i++ ) + writeU32( buffer + 0x4C+i*4, _bb_blocks[i] ); + + return true; +} + +#ifndef NDEBUG +template +void HeaderT<_>::debug() const +{ + std::cout << std::endl; + std::cout << "b_shift " << _b_shift << std::endl; + std::cout << "s_shift " << _s_shift << std::endl; + std::cout << "num_bat " << _num_bat << std::endl; + std::cout << "dirent_start " << _dirent_start << std::endl; + std::cout << "threshold " << _threshold << std::endl; + std::cout << "sbat_start " << _sbat_start << std::endl; + std::cout << "num_sbat " << _num_sbat << std::endl; + std::cout << "mbat_start " << _mbat_start << std::endl; + std::cout << "num_mbat " << _num_mbat << std::endl; + + unsigned s = (_num_bat<=109) ? _num_bat : 109; + std::cout << "bat blocks: "; + for( unsigned i = 0; i < s; i++ ) + std::cout << _bb_blocks[i] << " "; + std::cout << std::endl; +} +#endif + +} diff --git a/Common/DocxFormat/Source/CompoundDocument/detail/storage.hpp b/Common/DocxFormat/Source/CompoundDocument/detail/storage.hpp new file mode 100644 index 0000000000..5f69b00ce1 --- /dev/null +++ b/Common/DocxFormat/Source/CompoundDocument/detail/storage.hpp @@ -0,0 +1,600 @@ +/* POLE - Portable C++ library to access OLE Storage + Copyright (C) 2005-2006 Jorge Lodos Vigil + Copyright (C) 2002-2005 Ariya Hidayat + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of the authors nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// storage header +#pragma once + +#include +#include +#include "header.hpp" +#include "dirtree.hpp" + +#include "../../../../../../../DesktopEditor/common/File.h" + +namespace POLE +{ + +template +class StreamImplT; + +template +class StorageIOT +{ +public: + enum { Ok, OpenFailed, OpenSmallFatFailed, NotOLE, BadOLE, UnknownError, NewOLE, StupidWorkaroundForBrokenCompiler=255 }; + +// Construction/destruction +public: + StorageIOT( const char* filename, std::ios_base::openmode mode, bool create); + StorageIOT( const std::wstring& filename, std::ios_base::openmode mode, bool create); + StorageIOT( std::iostream* stream ); + ~StorageIOT(); + +// Attributes +public: + int result() const { return _result; } + const Header* header() const { return _header; } + const DirEntry* entry(const std::string& path, bool create = false) const { return _dirtree->entry(path, create); } + void fullName( const DirEntry* entry, std::string& name) const { _dirtree->fullName( entry->index(), name); } + void current_path( std::string& result) const { _dirtree->current_path(result); } + const DirEntry* root_entry() const { return _dirtree->root_entry(); } + const DirEntry* current_entry() const { return _dirtree->current_entry(); } + void listDirectory(std::list&) const; + ULONG32 small_block_size() const { return (_sbat) ? _sbat->block_size() : 0; } + ULONG32 big_block_size() const { return (_bbat) ? _bbat->block_size() : 0; } + void listEntries(std::vector& result) const + { + _dirtree->listDirectory(result); + } + void listAll(std::vector& result) const + { + _dirtree->listAll(result); + } + bool follow_small_block_table( ULONG32 start, std::vector& chain ) const + { + return (_sbat) ? _sbat->follow(start, chain) : false; + } + bool follow_big_block_table( ULONG32 start, std::vector& chain ) const + { + return (_bbat) ? _bbat->follow(start, chain) : false; + } + + const std::vector& sb_blocks() const { return _sb_blocks; } + + void get_entry_childrens(size_t index, std::vector result) const + { + _dirtree->children(index, result); + } + + void children( size_t index, std::vector& result ) const + { + if (_dirtree) + _dirtree->children(index, result); + } + +// Operations +public: + bool create( const char* filename ); + bool enterDirectory( const std::string& directory ) { return _dirtree->enterDirectory( directory ); } + void leaveDirectory() { _dirtree->leaveDirectory(); } + std::streamsize loadSmallBlocks( const std::vector& blocks, unsigned char* buffer, std::streamsize maxlen ); + std::streamsize loadBigBlocks( const std::vector& blocks, unsigned char* buffer, std::streamsize maxlen ); + std::streamsize loadBigBlock(ULONG32 block, unsigned char* buffer, std::streamsize maxlen); + std::streamsize saveBlock(ULONG32 block, const unsigned char* buffer, std::streamsize maxlen); + bool delete_entry(const std::string& path); + bool flush(); + void notify_dirtree_changed() { m_dtmodified = true; } +#ifndef NDEBUG + void debug() const; +#endif + + +// Implementation +private: + void init(); + bool load(); + void close(); + + std::iostream* _stream; + std::fstream* _file; + ULONG32 _size; // size of the storage stream + int _result; // result of last operation + std::vector _sb_blocks; // blocks for "small" files + + Header* _header; // storage header + DirTree* _dirtree; // directory tree + AllocTable* _bbat; // allocation table for big blocks + AllocTable* _sbat; // allocation table for small blocks + bool m_dtmodified; + + // no copy or assign + StorageIOT( const StorageIOT<_>& ); + StorageIOT<_>& operator=( const StorageIOT<_>& ); +}; + +typedef StorageIOT StorageIO; + +// =========== StorageIOT ========== + +template +StorageIOT<_>::StorageIOT( const char* filename, std::ios_base::openmode mode, bool create) +{ + m_dtmodified = false; + init(); + + // open the file, check for error + _result = OpenFailed; + mode |= std::ios_base::in; // we must always read + if (create) + mode |= std::ios_base::out | std::ios_base::trunc; // make sure the file will be created if needed + else + mode &= ~std::ios_base::trunc; // make sure the file won't be created + std::fstream* file = new std::fstream( filename, std::ios::binary | mode); + if( !file) + return; + if (file->fail()) + { + delete file; + return; + } + _file = file; + _stream = file; + if (!create) + load(); +} + +template +StorageIOT<_>::StorageIOT( const std::wstring& filename, std::ios_base::openmode mode, bool create) +{ +#ifdef WIN32 + m_dtmodified = false; + init(); + + // open the file, check for error + _result = OpenFailed; + mode |= std::ios_base::in; // we must always read + if (create) + mode |= std::ios_base::out | std::ios_base::trunc; // make sure the file will be created if needed + else + mode &= ~std::ios_base::trunc; // make sure the file won't be created + std::fstream* file = new std::fstream( filename.c_str(), std::ios::binary | mode); + if( !file) + return; + if (file->fail()) + { + delete file; + return; + } + _file = file; + _stream = file; + if (!create) + load(); +#else + m_dtmodified = false; + init(); + + // open the file, check for error + _result = OpenFailed; + mode |= std::ios_base::in; // we must always read + if (create) + mode |= std::ios_base::out | std::ios_base::trunc; // make sure the file will be created if needed + else + mode &= ~std::ios_base::trunc; // make sure the file won't be created + + BYTE* pData = NULL; + LONG lDataLen = 0; + NSFile::CUtf8Converter::GetUtf8StringFromUnicode(filename.c_str(), (LONG)filename.length(), pData, lDataLen); + + std::fstream* file = new std::fstream( (const char*)pData, std::ios::binary | mode); + + if (NULL != pData) + delete [] pData; + + if( !file) + return; + if (file->fail()) + { + delete file; + return; + } + _file = file; + _stream = file; + if (!create) + load(); +#endif +} + +template +StorageIOT<_>::StorageIOT( std::iostream* stream ) +{ + init(); + _result = OpenFailed; + _stream = stream; + load(); +} + +template +StorageIOT<_>::~StorageIOT() +{ + flush(); + close(); + if (_sbat) delete _sbat; + if (_bbat) delete _bbat; + delete _dirtree; + delete _header; +} + +template +void StorageIOT<_>::init() +{ + _result = NewOLE; + _file = NULL; + _stream = NULL; + + _header = new Header(); + _dirtree = new DirTree(); + _bbat = new AllocTable(1 << _header->b_shift()); + _sbat = new AllocTable(1 << _header->s_shift()); + + _size = 0; +} + +template +bool StorageIOT<_>::load() +{ + if (!_stream) return false; + + // find size of input file + _stream->seekg( 0, std::ios::end ); + _size = _stream->tellg(); + _stream->seekg( 0 ); + + // load header + unsigned char buf_header[512]; + _stream->read( (char*)buf_header, 512 ); + bool res = _header->load( buf_header, 512 ); + if (!res) + return false; + + // check OLE magic id + _result = NotOLE; + if (!_header->is_ole()) + return false; + + // sanity checks + _result = BadOLE; + if (!_header->valid()) + return false; + + // important block size + _bbat->set_block_size(1 << _header->b_shift()); + _sbat->set_block_size(1 << _header->s_shift()); + + // find blocks allocated to store big bat + // the first 109 blocks are in header, the rest in meta bat + std::vector blocks; + blocks.resize( _header->num_bat() ); + for( unsigned i = 0; i < 109; i++ ) + { + if( i >= _header->num_bat() ) + break; + else + blocks[i] = _header->bb_blocks()[i]; + } + if( (_header->num_bat() > 109) && (_header->num_mbat() > 0) ) + { + unsigned char* buffer = new unsigned char[ _bbat->block_size() ]; + if (!buffer) + return false; + unsigned k = 109; + for( unsigned r = 0; r < _header->num_mbat(); r++ ) + { + size_t bytes = loadBigBlock(_header->mbat_start()+r+1, buffer, _bbat->block_size() ); + if (bytes != _bbat->block_size()) + break; + + for( unsigned s=0; s < _bbat->block_size(); s+=4 ) + { + if( k >= _header->num_bat() ) + break; + else + blocks[k++] = readU32( buffer + s ); + } + } + delete[] buffer; + } + + // load big bat + std::streamsize buflen = _bbat->block_size()*(std::streamsize)blocks.size(); + if( buflen > 0 ) + { + unsigned char* buffer = new unsigned char[ buflen ]; + if (!buffer) + return false; + loadBigBlocks( blocks, buffer, buflen ); + bool res = _bbat->load( buffer, buflen ); + delete[] buffer; + if (!res) + return false; + } + + // load directory tree + blocks.clear(); + if (!_bbat->follow( _header->dirent_start(), blocks )) + return false; + buflen = _bbat->block_size()*(std::streamsize)blocks.size(); + unsigned char* buffer = new unsigned char[ buflen ]; + if (!buffer) + return false; + loadBigBlocks( blocks, buffer, buflen ); + if (!_dirtree->load( buffer, buflen )) + { + delete[] buffer; + return false; + } + unsigned sb_start = readU32( buffer + 0x74 ); + delete[] buffer; + + _result = OpenSmallFatFailed; + // fetch block chain as data for small-files + if (!_bbat->follow( sb_start, _sb_blocks ))// small files + return false; + + // load small bat + blocks.clear(); + if (!_bbat->follow( _header->sbat_start(), blocks )) + return false; + buflen = _bbat->block_size()*(std::streamsize)blocks.size(); + if( buflen > 0 ) + { + buffer = new unsigned char[ buflen ]; + if (!buffer) + return false; + loadBigBlocks( blocks, buffer, buflen ); + bool res = _sbat->load( buffer, buflen ); + delete[] buffer; + if (!res) + return false; + } + +// for troubleshooting, just enable this block +#if 0 + debug(); +#endif + + // so far so good + _result = Ok; + return true; +} + +template +bool StorageIOT<_>::create( const char* filename ) +{ + // std::cout << "Creating " << filename << std::endl; + + std::fstream* file = new std::fstream(filename, std::ios::out|std::ios::binary); + if( !file || file->fail() ) + { + std::cerr << "Can't create " << filename << std::endl; + _result = OpenFailed; + if (file) + delete file; + return false; + } + + // so far so good + _result = Ok; + _file = file; + _stream = file; + return true; +} + +template +void StorageIOT<_>::close() +{ + flush(); + if (_file) + { + _file->close(); + delete _file; + _file = NULL; + } +} + +template +std::streamsize StorageIOT<_>::loadBigBlocks( const std::vector& blocks, unsigned char* data, std::streamsize maxlen ) +{ + // sentinel + if( !_stream ) return 0; + if( !data ) return 0; + if( !_stream->good() ) return 0; + if( maxlen == 0 ) return 0; + size_t block_num = blocks.size(); + if( block_num < 1 ) return 0; + + // read block one by one, seems fast enough + std::streamsize totalbytes = 0; + for( ULONG32 i=0; (i < block_num ) && ( totalbytes < maxlen ); i++ ) + { + ULONG32 block = blocks[i]; + std::streamsize p = (_bbat->block_size() < (unsigned)(maxlen-totalbytes)) ? _bbat->block_size() : maxlen-totalbytes; + std::streamsize bytes = loadBigBlock(block+1, data+totalbytes, p ); + totalbytes += bytes; + } + + return totalbytes; +} + +template +std::streamsize StorageIOT<_>::loadBigBlock( ULONG32 block, unsigned char* data, std::streamsize maxlen ) +{ + assert(_stream); + assert((unsigned)maxlen <= big_block_size()); + + ULONG32 block_pos = block * _bbat->block_size(); + if (block_pos > _size) + return 0; + if (block_pos + maxlen > _size) + maxlen = _size - block_pos; + + _stream->seekg( block_pos ); + _stream->read( (char*)data, (std::streamsize)maxlen ); + + assert(!_stream->fail()); + return _stream->gcount(); +} + +// return number of bytes which has been read +template +std::streamsize StorageIOT<_>::loadSmallBlocks( const std::vector& blocks, unsigned char* data, std::streamsize maxlen ) +{ + // sentinel + if( !data ) return 0; + if( !_stream || !_stream->good() ) return 0; + if( maxlen == 0 ) return 0; + size_t block_num = blocks.size(); + if( block_num < 1 ) return 0; + + // our own local buffer + unsigned char* buf = new unsigned char[ _bbat->block_size() ]; + if (!buf) return 0; + + ULONG32 loaded_bblock = 0; + bool bblock_loaded = false; + + // read small block one by one + std::streamsize totalbytes = 0; + for( ULONG32 i=0; ( iblock_size(); + ULONG32 bbindex = pos / _bbat->block_size(); + if( bbindex >= _sb_blocks.size() ) break; + + ULONG32 bblock = _sb_blocks[ bbindex ] + 1; + if (bblock != loaded_bblock || !bblock_loaded) + { + size_t read = loadBigBlock( bblock, buf, _bbat->block_size()); + if (read != _bbat->block_size()) + break; + bblock_loaded = true; + loaded_bblock = bblock; + } + + // copy the data + ULONG32 offset = pos % _bbat->block_size(); + std::streamsize p = _sbat->block_size(); + if (p > maxlen-totalbytes) + p = maxlen-totalbytes; + if ((unsigned)p > _bbat->block_size()-offset) + p = _bbat->block_size()-offset; + memcpy( data + totalbytes, buf + offset, p ); + totalbytes += p; + } + + delete[] buf; + return totalbytes; +} + +// list all files and subdirs in current path +template +void StorageIOT<_>::listDirectory(std::list& result) const +{ + std::vector entries; + _dirtree->listDirectory(entries); + result.resize(entries.size()); + for( unsigned i = 0; i < entries.size(); i++ ) + result.push_back( entries[i]->name() ); +} + +// Write a bigblock +template +std::streamsize StorageIOT<_>::saveBlock(ULONG32 fisical_offset, const unsigned char* data, std::streamsize len) +{ + assert((unsigned)len <= big_block_size()); + + _file->seekp(fisical_offset); + _file->write((const char*)data, (std::streamsize)len); + return len; +} + +template +bool StorageIOT<_>::delete_entry(const std::string& path) +{ + if (!_dirtree || !_dirtree->delete_entry(path)) + return false; + + m_dtmodified = true; + +// todo: delete blocks + + return true; +} + +template +bool StorageIOT<_>::flush() +{ +// for troubleshooting, just enable this block +#if 0 + debug(); +#endif + + if (m_dtmodified && _bbat && _header) + { + std::vector blocks; + if (!_bbat->follow( _header->dirent_start(), blocks )) + return false; + size_t bufflen = blocks.size() * _bbat->block_size(); + unsigned char *buffer = new unsigned char[bufflen]; + if (!_dirtree->save(buffer, bufflen)) + return false; + for (ULONG32 ndx = 0; ndx < blocks.size(); ++ndx) + { + ULONG32 fisical_offset = (blocks[ndx] * big_block_size()) + big_block_size(); + saveBlock(fisical_offset, buffer, big_block_size()); + buffer += big_block_size(); + } + m_dtmodified = false; + } + + return !m_dtmodified; +} + +#ifndef NDEBUG +template +void StorageIOT<_>::debug() const +{ + _header->debug(); + _sbat->debug(); + _bbat->debug(); + _dirtree->debug(); +} +#endif + +} diff --git a/Common/DocxFormat/Source/CompoundDocument/detail/stream.hpp b/Common/DocxFormat/Source/CompoundDocument/detail/stream.hpp new file mode 100644 index 0000000000..c222611b40 --- /dev/null +++ b/Common/DocxFormat/Source/CompoundDocument/detail/stream.hpp @@ -0,0 +1,576 @@ +/* POLE - Portable C++ library to access OLE Storage + Copyright (C) 2005-2006 Jorge Lodos Vigil + Copyright (C) 2002-2005 Ariya Hidayat + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of the authors nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// storage header +#pragma once + +#include "storage.hpp" + +namespace POLE +{ + +template +class StreamImplT +{ +public: + enum {Ok = 0, Eof = 1, Bad = 2, Corrupted = 3}; + static const std::string null_path; + +// Construction/destruction +public: + StreamImplT( const StreamImplT<_>& ); + StreamImplT(StorageIO* io, const DirEntry* e): _entry(e), _io(io) { init(); } + StreamImplT(StorageIO* io, const std::string& path): _entry(io->entry(path)), _io(io) { init(); } + ~StreamImplT() { delete[] _cache_data; } + +// Attributes +public: + const std::string& path() const { return _entry ? _entry->name() : null_path; } + std::streamsize size() const { return (_entry) ? _entry->size() : 0; } + std::streampos tellg() const { return _gpos; } + std::streampos tellp() const { return _ppos; } + bool fail() const { return (_state & Bad) != 0; } + bool eof() const { return (_state & Eof) != 0; } + +// Operations +public: + std::streampos seek(std::streampos pos, std::ios::seekdir origin, std::ios::openmode mode) + { + if (mode == std::ios::in) + { + seekg(pos, origin); + return tellg(); + } + if (mode == std::ios::out) + { + seekp(pos, origin); + return tellp(); + } + + // Bad seek + assert(false); + return 0; + } + void seekg(std::streampos pos, std::ios_base::seekdir origin); + void seekp(std::streampos pos, std::ios_base::seekdir origin); + + int getch(); + std::streamsize read( unsigned char* data, std::streamsize maxlen ); + std::streamsize write(const unsigned char* data, std::streamsize maxlen); + bool reserve(std::streamsize size); + bool resize(std::streamsize size, char val); + +// Implementation +private: + void init(); + std::streamsize read( std::streampos pos, unsigned char* data, std::streamsize maxlen ); + void update_cache(); + + StorageIO* _io; + const DirEntry* _entry; + std::vector _blocks; + std::streampos _gpos; // pointer for read + std::streampos _ppos; // pointer for write + + // simple cache system to speed-up getch() + unsigned char* _cache_data; + std::streamsize _cache_size; + std::streampos _cache_pos; + int _state; + + // no default, copy or assign + StreamImplT( ); + StreamImplT<_>& operator=( const StreamImplT<_>& ); +}; + +typedef StreamImplT StreamImpl; + +// =========== StreamImplT ========== + +template +const std::string StreamImplT<_>::null_path; + +template +StreamImplT<_>::StreamImplT( const StreamImplT<_>& stream) +{ + _state = stream._state; + _io = stream._io; + _entry = stream._entry; + _blocks = stream._blocks; + _gpos = stream._gpos; + _ppos = stream._ppos; + + _cache_size = stream._cache_size; + _cache_pos = stream._cache_pos; + _cache_data = new unsigned char[4096]; + for (std::streamsize i = 0; i<_cache_size; i++) + _cache_data[i] = stream._cache_data[i]; +} + +template +void StreamImplT<_>::init() +{ + _state = StreamImpl::Ok; + _gpos = 0; + _ppos = 0; + // prepare cache + _cache_pos = 0; + _cache_size = 4096; // optimal ? + _cache_data = new unsigned char[_cache_size]; + + // sanity check + if (!_entry) + { + _state = StreamImpl::Bad; + return; + } + + if( _entry->size() >= _io->header()->threshold() ) + { + if (!_io->follow_big_block_table( _entry->start(), _blocks )) + _state = StreamImpl::Bad; + } + else + if (!_io->follow_small_block_table( _entry->start(), _blocks )) + _state = StreamImpl::Bad; + + //update_cache(); +} + +template +void StreamImplT<_>::seekg( std::streampos pos, std::ios_base::seekdir origin) +{ + switch (origin) + { + case std::ios_base::beg: + if ((unsigned)pos > _entry->size()) + { + _state |= StreamImpl::Eof; + _gpos = _entry->size(); + return; + } + else + { + _state &= ~StreamImpl::Eof; + } + _gpos = pos; + break; + case std::ios_base::cur: + if ((unsigned)(_gpos + pos) > _entry->size()) + { + _state |= StreamImpl::Eof; + _gpos = _entry->size(); + return; + } + else + { + _state &= ~StreamImpl::Eof; + } + _gpos += pos; + break; + case std::ios_base::end: + if ((unsigned)pos > _entry->size()) + { + _state |= StreamImpl::Eof; + _gpos = 0; + return; + } + else + { + _state &= ~StreamImpl::Eof; + } + _gpos = _entry->size() - pos; + break; + } +} + +template +void StreamImplT<_>::seekp( std::streampos pos, std::ios_base::seekdir origin) +{ + switch (origin) + { + case std::ios_base::beg: + if ((unsigned)pos > _entry->size()) + { + _state |= StreamImpl::Eof; + _ppos = _entry->size(); + return; + } + else + { + _state &= ~StreamImpl::Eof; + } + _ppos = pos; + break; + case std::ios_base::cur: + if ((unsigned)(_ppos + pos) > _entry->size()) + { + _state |= StreamImpl::Eof; + _ppos = _entry->size(); + return; + } + else + { + _state &= ~StreamImpl::Eof; + } + _ppos += pos; + break; + case std::ios_base::end: + if ((unsigned)pos > _entry->size()) + { + _state |= StreamImpl::Eof; + _ppos = 0; + return; + } + else + { + _state &= ~StreamImpl::Eof; + } + _ppos = _entry->size() - pos; + break; + } +} + +template +int StreamImplT<_>::getch() +{ + // sanity check + if (!_entry) + return 0; + + // past end-of-file ? + if( _gpos > static_cast(_entry->size()) ) + return -1; + + // need to update cache ? + if( !_cache_size || ( _gpos < _cache_pos ) || + ( (unsigned)_gpos >= (unsigned)_cache_pos + _cache_size ) ) + update_cache(); + + // something bad if we don't get good cache + if( !_cache_size ) + return -1; + + int data = _cache_data[_gpos - _cache_pos]; + _gpos += 1; + + return data; +} + +template +std::streamsize StreamImplT<_>::read( std::streampos pos, unsigned char* data, std::streamsize maxlen ) +{ + // sanity checks + if (!_entry) + return 0; + if( !data ) + return 0; + if ((maxlen + (unsigned)pos) > _entry->size()) + { + maxlen = _entry->size() - pos; + _state |= StreamImpl::Eof; + } + else + { + _state &= ~StreamImpl::Eof; + } + if( maxlen == 0 ) + return 0; + + std::streamsize totalbytes = 0; + std::streamsize max_block_num = (std::streamsize)_blocks.size(); + + if ( _entry->size() < _io->header()->threshold() ) + { + // small file + std::streamsize index = pos / _io->small_block_size(); + if( index >= max_block_num ) + return 0; + + // To read small blocks we have to read at least a big block, + // so read as many small blocks as fit in the big block every + // time. If we don't do this the same big block may be loaded + // several times. + + unsigned char* buf = new unsigned char[ _io->big_block_size() ]; + if (!buf) + return 0; + std::streamsize max_blocks_to_read = maxlen / _io->small_block_size(); + if (maxlen % _io->small_block_size()) + max_blocks_to_read++; + std::streamsize small_blocks_in_big_blocks = _io->big_block_size() / _io->small_block_size(); + std::vector blocks; + blocks.reserve(small_blocks_in_big_blocks); + + // Read the small blocks that start not at the beginning of the + // big block. + std::streamsize read_blocks = 0; + std::streamsize offset = pos % _io->small_block_size(); + std::streamsize unaligned_blocks = small_blocks_in_big_blocks - index % small_blocks_in_big_blocks; + unaligned_blocks %= small_blocks_in_big_blocks; + std::streamsize j = 0; + if (unaligned_blocks) + { + for (; j < unaligned_blocks && j < max_block_num && j < max_blocks_to_read; ++j) + if (index+j < max_block_num) + blocks.push_back(_blocks[index+j]); + else + break; + index += j; + std::streamsize bytes = j * _io->small_block_size(); + std::streamsize read = _io->loadSmallBlocks( blocks, buf, bytes ); + if (read != bytes) + { + memcpy(data, buf + offset, read); + delete[] buf; + return totalbytes; + } + read_blocks += j; + std::streamsize count = bytes - offset; + if(count > maxlen) + count = maxlen; + memcpy(data, buf + offset, count); + totalbytes += count; + if (totalbytes == maxlen) + { + delete[] buf; + return totalbytes; + } + offset = 0; + } + + // Read the remaining small blocks. These start at the beginning of a + // big block. + size_t max_bblocks = max_blocks_to_read / small_blocks_in_big_blocks + 1; + for (size_t i = 0; ismall_block_size(); + std::streamsize read = _io->loadSmallBlocks( blocks, buf, bytes ); + read_blocks += j; + std::streamsize count = read - offset; + if(count > maxlen-totalbytes) + count = maxlen-totalbytes; + memcpy(data+totalbytes, buf + offset, count); + totalbytes += count; + offset = 0; + if (totalbytes == maxlen) + break; + } + delete[] buf; + return totalbytes; + } + + // big file + std::streamsize index = pos / _io->big_block_size(); + + if( index >= max_block_num ) + return 0; + + unsigned char* buf = new unsigned char[ _io->big_block_size() ]; + size_t offset = pos % _io->big_block_size(); + for (; index < max_block_num && totalbytes < maxlen; ++index ) + { + ULONG32 block = _blocks[index]; + size_t read = _io->loadBigBlock(block+1, buf, _io->big_block_size()); + if (read != _io->big_block_size()) + break; + std::streamsize count = _io->big_block_size() - offset; + if( count > maxlen-totalbytes ) count = maxlen-totalbytes; + memcpy( data+totalbytes, buf + offset, count ); + totalbytes += count; + offset = 0; + } + + delete [] buf; + return totalbytes; +} + +template +std::streamsize StreamImplT<_>::read( unsigned char* data, std::streamsize maxlen ) +{ + std::streamsize bytes = read( tellg(), data, maxlen ); + _gpos += bytes; + + if ((unsigned)_gpos == _entry->size()) + _state |= StreamImpl::Eof; + + return bytes; +} + +template +void StreamImplT<_>::update_cache() +{ + // sanity checks + if (!_entry) + return; + if( !_cache_data ) + return; + + _cache_pos = _gpos - ( _gpos % _cache_size ); + std::streamsize bytes = _cache_size; + if( (unsigned)_cache_pos + bytes > _entry->size() ) + bytes = _entry->size() - _cache_pos; + _cache_size = read( _cache_pos, _cache_data, bytes ); +} + +/* +para escribir: +1. El entry de cada stream contiene el bloque en que comienza, hay que cargarlos todos en un vector utilizando el follow de la clase AllocTable. +2. Para conocer en que bloque del stream se debe comenzar a escribir, esto se hace dividiendo el offset con respecto al inicio del stream entre el tamaño de cada bloque. +3. Para calcular el offset dentro de ese bloque se toma el resto de la division anterior. +4. Para conocer el desplazamiento fisico donde hay que escribir en el fichero se suma el offset dentro del bloque y se suma con el offset del bloque en el que se va a escribir, este ltimo se calcula como el (bloque*512)+512. +5. Para saber la cantidad de bytes afectados en un bloque dado se resta el tamaño del bloque entre el offset dentro del bloque. Si es menos, entonces todo pertenece a ese bloque, si es mayor, entonces se escriben los bytes desde el desplazamiento inicial hasta el final del bloque y el resto pertenece al bloque adyacente. +6. Es necesario incrementar el buffer de datos en la cantidad de bytes copiados y restar de la cantidad total de bytes lo que se copio +7. Repetir la operacion con otro bloque si es necesario. +*/ +template +std::streamsize StreamImplT<_>::write(const unsigned char* data, std::streamsize maxlen) +{ + // Sanity checks + if(! _entry) + return 0; + if(!data) + return 0; + if((maxlen + (unsigned)_ppos) > _entry->size()) + { + maxlen = _entry->size() - _ppos; + _state |= StreamImpl::Eof; + } + else + { + _state &= ~StreamImpl::Eof; + } + if(maxlen == 0) + return 0; + + size_t max_block_num = _blocks.size(); + + // Amount of written byes + std::streamsize written = 0; + std::streamsize count = 0; + std::streamsize data_len = maxlen; + + if (_entry->size() < _io->header()->threshold()) + { + // small file + size_t index = _ppos / _io->small_block_size(); + if( index >= max_block_num ) + return 0; + + size_t offset = _ppos % _io->small_block_size(); + std::vector _sbroot_entry = _io->sb_blocks(); + for (; index < max_block_num && count < maxlen; ++index) + { + // Take the minifat sector index + ULONG32 minifat_index = _blocks[index]; + ULONG32 sbindex_offset = minifat_index % 8; + // Calculate the the root entry's big block index + ULONG32 position = minifat_index * _io->small_block_size(); + ULONG32 bbindex = position / _io->big_block_size(); + // Fisical offset inside the file + ULONG32 bbindice = _sbroot_entry[bbindex]; + ULONG32 fisical_offset = (((bbindice * _io->big_block_size()) + _io->big_block_size()) + + (sbindex_offset * _io->small_block_size())) + offset; + + // Amount of bytes that can actually be written + std::streamsize canwrite = _io->small_block_size() - offset; + if (canwrite > data_len ) + canwrite = data_len; + + written = _io->saveBlock(fisical_offset, data, canwrite); + count += written; + if (written < canwrite) + { + _state |= StreamImpl::Bad; + return count; + } + data += written; + data_len -= written; + offset = 0; + } + } + else + { + // big file + // Ordinal of the first block for writing + size_t index = _ppos / _io->big_block_size(); + if(index >= max_block_num) + return 0; + + // Offset inside this block + size_t offset = _ppos % _io->big_block_size(); + + for (; index < max_block_num && count < maxlen; ++index) + { + // Fisical offset inside the file + ULONG32 fisical_offset = ((_blocks[index] * _io->big_block_size()) + _io->big_block_size() + offset); + + // Amount of bytes that can actually be written + std::streamsize canwrite = _io->big_block_size() - offset; + if (canwrite > data_len ) + canwrite = data_len; + + written = _io->saveBlock(fisical_offset, data, canwrite); + count += written; + if (written < canwrite) + { + _state |= StreamImpl::Bad; + return count; + } + data += written; + data_len -= written; + offset = 0; + } + } + return count; +} + +template +bool StreamImplT<_>::reserve(std::streamsize size) +{ +// todo: implement + return false; +} + +template +bool StreamImplT<_>::resize(std::streamsize size, char val) +{ +// todo: implement + return false; +} + +} diff --git a/Common/DocxFormat/Source/CompoundDocument/detail/util.hpp b/Common/DocxFormat/Source/CompoundDocument/detail/util.hpp new file mode 100644 index 0000000000..6035639d80 --- /dev/null +++ b/Common/DocxFormat/Source/CompoundDocument/detail/util.hpp @@ -0,0 +1,64 @@ +/* POLE - Portable C++ library to access OLE Storage + Copyright (C) 2005-2006 Jorge Lodos Vigil + Copyright (C) 2002-2005 Ariya Hidayat + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of the authors nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// util header +#pragma once + +namespace POLE +{ + +typedef unsigned char ULONG8; +typedef unsigned short ULONG16; +typedef unsigned long ULONG32; + +inline ULONG32 readU32( const unsigned char* ptr ) +{ + return ptr[0]+(ptr[1]<<8)+(ptr[2]<<16)+(ptr[3]<<24); +} + +inline void writeU32( unsigned char* ptr, ULONG32 data ) +{ + ptr[0] = (unsigned char)(data & 0xff); + ptr[1] = (unsigned char)((data >> 8) & 0xff); + ptr[2] = (unsigned char)((data >> 16) & 0xff); + ptr[3] = (unsigned char)((data >> 24) & 0xff); +} + +inline ULONG16 readU16( const unsigned char* ptr ) +{ + return ptr[0]+(ptr[1]<<8); +} + +inline void writeU16( unsigned char* ptr, ULONG16 data ) +{ + ptr[0] = (unsigned char)(data & 0xff); + ptr[1] = (unsigned char)((data >> 8) & 0xff); +} + +} diff --git a/Common/DocxFormat/Source/CompoundDocument/pole.h b/Common/DocxFormat/Source/CompoundDocument/pole.h new file mode 100644 index 0000000000..074c1bea84 --- /dev/null +++ b/Common/DocxFormat/Source/CompoundDocument/pole.h @@ -0,0 +1,314 @@ +/* POLE - Portable C++ library to access OLE Storage + Copyright (C) 2005-2006 Jorge Lodos Vigil + Copyright (C) 2002-2005 Ariya Hidayat + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of the authors nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef POLE_H +#define POLE_H + +#pragma once + +#include "./detail/stream.hpp" + +namespace POLE +{ + +class Stream; + +template +class StorageT +{ +public: + enum { Ok, OpenFailed, OpenSmallFatFailed, NotOLE, BadOLE, UnknownError, StupidWorkaroundForBrokenCompiler=255 }; + + // Constructs a storage with name filename. + StorageT( const char* filename, std::ios_base::openmode mode = std::ios_base::in, bool create = false ) + { + io = new StorageIO( filename, mode, create ); + } + StorageT( const std::wstring& filename, std::ios_base::openmode mode = std::ios_base::in, bool create = false ) + { + io = new StorageIO( filename, mode, create ); + } + + // Constructs a storage from a stream. + StorageT( std::iostream& stream ) + { + io = new StorageIO( &stream ); + } + + // Destroys the storage. + ~StorageT() + { + delete io; + std::list::iterator it; + for( it = streams.begin(); it != streams.end(); ++it ) + delete *it; + } + +// Attributes +public: + + // Returns the error code of last operation. + int result() const + { + return io->result(); + } + + void fullName( const DirEntry* entry, std::string& name) const + { + io->fullName( entry, name); + } + + // Returns the current path. + void current_path( std::string& result) const + { + io->current_path(result); + } + + // Returns the root entry. + const DirEntry* root_entry() const + { + return io->root_entry(); + } + + // Returns the current entry. + const DirEntry* current_entry() const + { + return io->current_entry(); + } + + const DirEntry* getEntry(const std::string& name) const + { + return io->entry( name ); + } + + void listEntries(std::vector& result) const + { + io->listEntries(result); + } + + void listAll(std::vector& result) const + { + io->listAll(result); + } + +// Operations +public: + // Changes path to directory. Returns true if no error occurs. + bool enterDirectory( const std::string& directory ) + { + return io->enterDirectory( directory ); + } + + // Goes to one directory up. + void leaveDirectory() + { + io->leaveDirectory(); + } + + // Finds and returns a stream with the specified name. + Stream* stream( const std::string& name, bool reuse = false ); + + // If there is a stream using this entry it will be corrupted. + // Any existent entry list is not valid anymore + bool delete_entry(const std::string& path) + { + return io->delete_entry(path); + } + + bool flush() + { + return io->flush(); + } + +#ifndef NDEBUG + void debug() const + { + io->debug(); + } +#endif + +private: + StorageIO* io; + std::list streams; + + // no copy or assign + StorageT( const StorageT<_>& ); + StorageT<_>& operator=( const StorageT<_>& ); +}; + +typedef StorageT Storage; + +class Stream +{ + friend class StorageT; + +public: // Attributes + + // Returns the path for this stream. + const std::string& path() const + { + return impl ? impl->path() : StreamImpl::null_path; + } + + // Returns the stream size. + std::streamsize size() const + { + return impl ? impl->size() : 0; + } + + // Returns the read pointer. + std::streampos tellg() const + { + return impl ? impl->tellg() : 0; + } + + // Returns the write pointer. + std::streampos tellp() const + { + return impl ? impl->tellp() : 0; + } + + // Return the Eof state of the stream + bool eof() const + { + return impl ? impl->eof() : true; + } + + // Return the fail state of the Stream + bool fail() const + { + return impl ? impl->fail() : true; + } + +public: // Operations + + // Sets the read position. + std::streampos seek( std::streampos pos, std::ios_base::seekdir origin, std::ios_base::openmode mode ) + { + if( impl ) return impl->seek( pos, origin, mode ); + return 0; + } + + // Sets the read position. + void seekg( std::streampos pos, std::ios_base::seekdir origin = std::ios_base::beg ) + { + if( impl ) impl->seekg( pos, origin ); + } + + // Sets the write position. + void seekp( std::streampos pos, std::ios_base::seekdir origin = std::ios_base::beg ) + { + if( impl ) impl->seekp( pos, origin ); + } + + // Reads a byte. + int getch() + { + return impl ? impl->getch() : 0; + } + + // Reads a block of data. + std::streamsize read( unsigned char* data, std::streamsize maxlen ) + { + return impl ? impl->read( data, maxlen ) : 0; + } + + // Write a block of data + std::streamsize write(const unsigned char* data, std::streamsize len) + { + return impl ? impl->write( data, len ) : 0; + } + + // Make the size grow to size. If size is less than current size no + // action will be taken. + bool reserve(std::streamsize size) + { + return impl ? impl->reserve( size ) : false; + } + + // Make the size exactly size. If size is less than current size the + // extra bytes will be truncated. If it is greater the new data will + // be filled with val. + bool resize(std::streamsize size, char val = 0) + { + return impl ? impl->resize( size, val ) : false; + } + +private: + Stream(StreamImpl* i) { impl = i; } + ~Stream() { delete impl; } + + // no default, copy or assign + Stream(); + Stream( const Stream& ); + Stream& operator=( const Stream& ); + + StreamImpl* impl; +}; + +// =========== StorageT ========== + +template +Stream* StorageT<_>::stream( const std::string& name, bool reuse ) +{ + // sanity check + if( !name.length() ) return (Stream*)0; + if( !io ) return (Stream*)0; + + // make absolute if necesary + std::string fullName = name; + std::string path_; + current_path(path_); + if( name[0] != '/' ) fullName.insert( 0, path_ + "/" ); + + // If a stream for this path already exists return it + if (reuse) + { + std::list::iterator it; + for( it = streams.begin(); it != streams.end(); ++it ) + if ((*it)->path() == name) + return *it; + } + + const DirEntry* entry = io->entry( name ); + if( !entry ) return (Stream*)0; + + // We create the StreamImpl here instead of in the constructor to avoid + // passing implementation parameters in the constructor. + // The created StreamImpl will be deleted in the Stream destructor. + StreamImpl* i = new StreamImpl( io, entry ); + Stream* s = new Stream(i); + streams.push_back( s ); + + return s; +} + +} + +#endif // POLE_H