]> git.saurik.com Git - wxWidgets.git/blame - src/common/fs_arc.cpp
Rewind the input stream after failing to load image from it.
[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 19
b4f4d3dd 20#ifndef WX_PRECOMP
be38a31d
MW
21 #include "wx/intl.h"
22 #include "wx/log.h"
23#endif
24
920991ee 25#if WXWIN_COMPATIBILITY_2_6 && wxUSE_ZIPSTREAM
5c80cef4
MW
26 #include "wx/zipstrm.h"
27#else
28 #include "wx/archive.h"
29#endif
30
916af76f 31#include "wx/private/fileback.h"
f068697b
MW
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
45WX_DECLARE_STRING_HASH_MAP(wxArchiveEntry*, wxArchiveFSEntryHash);
46
47struct wxArchiveFSEntry
48{
49 wxArchiveEntry *entry;
50 wxArchiveFSEntry *next;
51};
52
53class wxArchiveFSCacheDataImpl
54{
55public:
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);
be38a31d 70
f068697b
MW
71private:
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
86wxArchiveFSCacheDataImpl::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
98wxArchiveFSCacheDataImpl::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
109wxArchiveFSCacheDataImpl::~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
125wxArchiveFSEntry *wxArchiveFSCacheDataImpl::AddToCache(wxArchiveEntry *entry)
126{
3acc9200 127 m_hash[entry->GetName(wxPATH_UNIX)] = entry;
f068697b
MW
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
136void wxArchiveFSCacheDataImpl::CloseStreams()
137{
5276b0a5
VZ
138 wxDELETE(m_archive);
139 wxDELETE(m_stream);
f068697b
MW
140}
141
142wxArchiveEntry *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
3acc9200 158 if (entry->GetName(wxPATH_UNIX) == name)
f068697b
MW
159 return entry;
160 }
161
162 CloseStreams();
163
164 return NULL;
165}
166
167wxInputStream* wxArchiveFSCacheDataImpl::NewStream() const
168{
169 if (m_backer)
170 return new wxBackedInputStream(m_backer);
171 else
172 return NULL;
173}
174
175wxArchiveFSEntry *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}
be38a31d
MW
191
192//---------------------------------------------------------------------------
f068697b
MW
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.
be38a31d 198//---------------------------------------------------------------------------
be38a31d 199
f068697b 200class wxArchiveFSCacheData
be38a31d 201{
f068697b
MW
202public:
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
219private:
220 wxArchiveFSCacheDataImpl *m_impl;
221};
be38a31d 222
f068697b
MW
223wxArchiveFSCacheData::wxArchiveFSCacheData(
224 const wxArchiveClassFactory& factory,
225 const wxBackingFile& backer)
226 : m_impl(new wxArchiveFSCacheDataImpl(factory, backer))
227{
228}
229
230wxArchiveFSCacheData::wxArchiveFSCacheData(
231 const wxArchiveClassFactory& factory,
232 wxInputStream *stream)
233 : m_impl(new wxArchiveFSCacheDataImpl(factory, stream))
234{
235}
236
237wxArchiveFSCacheData::wxArchiveFSCacheData(const wxArchiveFSCacheData& data)
238 : m_impl(data.m_impl ? data.m_impl->AddRef() : NULL)
239{
240}
241
242wxArchiveFSCacheData& 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 }
be38a31d 255
f068697b
MW
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
267WX_DECLARE_STRING_HASH_MAP(wxArchiveFSCacheData, wxArchiveFSCacheDataHash);
268
269class wxArchiveFSCache
270{
271public:
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
281private:
282 wxArchiveFSCacheDataHash m_hash;
be38a31d
MW
283};
284
f068697b
MW
285wxArchiveFSCacheData* wxArchiveFSCache::Add(
286 const wxString& name,
287 const wxArchiveClassFactory& factory,
288 wxInputStream *stream)
289{
d2b23e51 290 wxArchiveFSCacheData& data = m_hash[name];
f068697b
MW
291
292 if (stream->IsSeekable())
293 data = wxArchiveFSCacheData(factory, stream);
294 else
295 data = wxArchiveFSCacheData(factory, wxBackingFile(stream));
296
297 return &data;
298}
299
300wxArchiveFSCacheData *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
be38a31d 310//----------------------------------------------------------------------------
f068697b 311// wxArchiveFSHandler
be38a31d
MW
312//----------------------------------------------------------------------------
313
f068697b
MW
314IMPLEMENT_DYNAMIC_CLASS(wxArchiveFSHandler, wxFileSystemHandler)
315
316wxArchiveFSHandler::wxArchiveFSHandler()
317 : wxFileSystemHandler()
be38a31d
MW
318{
319 m_Archive = NULL;
f068697b 320 m_FindEntry = NULL;
be38a31d
MW
321 m_ZipFile = m_Pattern = m_BaseDir = wxEmptyString;
322 m_AllowDirs = m_AllowFiles = true;
323 m_DirsFound = NULL;
f068697b 324 m_cache = NULL;
be38a31d
MW
325}
326
f068697b 327wxArchiveFSHandler::~wxArchiveFSHandler()
be38a31d
MW
328{
329 Cleanup();
f068697b 330 delete m_cache;
be38a31d
MW
331}
332
f068697b 333void wxArchiveFSHandler::Cleanup()
be38a31d 334{
be38a31d
MW
335 wxDELETE(m_DirsFound);
336}
be38a31d 337
f068697b 338bool wxArchiveFSHandler::CanOpen(const wxString& location)
be38a31d
MW
339{
340 wxString p = GetProtocol(location);
f068697b 341 return wxArchiveClassFactory::Find(p) != NULL;
be38a31d
MW
342}
343
f068697b
MW
344wxFSFile* wxArchiveFSHandler::OpenFile(
345 wxFileSystem& WXUNUSED(fs),
346 const wxString& location)
be38a31d
MW
347{
348 wxString right = GetRightLocation(location);
349 wxString left = GetLeftLocation(location);
f068697b
MW
350 wxString protocol = GetProtocol(location);
351 wxString key = left + wxT("#") + protocol + wxT(":");
be38a31d
MW
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
f79cf732 361 if (right.Length() && right.GetChar(0) == wxT('/')) right = right.Mid(1);
be38a31d 362
f068697b
MW
363 if (!m_cache)
364 m_cache = new wxArchiveFSCache;
be38a31d 365
f068697b
MW
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)
be38a31d 373 {
f068697b
MW
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 }
be38a31d 394
f068697b 395 wxArchiveInputStream *s = factory->NewStream(leftStream);
3681c515
VZ
396 if ( !s )
397 return NULL;
398
f068697b
MW
399 s->OpenEntry(*entry);
400
3681c515 401 if (!s->IsOk())
5c80cef4 402 {
3681c515
VZ
403 delete s;
404 return NULL;
405 }
406
920991ee 407#if WXWIN_COMPATIBILITY_2_6 && wxUSE_ZIPSTREAM
3681c515
VZ
408 if (factory->IsKindOf(CLASSINFO(wxZipClassFactory)))
409 ((wxZipInputStream*)s)->m_allowSeeking = true;
5c80cef4
MW
410#endif // WXWIN_COMPATIBILITY_2_6
411
3681c515
VZ
412 return new wxFSFile(s,
413 key + right,
69cce151 414 wxEmptyString,
3681c515 415 GetAnchor(location)
be38a31d 416#if wxUSE_DATETIME
3681c515 417 , entry->GetDateTime()
be38a31d 418#endif // wxUSE_DATETIME
3681c515 419 );
be38a31d
MW
420}
421
f068697b 422wxString wxArchiveFSHandler::FindFirst(const wxString& spec, int flags)
be38a31d
MW
423{
424 wxString right = GetRightLocation(spec);
425 wxString left = GetLeftLocation(spec);
f068697b
MW
426 wxString protocol = GetProtocol(spec);
427 wxString key = left + wxT("#") + protocol + wxT(":");
be38a31d
MW
428
429 if (!right.empty() && right.Last() == wxT('/')) right.RemoveLast();
430
f068697b
MW
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)
be38a31d 441 {
f068697b
MW
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;
be38a31d
MW
447 }
448
f068697b
MW
449 m_FindEntry = NULL;
450
be38a31d
MW
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
f068697b 461 m_ZipFile = key;
be38a31d
MW
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;
f068697b 473 m_DirsFound = new wxArchiveFilenameHashMap();
be38a31d
MW
474 if (right.empty()) // allow "/" to match the archive root
475 return spec;
476 }
477 return DoFind();
478 }
479 return wxEmptyString;
480}
481
f068697b 482wxString wxArchiveFSHandler::FindNext()
be38a31d
MW
483{
484 if (!m_Archive) return wxEmptyString;
485 return DoFind();
486}
487
f068697b 488wxString wxArchiveFSHandler::DoFind()
be38a31d
MW
489{
490 wxString namestr, dir, filename;
491 wxString match = wxEmptyString;
492
493 while (match == wxEmptyString)
494 {
f068697b
MW
495 m_FindEntry = m_Archive->GetNext(m_FindEntry);
496
497 if (!m_FindEntry)
be38a31d 498 {
be38a31d 499 m_Archive = NULL;
f068697b 500 m_FindEntry = NULL;
be38a31d
MW
501 break;
502 }
f068697b 503 namestr = m_FindEntry->entry->GetName(wxPATH_UNIX);
be38a31d
MW
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))
f068697b 517 match = m_ZipFile + dir + wxT("/") + filename;
be38a31d
MW
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))
f068697b 528 match = m_ZipFile + namestr;
be38a31d
MW
529 }
530
531 return match;
532}
533
f068697b 534#endif // wxUSE_FS_ARCHIVE