summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/path.c31
-rw-r--r--tests-clar/core/path.c98
2 files changed, 119 insertions, 10 deletions
diff --git a/src/path.c b/src/path.c
index a753a734d..50a990b27 100644
--- a/src/path.c
+++ b/src/path.c
@@ -646,12 +646,33 @@ int git_path_resolve_relative(git_buf *path, size_t ceiling)
/* do nothing with singleton dot */;
else if (len == 2 && from[0] == '.' && from[1] == '.') {
- while (to > base && to[-1] == '/') to--;
- while (to > base && to[-1] != '/') to--;
- }
+ /* error out if trying to up one from a hard base */
+ if (to == base && ceiling != 0) {
+ giterr_set(GITERR_INVALID,
+ "Cannot strip root component off url");
+ return -1;
+ }
+
+ /* no more path segments to strip,
+ * use '../' as a new base path */
+ if (to == base) {
+ if (*next == '/')
+ len++;
- else {
- if (*next == '/')
+ if (to != from)
+ memmove(to, from, len);
+
+ to += len;
+ /* this is now the base, can't back up from a
+ * relative prefix */
+ base = to;
+ } else {
+ /* back up a path segment */
+ while (to > base && to[-1] == '/') to--;
+ while (to > base && to[-1] != '/') to--;
+ }
+ } else {
+ if (*next == '/' && *from != '/')
len++;
if (to != from)
diff --git a/tests-clar/core/path.c b/tests-clar/core/path.c
index 407770baa..d35a5bda8 100644
--- a/tests-clar/core/path.c
+++ b/tests-clar/core/path.c
@@ -446,16 +446,15 @@ void test_core_path__14_apply_relative(void)
cl_git_pass(git_path_apply_relative(&p, "../../../../../.."));
cl_assert_equal_s("/this/", p.ptr);
- cl_git_pass(git_path_apply_relative(&p, "../../../../../"));
+ cl_git_pass(git_path_apply_relative(&p, "../"));
cl_assert_equal_s("/", p.ptr);
- cl_git_pass(git_path_apply_relative(&p, "../../../../.."));
- cl_assert_equal_s("/", p.ptr);
+ cl_git_fail(git_path_apply_relative(&p, "../../.."));
cl_git_pass(git_buf_sets(&p, "d:/another/test"));
- cl_git_pass(git_path_apply_relative(&p, "../../../../.."));
+ cl_git_pass(git_path_apply_relative(&p, "../.."));
cl_assert_equal_s("d:/", p.ptr);
cl_git_pass(git_path_apply_relative(&p, "from/here/to/../and/./back/."));
@@ -473,8 +472,97 @@ void test_core_path__14_apply_relative(void)
cl_git_pass(git_path_apply_relative(&p, ".."));
cl_assert_equal_s("https://my.url.com/full/path/", p.ptr);
- cl_git_pass(git_path_apply_relative(&p, "../../../../../"));
+ cl_git_pass(git_path_apply_relative(&p, "../../../"));
cl_assert_equal_s("https://", p.ptr);
+
+ cl_git_pass(git_buf_sets(&p, "../../this/is/relative"));
+
+ cl_git_pass(git_path_apply_relative(&p, "../../preserves/the/prefix"));
+ cl_assert_equal_s("../../this/preserves/the/prefix", p.ptr);
+
+ cl_git_pass(git_path_apply_relative(&p, "../../../../that"));
+ cl_assert_equal_s("../../that", p.ptr);
+
+ cl_git_pass(git_path_apply_relative(&p, "../there"));
+ cl_assert_equal_s("../../there", p.ptr);
git_buf_free(&p);
}
+
+static inline void assert_resolve_relative(git_buf *buf, const char *expected, const char *path)
+{
+ cl_git_pass(git_buf_sets(buf, path));
+ cl_git_pass(git_path_resolve_relative(buf, 0));
+ cl_assert_equal_s(expected, buf->ptr);
+}
+
+void test_core_path__15_resolve_relative(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ assert_resolve_relative(&buf, "", "");
+ assert_resolve_relative(&buf, "", ".");
+ assert_resolve_relative(&buf, "", "./");
+ assert_resolve_relative(&buf, "..", "..");
+ assert_resolve_relative(&buf, "../", "../");
+ assert_resolve_relative(&buf, "..", "./..");
+ assert_resolve_relative(&buf, "../", "./../");
+ assert_resolve_relative(&buf, "../", "../.");
+ assert_resolve_relative(&buf, "../", ".././");
+ assert_resolve_relative(&buf, "../..", "../..");
+ assert_resolve_relative(&buf, "../../", "../../");
+
+ assert_resolve_relative(&buf, "/", "/");
+ assert_resolve_relative(&buf, "/", "/.");
+
+ assert_resolve_relative(&buf, "", "a/..");
+ assert_resolve_relative(&buf, "", "a/../");
+ assert_resolve_relative(&buf, "", "a/../.");
+
+ assert_resolve_relative(&buf, "/a", "/a");
+ assert_resolve_relative(&buf, "/a/", "/a/.");
+ assert_resolve_relative(&buf, "/", "/a/../");
+ assert_resolve_relative(&buf, "/", "/a/../.");
+ assert_resolve_relative(&buf, "/", "/a/.././");
+
+ assert_resolve_relative(&buf, "a", "a");
+ assert_resolve_relative(&buf, "a/", "a/");
+ assert_resolve_relative(&buf, "a/", "a/.");
+ assert_resolve_relative(&buf, "a/", "a/./");
+
+ assert_resolve_relative(&buf, "a/b", "a//b");
+ assert_resolve_relative(&buf, "a/b/c", "a/b/c");
+ assert_resolve_relative(&buf, "b/c", "./b/c");
+ assert_resolve_relative(&buf, "a/c", "a/./c");
+ assert_resolve_relative(&buf, "a/b/", "a/b/.");
+
+ assert_resolve_relative(&buf, "/a/b/c", "///a/b/c");
+ assert_resolve_relative(&buf, "/a/b/c", "//a/b/c");
+ assert_resolve_relative(&buf, "/", "////");
+ assert_resolve_relative(&buf, "/a", "///a");
+ assert_resolve_relative(&buf, "/", "///.");
+ assert_resolve_relative(&buf, "/", "///a/..");
+
+ assert_resolve_relative(&buf, "../../path", "../../test//../././path");
+ assert_resolve_relative(&buf, "../d", "a/b/../../../c/../d");
+
+ cl_git_pass(git_buf_sets(&buf, "/.."));
+ cl_git_fail(git_path_resolve_relative(&buf, 0));
+
+ cl_git_pass(git_buf_sets(&buf, "/./.."));
+ cl_git_fail(git_path_resolve_relative(&buf, 0));
+
+ cl_git_pass(git_buf_sets(&buf, "/.//.."));
+ cl_git_fail(git_path_resolve_relative(&buf, 0));
+
+ cl_git_pass(git_buf_sets(&buf, "/../."));
+ cl_git_fail(git_path_resolve_relative(&buf, 0));
+
+ cl_git_pass(git_buf_sets(&buf, "/../.././../a"));
+ cl_git_fail(git_path_resolve_relative(&buf, 0));
+
+ cl_git_pass(git_buf_sets(&buf, "////.."));
+ cl_git_fail(git_path_resolve_relative(&buf, 0));
+
+ git_buf_free(&buf);
+}