From: Michael Tremer Date: Fri, 24 Nov 2023 16:49:57 +0000 (+0000) Subject: path: Add function to compute relative paths X-Git-Tag: 0.9.30~1295 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=827eca07301c0f67f14f38e060d72b7d13551aec;p=pakfire.git path: Add function to compute relative paths Signed-off-by: Michael Tremer --- diff --git a/src/libpakfire/include/pakfire/path.h b/src/libpakfire/include/pakfire/path.h index b619bf073..cb2d72a8d 100644 --- a/src/libpakfire/include/pakfire/path.h +++ b/src/libpakfire/include/pakfire/path.h @@ -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 */ diff --git a/src/libpakfire/path.c b/src/libpakfire/path.c index ee2fdee6e..99edf24cd 100644 --- a/src/libpakfire/path.c +++ b/src/libpakfire/path.c @@ -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; +} diff --git a/tests/libpakfire/path.c b/tests/libpakfire/path.c index ffc0d51d2..c7214c46f 100644 --- a/tests/libpakfire/path.c +++ b/tests/libpakfire/path.c @@ -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); }