}
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
*/
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));
/*
- * Copyright (C) 2008-2013 Tobias Brunner
+ * Copyright (C) 2008-2014 Tobias Brunner
* Copyright (C) 2005-2008 Martin Willi
* Hochschule fuer Technik Rapperswil
*
* for more details.
*/
-#include "utils.h"
-
+#define _GNU_SOURCE /* for memrchr */
#include <sys/stat.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include <pthread.h>
+#include "utils.h"
+
#include "collections/enumerator.h"
#include "utils/debug.h"
#include "utils/chunk.h"
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.
*/
/*
- * Copyright (C) 2008-2013 Tobias Brunner
+ * Copyright (C) 2008-2014 Tobias Brunner
* Copyright (C) 2008 Martin Willi
* Hochschule fuer Technik Rapperswil
*
*/
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.
*