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