]> git.saurik.com Git - wxWidgets.git/blame - src/common/fs_arc.cpp
fixing pattern phase bugs
[wxWidgets.git] / src / common / fs_arc.cpp
CommitLineData
be38a31d 1/////////////////////////////////////////////////////////////////////////////
f068697b
MW
2// Name: fs_arc.cpp
3// Purpose: wxArchive file system
4// Author: Vaclav Slavik, Mike Wetherell
5// Copyright: (c) 1999 Vaclav Slavik, (c) 2006 Mike Wetherell
be38a31d
MW
6// CVS-ID: $Id$
7// Licence: wxWindows licence
8/////////////////////////////////////////////////////////////////////////////
9
10#include "wx/wxprec.h"
11
12#ifdef __BORLANDC__
13#pragma hdrstop
14#endif
15
f068697b
MW
16#if wxUSE_FS_ARCHIVE
17
18#include "wx/fs_arc.h"
be38a31d
MW
19
20#ifndef WXPRECOMP
21 #include "wx/intl.h"
22 #include "wx/log.h"
23#endif
24
f068697b 25#include "wx/archive.h"
916af76f 26#include "wx/private/fileback.h"
f068697b
MW
27
28//---------------------------------------------------------------------------
29// wxArchiveFSCacheDataImpl
30//
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.
33//
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
37// version.
38//---------------------------------------------------------------------------
39
40WX_DECLARE_STRING_HASH_MAP(wxArchiveEntry*, wxArchiveFSEntryHash);
41
42struct wxArchiveFSEntry
43{
44 wxArchiveEntry *entry;
45 wxArchiveFSEntry *next;
46};
47
48class wxArchiveFSCacheDataImpl
49{
50public:
51 wxArchiveFSCacheDataImpl(const wxArchiveClassFactory& factory,
52 const wxBackingFile& backer);
53 wxArchiveFSCacheDataImpl(const wxArchiveClassFactory& factory,
54 wxInputStream *stream);
55
56 ~wxArchiveFSCacheDataImpl();
57
58 void Release() { if (--m_refcount == 0) delete this; }
59 wxArchiveFSCacheDataImpl *AddRef() { m_refcount++; return this; }
60
61 wxArchiveEntry *Get(const wxString& name);
62 wxInputStream *NewStream() const;
63
64 wxArchiveFSEntry *GetNext(wxArchiveFSEntry *fse);
be38a31d 65
f068697b
MW
66private:
67 wxArchiveFSEntry *AddToCache(wxArchiveEntry *entry);
68 void CloseStreams();
69
70 int m_refcount;
71
72 wxArchiveFSEntryHash m_hash;
73 wxArchiveFSEntry *m_begin;
74 wxArchiveFSEntry **m_endptr;
75
76 wxBackingFile m_backer;
77 wxInputStream *m_stream;
78 wxArchiveInputStream *m_archive;
79};
80
81wxArchiveFSCacheDataImpl::wxArchiveFSCacheDataImpl(
82 const wxArchiveClassFactory& factory,
83 const wxBackingFile& backer)
84 : m_refcount(1),
85 m_begin(NULL),
86 m_endptr(&m_begin),
87 m_backer(backer),
88 m_stream(new wxBackedInputStream(backer)),
89 m_archive(factory.NewStream(*m_stream))
90{
91}
92
93wxArchiveFSCacheDataImpl::wxArchiveFSCacheDataImpl(
94 const wxArchiveClassFactory& factory,
95 wxInputStream *stream)
96 : m_refcount(1),
97 m_begin(NULL),
98 m_endptr(&m_begin),
99 m_stream(stream),
100 m_archive(factory.NewStream(*m_stream))
101{
102}
103
104wxArchiveFSCacheDataImpl::~wxArchiveFSCacheDataImpl()
105{
106 WX_CLEAR_HASH_MAP(wxArchiveFSEntryHash, m_hash);
107
108 wxArchiveFSEntry *entry = m_begin;
109
110 while (entry)
111 {
112 wxArchiveFSEntry *next = entry->next;
113 delete entry;
114 entry = next;
115 }
116
117 CloseStreams();
118}
119
120wxArchiveFSEntry *wxArchiveFSCacheDataImpl::AddToCache(wxArchiveEntry *entry)
121{
122 m_hash[entry->GetName()] = entry;
123 wxArchiveFSEntry *fse = new wxArchiveFSEntry;
124 *m_endptr = fse;
125 (*m_endptr)->entry = entry;
126 (*m_endptr)->next = NULL;
127 m_endptr = &(*m_endptr)->next;
128 return fse;
129}
130
131void wxArchiveFSCacheDataImpl::CloseStreams()
132{
133 delete m_archive;
134 m_archive = NULL;
135 delete m_stream;
136 m_stream = NULL;
137}
138
139wxArchiveEntry *wxArchiveFSCacheDataImpl::Get(const wxString& name)
140{
141 wxArchiveFSEntryHash::iterator it = m_hash.find(name);
142
143 if (it != m_hash.end())
144 return it->second;
145
146 if (!m_archive)
147 return NULL;
148
149 wxArchiveEntry *entry;
150
151 while ((entry = m_archive->GetNextEntry()) != NULL)
152 {
153 AddToCache(entry);
154
155 if (entry->GetName() == name)
156 return entry;
157 }
158
159 CloseStreams();
160
161 return NULL;
162}
163
164wxInputStream* wxArchiveFSCacheDataImpl::NewStream() const
165{
166 if (m_backer)
167 return new wxBackedInputStream(m_backer);
168 else
169 return NULL;
170}
171
172wxArchiveFSEntry *wxArchiveFSCacheDataImpl::GetNext(wxArchiveFSEntry *fse)
173{
174 wxArchiveFSEntry *next = fse ? fse->next : m_begin;
175
176 if (!next && m_archive)
177 {
178 wxArchiveEntry *entry = m_archive->GetNextEntry();
179
180 if (entry)
181 next = AddToCache(entry);
182 else
183 CloseStreams();
184 }
185
186 return next;
187}
be38a31d
MW
188
189//---------------------------------------------------------------------------
f068697b
MW
190// wxArchiveFSCacheData
191//
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.
be38a31d 195//---------------------------------------------------------------------------
be38a31d 196
f068697b 197class wxArchiveFSCacheData
be38a31d 198{
f068697b
MW
199public:
200 wxArchiveFSCacheData() : m_impl(NULL) { }
201 wxArchiveFSCacheData(const wxArchiveClassFactory& factory,
202 const wxBackingFile& backer);
203 wxArchiveFSCacheData(const wxArchiveClassFactory& factory,
204 wxInputStream *stream);
205
206 wxArchiveFSCacheData(const wxArchiveFSCacheData& data);
207 wxArchiveFSCacheData& operator=(const wxArchiveFSCacheData& data);
208
209 ~wxArchiveFSCacheData() { if (m_impl) m_impl->Release(); }
210
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); }
215
216private:
217 wxArchiveFSCacheDataImpl *m_impl;
218};
be38a31d 219
f068697b
MW
220wxArchiveFSCacheData::wxArchiveFSCacheData(
221 const wxArchiveClassFactory& factory,
222 const wxBackingFile& backer)
223 : m_impl(new wxArchiveFSCacheDataImpl(factory, backer))
224{
225}
226
227wxArchiveFSCacheData::wxArchiveFSCacheData(
228 const wxArchiveClassFactory& factory,
229 wxInputStream *stream)
230 : m_impl(new wxArchiveFSCacheDataImpl(factory, stream))
231{
232}
233
234wxArchiveFSCacheData::wxArchiveFSCacheData(const wxArchiveFSCacheData& data)
235 : m_impl(data.m_impl ? data.m_impl->AddRef() : NULL)
236{
237}
238
239wxArchiveFSCacheData& wxArchiveFSCacheData::operator=(
240 const wxArchiveFSCacheData& data)
241{
242 if (data.m_impl != m_impl)
243 {
244 if (m_impl)
245 m_impl->Release();
246
247 m_impl = data.m_impl;
248
249 if (m_impl)
250 m_impl->AddRef();
251 }
be38a31d 252
f068697b
MW
253 return *this;
254}
255
256//---------------------------------------------------------------------------
257// wxArchiveFSCache
258//
259// wxArchiveFSCacheData caches a single archive, and this class holds a
260// collection of them to cache all the archives accessed by this instance
261// of wxFileSystem.
262//---------------------------------------------------------------------------
263
264WX_DECLARE_STRING_HASH_MAP(wxArchiveFSCacheData, wxArchiveFSCacheDataHash);
265
266class wxArchiveFSCache
267{
268public:
269 wxArchiveFSCache() { }
270 ~wxArchiveFSCache() { }
271
272 wxArchiveFSCacheData* Add(const wxString& name,
273 const wxArchiveClassFactory& factory,
274 wxInputStream *stream);
275
276 wxArchiveFSCacheData *Get(const wxString& name);
277
278private:
279 wxArchiveFSCacheDataHash m_hash;
be38a31d
MW
280};
281
f068697b
MW
282wxArchiveFSCacheData* wxArchiveFSCache::Add(
283 const wxString& name,
284 const wxArchiveClassFactory& factory,
285 wxInputStream *stream)
286{
d2b23e51 287 wxArchiveFSCacheData& data = m_hash[name];
f068697b
MW
288
289 if (stream->IsSeekable())
290 data = wxArchiveFSCacheData(factory, stream);
291 else
292 data = wxArchiveFSCacheData(factory, wxBackingFile(stream));
293
294 return &data;
295}
296
297wxArchiveFSCacheData *wxArchiveFSCache::Get(const wxString& name)
298{
299 wxArchiveFSCacheDataHash::iterator it;
300
301 if ((it = m_hash.find(name)) != m_hash.end())
302 return &it->second;
303
304 return NULL;
305}
306
be38a31d 307//----------------------------------------------------------------------------
f068697b 308// wxArchiveFSHandler
be38a31d
MW
309//----------------------------------------------------------------------------
310
f068697b
MW
311IMPLEMENT_DYNAMIC_CLASS(wxArchiveFSHandler, wxFileSystemHandler)
312
313wxArchiveFSHandler::wxArchiveFSHandler()
314 : wxFileSystemHandler()
be38a31d
MW
315{
316 m_Archive = NULL;
f068697b 317 m_FindEntry = NULL;
be38a31d
MW
318 m_ZipFile = m_Pattern = m_BaseDir = wxEmptyString;
319 m_AllowDirs = m_AllowFiles = true;
320 m_DirsFound = NULL;
f068697b 321 m_cache = NULL;
be38a31d
MW
322}
323
f068697b 324wxArchiveFSHandler::~wxArchiveFSHandler()
be38a31d
MW
325{
326 Cleanup();
f068697b 327 delete m_cache;
be38a31d
MW
328}
329
f068697b 330void wxArchiveFSHandler::Cleanup()
be38a31d 331{
be38a31d
MW
332 wxDELETE(m_DirsFound);
333}
be38a31d 334
f068697b 335bool wxArchiveFSHandler::CanOpen(const wxString& location)
be38a31d
MW
336{
337 wxString p = GetProtocol(location);
f068697b 338 return wxArchiveClassFactory::Find(p) != NULL;
be38a31d
MW
339}
340
f068697b
MW
341wxFSFile* wxArchiveFSHandler::OpenFile(
342 wxFileSystem& WXUNUSED(fs),
343 const wxString& location)
be38a31d
MW
344{
345 wxString right = GetRightLocation(location);
346 wxString left = GetLeftLocation(location);
f068697b
MW
347 wxString protocol = GetProtocol(location);
348 wxString key = left + wxT("#") + protocol + wxT(":");
be38a31d
MW
349
350 if (right.Contains(wxT("./")))
351 {
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);
356 }
357
358 if (right.GetChar(0) == wxT('/')) right = right.Mid(1);
359
f068697b
MW
360 if (!m_cache)
361 m_cache = new wxArchiveFSCache;
be38a31d 362
f068697b
MW
363 const wxArchiveClassFactory *factory;
364 factory = wxArchiveClassFactory::Find(protocol);
365 if (!factory)
366 return NULL;
367
368 wxArchiveFSCacheData *cached = m_cache->Get(key);
369 if (!cached)
be38a31d 370 {
f068697b
MW
371 wxFSFile *leftFile = m_fs.OpenFile(left);
372 if (!leftFile)
373 return NULL;
374 cached = m_cache->Add(key, *factory, leftFile->DetachStream());
375 delete leftFile;
376 }
377
378 wxArchiveEntry *entry = cached->Get(right);
379 if (!entry)
380 return NULL;
381
382 wxInputStream *leftStream = cached->NewStream();
383 if (!leftStream)
384 {
385 wxFSFile *leftFile = m_fs.OpenFile(left);
386 if (!leftFile)
387 return NULL;
388 leftStream = leftFile->DetachStream();
389 delete leftFile;
390 }
be38a31d 391
f068697b
MW
392 wxArchiveInputStream *s = factory->NewStream(leftStream);
393 s->OpenEntry(*entry);
394
395 if (s && s->IsOk())
396 return new wxFSFile(s,
397 key + right,
be38a31d
MW
398 GetMimeTypeFromExt(location),
399 GetAnchor(location)
400#if wxUSE_DATETIME
f068697b 401 , entry->GetDateTime()
be38a31d
MW
402#endif // wxUSE_DATETIME
403 );
be38a31d
MW
404
405 delete s;
406 return NULL;
407}
408
f068697b 409wxString wxArchiveFSHandler::FindFirst(const wxString& spec, int flags)
be38a31d
MW
410{
411 wxString right = GetRightLocation(spec);
412 wxString left = GetLeftLocation(spec);
f068697b
MW
413 wxString protocol = GetProtocol(spec);
414 wxString key = left + wxT("#") + protocol + wxT(":");
be38a31d
MW
415
416 if (!right.empty() && right.Last() == wxT('/')) right.RemoveLast();
417
f068697b
MW
418 if (!m_cache)
419 m_cache = new wxArchiveFSCache;
420
421 const wxArchiveClassFactory *factory;
422 factory = wxArchiveClassFactory::Find(protocol);
423 if (!factory)
424 return wxEmptyString;
425
426 m_Archive = m_cache->Get(key);
427 if (!m_Archive)
be38a31d 428 {
f068697b
MW
429 wxFSFile *leftFile = m_fs.OpenFile(left);
430 if (!leftFile)
431 return wxEmptyString;
432 m_Archive = m_cache->Add(key, *factory, leftFile->DetachStream());
433 delete leftFile;
be38a31d
MW
434 }
435
f068697b
MW
436 m_FindEntry = NULL;
437
be38a31d
MW
438 switch (flags)
439 {
440 case wxFILE:
441 m_AllowDirs = false, m_AllowFiles = true; break;
442 case wxDIR:
443 m_AllowDirs = true, m_AllowFiles = false; break;
444 default:
445 m_AllowDirs = m_AllowFiles = true; break;
446 }
447
f068697b 448 m_ZipFile = key;
be38a31d
MW
449
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);
454
455 if (m_Archive)
456 {
457 if (m_AllowDirs)
458 {
459 delete m_DirsFound;
f068697b 460 m_DirsFound = new wxArchiveFilenameHashMap();
be38a31d
MW
461 if (right.empty()) // allow "/" to match the archive root
462 return spec;
463 }
464 return DoFind();
465 }
466 return wxEmptyString;
467}
468
f068697b 469wxString wxArchiveFSHandler::FindNext()
be38a31d
MW
470{
471 if (!m_Archive) return wxEmptyString;
472 return DoFind();
473}
474
f068697b 475wxString wxArchiveFSHandler::DoFind()
be38a31d
MW
476{
477 wxString namestr, dir, filename;
478 wxString match = wxEmptyString;
479
480 while (match == wxEmptyString)
481 {
f068697b
MW
482 m_FindEntry = m_Archive->GetNext(m_FindEntry);
483
484 if (!m_FindEntry)
be38a31d 485 {
be38a31d 486 m_Archive = NULL;
f068697b 487 m_FindEntry = NULL;
be38a31d
MW
488 break;
489 }
f068697b 490 namestr = m_FindEntry->entry->GetName(wxPATH_UNIX);
be38a31d
MW
491
492 if (m_AllowDirs)
493 {
494 dir = namestr.BeforeLast(wxT('/'));
495 while (!dir.empty())
496 {
497 if( m_DirsFound->find(dir) == m_DirsFound->end() )
498 {
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))
f068697b 504 match = m_ZipFile + dir + wxT("/") + filename;
be38a31d
MW
505 }
506 else
507 break; // already tranversed
508 }
509 }
510
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))
f068697b 515 match = m_ZipFile + namestr;
be38a31d
MW
516 }
517
518 return match;
519}
520
f068697b 521#endif // wxUSE_FS_ARCHIVE