/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <errno.h>
+#include <fnmatch.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include "extract-word.h"
#include "fd-util.h"
#include "fs-util.h"
+#include "glob-util.h"
#include "log.h"
#include "macro.h"
#include "path-util.h"
return false;
}
+
+int path_glob_can_match(const char *pattern, const char *prefix, char **ret) {
+ assert(pattern);
+ assert(prefix);
+
+ for (const char *a = pattern, *b = prefix;;) {
+ _cleanup_free_ char *g = NULL, *h = NULL;
+ const char *p, *q;
+ int r, s;
+
+ r = path_find_first_component(&a, /* accept_dot_dot = */ false, &p);
+ if (r < 0)
+ return r;
+
+ s = path_find_first_component(&b, /* accept_dot_dot = */ false, &q);
+ if (s < 0)
+ return s;
+
+ if (s == 0) {
+ /* The pattern matches the prefix. */
+ if (ret) {
+ char *t;
+
+ t = path_join(prefix, p);
+ if (!t)
+ return -ENOMEM;
+
+ *ret = t;
+ }
+ return true;
+ }
+
+ if (r == 0)
+ break;
+
+ if (r == s && strneq(p, q, r))
+ continue; /* common component. Check next. */
+
+ g = strndup(p, r);
+ if (!g)
+ return -ENOMEM;
+
+ if (!string_is_glob(g))
+ break;
+
+ /* We found a glob component. Check if the glob pattern matches the prefix component. */
+
+ h = strndup(q, s);
+ if (!h)
+ return -ENOMEM;
+
+ if (fnmatch(g, h, 0) != 0)
+ break;
+ }
+
+ /* The pattern does not match the prefix. */
+ if (ret)
+ *ret = NULL;
+ return false;
+}
assert_se(streq_ptr(path_startswith_strv("/foo2/bar", STRV_MAKE("/foo/quux", "", "/zzz")), NULL));
}
+static void test_path_glob_can_match_one(const char *pattern, const char *prefix, const char *expected) {
+ _cleanup_free_ char *result = NULL;
+
+ log_debug("%s(%s, %s, %s)", __func__, pattern, prefix, strnull(expected));
+
+ assert_se(path_glob_can_match(pattern, prefix, &result) == !!expected);
+ assert_se(streq_ptr(result, expected));
+}
+
+TEST(path_glob_can_match) {
+ test_path_glob_can_match_one("/foo/hoge/aaa", "/foo/hoge/aaa/bbb", NULL);
+ test_path_glob_can_match_one("/foo/hoge/aaa", "/foo/hoge/aaa", "/foo/hoge/aaa");
+ test_path_glob_can_match_one("/foo/hoge/aaa", "/foo/hoge", "/foo/hoge/aaa");
+ test_path_glob_can_match_one("/foo/hoge/aaa", "/foo", "/foo/hoge/aaa");
+ test_path_glob_can_match_one("/foo/hoge/aaa", "/", "/foo/hoge/aaa");
+
+ test_path_glob_can_match_one("/foo/*/aaa", "/foo/hoge/aaa/bbb", NULL);
+ test_path_glob_can_match_one("/foo/*/aaa", "/foo/hoge/aaa", "/foo/hoge/aaa");
+ test_path_glob_can_match_one("/foo/*/aaa", "/foo/hoge", "/foo/hoge/aaa");
+ test_path_glob_can_match_one("/foo/*/aaa", "/foo", "/foo/*/aaa");
+ test_path_glob_can_match_one("/foo/*/aaa", "/", "/foo/*/aaa");
+
+ test_path_glob_can_match_one("/foo/*/*/aaa", "/foo/xxx/yyy/aaa/bbb", NULL);
+ test_path_glob_can_match_one("/foo/*/*/aaa", "/foo/xxx/yyy/aaa", "/foo/xxx/yyy/aaa");
+ test_path_glob_can_match_one("/foo/*/*/aaa", "/foo/xxx/yyy", "/foo/xxx/yyy/aaa");
+ test_path_glob_can_match_one("/foo/*/*/aaa", "/foo/xxx", "/foo/xxx/*/aaa");
+ test_path_glob_can_match_one("/foo/*/*/aaa", "/foo", "/foo/*/*/aaa");
+ test_path_glob_can_match_one("/foo/*/*/aaa", "/", "/foo/*/*/aaa");
+
+ test_path_glob_can_match_one("/foo/*/aaa/*", "/foo/xxx/aaa/bbb/ccc", NULL);
+ test_path_glob_can_match_one("/foo/*/aaa/*", "/foo/xxx/aaa/bbb", "/foo/xxx/aaa/bbb");
+ test_path_glob_can_match_one("/foo/*/aaa/*", "/foo/xxx/ccc", NULL);
+ test_path_glob_can_match_one("/foo/*/aaa/*", "/foo/xxx/aaa", "/foo/xxx/aaa/*");
+ test_path_glob_can_match_one("/foo/*/aaa/*", "/foo/xxx", "/foo/xxx/aaa/*");
+ test_path_glob_can_match_one("/foo/*/aaa/*", "/foo", "/foo/*/aaa/*");
+ test_path_glob_can_match_one("/foo/*/aaa/*", "/", "/foo/*/aaa/*");
+}
+
TEST(print_MAX) {
log_info("PATH_MAX=%zu\n"
"FILENAME_MAX=%zu\n"