From 766141bc7735e14e85d3e06854645e6b66877676 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 21 Feb 2014 18:44:15 +0100 Subject: [PATCH] utils: Add thread-safe variants of dirname(3) and basename(3) --- src/libstrongswan/tests/suites/test_utils.c | 56 ++++++++++++++++++ src/libstrongswan/utils/utils.c | 64 ++++++++++++++++++++- src/libstrongswan/utils/utils.h | 28 ++++++++- 3 files changed, 144 insertions(+), 4 deletions(-) diff --git a/src/libstrongswan/tests/suites/test_utils.c b/src/libstrongswan/tests/suites/test_utils.c index 2d290f81e..92f65596a 100644 --- a/src/libstrongswan/tests/suites/test_utils.c +++ b/src/libstrongswan/tests/suites/test_utils.c @@ -467,6 +467,57 @@ START_TEST(test_strreplace) } END_TEST +/******************************************************************************* + * path_dirname/basename + */ + +static struct { + char *path; + char *dir; + char *base; +} path_data[] = { + {NULL, ".", "."}, + {"", ".", "."}, + {".", ".", "."}, + {"..", ".", ".."}, + {"/", "/", "/"}, + {"//", "/", "/"}, + {"foo", ".", "foo"}, + {"f/", ".", "f"}, + {"foo/", ".", "foo"}, + {"foo//", ".", "foo"}, + {"/f", "/", "f"}, + {"/f/", "/", "f"}, + {"/foo", "/", "foo"}, + {"/foo/", "/", "foo"}, + {"//foo/", "/", "foo"}, + {"foo/bar", "foo", "bar"}, + {"foo//bar", "foo", "bar"}, + {"/foo/bar", "/foo", "bar"}, + {"/foo/bar/", "/foo", "bar"}, + {"/foo/bar/baz", "/foo/bar", "baz"}, +}; + +START_TEST(test_path_dirname) +{ + char *dir; + + dir = path_dirname(path_data[_i].path); + ck_assert_str_eq(path_data[_i].dir, dir); + free(dir); +} +END_TEST + +START_TEST(test_path_basename) +{ + char *base; + + base = path_basename(path_data[_i].path); + ck_assert_str_eq(path_data[_i].base, base); + free(base); +} +END_TEST + /******************************************************************************* * time_printf_hook */ @@ -630,6 +681,11 @@ Suite *utils_suite_create() tcase_add_loop_test(tc, test_strreplace, 0, countof(strreplace_data)); suite_add_tcase(s, tc); + tc = tcase_create("path_dirname/basename"); + tcase_add_loop_test(tc, test_path_dirname, 0, countof(path_data)); + tcase_add_loop_test(tc, test_path_basename, 0, countof(path_data)); + suite_add_tcase(s, tc); + tc = tcase_create("printf_hooks"); tcase_add_loop_test(tc, test_time_printf_hook, 0, countof(time_data)); tcase_add_loop_test(tc, test_time_delta_printf_hook, 0, countof(time_delta_data)); diff --git a/src/libstrongswan/utils/utils.c b/src/libstrongswan/utils/utils.c index e9f8b1892..406c5c811 100644 --- a/src/libstrongswan/utils/utils.c +++ b/src/libstrongswan/utils/utils.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2013 Tobias Brunner + * Copyright (C) 2008-2014 Tobias Brunner * Copyright (C) 2005-2008 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -14,8 +14,7 @@ * for more details. */ -#include "utils.h" - +#define _GNU_SOURCE /* for memrchr */ #include #include #include @@ -27,6 +26,8 @@ #include #include +#include "utils.h" + #include "collections/enumerator.h" #include "utils/debug.h" #include "utils/chunk.h" @@ -193,6 +194,63 @@ char* strreplace(const char *str, const char *search, const char *replace) return res; } +/** + * Described in header. + */ +char* path_dirname(const char *path) +{ + char *pos; + + pos = path ? strrchr(path, '/') : NULL; + + if (pos && !pos[1]) + { /* if path ends with slashes we have to look beyond them */ + while (pos > path && *pos == '/') + { /* skip trailing slashes */ + pos--; + } + pos = memrchr(path, '/', pos - path + 1); + } + if (!pos) + { + return strdup("."); + } + while (pos > path && *pos == '/') + { /* skip superfluous slashes */ + pos--; + } + return strndup(path, pos - path + 1); +} + +/** + * Described in header. + */ +char* path_basename(const char *path) +{ + char *pos, *trail = NULL; + + if (!path || !*path) + { + return strdup("."); + } + pos = strrchr(path, '/'); + if (pos && !pos[1]) + { /* if path ends with slashes we have to look beyond them */ + while (pos > path && *pos == '/') + { /* skip trailing slashes */ + pos--; + } + if (pos == path && *pos == '/') + { /* contains only slashes */ + return strdup("/"); + } + trail = pos + 1; + pos = memrchr(path, '/', trail - path); + } + pos = pos ? pos + 1 : (char*)path; + return trail ? strndup(pos, trail - pos) : strdup(pos); +} + /** * Described in header. */ diff --git a/src/libstrongswan/utils/utils.h b/src/libstrongswan/utils/utils.h index 0b26ba534..4c113900e 100644 --- a/src/libstrongswan/utils/utils.h +++ b/src/libstrongswan/utils/utils.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2013 Tobias Brunner + * Copyright (C) 2008-2014 Tobias Brunner * Copyright (C) 2008 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -486,6 +486,32 @@ char *translate(char *str, const char *from, const char *to); */ char *strreplace(const char *str, const char *search, const char *replace); +/** + * Like dirname(3) returns the directory part of the given null-terminated + * pathname, up to but not including the final '/' (or '.' if no '/' is found). + * Trailing '/' are not counted as part of the pathname. + * + * The difference is that it does this in a thread-safe manner (i.e. it does not + * use static buffers) and does not modify the original path. + * + * @param path original pathname + * @return allocated directory component + */ +char *path_dirname(const char *path); + +/** + * Like basename(3) returns the filename part of the given null-terminated path, + * i.e. the part following the final '/' (or '.' if path is empty or NULL). + * Trailing '/' are not counted as part of the pathname. + * + * The difference is that it does this in a thread-safe manner (i.e. it does not + * use static buffers) and does not modify the original path. + * + * @param path original pathname + * @return allocated filename component + */ +char *path_basename(const char *path); + /** * Creates a directory and all required parent directories. *