From: Joel Rosdahl Date: Wed, 19 Feb 2020 19:33:46 +0000 (+0100) Subject: C++-ify get_relative_path X-Git-Tag: v4.0~594 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=cd3d3ae2d327d304fa9c493377ca5cf1dd730ffc;p=thirdparty%2Fccache.git C++-ify get_relative_path --- diff --git a/src/Util.cpp b/src/Util.cpp index e3bf60c36..f5ac37008 100644 --- a/src/Util.cpp +++ b/src/Util.cpp @@ -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, diff --git a/src/Util.hpp b/src/Util.hpp index 3f6b3be45..447f7f884 100644 --- a/src/Util.hpp +++ b/src/Util.hpp @@ -147,6 +147,13 @@ void get_level_1_files(const std::string& dir, const ProgressReceiver& progress_receiver, std::vector>& 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. diff --git a/src/ccache.cpp b/src/ccache.cpp index f238a5150..c025b4e4e 100644 --- a/src/ccache.cpp +++ b/src/ccache.cpp @@ -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; diff --git a/src/legacy_util.cpp b/src/legacy_util.cpp index 1b89a1805..e003bbf69 100644 --- a/src/legacy_util.cpp +++ b/src/legacy_util.cpp @@ -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) diff --git a/src/legacy_util.hpp b/src/legacy_util.hpp index 329d26ffd..53db517b9 100644 --- a/src/legacy_util.hpp +++ b/src/legacy_util.hpp @@ -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; diff --git a/unittest/test_Util.cpp b/unittest/test_Util.cpp index 97e8cc5bf..bae807862 100644 --- a/unittest/test_Util.cpp +++ b/unittest/test_Util.cpp @@ -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") diff --git a/unittest/test_legacy_util.cpp b/unittest/test_legacy_util.cpp index 3dc7338ba..fb88e99a8 100644 --- a/unittest/test_legacy_util.cpp +++ b/unittest/test_legacy_util.cpp @@ -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;