]> git.saurik.com Git - wxWidgets.git/blame - src/common/fs_arc.cpp
document On{Open,Save}Document()
[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{
138 delete m_archive;
139 m_archive = NULL;
140 delete m_stream;
141 m_stream = NULL;
142}
143
144wxArchiveEntry *wxArchiveFSCacheDataImpl::Get(const wxString& name)
145{
146 wxArchiveFSEntryHash::iterator it = m_hash.find(name);
147
148 if (it != m_hash.end())
149 return it->second;
150
151 if (!m_archive)
152 return NULL;
153
154 wxArchiveEntry *entry;
155
156 while ((entry = m_archive->GetNextEntry()) != NULL)
157 {
158 AddToCache(entry);
159
3acc9200 160 if (entry->GetName(wxPATH_UNIX) == name)
f068697b
MW
161 return entry;
162 }
163
164 CloseStreams();
165
166 return NULL;
167}
168
169wxInputStream* wxArchiveFSCacheDataImpl::NewStream() const
170{
171 if (m_backer)
172 return new wxBackedInputStream(m_backer);
173 else
174 return NULL;
175}
176
177wxArchiveFSEntry *wxArchiveFSCacheDataImpl::GetNext(wxArchiveFSEntry *fse)
178{
179 wxArchiveFSEntry *next = fse ? fse->next : m_begin;
180
181 if (!next && m_archive)
182 {
183 wxArchiveEntry *entry = m_archive->GetNextEntry();
184
185 if (entry)
186 next = AddToCache(entry);
187 else
188 CloseStreams();
189 }
190
191 return next;
192}
be38a31d
MW
193
194//---------------------------------------------------------------------------
f068697b
MW
195// wxArchiveFSCacheData
196//
197// This is the inteface for wxArchiveFSCacheDataImpl above. Holds the catalog
198// of an archive file, and if it is being read from a non-seekable stream, a
199// copy of its backing file.
be38a31d 200//---------------------------------------------------------------------------
be38a31d 201
f068697b 202class wxArchiveFSCacheData
be38a31d 203{
f068697b
MW
204public:
205 wxArchiveFSCacheData() : m_impl(NULL) { }
206 wxArchiveFSCacheData(const wxArchiveClassFactory& factory,
207 const wxBackingFile& backer);
208 wxArchiveFSCacheData(const wxArchiveClassFactory& factory,
209 wxInputStream *stream);
210
211 wxArchiveFSCacheData(const wxArchiveFSCacheData& data);
212 wxArchiveFSCacheData& operator=(const wxArchiveFSCacheData& data);
213
214 ~wxArchiveFSCacheData() { if (m_impl) m_impl->Release(); }
215
216 wxArchiveEntry *Get(const wxString& name) { return m_impl->Get(name); }
217 wxInputStream *NewStream() const { return m_impl->NewStream(); }
218 wxArchiveFSEntry *GetNext(wxArchiveFSEntry *fse)
219 { return m_impl->GetNext(fse); }
220
221private:
222 wxArchiveFSCacheDataImpl *m_impl;
223};
be38a31d 224
f068697b
MW
225wxArchiveFSCacheData::wxArchiveFSCacheData(
226 const wxArchiveClassFactory& factory,
227 const wxBackingFile& backer)
228 : m_impl(new wxArchiveFSCacheDataImpl(factory, backer))
229{
230}
231
232wxArchiveFSCacheData::wxArchiveFSCacheData(
233 const wxArchiveClassFactory& factory,
234 wxInputStream *stream)
235 : m_impl(new wxArchiveFSCacheDataImpl(factory, stream))
236{
237}
238
239wxArchiveFSCacheData::wxArchiveFSCacheData(const wxArchiveFSCacheData& data)
240 : m_impl(data.m_impl ? data.m_impl->AddRef() : NULL)
241{
242}
243
244wxArchiveFSCacheData& wxArchiveFSCacheData::operator=(
245 const wxArchiveFSCacheData& data)
246{
247 if (data.m_impl != m_impl)
248 {
249 if (m_impl)
250 m_impl->Release();
251
252 m_impl = data.m_impl;
253
254 if (m_impl)
255 m_impl->AddRef();
256 }
be38a31d 257
f068697b
MW
258 return *this;
259}
260
261//---------------------------------------------------------------------------
262// wxArchiveFSCache
263//
264// wxArchiveFSCacheData caches a single archive, and this class holds a
265// collection of them to cache all the archives accessed by this instance
266// of wxFileSystem.
267//---------------------------------------------------------------------------
268
269WX_DECLARE_STRING_HASH_MAP(wxArchiveFSCacheData, wxArchiveFSCacheDataHash);
270
271class wxArchiveFSCache
272{
273public:
274 wxArchiveFSCache() { }
275 ~wxArchiveFSCache() { }
276
277 wxArchiveFSCacheData* Add(const wxString& name,
278 const wxArchiveClassFactory& factory,
279 wxInputStream *stream);
280
281 wxArchiveFSCacheData *Get(const wxString& name);
282
283private:
284 wxArchiveFSCacheDataHash m_hash;
be38a31d
MW
285};
286
f068697b
MW
287wxArchiveFSCacheData* wxArchiveFSCache::Add(
288 const wxString& name,
289 const wxArchiveClassFactory& factory,
290 wxInputStream *stream)
291{
d2b23e51 292 wxArchiveFSCacheData& data = m_hash[name];
f068697b
MW
293
294 if (stream->IsSeekable())
295 data = wxArchiveFSCacheData(factory, stream);
296 else
297 data = wxArchiveFSCacheData(factory, wxBackingFile(stream));
298
299 return &data;
300}
301
302wxArchiveFSCacheData *wxArchiveFSCache::Get(const wxString& name)
303{
304 wxArchiveFSCacheDataHash::iterator it;
305
306 if ((it = m_hash.find(name)) != m_hash.end())
307 return &it->second;
308
309 return NULL;
310}
311
be38a31d 312//----------------------------------------------------------------------------
f068697b 313// wxArchiveFSHandler
be38a31d
MW
314//----------------------------------------------------------------------------
315
f068697b
MW
316IMPLEMENT_DYNAMIC_CLASS(wxArchiveFSHandler, wxFileSystemHandler)
317
318wxArchiveFSHandler::wxArchiveFSHandler()
319 : wxFileSystemHandler()
be38a31d
MW
320{
321 m_Archive = NULL;
f068697b 322 m_FindEntry = NULL;
be38a31d
MW
323 m_ZipFile = m_Pattern = m_BaseDir = wxEmptyString;
324 m_AllowDirs = m_AllowFiles = true;
325 m_DirsFound = NULL;
f068697b 326 m_cache = NULL;
be38a31d
MW
327}
328
f068697b 329wxArchiveFSHandler::~wxArchiveFSHandler()
be38a31d
MW
330{
331 Cleanup();
f068697b 332 delete m_cache;
be38a31d
MW
333}
334
f068697b 335void wxArchiveFSHandler::Cleanup()
be38a31d 336{
be38a31d
MW
337 wxDELETE(m_DirsFound);
338}
be38a31d 339
f068697b 340bool wxArchiveFSHandler::CanOpen(const wxString& location)
be38a31d
MW
341{
342 wxString p = GetProtocol(location);
f068697b 343 return wxArchiveClassFactory::Find(p) != NULL;
be38a31d
MW
344}
345
f068697b
MW
346wxFSFile* wxArchiveFSHandler::OpenFile(
347 wxFileSystem& WXUNUSED(fs),
348 const wxString& location)
be38a31d
MW
349{
350 wxString right = GetRightLocation(location);
351 wxString left = GetLeftLocation(location);
f068697b
MW
352 wxString protocol = GetProtocol(location);
353 wxString key = left + wxT("#") + protocol + wxT(":");
be38a31d
MW
354
355 if (right.Contains(wxT("./")))
356 {
357 if (right.GetChar(0) != wxT('/')) right = wxT('/') + right;
358 wxFileName rightPart(right, wxPATH_UNIX);
359 rightPart.Normalize(wxPATH_NORM_DOTS, wxT("/"), wxPATH_UNIX);
360 right = rightPart.GetFullPath(wxPATH_UNIX);
361 }
362
363 if (right.GetChar(0) == wxT('/')) right = right.Mid(1);
364
f068697b
MW
365 if (!m_cache)
366 m_cache = new wxArchiveFSCache;
be38a31d 367
f068697b
MW
368 const wxArchiveClassFactory *factory;
369 factory = wxArchiveClassFactory::Find(protocol);
370 if (!factory)
371 return NULL;
372
373 wxArchiveFSCacheData *cached = m_cache->Get(key);
374 if (!cached)
be38a31d 375 {
f068697b
MW
376 wxFSFile *leftFile = m_fs.OpenFile(left);
377 if (!leftFile)
378 return NULL;
379 cached = m_cache->Add(key, *factory, leftFile->DetachStream());
380 delete leftFile;
381 }
382
383 wxArchiveEntry *entry = cached->Get(right);
384 if (!entry)
385 return NULL;
386
387 wxInputStream *leftStream = cached->NewStream();
388 if (!leftStream)
389 {
390 wxFSFile *leftFile = m_fs.OpenFile(left);
391 if (!leftFile)
392 return NULL;
393 leftStream = leftFile->DetachStream();
394 delete leftFile;
395 }
be38a31d 396
f068697b 397 wxArchiveInputStream *s = factory->NewStream(leftStream);
3681c515
VZ
398 if ( !s )
399 return NULL;
400
f068697b
MW
401 s->OpenEntry(*entry);
402
3681c515 403 if (!s->IsOk())
5c80cef4 404 {
3681c515
VZ
405 delete s;
406 return NULL;
407 }
408
920991ee 409#if WXWIN_COMPATIBILITY_2_6 && wxUSE_ZIPSTREAM
3681c515
VZ
410 if (factory->IsKindOf(CLASSINFO(wxZipClassFactory)))
411 ((wxZipInputStream*)s)->m_allowSeeking = true;
5c80cef4
MW
412#endif // WXWIN_COMPATIBILITY_2_6
413
3681c515
VZ
414 return new wxFSFile(s,
415 key + right,
69cce151 416 wxEmptyString,
3681c515 417 GetAnchor(location)
be38a31d 418#if wxUSE_DATETIME
3681c515 419 , entry->GetDateTime()
be38a31d 420#endif // wxUSE_DATETIME
3681c515 421 );
be38a31d
MW
422}
423
f068697b 424wxString wxArchiveFSHandler::FindFirst(const wxString& spec, int flags)
be38a31d
MW
425{
426 wxString right = GetRightLocation(spec);
427 wxString left = GetLeftLocation(spec);
f068697b
MW
428 wxString protocol = GetProtocol(spec);
429 wxString key = left + wxT("#") + protocol + wxT(":");
be38a31d
MW
430
431 if (!right.empty() && right.Last() == wxT('/')) right.RemoveLast();
432
f068697b
MW
433 if (!m_cache)
434 m_cache = new wxArchiveFSCache;
435
436 const wxArchiveClassFactory *factory;
437 factory = wxArchiveClassFactory::Find(protocol);
438 if (!factory)
439 return wxEmptyString;
440
441 m_Archive = m_cache->Get(key);
442 if (!m_Archive)
be38a31d 443 {
f068697b
MW
444 wxFSFile *leftFile = m_fs.OpenFile(left);
445 if (!leftFile)
446 return wxEmptyString;
447 m_Archive = m_cache->Add(key, *factory, leftFile->DetachStream());
448 delete leftFile;
be38a31d
MW
449 }
450
f068697b
MW
451 m_FindEntry = NULL;
452
be38a31d
MW
453 switch (flags)
454 {
455 case wxFILE:
456 m_AllowDirs = false, m_AllowFiles = true; break;
457 case wxDIR:
458 m_AllowDirs = true, m_AllowFiles = false; break;
459 default:
460 m_AllowDirs = m_AllowFiles = true; break;
461 }
462
f068697b 463 m_ZipFile = key;
be38a31d
MW
464
465 m_Pattern = right.AfterLast(wxT('/'));
466 m_BaseDir = right.BeforeLast(wxT('/'));
467 if (m_BaseDir.StartsWith(wxT("/")))
468 m_BaseDir = m_BaseDir.Mid(1);
469
470 if (m_Archive)
471 {
472 if (m_AllowDirs)
473 {
474 delete m_DirsFound;
f068697b 475 m_DirsFound = new wxArchiveFilenameHashMap();
be38a31d
MW
476 if (right.empty()) // allow "/" to match the archive root
477 return spec;
478 }
479 return DoFind();
480 }
481 return wxEmptyString;
482}
483
f068697b 484wxString wxArchiveFSHandler::FindNext()
be38a31d
MW
485{
486 if (!m_Archive) return wxEmptyString;
487 return DoFind();
488}
489
f068697b 490wxString wxArchiveFSHandler::DoFind()
be38a31d
MW
491{
492 wxString namestr, dir, filename;
493 wxString match = wxEmptyString;
494
495 while (match == wxEmptyString)
496 {
f068697b
MW
497 m_FindEntry = m_Archive->GetNext(m_FindEntry);
498
499 if (!m_FindEntry)
be38a31d 500 {
be38a31d 501 m_Archive = NULL;
f068697b 502 m_FindEntry = NULL;
be38a31d
MW
503 break;
504 }
f068697b 505 namestr = m_FindEntry->entry->GetName(wxPATH_UNIX);
be38a31d
MW
506
507 if (m_AllowDirs)
508 {
509 dir = namestr.BeforeLast(wxT('/'));
510 while (!dir.empty())
511 {
512 if( m_DirsFound->find(dir) == m_DirsFound->end() )
513 {
514 (*m_DirsFound)[dir] = 1;
515 filename = dir.AfterLast(wxT('/'));
516 dir = dir.BeforeLast(wxT('/'));
517 if (!filename.empty() && m_BaseDir == dir &&
518 wxMatchWild(m_Pattern, filename, false))
f068697b 519 match = m_ZipFile + dir + wxT("/") + filename;
be38a31d
MW
520 }
521 else
522 break; // already tranversed
523 }
524 }
525
526 filename = namestr.AfterLast(wxT('/'));
527 dir = namestr.BeforeLast(wxT('/'));
528 if (m_AllowFiles && !filename.empty() && m_BaseDir == dir &&
529 wxMatchWild(m_Pattern, filename, false))
f068697b 530 match = m_ZipFile + namestr;
be38a31d
MW
531 }
532
533 return match;
534}
535
f068697b 536#endif // wxUSE_FS_ARCHIVE