1 /////////////////////////////////////////////////////////////////////////////
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()
144 wxArchiveEntry
*wxArchiveFSCacheDataImpl::Get(const wxString
& name
)
146 wxArchiveFSEntryHash::iterator it
= m_hash
.find(name
);
148 if (it
!= m_hash
.end())
154 wxArchiveEntry
*entry
;
156 while ((entry
= m_archive
->GetNextEntry()) != NULL
)
160 if (entry
->GetName(wxPATH_UNIX
) == name
)
169 wxInputStream
* wxArchiveFSCacheDataImpl::NewStream() const
172 return new wxBackedInputStream(m_backer
);
177 wxArchiveFSEntry
*wxArchiveFSCacheDataImpl::GetNext(wxArchiveFSEntry
*fse
)
179 wxArchiveFSEntry
*next
= fse
? fse
->next
: m_begin
;
181 if (!next
&& m_archive
)
183 wxArchiveEntry
*entry
= m_archive
->GetNextEntry();
186 next
= AddToCache(entry
);
194 //---------------------------------------------------------------------------
195 // wxArchiveFSCacheData
197 // This is the inteface for wxArchiveFSCacheDataImpl above. Holds the catalog
198 // of an archive file, and if it is being read from a non-seekable stream, a
199 // copy of its backing file.
200 //---------------------------------------------------------------------------
202 class wxArchiveFSCacheData
205 wxArchiveFSCacheData() : m_impl(NULL
) { }
206 wxArchiveFSCacheData(const wxArchiveClassFactory
& factory
,
207 const wxBackingFile
& backer
);
208 wxArchiveFSCacheData(const wxArchiveClassFactory
& factory
,
209 wxInputStream
*stream
);
211 wxArchiveFSCacheData(const wxArchiveFSCacheData
& data
);
212 wxArchiveFSCacheData
& operator=(const wxArchiveFSCacheData
& data
);
214 ~wxArchiveFSCacheData() { if (m_impl
) m_impl
->Release(); }
216 wxArchiveEntry
*Get(const wxString
& name
) { return m_impl
->Get(name
); }
217 wxInputStream
*NewStream() const { return m_impl
->NewStream(); }
218 wxArchiveFSEntry
*GetNext(wxArchiveFSEntry
*fse
)
219 { return m_impl
->GetNext(fse
); }
222 wxArchiveFSCacheDataImpl
*m_impl
;
225 wxArchiveFSCacheData::wxArchiveFSCacheData(
226 const wxArchiveClassFactory
& factory
,
227 const wxBackingFile
& backer
)
228 : m_impl(new wxArchiveFSCacheDataImpl(factory
, backer
))
232 wxArchiveFSCacheData::wxArchiveFSCacheData(
233 const wxArchiveClassFactory
& factory
,
234 wxInputStream
*stream
)
235 : m_impl(new wxArchiveFSCacheDataImpl(factory
, stream
))
239 wxArchiveFSCacheData::wxArchiveFSCacheData(const wxArchiveFSCacheData
& data
)
240 : m_impl(data
.m_impl
? data
.m_impl
->AddRef() : NULL
)
244 wxArchiveFSCacheData
& wxArchiveFSCacheData::operator=(
245 const wxArchiveFSCacheData
& data
)
247 if (data
.m_impl
!= m_impl
)
252 m_impl
= data
.m_impl
;
261 //---------------------------------------------------------------------------
264 // wxArchiveFSCacheData caches a single archive, and this class holds a
265 // collection of them to cache all the archives accessed by this instance
267 //---------------------------------------------------------------------------
269 WX_DECLARE_STRING_HASH_MAP(wxArchiveFSCacheData
, wxArchiveFSCacheDataHash
);
271 class wxArchiveFSCache
274 wxArchiveFSCache() { }
275 ~wxArchiveFSCache() { }
277 wxArchiveFSCacheData
* Add(const wxString
& name
,
278 const wxArchiveClassFactory
& factory
,
279 wxInputStream
*stream
);
281 wxArchiveFSCacheData
*Get(const wxString
& name
);
284 wxArchiveFSCacheDataHash m_hash
;
287 wxArchiveFSCacheData
* wxArchiveFSCache::Add(
288 const wxString
& name
,
289 const wxArchiveClassFactory
& factory
,
290 wxInputStream
*stream
)
292 wxArchiveFSCacheData
& data
= m_hash
[name
];
294 if (stream
->IsSeekable())
295 data
= wxArchiveFSCacheData(factory
, stream
);
297 data
= wxArchiveFSCacheData(factory
, wxBackingFile(stream
));
302 wxArchiveFSCacheData
*wxArchiveFSCache::Get(const wxString
& name
)
304 wxArchiveFSCacheDataHash::iterator it
;
306 if ((it
= m_hash
.find(name
)) != m_hash
.end())
312 //----------------------------------------------------------------------------
313 // wxArchiveFSHandler
314 //----------------------------------------------------------------------------
316 IMPLEMENT_DYNAMIC_CLASS(wxArchiveFSHandler
, wxFileSystemHandler
)
318 wxArchiveFSHandler::wxArchiveFSHandler()
319 : wxFileSystemHandler()
323 m_ZipFile
= m_Pattern
= m_BaseDir
= wxEmptyString
;
324 m_AllowDirs
= m_AllowFiles
= true;
329 wxArchiveFSHandler::~wxArchiveFSHandler()
335 void wxArchiveFSHandler::Cleanup()
337 wxDELETE(m_DirsFound
);
340 bool wxArchiveFSHandler::CanOpen(const wxString
& location
)
342 wxString p
= GetProtocol(location
);
343 return wxArchiveClassFactory::Find(p
) != NULL
;
346 wxFSFile
* wxArchiveFSHandler::OpenFile(
347 wxFileSystem
& WXUNUSED(fs
),
348 const wxString
& location
)
350 wxString right
= GetRightLocation(location
);
351 wxString left
= GetLeftLocation(location
);
352 wxString protocol
= GetProtocol(location
);
353 wxString key
= left
+ wxT("#") + protocol
+ wxT(":");
355 if (right
.Contains(wxT("./")))
357 if (right
.GetChar(0) != wxT('/')) right
= wxT('/') + right
;
358 wxFileName
rightPart(right
, wxPATH_UNIX
);
359 rightPart
.Normalize(wxPATH_NORM_DOTS
, wxT("/"), wxPATH_UNIX
);
360 right
= rightPart
.GetFullPath(wxPATH_UNIX
);
363 if (right
.GetChar(0) == wxT('/')) right
= right
.Mid(1);
366 m_cache
= new wxArchiveFSCache
;
368 const wxArchiveClassFactory
*factory
;
369 factory
= wxArchiveClassFactory::Find(protocol
);
373 wxArchiveFSCacheData
*cached
= m_cache
->Get(key
);
376 wxFSFile
*leftFile
= m_fs
.OpenFile(left
);
379 cached
= m_cache
->Add(key
, *factory
, leftFile
->DetachStream());
383 wxArchiveEntry
*entry
= cached
->Get(right
);
387 wxInputStream
*leftStream
= cached
->NewStream();
390 wxFSFile
*leftFile
= m_fs
.OpenFile(left
);
393 leftStream
= leftFile
->DetachStream();
397 wxArchiveInputStream
*s
= factory
->NewStream(leftStream
);
401 s
->OpenEntry(*entry
);
409 #if WXWIN_COMPATIBILITY_2_6 && wxUSE_ZIPSTREAM
410 if (factory
->IsKindOf(CLASSINFO(wxZipClassFactory
)))
411 ((wxZipInputStream
*)s
)->m_allowSeeking
= true;
412 #endif // WXWIN_COMPATIBILITY_2_6
414 return new wxFSFile(s
,
419 , entry
->GetDateTime()
420 #endif // wxUSE_DATETIME
424 wxString
wxArchiveFSHandler::FindFirst(const wxString
& spec
, int flags
)
426 wxString right
= GetRightLocation(spec
);
427 wxString left
= GetLeftLocation(spec
);
428 wxString protocol
= GetProtocol(spec
);
429 wxString key
= left
+ wxT("#") + protocol
+ wxT(":");
431 if (!right
.empty() && right
.Last() == wxT('/')) right
.RemoveLast();
434 m_cache
= new wxArchiveFSCache
;
436 const wxArchiveClassFactory
*factory
;
437 factory
= wxArchiveClassFactory::Find(protocol
);
439 return wxEmptyString
;
441 m_Archive
= m_cache
->Get(key
);
444 wxFSFile
*leftFile
= m_fs
.OpenFile(left
);
446 return wxEmptyString
;
447 m_Archive
= m_cache
->Add(key
, *factory
, leftFile
->DetachStream());
456 m_AllowDirs
= false, m_AllowFiles
= true; break;
458 m_AllowDirs
= true, m_AllowFiles
= false; break;
460 m_AllowDirs
= m_AllowFiles
= true; break;
465 m_Pattern
= right
.AfterLast(wxT('/'));
466 m_BaseDir
= right
.BeforeLast(wxT('/'));
467 if (m_BaseDir
.StartsWith(wxT("/")))
468 m_BaseDir
= m_BaseDir
.Mid(1);
475 m_DirsFound
= new wxArchiveFilenameHashMap();
476 if (right
.empty()) // allow "/" to match the archive root
481 return wxEmptyString
;
484 wxString
wxArchiveFSHandler::FindNext()
486 if (!m_Archive
) return wxEmptyString
;
490 wxString
wxArchiveFSHandler::DoFind()
492 wxString namestr
, dir
, filename
;
493 wxString match
= wxEmptyString
;
495 while (match
== wxEmptyString
)
497 m_FindEntry
= m_Archive
->GetNext(m_FindEntry
);
505 namestr
= m_FindEntry
->entry
->GetName(wxPATH_UNIX
);
509 dir
= namestr
.BeforeLast(wxT('/'));
512 if( m_DirsFound
->find(dir
) == m_DirsFound
->end() )
514 (*m_DirsFound
)[dir
] = 1;
515 filename
= dir
.AfterLast(wxT('/'));
516 dir
= dir
.BeforeLast(wxT('/'));
517 if (!filename
.empty() && m_BaseDir
== dir
&&
518 wxMatchWild(m_Pattern
, filename
, false))
519 match
= m_ZipFile
+ dir
+ wxT("/") + filename
;
522 break; // already tranversed
526 filename
= namestr
.AfterLast(wxT('/'));
527 dir
= namestr
.BeforeLast(wxT('/'));
528 if (m_AllowFiles
&& !filename
.empty() && m_BaseDir
== dir
&&
529 wxMatchWild(m_Pattern
, filename
, false))
530 match
= m_ZipFile
+ namestr
;
536 #endif // wxUSE_FS_ARCHIVE