X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/be38a31d378be07b10c97786074e8f813ddf6fcb..511383f91b1443d576276c826fdfd5286f9dbbdb:/src/common/fs_arc.cpp diff --git a/src/common/fs_arc.cpp b/src/common/fs_arc.cpp index f96f1b8ee3..ff639417dd 100644 --- a/src/common/fs_arc.cpp +++ b/src/common/fs_arc.cpp @@ -1,8 +1,8 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: fs_zip.cpp -// Purpose: ZIP file system -// Author: Vaclav Slavik -// Copyright: (c) 1999 Vaclav Slavik +// Name: fs_arc.cpp +// Purpose: wxArchive file system +// Author: Vaclav Slavik, Mike Wetherell +// Copyright: (c) 1999 Vaclav Slavik, (c) 2006 Mike Wetherell // CVS-ID: $Id$ // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// @@ -13,82 +13,344 @@ #pragma hdrstop #endif -#if wxUSE_FILESYSTEM && wxUSE_FS_ZIP && wxUSE_ZIPSTREAM && wxUSE_ZLIB +#if wxUSE_FS_ARCHIVE -#ifndef WXPRECOMP +#include "wx/fs_arc.h" + +#ifndef WX_PRECOMP #include "wx/intl.h" #include "wx/log.h" #endif -#include "wx/filesys.h" -#include "wx/wfstream.h" -#include "wx/zipstrm.h" -#include "wx/fs_zip.h" +#if WXWIN_COMPATIBILITY_2_6 + #include "wx/zipstrm.h" +#else + #include "wx/archive.h" +#endif +#include "wx/private/fileback.h" //--------------------------------------------------------------------------- -// wxZipFSInputStream +// wxArchiveFSCacheDataImpl +// +// Holds the catalog of an archive file, and if it is being read from a +// non-seekable stream, a copy of its backing file. +// +// This class is actually the reference counted implementation for the +// wxArchiveFSCacheData class below. It was done that way to allow sharing +// between instances of wxFileSystem, though that's a feature not used in this +// version. //--------------------------------------------------------------------------- -// Helper class for wxZipFSHandler -class wxZipFSInputStream : public wxZipInputStream +WX_DECLARE_STRING_HASH_MAP(wxArchiveEntry*, wxArchiveFSEntryHash); + +struct wxArchiveFSEntry { - public: - wxZipFSInputStream(wxFSFile *file) - : wxZipInputStream(*file->GetStream()) - { - m_file = file; -#if WXWIN_COMPATIBILITY_2_6 - m_allowSeeking = true; -#endif - } + wxArchiveEntry *entry; + wxArchiveFSEntry *next; +}; + +class wxArchiveFSCacheDataImpl +{ +public: + wxArchiveFSCacheDataImpl(const wxArchiveClassFactory& factory, + const wxBackingFile& backer); + wxArchiveFSCacheDataImpl(const wxArchiveClassFactory& factory, + wxInputStream *stream); + + ~wxArchiveFSCacheDataImpl(); - virtual ~wxZipFSInputStream() { delete m_file; } + void Release() { if (--m_refcount == 0) delete this; } + wxArchiveFSCacheDataImpl *AddRef() { m_refcount++; return this; } - private: - wxFSFile *m_file; + wxArchiveEntry *Get(const wxString& name); + wxInputStream *NewStream() const; + + wxArchiveFSEntry *GetNext(wxArchiveFSEntry *fse); + +private: + wxArchiveFSEntry *AddToCache(wxArchiveEntry *entry); + void CloseStreams(); + + int m_refcount; + + wxArchiveFSEntryHash m_hash; + wxArchiveFSEntry *m_begin; + wxArchiveFSEntry **m_endptr; + + wxBackingFile m_backer; + wxInputStream *m_stream; + wxArchiveInputStream *m_archive; }; +wxArchiveFSCacheDataImpl::wxArchiveFSCacheDataImpl( + const wxArchiveClassFactory& factory, + const wxBackingFile& backer) + : m_refcount(1), + m_begin(NULL), + m_endptr(&m_begin), + m_backer(backer), + m_stream(new wxBackedInputStream(backer)), + m_archive(factory.NewStream(*m_stream)) +{ +} + +wxArchiveFSCacheDataImpl::wxArchiveFSCacheDataImpl( + const wxArchiveClassFactory& factory, + wxInputStream *stream) + : m_refcount(1), + m_begin(NULL), + m_endptr(&m_begin), + m_stream(stream), + m_archive(factory.NewStream(*m_stream)) +{ +} + +wxArchiveFSCacheDataImpl::~wxArchiveFSCacheDataImpl() +{ + WX_CLEAR_HASH_MAP(wxArchiveFSEntryHash, m_hash); + + wxArchiveFSEntry *entry = m_begin; + + while (entry) + { + wxArchiveFSEntry *next = entry->next; + delete entry; + entry = next; + } + + CloseStreams(); +} + +wxArchiveFSEntry *wxArchiveFSCacheDataImpl::AddToCache(wxArchiveEntry *entry) +{ + m_hash[entry->GetName(wxPATH_UNIX)] = entry; + wxArchiveFSEntry *fse = new wxArchiveFSEntry; + *m_endptr = fse; + (*m_endptr)->entry = entry; + (*m_endptr)->next = NULL; + m_endptr = &(*m_endptr)->next; + return fse; +} + +void wxArchiveFSCacheDataImpl::CloseStreams() +{ + delete m_archive; + m_archive = NULL; + delete m_stream; + m_stream = NULL; +} + +wxArchiveEntry *wxArchiveFSCacheDataImpl::Get(const wxString& name) +{ + wxArchiveFSEntryHash::iterator it = m_hash.find(name); + + if (it != m_hash.end()) + return it->second; + + if (!m_archive) + return NULL; + + wxArchiveEntry *entry; + + while ((entry = m_archive->GetNextEntry()) != NULL) + { + AddToCache(entry); + + if (entry->GetName(wxPATH_UNIX) == name) + return entry; + } + + CloseStreams(); + + return NULL; +} + +wxInputStream* wxArchiveFSCacheDataImpl::NewStream() const +{ + if (m_backer) + return new wxBackedInputStream(m_backer); + else + return NULL; +} + +wxArchiveFSEntry *wxArchiveFSCacheDataImpl::GetNext(wxArchiveFSEntry *fse) +{ + wxArchiveFSEntry *next = fse ? fse->next : m_begin; + + if (!next && m_archive) + { + wxArchiveEntry *entry = m_archive->GetNextEntry(); + + if (entry) + next = AddToCache(entry); + else + CloseStreams(); + } + + return next; +} + +//--------------------------------------------------------------------------- +// wxArchiveFSCacheData +// +// This is the inteface for wxArchiveFSCacheDataImpl above. Holds the catalog +// of an archive file, and if it is being read from a non-seekable stream, a +// copy of its backing file. +//--------------------------------------------------------------------------- + +class wxArchiveFSCacheData +{ +public: + wxArchiveFSCacheData() : m_impl(NULL) { } + wxArchiveFSCacheData(const wxArchiveClassFactory& factory, + const wxBackingFile& backer); + wxArchiveFSCacheData(const wxArchiveClassFactory& factory, + wxInputStream *stream); + + wxArchiveFSCacheData(const wxArchiveFSCacheData& data); + wxArchiveFSCacheData& operator=(const wxArchiveFSCacheData& data); + + ~wxArchiveFSCacheData() { if (m_impl) m_impl->Release(); } + + wxArchiveEntry *Get(const wxString& name) { return m_impl->Get(name); } + wxInputStream *NewStream() const { return m_impl->NewStream(); } + wxArchiveFSEntry *GetNext(wxArchiveFSEntry *fse) + { return m_impl->GetNext(fse); } + +private: + wxArchiveFSCacheDataImpl *m_impl; +}; + +wxArchiveFSCacheData::wxArchiveFSCacheData( + const wxArchiveClassFactory& factory, + const wxBackingFile& backer) + : m_impl(new wxArchiveFSCacheDataImpl(factory, backer)) +{ +} + +wxArchiveFSCacheData::wxArchiveFSCacheData( + const wxArchiveClassFactory& factory, + wxInputStream *stream) + : m_impl(new wxArchiveFSCacheDataImpl(factory, stream)) +{ +} + +wxArchiveFSCacheData::wxArchiveFSCacheData(const wxArchiveFSCacheData& data) + : m_impl(data.m_impl ? data.m_impl->AddRef() : NULL) +{ +} + +wxArchiveFSCacheData& wxArchiveFSCacheData::operator=( + const wxArchiveFSCacheData& data) +{ + if (data.m_impl != m_impl) + { + if (m_impl) + m_impl->Release(); + + m_impl = data.m_impl; + + if (m_impl) + m_impl->AddRef(); + } + + return *this; +} + +//--------------------------------------------------------------------------- +// wxArchiveFSCache +// +// wxArchiveFSCacheData caches a single archive, and this class holds a +// collection of them to cache all the archives accessed by this instance +// of wxFileSystem. +//--------------------------------------------------------------------------- + +WX_DECLARE_STRING_HASH_MAP(wxArchiveFSCacheData, wxArchiveFSCacheDataHash); + +class wxArchiveFSCache +{ +public: + wxArchiveFSCache() { } + ~wxArchiveFSCache() { } + + wxArchiveFSCacheData* Add(const wxString& name, + const wxArchiveClassFactory& factory, + wxInputStream *stream); + + wxArchiveFSCacheData *Get(const wxString& name); + +private: + wxArchiveFSCacheDataHash m_hash; +}; + +wxArchiveFSCacheData* wxArchiveFSCache::Add( + const wxString& name, + const wxArchiveClassFactory& factory, + wxInputStream *stream) +{ + wxArchiveFSCacheData& data = m_hash[name]; + + if (stream->IsSeekable()) + data = wxArchiveFSCacheData(factory, stream); + else + data = wxArchiveFSCacheData(factory, wxBackingFile(stream)); + + return &data; +} + +wxArchiveFSCacheData *wxArchiveFSCache::Get(const wxString& name) +{ + wxArchiveFSCacheDataHash::iterator it; + + if ((it = m_hash.find(name)) != m_hash.end()) + return &it->second; + + return NULL; +} + //---------------------------------------------------------------------------- -// wxZipFSHandler +// wxArchiveFSHandler //---------------------------------------------------------------------------- -wxZipFSHandler::wxZipFSHandler() : wxFileSystemHandler() +IMPLEMENT_DYNAMIC_CLASS(wxArchiveFSHandler, wxFileSystemHandler) + +wxArchiveFSHandler::wxArchiveFSHandler() + : wxFileSystemHandler() { m_Archive = NULL; + m_FindEntry = NULL; m_ZipFile = m_Pattern = m_BaseDir = wxEmptyString; m_AllowDirs = m_AllowFiles = true; m_DirsFound = NULL; + m_cache = NULL; } - - -wxZipFSHandler::~wxZipFSHandler() +wxArchiveFSHandler::~wxArchiveFSHandler() { Cleanup(); + delete m_cache; } - -void wxZipFSHandler::Cleanup() +void wxArchiveFSHandler::Cleanup() { - wxDELETE(m_Archive); wxDELETE(m_DirsFound); } - - -bool wxZipFSHandler::CanOpen(const wxString& location) +bool wxArchiveFSHandler::CanOpen(const wxString& location) { wxString p = GetProtocol(location); - return (p == wxT("zip")); + return wxArchiveClassFactory::Find(p) != NULL; } - -wxFSFile* wxZipFSHandler::OpenFile(wxFileSystem& WXUNUSED(fs), const wxString& location) +wxFSFile* wxArchiveFSHandler::OpenFile( + wxFileSystem& WXUNUSED(fs), + const wxString& location) { wxString right = GetRightLocation(location); wxString left = GetLeftLocation(location); - wxZipInputStream *s; + wxString protocol = GetProtocol(location); + wxString key = left + wxT("#") + protocol + wxT(":"); if (right.Contains(wxT("./"))) { @@ -100,65 +362,94 @@ wxFSFile* wxZipFSHandler::OpenFile(wxFileSystem& WXUNUSED(fs), const wxString& l if (right.GetChar(0) == wxT('/')) right = right.Mid(1); - // a new wxFileSystem object is needed here to avoid infinite recursion - wxFSFile *leftFile = wxFileSystem().OpenFile(left); - if (!leftFile) - return NULL; + if (!m_cache) + m_cache = new wxArchiveFSCache; - s = new wxZipFSInputStream(leftFile); - if (s && s->IsOk()) + const wxArchiveClassFactory *factory; + factory = wxArchiveClassFactory::Find(protocol); + if (!factory) + return NULL; + + wxArchiveFSCacheData *cached = m_cache->Get(key); + if (!cached) { -#if wxUSE_DATETIME - wxDateTime dtMod; -#endif // wxUSE_DATETIME + wxFSFile *leftFile = m_fs.OpenFile(left); + if (!leftFile) + return NULL; + cached = m_cache->Add(key, *factory, leftFile->DetachStream()); + delete leftFile; + } - bool found = false; - while (!found) - { - wxZipEntry *ent = s->GetNextEntry(); - if (!ent) - break; - - if (ent->GetInternalName() == right) - { - found = true; - dtMod = ent->GetDateTime(); - } - - delete ent; - } - if (found) - { - return new wxFSFile(s, - left + wxT("#zip:") + right, - GetMimeTypeFromExt(location), - GetAnchor(location) -#if wxUSE_DATETIME - , dtMod -#endif // wxUSE_DATETIME - ); - } + wxArchiveEntry *entry = cached->Get(right); + if (!entry) + return NULL; + + wxInputStream *leftStream = cached->NewStream(); + if (!leftStream) + { + wxFSFile *leftFile = m_fs.OpenFile(left); + if (!leftFile) + return NULL; + leftStream = leftFile->DetachStream(); + delete leftFile; } - delete s; - return NULL; -} + wxArchiveInputStream *s = factory->NewStream(leftStream); + if ( !s ) + return NULL; + + s->OpenEntry(*entry); + if (!s->IsOk()) + { + delete s; + return NULL; + } +#if WXWIN_COMPATIBILITY_2_6 + if (factory->IsKindOf(CLASSINFO(wxZipClassFactory))) + ((wxZipInputStream*)s)->m_allowSeeking = true; +#endif // WXWIN_COMPATIBILITY_2_6 + + return new wxFSFile(s, + key + right, + wxEmptyString, + GetAnchor(location) +#if wxUSE_DATETIME + , entry->GetDateTime() +#endif // wxUSE_DATETIME + ); +} -wxString wxZipFSHandler::FindFirst(const wxString& spec, int flags) +wxString wxArchiveFSHandler::FindFirst(const wxString& spec, int flags) { wxString right = GetRightLocation(spec); wxString left = GetLeftLocation(spec); + wxString protocol = GetProtocol(spec); + wxString key = left + wxT("#") + protocol + wxT(":"); if (!right.empty() && right.Last() == wxT('/')) right.RemoveLast(); - if (m_Archive) + if (!m_cache) + m_cache = new wxArchiveFSCache; + + const wxArchiveClassFactory *factory; + factory = wxArchiveClassFactory::Find(protocol); + if (!factory) + return wxEmptyString; + + m_Archive = m_cache->Get(key); + if (!m_Archive) { - delete m_Archive; - m_Archive = NULL; + wxFSFile *leftFile = m_fs.OpenFile(left); + if (!leftFile) + return wxEmptyString; + m_Archive = m_cache->Add(key, *factory, leftFile->DetachStream()); + delete leftFile; } + m_FindEntry = NULL; + switch (flags) { case wxFILE: @@ -169,11 +460,7 @@ wxString wxZipFSHandler::FindFirst(const wxString& spec, int flags) m_AllowDirs = m_AllowFiles = true; break; } - m_ZipFile = left; - - wxFSFile *leftFile = wxFileSystem().OpenFile(left); - if (leftFile) - m_Archive = new wxZipFSInputStream(leftFile); + m_ZipFile = key; m_Pattern = right.AfterLast(wxT('/')); m_BaseDir = right.BeforeLast(wxT('/')); @@ -185,7 +472,7 @@ wxString wxZipFSHandler::FindFirst(const wxString& spec, int flags) if (m_AllowDirs) { delete m_DirsFound; - m_DirsFound = new wxZipFilenameHashMap(); + m_DirsFound = new wxArchiveFilenameHashMap(); if (right.empty()) // allow "/" to match the archive root return spec; } @@ -194,32 +481,28 @@ wxString wxZipFSHandler::FindFirst(const wxString& spec, int flags) return wxEmptyString; } - - -wxString wxZipFSHandler::FindNext() +wxString wxArchiveFSHandler::FindNext() { if (!m_Archive) return wxEmptyString; return DoFind(); } - - -wxString wxZipFSHandler::DoFind() +wxString wxArchiveFSHandler::DoFind() { wxString namestr, dir, filename; wxString match = wxEmptyString; while (match == wxEmptyString) { - wxZipEntry *entry = m_Archive->GetNextEntry(); - if (!entry) + m_FindEntry = m_Archive->GetNext(m_FindEntry); + + if (!m_FindEntry) { - delete m_Archive; m_Archive = NULL; + m_FindEntry = NULL; break; } - namestr = entry->GetName(wxPATH_UNIX); - delete entry; + namestr = m_FindEntry->entry->GetName(wxPATH_UNIX); if (m_AllowDirs) { @@ -233,7 +516,7 @@ wxString wxZipFSHandler::DoFind() dir = dir.BeforeLast(wxT('/')); if (!filename.empty() && m_BaseDir == dir && wxMatchWild(m_Pattern, filename, false)) - match = m_ZipFile + wxT("#zip:") + dir + wxT("/") + filename; + match = m_ZipFile + dir + wxT("/") + filename; } else break; // already tranversed @@ -244,13 +527,10 @@ wxString wxZipFSHandler::DoFind() dir = namestr.BeforeLast(wxT('/')); if (m_AllowFiles && !filename.empty() && m_BaseDir == dir && wxMatchWild(m_Pattern, filename, false)) - match = m_ZipFile + wxT("#zip:") + namestr; + match = m_ZipFile + namestr; } return match; } - - -#endif - //wxUSE_FILESYSTEM && wxUSE_FS_ZIP && wxUSE_ZIPSTREAM +#endif // wxUSE_FS_ARCHIVE