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