]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/common/filesys.cpp
Initial work on virtual file system support for the WebKitGTK+ backend. It now suppor...
[wxWidgets.git] / src / common / filesys.cpp
... / ...
CommitLineData
1/////////////////////////////////////////////////////////////////////////////
2// Name: src/common/filesys.cpp
3// Purpose: wxFileSystem class - interface for opening files
4// Author: Vaclav Slavik
5// Copyright: (c) 1999 Vaclav Slavik
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
17#if wxUSE_FILESYSTEM
18
19#include "wx/filesys.h"
20
21#ifndef WX_PRECOMP
22 #include "wx/log.h"
23 #include "wx/module.h"
24#endif
25
26#include "wx/sysopt.h"
27#include "wx/wfstream.h"
28#include "wx/mimetype.h"
29#include "wx/filename.h"
30#include "wx/tokenzr.h"
31#include "wx/uri.h"
32#include "wx/private/fileback.h"
33
34// ----------------------------------------------------------------------------
35// wxFSFile
36// ----------------------------------------------------------------------------
37
38const wxString& wxFSFile::GetMimeType() const
39{
40 if ( m_MimeType.empty() && !m_Location.empty() )
41 {
42 wxConstCast(this, wxFSFile)->m_MimeType =
43 wxFileSystemHandler::GetMimeTypeFromExt(m_Location);
44 }
45
46 return m_MimeType;
47}
48
49// ----------------------------------------------------------------------------
50// wxFileSystemHandler
51// ----------------------------------------------------------------------------
52
53IMPLEMENT_ABSTRACT_CLASS(wxFileSystemHandler, wxObject)
54
55
56/* static */
57wxString wxFileSystemHandler::GetMimeTypeFromExt(const wxString& location)
58{
59 wxString ext, mime;
60 wxString loc = GetRightLocation(location);
61 wxChar c;
62 int l = loc.length(), l2;
63
64 l2 = l;
65 for (int i = l-1; i >= 0; i--)
66 {
67 c = loc[(unsigned int) i];
68 if ( c == wxT('#') )
69 l2 = i + 1;
70 if ( c == wxT('.') )
71 {
72 ext = loc.Right(l2-i-1);
73 break;
74 }
75 if ( (c == wxT('/')) || (c == wxT('\\')) || (c == wxT(':')) )
76 return wxEmptyString;
77 }
78
79#if wxUSE_MIMETYPE
80 static bool s_MinimalMimeEnsured = false;
81
82 // Don't use mime types manager if the application doesn't need it and it would be
83 // cause an unacceptable delay, especially on startup.
84#if wxUSE_SYSTEM_OPTIONS
85 if ( !wxSystemOptions::GetOptionInt(wxT("filesys.no-mimetypesmanager")) )
86#endif
87 {
88 if (!s_MinimalMimeEnsured)
89 {
90 static const wxFileTypeInfo fallbacks[] =
91 {
92 wxFileTypeInfo(wxT("image/jpeg"),
93 wxEmptyString,
94 wxEmptyString,
95 wxT("JPEG image (from fallback)"),
96 wxT("jpg"), wxT("jpeg"), wxT("JPG"), wxT("JPEG"), wxNullPtr),
97 wxFileTypeInfo(wxT("image/gif"),
98 wxEmptyString,
99 wxEmptyString,
100 wxT("GIF image (from fallback)"),
101 wxT("gif"), wxT("GIF"), wxNullPtr),
102 wxFileTypeInfo(wxT("image/png"),
103 wxEmptyString,
104 wxEmptyString,
105 wxT("PNG image (from fallback)"),
106 wxT("png"), wxT("PNG"), wxNullPtr),
107 wxFileTypeInfo(wxT("image/bmp"),
108 wxEmptyString,
109 wxEmptyString,
110 wxT("windows bitmap image (from fallback)"),
111 wxT("bmp"), wxT("BMP"), wxNullPtr),
112 wxFileTypeInfo(wxT("text/html"),
113 wxEmptyString,
114 wxEmptyString,
115 wxT("HTML document (from fallback)"),
116 wxT("htm"), wxT("html"), wxT("HTM"), wxT("HTML"), wxNullPtr),
117 // must terminate the table with this!
118 wxFileTypeInfo()
119 };
120 wxTheMimeTypesManager->AddFallbacks(fallbacks);
121 s_MinimalMimeEnsured = true;
122 }
123
124 wxFileType *ft = wxTheMimeTypesManager->GetFileTypeFromExtension(ext);
125 if ( !ft || !ft -> GetMimeType(&mime) )
126 {
127 mime = wxEmptyString;
128 }
129
130 delete ft;
131
132 return mime;
133 }
134 else
135#endif
136 {
137 if ( ext.IsSameAs(wxT("htm"), false) || ext.IsSameAs(wxT("html"), false) )
138 return wxT("text/html");
139 if ( ext.IsSameAs(wxT("jpg"), false) || ext.IsSameAs(wxT("jpeg"), false) )
140 return wxT("image/jpeg");
141 if ( ext.IsSameAs(wxT("gif"), false) )
142 return wxT("image/gif");
143 if ( ext.IsSameAs(wxT("png"), false) )
144 return wxT("image/png");
145 if ( ext.IsSameAs(wxT("bmp"), false) )
146 return wxT("image/bmp");
147 return wxEmptyString;
148 }
149}
150
151
152
153/* static */
154wxString wxFileSystemHandler::GetProtocol(const wxString& location)
155{
156 wxString s = wxEmptyString;
157 int i, l = location.length();
158 bool fnd = false;
159
160 for (i = l-1; (i >= 0) && ((location[i] != wxT('#')) || (!fnd)); i--) {
161 if ((location[i] == wxT(':')) && (i != 1 /*win: C:\path*/)) fnd = true;
162 }
163 if (!fnd) return wxT("file");
164 for (++i; (i < l) && (location[i] != wxT(':')); i++) s << location[i];
165 return s;
166}
167
168
169/* static */
170wxString wxFileSystemHandler::GetLeftLocation(const wxString& location)
171{
172 int i;
173 bool fnd = false;
174
175 for (i = location.length()-1; i >= 0; i--) {
176 if ((location[i] == wxT(':')) && (i != 1 /*win: C:\path*/)) fnd = true;
177 else if (fnd && (location[i] == wxT('#'))) return location.Left(i);
178 }
179 return wxEmptyString;
180}
181
182/* static */
183wxString wxFileSystemHandler::GetRightLocation(const wxString& location)
184{
185 int i, l = location.length();
186 int l2 = l + 1;
187
188 for (i = l-1;
189 (i >= 0) &&
190 ((location[i] != wxT(':')) || (i == 1) || (location[i-2] == wxT(':')));
191 i--)
192 {
193 if (location[i] == wxT('#')) l2 = i + 1;
194 }
195 if (i == 0) return wxEmptyString;
196 else return location.Mid(i + 1, l2 - i - 2);
197}
198
199/* static */
200wxString wxFileSystemHandler::GetAnchor(const wxString& location)
201{
202 wxChar c;
203 int l = location.length();
204
205 for (int i = l-1; i >= 0; i--) {
206 c = location[i];
207 if (c == wxT('#'))
208 return location.Right(l-i-1);
209 else if ((c == wxT('/')) || (c == wxT('\\')) || (c == wxT(':')))
210 return wxEmptyString;
211 }
212 return wxEmptyString;
213}
214
215
216wxString wxFileSystemHandler::FindFirst(const wxString& WXUNUSED(spec),
217 int WXUNUSED(flags))
218{
219 return wxEmptyString;
220}
221
222wxString wxFileSystemHandler::FindNext()
223{
224 return wxEmptyString;
225}
226
227//--------------------------------------------------------------------------------
228// wxLocalFSHandler
229//--------------------------------------------------------------------------------
230
231
232wxString wxLocalFSHandler::ms_root;
233
234bool wxLocalFSHandler::CanOpen(const wxString& location)
235{
236 return GetProtocol(location) == wxT("file");
237}
238
239wxFSFile* wxLocalFSHandler::OpenFile(wxFileSystem& WXUNUSED(fs), const wxString& location)
240{
241 // location has Unix path separators
242 wxString right = GetRightLocation(location);
243 wxFileName fn = wxFileSystem::URLToFileName(right);
244 wxString fullpath = ms_root + fn.GetFullPath();
245
246 if (!wxFileExists(fullpath))
247 return NULL;
248
249 // we need to check whether we can really read from this file, otherwise
250 // wxFSFile is not going to work
251#if wxUSE_FFILE
252 wxFFileInputStream *is = new wxFFileInputStream(fullpath);
253#elif wxUSE_FILE
254 wxFileInputStream *is = new wxFileInputStream(fullpath);
255#else
256#error One of wxUSE_FILE or wxUSE_FFILE must be set to 1 for wxFSHandler to work
257#endif
258 if ( !is->IsOk() )
259 {
260 delete is;
261 return NULL;
262 }
263
264 return new wxFSFile(is,
265 right,
266 wxEmptyString,
267 GetAnchor(location)
268#if wxUSE_DATETIME
269 ,wxDateTime(wxFileModificationTime(fullpath))
270#endif // wxUSE_DATETIME
271 );
272}
273
274wxString wxLocalFSHandler::FindFirst(const wxString& spec, int flags)
275{
276 wxFileName fn = wxFileSystem::URLToFileName(GetRightLocation(spec));
277 const wxString found = wxFindFirstFile(ms_root + fn.GetFullPath(), flags);
278 if ( found.empty() )
279 return found;
280 return wxFileSystem::FileNameToURL(found);
281}
282
283wxString wxLocalFSHandler::FindNext()
284{
285 const wxString found = wxFindNextFile();
286 if ( found.empty() )
287 return found;
288 return wxFileSystem::FileNameToURL(found);
289}
290
291
292
293//-----------------------------------------------------------------------------
294// wxFileSystem
295//-----------------------------------------------------------------------------
296
297IMPLEMENT_DYNAMIC_CLASS(wxFileSystem, wxObject)
298IMPLEMENT_ABSTRACT_CLASS(wxFSFile, wxObject)
299
300
301wxList wxFileSystem::m_Handlers;
302
303
304wxFileSystem::~wxFileSystem()
305{
306 WX_CLEAR_HASH_MAP(wxFSHandlerHash, m_LocalHandlers)
307}
308
309
310static wxString MakeCorrectPath(const wxString& path)
311{
312 wxString p(path);
313 wxString r;
314 int i, j, cnt;
315
316 cnt = p.length();
317 for (i = 0; i < cnt; i++)
318 if (p.GetChar(i) == wxT('\\')) p.GetWritableChar(i) = wxT('/'); // Want to be windows-safe
319
320 if (p.Left(2) == wxT("./")) { p = p.Mid(2); cnt -= 2; }
321
322 if (cnt < 3) return p;
323
324 r << p.GetChar(0) << p.GetChar(1);
325
326 // skip trailing ../.., if any
327 for (i = 2; i < cnt && (p.GetChar(i) == wxT('/') || p.GetChar(i) == wxT('.')); i++) r << p.GetChar(i);
328
329 // remove back references: translate dir1/../dir2 to dir2
330 for (; i < cnt; i++)
331 {
332 r << p.GetChar(i);
333 if (p.GetChar(i) == wxT('/') && p.GetChar(i-1) == wxT('.') && p.GetChar(i-2) == wxT('.'))
334 {
335 for (j = r.length() - 2; j >= 0 && r.GetChar(j) != wxT('/') && r.GetChar(j) != wxT(':'); j--) {}
336 if (j >= 0 && r.GetChar(j) != wxT(':'))
337 {
338 for (j = j - 1; j >= 0 && r.GetChar(j) != wxT('/') && r.GetChar(j) != wxT(':'); j--) {}
339 r.Remove(j + 1);
340 }
341 }
342 }
343
344 for (; i < cnt; i++) r << p.GetChar(i);
345
346 return r;
347}
348
349
350void wxFileSystem::ChangePathTo(const wxString& location, bool is_dir)
351{
352 int i, pathpos = -1;
353
354 m_Path = MakeCorrectPath(location);
355
356 if (is_dir)
357 {
358 if (!m_Path.empty() && m_Path.Last() != wxT('/') && m_Path.Last() != wxT(':'))
359 m_Path << wxT('/');
360 }
361
362 else
363 {
364 for (i = m_Path.length()-1; i >= 0; i--)
365 {
366 if (m_Path[(unsigned int) i] == wxT('/'))
367 {
368 if ((i > 1) && (m_Path[(unsigned int) (i-1)] == wxT('/')) && (m_Path[(unsigned int) (i-2)] == wxT(':')))
369 {
370 i -= 2;
371 continue;
372 }
373 else
374 {
375 pathpos = i;
376 break;
377 }
378 }
379 else if (m_Path[(unsigned int) i] == wxT(':')) {
380 pathpos = i;
381 break;
382 }
383 }
384 if (pathpos == -1)
385 {
386 for (i = 0; i < (int) m_Path.length(); i++)
387 {
388 if (m_Path[(unsigned int) i] == wxT(':'))
389 {
390 m_Path.Remove(i+1);
391 break;
392 }
393 }
394 if (i == (int) m_Path.length())
395 m_Path = wxEmptyString;
396 }
397 else
398 {
399 m_Path.Remove(pathpos+1);
400 }
401 }
402}
403
404
405
406wxFileSystemHandler *wxFileSystem::MakeLocal(wxFileSystemHandler *h)
407{
408 wxClassInfo *classinfo = h->GetClassInfo();
409
410 if (classinfo->IsDynamic())
411 {
412 wxFileSystemHandler*& local = m_LocalHandlers[classinfo];
413 if (!local)
414 local = (wxFileSystemHandler*)classinfo->CreateObject();
415 return local;
416 }
417 else
418 {
419 return h;
420 }
421}
422
423
424
425wxFSFile* wxFileSystem::OpenFile(const wxString& location, int flags)
426{
427 if ((flags & wxFS_READ) == 0)
428 return NULL;
429
430 wxString loc = MakeCorrectPath(location);
431 unsigned i, ln;
432 wxChar meta;
433 wxFSFile *s = NULL;
434 wxList::compatibility_iterator node;
435
436 ln = loc.length();
437 meta = 0;
438 for (i = 0; i < ln; i++)
439 {
440 switch ( loc[i].GetValue() )
441 {
442 case wxT('/') : case wxT(':') : case wxT('#') :
443 meta = loc[i];
444 break;
445 }
446 if (meta != 0) break;
447 }
448 m_LastName = wxEmptyString;
449
450 // try relative paths first :
451 if (meta != wxT(':'))
452 {
453 node = m_Handlers.GetFirst();
454 while (node)
455 {
456 wxFileSystemHandler *h = (wxFileSystemHandler*) node -> GetData();
457 if (h->CanOpen(m_Path + loc))
458 {
459 s = MakeLocal(h)->OpenFile(*this, m_Path + loc);
460 if (s) { m_LastName = m_Path + loc; break; }
461 }
462 node = node->GetNext();
463 }
464 }
465
466 // if failed, try absolute paths :
467 if (s == NULL)
468 {
469 node = m_Handlers.GetFirst();
470 while (node)
471 {
472 wxFileSystemHandler *h = (wxFileSystemHandler*) node->GetData();
473 if (h->CanOpen(loc))
474 {
475 s = MakeLocal(h)->OpenFile(*this, loc);
476 if (s) { m_LastName = loc; break; }
477 }
478 node = node->GetNext();
479 }
480 }
481
482 if (s && (flags & wxFS_SEEKABLE) != 0 && !s->GetStream()->IsSeekable())
483 {
484 wxBackedInputStream *stream;
485 stream = new wxBackedInputStream(s->DetachStream());
486 stream->FindLength();
487 s->SetStream(stream);
488 }
489
490 return (s);
491}
492
493
494
495wxString wxFileSystem::FindFirst(const wxString& spec, int flags)
496{
497 wxList::compatibility_iterator node;
498 wxString spec2(spec);
499
500 m_FindFileHandler = NULL;
501
502 for (int i = spec2.length()-1; i >= 0; i--)
503 if (spec2[(unsigned int) i] == wxT('\\')) spec2.GetWritableChar(i) = wxT('/'); // Want to be windows-safe
504
505 node = m_Handlers.GetFirst();
506 while (node)
507 {
508 wxFileSystemHandler *h = (wxFileSystemHandler*) node -> GetData();
509 if (h -> CanOpen(m_Path + spec2))
510 {
511 m_FindFileHandler = MakeLocal(h);
512 return m_FindFileHandler -> FindFirst(m_Path + spec2, flags);
513 }
514 node = node->GetNext();
515 }
516
517 node = m_Handlers.GetFirst();
518 while (node)
519 {
520 wxFileSystemHandler *h = (wxFileSystemHandler*) node -> GetData();
521 if (h -> CanOpen(spec2))
522 {
523 m_FindFileHandler = MakeLocal(h);
524 return m_FindFileHandler -> FindFirst(spec2, flags);
525 }
526 node = node->GetNext();
527 }
528
529 return wxEmptyString;
530}
531
532
533
534wxString wxFileSystem::FindNext()
535{
536 if (m_FindFileHandler == NULL) return wxEmptyString;
537 else return m_FindFileHandler -> FindNext();
538}
539
540bool wxFileSystem::FindFileInPath(wxString *pStr,
541 const wxString& path,
542 const wxString& basename)
543{
544 // we assume that it's not empty
545 wxCHECK_MSG( !basename.empty(), false,
546 wxT("empty file name in wxFileSystem::FindFileInPath"));
547
548 wxString name;
549 // skip path separator in the beginning of the file name if present
550 if ( wxIsPathSeparator(basename[0u]) )
551 name = basename.substr(1);
552 else
553 name = basename;
554
555 wxStringTokenizer tokenizer(path, wxPATH_SEP);
556 while ( tokenizer.HasMoreTokens() )
557 {
558 wxString strFile = tokenizer.GetNextToken();
559 if ( !wxEndsWithPathSeparator(strFile) )
560 strFile += wxFILE_SEP_PATH;
561 strFile += name;
562
563 wxFSFile *file = OpenFile(strFile);
564 if ( file )
565 {
566 delete file;
567 *pStr = strFile;
568 return true;
569 }
570 }
571
572 return false;
573}
574
575void wxFileSystem::AddHandler(wxFileSystemHandler *handler)
576{
577 // prepend the handler to the beginning of the list because handlers added
578 // last should have the highest priority to allow overriding them
579 m_Handlers.Insert((size_t)0, handler);
580}
581
582wxFileSystemHandler* wxFileSystem::RemoveHandler(wxFileSystemHandler *handler)
583{
584 // if handler has already been removed (or deleted)
585 // we return NULL. This is by design in case
586 // CleanUpHandlers() is called before RemoveHandler
587 // is called, as we cannot control the order
588 // which modules are unloaded
589 if (!m_Handlers.DeleteObject(handler))
590 return NULL;
591
592 return handler;
593}
594
595
596bool wxFileSystem::HasHandlerForPath(const wxString &location)
597{
598 for ( wxList::compatibility_iterator node = m_Handlers.GetFirst();
599 node; node = node->GetNext() )
600 {
601 wxFileSystemHandler *h = (wxFileSystemHandler*) node->GetData();
602 if (h->CanOpen(location))
603 return true;
604 }
605
606 return false;
607}
608
609void wxFileSystem::CleanUpHandlers()
610{
611 WX_CLEAR_LIST(wxList, m_Handlers);
612}
613
614static const wxString g_unixPathString(wxT("/"));
615static const wxString g_nativePathString(wxFILE_SEP_PATH);
616
617// Returns the native path for a file URL
618wxFileName wxFileSystem::URLToFileName(const wxString& url)
619{
620 wxString path = url;
621
622 if ( path.Find(wxT("file://")) == 0 )
623 {
624 path = path.Mid(7);
625 }
626 else if ( path.Find(wxT("file:")) == 0 )
627 {
628 path = path.Mid(5);
629 }
630 // Remove preceding double slash on Mac Classic
631#if defined(__WXMAC__) && !defined(__UNIX__)
632 else if ( path.Find(wxT("//")) == 0 )
633 path = path.Mid(2);
634#endif
635
636 path = wxURI::Unescape(path);
637
638#ifdef __WXMSW__
639 // file urls either start with a forward slash (local harddisk),
640 // otherwise they have a servername/sharename notation,
641 // which only exists on msw and corresponds to a unc
642 if ( path[0u] == wxT('/') && path [1u] != wxT('/'))
643 {
644 path = path.Mid(1);
645 }
646 else if ( (url.Find(wxT("file://")) == 0) &&
647 (path.Find(wxT('/')) != wxNOT_FOUND) &&
648 (path.length() > 1) && (path[1u] != wxT(':')) )
649 {
650 path = wxT("//") + path;
651 }
652#endif
653
654 path.Replace(g_unixPathString, g_nativePathString);
655
656 return wxFileName(path, wxPATH_NATIVE);
657}
658
659// Escapes non-ASCII and others characters in file: URL to be valid URLs
660static wxString EscapeFileNameCharsInURL(const char *in)
661{
662 wxString s;
663
664 for ( const unsigned char *p = (const unsigned char*)in; *p; ++p )
665 {
666 const unsigned char c = *p;
667
668 // notice that all colons *must* be encoded in the paths used by
669 // wxFileSystem even though this makes URLs produced by this method
670 // unusable with IE under Windows as it requires "file:///c:/foo.bar"
671 // and doesn't accept "file:///c%3a/foo.bar" -- but then we never made
672 // any guarantees about general suitability of the strings returned by
673 // this method, they must work with wxFileSystem only and not encoding
674 // the colon breaks handling of
675 // "http://wherever/whatever.zip#zip:filename.ext" URLs so we really
676 // can't do this without heavy changes to the parsing code here, in
677 // particular in GetRightLocation()
678
679 if ( c == '/' || c == '-' || c == '.' || c == '_' || c == '~' ||
680 (c >= '0' && c <= '9') ||
681 (c >= 'a' && c <= 'z') ||
682 (c >= 'A' && c <= 'Z') )
683 {
684 s << c;
685 }
686 else
687 {
688 s << wxString::Format("%%%02x", c);
689 }
690 }
691
692 return s;
693}
694
695// Returns the file URL for a native path
696wxString wxFileSystem::FileNameToURL(const wxFileName& filename)
697{
698 wxFileName fn = filename;
699 fn.Normalize(wxPATH_NORM_DOTS | wxPATH_NORM_TILDE | wxPATH_NORM_ABSOLUTE);
700 wxString url = fn.GetFullPath(wxPATH_NATIVE);
701
702#ifndef __UNIX__
703 // unc notation, wxMSW
704 if ( url.Find(wxT("\\\\")) == 0 )
705 {
706 url = wxT("//") + url.Mid(2);
707 }
708 else
709 {
710 url = wxT("/") + url;
711#ifdef __WXMAC__
712 url = wxT("/") + url;
713#endif
714
715 }
716#endif
717
718 url.Replace(g_nativePathString, g_unixPathString);
719
720 // Do wxURI- and common practice-compatible escaping: encode the string
721 // into UTF-8, then escape anything non-ASCII:
722 return wxT("file:") + EscapeFileNameCharsInURL(url.utf8_str());
723}
724
725
726///// Module:
727
728class wxFileSystemModule : public wxModule
729{
730 DECLARE_DYNAMIC_CLASS(wxFileSystemModule)
731
732 public:
733 wxFileSystemModule() :
734 wxModule(),
735 m_handler(NULL)
736 {
737 }
738
739 virtual bool OnInit()
740 {
741 m_handler = new wxLocalFSHandler;
742 wxFileSystem::AddHandler(m_handler);
743 return true;
744 }
745 virtual void OnExit()
746 {
747 delete wxFileSystem::RemoveHandler(m_handler);
748
749 wxFileSystem::CleanUpHandlers();
750 }
751
752 private:
753 wxFileSystemHandler* m_handler;
754
755};
756
757IMPLEMENT_DYNAMIC_CLASS(wxFileSystemModule, wxModule)
758
759#endif
760 // wxUSE_FILESYSTEM