This keyword is not expanded by Git which means it's not replaced with the correct revision value in the releases made using git-based scripts and it's confusing to have lines with unexpanded "$Id$" in the released files. As expanding them with Git is not that simple (it could be done with git archive and export-subst attribute) and there are not many benefits in having them in the first place, just remove all these lines. If nothing else, this will make an eventual transition to Git simpler. Closes #14487. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@74602 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
534 lines
14 KiB
C++
534 lines
14 KiB
C++
/////////////////////////////////////////////////////////////////////////////
|
|
// Name: src/common/fs_arc.cpp
|
|
// Purpose: wxArchive file system
|
|
// Author: Vaclav Slavik, Mike Wetherell
|
|
// Copyright: (c) 1999 Vaclav Slavik, (c) 2006 Mike Wetherell
|
|
// Licence: wxWindows licence
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "wx/wxprec.h"
|
|
|
|
#ifdef __BORLANDC__
|
|
#pragma hdrstop
|
|
#endif
|
|
|
|
#if wxUSE_FS_ARCHIVE
|
|
|
|
#include "wx/fs_arc.h"
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include "wx/intl.h"
|
|
#include "wx/log.h"
|
|
#endif
|
|
|
|
#if WXWIN_COMPATIBILITY_2_6 && wxUSE_ZIPSTREAM
|
|
#include "wx/zipstrm.h"
|
|
#else
|
|
#include "wx/archive.h"
|
|
#endif
|
|
|
|
#include "wx/private/fileback.h"
|
|
|
|
//---------------------------------------------------------------------------
|
|
// 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.
|
|
//---------------------------------------------------------------------------
|
|
|
|
WX_DECLARE_STRING_HASH_MAP(wxArchiveEntry*, wxArchiveFSEntryHash);
|
|
|
|
struct wxArchiveFSEntry
|
|
{
|
|
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(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()
|
|
{
|
|
wxDELETE(m_archive);
|
|
wxDELETE(m_stream);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// wxArchiveFSHandler
|
|
//----------------------------------------------------------------------------
|
|
|
|
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;
|
|
}
|
|
|
|
wxArchiveFSHandler::~wxArchiveFSHandler()
|
|
{
|
|
Cleanup();
|
|
delete m_cache;
|
|
}
|
|
|
|
void wxArchiveFSHandler::Cleanup()
|
|
{
|
|
wxDELETE(m_DirsFound);
|
|
}
|
|
|
|
bool wxArchiveFSHandler::CanOpen(const wxString& location)
|
|
{
|
|
wxString p = GetProtocol(location);
|
|
return wxArchiveClassFactory::Find(p) != NULL;
|
|
}
|
|
|
|
wxFSFile* wxArchiveFSHandler::OpenFile(
|
|
wxFileSystem& WXUNUSED(fs),
|
|
const wxString& location)
|
|
{
|
|
wxString right = GetRightLocation(location);
|
|
wxString left = GetLeftLocation(location);
|
|
wxString protocol = GetProtocol(location);
|
|
wxString key = left + wxT("#") + protocol + wxT(":");
|
|
|
|
if (right.Contains(wxT("./")))
|
|
{
|
|
if (right.GetChar(0) != wxT('/')) right = wxT('/') + right;
|
|
wxFileName rightPart(right, wxPATH_UNIX);
|
|
rightPart.Normalize(wxPATH_NORM_DOTS, wxT("/"), wxPATH_UNIX);
|
|
right = rightPart.GetFullPath(wxPATH_UNIX);
|
|
}
|
|
|
|
if (!right.empty() && right.GetChar(0) == wxT('/')) right = right.Mid(1);
|
|
|
|
if (!m_cache)
|
|
m_cache = new wxArchiveFSCache;
|
|
|
|
const wxArchiveClassFactory *factory;
|
|
factory = wxArchiveClassFactory::Find(protocol);
|
|
if (!factory)
|
|
return NULL;
|
|
|
|
wxArchiveFSCacheData *cached = m_cache->Get(key);
|
|
if (!cached)
|
|
{
|
|
wxFSFile *leftFile = m_fs.OpenFile(left);
|
|
if (!leftFile)
|
|
return NULL;
|
|
cached = m_cache->Add(key, *factory, leftFile->DetachStream());
|
|
delete leftFile;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
wxArchiveInputStream *s = factory->NewStream(leftStream);
|
|
if ( !s )
|
|
return NULL;
|
|
|
|
s->OpenEntry(*entry);
|
|
|
|
if (!s->IsOk())
|
|
{
|
|
delete s;
|
|
return NULL;
|
|
}
|
|
|
|
#if WXWIN_COMPATIBILITY_2_6 && wxUSE_ZIPSTREAM
|
|
if (wxDynamicCast(factory, 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 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_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)
|
|
{
|
|
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:
|
|
m_AllowDirs = false, m_AllowFiles = true; break;
|
|
case wxDIR:
|
|
m_AllowDirs = true, m_AllowFiles = false; break;
|
|
default:
|
|
m_AllowDirs = m_AllowFiles = true; break;
|
|
}
|
|
|
|
m_ZipFile = key;
|
|
|
|
m_Pattern = right.AfterLast(wxT('/'));
|
|
m_BaseDir = right.BeforeLast(wxT('/'));
|
|
if (m_BaseDir.StartsWith(wxT("/")))
|
|
m_BaseDir = m_BaseDir.Mid(1);
|
|
|
|
if (m_Archive)
|
|
{
|
|
if (m_AllowDirs)
|
|
{
|
|
delete m_DirsFound;
|
|
m_DirsFound = new wxArchiveFilenameHashMap();
|
|
if (right.empty()) // allow "/" to match the archive root
|
|
return spec;
|
|
}
|
|
return DoFind();
|
|
}
|
|
return wxEmptyString;
|
|
}
|
|
|
|
wxString wxArchiveFSHandler::FindNext()
|
|
{
|
|
if (!m_Archive) return wxEmptyString;
|
|
return DoFind();
|
|
}
|
|
|
|
wxString wxArchiveFSHandler::DoFind()
|
|
{
|
|
wxString namestr, dir, filename;
|
|
wxString match = wxEmptyString;
|
|
|
|
while (match == wxEmptyString)
|
|
{
|
|
m_FindEntry = m_Archive->GetNext(m_FindEntry);
|
|
|
|
if (!m_FindEntry)
|
|
{
|
|
m_Archive = NULL;
|
|
m_FindEntry = NULL;
|
|
break;
|
|
}
|
|
namestr = m_FindEntry->entry->GetName(wxPATH_UNIX);
|
|
|
|
if (m_AllowDirs)
|
|
{
|
|
dir = namestr.BeforeLast(wxT('/'));
|
|
while (!dir.empty())
|
|
{
|
|
if( m_DirsFound->find(dir) == m_DirsFound->end() )
|
|
{
|
|
(*m_DirsFound)[dir] = 1;
|
|
filename = dir.AfterLast(wxT('/'));
|
|
dir = dir.BeforeLast(wxT('/'));
|
|
if (!filename.empty() && m_BaseDir == dir &&
|
|
wxMatchWild(m_Pattern, filename, false))
|
|
match = m_ZipFile + dir + wxT("/") + filename;
|
|
}
|
|
else
|
|
break; // already tranversed
|
|
}
|
|
}
|
|
|
|
filename = namestr.AfterLast(wxT('/'));
|
|
dir = namestr.BeforeLast(wxT('/'));
|
|
if (m_AllowFiles && !filename.empty() && m_BaseDir == dir &&
|
|
wxMatchWild(m_Pattern, filename, false))
|
|
match = m_ZipFile + namestr;
|
|
}
|
|
|
|
return match;
|
|
}
|
|
|
|
#endif // wxUSE_FS_ARCHIVE
|