1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/common/fs_arc.cpp 
   3 // Purpose:     wxArchive file system 
   4 // Author:      Vaclav Slavik, Mike Wetherell 
   5 // Copyright:   (c) 1999 Vaclav Slavik, (c) 2006 Mike Wetherell 
   7 // Licence:     wxWindows licence 
   8 ///////////////////////////////////////////////////////////////////////////// 
  10 #include "wx/wxprec.h" 
  18 #include "wx/fs_arc.h" 
  25 #if WXWIN_COMPATIBILITY_2_6 && wxUSE_ZIPSTREAM 
  26     #include "wx/zipstrm.h" 
  28     #include "wx/archive.h" 
  31 #include "wx/private/fileback.h" 
  33 //--------------------------------------------------------------------------- 
  34 // wxArchiveFSCacheDataImpl 
  36 // Holds the catalog of an archive file, and if it is being read from a 
  37 // non-seekable stream, a copy of its backing file. 
  39 // This class is actually the reference counted implementation for the 
  40 // wxArchiveFSCacheData class below. It was done that way to allow sharing 
  41 // between instances of wxFileSystem, though that's a feature not used in this 
  43 //--------------------------------------------------------------------------- 
  45 WX_DECLARE_STRING_HASH_MAP(wxArchiveEntry
*, wxArchiveFSEntryHash
); 
  47 struct wxArchiveFSEntry
 
  49     wxArchiveEntry 
*entry
; 
  50     wxArchiveFSEntry 
*next
; 
  53 class wxArchiveFSCacheDataImpl
 
  56     wxArchiveFSCacheDataImpl(const wxArchiveClassFactory
& factory
, 
  57                              const wxBackingFile
& backer
); 
  58     wxArchiveFSCacheDataImpl(const wxArchiveClassFactory
& factory
, 
  59                              wxInputStream 
*stream
); 
  61     ~wxArchiveFSCacheDataImpl(); 
  63     void Release() { if (--m_refcount 
== 0) delete this; } 
  64     wxArchiveFSCacheDataImpl 
*AddRef() { m_refcount
++; return this; } 
  66     wxArchiveEntry 
*Get(const wxString
& name
); 
  67     wxInputStream 
*NewStream() const; 
  69     wxArchiveFSEntry 
*GetNext(wxArchiveFSEntry 
*fse
); 
  72     wxArchiveFSEntry 
*AddToCache(wxArchiveEntry 
*entry
); 
  77     wxArchiveFSEntryHash m_hash
; 
  78     wxArchiveFSEntry 
*m_begin
; 
  79     wxArchiveFSEntry 
**m_endptr
; 
  81     wxBackingFile m_backer
; 
  82     wxInputStream 
*m_stream
; 
  83     wxArchiveInputStream 
*m_archive
; 
  86 wxArchiveFSCacheDataImpl::wxArchiveFSCacheDataImpl( 
  87         const wxArchiveClassFactory
& factory
, 
  88         const wxBackingFile
& backer
) 
  93     m_stream(new wxBackedInputStream(backer
)), 
  94     m_archive(factory
.NewStream(*m_stream
)) 
  98 wxArchiveFSCacheDataImpl::wxArchiveFSCacheDataImpl( 
  99         const wxArchiveClassFactory
& factory
, 
 100         wxInputStream 
*stream
) 
 105     m_archive(factory
.NewStream(*m_stream
)) 
 109 wxArchiveFSCacheDataImpl::~wxArchiveFSCacheDataImpl() 
 111     WX_CLEAR_HASH_MAP(wxArchiveFSEntryHash
, m_hash
); 
 113     wxArchiveFSEntry 
*entry 
= m_begin
; 
 117         wxArchiveFSEntry 
*next 
= entry
->next
; 
 125 wxArchiveFSEntry 
*wxArchiveFSCacheDataImpl::AddToCache(wxArchiveEntry 
*entry
) 
 127     m_hash
[entry
->GetName(wxPATH_UNIX
)] = entry
; 
 128     wxArchiveFSEntry 
