]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
Improve get_relative_path and add unit tests
authorJoel Rosdahl <joel@rosdahl.net>
Sat, 28 Jul 2012 09:50:06 +0000 (11:50 +0200)
committerJoel Rosdahl <joel@rosdahl.net>
Sat, 28 Jul 2012 09:50:06 +0000 (11:50 +0200)
test/test_util.c
util.c

index b8d24beebf56d081d0ace81e01c1ab4ee263f96e..10478baa7d6eac753ec3574144c8a57ca159b7fc 100644 (file)
@@ -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 acfcd9db66fc31e44f02ca271522e2c346e24785..b4898000642b6df777f8502d89846daa5a80c225 100644 (file)
--- 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] == '/') {