]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
path-util: introduce path_compare_filename()
authorYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 6 Apr 2023 20:39:44 +0000 (05:39 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 7 Apr 2023 20:08:51 +0000 (05:08 +0900)
src/basic/path-util.c
src/basic/path-util.h
src/test/test-path-util.c

index ae0b25d1551024ae0415cf476e5b22d71bbf0432..a5ab6045cd24418ccb1ca76b8342a36ed602231f 100644 (file)
@@ -491,25 +491,33 @@ bool path_equal_or_files_same(const char *a, const char *b, int flags) {
         return path_equal(a, b) || files_same(a, b, flags) > 0;
 }
 
-bool path_equal_filename(const char *a, const char *b) {
-        _cleanup_free_ char *a_basename = NULL, *b_basename = NULL;
-        int r;
+int path_compare_filename(const char *a, const char *b) {
+        _cleanup_free_ char *fa = NULL, *fb = NULL;
+        int r, j, k;
 
-        assert(a);
-        assert(b);
+        /* Order NULL before non-NULL */
+        r = CMP(!!a, !!b);
+        if (r != 0)
+                return r;
 
-        r = path_extract_filename(a, &a_basename);
-        if (r < 0) {
-                log_debug_errno(r, "Failed to parse basename of %s: %m", a);
-                return false;
-        }
-        r = path_extract_filename(b, &b_basename);
-        if (r < 0) {
-                log_debug_errno(r, "Failed to parse basename of %s: %m", b);
-                return false;
-        }
+        j = path_extract_filename(a, &fa);
+        k = path_extract_filename(b, &fb);
+
+        /* When one of paths is "." or root, then order it earlier. */
+        r = CMP(j != -EADDRNOTAVAIL, k != -EADDRNOTAVAIL);
+        if (r != 0)
+                return r;
+
+        /* When one of paths is invalid (or we get OOM), order invalid path after valid one. */
+        r = CMP(j < 0, k < 0);
+        if (r != 0)
+                return r;
+
+        /* fallback to use strcmp() if both paths are invalid. */
+        if (j < 0)
+                return strcmp(a, b);
 
-        return path_equal(a_basename, b_basename);
+        return strcmp(fa, fb);
 }
 
 char* path_extend_internal(char **x, ...) {
index 56f01f41d8dfc409e16b67907d86faad0f8ba68c..7843599816e69972bc283daf7e074df654adbe0d 100644 (file)
@@ -66,15 +66,18 @@ char *path_startswith_full(const char *path, const char *prefix, bool accept_dot
 static inline char* path_startswith(const char *path, const char *prefix) {
         return path_startswith_full(path, prefix, true);
 }
-int path_compare(const char *a, const char *b) _pure_;
 
+int path_compare(const char *a, const char *b) _pure_;
 static inline bool path_equal(const char *a, const char *b) {
         return path_compare(a, b) == 0;
 }
 
+int path_compare_filename(const char *a, const char *b);
+static inline bool path_equal_filename(const char *a, const char *b) {
+        return path_compare_filename(a, b) == 0;
+}
+
 bool path_equal_or_files_same(const char *a, const char *b, int flags);
-/* Compares only the last portion of the input paths, ie: the filenames */
-bool path_equal_filename(const char *a, const char *b);
 
 char* path_extend_internal(char **x, ...);
 #define path_extend(x, ...) path_extend_internal(x, __VA_ARGS__, POINTER_MAX)
index 136005d51fe7f1cf4f053f5e7a8ff82cadcdaf1f..87e33919e03175060151c33b4a4c8670a9148fa7 100644 (file)
@@ -46,11 +46,6 @@ TEST(path) {
         assert_se(!path_equal_ptr("/a", "/b"));
         assert_se(!path_equal_ptr("/a", NULL));
         assert_se(!path_equal_ptr(NULL, "/a"));
-
-        assert_se(path_equal_filename("/a/c", "/b/c"));
-        assert_se(path_equal_filename("/a", "/a"));
-        assert_se(!path_equal_filename("/a/b", "/a/c"));
-        assert_se(!path_equal_filename("/b", "/c"));
 }
 
 static void test_path_simplify_one(const char *in, const char *out) {
@@ -150,6 +145,55 @@ TEST(path_compare) {
         test_path_compare_one("/foo/a/b", "/foo/aaa", -1);
 }
 
+static void test_path_compare_filename_one(const char *a, const char *b, int expected) {
+        int r;
+
+        assert_se(path_compare_filename(a, a) == 0);
+        assert_se(path_compare_filename(b, b) == 0);
+
+        r = path_compare_filename(a, b);
+        assert_se((r > 0) == (expected > 0) && (r < 0) == (expected < 0));
+        r = path_compare_filename(b, a);
+        assert_se((r < 0) == (expected > 0) && (r > 0) == (expected < 0));
+
+        assert_se(path_equal_filename(a, a) == 1);
+        assert_se(path_equal_filename(b, b) == 1);
+        assert_se(path_equal_filename(a, b) == (expected == 0));
+        assert_se(path_equal_filename(b, a) == (expected == 0));
+}
+
+TEST(path_compare_filename) {
+        test_path_compare_filename_one("/goo", "/goo", 0);
+        test_path_compare_filename_one("/goo", "/goo", 0);
+        test_path_compare_filename_one("//goo", "/goo", 0);
+        test_path_compare_filename_one("//goo/////", "/goo", 0);
+        test_path_compare_filename_one("goo/////", "goo", 0);
+        test_path_compare_filename_one("/goo/boo", "/goo//boo", 0);
+        test_path_compare_filename_one("//goo/boo", "/goo/boo//", 0);
+        test_path_compare_filename_one("//goo/././//./boo//././//", "/goo/boo//.", 0);
+        test_path_compare_filename_one("/.", "//.///", -1);
+        test_path_compare_filename_one("/x", "x/", 0);
+        test_path_compare_filename_one("x/", "/", 1);
+        test_path_compare_filename_one("/x/./y", "x/y", 0);
+        test_path_compare_filename_one("/x/./y", "/x/y", 0);
+        test_path_compare_filename_one("/x/./././y", "/x/y/././.", 0);
+        test_path_compare_filename_one("./x/./././y", "./x/y/././.", 0);
+        test_path_compare_filename_one(".", "./.", -1);
+        test_path_compare_filename_one(".", "././.", -1);
+        test_path_compare_filename_one("./..", ".", 1);
+        test_path_compare_filename_one("x/.y", "x/y", -1);
+        test_path_compare_filename_one("foo", "/foo", 0);
+        test_path_compare_filename_one("/foo", "/foo/bar", 1);
+        test_path_compare_filename_one("/foo/aaa", "/foo/b", -1);
+        test_path_compare_filename_one("/foo/aaa", "/foo/b/a", 1);
+        test_path_compare_filename_one("/foo/a", "/foo/aaa", -1);
+        test_path_compare_filename_one("/foo/a/b", "/foo/aaa", 1);
+        test_path_compare_filename_one("/a/c", "/b/c", 0);
+        test_path_compare_filename_one("/a", "/a", 0);
+        test_path_compare_filename_one("/a/b", "/a/c", -1);
+        test_path_compare_filename_one("/b", "/c", -1);
+}
+
 TEST(path_equal_root) {
         /* Nail down the details of how path_equal("/", ...) works. */