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/sysopt.h" 
  27 #include "wx/wfstream.h" 
  28 #include "wx/mimetype.h" 
  29 #include "wx/filename.h" 
  30 #include "wx/tokenzr.h" 
  32 #include "wx/private/fileback.h" 
  34 // ---------------------------------------------------------------------------- 
  36 // ---------------------------------------------------------------------------- 
  38 const wxString
& wxFSFile::GetMimeType() const 
  40     if ( m_MimeType
.empty() && !m_Location
.empty() ) 
  42         wxConstCast(this, wxFSFile
)->m_MimeType 
= 
  43             wxFileSystemHandler::GetMimeTypeFromExt(m_Location
); 
  49 // ---------------------------------------------------------------------------- 
  50 // wxFileSystemHandler 
  51 // ---------------------------------------------------------------------------- 
  53 IMPLEMENT_ABSTRACT_CLASS(wxFileSystemHandler
, wxObject
) 
  57 wxString 
wxFileSystemHandler::GetMimeTypeFromExt(const wxString
& location
) 
  60     wxString loc 
= GetRightLocation(location
); 
  62     int l 
= loc
.length(), l2
; 
  65     for (int i 
= l
-1; i 
>= 0; i
--) 
  67         c 
= loc
[(unsigned int) i
]; 
  72             ext 
= loc
.Right(l2
-i
-1); 
  75         if ( (c 
== wxT('/')) || (c 
== wxT('\\')) || (c 
== wxT(':')) ) 
  80     static bool s_MinimalMimeEnsured 
= false; 
  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")) ) 
  88         if (!s_MinimalMimeEnsured
) 
  90             static const wxFileTypeInfo fallbacks
[] = 
  92                 wxFileTypeInfo(wxT("image/jpeg"), 
  95                     wxT("JPEG image (from fallback)"), 
  96                     wxT("jpg"), wxT("jpeg"), wxT("JPG"), wxT("JPEG"), wxNullPtr
), 
  97                 wxFileTypeInfo(wxT("image/gif"), 
 100                     wxT("GIF image (from fallback)"), 
 101                     wxT("gif"), wxT("GIF"), wxNullPtr
), 
 102                 wxFileTypeInfo(wxT("image/png"), 
 105                     wxT("PNG image (from fallback)"), 
 106                     wxT("png"), wxT("PNG"), wxNullPtr
), 
 107                 wxFileTypeInfo(wxT("image/bmp"), 
 110                     wxT("windows bitmap image (from fallback)"), 
 111                     wxT("bmp"), wxT("BMP"), wxNullPtr
), 
 112                 wxFileTypeInfo(wxT("text/html"), 
 115                     wxT("HTML document (from fallback)"), 
 116                     wxT("htm"), wxT("html"), wxT("HTM"), wxT("HTML"), wxNullPtr
), 
 117                 // must terminate the table with this! 
 120             wxTheMimeTypesManager
->AddFallbacks(fallbacks
); 
 121             s_MinimalMimeEnsured 
= true; 
 124         wxFileType 
*ft 
= wxTheMimeTypesManager
->GetFileTypeFromExtension(ext
); 
 125         if ( !ft 
|| !ft 
-> GetMimeType(&mime
) ) 
 127             mime 
= wxEmptyString
; 
 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
; 
 154 wxString 
wxFileSystemHandler::GetProtocol(const wxString
& location
) 
 156     wxString s 
= wxEmptyString
; 
 157     int i
, l 
= location
.length(); 
 160     for (i 
= l
-1; (i 
>= 0) && ((location
[i
] != wxT('#')) || (!fnd
)); i
--) { 
 161         if ((location
[i
] == wxT(':')) && (i 
!= 1 /*win: C:\path*/)) fnd 
= true; 
 163     if (!fnd
) return wxT("file"); 
 164     for (++i
; (i 
< l
) && (location
[i
] != wxT(':')); i
++) s 
<< location
[i
]; 
 170 wxString 
wxFileSystemHandler::GetLeftLocation(const wxString
& location
) 
 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
); 
 179     return wxEmptyString
