/*
- * 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
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
/*
* 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)
++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--;
}
}
/*
- * 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)
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] == '/') {