From c4049689d1ff6e3b9f59358023aebb1a7e0fd149 Mon Sep 17 00:00:00 2001 From: Brad King <brad.king@kitware.com> Date: Mon, 12 Sep 2016 15:05:29 -0400 Subject: [PATCH] SystemTools: Teach GetActualCaseForPath to convert as much as possible If only some leading components of the input path exist then convert those and leave the rest unchanged. This way if a file path is constructed using GetActualCaseForPath and created on disk, then a following GetActualCaseForPath on the same now-existing path will return the same value. Change-Id: I6e09a4d411f8bf107312f39d7409ba2d22bf176b CMake-Issue: 16295 --- SystemTools.cxx | 70 +++++++++++++++++++++++++--------------------- SystemTools.hxx.in | 9 +++--- 2 files changed, 43 insertions(+), 36 deletions(-) diff --git a/SystemTools.cxx b/SystemTools.cxx index 1a73b16..4281c38 100644 --- a/SystemTools.cxx +++ b/SystemTools.cxx @@ -3982,16 +3982,16 @@ std::string SystemTools::RelativePath(const std::string& local, const std::strin } #ifdef _WIN32 -static int GetCasePathName(const std::string & pathIn, - std::string & casePath) +static std::string GetCasePathName(std::string const& pathIn) { + std::string casePath; std::vector<std::string> path_components; SystemTools::SplitPath(pathIn, path_components); if(path_components[0].empty()) // First component always exists. { // Relative paths cannot be converted. - casePath = ""; - return 0; + casePath = pathIn; + return casePath; } // Start with root component. @@ -4015,38 +4015,45 @@ static int GetCasePathName(const std::string & pathIn, sep = "/"; } + // Convert case of all components that exist. + bool converting = true; for(; idx < path_components.size(); idx++) { casePath += sep; sep = "/"; - std::string test_str = casePath; - test_str += path_components[idx]; - - // If path component contains wildcards, we skip matching - // because these filenames are not allowed on windows, - // and we do not want to match a different file. - if(path_components[idx].find('*') != std::string::npos || - path_components[idx].find('?') != std::string::npos) - { - casePath = ""; - return 0; - } - WIN32_FIND_DATAW findData; - HANDLE hFind = ::FindFirstFileW(Encoding::ToWide(test_str).c_str(), - &findData); - if (INVALID_HANDLE_VALUE != hFind) - { - casePath += Encoding::ToNarrow(findData.cFileName); - ::FindClose(hFind); - } - else + if (converting) { - casePath = ""; - return 0; + // If path component contains wildcards, we skip matching + // because these filenames are not allowed on windows, + // and we do not want to match a different file. + if(path_components[idx].find('*') != std::string::npos || + path_components[idx].find('?') != std::string::npos) + { + converting = false; + } + else + { + std::string test_str = casePath; + test_str += path_components[idx]; + WIN32_FIND_DATAW findData; + HANDLE hFind = ::FindFirstFileW(Encoding::ToWide(test_str).c_str(), + &findData); + if (INVALID_HANDLE_VALUE != hFind) + { + path_components[idx] = Encoding::ToNarrow(findData.cFileName); + ::FindClose(hFind); + } + else + { + converting = false; + } + } } + + casePath += path_components[idx]; } - return (int)casePath.size(); + return casePath; } #endif @@ -4065,11 +4072,10 @@ std::string SystemTools::GetActualCaseForPath(const std::string& p) { return i->second; } - std::string casePath; - int len = GetCasePathName(p, casePath); - if(len == 0 || len > MAX_PATH+1) + std::string casePath = GetCasePathName(p); + if (casePath.size() > MAX_PATH) { - return p; + return casePath; } (*SystemTools::PathCaseMap)[p] = casePath; return casePath; diff --git a/SystemTools.hxx.in b/SystemTools.hxx.in index 28ff0b3..5849145 100644 --- a/SystemTools.hxx.in +++ b/SystemTools.hxx.in @@ -374,10 +374,11 @@ public: static const char* GetExecutableExtension(); /** - * Given a path that exists on a windows machine, return the - * actuall case of the path as it was created. If the file - * does not exist path is returned unchanged. This does nothing - * on unix but return path. + * Given a path on a Windows machine, return the actual case of + * the path as it exists on disk. Path components that do not + * exist on disk are returned unchanged. Relative paths are always + * returned unchanged. Drive letters are always made upper case. + * This does nothing on non-Windows systems but return the path. */ static std::string GetActualCaseForPath(const std::string& path); -- GitLab