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()
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
.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