]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
C++-ify get_relative_path
authorJoel Rosdahl <joel@rosdahl.net>
Wed, 19 Feb 2020 19:33:46 +0000 (20:33 +0100)
committerJoel Rosdahl <joel@rosdahl.net>
Sun, 23 Feb 2020 20:07:12 +0000 (21:07 +0100)
src/Util.cpp
src/Util.hpp
src/ccache.cpp
src/legacy_util.cpp
src/legacy_util.hpp
unittest/test_Util.cpp
unittest/test_legacy_util.cpp

index e3bf60c36e322939293fb1722bf3c88c603d8c95..f5ac3700861c1d651519363d2155cf5743fca7e1 100644 (file)
@@ -278,6 +278,50 @@ get_level_1_files(const std::string& dir,
   get_cache_files_internal(dir, 1, progress_receiver, files);
 }
 
+std::string
+get_relative_path(string_view dir, string_view path)
+{
+  assert(Util::is_absolute_path(dir));
+  assert(Util::is_absolute_path(path));
+
+#ifdef _WIN32
+  // Paths can be escaped by a slash for use with e.g. -isystem.
+  if (dir.length() >= 3 && dir[0] == '/' && dir[2] == ':') {
+    dir = dir.substr(1);
+  }
+  if (path.length() >= 3 && path[0] == '/' && path[2] == ':') {
+    path = path.substr(1);
+  }
+  if (dir[0] != path[0]) {
+    // Drive letters differ.
+    return std::string(path);
+  }
+  dir = dir.substr(2);
+  path = path.substr(2);
+#endif
+
+  std::string result;
+  size_t common_prefix_len = Util::common_dir_prefix_length(dir, path);
+  if (common_prefix_len > 0 || dir != "/") {
+    for (size_t i = common_prefix_len; i < dir.length(); ++i) {
+      if (dir[i] == '/') {
+        if (!result.empty()) {
+          result += '/';
+        }
+        result += "..";
+      }
+    }
+  }
+  if (path.length() > common_prefix_len) {
+    if (!result.empty()) {
+      result += '/';
+    }
+    result += std::string(path.substr(common_prefix_len + 1));
+  }
+  result.erase(result.find_last_not_of('/') + 1);
+  return result.empty() ? "." : result;
+}
+
 std::string
 get_path_in_cache(string_view cache_dir,
                   uint32_t levels,
index 3f6b3be4570c9633e764b078e720fb61142f7363..447f7f8841871f953aec10475ca35c36370393d0 100644 (file)
@@ -147,6 +147,13 @@ void get_level_1_files(const std::string& dir,
                        const ProgressReceiver& progress_receiver,
                        std::vector<std::shared_ptr<CacheFile>>& files);
 
+// Compute a relative path from `dir` (an absolute path to a directory) to
+// `path` (an absolute path). Assumes that both `dir` and `path` are normalized.
+// The algorithm does *not* follow symlinks, so the result may not actually
+// resolve to the same file as `path`.
+std::string get_relative_path(nonstd::string_view dir,
+                              nonstd::string_view path);
+
 // Join `cache_dir`, a '/', `name`, and `suffix` into a single path and return
 // it. Additionally `levels` single-character, '/'-separated subpaths are split
 // from the beginning of `name` before joining them all.
index f238a5150a9b617b0f19dd9f3d6e08c7b9d18337..c025b4e4e25fb45599e92c1afabb950deeac5f0c 100644 (file)
@@ -663,14 +663,12 @@ make_relative_path(const Context& ctx, const char* path)
 
   std::string canon_path = Util::real_path(path, true);
   if (!canon_path.empty()) {
-    char* relpath =
-      get_relative_path(ctx.actual_cwd.c_str(), canon_path.c_str());
+    std::string relpath = Util::get_relative_path(ctx.actual_cwd, canon_path);
     if (path_suffix) {
       result = fmt::format("{}/{}", relpath, path_suffix);
     } else {
       result = relpath;
     }
-    free(relpath);
   } else {
     // path doesn't exist, so leave it as it is.
     result = path;
index 1b89a18056d0ebe9f5380299bb30d2833ca2ab7a..e003bbf69d10003c1f27c4df084432611d6bbd24 100644 (file)
@@ -708,59 +708,6 @@ same_executable_name(const char* s1, const char* s2)
 #endif
 }
 
-// Compute a relative path from from (an absolute path to a directory) to to (a
-// path). Assumes that both from and to are well-formed and canonical. Caller
-// frees.
-char*
-get_relative_path(const char* from, const char* to)
-{
-  size_t common_prefix_len;
-  char* result;
-
-  assert(from && Util::is_absolute_path(from));
-  assert(to);
-
-  if (!*to || !Util::is_absolute_path(to)) {
-    return x_strdup(to);
-  }
-
-#ifdef _WIN32
-  // Paths can be escaped by a slash for use with -isystem.
-  if (from[0] == '/') {
-    from++;
-  }
-  if (to[0] == '/') {
-    to++;
-  }
-  // Both paths are absolute, drop the drive letters.
-  assert(from[0] == to[0]); // Assume the same drive letter.
-  from += 2;
-  to += 2;
-#endif
-
-  result = x_strdup("");
-  common_prefix_len = Util::common_dir_prefix_length(from, to);
-  if (common_prefix_len > 0 || !str_eq(from, "/")) {
-    const char* p;
-    for (p = from + common_prefix_len; *p; p++) {
-      if (*p == '/') {
-        reformat(&result, "../%s", result);
-      }
-    }
-  }
-  if (strlen(to) > common_prefix_len) {
-    reformat(&result, "%s%s", result, to + common_prefix_len + 1);
-  }
-  for (int i = strlen(result) - 1; i >= 0 && result[i] == '/'; i--) {
-    result[i] = '\0';
-  }
-  if (str_eq(result, "")) {
-    free(result);
-    result = x_strdup(".");
-  }
-  return result;
-}
-
 // Return whether the argument is a full path.
 bool
 is_full_path(const char* path)
index 329d26ffd2f4f7f9d0e211ac95f177c1a82c6a63..53db517b972049ceed7e59226a1ab923e6b5226d 100644 (file)
@@ -54,7 +54,6 @@ int create_tmp_fd(char** fname);
 FILE* create_tmp_file(char** fname, const char* mode);
 const char* get_home_directory();
 bool same_executable_name(const char* s1, const char* s2);
-char* get_relative_path(const char* from, const char* to);
 bool is_full_path(const char* path);
 void update_mtime(const char* path);
 void x_exit(int status) ATTR_NORETURN;
index 97e8cc5bf3fb3d5b8a07f4fdfcb663ebfea39523..bae8078621394f09589016167966b9879efa5878 100644 (file)
@@ -240,6 +240,35 @@ TEST_CASE("Util::get_level_1_files")
   }
 }
 
