Add wxArchiveFSHandler.
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@42544 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -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,340 @@
|
||||
#pragma hdrstop
|
||||
#endif
|
||||
|
||||
#if wxUSE_FILESYSTEM && wxUSE_FS_ZIP && wxUSE_ZIPSTREAM && wxUSE_ZLIB
|
||||
#if wxUSE_FS_ARCHIVE
|
||||
|
||||
#include "wx/fs_arc.h"
|
||||
|
||||
#ifndef WXPRECOMP
|
||||
#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"
|
||||
|
||||
#include "wx/archive.h"
|
||||
#include "wx/fileback.h"
|
||||
#include "wx/thread.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
|
||||
}
|
||||
|
||||
virtual ~wxZipFSInputStream() { delete m_file; }
|
||||
|
||||
private:
|
||||
wxFSFile *m_file;
|
||||
wxArchiveEntry *entry;
|
||||
wxArchiveFSEntry *next;
|
||||
};
|
||||
|
||||
class wxArchiveFSCacheDataImpl
|
||||
{
|
||||
public:
|
||||
wxArchiveFSCacheDataImpl(const wxArchiveClassFactory& factory,
|
||||
const wxBackingFile& backer);
|
||||
wxArchiveFSCacheDataImpl(const wxArchiveClassFactory& factory,
|
||||
wxInputStream *stream);
|
||||
|
||||
~wxArchiveFSCacheDataImpl();
|
||||
|
||||
void Release() { if (--m_refcount == 0) delete this; }
|
||||
wxArchiveFSCacheDataImpl *AddRef() { m_refcount++; return this; }
|
||||
|
||||
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()] = 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() == 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 +358,84 @@ 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;
|
||||
wxArchiveEntry *entry = cached->Get(right);
|
||||
if (!entry)
|
||||
return NULL;
|
||||
|
||||
if (ent->GetInternalName() == right)
|
||||
{
|
||||
found = true;
|
||||
dtMod = ent->GetDateTime();
|
||||
}
|
||||
wxInputStream *leftStream = cached->NewStream();
|
||||
if (!leftStream)
|
||||
{
|
||||
wxFSFile *leftFile = m_fs.OpenFile(left);
|
||||
if (!leftFile)
|
||||
return NULL;
|
||||
leftStream = leftFile->DetachStream();
|
||||
delete leftFile;
|
||||
}
|
||||
|
||||
delete ent;
|
||||
}
|
||||
if (found)
|
||||
{
|
||||
return new wxFSFile(s,
|
||||
left + wxT("#zip:") + right,
|
||||
wxArchiveInputStream *s = factory->NewStream(leftStream);
|
||||
s->OpenEntry(*entry);
|
||||
|
||||
if (s && s->IsOk())
|
||||
return new wxFSFile(s,
|
||||
key + right,
|
||||
GetMimeTypeFromExt(location),
|
||||
GetAnchor(location)
|
||||
#if wxUSE_DATETIME
|
||||
, dtMod
|
||||
, entry->GetDateTime()
|
||||
#endif // wxUSE_DATETIME
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
delete s;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
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 +446,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 +458,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 +467,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 +502,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 +513,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
|
||||
|
Reference in New Issue
Block a user