]> git.saurik.com Git - wxWidgets.git/blob - src/common/fs_arc.cpp
Moved version number to 2.7.2.0 and rebaked
[wxWidgets.git] / src / common / fs_arc.cpp
1 /////////////////////////////////////////////////////////////////////////////
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
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
16 #if wxUSE_FS_ARCHIVE
17
18 #include "wx/fs_arc.h"
19
20 #ifndef WXPRECOMP
21 #include "wx/intl.h"
22 #include "wx/log.h"
23 #endif
24
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
41 WX_DECLARE_STRING_HASH_MAP(wxArchiveEntry*, wxArchiveFSEntryHash);
42
43 struct wxArchiveFSEntry
44 {
45 wxArchiveEntry *entry;
46 wxArchiveFSEntry *next;
47 };
48
49 class wxArchiveFSCacheDataImpl
50 {
51 public:
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);
66
67 private:
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
82 wxArchiveFSCacheDataImpl::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
94 wxArchiveFSCacheDataImpl::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
105 wxArchiveFSCacheDataImpl::~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
121 wxArchiveFSEntry *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
132 void wxArchiveFSCacheDataImpl::CloseStreams()
133 {
134 delete m_archive;
135 m_archive = NULL;
136 delete m_stream;
137 m_stream = NULL;
138 }
139
140 wxArchiveEntry *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
165 wxInputStream* wxArchiveFSCacheDataImpl::NewStream() const
166 {
167 if (m_backer)
168 return new wxBackedInputStream(m_backer);
169 else
170 return NULL;
171 }
172
173 wxArchiveFSEntry *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 }
189
190 //---------------------------------------------------------------------------
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.
196 //---------------------------------------------------------------------------
197
198 class wxArchiveFSCacheData
199 {
200 public:
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
217 private:
218 wxArchiveFSCacheDataImpl *m_impl;
219 };
220
221 wxArchiveFSCacheData::wxArchiveFSCacheData(
222 const wxArchiveClassFactory& factory,
223 const wxBackingFile& backer)
224 : m_impl(new wxArchiveFSCacheDataImpl(factory, backer))
225 {
226 }
227
228 wxArchiveFSCacheData::wxArchiveFSCacheData(
229 const wxArchiveClassFactory& factory,
230 wxInputStream *stream)
231 : m_impl(new wxArchiveFSCacheDataImpl(factory, stream))
232 {
233 }
234
235 wxArchiveFSCacheData::wxArchiveFSCacheData(const wxArchiveFSCacheData& data)
236 : m_impl(data.m_impl ? data.m_impl->AddRef() : NULL)
237 {
238 }
239
240 wxArchiveFSCacheData& 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 }
253
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
265 WX_DECLARE_STRING_HASH_MAP(wxArchiveFSCacheData, wxArchiveFSCacheDataHash);
266
267 class wxArchiveFSCache
268 {
269 public:
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
279 private:
280 wxArchiveFSCacheDataHash m_hash;
281 };
282
283 wxArchiveFSCacheData* wxArchiveFSCache::Add(
284 const wxString& name,
285 const wxArchiveClassFactory& factory,
286 wxInputStream *stream)
287 {
288 wxArchiveFSCacheData& data = m_hash[name];
289
290 if (stream->IsSeekable())
291 data = wxArchiveFSCacheData(factory, stream);
292 else
293 data = wxArchiveFSCacheData(factory, wxBackingFile(stream));
294
295 return &data;
296 }
297
298 wxArchiveFSCacheData *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
308 //----------------------------------------------------------------------------
309 // wxArchiveFSHandler
310 //----------------------------------------------------------------------------
311
312 IMPLEMENT_DYNAMIC_CLASS(wxArchiveFSHandler, wxFileSystemHandler)
313
314 wxArchiveFSHandler::wxArchiveFSHandler()
315 : wxFileSystemHandler()
316 {
317 m_Archive = NULL;
318 m_FindEntry = NULL;
319 m_ZipFile = m_Pattern = m_BaseDir = wxEmptyString;
320 m_AllowDirs = m_AllowFiles = true;
321 m_DirsFound = NULL;
322 m_cache = NULL;
323 }
324
325 wxArchiveFSHandler::~wxArchiveFSHandler()
326 {
327 Cleanup();
328 delete m_cache;
329 }
330
331 void wxArchiveFSHandler::Cleanup()
332 {
333 wxDELETE(m_DirsFound);
334 }
335
336 bool wxArchiveFSHandler::CanOpen(const wxString& location)
337 {
338 wxString p = GetProtocol(location);
339 return wxArchiveClassFactory::Find(p) != NULL;
340 }
341
342 wxFSFile* wxArchiveFSHandler::OpenFile(
343 wxFileSystem& WXUNUSED(fs),
344 const wxString& location)
345 {
346 wxString right = GetRightLocation(location);
347 wxString left = GetLeftLocation(location);
348 wxString protocol = GetProtocol(location);
349 wxString key = left + wxT("#") + protocol + wxT(":");
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
361 if (!m_cache)
362 m_cache = new wxArchiveFSCache;
363
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)
371 {
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 }
392
393 wxArchiveInputStream *s = factory->NewStream(leftStream);
394 s->OpenEntry(*entry);
395
396 if (s && s->IsOk())
397 return new wxFSFile(s,
398 key + right,
399 GetMimeTypeFromExt(location),
400 GetAnchor(location)
401 #if wxUSE_DATETIME
402 , entry->GetDateTime()
403 #endif // wxUSE_DATETIME
404 );
405
406 delete s;
407 return NULL;
408 }
409
410 wxString wxArchiveFSHandler::FindFirst(const wxString& spec, int flags)
411 {
412 wxString right = GetRightLocation(spec);
413 wxString left = GetLeftLocation(spec);
414 wxString protocol = GetProtocol(spec);
415 wxString key = left + wxT("#") + protocol + wxT(":");
416
417 if (!right.empty() && right.Last() == wxT('/')) right.RemoveLast();
418
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)
429 {
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;
435 }
436
437 m_FindEntry = NULL;
438
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
449 m_ZipFile = key;
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;
461 m_DirsFound = new wxArchiveFilenameHashMap();
462 if (right.empty()) // allow "/" to match the archive root
463 return spec;
464 }
465 return DoFind();
466 }
467 return wxEmptyString;
468 }
469
470 wxString wxArchiveFSHandler::FindNext()
471 {
472 if (!m_Archive) return wxEmptyString;
473 return DoFind();
474 }
475
476 wxString wxArchiveFSHandler::DoFind()
477 {
478 wxString namestr, dir, filename;
479 wxString match = wxEmptyString;
480
481 while (match == wxEmptyString)
482 {
483 m_FindEntry = m_Archive->GetNext(m_FindEntry);
484
485 if (!m_FindEntry)
486 {
487 m_Archive = NULL;
488 m_FindEntry = NULL;
489 break;
490 }
491 namestr = m_FindEntry->entry->GetName(wxPATH_UNIX);
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))
505 match = m_ZipFile + dir + wxT("/") + filename;
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))
516 match = m_ZipFile + namestr;
517 }
518
519 return match;
520 }
521
522 #endif // wxUSE_FS_ARCHIVE