__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 */
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);
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;
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;
+}
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);
}