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
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
10 #include "wx/wxprec.h"
19 #include "wx/filesys.h"
23 #include "wx/module.h"
26 #include "wx/wfstream.h"
27 #include "wx/mimetype.h"
28 #include "wx/filename.h"
29 #include "wx/tokenzr.h"
31 #include "wx/private/fileback.h"
33 // ----------------------------------------------------------------------------
35 // ----------------------------------------------------------------------------
37 const wxString
& wxFSFile::GetMimeType() const
39 if ( m_MimeType
.empty() && !m_Location
.empty() )
41 wxConstCast(this, wxFSFile
)->m_MimeType
=
42 wxFileSystemHandler::GetMimeTypeFromExt(m_Location
);
48 // ----------------------------------------------------------------------------
49 // wxFileSystemHandler
50 // ----------------------------------------------------------------------------
52 IMPLEMENT_ABSTRACT_CLASS(wxFileSystemHandler
, wxObject
)
56 wxString
wxFileSystemHandler::GetMimeTypeFromExt(const wxString
& location
)
59 wxString loc
= GetRightLocation(location
);
61 int l
= loc
.length(), l2
;
64 for (int i
= l
-1; i
>= 0; i
--)
66 c
= loc
[(unsigned int) i
];
71 ext
= loc
.Right(l2
-i
-1);
74 if ( (c
== wxT('/')) || (c
== wxT('\\')) || (c
== wxT(':')) )
79 static bool s_MinimalMimeEnsured
= false;
80 if (!s_MinimalMimeEnsured
)
82 static const wxFileTypeInfo fallbacks
[] =
84 wxFileTypeInfo(_T("image/jpeg"),
87 _T("JPEG image (from fallback)"),
88 _T("jpg"), _T("jpeg"), _T("JPG"), _T("JPEG"), wxNullPtr
),
89 wxFileTypeInfo(_T("image/gif"),
92 _T("GIF image (from fallback)"),
93 _T("gif"), _T("GIF"), wxNullPtr
),
94 wxFileTypeInfo(_T("image/png"),
97 _T("PNG image (from fallback)"),
98 _T("png"), _T("PNG"), wxNullPtr
),
99 wxFileTypeInfo(_T("image/bmp"),
102 _T("windows bitmap image (from fallback)"),
103 _T("bmp"), _T("BMP"), wxNullPtr
),
104 wxFileTypeInfo(_T("text/html"),
107 _T("HTML document (from fallback)"),
108 _T("htm"), _T("html"), _T("HTM"), _T("HTML"), wxNullPtr
),
109 // must terminate the table with this!
112 wxTheMimeTypesManager
->AddFallbacks(fallbacks
);
113 s_MinimalMimeEnsured
= true;
116 wxFileType
*ft
= wxTheMimeTypesManager
->GetFileTypeFromExtension(ext
);
117 if ( !ft
|| !ft
-> GetMimeType(&mime
) )
119 mime
= wxEmptyString
;
126 if ( ext
.IsSameAs(wxT("htm"), false) || ext
.IsSameAs(_T("html"), false) )
127 return wxT("text/html");
128 if ( ext
.IsSameAs(wxT("jpg"), false) || ext
.IsSameAs(_T("jpeg"), false) )
129 return wxT("image/jpeg");
130 if ( ext
.IsSameAs(wxT("gif"), false) )
131 return wxT("image/gif");
132 if ( ext
.IsSameAs(wxT("png"), false) )
133 return wxT("image/png");
134 if ( ext
.IsSameAs(wxT("bmp"), false) )
135 return wxT("image/bmp");
136 return wxEmptyString
;
143 wxString
wxFileSystemHandler::GetProtocol(const wxString
& location
)
145 wxString s
= wxEmptyString
;
146 int i
, l
= location
.length();
149 for (i
= l
-1; (i
>= 0) && ((location
[i
] != wxT('#')) || (!fnd
)); i
--) {
150 if ((location
[i
] == wxT(':')) && (i
!= 1 /*win: C:\path*/)) fnd
= true;
152 if (!fnd
) return wxT("file");
153 for (++i
; (i
< l
) && (location
[i
] != wxT(':')); i
++) s
<< location
[i
];
159 wxString
wxFileSystemHandler::GetLeftLocation(const wxString
& location
)
164 for (i
= location
.length()-1; i
>= 0; i
--) {
165 if ((location
[i
] == wxT(':')) && (i
!= 1 /*win: C:\path*/)) fnd
= true;
166 else if (fnd
&& (location
[i
] == wxT('#'))) return location
.Left(i
);
168 return wxEmptyString
;
172 wxString
wxFileSystemHandler::GetRightLocation(const wxString
& location
)
174 int i
, l
= location
.length();
179 ((location
[i
] != wxT(':')) || (i
== 1) || (location
[i
-2] == wxT(':')));
182 if (location
[i
] == wxT('#')) l2
= i
+ 1;
184 if (i
== 0) return wxEmptyString
;
185 else return location
.Mid(i
+ 1, l2
- i
- 2);
189 wxString
wxFileSystemHandler::GetAnchor(const wxString
& location
)
192 int l
= location
.length();
194 for (int i
= l
-1; i
>= 0; i
--) {
196 if (c
== wxT('#')) return location
.Right(l
-i
-1);
197 else if ((c
== wxT('.')) || (c
== wxT('/')) || (c
== wxT('\\')) || (c
== wxT(':'))) return wxEmptyString
;
199 return wxEmptyString
;
203 wxString
wxFileSystemHandler::FindFirst(const wxString
& WXUNUSED(spec
),
206 return wxEmptyString
;
209 wxString
wxFileSystemHandler::FindNext()
211 return wxEmptyString
;
214 //--------------------------------------------------------------------------------
216 //--------------------------------------------------------------------------------
219 wxString
wxLocalFSHandler::ms_root
;
221 bool wxLocalFSHandler::CanOpen(const wxString
& location
)
223 return GetProtocol(location
) == wxT("file");
226 wxFSFile
* wxLocalFSHandler::OpenFile(wxFileSystem
& WXUNUSED(fs
), const wxString
& location
)
228 // location has Unix path separators
229 wxString right
= GetRightLocation(location
);
230 wxFileName fn
= wxFileSystem::URLToFileName(right
);
231 wxString fullpath
= ms_root
+ fn
.GetFullPath();
233 if (!wxFileExists(fullpath
))
234 return (wxFSFile
*) NULL
;
236 // we need to check whether we can really read from this file, otherwise
237 // wxFSFile is not going to work
239 wxFFileInputStream
*is
= new wxFFileInputStream(fullpath
);
241 wxFileInputStream
*is
= new wxFileInputStream(fullpath
);
243 #error One of wxUSE_FILE or wxUSE_FFILE must be set to 1 for wxFSHandler to work
248 return (wxFSFile
*) NULL
;
251 return new wxFSFile(is
,
256 ,wxDateTime(wxFileModificationTime(fullpath
))
257 #endif // wxUSE_DATETIME
261 wxString
wxLocalFSHandler::FindFirst(const wxString
& spec
, int flags
)
263 wxFileName fn
= wxFileSystem::URLToFileName(GetRightLocation(spec
));
264 return wxFindFirstFile(ms_root
+ fn
.GetFullPath(), flags
);
267 wxString
wxLocalFSHandler::FindNext()
269 return wxFindNextFile();
274 //-----------------------------------------------------------------------------
276 //-----------------------------------------------------------------------------
278 IMPLEMENT_DYNAMIC_CLASS(wxFileSystem
, wxObject
)
279 IMPLEMENT_ABSTRACT_CLASS(wxFSFile
, wxObject
)
282 wxList
wxFileSystem::m_Handlers
;
285 wxFileSystem::~wxFileSystem()
287 WX_CLEAR_HASH_MAP(wxFSHandlerHash
, m_LocalHandlers
)
291 static wxString
MakeCorrectPath(const wxString
& path
)
298 for (i
= 0; i
< cnt
; i
++)
299 if (p
.GetChar(i
) == wxT('\\')) p
.GetWritableChar(i
) = wxT('/'); // Want to be windows-safe
301 if (p
.Left(2) == wxT("./")) { p
= p
.Mid(2); cnt
-= 2; }
303 if (cnt
< 3) return p
;
305 r
<< p
.GetChar(0) << p
.GetChar(1);
307 // skip trailing ../.., if any
308 for (i
= 2; i
< cnt
&& (p
.GetChar(i
) == wxT('/') || p
.GetChar(i
) == wxT('.')); i
++) r
<< p
.GetChar(i
);
310 // remove back references: translate dir1/../dir2 to dir2
314 if (p
.GetChar(i
) == wxT('/') && p
.GetChar(i
-1) == wxT('.') && p
.GetChar(i
-2) == wxT('.'))
316 for (j
= r
.length() - 2; j
>= 0 && r
.GetChar(j
) != wxT('/') && r
.GetChar(j
) != wxT(':'); j
--) {}
317 if (j
>= 0 && r
.GetChar(j
) != wxT(':'))
319 for (j
= j
- 1; j
>= 0 && r
.GetChar(j
) != wxT('/') && r
.GetChar(j
) != wxT(':'); j
--) {}
325 for (; i
< cnt
; i
++) r
<< p
.GetChar(i
);
331 void wxFileSystem::ChangePathTo(const wxString
& location
, bool is_dir
)
335 m_Path
= MakeCorrectPath(location
);
339 if (m_Path
.length() > 0 && m_Path
.Last() != wxT('/') && m_Path
.Last() != wxT(':'))
345 for (i
= m_Path
.length()-1; i
>= 0; i
--)
347 if (m_Path
[(unsigned int) i
] == wxT('/'))
349 if ((i
> 1) && (m_Path
[(unsigned int) (i
-1)] == wxT('/')) && (m_Path
[(unsigned int) (i
-2)] == wxT(':')))
360 else if (m_Path
[(unsigned int) i
] == wxT(':')) {
367 for (i
= 0; i
< (int) m_Path
.length(); i
++)
369 if (m_Path
[(unsigned int) i
] == wxT(':'))
375 if (i
== (int) m_Path
.length())
376 m_Path
= wxEmptyString
;
380 m_Path
.Remove(pathpos
+1);
387 wxFileSystemHandler
*wxFileSystem::MakeLocal(wxFileSystemHandler
*h
)
389 wxClassInfo
*classinfo
= h
->GetClassInfo();
391 if (classinfo
->IsDynamic())
393 wxFileSystemHandler
*& local
= m_LocalHandlers
[classinfo
];
395 local
= (wxFileSystemHandler
*)classinfo
->CreateObject();
406 wxFSFile
* wxFileSystem::OpenFile(const wxString
& location
, int flags
)
408 if ((flags
& wxFS_READ
) == 0)
411 wxString loc
= MakeCorrectPath(location
);
415 wxList::compatibility_iterator node
;
419 for (i
= 0; i
< ln
; i
++)
421 switch ( loc
[i
].GetValue() )
423 case wxT('/') : case wxT(':') : case wxT('#') :
427 if (meta
!= 0) break;
429 m_LastName
= wxEmptyString
;
431 // try relative paths first :
432 if (meta
!= wxT(':'))
434 node
= m_Handlers
.GetFirst();
437 wxFileSystemHandler
*h
= (wxFileSystemHandler
*) node
-> GetData();
438 if (h
->CanOpen(m_Path
+ loc
))
440 s
= MakeLocal(h
)->OpenFile(*this, m_Path
+ loc
);
441 if (s
) { m_LastName
= m_Path
+ loc
; break; }
443 node
= node
->GetNext();
447 // if failed, try absolute paths :
450 node
= m_Handlers
.GetFirst();
453 wxFileSystemHandler
*h
= (wxFileSystemHandler
*) node
->GetData();
456 s
= MakeLocal(h
)->OpenFile(*this, loc
);
457 if (s
) { m_LastName
= loc
; break; }
459 node
= node
->GetNext();
463 if (s
&& (flags
& wxFS_SEEKABLE
) != 0 && !s
->GetStream()->IsSeekable())
465 wxBackedInputStream
*stream
;
466 stream
= new wxBackedInputStream(s
->DetachStream());
467 stream
->FindLength();
468 s
->SetStream(stream
);
476 wxString
wxFileSystem::FindFirst(const wxString
& spec
, int flags
)
478 wxList::compatibility_iterator node
;
479 wxString
spec2(spec
);
481 m_FindFileHandler
= NULL
;
483 for (int i
= spec2
.length()-1; i
>= 0; i
--)
484 if (spec2
[(unsigned int) i
] == wxT('\\')) spec2
.GetWritableChar(i
) = wxT('/'); // Want to be windows-safe
486 node
= m_Handlers
.GetFirst();
489 wxFileSystemHandler
*h
= (wxFileSystemHandler
*) node
-> GetData();
490 if (h
-> CanOpen(m_Path
+ spec2
))
492 m_FindFileHandler
= MakeLocal(h
);
493 return m_FindFileHandler
-> FindFirst(m_Path
+ spec2
, flags
);
495 node
= node
->GetNext();
498 node
= m_Handlers
.GetFirst();
501 wxFileSystemHandler
*h
= (wxFileSystemHandler
*) node
-> GetData();
502 if (h
-> CanOpen(spec2
))
504 m_FindFileHandler
= MakeLocal(h
);
505 return m_FindFileHandler
-> FindFirst(spec2
, flags
);
507 node
= node
->GetNext();
510 return wxEmptyString
;
515 wxString
wxFileSystem::FindNext()
517 if (m_FindFileHandler
== NULL
) return wxEmptyString
;
518 else return m_FindFileHandler
-> FindNext();
521 bool wxFileSystem::FindFileInPath(wxString
*pStr
,
522 const wxString
& path
,
523 const wxString
& basename
)
525 // we assume that it's not empty
526 wxCHECK_MSG( !basename
.empty(), false,
527 _T("empty file name in wxFileSystem::FindFileInPath"));
530 // skip path separator in the beginning of the file name if present
531 if ( wxIsPathSeparator(basename
[0u]) )
532 name
= basename
.substr(1);
536 wxStringTokenizer
tokenizer(path
, wxPATH_SEP
);
537 while ( tokenizer
.HasMoreTokens() )
539 wxString strFile
= tokenizer
.GetNextToken();
540 if ( !wxEndsWithPathSeparator(strFile
) )
541 strFile
+= wxFILE_SEP_PATH
;
544 wxFSFile
*file
= OpenFile(strFile
);
556 void wxFileSystem::AddHandler(wxFileSystemHandler
*handler
)
558 // prepend the handler to the beginning of the list because handlers added
559 // last should have the highest priority to allow overriding them
560 m_Handlers
.Insert((size_t)0, handler
);
563 wxFileSystemHandler
* wxFileSystem::RemoveHandler(wxFileSystemHandler
*handler
)
565 // if handler has already been removed (or deleted)
566 // we return NULL. This is by design in case
567 // CleanUpHandlers() is called before RemoveHandler
568 // is called, as we cannot control the order
569 // which modules are unloaded
570 if (!m_Handlers
.DeleteObject(handler
))
577 bool wxFileSystem::HasHandlerForPath(const wxString
&location
)
579 for ( wxList::compatibility_iterator node
= m_Handlers
.GetFirst();
580 node
; node
= node
->GetNext() )
582 wxFileSystemHandler
*h
= (wxFileSystemHandler
*) node
->GetData();
583 if (h
->CanOpen(location
))
590 void wxFileSystem::CleanUpHandlers()
592 WX_CLEAR_LIST(wxList
, m_Handlers
);
595 static const wxString
g_unixPathString(wxT("/"));
596 static const wxString
g_nativePathString(wxFILE_SEP_PATH
);
598 // Returns the native path for a file URL
599 wxFileName
wxFileSystem::URLToFileName(const wxString
& url
)
603 if ( path
.Find(wxT("file://")) == 0 )
607 else if ( path
.Find(wxT("file:")) == 0 )
611 // Remove preceding double slash on Mac Classic
612 #if defined(__WXMAC__) && !defined(__UNIX__)
613 else if ( path
.Find(wxT("//")) == 0 )
617 path
= wxURI::Unescape(path
);
620 // file urls either start with a forward slash (local harddisk),
621 // otherwise they have a servername/sharename notation,
622 // which only exists on msw and corresponds to a unc
623 if ( path
[0u] == wxT('/') && path
[1u] != wxT('/'))
627 else if ( (url
.Find(wxT("file://")) == 0) &&
628 (path
.Find(wxT('/')) != wxNOT_FOUND
) &&
629 (path
.length() > 1) && (path
[1u] != wxT(':')) )
631 path
= wxT("//") + path
;
635 path
.Replace(g_unixPathString
, g_nativePathString
);
637 return wxFileName(path
, wxPATH_NATIVE
);
640 // Returns the file URL for a native path
641 wxString
wxFileSystem::FileNameToURL(const wxFileName
& filename
)
643 wxFileName fn
= filename
;
644 fn
.Normalize(wxPATH_NORM_DOTS
| wxPATH_NORM_TILDE
| wxPATH_NORM_ABSOLUTE
);
645 wxString url
= fn
.GetFullPath(wxPATH_NATIVE
);
648 // unc notation, wxMSW
649 if ( url
.Find(wxT("\\\\")) == 0 )
651 url
= wxT("//") + url
.Mid(2);
655 url
= wxT("/") + url
;
657 url
= wxT("/") + url
;
663 url
.Replace(g_nativePathString
, g_unixPathString
);
664 url
.Replace(wxT("%"), wxT("%25")); // '%'s must be replaced first!
665 url
.Replace(wxT("#"), wxT("%23"));
667 // notice that all colons *must* be encoded in the paths used by
668 // wxFileSystem even though this makes URLs produced by this method
669 // unusable with IE under Windows as it requires "file:///c:/foo.bar" and
670 // doesn't accept "file:///c%3a/foo.bar" -- but then we never made any
671 // guarantees about general suitability of the strings returned by this
672 // method, they must work with wxFileSystem only and not encoding the colon
673 // breaks handling of "http://wherever/whatever.zip#zip:filename.ext" URLs
674 // so we really can't do this without heavy changes to the parsing code
675 // here, in particular in GetRightLocation()
676 url
.Replace(wxT(":"), wxT("%3A"));
677 url
= wxT("file:") + url
;
684 class wxFileSystemModule
: public wxModule
686 DECLARE_DYNAMIC_CLASS(wxFileSystemModule
)
689 wxFileSystemModule() :
695 virtual bool OnInit()
697 m_handler
= new wxLocalFSHandler
;
698 wxFileSystem::AddHandler(m_handler
);
701 virtual void OnExit()
703 delete wxFileSystem::RemoveHandler(m_handler
);
705 wxFileSystem::CleanUpHandlers();
709 wxFileSystemHandler
* m_handler
;
713 IMPLEMENT_DYNAMIC_CLASS(wxFileSystemModule
, wxModule
)