; 
 183 wxString 
wxFileSystemHandler::GetRightLocation(const wxString
& location
) 
 185     int i
, l 
= location
.length(); 
 190          ((location
[i
] != wxT(':')) || (i 
== 1) || (location
[i
-2] == wxT(':'))); 
 193         if (location
[i
] == wxT('#')) l2 
= i 
+ 1; 
 195     if (i 
== 0) return wxEmptyString
; 
 196     else return location
.Mid(i 
+ 1, l2 
- i 
- 2); 
 200 wxString 
wxFileSystemHandler::GetAnchor(const wxString
& location
) 
 203     int l 
= location
.length(); 
 205     for (int i 
= l
-1; i 
>= 0; i
--) { 
 208             return location
.Right(l
-i
-1); 
 209         else if ((c 
== wxT('/')) || (c 
== wxT('\\')) || (c 
== wxT(':'))) 
 210             return wxEmptyString
; 
 212     return wxEmptyString
; 
 216 wxString 
wxFileSystemHandler::FindFirst(const wxString
& WXUNUSED(spec
), 
 219     return wxEmptyString
; 
 222 wxString 
wxFileSystemHandler::FindNext() 
 224     return wxEmptyString
; 
 227 //-------------------------------------------------------------------------------- 
 229 //-------------------------------------------------------------------------------- 
 232 wxString 
wxLocalFSHandler::ms_root
; 
 234 bool wxLocalFSHandler::CanOpen(const wxString
& location
) 
 236     return GetProtocol(location
) == wxT("file"); 
 239 wxFSFile
* wxLocalFSHandler::OpenFile(wxFileSystem
& WXUNUSED(fs
), const wxString
& location
) 
 241     // location has Unix path separators 
 242     wxString right 
= GetRightLocation(location
); 
 243     wxFileName fn 
= wxFileSystem::URLToFileName(right
); 
 244     wxString fullpath 
= ms_root 
+ fn
.GetFullPath(); 
 246     if (!wxFileExists(fullpath
)) 
 249     // we need to check whether we can really read from this file, otherwise 
 250     // wxFSFile is not going to work 
 252     wxFFileInputStream 
*is 
= new wxFFileInputStream(fullpath
); 
 254     wxFileInputStream 
*is 
= new wxFileInputStream(fullpath
); 
 256 #error One of wxUSE_FILE or wxUSE_FFILE must be set to 1 for wxFSHandler to work 
 264     return new wxFSFile(is
, 
 269                         ,wxDateTime(wxFileModificationTime(fullpath
)) 
 270 #endif // wxUSE_DATETIME 
 274 wxString 
wxLocalFSHandler::FindFirst(const wxString
& spec
, int flags
) 
 276     wxFileName fn 
= wxFileSystem::URLToFileName(GetRightLocation(spec
)); 
 277     const wxString found 
= wxFindFirstFile(ms_root 
+ fn
.GetFullPath(), flags
); 
 280     return wxFileSystem::FileNameToURL(found
); 
 283 wxString 
wxLocalFSHandler::FindNext() 
 285     const wxString found 
= wxFindNextFile(); 
 288     return wxFileSystem::FileNameToURL(found
); 
 293 //----------------------------------------------------------------------------- 
 295 //----------------------------------------------------------------------------- 
 297 IMPLEMENT_DYNAMIC_CLASS(wxFileSystem
, wxObject
) 
 298 IMPLEMENT_ABSTRACT_CLASS(wxFSFile
, wxObject
) 
 301 wxList 
wxFileSystem::m_Handlers
; 
 304 wxFileSystem::~wxFileSystem() 
 306     WX_CLEAR_HASH_MAP(wxFSHandlerHash
, m_LocalHandlers
) 
 310 static wxString 
MakeCorrectPath(const wxString
& path
) 
 317     for (i 
= 0; i 
< cnt
; i
++) 
 318       if (p
.GetChar(i
) == wxT('\\')) p
.GetWritableChar(i
) = wxT('/'); // Want to be windows-safe 
 320     if (p
.Left(2) == wxT("./")) { p 
= p
.Mid(2); cnt 
-= 2; } 
 322     if (cnt 
< 3) return p
; 
 324     r 
<< p
.GetChar(0) << p
.GetChar(1); 
 326     // skip trailing ../.., if any 
 327     for (i 
= 2; i 
< cnt 
&& (p
.GetChar(i
) == wxT('/') || p
.GetChar(i
) == wxT('.')); i
++) r 
<< p
.GetChar(i
); 
 329     // remove back references: translate dir1/../dir2 to dir2 
 333         if (p
.GetChar(i
) == wxT('/') && p
.GetChar(i
-1) == wxT('.') && p
.GetChar(i
-2) == wxT('.')) 
 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(':')) 
 338                 for (j 
= j 
- 1; j 
>= 0 && r
.GetChar(j
) != wxT('/') && r
.GetChar(j
) != wxT(':'); j
--) {} 
 344     for (; i 
< cnt
; i
++) r 
<< p
.GetChar(i
); 
 350 void wxFileSystem::ChangePathTo(const wxString
& location
, bool is_dir
) 
 354     m_Path 
= MakeCorrectPath(location
); 
 358         if (!m_Path
.empty() && m_Path
.Last() != wxT('/') && m_Path
.Last() != wxT(':')) 
 364         for (i 
= m_Path
.length()-1; i 
>= 0; i
--) 
 366             if (m_Path
[(unsigned int) i
] == wxT('/')) 
 368                 if ((i 
> 1) && (m_Path
[(unsigned int) (i
-1)] == wxT('/')) && (m_Path
[(unsigned int) (i
-2)] == wxT(':'))) 
 379             else if (m_Path
[(unsigned int) i
] == wxT(':')) { 
 386             for (i 
= 0; i 
< (int) m_Path
.length(); i
++) 
 388                 if (m_Path
[(unsigned int) i
] == wxT(':')) 
 394             if (i 
== (int) m_Path
.length()) 
 395                 m_Path 
= wxEmptyString
; 
 399             m_Path
.Remove(pathpos
+1); 
 406 wxFileSystemHandler 
*wxFileSystem::MakeLocal(wxFileSystemHandler 
*h
) 
 408     wxClassInfo 
*classinfo 
= h
->GetClassInfo(); 
 410     if (classinfo
->IsDynamic()) 
 412         wxFileSystemHandler
*& local 
= m_LocalHandlers
[classinfo
]; 
 414             local 
= (wxFileSystemHandler
*)classinfo
->CreateObject(); 
 425 wxFSFile
* wxFileSystem::OpenFile(const wxString
& location
, int flags
) 
 427     if ((flags 
& wxFS_READ
) == 0) 
 430     wxString loc 
= MakeCorrectPath(location
); 
 434     wxList::compatibility_iterator node
; 
 438     for (i 
= 0; i 
< ln
; i
++) 
 440         switch ( loc
[i
].GetValue() ) 
 442             case wxT('/') : case wxT(':') : case wxT('#') : 
 446         if (meta 
!= 0) break; 
 448     m_LastName 
= wxEmptyString
; 
 450     // try relative paths first : 
 451     if (meta 
!= wxT(':')) 
 453         node 
= m_Handlers
.GetFirst(); 
 456             wxFileSystemHandler 
*h 
= (wxFileSystemHandler
*) node 
-> GetData(); 
 457             if (h
->CanOpen(m_Path 
+ loc
)) 
 459                 s 
= MakeLocal(h
)->OpenFile(*this, m_Path 
+ loc
); 
 460                 if (s
) { m_LastName 
= m_Path 
+ loc
; break; } 
 462             node 
= node
->GetNext(); 
 466     // if failed, try absolute paths : 
 469         node 
= m_Handlers
.GetFirst(); 
 472             wxFileSystemHandler 
*h 
= (wxFileSystemHandler
*) node
->GetData(); 
 475                 s 
= MakeLocal(h
)->OpenFile(*this, loc
); 
 476                 if (s
) { m_LastName 
= loc
; break; } 
 478             node 
= node
->GetNext(); 
 482     if (s 
&& (flags 
& wxFS_SEEKABLE
) != 0 && !s
->GetStream()->IsSeekable()) 
 484         wxBackedInputStream 
*stream
; 
 485         stream 
= new wxBackedInputStream(s
->DetachStream()); 
 486         stream
->FindLength(); 
 487         s
->SetStream(stream
); 
 495 wxString 
wxFileSystem::FindFirst(const wxString
& spec
, int flags
) 
 497     wxList::compatibility_iterator node
; 
 498     wxString 
spec2(spec
); 
 500     m_FindFileHandler 
= NULL
; 
 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 
 505     node 
= m_Handlers
.GetFirst(); 
 508         wxFileSystemHandler 
*h 
= (wxFileSystemHandler
*) node 
-> GetData(); 
 509         if (h 
-> CanOpen(m_Path 
+ spec2
)) 
 511             m_FindFileHandler 
= MakeLocal(h
); 
 512             return m_FindFileHandler 
-> FindFirst(m_Path 
+ spec2
, flags
); 
 514         node 
= node
->GetNext(); 
 517     node 
= m_Handlers
.GetFirst(); 
 520         wxFileSystemHandler 
*h 
= (wxFileSystemHandler
*) node 
-> GetData(); 
 521         if (h 
-> CanOpen(spec2
)) 
 523             m_FindFileHandler 
= MakeLocal(h
); 
 524             return m_FindFileHandler 
-> FindFirst(spec2
, flags
); 
 526         node 
= node
->GetNext(); 
 529     return wxEmptyString
; 
 534 wxString 
wxFileSystem::FindNext() 
 536     if (m_FindFileHandler 
== NULL
) return wxEmptyString
; 
 537     else return m_FindFileHandler 
-> FindNext(); 
 540 bool wxFileSystem::FindFileInPath(wxString 
*pStr
, 
 541                                   const wxString
& path
, 
 542                                   const wxString
& basename
) 
 544     // we assume that it's not empty 
 545     wxCHECK_MSG( !basename
.empty(), false, 
 546                 wxT("empty file name in wxFileSystem::FindFileInPath")); 
 549     // skip path separator in the beginning of the file name if present 
 550     if ( wxIsPathSeparator(basename
[0u]) ) 
 551         name 
= basename
.substr(1); 
 555     wxStringTokenizer 
tokenizer(path
, wxPATH_SEP
); 
 556     while ( tokenizer
.HasMoreTokens() ) 
 558         wxString strFile 
= tokenizer
.GetNextToken(); 
 559         if ( !wxEndsWithPathSeparator(strFile
) ) 
 560             strFile 
+= wxFILE_SEP_PATH
; 
 563         wxFSFile 
*file 
= OpenFile(strFile
); 
 575 void wxFileSystem::AddHandler(wxFileSystemHandler 
*handler
) 
 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
); 
 582 wxFileSystemHandler
* wxFileSystem::RemoveHandler(wxFileSystemHandler 
*handler
) 
 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
)) 
 596 bool wxFileSystem::HasHandlerForPath(const wxString 
&location
) 
 598     for ( wxList::compatibility_iterator node 
= m_Handlers
.GetFirst(); 
 599            node
; node 
= node
->GetNext() ) 
 601         wxFileSystemHandler 
