From: Joel Rosdahl Date: Sat, 28 Jul 2012 09:50:06 +0000 (+0200) Subject: Improve get_relative_path and add unit tests X-Git-Tag: v3.1.8~11 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a5f8e78c61867fe64881323018bbeca83a43f6a1;p=thirdparty%2Fccache.git Improve get_relative_path and add unit tests --- diff --git a/test/test_util.c b/test/test_util.c index b8d24beeb..10478baa7 100644 --- a/test/test_util.c +++ b/test/test_util.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Joel Rosdahl + * Copyright (C) 2010, 2012 Joel Rosdahl * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -41,4 +41,32 @@ TEST(dirname) CHECK_STR_EQ_FREE2("dir1/dir2", dirname("dir1/dir2/")); } +TEST(common_dir_prefix_length) +{ + CHECK_UNS_EQ(0, common_dir_prefix_length("", "")); + CHECK_UNS_EQ(0, common_dir_prefix_length("/", "/")); + CHECK_UNS_EQ(0, common_dir_prefix_length("/", "/b")); + CHECK_UNS_EQ(0, common_dir_prefix_length("/a", "/b")); + CHECK_UNS_EQ(2, common_dir_prefix_length("/a", "/a")); + CHECK_UNS_EQ(2, common_dir_prefix_length("/a", "/a/b")); + CHECK_UNS_EQ(2, common_dir_prefix_length("/a/b", "/a/c")); + CHECK_UNS_EQ(4, common_dir_prefix_length("/a/b", "/a/b")); +} + +TEST(get_relative_path) +{ + 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")); +} + TEST_SUITE_END diff --git a/util.c b/util.c index acfcd9db6..b48980006 100644 --- a/util.c +++ b/util.c @@ -964,7 +964,7 @@ same_executable_name(const char *s1, const char *s2) /* * Compute the length of the longest directory path that is common to two - * strings. + * paths. s1 is assumed to be the path to a directory. */ size_t common_dir_prefix_length(const char *s1, const char *s2) @@ -976,7 +976,21 @@ common_dir_prefix_length(const char *s1, const char *s2) ++p1; ++p2; } - while (p1 > s1 && ((*p1 && *p1 != '/' ) || (*p2 && *p2 != '/'))) { + if (*p2 == '/') { + /* s2 starts with "s1/". */ + return p1 - s1; + } + if (!*p2) { + /* s2 is equal to s1. */ + if (p2 == s2 + 1) { + /* Special case for s1 and s2 both being "/". */ + return 0; + } else { + return p1 - s1; + } + } + /* Compute the common directory prefix */ + while (p1 > s1 && *p1 != '/') { p1--; p2--; } @@ -984,8 +998,9 @@ common_dir_prefix_length(const char *s1, const char *s2) } /* - * Compute a relative path from from to to. Assumes that both from and to are - * well-formed and canonical. Caller frees. + * 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) @@ -995,23 +1010,24 @@ get_relative_path(const char *from, const char *to) const char *p; char *result; + assert(from && from[0] == '/'); + assert(to); + if (!*to || *to != '/') { return x_strdup(to); } result = x_strdup(""); common_prefix_len = common_dir_prefix_length(from, to); - for (p = from + common_prefix_len; *p; p++) { - if (*p == '/') { - x_asprintf2(&result, "../%s", result); + if (common_prefix_len > 0 || !str_eq(from, "/")) { + for (p = from + common_prefix_len; *p; p++) { + if (*p == '/') { + x_asprintf2(&result, "../%s", result); + } } } if (strlen(to) > common_prefix_len) { - p = to + common_prefix_len + 1; - while (*p == '/') { - p++; - } - x_asprintf2(&result, "%s%s", result, p); + x_asprintf2(&result, "%s%s", result, to + common_prefix_len + 1); } i = strlen(result) - 1; while (i >= 0 && result[i] == '/') {