From 88272c0fe5aca0bcdb60aeb1b4d5f4cf1c9a47b4 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 3 Jun 2018 23:54:32 +0900 Subject: path-util: make path_make_relative() support path including dots --- src/basic/path-util.c | 68 ++++++++++++++++++++++------------------------- src/test/test-path-util.c | 2 +- 2 files changed, 33 insertions(+), 37 deletions(-) diff --git a/src/basic/path-util.c b/src/basic/path-util.c index eb91adf5b..4bca37266 100644 --- a/src/basic/path-util.c +++ b/src/basic/path-util.c @@ -130,8 +130,8 @@ int path_make_absolute_cwd(const char *p, char **ret) { #if 0 /// UNNEEDED by elogind int path_make_relative(const char *from_dir, const char *to_path, char **_r) { - char *r, *p; - unsigned n_parents; + char *f, *t, *r, *p; + unsigned n_parents = 0; assert(from_dir); assert(to_path); @@ -139,85 +139,81 @@ int path_make_relative(const char *from_dir, const char *to_path, char **_r) { /* Strips the common part, and adds ".." elements as necessary. */ - if (!path_is_absolute(from_dir)) + if (!path_is_absolute(from_dir) || !path_is_absolute(to_path)) return -EINVAL; - if (!path_is_absolute(to_path)) - return -EINVAL; + f = strdupa(from_dir); + t = strdupa(to_path); + + path_simplify(f, true); + path_simplify(t, true); /* Skip the common part. */ for (;;) { size_t a, b; - from_dir += strspn(from_dir, "/"); - to_path += strspn(to_path, "/"); + f += *f == '/'; + t += *t == '/'; - if (!*from_dir) { - if (!*to_path) + if (!*f) { + if (!*t) /* from_dir equals to_path. */ r = strdup("."); else /* from_dir is a parent directory of to_path. */ - r = strdup(to_path); + r = strdup(t); if (!r) return -ENOMEM; - path_simplify(r, false); - *_r = r; return 0; } - if (!*to_path) + if (!*t) break; - a = strcspn(from_dir, "/"); - b = strcspn(to_path, "/"); - - if (a != b) - break; + a = strcspn(f, "/"); + b = strcspn(t, "/"); - if (memcmp(from_dir, to_path, a) != 0) + if (a != b || memcmp(f, t, a) != 0) break; - from_dir += a; - to_path += b; + f += a; + t += b; } /* If we're here, then "from_dir" has one or more elements that need to * be replaced with "..". */ /* Count the number of necessary ".." elements. */ - for (n_parents = 0;;) { + for (; *f;) { size_t w; - from_dir += strspn(from_dir, "/"); - - if (!*from_dir) - break; - - w = strcspn(from_dir, "/"); + w = strcspn(f, "/"); /* If this includes ".." we can't do a simple series of "..", refuse */ - if (w == 2 && from_dir[0] == '.' && from_dir[1] == '.') + if (w == 2 && f[0] == '.' && f[1] == '.') return -EINVAL; - /* Count number of elements, except if they are "." */ - if (w != 1 || from_dir[0] != '.') - n_parents++; + /* Count number of elements */ + n_parents++; - from_dir += w; + f += w; + f += *f == '/'; } - r = new(char, n_parents * 3 + strlen(to_path) + 1); + r = new(char, n_parents * 3 + strlen(t) + 1); if (!r) return -ENOMEM; for (p = r; n_parents > 0; n_parents--) p = mempcpy(p, "../", 3); - strcpy(p, to_path); - path_simplify(r, false); + if (*t) + strcpy(p, t); + else + /* Remove trailing slash */ + *(--p) = 0; *_r = r; return 0; diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c index 80bca48e0..d0f7a5772 100644 --- a/src/test/test-path-util.c +++ b/src/test/test-path-util.c @@ -287,7 +287,7 @@ static void test_make_relative(void) { test("/some/path", "/", "../.."); test("/some/path", "/some/other/path", "../other/path"); test("/some/path/./dot", "/some/further/path", "../../further/path"); - test("//extra/////slashes///won't////fool///anybody//", "////extra///slashes////are/just///fine///", "../../../are/just/fine"); + test("//extra.//.//./.slashes//./won't////fo.ol///anybody//", "/././/extra././/.slashes////ar.e/.just/././.fine///", "../../../ar.e/.just/.fine"); } #endif // 0 -- cgit v1.2.3