[rhythmbox/uri-is-descendant: 1/2] lib: add rb_uri_is_descendant




commit bb6fba636f870c4a820abc6614582e045d6e9195
Author: Jonathan Matthew <jonathan d14n org>
Date:   Tue Mar 23 23:02:36 2021 +1000

    lib: add rb_uri_is_descendant
    
    This checks if one canonical URI is a descendant of another, that is,
    if removing some number of path components from the first URI would
    result in the second.

 lib/rb-file-helpers.c     | 42 ++++++++++++++++++++++++++++++++++++++++++
 lib/rb-file-helpers.h     |  1 +
 tests/test-file-helpers.c | 17 +++++++++++++++++
 3 files changed, 60 insertions(+)
---
diff --git a/lib/rb-file-helpers.c b/lib/rb-file-helpers.c
index bc8e49ddf..4e5b2a53b 100644
--- a/lib/rb-file-helpers.c
+++ b/lib/rb-file-helpers.c
@@ -1483,6 +1483,48 @@ rb_file_find_extant_parent (GFile *file)
        return file;
 }
 
+/**
+ * rb_uri_is_descendant:
+ * @uri: URI to check
+ * @ancestor: a URI to check against
+ *
+ * Checks if @uri refers to a path beneath @ancestor, such that removing some number
+ * of path segments of @uri would result in @ancestor.
+ * It doesn't do any filesystem operations, it just looks at the URIs as strings.
+ * The URI strings should be built by looking at a filesystem rather than user input,
+ * and must not have path segments that are empty (multiple slashes) or '.' or '..'.
+ *
+ * Given this input, checking if one URI is a descendant of another is pretty simple.
+ * A descendant URI must have the ancestor as a prefix, and if the ancestor ends with
+ * a slash, there must be at least one character after that, otherwise the following
+ * character must be a slash with at least one character after it.
+ *
+ * Return value: %TRUE if @uri is a descendant of @ancestor
+ */
+gboolean
+rb_uri_is_descendant (const char *uri, const char *ancestor)
+{
+       int len;
+
+       if (g_str_has_prefix (uri, ancestor) == FALSE)
+               return FALSE;
+
+       len = strlen(ancestor);
+       if (ancestor[len - 1] == '/') {
+               /*
+                * following character in uri must be a new path segment, not
+                * the end of the uri.  not considering multiple slashes here.
+                */
+               return (uri[len] != '\0');
+       } else {
+               /*
+                * following character in uri must be a separator, with something after it.
+                * not considering multiple slashes here.
+                */
+               return ((uri[len] == '/') && strlen(uri) > (len + 1));
+       }
+}
+
 /**
  * rb_uri_get_filesystem_type:
  * @uri: URI to get filesystem type for
diff --git a/lib/rb-file-helpers.h b/lib/rb-file-helpers.h
index 606eeb31e..8196f63fb 100644
--- a/lib/rb-file-helpers.h
+++ b/lib/rb-file-helpers.h
@@ -60,6 +60,7 @@ gboolean      rb_uri_is_writable      (const char *uri);
 gboolean       rb_uri_is_local         (const char *uri);
 gboolean       rb_uri_is_hidden        (const char *uri);
 gboolean       rb_uri_could_be_podcast (const char *uri, gboolean *is_opml);
+gboolean       rb_uri_is_descendant    (const char *uri, const char *ancestor);
 char *         rb_uri_make_hidden      (const char *uri);
 char *         rb_uri_get_dir_name     (const char *uri);
 char *         rb_uri_get_short_path_name (const char *uri);
diff --git a/tests/test-file-helpers.c b/tests/test-file-helpers.c
index 855b15fc0..f20d85e99 100644
--- a/tests/test-file-helpers.c
+++ b/tests/test-file-helpers.c
@@ -100,6 +100,22 @@ START_TEST (test_rb_check_dir_has_space)
 }
 END_TEST
 
+START_TEST (test_rb_uri_is_descendant)
+{
+       ck_assert (rb_uri_is_descendant ("file:///tmp", "file:///"));
+       ck_assert (rb_uri_is_descendant ("file:///tmp/2", "file:///"));
+       ck_assert (rb_uri_is_descendant ("file:///tmp/2", "file:///tmp"));
+       ck_assert (rb_uri_is_descendant ("file:///tmp/2", "file:///tmp/"));
+       ck_assert (rb_uri_is_descendant ("file:///tmp/", "file:///tmp") == FALSE);
+       ck_assert (rb_uri_is_descendant ("file:///tmp", "file:///tmp/") == FALSE);
+       ck_assert (rb_uri_is_descendant ("file:///tmp/", "file:///tmp/") == FALSE);
+       ck_assert (rb_uri_is_descendant ("file:///tmp", "file:///tmp") == FALSE);
+       ck_assert (rb_uri_is_descendant ("file:///tmp2", "file:///tmp") == FALSE);
+       ck_assert (rb_uri_is_descendant ("file:///tmp/2", "file:///tmp2") == FALSE);
+       ck_assert (rb_uri_is_descendant ("file:///tmp/22", "file:///tmp/2") == FALSE);
+}
+END_TEST
+
 static Suite *
 rb_file_helpers_suite ()
 {
@@ -110,6 +126,7 @@ rb_file_helpers_suite ()
 
        tcase_add_test (tc_chain, test_rb_uri_get_short_path_name);
        tcase_add_test (tc_chain, test_rb_check_dir_has_space);
+       tcase_add_test (tc_chain, test_rb_uri_is_descendant);
 
        return s;
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]