]> git.ipfire.org Git - pakfire.git/commitdiff
util: Implement a simple path matching function that supports **
authorMichael Tremer <michael.tremer@ipfire.org>
Thu, 16 Mar 2023 15:54:51 +0000 (15:54 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Thu, 16 Mar 2023 15:54:51 +0000 (15:54 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/libpakfire/include/pakfire/util.h
src/libpakfire/util.c
tests/libpakfire/util.c

index 18b1e5b7de4e2f37c24d8f5ad32f63c49ad5ebab..661fff4d1e50441c9bc59c003ed364cda92396d6 100644 (file)
@@ -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);
index 4711a1d8bd095e4ce9257031c2546649a0052863..a972e96bf56b3cf704ec5765db25a2ad6dd3cd05 100644 (file)
@@ -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;
 
index 70f411b7c2f9afa3053035d15e3c2731fe62582e..37ec2685412f557995e77bb8f0da92e3a156893a 100644 (file)
@@ -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);
 }