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/private/fileback.h"
28 //---------------------------------------------------------------------------
29 // wxArchiveFSCacheDataImpl
31 // Holds the catalog of an archive file, and if it is being read from a
32 // non-seekable stream, a copy of its backing file.
34 // This class is actually the reference counted implementation for the
35 // wxArchiveFSCacheData class below. It was done that way to allow sharing
36 // between instances of wxFileSystem, though that's a feature not used in this
38 //---------------------------------------------------------------------------
40 WX_DECLARE_STRING_HASH_MAP(wxArchiveEntry
*, wxArchiveFSEntryHash
);
42 struct wxArchiveFSEntry
44 wxArchiveEntry
*entry
;
45 wxArchiveFSEntry
*next
;
48 class wxArchiveFSCacheDataImpl
51 wxArchiveFSCacheDataImpl(const wxArchiveClassFactory
& factory
,
52 const wxBackingFile
& backer
);
53 wxArchiveFSCacheDataImpl(const wxArchiveClassFactory
& factory
,
54 wxInputStream
*stream
);
56 ~wxArchiveFSCacheDataImpl();
58 void Release() { if (--m_refcount
== 0) delete this; }
59 wxArchiveFSCacheDataImpl
*AddRef() { m_refcount
++; return this; }
61 wxArchiveEntry
*Get(const wxString
& name
);
62 wxInputStream
*NewStream() const;
64 wxArchiveFSEntry
*GetNext(wxArchiveFSEntry
*fse
);
67 wxArchiveFSEntry
*AddToCache(wxArchiveEntry
*entry
);
72 wxArchiveFSEntryHash m_hash
;
73 wxArchiveFSEntry
*m_begin
;
74 wxArchiveFSEntry
**m_endptr
;
76 wxBackingFile m_backer
;
77 wxInputStream
*m_stream
;
78 wxArchiveInputStream
*m_archive
;
81 wxArchiveFSCacheDataImpl::wxArchiveFSCacheDataImpl(
82 const wxArchiveClassFactory
& factory
,
83 const wxBackingFile
& backer
)
88 m_stream(new wxBackedInputStream(backer
)),
89 m_archive(factory
.NewStream(*m_stream
))
93 wxArchiveFSCacheDataImpl::wxArchiveFSCacheDataImpl(
94 const wxArchiveClassFactory
& factory
,
95 wxInputStream
*stream
)
100 m_archive(factory
.NewStream(*m_stream
))
104 wxArchiveFSCacheDataImpl::~wxArchiveFSCacheDataImpl()
106 WX_CLEAR_HASH_MAP(wxArchiveFSEntryHash
, m_hash
);
108 wxArchiveFSEntry
*entry
= m_begin
;
112 wxArchiveFSEntry
*next
= entry
->next
;
120 wxArchiveFSEntry
*wxArchiveFSCacheDataImpl::AddToCache(wxArchiveEntry
*entry
)
122 m_hash
[entry
->GetName()] = entry
;
123 wxArchiveFSEntry
*fse
= new wxArchiveFSEntry
;
125 (*m_endptr
)->entry
= entry
;
126 (*m_endptr
)->next
= NULL
;
127 m_endptr
= &(*m_endptr
)->next
;
131 void wxArchiveFSCacheDataImpl::CloseStreams()
139 wxArchiveEntry
*wxArchiveFSCacheDataImpl::Get(const wxString
& name
)
141 wxArchiveFSEntryHash::iterator it
= m_hash
.find(name
);
143 if (it
!= m_hash
.end())
149 wxArchiveEntry
*entry
;
151 while ((entry
= m_archive
->GetNextEntry()) != NULL
)
155 if (entry
->GetName() == name
)
164 wxInputStream
* wxArchiveFSCacheDataImpl::NewStream() const
167 return new wxBackedInputStream(m_backer
);
172 wxArchiveFSEntry
*wxArchiveFSCacheDataImpl::GetNext(wxArchiveFSEntry
*fse
)
174 wxArchiveFSEntry
*next
= fse
? fse
->next
: m_begin
;
176 if (!next
&& m_archive
)
178 wxArchiveEntry
*entry
= m_archive
->GetNextEntry();
181 next
= AddToCache(entry
);
189 //---------------------------------------------------------------------------
190 // wxArchiveFSCacheData
192 // This is the inteface for wxArchiveFSCacheDataImpl above. Holds the catalog
193 // of an archive file, and if it is being read from a non-seekable stream, a
194 // copy of its backing file.
195 //---------------------------------------------------------------------------
197 class wxArchiveFSCacheData
200 wxArchiveFSCacheData() : m_impl(NULL
) { }
201 wxArchiveFSCacheData(const wxArchiveClassFactory
& factory
,
202 const wxBackingFile
& backer
);
203 wxArchiveFSCacheData(const wxArchiveClassFactory
& factory
,
204 wxInputStream
*stream
);
206 wxArchiveFSCacheData(const wxArchiveFSCacheData
& data
);
207 wxArchiveFSCacheData
& operator=(const wxArchiveFSCacheData
& data
);
209 ~wxArchiveFSCacheData() { if (m_impl
) m_impl
->Release(); }
211 wxArchiveEntry
*Get(const wxString
& name
) { return m_impl
->Get(name
); }
212 wxInputStream
*NewStream() const { return m_impl
->NewStream(); }
213 wxArchiveFSEntry
*GetNext(wxArchiveFSEntry
*fse
)
214 { return m_impl
->GetNext(fse
); }
217 wxArchiveFSCacheDataImpl
*m_impl
;
220 wxArchiveFSCacheData::wxArchiveFSCacheData(
221 const wxArchiveClassFactory
& factory
,
222 const wxBackingFile
& backer
)
223 : m_impl(new wxArchiveFSCacheDataImpl(factory
, backer
))
227 wxArchiveFSCacheData::wxArchiveFSCacheData(
228 const wxArchiveClassFactory
& factory
,
229 wxInputStream
*stream
)
230 : m_impl(new wxArchiveFSCacheDataImpl(factory
, stream
))
234 wxArchiveFSCacheData::wxArchiveFSCacheData(const wxArchiveFSCacheData
& data
)
235 : m_impl(data
.m_impl
? data
.m_impl
->AddRef() : NULL
)
239 wxArchiveFSCacheData
& wxArchiveFSCacheData::operator=(
240 const wxArchiveFSCacheData
& data
)
242 if (data
.m_impl
!= m_impl
)
247 m_impl
= data
.m_impl
;
256 //---------------------------------------------------------------------------
259 // wxArchiveFSCacheData caches a single archive, and this class holds a
260 // collection of them to cache all the archives accessed by this instance
262 //---------------------------------------------------------------------------
264 WX_DECLARE_STRING_HASH_MAP(wxArchiveFSCacheData
, wxArchiveFSCacheDataHash
);
266 class wxArchiveFSCache
269 wxArchiveFSCache() { }
270 ~wxArchiveFSCache() { }
272 wxArchiveFSCacheData
* Add(const wxString
& name
,
273 const wxArchiveClassFactory
& factory
,
274 wxInputStream
*stream
);
276 wxArchiveFSCacheData
*Get(const wxString
& name
);
279 wxArchiveFSCacheDataHash m_hash
;
282 wxArchiveFSCacheData
* wxArchiveFSCache::Add(
283 const wxString
& name
,
284 const wxArchiveClassFactory
& factory
,
285 wxInputStream
*stream
)
287 wxArchiveFSCacheData
& data
= m_hash
[name
];
289 if (stream
->IsSeekable())
290 data
= wxArchiveFSCacheData(factory
, stream
);
292 data
= wxArchiveFSCacheData(factory
, wxBackingFile(stream
));
297 wxArchiveFSCacheData
*wxArchiveFSCache::Get(const wxString
& name
)
299 wxArchiveFSCacheDataHash::iterator it
;
301 if ((it
= m_hash
.find(name
)) != m_hash
.end())
307 //----------------------------------------------------------------------------
308 // wxArchiveFSHandler
309 //----------------------------------------------------------------------------
311 IMPLEMENT_DYNAMIC_CLASS(wxArchiveFSHandler
, wxFileSystemHandler
)
313 wxArchiveFSHandler::wxArchiveFSHandler()
314 : wxFileSystemHandler()
318 m_ZipFile
= m_Pattern
= m_BaseDir
= wxEmptyString
;
319 m_AllowDirs
= m_AllowFiles
= true;
324 wxArchiveFSHandler::~wxArchiveFSHandler()
330 void wxArchiveFSHandler::Cleanup()
332 wxDELETE(m_DirsFound
);
335 bool wxArchiveFSHandler::CanOpen(const wxString
& location
)
337 wxString p
= GetProtocol(location
);
338 return wxArchiveClassFactory::Find(p
) != NULL
;
341 wxFSFile
* wxArchiveFSHandler::OpenFile(
342 wxFileSystem
& WXUNUSED(fs
),
343 const wxString
& location
)
345 wxString right
= GetRightLocation(location
);
346 wxString left
= GetLeftLocation(location
);
347 wxString protocol
= GetProtocol(location
);
348 wxString key
= left
+ wxT("#") + protocol
+ wxT(":");
350 if (right
.Contains(wxT("./")))
352 if (right
.GetChar(0) != wxT('/')) right
= wxT('/') + right
;
353 wxFileName
rightPart(right
, wxPATH_UNIX
);
354 rightPart
.Normalize(wxPATH_NORM_DOTS
, wxT("/"), wxPATH_UNIX
);
355 right
= rightPart
.GetFullPath(wxPATH_UNIX
);
358 if (right
.GetChar(0) == wxT('/')) right
= right
.Mid(1);
361 m_cache
= new wxArchiveFSCache
;
363 const wxArchiveClassFactory
*factory
;
364 factory
= wxArchiveClassFactory::Find(protocol
);
368 wxArchiveFSCacheData
*cached
= m_cache
->Get(key
);
371 wxFSFile
*leftFile
= m_fs
.OpenFile(left
);
374 cached
= m_cache
->Add(key
, *factory
, leftFile
->DetachStream());
378 wxArchiveEntry
*entry
= cached
->Get(right
);
382 wxInputStream
*leftStream
= cached
->NewStream();
385 wxFSFile
*leftFile
= m_fs
.OpenFile(left
);
388 leftStream
= leftFile
->DetachStream();
392 wxArchiveInputStream
*s
= factory
->NewStream(leftStream
);
393 s
->OpenEntry(*entry
);
396 return new wxFSFile(s
,
398 GetMimeTypeFromExt(location
),
401 , entry
->GetDateTime()
402 #endif // wxUSE_DATETIME
409 wxString
wxArchiveFSHandler::FindFirst(const wxString
& spec
, int flags
)
411 wxString right
= GetRightLocation(spec
);
412 wxString left
= GetLeftLocation(spec
);
413 wxString protocol
= GetProtocol(spec
);
414 wxString key
= left
+ wxT("#") + protocol
+ wxT(":");
416 if (!right
.empty() && right
.Last() == wxT('/')) right
.RemoveLast();
419 m_cache
= new wxArchiveFSCache
;
421 const wxArchiveClassFactory
*factory
;
422 factory
= wxArchiveClassFactory::Find(protocol
);
424 return wxEmptyString
;
426 m_Archive
= m_cache
->Get(key
);
429 wxFSFile
*leftFile
= m_fs
.OpenFile(left
);
431 return wxEmptyString
;
432 m_Archive
= m_cache
->Add(key
, *factory
, leftFile
->DetachStream());
441 m_AllowDirs
= false, m_AllowFiles
= true; break;
443 m_AllowDirs
= true, m_AllowFiles
= false; break;
445 m_AllowDirs
= m_AllowFiles
= true; break;
450 m_Pattern
= right
.AfterLast(wxT('/'));
451 m_BaseDir
= right
.BeforeLast(wxT('/'));
452 if (m_BaseDir
.StartsWith(wxT("/")))
453 m_BaseDir
= m_BaseDir
.Mid(1);
460 m_DirsFound
= new wxArchiveFilenameHashMap();
461 if (right
.empty()) // allow "/" to match the archive root
466 return wxEmptyString
;
469 wxString
wxArchiveFSHandler::FindNext()
471 if (!m_Archive
) return wxEmptyString
;
475 wxString
wxArchiveFSHandler::DoFind()
477 wxString namestr
, dir
, filename
;
478 wxString match
= wxEmptyString
;
480 while (match
== wxEmptyString
)
482 m_FindEntry
= m_Archive
->GetNext(m_FindEntry
);
490 namestr
= m_FindEntry
->entry
->GetName(wxPATH_UNIX
);
494 dir
= namestr
.BeforeLast(wxT('/'));
497 if( m_DirsFound
->find(dir
) == m_DirsFound
->end() )
499 (*m_DirsFound
)[dir
] = 1;
500 filename
= dir
.AfterLast(wxT('/'));
501 dir
= dir
.BeforeLast(wxT('/'));
502 if (!filename
.empty() && m_BaseDir
== dir
&&
503 wxMatchWild(m_Pattern
, filename
, false))
504 match
= m_ZipFile
+ dir
+ wxT("/") + filename
;
507 break; // already tranversed
511 filename
= namestr
.AfterLast(wxT('/'));
512 dir
= namestr
.BeforeLast(wxT('/'));
513 if (m_AllowFiles
&& !filename
.empty() && m_BaseDir
== dir
&&
514 wxMatchWild(m_Pattern
, filename
, false))
515 match
= m_ZipFile
+ namestr
;
521 #endif // wxUSE_FS_ARCHIVE