From: Yu Watanabe Date: Thu, 31 May 2018 14:39:31 +0000 (+0900) Subject: path-util: introduce path_simplify() X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=commitdiff_plain;h=7851cfbcac23fc8c8e167c3691a1f63fd277fa1a;p=elogind.git path-util: introduce path_simplify() The function is similar to path_kill_slashes() but also removes initial './', trailing '/.', and '/./' in the path. When the second argument of path_simplify() is false, then it behaves as the same as path_kill_slashes(). Hence, this also replaces path_kill_slashes() with path_simplify(). --- diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c index 0570e9abf..63117afb1 100644 --- a/src/basic/cgroup-util.c +++ b/src/basic/cgroup-util.c @@ -634,7 +634,7 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch if (!t) return -ENOMEM; - *fs = path_kill_slashes(t); + *fs = path_simplify(t, false); return 0; } @@ -651,7 +651,7 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch if (r < 0) return r; - path_kill_slashes(*fs); + path_simplify(*fs, false); return 0; } @@ -1267,7 +1267,7 @@ int cg_split_spec(const char *spec, char **controller, char **path) { if (!t) return -ENOMEM; - *path = path_kill_slashes(t); + *path = path_simplify(t, false); } if (controller) @@ -1319,7 +1319,7 @@ int cg_split_spec(const char *spec, char **controller, char **path) { return -EINVAL; } - path_kill_slashes(u); + path_simplify(u, false); } if (controller) @@ -1350,7 +1350,7 @@ int cg_mangle_path(const char *path, char **result) { if (!t) return -ENOMEM; - *result = path_kill_slashes(t); + *result = path_simplify(t, false); return 0; } diff --git a/src/basic/fileio.c b/src/basic/fileio.c index 859298ff0..245402f6d 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -1240,7 +1240,7 @@ int tempfn_xxxxxx(const char *p, const char *extra, char **ret) { strcpy(stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn), "XXXXXX"); - *ret = path_kill_slashes(t); + *ret = path_simplify(t, false); return 0; } @@ -1281,7 +1281,7 @@ int tempfn_random(const char *p, const char *extra, char **ret) { *x = 0; - *ret = path_kill_slashes(t); + *ret = path_simplify(t, false); return 0; } @@ -1322,7 +1322,7 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) { *x = 0; - *ret = path_kill_slashes(t); + *ret = path_simplify(t, false); return 0; } diff --git a/src/basic/mount-util.c b/src/basic/mount-util.c index 9d1f34e08..76c0969d4 100644 --- a/src/basic/mount-util.c +++ b/src/basic/mount-util.c @@ -414,7 +414,7 @@ int bind_remount_recursive_with_mountinfo(const char *prefix, bool ro, char **bl if (!cleaned) return -ENOMEM; - path_kill_slashes(cleaned); + path_simplify(cleaned, false); done = set_new(&path_hash_ops); if (!done) diff --git a/src/basic/path-util.c b/src/basic/path-util.c index 957c13bb3..eb91adf5b 100644 --- a/src/basic/path-util.c +++ b/src/basic/path-util.c @@ -162,7 +162,7 @@ int path_make_relative(const char *from_dir, const char *to_path, char **_r) { if (!r) return -ENOMEM; - path_kill_slashes(r); + path_simplify(r, false); *_r = r; return 0; @@ -217,7 +217,7 @@ int path_make_relative(const char *from_dir, const char *to_path, char **_r) { p = mempcpy(p, "../", 3); strcpy(p, to_path); - path_kill_slashes(r); + path_simplify(r, false); *_r = r; return 0; @@ -238,7 +238,7 @@ int path_strv_make_absolute_cwd(char **l) { if (r < 0) return r; - path_kill_slashes(t); + path_simplify(t, false); free_and_replace(*s, t); } @@ -339,17 +339,30 @@ char **path_strv_resolve_uniq(char **l, const char *root) { return strv_uniq(l); } -char *path_kill_slashes(char *path) { +char *path_simplify(char *path, bool kill_dots) { char *f, *t; - bool slash = false; + bool slash = false, ignore_slash = false, absolute; - /* Removes redundant inner and trailing slashes. Modifies the - * passed string in-place. + assert(path); + + /* Removes redundant inner and trailing slashes. Also removes unnecessary dots + * if kill_dots is true. Modifies the passed string in-place. * - * ///foo///bar/ becomes /foo/bar + * ///foo//./bar/. becomes /foo/./bar/. (if kill_dots is false) + * ///foo//./bar/. becomes /foo/bar (if kill_dots is true) + * .//./foo//./bar/. becomes ./foo/bar (if kill_dots is false) + * .//./foo//./bar/. becomes foo/bar (if kill_dots is true) */ - for (f = path, t = path; *f; f++) { + absolute = path_is_absolute(path); + + f = path; + if (kill_dots && *f == '.' && IN_SET(f[1], 0, '/')) { + ignore_slash = true; + f++; + } + + for (t = path; *f; f++) { if (*f == '/') { slash = true; @@ -357,17 +370,21 @@ char *path_kill_slashes(char *path) { } if (slash) { + if (kill_dots && *f == '.' && IN_SET(f[1], 0, '/')) + continue; + slash = false; - *(t++) = '/'; + if (ignore_slash) + ignore_slash = false; + else + *(t++) = '/'; } *(t++) = *f; } - /* Special rule, if we are talking of the root directory, a - trailing slash is good */ - - if (t == path && slash) + /* Special rule, if we are talking of the root directory, a trailing slash is good */ + if (absolute && t == path) *(t++) = '/'; *t = 0; @@ -534,7 +551,7 @@ int find_binary(const char *name, char **ret) { /* Found it! */ if (ret) { - *ret = path_kill_slashes(j); + *ret = path_simplify(j, false); j = NULL; } @@ -691,12 +708,12 @@ int parse_path_argument_and_warn(const char *path, bool suppress_root, char **ar if (r < 0) return log_error_errno(r, "Failed to parse path \"%s\" and make it absolute: %m", path); - path_kill_slashes(p); - if (suppress_root && path_equal(p, "/")) + path_simplify(p, false); + if (suppress_root && empty_or_root(p)) p = mfree(p); - free(*arg); - *arg = p; + free_and_replace(*arg, p); + return 0; } #endif // 0 diff --git a/src/basic/path-util.h b/src/basic/path-util.h index e1f22ed97..4fabe06e6 100644 --- a/src/basic/path-util.h +++ b/src/basic/path-util.h @@ -56,12 +56,12 @@ 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); #endif // 0 -char* path_kill_slashes(char *path); char* path_startswith(const char *path, const char *prefix) _pure_; int path_compare(const char *a, const char *b) _pure_; bool path_equal(const char *a, const char *b) _pure_; bool path_equal_or_files_same(const char *a, const char *b, int flags); char* path_join(const char *root, const char *path, const char *rest); +char* path_simplify(char *path, bool kill_dots); static inline bool path_equal_ptr(const char *a, const char *b) { return !!a == !!b && (!a || path_equal(a, b)); @@ -111,11 +111,11 @@ int mkfs_exists(const char *fstype); * the tree, to root. Also returns "" (and not "/"!) for the root * directory. Excludes the specified directory itself */ #define PATH_FOREACH_PREFIX(prefix, path) \ - for (char *_slash = ({ path_kill_slashes(strcpy(prefix, path)); streq(prefix, "/") ? NULL : strrchr(prefix, '/'); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/')) + for (char *_slash = ({ path_simplify(strcpy(prefix, path), false); streq(prefix, "/") ? NULL : strrchr(prefix, '/'); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/')) /* Same as PATH_FOREACH_PREFIX but also includes the specified path itself */ #define PATH_FOREACH_PREFIX_MORE(prefix, path) \ - for (char *_slash = ({ path_kill_slashes(strcpy(prefix, path)); if (streq(prefix, "/")) prefix[0] = 0; strrchr(prefix, 0); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/')) + for (char *_slash = ({ path_simplify(strcpy(prefix, path), false); if (streq(prefix, "/")) prefix[0] = 0; strrchr(prefix, 0); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/')) char *prefix_root(const char *root, const char *path); diff --git a/src/basic/unit-name.c b/src/basic/unit-name.c index 113d9bb10..12323fb4d 100644 --- a/src/basic/unit-name.c +++ b/src/basic/unit-name.c @@ -388,7 +388,7 @@ int unit_name_path_escape(const char *f, char **ret) { if (!p) return -ENOMEM; - path_kill_slashes(p); + path_simplify(p, false); if (empty_or_root(p)) s = strdup("-"); diff --git a/src/core/cgroup.c b/src/core/cgroup.c index a1e66864b..1c8245e0b 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -1520,7 +1520,7 @@ static int unit_attach_pid_to_cgroup_via_bus(Unit *u, pid_t pid, const char *suf return -EINVAL; pp = strjoina("/", pp, suffix_path); - path_kill_slashes(pp); + path_simplify(pp, false); r = sd_bus_call_method(u->manager->system_bus, "org.freedesktop.systemd1", diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c index eeb93e284..899711e54 100644 --- a/src/shared/conf-parser.c +++ b/src/shared/conf-parser.c @@ -41,6 +41,7 @@ //#include "rlimit-util.h" //#include "rlimit-util.h" //#include "rlimit-util.h" +//#include "rlimit-util.h" int config_item_table_lookup( const void *table, @@ -757,7 +758,7 @@ int config_parse_path( if (!n) return log_oom(); - path_kill_slashes(n); + path_simplify(n, false); finalize: free(*s); diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c index e7a247027..80bca48e0 100644 --- a/src/test/test-path-util.c +++ b/src/test/test-path-util.c @@ -26,6 +26,16 @@ assert_se(path_equal(b, a) == !result); \ } +static void test_path_simplify(const char *in, const char *out, const char *out_dot) { + char *p; + + p = strdupa(in); + assert_se(streq(path_simplify(p, false), out)); + + p = strdupa(in); + assert_se(streq(path_simplify(p, true), out_dot)); +} + static void test_path(void) { _cleanup_close_ int fd = -1; @@ -69,15 +79,28 @@ static void test_path(void) { assert_se(fd >= 0); assert_se(fd_is_mount_point(fd, "/", 0) > 0); - { - char p1[] = "aaa/bbb////ccc"; - char p2[] = "//aaa/.////ccc"; - char p3[] = "/./"; - - assert_se(path_equal(path_kill_slashes(p1), "aaa/bbb/ccc")); - assert_se(path_equal(path_kill_slashes(p2), "/aaa/./ccc")); - assert_se(path_equal(path_kill_slashes(p3), "/./")); - } + test_path_simplify("aaa/bbb////ccc", "aaa/bbb/ccc", "aaa/bbb/ccc"); + test_path_simplify("//aaa/.////ccc", "/aaa/./ccc", "/aaa/ccc"); + test_path_simplify("///", "/", "/"); + test_path_simplify("///.//", "/.", "/"); + test_path_simplify("///.//.///", "/./.", "/"); + test_path_simplify("////.././///../.", "/.././../.", "/../.."); + test_path_simplify(".", ".", ""); + test_path_simplify("./", ".", ""); + test_path_simplify(".///.//./.", "./././.", ""); + test_path_simplify(".///.//././/", "./././.", ""); + test_path_simplify("//./aaa///.//./.bbb/..///c.//d.dd///..eeee/.", + "/./aaa/././.bbb/../c./d.dd/..eeee/.", + "/aaa/.bbb/../c./d.dd/..eeee"); + test_path_simplify("//./aaa///.//./.bbb/..///c.//d.dd///..eeee/..", + "/./aaa/././.bbb/../c./d.dd/..eeee/..", + "/aaa/.bbb/../c./d.dd/..eeee/.."); + test_path_simplify(".//./aaa///.//./.bbb/..///c.//d.dd///..eeee/..", + "././aaa/././.bbb/../c./d.dd/..eeee/..", + "aaa/.bbb/../c./d.dd/..eeee/.."); + test_path_simplify("..//./aaa///.//./.bbb/..///c.//d.dd///..eeee/..", + ".././aaa/././.bbb/../c./d.dd/..eeee/..", + "../aaa/.bbb/../c./d.dd/..eeee/.."); assert_se(PATH_IN_SET("/bin", "/", "/bin", "/foo")); assert_se(PATH_IN_SET("/bin", "/bin"));