*fse 
= new wxArchiveFSEntry
; 
 130     (*m_endptr
)->entry 
= entry
; 
 131     (*m_endptr
)->next 
= NULL
; 
 132     m_endptr 
= &(*m_endptr
)->next
; 
 136 void wxArchiveFSCacheDataImpl::CloseStreams() 
 142 wxArchiveEntry 
*wxArchiveFSCacheDataImpl::Get(const wxString
& name
) 
 144     wxArchiveFSEntryHash::iterator it 
= m_hash
.find(name
); 
 146     if (it 
!= m_hash
.end()) 
 152     wxArchiveEntry 
*entry
; 
 154     while ((entry 
= m_archive
->GetNextEntry()) != NULL
) 
 158         if (entry
->GetName(wxPATH_UNIX
) == name
) 
 167 wxInputStream
* wxArchiveFSCacheDataImpl::NewStream() const 
 170         return new wxBackedInputStream(m_backer
); 
 175 wxArchiveFSEntry 
*wxArchiveFSCacheDataImpl::GetNext(wxArchiveFSEntry 
*fse
) 
 177     wxArchiveFSEntry 
*next 
= fse 
? fse
->next 
: m_begin
; 
 179     if (!next 
&& m_archive
) 
 181         wxArchiveEntry 
*entry 
= m_archive
->GetNextEntry(); 
 184             next 
= AddToCache(entry
); 
 192 //--------------------------------------------------------------------------- 
 193 // wxArchiveFSCacheData 
 195 // This is the inteface for wxArchiveFSCacheDataImpl above. Holds the catalog 
 196 // of an archive file, and if it is being read from a non-seekable stream, a 
 197 // copy of its backing file. 
 198 //--------------------------------------------------------------------------- 
 200 class wxArchiveFSCacheData
 
 203     wxArchiveFSCacheData() : m_impl(NULL
) { } 
 204     wxArchiveFSCacheData(const wxArchiveClassFactory
& factory
, 
 205                          const wxBackingFile
& backer
); 
 206     wxArchiveFSCacheData(const wxArchiveClassFactory
& factory
, 
 207                          wxInputStream 
*stream
); 
 209     wxArchiveFSCacheData(const wxArchiveFSCacheData
& data
); 
 210     wxArchiveFSCacheData
& operator=(const wxArchiveFSCacheData
& data
); 
 212     ~wxArchiveFSCacheData() { if (m_impl
) m_impl
->Release(); } 
 214     wxArchiveEntry 
*Get(const wxString
& name
) { return m_impl
->Get(name
); } 
 215     wxInputStream 
*NewStream() const { return m_impl
->NewStream(); } 
 216     wxArchiveFSEntry 
*GetNext(wxArchiveFSEntry 
*fse
) 
 217         { return m_impl
->GetNext(fse
); } 
 220     wxArchiveFSCacheDataImpl 
*m_impl
; 
 223 wxArchiveFSCacheData::wxArchiveFSCacheData( 
 224         const wxArchiveClassFactory
& factory
, 
 225         const wxBackingFile
& backer
) 
 226   : m_impl(new wxArchiveFSCacheDataImpl(factory
, backer
)) 
 230 wxArchiveFSCacheData::wxArchiveFSCacheData( 
 231         const wxArchiveClassFactory
& factory
, 
 232         wxInputStream 
*stream
) 
 233   : m_impl(new wxArchiveFSCacheDataImpl(factory
, stream
)) 
 237 wxArchiveFSCacheData::wxArchiveFSCacheData(const wxArchiveFSCacheData
& data
) 
 238   : m_impl(data
.m_impl 
? data
.m_impl
->AddRef() : NULL
) 
 242 wxArchiveFSCacheData
& wxArchiveFSCacheData::operator=( 
 243         const wxArchiveFSCacheData
& data
) 
 245     if (data
.m_impl 
!= m_impl
) 
 250         m_impl 
= data
.m_impl
; 
 259 //--------------------------------------------------------------------------- 
 262 // wxArchiveFSCacheData caches a single archive, and this class holds a 
 263 // collection of them to cache all the archives accessed by this instance 
 265 //--------------------------------------------------------------------------- 
 267 WX_DECLARE_STRING_HASH_MAP(wxArchiveFSCacheData
, wxArchiveFSCacheDataHash
); 
 269 class wxArchiveFSCache
 
 272     wxArchiveFSCache() { } 
 273     ~wxArchiveFSCache() { } 
 275     wxArchiveFSCacheData
