diff --git a/Encoding.hxx.in b/Encoding.hxx.in index bf93f50141cb9d2af036c96762b503e93f82e817..09691fd3f4bae3e900bc54ea98655a78f03f4338 100644 --- a/Encoding.hxx.in +++ b/Encoding.hxx.in @@ -59,6 +59,17 @@ public: static std::string ToNarrow(const std::wstring& str); static std::string ToNarrow(const wchar_t* str); +#if defined(_WIN32) + /** + * Convert the path to an extended length path to avoid MAX_PATH length + * limitations on Windows. If the input is a local path the result will be + * prefixed with \\?\; if the input is instead a network path, the result + * will be prefixed with \\?\UNC\. All output will also be converted to + * absolute paths with Windows-style backslashes. + **/ + static std::wstring ToWindowsExtendedPath(std::string const&); +#endif + #endif // @KWSYS_NAMESPACE@_STL_HAS_WSTRING }; // class Encoding diff --git a/EncodingCXX.cxx b/EncodingCXX.cxx index e904c1af17c3dc64fa92450f639c7732175c2a11..641c0e6a8e6ad1c5b027431555ec2eab78b4256a 100644 --- a/EncodingCXX.cxx +++ b/EncodingCXX.cxx @@ -29,6 +29,7 @@ #if defined(_WIN32) #include <windows.h> +#include <ctype.h> #include <shellapi.h> #endif @@ -214,6 +215,63 @@ std::string Encoding::ToNarrow(const wchar_t* wcstr) } return str; } + +#if defined(_WIN32) +// Convert local paths to UNC style paths +std::wstring Encoding::ToWindowsExtendedPath(std::string const& source) +{ + std::wstring wsource = Encoding::ToWide(source); + + // Resolve any relative paths + DWORD wfull_len; + + /* The +3 is a workaround for a bug in some versions of GetFullPathNameW that + * won't return a large enough buffer size if the input is too small */ + wfull_len = GetFullPathNameW(wsource.c_str(), 0, NULL, NULL) + 3; + std::vector<wchar_t> wfull(wfull_len); + GetFullPathNameW(wsource.c_str(), wfull_len, &wfull[0], NULL); + + /* This should get the correct size without any extra padding from the + * previous size workaround. */ + wfull_len = static_cast<DWORD>(wcslen(&wfull[0])); + + if (wfull_len >= 2 && isalpha(wfull[0]) && + wfull[1] == L':') { /* C:\Foo\bar\FooBar.txt */ + return L"\\\\?\\" + std::wstring(&wfull[0]); + } else if (wfull_len >= 2 && wfull[0] == L'\\' && + wfull[1] == L'\\') { /* Starts with \\ */ + if (wfull_len >= 4 && wfull[2] == L'?' && + wfull[3] == L'\\') { /* Starts with \\?\ */ + if (wfull_len >= 8 && wfull[4] == L'U' && wfull[5] == L'N' && + wfull[6] == L'C' && + wfull[7] == L'\\') { /* \\?\UNC\Foo\bar\FooBar.txt */ + return std::wstring(&wfull[0]); + } else if (wfull_len >= 6 && isalpha(wfull[4]) && + wfull[5] == L':') { /* \\?\C:\Foo\bar\FooBar.txt */ + return std::wstring(&wfull[0]); + } else if (wfull_len >= 5) { /* \\?\Foo\bar\FooBar.txt */ + return L"\\\\?\\UNC\\" + std::wstring(&wfull[4]); + } + } else if (wfull_len >= 4 && wfull[2] == L'.' && + wfull[3] == L'\\') { /* Starts with \\.\ a device name */ + if (wfull_len >= 6 && isalpha(wfull[4]) && + wfull[5] == L':') { /* \\.\C:\Foo\bar\FooBar.txt */ + return L"\\\\?\\" + std::wstring(&wfull[4]); + } else if (wfull_len >= + 5) { /* \\.\Foo\bar\ Device name is left unchanged */ + return std::wstring(&wfull[0]); + } + } else if (wfull_len >= 3) { /* \\Foo\bar\FooBar.txt */ + return L"\\\\?\\UNC\\" + std::wstring(&wfull[2]); + } + } + + // If this case has been reached, then the path is invalid. Leave it + // unchanged + return Encoding::ToWide(source); +} +#endif + #endif // KWSYS_STL_HAS_WSTRING } // namespace KWSYS_NAMESPACE diff --git a/SystemTools.cxx b/SystemTools.cxx index 100a49ce89257dd476baac570cbd8e45aebe0c21..7ba75881932cdefd5e25e09759a91428d683c948 100644 --- a/SystemTools.cxx +++ b/SystemTools.cxx @@ -1960,59 +1960,10 @@ void SystemTools::ConvertToUnixSlashes(std::string& path) } #ifdef _WIN32 -// Convert local paths to UNC style paths std::wstring SystemTools::ConvertToWindowsExtendedPath( const std::string& source) { - std::wstring wsource = Encoding::ToWide(source); - - // Resolve any relative paths - DWORD wfull_len; - - /* The +3 is a workaround for a bug in some versions of GetFullPathNameW that - * won't return a large enough buffer size if the input is too small */ - wfull_len = GetFullPathNameW(wsource.c_str(), 0, NULL, NULL) + 3; - std::vector<wchar_t> wfull(wfull_len); - GetFullPathNameW(wsource.c_str(), wfull_len, &wfull[0], NULL); - - /* This should get the correct size without any extra padding from the - * previous size workaround. */ - wfull_len = static_cast<DWORD>(wcslen(&wfull[0])); - - if (wfull_len >= 2 && isalpha(wfull[0]) && - wfull[1] == L':') { /* C:\Foo\bar\FooBar.txt */ - return L"\\\\?\\" + std::wstring(&wfull[0]); - } else if (wfull_len >= 2 && wfull[0] == L'\\' && - wfull[1] == L'\\') { /* Starts with \\ */ - if (wfull_len >= 4 && wfull[2] == L'?' && - wfull[3] == L'\\') { /* Starts with \\?\ */ - if (wfull_len >= 8 && wfull[4] == L'U' && wfull[5] == L'N' && - wfull[6] == L'C' && - wfull[7] == L'\\') { /* \\?\UNC\Foo\bar\FooBar.txt */ - return std::wstring(&wfull[0]); - } else if (wfull_len >= 6 && isalpha(wfull[4]) && - wfull[5] == L':') { /* \\?\C:\Foo\bar\FooBar.txt */ - return std::wstring(&wfull[0]); - } else if (wfull_len >= 5) { /* \\?\Foo\bar\FooBar.txt */ - return L"\\\\?\\UNC\\" + std::wstring(&wfull[4]); - } - } else if (wfull_len >= 4 && wfull[2] == L'.' && - wfull[3] == L'\\') { /* Starts with \\.\ a device name */ - if (wfull_len >= 6 && isalpha(wfull[4]) && - wfull[5] == L':') { /* \\.\C:\Foo\bar\FooBar.txt */ - return L"\\\\?\\" + std::wstring(&wfull[4]); - } else if (wfull_len >= - 5) { /* \\.\Foo\bar\ Device name is left unchanged */ - return std::wstring(&wfull[0]); - } - } else if (wfull_len >= 3) { /* \\Foo\bar\FooBar.txt */ - return L"\\\\?\\UNC\\" + std::wstring(&wfull[2]); - } - } - - // If this case has been reached, then the path is invalid. Leave it - // unchanged - return Encoding::ToWide(source); + return Encoding::ToWindowsExtendedPath(source); } #endif diff --git a/SystemTools.hxx.in b/SystemTools.hxx.in index 53abce7a0d12b47270f732ac396bbb803eae533f..0849e1da8feffc93045aa10497438ab00d819748 100644 --- a/SystemTools.hxx.in +++ b/SystemTools.hxx.in @@ -265,13 +265,7 @@ public: static void ConvertToUnixSlashes(std::string& path); #ifdef _WIN32 - /** - * Convert the path to an extended length path to avoid MAX_PATH length - * limitations on Windows. If the input is a local path the result will be - * prefixed with \\?\; if the input is instead a network path, the result - * will be prefixed with \\?\UNC\. All output will also be converted to - * absolute paths with Windows-style backslashes. - **/ + /** Calls Encoding::ToWindowsExtendedPath. */ static std::wstring ConvertToWindowsExtendedPath(const std::string&); #endif diff --git a/testEncoding.cxx b/testEncoding.cxx index 03f2ec9e33dc8f72cc6f1d342a9711f21383b070..457e8a896ec3b3ffb32ec77fad58bd37ab80f820 100644 --- a/testEncoding.cxx +++ b/testEncoding.cxx @@ -180,6 +180,88 @@ static int testCommandLineArguments() return status; } +static int testToWindowsExtendedPath() +{ +#ifdef _WIN32 + int ret = 0; + if (kwsys::Encoding::ToWindowsExtendedPath( + "L:\\Local Mojo\\Hex Power Pack\\Iffy Voodoo") != + L"\\\\?\\L:\\Local Mojo\\Hex Power Pack\\Iffy Voodoo") { + std::cout << "Problem with ToWindowsExtendedPath " + << "\"L:\\Local Mojo\\Hex Power Pack\\Iffy Voodoo\"" + << std::endl; + ++ret; + } + + if (kwsys::Encoding::ToWindowsExtendedPath( + "L:/Local Mojo/Hex Power Pack/Iffy Voodoo") != + L"\\\\?\\L:\\Local Mojo\\Hex Power Pack\\Iffy Voodoo") { + std::cout << "Problem with ToWindowsExtendedPath " + << "\"L:/Local Mojo/Hex Power Pack/Iffy Voodoo\"" << std::endl; + ++ret; + } + + if (kwsys::Encoding::ToWindowsExtendedPath( + "\\\\Foo\\Local Mojo\\Hex Power Pack\\Iffy Voodoo") != + L"\\\\?\\UNC\\Foo\\Local Mojo\\Hex Power Pack\\Iffy Voodoo") { + std::cout << "Problem with ToWindowsExtendedPath " + << "\"\\\\Foo\\Local Mojo\\Hex Power Pack\\Iffy Voodoo\"" + << std::endl; + ++ret; + } + + if (kwsys::Encoding::ToWindowsExtendedPath( + "//Foo/Local Mojo/Hex Power Pack/Iffy Voodoo") != + L"\\\\?\\UNC\\Foo\\Local Mojo\\Hex Power Pack\\Iffy Voodoo") { + std::cout << "Problem with ToWindowsExtendedPath " + << "\"//Foo/Local Mojo/Hex Power Pack/Iffy Voodoo\"" + << std::endl; + ++ret; + } + + if (kwsys::Encoding::ToWindowsExtendedPath("//") != L"//") { + std::cout << "Problem with ToWindowsExtendedPath " + << "\"//\"" << std::endl; + ++ret; + } + + if (kwsys::Encoding::ToWindowsExtendedPath("\\\\.\\") != L"\\\\.\\") { + std::cout << "Problem with ToWindowsExtendedPath " + << "\"\\\\.\\\"" << std::endl; + ++ret; + } + + if (kwsys::Encoding::ToWindowsExtendedPath("\\\\.\\X") != L"\\\\.\\X") { + std::cout << "Problem with ToWindowsExtendedPath " + << "\"\\\\.\\X\"" << std::endl; + ++ret; + } + + if (kwsys::Encoding::ToWindowsExtendedPath("\\\\.\\X:") != L"\\\\?\\X:") { + std::cout << "Problem with ToWindowsExtendedPath " + << "\"\\\\.\\X:\"" << std::endl; + ++ret; + } + + if (kwsys::Encoding::ToWindowsExtendedPath("\\\\.\\X:\\") != + L"\\\\?\\X:\\") { + std::cout << "Problem with ToWindowsExtendedPath " + << "\"\\\\.\\X:\\\"" << std::endl; + ++ret; + } + + if (kwsys::Encoding::ToWindowsExtendedPath("NUL") != L"\\\\.\\NUL") { + std::cout << "Problem with ToWindowsExtendedPath " + << "\"NUL\"" << std::endl; + ++ret; + } + + return ret; +#else + return 0; +#endif +} + //---------------------------------------------------------------------------- int testEncoding(int, char* []) { @@ -196,6 +278,7 @@ int testEncoding(int, char* []) ret |= testRobustEncoding(); ret |= testCommandLineArguments(); ret |= testWithNulls(); + ret |= testToWindowsExtendedPath(); return ret; } diff --git a/testSystemTools.cxx b/testSystemTools.cxx index 9b08a04a034511883e59104ec8751865ea67c126..d11bcaef87563d92f8b4f98ecdfa7249d0eaff49 100644 --- a/testSystemTools.cxx +++ b/testSystemTools.cxx @@ -585,85 +585,6 @@ static bool CheckStringOperations() res = false; } -#ifdef _WIN32 - if (kwsys::SystemTools::ConvertToWindowsExtendedPath( - "L:\\Local Mojo\\Hex Power Pack\\Iffy Voodoo") != - L"\\\\?\\L:\\Local Mojo\\Hex Power Pack\\Iffy Voodoo") { - std::cerr << "Problem with ConvertToWindowsExtendedPath " - << "\"L:\\Local Mojo\\Hex Power Pack\\Iffy Voodoo\"" - << std::endl; - res = false; - } - - if (kwsys::SystemTools::ConvertToWindowsExtendedPath( - "L:/Local Mojo/Hex Power Pack/Iffy Voodoo") != - L"\\\\?\\L:\\Local Mojo\\Hex Power Pack\\Iffy Voodoo") { - std::cerr << "Problem with ConvertToWindowsExtendedPath " - << "\"L:/Local Mojo/Hex Power Pack/Iffy Voodoo\"" << std::endl; - res = false; - } - - if (kwsys::SystemTools::ConvertToWindowsExtendedPath( - "\\\\Foo\\Local Mojo\\Hex Power Pack\\Iffy Voodoo") != - L"\\\\?\\UNC\\Foo\\Local Mojo\\Hex Power Pack\\Iffy Voodoo") { - std::cerr << "Problem with ConvertToWindowsExtendedPath " - << "\"\\\\Foo\\Local Mojo\\Hex Power Pack\\Iffy Voodoo\"" - << std::endl; - res = false; - } - - if (kwsys::SystemTools::ConvertToWindowsExtendedPath( - "//Foo/Local Mojo/Hex Power Pack/Iffy Voodoo") != - L"\\\\?\\UNC\\Foo\\Local Mojo\\Hex Power Pack\\Iffy Voodoo") { - std::cerr << "Problem with ConvertToWindowsExtendedPath " - << "\"//Foo/Local Mojo/Hex Power Pack/Iffy Voodoo\"" - << std::endl; - res = false; - } - - if (kwsys::SystemTools::ConvertToWindowsExtendedPath("//") != L"//") { - std::cerr << "Problem with ConvertToWindowsExtendedPath " - << "\"//\"" << std::endl; - res = false; - } - - if (kwsys::SystemTools::ConvertToWindowsExtendedPath("\\\\.\\") != - L"\\\\.\\") { - std::cerr << "Problem with ConvertToWindowsExtendedPath " - << "\"\\\\.\\\"" << std::endl; - res = false; - } - - if (kwsys::SystemTools::ConvertToWindowsExtendedPath("\\\\.\\X") != - L"\\\\.\\X") { - std::cerr << "Problem with ConvertToWindowsExtendedPath " - << "\"\\\\.\\X\"" << std::endl; - res = false; - } - - if (kwsys::SystemTools::ConvertToWindowsExtendedPath("\\\\.\\X:") != - L"\\\\?\\X:") { - std::cerr << "Problem with ConvertToWindowsExtendedPath " - << "\"\\\\.\\X:\"" << std::endl; - res = false; - } - - if (kwsys::SystemTools::ConvertToWindowsExtendedPath("\\\\.\\X:\\") != - L"\\\\?\\X:\\") { - std::cerr << "Problem with ConvertToWindowsExtendedPath " - << "\"\\\\.\\X:\\\"" << std::endl; - res = false; - } - - if (kwsys::SystemTools::ConvertToWindowsExtendedPath("NUL") != - L"\\\\.\\NUL") { - std::cerr << "Problem with ConvertToWindowsExtendedPath " - << "\"NUL\"" << std::endl; - res = false; - } - -#endif - if (kwsys::SystemTools::ConvertToWindowsOutputPath( "L://Local Mojo/Hex Power Pack/Iffy Voodoo") != "\"L:\\Local Mojo\\Hex Power Pack\\Iffy Voodoo\"") {