From: Michael Tremer Date: Thu, 16 Mar 2023 15:54:51 +0000 (+0000) Subject: util: Implement a simple path matching function that supports ** X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=6871e28d5bb7ac2d2fbcb168a830474ab16a8b14;p=people%2Fstevee%2Fpakfire.git util: Implement a simple path matching function that supports ** Signed-off-by: Michael Tremer --- diff --git a/src/libpakfire/include/pakfire/util.h b/src/libpakfire/include/pakfire/util.h index 18b1e5b7..661fff4d 100644 --- a/src/libpakfire/include/pakfire/util.h +++ b/src/libpakfire/include/pakfire/util.h @@ -37,6 +37,7 @@ char* pakfire_unquote_in_place(char* s); int pakfire_path_exists(const char* path); +int pakfire_path_match(const char* p, const char* s); time_t pakfire_path_age(const char* path); int pakfire_path_strip_extension(char* path); diff --git a/src/libpakfire/util.c b/src/libpakfire/util.c index 4711a1d8..a972e96b 100644 --- a/src/libpakfire/util.c +++ b/src/libpakfire/util.c @@ -149,6 +149,88 @@ int pakfire_path_exists(const char* path) { return !access(path, F_OK); } +/* + This function will handle any stars in the pattern matching + + stars will be set to non-zero if we encountered a double star +*/ +static int __pakfire_path_match_star(const char* p, const char* s) { + unsigned int stars = 0; + + // Count how many stars we found + while (p && *p == '*') { + // Consume the star + p++; + + // Increment the counter + stars++; + } + + // We do not allow more than two stars + if (stars > 2) { + errno = EINVAL; + return -1; + } + + // Consume the string... + for (; *s; s++) { + switch (*s) { + // Found slash! + case '/': + if (stars == 1) + return pakfire_path_match(p, s); + + // Otherwise fall through + + // Read as many characters as possible + default: + continue; + } + } + + // If we reached the end of the string, * has consumed everything + return 1; +} + +/* + This is our custom implementation of fnmatch() + which supports ** and stops at slashes. +*/ +int pakfire_path_match(const char* p, const char* s) { + // Empty pattern matches nothing + if (!*p) + return 0; + + // Consume the pattern and string... + for (; *p; p++, s++) { + switch (*p) { + // Match any character + case '?': + // No match if we reached the end + if (!*s) + return 0; + + continue; + + // Match multiple characters + case '*': + return __pakfire_path_match_star(p, s); + + // All other characters + default: + // Character matches + if (*s == *p) + continue; + + // No match + return 0; + } + } + + // We reached the end of the string and all characters matched + return 1; +} + time_t pakfire_path_age(const char* path) { struct stat st; diff --git a/tests/libpakfire/util.c b/tests/libpakfire/util.c index 70f411b7..37ec2685 100644 --- a/tests/libpakfire/util.c +++ b/tests/libpakfire/util.c @@ -72,10 +72,36 @@ FAIL: return EXIT_FAILURE; } +static int test_path_match(const struct test* t) { + // Simple string match + ASSERT_TRUE(pakfire_path_match("/abc", "/abc")); + + // Simple negative string match + ASSERT_FALSE(pakfire_path_match("/abc", "/def")); + + // Simple star match + ASSERT_TRUE(pakfire_path_match("/usr/*", "/usr/bin")); + ASSERT_FALSE(pakfire_path_match("/usr/*", "/usr/bin/bash")); + + // Double star match + ASSERT_TRUE(pakfire_path_match("/usr/**", "/usr/bin")); + ASSERT_TRUE(pakfire_path_match("/usr/**", "/usr/bin/bash")); + + // Tripe star matches are invalid + ASSERT_ERRNO(pakfire_path_match("/usr/***", "/usr/bin"), EINVAL); + ASSERT_ERRNO(pakfire_path_match("/usr/***", "/usr/bin/bash"), EINVAL); + + return EXIT_SUCCESS; + +FAIL: + return EXIT_FAILURE; +} + int main(int argc, const char* argv[]) { testsuite_add_test(test_basename); testsuite_add_test(test_dirname); testsuite_add_test(test_mkdir); + testsuite_add_test(test_path_match); return testsuite_run(argc, argv); }