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