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