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
6 // Licence: wxWindows licence
7 /////////////////////////////////////////////////////////////////////////////
17 #include "wx/fs_arc.h"
24 #if WXWIN_COMPATIBILITY_2_6 && wxUSE_ZIPSTREAM
25 #include "wx/zipstrm.h"
27 #include "wx/archive.h"
30 #include "wx/private/fileback.h"
32 //---------------------------------------------------------------------------
33 // wxArchiveFSCacheDataImpl
35 // Holds the catalog of an archive file, and if it is being read from a
36 // non-seekable stream, a copy of its backing file.
38 // This class is actually the reference counted implementation for the
39 // wxArchiveFSCacheData class below. It was done that way to allow sharing
40 // between instances of wxFileSystem, though that's a feature not used in this
42 //---------------------------------------------------------------------------
44 WX_DECLARE_STRING_HASH_MAP(wxArchiveEntry
*, wxArchiveFSEntryHash
);
46 struct wxArchiveFSEntry
48 wxArchiveEntry
*entry
;
49 wxArchiveFSEntry
*next
;
52 class wxArchiveFSCacheDataImpl
55 wxArchiveFSCacheDataImpl(const wxArchiveClassFactory
& factory
,
56 const wxBackingFile
& backer
);
57 wxArchiveFSCacheDataImpl(const wxArchiveClassFactory
& factory
,
58 wxInputStream
*stream
);
60 ~wxArchiveFSCacheDataImpl();
62 void Release() { if (--m_refcount
== 0) delete this; }
63 wxArchiveFSCacheDataImpl
*AddRef() { m_refcount
++; return this; }
65 wxArchiveEntry
*Get(const wxString
& name
);
66 wxInputStream
*NewStream() const;
68 wxArchiveFSEntry
*GetNext(wxArchiveFSEntry
*fse
);
71 wxArchiveFSEntry
*AddToCache(wxArchiveEntry
*entry
);
76 wxArchiveFSEntryHash m_hash
;
77 wxArchiveFSEntry
*m_begin
;
78 wxArchiveFSEntry
**m_endptr
;
80 wxBackingFile m_backer
;
81 wxInputStream
*m_stream
;
82 wxArchiveInputStream
*m_archive
;
85 wxArchiveFSCacheDataImpl::wxArchiveFSCacheDataImpl(
86 const wxArchiveClassFactory
& factory
,
87 const wxBackingFile
& backer
)
92 m_stream(new wxBackedInputStream(backer
)),
93 m_archive(factory
.NewStream(*m_stream
))
97 wxArchiveFSCacheDataImpl::wxArchiveFSCacheDataImpl(
98 const wxArchiveClassFactory
& factory
,
99 wxInputStream
*stream
)
104 m_archive(factory
.NewStream(*m_stream
))
108 wxArchiveFSCacheDataImpl::~wxArchiveFSCacheDataImpl()
110 WX_CLEAR_HASH_MAP(wxArchiveFSEntryHash
, m_hash
);
112 wxArchiveFSEntry
*entry
= m_begin
;
116 wxArchiveFSEntry
*next
= entry
->next
;
124 wxArchiveFSEntry
*wxArchiveFSCacheDataImpl::AddToCache(wxArchiveEntry
*entry
)
126 m_hash
[entry
->GetName(wxPATH_UNIX
)] = entry
;
127 wxArchiveFSEntry
*fse
= new wxArchiveFSEntry
;
129 (*m_endptr
)->entry
= entry
;
130 (*m_endptr
)->next
= NULL
;
131 m_endptr
= &(*m_endptr
)->next
;
135 void wxArchiveFSCacheDataImpl::CloseStreams()
141 wxArchiveEntry
*wxArchiveFSCacheDataImpl::Get(const wxString
& name
)
143 wxArchiveFSEntryHash::iterator it
= m_hash
.find(name
);
145 if (it
!= m_hash
.end())
151 wxArchiveEntry
*entry
;
153 while ((entry
= m_archive
->GetNextEntry()) != NULL
)
157 if (entry
->GetName(wxPATH_UNIX
) == name
)
166 wxInputStream
* wxArchiveFSCacheDataImpl::NewStream() const
169 return new wxBackedInputStream(m_backer
);
174 wxArchiveFSEntry
*wxArchiveFSCacheDataImpl::GetNext(wxArchiveFSEntry
*fse
)
176 wxArchiveFSEntry
*next
= fse
? fse
->next
: m_begin
;
178 if (!next
&& m_archive
)
180 wxArchiveEntry
*entry
= m_archive
->GetNextEntry();
183 next
= AddToCache(entry
);
191 //---------------------------------------------------------------------------
192 // wxArchiveFSCacheData
194 // This is the inteface for wxArchiveFSCacheDataImpl above. Holds the catalog
195 // of an archive file, and if it is being read from a non-seekable stream, a
196 // copy of its backing file.
197 //---------------------------------------------------------------------------
199 class wxArchiveFSCacheData
202 wxArchiveFSCacheData() : m_impl(NULL
) { }
203 wxArchiveFSCacheData(const wxArchiveClassFactory
& factory
,
204 const wxBackingFile
& backer
);
205 wxArchiveFSCacheData(const wxArchiveClassFactory
& factory
,
206 wxInputStream
*stream
);
208 wxArchiveFSCacheData(const wxArchiveFSCacheData
& data
);
209 wxArchiveFSCacheData
& operator=(const wxArchiveFSCacheData
& data
);
211 ~wxArchiveFSCacheData() { if (m_impl
) m_impl
->Release(); }
213 wxArchiveEntry
*Get(const wxString
& name
) { return m_impl
->Get(name
); }
214 wxInputStream
*NewStream() const { return m_impl
->NewStream(); }
215 wxArchiveFSEntry
*GetNext(wxArchiveFSEntry
*fse
)
216 { return m_impl
->GetNext(fse
); }
219 wxArchiveFSCacheDataImpl
*m_impl
;
222 wxArchiveFSCacheData::wxArchiveFSCacheData(
223 const wxArchiveClassFactory
& factory
,
224 const wxBackingFile
& backer
)
225 : m_impl(new wxArchiveFSCacheDataImpl(factory
, backer
))
229 wxArchiveFSCacheData::wxArchiveFSCacheData(
230 const wxArchiveClassFactory
& factory
,
231 wxInputStream
*stream
)
232 : m_impl(new wxArchiveFSCacheDataImpl(factory
, stream
))
236 wxArchiveFSCacheData::wxArchiveFSCacheData(const wxArchiveFSCacheData
& data
)
237 : m_impl(data
.m_impl
? data
.m_impl
->AddRef() : NULL
)
241 wxArchiveFSCacheData
& wxArchiveFSCacheData::operator=(
242 const wxArchiveFSCacheData
& data
)
244 if (data
.m_impl
!= m_impl
)
249 m_impl
= data
.m_impl
;
258 //---------------------------------------------------------------------------
261 // wxArchiveFSCacheData caches a single archive, and this class holds a
262 // collection of them to cache all the archives accessed by this instance
264 //---------------------------------------------------------------------------
266 WX_DECLARE_STRING_HASH_MAP(wxArchiveFSCacheData
, wxArchiveFSCacheDataHash
);
268 class wxArchiveFSCache
271 wxArchiveFSCache() { }
272 ~wxArchiveFSCache() { }
274 wxArchiveFSCacheData
* Add(const wxString
& name
,
275 const wxArchiveClassFactory
& factory
,
276 wxInputStream
*stream
);
278 wxArchiveFSCacheData
*Get(const wxString
& name
);
281 wxArchiveFSCacheDataHash m_hash
;
284 wxArchiveFSCacheData
* wxArchiveFSCache::Add(
285 const wxString
& name
,
286 const wxArchiveClassFactory
& factory
,
287 wxInputStream
*stream
)
289 wxArchiveFSCacheData
& data
= m_hash
[name
];
291 if (stream
->IsSeekable())
292 data
= wxArchiveFSCacheData(factory
, stream
);
294 data
= wxArchiveFSCacheData(factory
, wxBackingFile(stream
));
299 wxArchiveFSCacheData
*wxArchiveFSCache::Get(const wxString
& name
)
301 wxArchiveFSCacheDataHash::iterator it
;
303 if ((it
= m_hash
.find(name
)) != m_hash
.end())
309 //----------------------------------------------------------------------------
310 // wxArchiveFSHandler
311 //----------------------------------------------------------------------------
313 IMPLEMENT_DYNAMIC_CLASS(wxArchiveFSHandler
, wxFileSystemHandler
)
315 wxArchiveFSHandler::wxArchiveFSHandler()
316 : wxFileSystemHandler()
320 m_ZipFile
= m_Pattern
= m_BaseDir
= wxEmptyString
;
321 m_AllowDirs
= m_AllowFiles
= true;
326 wxArchiveFSHandler::~wxArchiveFSHandler()
332 void wxArchiveFSHandler::Cleanup()
334 wxDELETE(m_DirsFound
);
337 bool wxArchiveFSHandler::CanOpen(const wxString
& location
)
339 wxString p
= GetProtocol(location
);
340 return wxArchiveClassFactory::Find(p
) != NULL
;
343 wxFSFile
* wxArchiveFSHandler::OpenFile(
344 wxFileSystem
& WXUNUSED(fs
),
345 const wxString
& location
)
347 wxString right
= GetRightLocation(location
);
348 wxString left
= GetLeftLocation(location
);
349 wxString protocol
= GetProtocol(location
);
350 wxString key
= left
+ wxT("#") + protocol
+ wxT(":");
352 if (right
.Contains(wxT("./")))
354 if (right
.GetChar(0) != wxT('/')) right
= wxT('/') + right
;
355 wxFileName
rightPart(right
, wxPATH_UNIX
);
356 rightPart
.Normalize(wxPATH_NORM_DOTS
, wxT("/"), wxPATH_UNIX
);
357 right
= rightPart
.GetFullPath(wxPATH_UNIX
);
360 if (!right
.empty() && right
.GetChar(0) == wxT('/')) right
= right
.Mid(1);
363 m_cache
= new wxArchiveFSCache
;
365 const wxArchiveClassFactory
*factory
;
366 factory
= wxArchiveClassFactory::Find(protocol
);
370 wxArchiveFSCacheData
*cached
= m_cache
->Get(key
);
373 wxFSFile
*leftFile
= m_fs
.OpenFile(left
);
376 cached
= m_cache
->Add(key
, *factory
, leftFile
->DetachStream());
380 wxArchiveEntry
*entry
= cached
->Get(right
);
384 wxInputStream
*leftStream
= cached
->NewStream();
387 wxFSFile
*leftFile
= m_fs
.OpenFile(left
);
390 leftStream
= leftFile
->DetachStream();
394 wxArchiveInputStream
*s
= factory
->NewStream(leftStream
);
398 s
->OpenEntry(*entry
);
406 #if WXWIN_COMPATIBILITY_2_6 && wxUSE_ZIPSTREAM
407 if (wxDynamicCast(factory
, wxZipClassFactory
))
408 ((wxZipInputStream
*)s
)->m_allowSeeking
= true;
409 #endif // WXWIN_COMPATIBILITY_2_6
411 return new wxFSFile(s
,
416 , entry
->GetDateTime()
417 #endif // wxUSE_DATETIME
421 wxString
wxArchiveFSHandler::FindFirst(const wxString
& spec
, int flags
)
423 wxString right
= GetRightLocation(spec
);
424 wxString left
= GetLeftLocation(spec
);
425 wxString protocol
= GetProtocol(spec
);
426 wxString key
= left
+ wxT("#") + protocol
+ wxT(":");
428 if (!right
.empty() && right
.Last() == wxT('/')) right
.RemoveLast();
431 m_cache
= new wxArchiveFSCache
;
433 const wxArchiveClassFactory
*factory
;
434 factory
= wxArchiveClassFactory::Find(protocol
);
436 return wxEmptyString
;
438 m_Archive
= m_cache
->Get(key
);
441 wxFSFile
*leftFile
= m_fs
.OpenFile(left
);
443 return wxEmptyString
;
444 m_Archive
= m_cache
->Add(key
, *factory
, leftFile
->DetachStream());
453 m_AllowDirs
= false, m_AllowFiles
= true; break;
455 m_AllowDirs
= true, m_AllowFiles
= false; break;
457 m_AllowDirs
= m_AllowFiles
= true; break;
462 m_Pattern
= right
.AfterLast(wxT('/'));
463 m_BaseDir
= right
.BeforeLast(wxT('/'));
464 if (m_BaseDir
.StartsWith(wxT("/")))
465 m_BaseDir
= m_BaseDir
.Mid(1);
472 m_DirsFound
= new wxArchiveFilenameHashMap();
473 if (right
.empty()) // allow "/" to match the archive root
478 return wxEmptyString
;
481 wxString
wxArchiveFSHandler::FindNext()
483 if (!m_Archive
) return wxEmptyString
;
487 wxString
wxArchiveFSHandler::DoFind()
489 wxString namestr
, dir
, filename
;
490 wxString match
= wxEmptyString
;
492 while (match
== wxEmptyString
)
494 m_FindEntry
= m_Archive
->GetNext(m_FindEntry
);
502 namestr
= m_FindEntry
->entry
->GetName(wxPATH_UNIX
);
506 dir
= namestr
.BeforeLast(wxT('/'));
509 if( m_DirsFound
->find(dir
) == m_DirsFound
->end() )
511 (*m_DirsFound
)[dir
] = 1;
512 filename
= dir
.AfterLast(wxT('/'));
513 dir
= dir
.BeforeLast(wxT('/'));
514 if (!filename
.empty() && m_BaseDir
== dir
&&
515 wxMatchWild(m_Pattern
, filename
, false))
516 match
= m_ZipFile
+ dir
+ wxT("/") + filename
;
519 break; // already tranversed
523 filename
= namestr
.AfterLast(wxT('/'));
524 dir
= namestr
.BeforeLast(wxT('/'));
525 if (m_AllowFiles
&& !filename
.empty() && m_BaseDir
== dir
&&
526 wxMatchWild(m_Pattern
, filename
, false))
527 match
= m_ZipFile
+ namestr
;
533 #endif // wxUSE_FS_ARCHIVE