]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
path-util: add new path_join_many() API
authorLennart Poettering <lennart@poettering.net>
Thu, 28 Jun 2018 20:28:40 +0000 (22:28 +0200)
committerLennart Poettering <lennart@poettering.net>
Thu, 29 Nov 2018 19:03:56 +0000 (20:03 +0100)
src/basic/path-util.c
src/basic/path-util.h
src/test/test-path-util.c

index b7f91ee3ae60c63beaf225bce0e39c4fa555aa39..ba31c858fd575de8e43752409a4158f768e7487e 100644 (file)
@@ -495,6 +495,70 @@ char* path_join(const char *root, const char *path, const char *rest) {
                                rest && rest[0] == '/' ? rest+1 : rest);
 }
 
+char* path_join_many_internal(const char *first, ...) {
+        char *joined, *q;
+        const char *p;
+        va_list ap;
+        bool slash;
+        size_t sz;
+
+        assert(first);
+
+        /* Joins all listed strings until NULL and places an "/" between them unless the strings end/begin already with
+         * one so that it is unnecessary. Note that "/" which are already duplicate won't be removed. The string
+         * returned is hence always equal or longer than the sum of the lengths of each individual string.
+         *
+         * Note: any listed empty string is simply skipped. This can be useful for concatenating strings of which some
+         * are optional.
+         *
+         * Examples:
+         *
+         * path_join_many("foo", "bar") → "foo/bar"
+         * path_join_many("foo/", "bar") → "foo/bar"
+         * path_join_many("", "foo", "", "bar", "") → "foo/bar" */
+
+        sz = strlen(first);
+        va_start(ap, first);
+        while ((p = va_arg(ap, char*))) {
+
+                if (*p == 0) /* Skip empty items */
+                        continue;
+
+                sz += 1 + strlen(p);
+        }
+        va_end(ap);
+
+        joined = new(char, sz + 1);
+        if (!joined)
+                return NULL;
+
+        if (first[0] != 0) {
+                q = stpcpy(joined, first);
+                slash = endswith(first, "/");
+        } else {
+                /* Skip empty items */
+                joined[0] = 0;
+                q = joined;
+                slash = true; /* no need to generate a slash anymore */
+        }
+
+        va_start(ap, first);
+        while ((p = va_arg(ap, char*))) {
+
+                if (*p == 0) /* Skip empty items */
+                        continue;
+
+                if (!slash && p[0] != '/')
+                        *(q++) = '/';
+
+                q = stpcpy(q, p);
+                slash = endswith(p, "/");
+        }
+        va_end(ap);
+
+        return joined;
+}
+
 int find_binary(const char *name, char **ret) {
         int last_error, r;
         const char *p;
index 53b980d3c166625bbecc0aceaedb8ab5452ad5a6..868d64e17d093a1d68b642f955b632a3a290e3cd 100644 (file)
@@ -50,6 +50,9 @@ int path_compare(const char *a, const char *b) _pure_;
 bool path_equal(const char *a, const char *b) _pure_;
 bool path_equal_or_files_same(const char *a, const char *b, int flags);
 char* path_join(const char *root, const char *path, const char *rest);
+char* path_join_many_internal(const char *first, ...) _sentinel_;
+#define path_join_many(x, ...) path_join_many_internal(x, __VA_ARGS__, NULL)
+
 char* path_simplify(char *path, bool kill_dots);
 
 static inline bool path_equal_ptr(const char *a, const char *b) {
index 7feae54068fce39b1207514157450c9dd9a85a21..e3bcfcd4bfea528cfd40c024801b1c0a2d29dda6 100644 (file)
@@ -567,6 +567,43 @@ static void test_path_startswith_set(void) {
         assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo2/bar", "/foo/quux", "", "/zzz"), NULL));
 }
 
+static void test_path_join_many(void) {
+        char *j;
+
+        assert_se(streq_ptr(j = path_join_many("", NULL), ""));
+        free(j);
+
+        assert_se(streq_ptr(j = path_join_many("foo", NULL), "foo"));
+        free(j);
+
+        assert_se(streq_ptr(j = path_join_many("foo", "bar"), "foo/bar"));
+        free(j);
+
+        assert_se(streq_ptr(j = path_join_many("", "foo", "", "bar", ""), "foo/bar"));
+        free(j);
+
+        assert_se(streq_ptr(j = path_join_many("", "", "", "", "foo", "", "", "", "bar", "", "", ""), "foo/bar"));
+        free(j);
+
+        assert_se(streq_ptr(j = path_join_many("", "/", "", "/foo/", "", "/", "", "/bar/", "", "/", ""), "//foo///bar//"));
+        free(j);
+
+        assert_se(streq_ptr(j = path_join_many("/", "foo", "/", "bar", "/"), "/foo/bar/"));
+        free(j);
+
+        assert_se(streq_ptr(j = path_join_many("foo", "bar", "baz"), "foo/bar/baz"));
+        free(j);
+
+        assert_se(streq_ptr(j = path_join_many("foo/", "bar", "/baz"), "foo/bar/baz"));
+        free(j);
+
+        assert_se(streq_ptr(j = path_join_many("foo/", "/bar/", "/baz"), "foo//bar//baz"));
+        free(j);
+
+        assert_se(streq_ptr(j = path_join_many("//foo/", "///bar/", "///baz//"), "//foo////bar////baz//"));
+        free(j);
+}
+
 int main(int argc, char **argv) {
         test_setup_logging(LOG_DEBUG);
 
@@ -588,6 +625,7 @@ int main(int argc, char **argv) {
         test_skip_dev_prefix();
         test_empty_or_root();
         test_path_startswith_set();
+        test_path_join_many();
 
         test_systemd_installation_has_version(argv[1]); /* NULL is OK */