diff --git a/include/wx/filename.h b/include/wx/filename.h index e2b05ec13e..203c98431b 100644 --- a/include/wx/filename.h +++ b/include/wx/filename.h @@ -258,6 +258,11 @@ public: // values bool SetPermissions(int permissions); + // Returns the native path for a file URL + static wxFileName URLToFileName(const wxString& url); + + // Returns the file URL for a native path + static wxString FileNameToURL(const wxFileName& filename); // time functions #if wxUSE_DATETIME diff --git a/interface/wx/filename.h b/interface/wx/filename.h index 330a172b66..4931050faf 100644 --- a/interface/wx/filename.h +++ b/interface/wx/filename.h @@ -1246,6 +1246,25 @@ public: */ bool SetPermissions(int permissions); + /** + Converts URL into a well-formed filename. + The URL must use the @c file protocol. + If the URL does not use @c file protocol + wxFileName object may not be good or may not exist + + @since 3.1.3 + */ + static wxFileName URLToFileName(const wxString& url); + + /** + Converts wxFileName into an URL. + + @see URLToFileName(), wxFileName + + @since 3.1.3 + */ + static wxString FileNameToURL(const wxFileName& filename); + /** Sets the file creation and last access/modification times (any of the pointers may be @NULL). diff --git a/src/common/filename.cpp b/src/common/filename.cpp index fa5a24fe43..f016b104a4 100644 --- a/src/common/filename.cpp +++ b/src/common/filename.cpp @@ -93,6 +93,7 @@ #include "wx/dynlib.h" #include "wx/dir.h" #include "wx/longlong.h" +#include "wx/uri.h" #if defined(__WIN32__) && defined(__MINGW32__) #include "wx/msw/gccpriv.h" @@ -2501,6 +2502,103 @@ bool wxFileName::SetPermissions(int permissions) return wxChmod(GetFullPath(), permissions) == 0; } +static const wxString g_unixPathString(wxT("/")); +static const wxString g_nativePathString(wxFILE_SEP_PATH); + +// Returns the native path for a file URL +wxFileName wxFileName::URLToFileName(const wxString& url) +{ + wxString path = url; + + if ( path.Find(wxT("file://")) == 0 ) + { + path = path.Mid(7); + } + else if ( path.Find(wxT("file:")) == 0 ) + { + path = path.Mid(5); + } + // Remove preceding double slash on Mac Classic +#if defined(__WXMAC__) && !defined(__UNIX__) + else if ( path.Find(wxT("//")) == 0 ) + path = path.Mid(2); +#endif + + path = wxURI::Unescape(path); + +#ifdef __WINDOWS__ + // file urls either start with a forward slash (local harddisk), + // otherwise they have a servername/sharename notation, + // which only exists on msw and corresponds to a unc + if ( path.length() > 1 && (path[0u] == wxT('/') && path [1u] != wxT('/')) ) + { + path = path.Mid(1); + } + else if ( (url.Find(wxT("file://")) == 0) && + (path.Find(wxT('/')) != wxNOT_FOUND) && + (path.length() > 1) && (path[1u] != wxT(':')) ) + { + path = wxT("//") + path; + } +#endif + + path.Replace(g_unixPathString, g_nativePathString); + + return wxFileName(path, wxPATH_NATIVE); +} + +// Escapes non-ASCII and others characters in file: URL to be valid URLs +static wxString EscapeFileNameCharsInURL(const char *in) +{ + wxString s; + + for ( const unsigned char *p = (const unsigned char*)in; *p; ++p ) + { + const unsigned char c = *p; + + // https://tools.ietf.org/html/rfc1738#section-5 + if ( (c >= '0' && c <= '9') || + (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + strchr("/:$-_.+!*'(),", c) ) // Plus '/' and ':' + { + s << c; + } + else + { + s << wxString::Format("%%%02x", c); + } + } + + return s; +} + +// Returns the file URL for a native path +wxString wxFileName::FileNameToURL(const wxFileName& filename) +{ + wxFileName fn = filename; + fn.Normalize(wxPATH_NORM_DOTS | wxPATH_NORM_TILDE | wxPATH_NORM_ABSOLUTE); + wxString url = fn.GetFullPath(wxPATH_NATIVE); + +#ifndef __UNIX__ + // unc notation, wxMSW + if ( url.Find(wxT("\\\\")) == 0 ) + { + url = url.Mid(2); + } + else + { + url = wxT("/") + url; + } +#endif + + url.Replace(g_nativePathString, g_unixPathString); + + // Do wxURI- and common practice-compatible escaping: encode the string + // into UTF-8, then escape anything non-ASCII: + return wxT("file://") + EscapeFileNameCharsInURL(url.utf8_str()); +} + // ---------------------------------------------------------------------------- // time functions // ---------------------------------------------------------------------------- diff --git a/src/common/filesys.cpp b/src/common/filesys.cpp index 8406b93898..83961e3ca1 100644 --- a/src/common/filesys.cpp +++ b/src/common/filesys.cpp @@ -275,7 +275,7 @@ wxFSFile* wxLocalFSHandler::OpenFile(wxFileSystem& WXUNUSED(fs), const wxString& { // location has Unix path separators wxString right = GetRightLocation(location); - wxFileName fn = wxFileSystem::URLToFileName(right); + wxFileName fn = wxFileName::URLToFileName(right); wxString fullpath = ms_root + fn.GetFullPath(); if (!wxFileExists(fullpath)) @@ -308,7 +308,7 @@ wxFSFile* wxLocalFSHandler::OpenFile(wxFileSystem& WXUNUSED(fs), const wxString& wxString wxLocalFSHandler::FindFirst(const wxString& spec, int flags) { - wxFileName fn = wxFileSystem::URLToFileName(GetRightLocation(spec)); + wxFileName fn = wxFileName::URLToFileName(GetRightLocation(spec)); const wxString found = wxFindFirstFile(ms_root + fn.GetFullPath(), flags); if ( found.empty() ) return found; @@ -645,101 +645,16 @@ void wxFileSystem::CleanUpHandlers() WX_CLEAR_LIST(wxList, m_Handlers); } -static const wxString g_unixPathString(wxT("/")); -static const wxString g_nativePathString(wxFILE_SEP_PATH); - // Returns the native path for a file URL wxFileName wxFileSystem::URLToFileName(const wxString& url) { - wxString path = url; - - if ( path.Find(wxT("file://")) == 0 ) - { - path = path.Mid(7); - } - else if ( path.Find(wxT("file:")) == 0 ) - { - path = path.Mid(5); - } - // Remove preceding double slash on Mac Classic -#if defined(__WXMAC__) && !defined(__UNIX__) - else if ( path.Find(wxT("//")) == 0 ) - path = path.Mid(2); -#endif - - path = wxURI::Unescape(path); - -#ifdef __WINDOWS__ - // file urls either start with a forward slash (local harddisk), - // otherwise they have a servername/sharename notation, - // which only exists on msw and corresponds to a unc - if ( path.length() > 1 && (path[0u] == wxT('/') && path [1u] != wxT('/')) ) - { - path = path.Mid(1); - } - else if ( (url.Find(wxT("file://")) == 0) && - (path.Find(wxT('/')) != wxNOT_FOUND) && - (path.length() > 1) && (path[1u] != wxT(':')) ) - { - path = wxT("//") + path; - } -#endif - - path.Replace(g_unixPathString, g_nativePathString); - - return wxFileName(path, wxPATH_NATIVE); -} - -// Escapes non-ASCII and others characters in file: URL to be valid URLs -static wxString EscapeFileNameCharsInURL(const char *in) -{ - wxString s; - - for ( const unsigned char *p = (const unsigned char*)in; *p; ++p ) - { - const unsigned char c = *p; - - // https://tools.ietf.org/html/rfc1738#section-5 - if ( (c >= '0' && c <= '9') || - (c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || - strchr("/:$-_.+!*'(),", c) ) // Plus '/' and ':' - { - s << c; - } - else - { - s << wxString::Format("%%%02x", c); - } - } - - return s; + return wxFileName::URLToFileName( url ); } // Returns the file URL for a native path wxString wxFileSystem::FileNameToURL(const wxFileName& filename) { - wxFileName fn = filename; - fn.Normalize(wxPATH_NORM_DOTS | wxPATH_NORM_TILDE | wxPATH_NORM_ABSOLUTE); - wxString url = fn.GetFullPath(wxPATH_NATIVE); - -#ifndef __UNIX__ - // unc notation, wxMSW - if ( url.Find(wxT("\\\\")) == 0 ) - { - url = url.Mid(2); - } - else - { - url = wxT("/") + url; - } -#endif - - url.Replace(g_nativePathString, g_unixPathString); - - // Do wxURI- and common practice-compatible escaping: encode the string - // into UTF-8, then escape anything non-ASCII: - return wxT("file://") + EscapeFileNameCharsInURL(url.utf8_str()); + return wxFileName::FileNameToURL( filename ); } diff --git a/src/common/utilscmn.cpp b/src/common/utilscmn.cpp index cc35d05bd2..25b00db24c 100644 --- a/src/common/utilscmn.cpp +++ b/src/common/utilscmn.cpp @@ -1132,12 +1132,8 @@ static bool DoLaunchDefaultBrowserHelper(const wxString& url, int flags) if ( params.scheme == "file" ) { - // TODO: extract URLToFileName() to some always compiled in - // function -#if wxUSE_FILESYSTEM // for same reason as above, remove the scheme from the URL - params.path = wxFileSystem::URLToFileName(url).GetFullPath(); -#endif // wxUSE_FILESYSTEM + params.path = wxFileName::URLToFileName(url).GetFullPath(); } } diff --git a/src/msw/webview_ie.cpp b/src/msw/webview_ie.cpp index c5a4e7f34d..6be4dc87cf 100644 --- a/src/msw/webview_ie.cpp +++ b/src/msw/webview_ie.cpp @@ -1360,7 +1360,7 @@ void wxWebViewIE::onActiveXEvent(wxActiveXEvent& evt) if(m_historyEnabled && !m_historyLoadingFromList && (url == GetCurrentURL() || (GetCurrentURL().substr(0, 4) == "file" && - wxFileSystem::URLToFileName(GetCurrentURL()).GetFullPath() == url))) + wxFileName::URLToFileName(GetCurrentURL()).GetFullPath() == url))) { //If we are not at the end of the list, then erase everything //between us and the end before adding the new page diff --git a/tests/file/filefn.cpp b/tests/file/filefn.cpp index 5d9e6a435e..0acd5b7976 100644 --- a/tests/file/filefn.cpp +++ b/tests/file/filefn.cpp @@ -246,7 +246,7 @@ void FileFunctionsTestCase::DoFindFile(const wxString& filePath) // Check if file can be found (method 2). wxFileSystem fs; wxString furl = fs.FindFirst(filePath, wxFILE); - wxFileName fname = wxFileSystem::URLToFileName(furl); + wxFileName fname = wxFileName::URLToFileName(furl); foundFile = fname.GetFullPath(); CPPUNIT_ASSERT_MESSAGE( msg, foundFile == filePath ); @@ -271,10 +271,10 @@ void FileFunctionsTestCase::FindFileNext() // Check using method 2. wxFileSystem fs; wxString furl = fs.FindFirst(fileMask, wxFILE); - fn1 = wxFileSystem::URLToFileName(furl); + fn1 = wxFileName::URLToFileName(furl); foundFile1 = fn1.GetFullPath(); furl = fs.FindNext(); - fn2 = wxFileSystem::URLToFileName(furl); + fn2 = wxFileName::URLToFileName(furl); foundFile2 = fn2.GetFullPath(); // Full names must be different. CPPUNIT_ASSERT( fn1.GetFullPath() != fn2.GetFullPath() ); diff --git a/tests/filesys/filesystest.cpp b/tests/filesys/filesystest.cpp index 6eea598ede..4f58313de4 100644 --- a/tests/filesys/filesystest.cpp +++ b/tests/filesys/filesystest.cpp @@ -180,7 +180,7 @@ void FileSystemTestCase::FileNameToUrlConversion() wxString url1 = wxFileSystem::FileNameToURL(fn1); CPPUNIT_ASSERT_EQUAL( d.expected, url1 ); - CPPUNIT_ASSERT( fn1.SameAs(wxFileSystem::URLToFileName(url1)) ); + CPPUNIT_ASSERT( fn1.SameAs(wxFileName::URLToFileName(url1)) ); } } @@ -196,7 +196,7 @@ void FileSystemTestCase::UnicodeFileNameToUrlConversion() wxString url = wxFileSystem::FileNameToURL(filename); - CPPUNIT_ASSERT( filename.SameAs(wxFileSystem::URLToFileName(url)) ); + CPPUNIT_ASSERT( filename.SameAs(wxFileName::URLToFileName(url)) ); } #endif // wxUSE_FILESYSTEM