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