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"
32 //--------------------------------------------------------------------------------
33 // wxFileSystemHandler
34 //--------------------------------------------------------------------------------
36 IMPLEMENT_ABSTRACT_CLASS(wxFileSystemHandler
, wxObject
)
39 wxString
wxFileSystemHandler::GetMimeTypeFromExt(const wxString
& location
)
42 wxString loc
= GetRightLocation(location
);
44 int l
= loc
.length(), l2
;
47 for (int i
= l
-1; i
>= 0; i
--)
49 c
= loc
[(unsigned int) i
];
54 ext
= loc
.Right(l2
-i
-1);
57 if ( (c
== wxT('/')) || (c
== wxT('\\')) || (c
== wxT(':')) )
62 static bool s_MinimalMimeEnsured
= false;
63 if (!s_MinimalMimeEnsured
)
65 static const wxFileTypeInfo fallbacks
[] =
67 wxFileTypeInfo(_T("image/jpeg"),
70 _T("JPEG image (from fallback)"),
71 _T("jpg"), _T("jpeg"), _T("JPG"), _T("JPEG"), NULL
),
72 wxFileTypeInfo(_T("image/gif"),
75 _T("GIF image (from fallback)"),
76 _T("gif"), _T("GIF"), NULL
),
77 wxFileTypeInfo(_T("image/png"),
80 _T("PNG image (from fallback)"),
81 _T("png"), _T("PNG"), NULL
),
82 wxFileTypeInfo(_T("image/bmp"),
85 _T("windows bitmap image (from fallback)"),
86 _T("bmp"), _T("BMP"), NULL
),
87 wxFileTypeInfo(_T("text/html"),
90 _T("HTML document (from fallback)"),
91 _T("htm"), _T("html"), _T("HTM"), _T("HTML"), NULL
),
92 // must terminate the table with this!
95 wxTheMimeTypesManager
->AddFallbacks(fallbacks
);
96 s_MinimalMimeEnsured
= true;
99 wxFileType
*ft
= wxTheMimeTypesManager
->GetFileTypeFromExtension(ext
);
100 if ( !ft
|| !ft
-> GetMimeType(&mime
) )
102 mime
= wxEmptyString
;
109 if ( ext
.IsSameAs(wxT("htm"), false) || ext
.IsSameAs(_T("html"), false) )
110 return wxT("text/html");
111 if ( ext
.IsSameAs(wxT("jpg"), false) || ext
.IsSameAs(_T("jpeg"), false) )
112 return wxT("image/jpeg");
113 if ( ext
.IsSameAs(wxT("gif"), false) )
114 return wxT("image/gif");
115 if ( ext
.IsSameAs(wxT("png"), false) )
116 return wxT("image/png");
117 if ( ext
.IsSameAs(wxT("bmp"), false) )
118 return wxT("image/bmp");
119 return wxEmptyString
;
125 wxString
wxFileSystemHandler::GetProtocol(const wxString
& location
) const
127 wxString s
= wxEmptyString
;
128 int i
, l
= location
.length();
131 for (i
= l
-1; (i
>= 0) && ((location
[i
] != wxT('#')) || (!fnd
)); i
--) {
132 if ((location
[i
] == wxT(':')) && (i
!= 1 /*win: C:\path*/)) fnd
= true;
134 if (!fnd
) return wxT("file");
135 for (++i
; (i
< l
) && (location
[i
] != wxT(':')); i
++) s
<< location
[i
];
140 wxString
wxFileSystemHandler::GetLeftLocation(const wxString
& location
) const
145 for (i
= location
.length()-1; i
>= 0; i
--) {
146 if ((location
[i
] == wxT(':')) && (i
!= 1 /*win: C:\path*/)) fnd
= true;
147 else if (fnd
&& (location
[i
] == wxT('#'))) return location
.Left(i
);
149 return wxEmptyString
;
152 wxString
wxFileSystemHandler::GetRightLocation(const wxString
& location
) const
154 int i
, l
= location
.length();
159 ((location
[i
] != wxT(':')) || (i
== 1) || (location
[i
-2] == wxT(':')));
162 if (location
[i
] == wxT('#')) l2
= i
+ 1;
164 if (i
== 0) return wxEmptyString
;
165 else return location
.Mid(i
+ 1, l2
- i
- 2);
168 wxString
wxFileSystemHandler::GetAnchor(const wxString
& location
) const
171 int l
= location
.length();
173 for (int i
= l
-1; i
>= 0; i
--) {
175 if (c
== wxT('#')) return location
.Right(l
-i
-1);
176 else if ((c
== wxT('.')) || (c
== wxT('/')) || (c
== wxT('\\')) || (c
== wxT(':'))) return wxEmptyString
;
178 return wxEmptyString
;
182 wxString
wxFileSystemHandler::FindFirst(const wxString
& WXUNUSED(spec
),
185 return wxEmptyString
;
188 wxString
wxFileSystemHandler::FindNext()
190 return wxEmptyString
;
193 //--------------------------------------------------------------------------------
195 //--------------------------------------------------------------------------------
198 wxString
wxLocalFSHandler::ms_root
;
200 bool wxLocalFSHandler::CanOpen(const wxString
& location
)
202 return GetProtocol(location
) == wxT("file");
205 wxFSFile
* wxLocalFSHandler::OpenFile(wxFileSystem
& WXUNUSED(fs
), const wxString
& location
)
207 // location has Unix path separators
208 wxString right
= GetRightLocation(location
);
209 wxFileName fn
= wxFileSystem::URLToFileName(right
);
210 wxString fullpath
= ms_root
+ fn
.GetFullPath();
212 if (!wxFileExists(fullpath
))
213 return (wxFSFile
*) NULL
;
215 // we need to check whether we can really read from this file, otherwise
216 // wxFSFile is not going to work
218 wxFileInputStream
*is
= new wxFileInputStream(fullpath
);
220 wxFFileInputStream
*is
= new wxFFileInputStream(fullpath
);
222 #error One of wxUSE_FILE or wxUSE_FFILE must be set to 1 for wxFSHandler to work
227 return (wxFSFile
*) NULL
;
230 return new wxFSFile(is
,
232 GetMimeTypeFromExt(location
),
235 ,wxDateTime(wxFileModificationTime(fullpath
))
236 #endif // wxUSE_DATETIME
240 wxString
wxLocalFSHandler::FindFirst(const wxString
& spec
, int flags
)
242 wxFileName fn
= wxFileSystem::URLToFileName(GetRightLocation(spec
));
243 return wxFindFirstFile(ms_root
+ fn
.GetFullPath(), flags
);
246 wxString
wxLocalFSHandler::FindNext()
248 return wxFindNextFile();
253 //-----------------------------------------------------------------------------
255 //-----------------------------------------------------------------------------
257 IMPLEMENT_DYNAMIC_CLASS(wxFileSystem
, wxObject
)
258 IMPLEMENT_ABSTRACT_CLASS(wxFSFile
, wxObject
)
261 wxList
wxFileSystem::m_Handlers
;
264 static wxString
MakeCorrectPath(const wxString
& path
)
271 for (i
= 0; i
< cnt
; i
++)
272 if (p
.GetChar(i
) == wxT('\\')) p
.GetWritableChar(i
) = wxT('/'); // Want to be windows-safe
274 if (p
.Left(2) == wxT("./")) { p
= p
.Mid(2); cnt
-= 2; }
276 if (cnt
< 3) return p
;
278 r
<< p
.GetChar(0) << p
.GetChar(1);
280 // skip trailing ../.., if any
281 for (i
= 2; i
< cnt
&& (p
.GetChar(i
) == wxT('/') || p
.GetChar(i
) == wxT('.')); i
++) r
<< p
.GetChar(i
);
283 // remove back references: translate dir1/../dir2 to dir2
287 if (p
.GetChar(i
) == wxT('/') && p
.GetChar(i
-1) == wxT('.') && p
.GetChar(i
-2) == wxT('.'))
289 for (j
= r
.length() - 2; j
>= 0 && r
.GetChar(j
) != wxT('/') && r
.GetChar(j
) != wxT(':'); j
--) {}
290 if (j
>= 0 && r
.GetChar(j
) != wxT(':'))
292 for (j
= j
- 1; j
>= 0 && r
.GetChar(j
) != wxT('/') && r
.GetChar(j
) != wxT(':'); j
--) {}
298 for (; i
< cnt
; i
++) r
<< p
.GetChar(i
);
304 void wxFileSystem::ChangePathTo(const wxString
& location
, bool is_dir
)
308 m_Path
= MakeCorrectPath(location
);
312 if (m_Path
.length() > 0 && m_Path
.Last() != wxT('/') && m_Path
.Last() != wxT(':'))
318 for (i
= m_Path
.length()-1; i
>= 0; i
--)
320 if (m_Path
[(unsigned int) i
] == wxT('/'))
322 if ((i
> 1) && (m_Path
[(unsigned int) (i
-1)] == wxT('/')) && (m_Path
[(unsigned int) (i
-2)] == wxT(':')))
333 else if (m_Path
[(unsigned int) i
] == wxT(':')) {
340 for (i
= 0; i
< (int) m_Path
.length(); i
++)
342 if (m_Path
[(unsigned int) i
] == wxT(':'))
348 if (i
== (int) m_Path
.length())
349 m_Path
= wxEmptyString
;
353 m_Path
.Remove(pathpos
+1);
360 wxFSFile
* wxFileSystem::OpenFile(const wxString
& location
)
362 wxString loc
= MakeCorrectPath(location
);
366 wxList::compatibility_iterator node
;
370 for (i
= 0; i
< ln
; i
++)
374 case wxT('/') : case wxT(':') : case wxT('#') :
378 if (meta
!= 0) break;
380 m_LastName
= wxEmptyString
;
382 // try relative paths first :
383 if (meta
!= wxT(':'))
385 node
= m_Handlers
.GetFirst();
388 wxFileSystemHandler
*h
= (wxFileSystemHandler
*) node
-> GetData();
389 if (h
->CanOpen(m_Path
+ loc
))
391 s
= h
->OpenFile(*this, m_Path
+ loc
);
392 if (s
) { m_LastName
= m_Path
+ loc
; break; }
394 node
= node
->GetNext();
398 // if failed, try absolute paths :
401 node
= m_Handlers
.GetFirst();
404 wxFileSystemHandler
*h
= (wxFileSystemHandler
*) node
->GetData();
407 s
= h
->OpenFile(*this, loc
);
408 if (s
) { m_LastName
= loc
; break; }
410 node
= node
->GetNext();
418 wxString
wxFileSystem::FindFirst(const wxString
& spec
, int flags
)
420 wxList::compatibility_iterator node
;
421 wxString
spec2(spec
);
423 m_FindFileHandler
= NULL
;
425 for (int i
= spec2
.length()-1; i
>= 0; i
--)
426 if (spec2
[(unsigned int) i
] == wxT('\\')) spec2
.GetWritableChar(i
) = wxT('/'); // Want to be windows-safe
428 node
= m_Handlers
.GetFirst();
431 m_FindFileHandler
= (wxFileSystemHandler
*) node
-> GetData();
432 if (m_FindFileHandler
-> CanOpen(m_Path
+ spec2
))
433 return m_FindFileHandler
-> FindFirst(m_Path
+ spec2
, flags
);
434 node
= node
->GetNext();
437 node
= m_Handlers
.GetFirst();
440 m_FindFileHandler
= (wxFileSystemHandler
*) node
-> GetData();
441 if (m_FindFileHandler
-> CanOpen(spec2
))
442 return m_FindFileHandler
-> FindFirst(spec2
, flags
);
443 node
= node
->GetNext();
446 return wxEmptyString
;
451 wxString
wxFileSystem::FindNext()
453 if (m_FindFileHandler
== NULL
) return wxEmptyString
;
454 else return m_FindFileHandler
-> FindNext();
457 bool wxFileSystem::FindFileInPath(wxString
*pStr
,
459 const wxChar
*basename
)
461 // we assume that it's not empty
462 wxCHECK_MSG( !wxIsEmpty(basename
), false,
463 _T("empty file name in wxFileSystem::FindFileInPath"));
465 // skip path separator in the beginning of the file name if present
466 if ( wxIsPathSeparator(*basename
) )
469 wxStringTokenizer
tokenizer(path
, wxPATH_SEP
);
470 while ( tokenizer
.HasMoreTokens() )
472 wxString strFile
= tokenizer
.GetNextToken();
473 if ( !wxEndsWithPathSeparator(strFile
) )
474 strFile
+= wxFILE_SEP_PATH
;
477 wxFSFile
*file
= OpenFile(strFile
);
489 void wxFileSystem::AddHandler(wxFileSystemHandler
*handler
)
491 // prepend the handler to the beginning of the list because handlers added
492 // last should have the highest priority to allow overriding them
493 m_Handlers
.Insert((size_t)0, handler
);
496 wxFileSystemHandler
* wxFileSystem::RemoveHandler(wxFileSystemHandler
*handler
)
498 // if handler has already been removed (or deleted)
499 // we return NULL. This is by design in case
500 // CleanUpHandlers() is called before RemoveHandler
501 // is called, as we cannot control the order
502 // which modules are unloaded
503 if (!m_Handlers
.DeleteObject(handler
))
510 bool wxFileSystem::HasHandlerForPath(const wxString
&location
)
512 for ( wxList::compatibility_iterator node
= m_Handlers
.GetFirst();
513 node
; node
= node
->GetNext() )
515 wxFileSystemHandler
*h
= (wxFileSystemHandler
*) node
->GetData();
516 if (h
->CanOpen(location
))
523 void wxFileSystem::CleanUpHandlers()
525 WX_CLEAR_LIST(wxList
, m_Handlers
);
528 static const wxString
g_unixPathString(wxT("/"));
529 static const wxString
g_nativePathString(wxFILE_SEP_PATH
);
531 // Returns the native path for a file URL
532 wxFileName
wxFileSystem::URLToFileName(const wxString
& url
)
536 if ( path
.Find(wxT("file://")) == 0 )
540 else if ( path
.Find(wxT("file:")) == 0 )
544 // Remove preceding double slash on Mac Classic
545 #if defined(__WXMAC__) && !defined(__UNIX__)
546 else if ( path
.Find(wxT("//")) == 0 )
550 path
.Replace(wxT("%25"), wxT("%"));
551 path
.Replace(wxT("%3A"), wxT(":"));
554 // file urls either start with a forward slash (local harddisk),
555 // otherwise they have a servername/sharename notation,
556 // which only exists on msw and corresponds to a unc
557 if ( path
[0u] == wxT('/') && path
[1u] != wxT('/'))
561 else if ( (url
.Find(wxT("file://")) == 0) &&
562 (path
.Find(wxT('/')) != wxNOT_FOUND
) &&
563 (path
.length() > 1) && (path
[1u] != wxT(':')) )
565 path
= wxT("//") + path
;
569 path
.Replace(g_unixPathString
, g_nativePathString
);
571 return wxFileName(path
, wxPATH_NATIVE
);
574 // Returns the file URL for a native path
575 wxString
wxFileSystem::FileNameToURL(const wxFileName
& filename
)
577 wxFileName fn
= filename
;
578 fn
.Normalize(wxPATH_NORM_DOTS
| wxPATH_NORM_TILDE
| wxPATH_NORM_ABSOLUTE
);
579 wxString url
= fn
.GetFullPath(wxPATH_NATIVE
);
582 // unc notation, wxMSW
583 if ( url
.Find(wxT("\\\\")) == 0 )
585 url
= wxT("//") + url
.Mid(2);
589 url
= wxT("/") + url
;
591 url
= wxT("/") + url
;
597 url
.Replace(g_nativePathString
, g_unixPathString
);
598 url
.Replace(wxT("%"), wxT("%25"));
599 url
.Replace(wxT(":"), wxT("%3A"));
600 url
= wxT("file:") + url
;
607 class wxFileSystemModule
: public wxModule
609 DECLARE_DYNAMIC_CLASS(wxFileSystemModule
)
612 wxFileSystemModule() :
618 virtual bool OnInit()
620 m_handler
= new wxLocalFSHandler
;
621 wxFileSystem::AddHandler(m_handler
);
624 virtual void OnExit()
626 delete wxFileSystem::RemoveHandler(m_handler
);
628 wxFileSystem::CleanUpHandlers();
632 wxFileSystemHandler
* m_handler
;
636 IMPLEMENT_DYNAMIC_CLASS(wxFileSystemModule
, wxModule
)