*h 
= (wxFileSystemHandler
*) node
->GetData(); 
 602         if (h
->CanOpen(location
)) 
 609 void wxFileSystem::CleanUpHandlers() 
 611     WX_CLEAR_LIST(wxList
, m_Handlers
); 
 614 static const wxString 
g_unixPathString(wxT("/")); 
 615 static const wxString 
g_nativePathString(wxFILE_SEP_PATH
); 
 617 // Returns the native path for a file URL 
 618 wxFileName 
wxFileSystem::URLToFileName(const wxString
& url
) 
 622     if ( path
.Find(wxT("file://")) == 0 ) 
 626     else if ( path
.Find(wxT("file:")) == 0 ) 
 630     // Remove preceding double slash on Mac Classic 
 631 #if defined(__WXMAC__) && !defined(__UNIX__) 
 632     else if ( path
.Find(wxT("//")) == 0 ) 
 636     path 
= wxURI::Unescape(path
); 
 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
.length() > 1 && (path
[0u] == wxT('/') && path 
[1u] != wxT('/')) ) 
 646     else if ( (url
.Find(wxT("file://")) == 0) && 
 647               (path
.Find(wxT('/')) != wxNOT_FOUND
) && 
 648               (path
.length() > 1) && (path
[1u] != wxT(':')) ) 
 650         path 
