From: Alberto Leiva Popper Date: Thu, 15 May 2025 22:03:49 +0000 (-0600) Subject: Clean up path_join() implementation X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8fab31bc5933ab7a2ba0d1ed187593387ba7b4a4;p=thirdparty%2FFORT-validator.git Clean up path_join() implementation --- diff --git a/src/types/path.c b/src/types/path.c index d793ab32..60ca4dc7 100644 --- a/src/types/path.c +++ b/src/types/path.c @@ -5,6 +5,7 @@ #include "alloc.h" #include "config.h" #include "log.h" +#include "types/str.h" /* These are arbitrary; feel free to change them. */ #ifndef INITIAL_CAPACITY /* Unit tests want to override this */ @@ -46,37 +47,61 @@ path_filename(char const *path) return slash ? (slash + 1) : path; } -/* - * Cannot return NULL. - * - * XXX I'm starting to use this more. Probably sanitize better. - */ -char * -path_join(char const *path1, char const *path2) +static void +trim_leading_slashes(struct sized_string *str) { - // XXX needed? - if (path1[0] == 0) - return pstrdup(path2); - if (path2 == NULL || path2[0] == 0) - return pstrdup(path1); + while (str->str[0] == '/') { + str->str++; + str->len--; + } +} - return path_njoin(path1, path2, strlen(path2)); +static void +trim_trailing_slashes(struct sized_string *str) +{ + while (str->len > 1 && str->str[str->len - 1] == '/') + str->len--; } +/* Result needs cleanup, cannot return NULL. */ char * -path_njoin(char const *p1, char const *p2, size_t p2len) +path_join(char const *path1, char const *path2) { + struct sized_string p1; + struct sized_string p2; size_t n; char *result; - int written; - n = strlen(p1) + p2len + 2; + if (path1) { + p1.str = path1; + p1.len = strlen(path1); + trim_trailing_slashes(&p1); + } else { + memset(&p1, 0, sizeof(p1)); + } + + if (path2) { + p2.str = path2; + p2.len = strlen(path2); + trim_leading_slashes(&p2); + } else { + memset(&p2, 0, sizeof(p2)); + } + + if (p1.len == 0 && p2.len == 0) + return pstrdup(""); + if (p1.len == 0 || p1.str[0] == '\0') + return pstrndup(p2.str, p2.len); + if (p2.len == 0 || p2.str[0] == '\0') + return pstrndup(p1.str, p1.len); + + n = p1.len + p2.len + 2; result = pmalloc(n); - written = snprintf(result, n, "%s/%.*s", p1, (int) p2len, p2); - if (written != n - 1) - pr_crit("path_njoin: %zu %d %s %.*s", - n, written, p1, (int) p2len, p2); + memcpy(result, p1.str, p1.len); + result[p1.len] = '/'; + memcpy(result + p1.len + 1, p2.str, p2.len); + result[n - 1] = '\0'; return result; } diff --git a/src/types/path.h b/src/types/path.h index ec31211e..b0c5dedc 100644 --- a/src/types/path.h +++ b/src/types/path.h @@ -15,6 +15,5 @@ bool token_next(struct tokenizer *tkn); char const *path_filename(char const *); char *path_join(char const *, char const *); -char *path_njoin(char const *, char const *, size_t); #endif /* SRC_TYPES_PATH_H_ */ diff --git a/src/types/str.h b/src/types/str.h index 6963ea46..33a9a9e9 100644 --- a/src/types/str.h +++ b/src/types/str.h @@ -5,6 +5,11 @@ #include #include +struct sized_string { + char const *str; + size_t len; +}; + char *str_concat(char const *, char const *); int hex2ulong(char const *, unsigned long *); diff --git a/test/Makefile.am b/test/Makefile.am index 4a6a8b27..6d50dd69 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -68,6 +68,10 @@ check_PROGRAMS += mft.test mft_test_SOURCES = object/manifest_test.c mft_test_LDADD = ${CHECK_LIBS} +check_PROGRAMS += path.test +path_test_SOURCES = types/path_test.c +path_test_LDADD = ${CHECK_LIBS} + check_PROGRAMS += pdu_handler.test pdu_handler_test_SOURCES = rtr/pdu_handler_test.c pdu_handler_test_LDADD = ${CHECK_LIBS} diff --git a/test/types/path_test.c b/test/types/path_test.c new file mode 100644 index 00000000..8f9f210b --- /dev/null +++ b/test/types/path_test.c @@ -0,0 +1,73 @@ +#include +#include + +#include "alloc.c" +#include "mock.c" +#include "types/path.c" + +#define TEST_JOIN(expected, a, b) \ + do { \ + char *actual = path_join(a, b); \ + ck_assert_pstr_eq(expected, actual); \ + free(actual); \ + } while (0); + +START_TEST(test_join) +{ + TEST_JOIN("", NULL, NULL); + TEST_JOIN("", "", NULL); + TEST_JOIN("", NULL, ""); + TEST_JOIN("", "", ""); + + TEST_JOIN("a", "a", NULL); + TEST_JOIN("b", NULL, "b"); + TEST_JOIN("a/b", "a", "b"); + TEST_JOIN("abcd/efg", "abcd", "efg"); + + TEST_JOIN("c/d", "c/", "d"); + TEST_JOIN("e/f", "e", "/f"); + TEST_JOIN("g/h", "g/", "/h"); + + TEST_JOIN("/c/d/", "/c/", "d/"); + TEST_JOIN("/e/f/", "/e", "/f/"); + TEST_JOIN("/g/h/", "/g/", "/h/"); + + TEST_JOIN("c/d", "c/////", "d"); + TEST_JOIN("e/f", "e", "/////////f"); + TEST_JOIN("g/h", "g///////", "//////h"); + + TEST_JOIN("/", "/", "/"); + TEST_JOIN("/", "/", ""); + TEST_JOIN("/", "/", NULL); + TEST_JOIN("", "", "/"); + TEST_JOIN("", NULL, "/"); +} +END_TEST + +static Suite * +create_suite(void) +{ + Suite *suite; + TCase *core; + + core = tcase_create("join"); + tcase_add_test(core, test_join); + + suite = suite_create("path"); + suite_add_tcase(suite, core); + return suite; +} + +int +main(void) +{ + SRunner *runner; + int failed; + + runner = srunner_create(create_suite()); + srunner_run_all(runner, CK_NORMAL); + failed = srunner_ntests_failed(runner); + srunner_free(runner); + + return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +}