]> git.ipfire.org Git - pakfire.git/commitdiff
path: Add function to compute relative paths
authorMichael Tremer <michael.tremer@ipfire.org>
Fri, 24 Nov 2023 16:49:57 +0000 (16:49 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Fri, 24 Nov 2023 16:49:57 +0000 (16:49 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/libpakfire/include/pakfire/path.h
src/libpakfire/path.c
tests/libpakfire/path.c

index b619bf0738a357ba1d564ea655d061315e5446c6..cb2d72a8db213ccb25f76903bd8adf3061619606 100644 (file)
@@ -43,4 +43,8 @@ int __pakfire_path_basename(char* buffer, const size_t length, const char* s);
        __pakfire_path_dirname(path, sizeof(path), s)
 int __pakfire_path_dirname(char* buffer, const size_t length, const char* s);
 
+#define pakfire_path_relative(path, root, s) \
+       __pakfire_path_relative(path, sizeof(path), root, s)
+int __pakfire_path_relative(char* buffer, const size_t length, const char* root, const char* s);
+
 #endif /* PAKFIRE_PATH_H */
index ee2fdee6e5ef69fa01f46b3c36b20575a785dcda..99edf24cd3d8225df6784565f8a6c22f7269ec4b 100644 (file)
@@ -54,9 +54,13 @@ static void pakfire_path_free(struct pakfire_path* path) {
        free(path);
 }
 
-static int pakfire_path_append_segment(struct pakfire_path* path, const char* segment) {
+static int pakfire_path_insert_segment(struct pakfire_path* path, unsigned int idx, const char* segment) {
        char* s = NULL;
 
+       // Index must be within range
+       if (idx >= path->num_segments + 1)
+               return -ERANGE;
+
        // Make space
        path->segments = reallocarray(path->segments, sizeof(*path->segments),
                path->num_segments + 2);
@@ -70,12 +74,26 @@ static int pakfire_path_append_segment(struct pakfire_path* path, const char* se
        if (!s)
                return -errno;
 
-       // Store the segment in the array
-       path->segments[path->num_segments++] = s;
+       unsigned int i = path->num_segments;
+
+       do {
+               if (i > idx)
+                       path->segments[i] = path->segments[i - 1];
+
+               else if (i == idx)
+                       path->segments[i] = s;
+       } while (i-- > idx);
+
+       // Increment the number of segments we have
+       path->num_segments++;
 
        return 0;
 }
 
+static int pakfire_path_append_segment(struct pakfire_path* path, const char* segment) {
+       return pakfire_path_insert_segment(path, path->num_segments, segment);
+}
+
 static int pakfire_path_remove_segment(struct pakfire_path* path, unsigned int idx) {
        if (idx >= path->num_segments)
                return -EINVAL;
@@ -411,3 +429,61 @@ ERROR:
 
        return r;
 }
+
+int __pakfire_path_relative(char* buffer, const size_t length, const char* __root, const char* s) {
+       struct pakfire_path* root = NULL;
+       struct pakfire_path* path = NULL;
+       int r;
+
+       // Parse the root
+       r = pakfire_path_parse(&root, __root);
+       if (r)
+               goto ERROR;
+
+       // Parse the path
+       r = pakfire_path_parse(&path, s);
+       if (r)
+               goto ERROR;
+
+       // Both paths must be absolute
+       if (!root->is_absolute || !path->is_absolute)
+               return -EINVAL;
+
+       // The result is no longer absolute
+       path->is_absolute = 0;
+
+       // Walk through all segments of the root
+       while (root->num_segments && path->num_segments) {
+               // If the first segments match, we drop them both
+               if (strcmp(root->segments[0], path->segments[0]) == 0) {
+                       r = pakfire_path_remove_segment(root, 0);
+                       if (r)
+                               goto ERROR;
+
+                       r = pakfire_path_remove_segment(path, 0);
+                       if (r)
+                               goto ERROR;
+
+               // Otherwise we have reached the end
+               } else {
+                       r = pakfire_path_insert_segment(path, 0, "..");
+                       if (r)
+                               goto ERROR;
+
+                       break;
+               }
+       }
+
+       // Write back the path
+       r = pakfire_path_to_string(path, buffer, length);
+       if (r)
+               goto ERROR;
+
+ERROR:
+       if (path)
+               pakfire_path_free(path);
+       if (root)
+               pakfire_path_free(root);
+
+       return r;
+}
index ffc0d51d24a2042bf3c6a40da847ad281dfd46c9..c7214c46ff73c8050e870f4c3c2a1b71194ed90b 100644 (file)
@@ -118,12 +118,37 @@ FAIL:
        return EXIT_FAILURE;
 }
 
+static int test_path_relative(const struct test* t) {
+       char path[PATH_MAX];
+
+       ASSERT_SUCCESS(pakfire_path_relative(path, "/", "/usr/bin/bash"));
+       ASSERT_STRING_EQUALS(path, "usr/bin/bash");
+
+       ASSERT_SUCCESS(pakfire_path_relative(path, "/usr", "/usr/bin/bash"));
+       ASSERT_STRING_EQUALS(path, "bin/bash");
+
+       ASSERT_SUCCESS(pakfire_path_relative(path, "/usr/bin", "/usr/bin/bash"));
+       ASSERT_STRING_EQUALS(path, "bash");
+
+       ASSERT_SUCCESS(pakfire_path_relative(path, "/usr/sbin", "/usr/bin/bash"));
+       ASSERT_STRING_EQUALS(path, "../bin/bash");
+
+       ASSERT_SUCCESS(pakfire_path_relative(path, "/dev", "/usr/bin/bash"));
+       ASSERT_STRING_EQUALS(path, "../usr/bin/bash");
+
+       return EXIT_SUCCESS;
+
+FAIL:
+       return EXIT_FAILURE;
+}
+
 int main(int argc, const char* argv[]) {
        testsuite_add_test(test_path_normalize, 0);
        testsuite_add_test(test_path_append, 0);
        testsuite_add_test(test_path_merge, 0);
        testsuite_add_test(test_path_basename, 0);
        testsuite_add_test(test_path_dirname, 0);
+       testsuite_add_test(test_path_relative, 0);
 
        return testsuite_run(argc, argv);
 }