= wxT("//") + path
; 
 654     path
.Replace(g_unixPathString
, g_nativePathString
); 
 656     return wxFileName(path
, wxPATH_NATIVE
); 
 659 // Escapes non-ASCII and others characters in file: URL to be valid URLs 
 660 static wxString 
EscapeFileNameCharsInURL(const char *in
) 
 664     for ( const unsigned char *p 
= (const unsigned char*)in
; *p
; ++p 
) 
 666         const unsigned char c 
= *p
; 
 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() 
 679         if ( c 
== '/' || c 
== '-' || c 
== '.' || c 
== '_' || c 
== '~' || 
 680              (c 
>= '0' && c 
<= '9') || 
 681              (c 
>= 'a' && c 
<= 'z') || 
 682              (c 
>= 'A' && c 
<= 'Z') ) 
 688             s 
<< wxString::Format("%%%02x", c
); 
 695 // Returns the file URL for a native path 
 696 wxString 
wxFileSystem::FileNameToURL(const wxFileName
& filename
) 
 698     wxFileName fn 
= filename
; 
 699     fn
.Normalize(wxPATH_NORM_DOTS 
| wxPATH_NORM_TILDE 
| wxPATH_NORM_ABSOLUTE
); 
 700     wxString url 
= fn
.GetFullPath(wxPATH_NATIVE
); 
 703     // unc notation, wxMSW 
 704     if ( url
.Find(wxT("\\\\")) == 0 ) 
 706         url 