+TEST_CASE("Util::get_relative_path")
+{
+#ifdef _WIN32
+  CHECK(Util::get_relative_path("C:/a", "C:/a") == ".");
+  CHECK(Util::get_relative_path("C:/a", "Z:/a") == "Z:/a");
+  CHECK(Util::get_relative_path("C:/a/b", "C:/a") == "..");
+  CHECK(Util::get_relative_path("C:/a", "C:/a/b") == "b");
+  CHECK(Util::get_relative_path("C:/a", "C:/a/b/c") == "b/c");
+  CHECK(Util::get_relative_path("C:/a/b", "C:/a/c") == "../c");
+  CHECK(Util::get_relative_path("C:/a/b", "C:/a/c/d") == "../c/d");
+  CHECK(Util::get_relative_path("C:/a/b/c", "C:/a/c/d") == "../../c/d");
+  CHECK(Util::get_relative_path("C:/a/b", "C:/") == "../..");
+  CHECK(Util::get_relative_path("C:/a/b", "C:/c") == "../../c");
+  CHECK(Util::get_relative_path("C:/", "C:/a/b") == "a/b");
+  CHECK(Util::get_relative_path("C:/a", "D:/a/b") == "D:/a/b");
+#else
+  CHECK(Util::get_relative_path("/a", "/a") == ".");
+  CHECK(Util::get_relative_path("/a/b", "/a") == "..");
+  CHECK(Util::get_relative_path("/a", "/a/b") == "b");
+  CHECK(Util::get_relative_path("/a", "/a/b/c") == "b/c");
+  CHECK(Util::get_relative_path("/a/b", "/a/c") == "../c");
+  CHECK(Util::get_relative_path("/a/b", "/a/c/d") == "../c/d");
+  CHECK(Util::get_relative_path("/a/b/c", "/a/c/d") == "../../c/d");
+  CHECK(Util::get_relative_path("/a/b", "/") == "../..");
+  CHECK(Util::get_relative_path("/a/b", "/c") == "../../c");
+  CHECK(Util::get_relative_path("/", "/a/b") == "a/b");
+#endif
+}
+
 TEST_CASE("Util::get_path_in_cache")
 {
   CHECK(Util::get_path_in_cache("/zz/ccache", 1, "ABCDEF", ".suffix")
index 3dc7338baf85b796c2324ef3d21d506e6692f006..fb88e99a86335dd3c28e1c5b775f70056c319f77 100644 (file)
@@ -35,37 +35,6 @@ TEST(x_dirname)
   CHECK_STR_EQ_FREE2("dir1/dir2", x_dirname("dir1/dir2/"));
 }
 
-TEST(get_relative_path)
-{
-#ifdef _WIN32
-  CHECK_STR_EQ_FREE2("a", get_relative_path("C:/doesn't matter", "a"));
-  CHECK_STR_EQ_FREE2("a/b", get_relative_path("C:/doesn't matter", "a/b"));
-  CHECK_STR_EQ_FREE2(".", get_relative_path("C:/a", "C:/a"));
-  CHECK_STR_EQ_FREE2("..", get_relative_path("C:/a/b", "C:/a"));
-  CHECK_STR_EQ_FREE2("b", get_relative_path("C:/a", "C:/a/b"));
-  CHECK_STR_EQ_FREE2("b/c", get_relative_path("C:/a", "C:/a/b/c"));
-  CHECK_STR_EQ_FREE2("../c", get_relative_path("C:/a/b", "C:/a/c"));
-  CHECK_STR_EQ_FREE2("../c/d", get_relative_path("C:/a/b", "C:/a/c/d"));
-  CHECK_STR_EQ_FREE2("../../c/d", get_relative_path("C:/a/b/c", "C:/a/c/d"));
-  CHECK_STR_EQ_FREE2("../..", get_relative_path("C:/a/b", "C:/"));
-  CHECK_STR_EQ_FREE2("../../c", get_relative_path("C:/a/b", "C:/c"));
-  CHECK_STR_EQ_FREE2("a/b", get_relative_path("C:/", "C:/a/b"));
-#else
-  CHECK_STR_EQ_FREE2("a", get_relative_path("/doesn't matter", "a"));
-  CHECK_STR_EQ_FREE2("a/b", get_relative_path("/doesn't matter", "a/b"));
-  CHECK_STR_EQ_FREE2(".", get_relative_path("/a", "/a"));
-  CHECK_STR_EQ_FREE2("..", get_relative_path("/a/b", "/a"));
-  CHECK_STR_EQ_FREE2("b", get_relative_path("/a", "/a/b"));
-  CHECK_STR_EQ_FREE2("b/c", get_relative_path("/a", "/a/b/c"));
-  CHECK_STR_EQ_FREE2("../c", get_relative_path("/a/b", "/a/c"));
-  CHECK_STR_EQ_FREE2("../c/d", get_relative_path("/a/b", "/a/c/d"));
-  CHECK_STR_EQ_FREE2("../../c/d", get_relative_path("/a/b/c", "/a/c/d"));
-  CHECK_STR_EQ_FREE2("../..", get_relative_path("/a/b", "/"));
-  CHECK_STR_EQ_FREE2("../../c", get_relative_path("/a/b", "/c"));
-  CHECK_STR_EQ_FREE2("a/b", get_relative_path("/", "/a/b"));
-#endif
-}
-
 TEST(subst_env_in_string)
 {
   char* errmsg;