* Add(const wxString
& name
, 
 276                               const wxArchiveClassFactory
& factory
, 
 277                               wxInputStream 
*stream
); 
 279     wxArchiveFSCacheData 
*Get(const wxString
& name
); 
 282     wxArchiveFSCacheDataHash m_hash
; 
 285 wxArchiveFSCacheData
* wxArchiveFSCache::Add( 
 286         const wxString
& name
, 
 287         const wxArchiveClassFactory
& factory
, 
 288         wxInputStream 
*stream
) 
 290     wxArchiveFSCacheData
& data 
= m_hash
[name
]; 
 292     if (stream
->IsSeekable()) 
 293         data 
= wxArchiveFSCacheData(factory
, stream
); 
 295         data 
= wxArchiveFSCacheData(factory
, wxBackingFile(stream
)); 
 300 wxArchiveFSCacheData 
*wxArchiveFSCache::Get(const wxString
& name
) 
 302     wxArchiveFSCacheDataHash::iterator it
; 
 304     if ((it 
= m_hash
.find(name
)) != m_hash
.end()) 
 310 //---------------------------------------------------------------------------- 
 311 // wxArchiveFSHandler 
 312 //---------------------------------------------------------------------------- 
 314 IMPLEMENT_DYNAMIC_CLASS(wxArchiveFSHandler
, wxFileSystemHandler
) 
 316 wxArchiveFSHandler::wxArchiveFSHandler() 
 317  :  wxFileSystemHandler() 
 321     m_ZipFile 
= m_Pattern 
= m_BaseDir 
= wxEmptyString
; 
 322     m_AllowDirs 
= m_AllowFiles 
= true; 
 327 wxArchiveFSHandler::~wxArchiveFSHandler() 
 333 void wxArchiveFSHandler::Cleanup() 
 335     wxDELETE(m_DirsFound
); 
 338 bool wxArchiveFSHandler::CanOpen(const wxString
& location
) 
 340     wxString p 
= GetProtocol(location
); 
 341     return wxArchiveClassFactory::Find(p
) != NULL
; 
 344 wxFSFile
* wxArchiveFSHandler::OpenFile( 
 345         wxFileSystem
& WXUNUSED(fs
), 
 346         const wxString
& location
) 
 348     wxString right 
= GetRightLocation(location
); 
 349     wxString left 
= GetLeftLocation(location
); 
 350     wxString protocol 
= GetProtocol(location
); 
 351     wxString key 
= left 
+ wxT("#") + protocol 
+ wxT(":"); 
 353     if (right
.Contains(wxT("./"))) 
 355         if (right
.GetChar(0) != wxT('/')) right 
= wxT('/') + right
; 
 356         wxFileName 
rightPart(right
, wxPATH_UNIX
); 
 357         rightPart
.Normalize(wxPATH_NORM_DOTS
, wxT("/"), wxPATH_UNIX
); 
 358         right 
= rightPart
.GetFullPath(wxPATH_UNIX
); 
 361     if (!right
.empty() && right
.GetChar(0) == wxT('/')) right 
= right
.Mid(1); 
 364         m_cache 
= new wxArchiveFSCache
; 
 366     const wxArchiveClassFactory 
*factory
; 
 367     factory 
= wxArchiveClassFactory::Find(protocol
); 
 371     wxArchiveFSCacheData 
*cached 
= m_cache
->Get(key
); 
 374         wxFSFile 
*leftFile 
= m_fs
.OpenFile(left
); 
 377         cached 
= m_cache
->Add(key
, *factory
, leftFile
->DetachStream()); 
 381     wxArchiveEntry 
*entry 
= cached
->Get(right
); 
 385     wxInputStream 
*leftStream 
= cached
->NewStream(); 
 388         wxFSFile 
*leftFile 
= m_fs
.OpenFile(left
); 
 391         leftStream 
