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 #include "wx/archive.h"
26 #include "wx/fileback.h"
27 #include "wx/thread.h"
29 //---------------------------------------------------------------------------
30 // wxArchiveFSCacheDataImpl
32 // Holds the catalog of an archive file, and if it is being read from a
33 // non-seekable stream, a copy of its backing file.
35 // This class is actually the reference counted implementation for the
36 // wxArchiveFSCacheData class below. It was done that way to allow sharing
37 // between instances of wxFileSystem, though that's a feature not used in this
39 //---------------------------------------------------------------------------
41 WX_DECLARE_STRING_HASH_MAP(wxArchiveEntry
*, wxArchiveFSEntryHash
);
43 struct wxArchiveFSEntry
45 wxArchiveEntry
*entry
;
46 wxArchiveFSEntry
*next
;
49 class wxArchiveFSCacheDataImpl
52 wxArchiveFSCacheDataImpl(const wxArchiveClassFactory
& factory
,
53 const wxBackingFile
& backer
);
54 wxArchiveFSCacheDataImpl(const wxArchiveClassFactory
& factory
,
55 wxInputStream
*stream
);
57 ~wxArchiveFSCacheDataImpl();
59 void Release() { if (--m_refcount
== 0) delete this; }
60 wxArchiveFSCacheDataImpl
*AddRef() { m_refcount
++; return this; }
62 wxArchiveEntry
*Get(const wxString
& name
);
63 wxInputStream
*NewStream() const;
65 wxArchiveFSEntry
*GetNext(wxArchiveFSEntry
*fse
);
68 wxArchiveFSEntry
*AddToCache(wxArchiveEntry
*entry
);
73 wxArchiveFSEntryHash m_hash
;
74 wxArchiveFSEntry
*m_begin
;
75 wxArchiveFSEntry
**m_endptr
;
77 wxBackingFile m_backer
;
78 wxInputStream
*m_stream
;
79 wxArchiveInputStream
*m_archive
;
82 wxArchiveFSCacheDataImpl::wxArchiveFSCacheDataImpl(
83 const wxArchiveClassFactory
& factory
,
84 const wxBackingFile
& backer
)
89 m_stream(new wxBackedInputStream(backer
)),
90 m_archive(factory
.NewStream(*m_stream
))
94 wxArchiveFSCacheDataImpl::wxArchiveFSCacheDataImpl(
95 const wxArchiveClassFactory
& factory
,
96 wxInputStream
*stream
)
101 m_archive(factory
.NewStream(*m_stream
))
105 wxArchiveFSCacheDataImpl::~wxArchiveFSCacheDataImpl()
107 WX_CLEAR_HASH_MAP(wxArchiveFSEntryHash
, m_hash
);
109 wxArchiveFSEntry
*entry
= m_begin
;
113 wxArchiveFSEntry
*next
= entry
->next
;
121 wxArchiveFSEntry
*wxArchiveFSCacheDataImpl::AddToCache(wxArchiveEntry
*entry
)
123 m_hash
[entry
->GetName()] = entry
;
124 wxArchiveFSEntry
*fse
= new wxArchiveFSEntry
;
126 (*m_endptr
)->entry
= entry
;
127 (*m_endptr
)->next
= NULL
;
128 m_endptr
= &(*m_endptr
)->next
;
132 void wxArchiveFSCacheDataImpl::CloseStreams()
140 wxArchiveEntry
*wxArchiveFSCacheDataImpl::Get(const wxString
& name
)
142 wxArchiveFSEntryHash::iterator it
= m_hash
.find(name
);
144 if (it
!= m_hash
.end())
150 wxArchiveEntry
*entry
;
152 while ((entry
= m_archive
->GetNextEntry()) != NULL
)
156 if (entry
->GetName() == name
)
165 wxInputStream
* wxArchiveFSCacheDataImpl::NewStream() const
168 return new wxBackedInputStream(m_backer
);
173 wxArchiveFSEntry
*wxArchiveFSCacheDataImpl::GetNext(wxArchiveFSEntry
*fse
)
175 wxArchiveFSEntry
*next
= fse
? fse
->next
: m_begin
;
177 if (!next
&& m_archive
)
179 wxArchiveEntry
*entry
= m_archive
->GetNextEntry();
182 next
= AddToCache(entry
);
190 //---------------------------------------------------------------------------
191 // wxArchiveFSCacheData
193 // This is the inteface for wxArchiveFSCacheDataImpl above. Holds the catalog
194 // of an archive file, and if it is being read from a non-seekable stream, a
195 // copy of its backing file.
196 //---------------------------------------------------------------------------
198 class wxArchiveFSCacheData
201 wxArchiveFSCacheData() : m_impl(NULL
) { }
202 wxArchiveFSCacheData(const wxArchiveClassFactory
& factory
,
203 const wxBackingFile
& backer
);
204 wxArchiveFSCacheData(const wxArchiveClassFactory
& factory
,
205 wxInputStream
*stream
);
207 wxArchiveFSCacheData(const wxArchiveFSCacheData
& data
);
208 wxArchiveFSCacheData
& operator=(const wxArchiveFSCacheData
& data
);
210 ~wxArchiveFSCacheData() { if (m_impl
) m_impl
->Release(); }
212 wxArchiveEntry
*Get(const wxString
& name
) { return m_impl
->Get(name
); }
213 wxInputStream
*NewStream() const { return m_impl
->NewStream(); }
214 wxArchiveFSEntry
*GetNext(wxArchiveFSEntry
*fse
)
215 { return m_impl
->GetNext(fse
); }
218 wxArchiveFSCacheDataImpl
*m_impl
;
221 wxArchiveFSCacheData::wxArchiveFSCacheData(
222 const wxArchiveClassFactory
& factory
,
223 const wxBackingFile
& backer
)
224 : m_impl(new wxArchiveFSCacheDataImpl(factory
, backer
))
228 wxArchiveFSCacheData::wxArchiveFSCacheData(
229 const wxArchiveClassFactory
& factory
,
230 wxInputStream
*stream
)
231 : m_impl(new wxArchiveFSCacheDataImpl(factory
, stream
))
235 wxArchiveFSCacheData::wxArchiveFSCacheData(const wxArchiveFSCacheData
& data
)
236 : m_impl(data
.m_impl
? data
.m_impl
->AddRef() : NULL
)
240 wxArchiveFSCacheData
& wxArchiveFSCacheData::operator=(
241 const wxArchiveFSCacheData
& data
)
243 if (data
.m_impl
!= m_impl
)
248 m_impl
= data
.m_impl
;
257 //---------------------------------------------------------------------------
260 // wxArchiveFSCacheData caches a single archive, and this class holds a
261 // collection of them to cache all the archives accessed by this instance
263 //---------------------------------------------------------------------------
265 WX_DECLARE_STRING_HASH_MAP(wxArchiveFSCacheData
, wxArchiveFSCacheDataHash
);
267 class wxArchiveFSCache
270 wxArchiveFSCache() { }
271 ~wxArchiveFSCache() { }
273 wxArchiveFSCacheData
* Add(const wxString
& name
,
274 const wxArchiveClassFactory
& factory
,
275 wxInputStream
*stream
);
277 wxArchiveFSCacheData
*Get(const wxString
& name
);
280 wxArchiveFSCacheDataHash m_hash
;
283 wxArchiveFSCacheData
* wxArchiveFSCache::Add(
284 const wxString
& name
,
285 const wxArchiveClassFactory
& factory
,
286 wxInputStream
*stream
)
288 wxArchiveFSCacheData
& data
= m_hash
[name
];
290 if (stream
->IsSeekable())
291 data
= wxArchiveFSCacheData(factory
, stream
);
293 data
= wxArchiveFSCacheData(factory
, wxBackingFile(stream
));
298 wxArchiveFSCacheData
*wxArchiveFSCache::Get(const wxString
& name
)
300 wxArchiveFSCacheDataHash::iterator it
;
302 if ((it
= m_hash
.find(name
)) != m_hash
.end())
308 //----------------------------------------------------------------------------
309 // wxArchiveFSHandler
310 //----------------------------------------------------------------------------
312 IMPLEMENT_DYNAMIC_CLASS(wxArchiveFSHandler
, wxFileSystemHandler
)
314 wxArchiveFSHandler::wxArchiveFSHandler()
315 : wxFileSystemHandler()
319 m_ZipFile
= m_Pattern
= m_BaseDir
= wxEmptyString
;
320 m_AllowDirs
= m_AllowFiles
= true;
325 wxArchiveFSHandler::~wxArchiveFSHandler()
331 void wxArchiveFSHandler::Cleanup()
333 wxDELETE(m_DirsFound
);
336 bool wxArchiveFSHandler::CanOpen(const wxString
& location
)
338 wxString p
= GetProtocol(location
);
339 return wxArchiveClassFactory::Find(p
) != NULL
;
342 wxFSFile
* wxArchiveFSHandler::OpenFile(
343 wxFileSystem
& WXUNUSED(fs
),
344 const wxString
& location
)
346 wxString right
= GetRightLocation(location
);
347 wxString left
= GetLeftLocation(location
);
348 wxString protocol
= GetProtocol(location
);
349 wxString key
= left
+ wxT("#") + protocol
+ wxT(":");
351 if (right
.Contains(wxT("./")))
353 if (right
.GetChar(0) != wxT('/')) right
= wxT('/') + right
;
354 wxFileName
rightPart(right
, wxPATH_UNIX
);
355 rightPart
.Normalize(wxPATH_NORM_DOTS
, wxT("/"), wxPATH_UNIX
);
356 right
= rightPart
.GetFullPath(wxPATH_UNIX
);
359 if (right
.GetChar(0) == wxT('/')) right
= right
.Mid(1);
362 m_cache
= new wxArchiveFSCache
;
364 const wxArchiveClassFactory
*factory
;
365 factory
= wxArchiveClassFactory::Find(protocol
);
369 wxArchiveFSCacheData
*cached
= m_cache
->Get(key
);
372 wxFSFile
*leftFile
= m_fs
.OpenFile(left
);
375 cached
= m_cache
->Add(key
, *factory
, leftFile
->DetachStream());
379 wxArchiveEntry
*entry
= cached
->Get(right
);
383 wxInputStream
*leftStream
= cached
->NewStream();
386 wxFSFile
*leftFile
= m_fs
.OpenFile(left
);
389 leftStream
= leftFile
->DetachStream();
393 wxArchiveInputStream
*s
= factory
->NewStream(leftStream
);
394 s
->OpenEntry(*entry
);
397 return new wxFSFile(s
,
399 GetMimeTypeFromExt(location
),
402 , entry
->GetDateTime()
403 #endif // wxUSE_DATETIME
410 wxString
wxArchiveFSHandler::FindFirst(const wxString
& spec
, int flags
)
412 wxString right
= GetRightLocation(spec
);
413 wxString left
= GetLeftLocation(spec
);
414 wxString protocol
= GetProtocol(spec
);
415 wxString key
= left
+ wxT("#") + protocol
+ wxT(":");
417 if (!right
.empty() && right
.Last() == wxT('/')) right
.RemoveLast();
420 m_cache
= new wxArchiveFSCache
;
422 const wxArchiveClassFactory
*factory
;
423 factory
= wxArchiveClassFactory::Find(protocol
);
425 return wxEmptyString
;
427 m_Archive
= m_cache
->Get(key
);
430 wxFSFile
*leftFile
= m_fs
.OpenFile(left
);
432 return wxEmptyString
;
433 m_Archive
= m_cache
->Add(key
, *factory
, leftFile
->DetachStream());
442 m_AllowDirs
= false, m_AllowFiles
= true; break;
444 m_AllowDirs
= true, m_AllowFiles
= false; break;
446 m_AllowDirs
= m_AllowFiles
= true; break;
451 m_Pattern
= right
.AfterLast(wxT('/'));
452 m_BaseDir
= right
.BeforeLast(wxT('/'));
453 if (m_BaseDir
.StartsWith(wxT("/")))
454 m_BaseDir
= m_BaseDir
.Mid(1);
461 m_DirsFound
= new wxArchiveFilenameHashMap();
462 if (right
.empty()) // allow "/" to match the archive root
467 return wxEmptyString
;
470 wxString
wxArchiveFSHandler::FindNext()
472 if (!m_Archive
) return wxEmptyString
;
476 wxString
wxArchiveFSHandler::DoFind()
478 wxString namestr
, dir
, filename
;
479 wxString match
= wxEmptyString
;
481 while (match
== wxEmptyString
)
483 m_FindEntry
= m_Archive
->GetNext(m_FindEntry
);
491 namestr
= m_FindEntry
->entry
->GetName(wxPATH_UNIX
);
495 dir
= namestr
.BeforeLast(wxT('/'));
498 if( m_DirsFound
->find(dir
) == m_DirsFound
->end() )
500 (*m_DirsFound
)[dir
] = 1;
501 filename
= dir
.AfterLast(wxT('/'));
502 dir
= dir
.BeforeLast(wxT('/'));
503 if (!filename
.empty() && m_BaseDir
== dir
&&
504 wxMatchWild(m_Pattern
, filename
, false))
505 match
= m_ZipFile
+ dir
+ wxT("/") + filename
;
508 break; // already tranversed
512 filename
= namestr
.AfterLast(wxT('/'));
513 dir
= namestr
.BeforeLast(wxT('/'));
514 if (m_AllowFiles
&& !filename
.empty() && m_BaseDir
== dir
&&
515 wxMatchWild(m_Pattern
, filename
, false))
516 match
= m_ZipFile
+ namestr
;
522 #endif // wxUSE_FS_ARCHIVE