= wxT("//") + url
.Mid(2); 
 710         url 
= wxT("/") + url
; 
 712         url 
= wxT("/") + url
; 
 718     url
.Replace(g_nativePathString
, g_unixPathString
); 
 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()); 
 728 class wxFileSystemModule 
: public wxModule
 
 730     DECLARE_DYNAMIC_CLASS(wxFileSystemModule
) 
 733         wxFileSystemModule() : 
 739         virtual bool OnInit() 
 741             m_handler 
= new wxLocalFSHandler
; 
 742             wxFileSystem::AddHandler(m_handler
); 
 745         virtual void OnExit() 
 747             delete wxFileSystem::RemoveHandler(m_handler
); 
 749             wxFileSystem::CleanUpHandlers(); 
 753         wxFileSystemHandler
* m_handler
; 
 757 IMPLEMENT_DYNAMIC_CLASS(wxFileSystemModule
, wxModule
) 
 761 wxFSInputStream::wxFSInputStream(const wxString
& filename
, int flags
) 
 764     m_file 
= fs
.OpenFile(filename
, flags 
| wxFS_READ
); 
 768         wxInputStream
* const stream 
= m_file
->GetStream(); 
 771             // Notice that we pass the stream by reference: it shouldn't be 
 772             // deleted by us as it's owned by m_file already. 
 773             InitParentStream(*stream
); 
 778 wxFSInputStream::~wxFSInputStream()