= leftFile
->DetachStream(); 
 395     wxArchiveInputStream 
*s 
= factory
->NewStream(leftStream
); 
 399     s
->OpenEntry(*entry
); 
 407 #if WXWIN_COMPATIBILITY_2_6 && wxUSE_ZIPSTREAM 
 408     if (factory
->IsKindOf(CLASSINFO(wxZipClassFactory
))) 
 409         ((wxZipInputStream
*)s
)->m_allowSeeking 
= true; 
 410 #endif // WXWIN_COMPATIBILITY_2_6 
 412     return new wxFSFile(s
, 
 417                         , entry
->GetDateTime() 
 418 #endif // wxUSE_DATETIME 
 422 wxString 
wxArchiveFSHandler::FindFirst(const wxString
& spec
, int flags
) 
 424     wxString right 
= GetRightLocation(spec
); 
 425     wxString left 
= GetLeftLocation(spec
); 
 426     wxString protocol 
= GetProtocol(spec
); 
 427     wxString key 
= left 
+ wxT("#") + protocol 
+ wxT(":"); 
 429     if (!right
.empty() && right
.Last() == wxT('/')) right
.RemoveLast(); 
 432         m_cache 
= new wxArchiveFSCache
; 
 434     const wxArchiveClassFactory 
*factory
; 
 435     factory 
= wxArchiveClassFactory::Find(protocol
); 
 437         return wxEmptyString
; 
 439     m_Archive 
= m_cache
->Get(key
); 
 442         wxFSFile 
*leftFile 
= m_fs
.OpenFile(left
); 
 444             return wxEmptyString
; 
 445         m_Archive 
= m_cache
->Add(key
, *factory
, leftFile
->DetachStream()); 
 454             m_AllowDirs 
= false, m_AllowFiles 
= true; break; 
 456             m_AllowDirs 
= true, m_AllowFiles 
= false; break; 
 458             m_AllowDirs 
= m_AllowFiles 
= true; break; 
 463     m_Pattern 
= right
.AfterLast(wxT('/')); 
 464     m_BaseDir 
= right
.BeforeLast(wxT('/')); 
 465     if (m_BaseDir
.StartsWith(wxT("/"))) 
 466         m_BaseDir 
= m_BaseDir
.Mid(1); 
 473             m_DirsFound 
= new wxArchiveFilenameHashMap(); 
 474             if (right
.empty())  // allow "/" to match the archive root 
 479     return wxEmptyString
; 
 482 wxString 
wxArchiveFSHandler::FindNext() 
 484     if (!m_Archive
) return wxEmptyString
; 
 488 wxString 
wxArchiveFSHandler::DoFind() 
 490     wxString namestr
, dir
, filename
; 
 491     wxString match 
= wxEmptyString
; 
 493     while (match 
== wxEmptyString
) 
 495         m_FindEntry 
= m_Archive
->GetNext(m_FindEntry
); 
 503         namestr 
= m_FindEntry
->entry
->GetName(wxPATH_UNIX
); 
 507             dir 
= namestr
.BeforeLast(wxT('/')); 
 510                 if( m_DirsFound
->find(dir
) == m_DirsFound
->end() ) 
 512                     (*m_DirsFound
)[dir
] = 1; 
 513                     filename 
= dir
.AfterLast(wxT('/')); 
 514                     dir 
= dir
.BeforeLast(wxT('/')); 
 515                     if (!filename
.empty() && m_BaseDir 
== dir 
&& 
 516                                 wxMatchWild(m_Pattern
, filename
, false)) 
 517                         match 
= m_ZipFile 
+ dir 
+ wxT("/") + filename
; 
 520                     break; // already tranversed 
 524         filename 
= namestr
.AfterLast(wxT('/')); 
 525         dir 
= namestr
.BeforeLast(wxT('/')); 
 526         if (m_AllowFiles 
&& !filename
.empty() && m_BaseDir 
== dir 
&& 
 527                             wxMatchWild(m_Pattern
, filename
, false)) 
 528             match 
= m_ZipFile 
+ namestr
; 
 534 #endif // wxUSE_FS_ARCHIVE