mirror of
https://github.com/systemd/systemd.git
synced 2025-08-25 13:49:55 +03:00
path-util: introduce path_find_first_component()
The function may be useful to iterate on each path component.
This commit is contained in:
@ -808,6 +808,90 @@ char* dirname_malloc(const char *path) {
|
||||
return dir2;
|
||||
}
|
||||
|
||||
static const char *skip_slash_or_dot(const char *p) {
|
||||
for (; !isempty(p); p++) {
|
||||
if (*p == '/')
|
||||
continue;
|
||||
if (startswith(p, "./")) {
|
||||
p++;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
int path_find_first_component(const char **p, bool accept_dot_dot, const char **ret) {
|
||||
const char *q, *first, *end_first, *next;
|
||||
size_t len;
|
||||
|
||||
assert(p);
|
||||
|
||||
/* When a path is input, then returns the pointer to the first component and its length, and
|
||||
* move the input pointer to the next component or nul. This skips both over any '/'
|
||||
* immediately *before* and *after* the first component before returning.
|
||||
*
|
||||
* Examples
|
||||
* Input: p: "//.//aaa///bbbbb/cc"
|
||||
* Output: p: "bbbbb///cc"
|
||||
* ret: "aaa///bbbbb/cc"
|
||||
* return value: 3 (== strlen("aaa"))
|
||||
*
|
||||
* Input: p: "aaa//"
|
||||
* Output: p: (pointer to NUL)
|
||||
* ret: "aaa//"
|
||||
* return value: 3 (== strlen("aaa"))
|
||||
*
|
||||
* Input: p: "/", ".", ""
|
||||
* Output: p: (pointer to NUL)
|
||||
* ret: NULL
|
||||
* return value: 0
|
||||
*
|
||||
* Input: p: NULL
|
||||
* Output: p: NULL
|
||||
* ret: NULL
|
||||
* return value: 0
|
||||
*
|
||||
* Input: p: "(too long component)"
|
||||
* Output: return value: -EINVAL
|
||||
*
|
||||
* (when accept_dot_dot is false)
|
||||
* Input: p: "//..//aaa///bbbbb/cc"
|
||||
* Output: return value: -EINVAL
|
||||
*/
|
||||
|
||||
q = *p;
|
||||
|
||||
first = skip_slash_or_dot(q);
|
||||
if (isempty(first)) {
|
||||
*p = first;
|
||||
if (ret)
|
||||
*ret = NULL;
|
||||
return 0;
|
||||
}
|
||||
if (streq(first, ".")) {
|
||||
*p = first + 1;
|
||||
if (ret)
|
||||
*ret = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
end_first = strchrnul(first, '/');
|
||||
len = end_first - first;
|
||||
|
||||
if (len > NAME_MAX)
|
||||
return -EINVAL;
|
||||
if (!accept_dot_dot && len == 2 && first[0] == '.' && first[1] == '.')
|
||||
return -EINVAL;
|
||||
|
||||
next = skip_slash_or_dot(end_first);
|
||||
|
||||
*p = next + streq(next, ".");
|
||||
if (ret)
|
||||
*ret = first;
|
||||
return len;
|
||||
}
|
||||
|
||||
const char *last_path_component(const char *path) {
|
||||
|
||||
/* Finds the last component of the path, preserving the optional trailing slash that signifies a directory.
|
||||
|
@ -149,6 +149,7 @@ int fsck_exists(const char *fstype);
|
||||
})
|
||||
|
||||
char* dirname_malloc(const char *path);
|
||||
int path_find_first_component(const char **p, bool accept_dot_dot, const char **ret);
|
||||
const char *last_path_component(const char *path);
|
||||
int path_extract_filename(const char *p, char **ret);
|
||||
int path_extract_directory(const char *p, char **ret);
|
||||
|
@ -577,6 +577,90 @@ static void test_file_in_same_dir(void) {
|
||||
free(t);
|
||||
}
|
||||
|
||||
static void test_path_find_first_component_one(
|
||||
const char *path,
|
||||
bool accept_dot_dot,
|
||||
char **expected,
|
||||
int ret) {
|
||||
|
||||
log_debug("/* %s(\"%s\", accept_dot_dot=%s) */", __func__, strnull(path), yes_no(accept_dot_dot));
|
||||
|
||||
for (const char *p = path;;) {
|
||||
const char *e;
|
||||
int r;
|
||||
|
||||
r = path_find_first_component(&p, accept_dot_dot, &e);
|
||||
if (r <= 0) {
|
||||
if (r == 0) {
|
||||
if (path)
|
||||
assert_se(p == path + strlen_ptr(path));
|
||||
else
|
||||
assert_se(!p);
|
||||
assert_se(!e);
|
||||
}
|
||||
assert_se(r == ret);
|
||||
assert_se(strv_isempty(expected));
|
||||
return;
|
||||
}
|
||||
|
||||
assert_se(e);
|
||||
assert_se(strcspn(e, "/") == (size_t) r);
|
||||
assert_se(strlen_ptr(*expected) == (size_t) r);
|
||||
assert_se(strneq(e, *expected++, r));
|
||||
}
|
||||
}
|
||||
|
||||
static void test_path_find_first_component(void) {
|
||||
_cleanup_free_ char *hoge = NULL;
|
||||
char foo[NAME_MAX * 2];
|
||||
|
||||
log_info("/* %s */", __func__);
|
||||
|
||||
test_path_find_first_component_one(NULL, false, NULL, 0);
|
||||
test_path_find_first_component_one("", false, NULL, 0);
|
||||
test_path_find_first_component_one("/", false, NULL, 0);
|
||||
test_path_find_first_component_one(".", false, NULL, 0);
|
||||
test_path_find_first_component_one("./", false, NULL, 0);
|
||||
test_path_find_first_component_one("./.", false, NULL, 0);
|
||||
test_path_find_first_component_one("..", false, NULL, -EINVAL);
|
||||
test_path_find_first_component_one("/..", false, NULL, -EINVAL);
|
||||
test_path_find_first_component_one("./..", false, NULL, -EINVAL);
|
||||
test_path_find_first_component_one("////./././//.", false, NULL, 0);
|
||||
test_path_find_first_component_one("a/b/c", false, STRV_MAKE("a", "b", "c"), 0);
|
||||
test_path_find_first_component_one("././//.///aa/bbb//./ccc", false, STRV_MAKE("aa", "bbb", "ccc"), 0);
|
||||
test_path_find_first_component_one("././//.///aa/.../../bbb//./ccc/.", false, STRV_MAKE("aa", "..."), -EINVAL);
|
||||
test_path_find_first_component_one("//./aaa///.//./.bbb/..///c.//d.dd///..eeee/.", false, STRV_MAKE("aaa", ".bbb"), -EINVAL);
|
||||
test_path_find_first_component_one("a/foo./b", false, STRV_MAKE("a", "foo.", "b"), 0);
|
||||
|
||||
test_path_find_first_component_one(NULL, true, NULL, 0);
|
||||
test_path_find_first_component_one("", true, NULL, 0);
|
||||
test_path_find_first_component_one("/", true, NULL, 0);
|
||||
test_path_find_first_component_one(".", true, NULL, 0);
|
||||
test_path_find_first_component_one("./", true, NULL, 0);
|
||||
test_path_find_first_component_one("./.", true, NULL, 0);
|
||||
test_path_find_first_component_one("..", true, STRV_MAKE(".."), 0);
|
||||
test_path_find_first_component_one("/..", true, STRV_MAKE(".."), 0);
|
||||
test_path_find_first_component_one("./..", true, STRV_MAKE(".."), 0);
|
||||
test_path_find_first_component_one("////./././//.", true, NULL, 0);
|
||||
test_path_find_first_component_one("a/b/c", true, STRV_MAKE("a", "b", "c"), 0);
|
||||
test_path_find_first_component_one("././//.///aa/bbb//./ccc", true, STRV_MAKE("aa", "bbb", "ccc"), 0);
|
||||
test_path_find_first_component_one("././//.///aa/.../../bbb//./ccc/.", true, STRV_MAKE("aa", "...", "..", "bbb", "ccc"), 0);
|
||||
test_path_find_first_component_one("//./aaa///.//./.bbb/..///c.//d.dd///..eeee/.", true, STRV_MAKE("aaa", ".bbb", "..", "c.", "d.dd", "..eeee"), 0);
|
||||
test_path_find_first_component_one("a/foo./b", true, STRV_MAKE("a", "foo.", "b"), 0);
|
||||
|
||||
memset(foo, 'a', sizeof(foo) -1);
|
||||
char_array_0(foo);
|
||||
|
||||
test_path_find_first_component_one(foo, false, NULL, -EINVAL);
|
||||
test_path_find_first_component_one(foo, true, NULL, -EINVAL);
|
||||
|
||||
hoge = strjoin("a/b/c/", foo, "//d/e/.//f/");
|
||||
assert_se(hoge);
|
||||
|
||||
test_path_find_first_component_one(hoge, false, STRV_MAKE("a", "b", "c"), -EINVAL);
|
||||
test_path_find_first_component_one(hoge, true, STRV_MAKE("a", "b", "c"), -EINVAL);
|
||||
}
|
||||
|
||||
static void test_last_path_component(void) {
|
||||
assert_se(last_path_component(NULL) == NULL);
|
||||
assert_se(streq(last_path_component("a/b/c"), "c"));
|
||||
@ -894,6 +978,7 @@ int main(int argc, char **argv) {
|
||||
test_path_startswith();
|
||||
test_prefix_root();
|
||||
test_file_in_same_dir();
|
||||
test_path_find_first_component();
|
||||
test_last_path_component();
|
||||
test_path_extract_filename();
|
||||
test_path_extract_directory();
|
||||
|
Reference in New Issue
Block a user