utils: add a safe version of basename
authorPeter Hutterer <peter.hutterer@who-t.net>
Thu, 18 Feb 2021 03:43:23 +0000 (13:43 +1000)
committerPeter Hutterer <peter.hutterer@who-t.net>
Tue, 23 Feb 2021 00:56:53 +0000 (10:56 +1000)
So we don't need to worry about the libgen.h include game.
And we can switch trunkname over to that, making it a bit simpler.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
src/util-strings.c
src/util-strings.h
test/test-utils.c

index 3e174806a12a2fcae5b7f62b0ec64cb571a24b01..7d4c4628fa6255b0ef5e2d2b22dad88960a2641a 100644 (file)
@@ -156,6 +156,30 @@ strv_join(char **strv, const char *joiner)
        return str;
 }
 
+/**
+ * Return a pointer to the basename within filename.
+ * If the filename the empty string or a directory (i.e. the last char of
+ * filename is '/') NULL is returned.
+ */
+const char *
+safe_basename(const char *filename)
+{
+       const char *basename;
+
+       if (*filename == '\0')
+               return NULL;
+
+       basename = strrchr(filename, '/');
+       if (basename == NULL)
+               return filename;
+
+       if (*(basename + 1) == '\0')
+               return NULL;
+
+       return basename + 1;
+}
+
+
 /**
  * Similar to basename() but returns the trunk only without the (last)
  * trailing suffix, so that:
@@ -170,19 +194,15 @@ strv_join(char **strv, const char *joiner)
 char *
 trunkname(const char *filename)
 {
-       /* See basename(3), there are two versions and they depend on
-        * whether libgen.h is included. We can't be sure which basename()
-        * applies here, so let's play it safe and assume it's the POSIX
-        * one. */
-       char *tmp = strdup(filename);
-       char *base = basename(tmp);
+       const char *base = safe_basename(filename);
        char *suffix;
-       char *trunk;
 
-       if ((suffix = rindex(base, '.')))
-           *suffix = '\0';
+       if (base == NULL)
+               return strdup("");
 
-       trunk = strdup(base);
-       free(tmp);
-       return trunk;
+       suffix = rindex(base, '.');
+       if (suffix == NULL)
+               return strdup(base);
+       else
+               return strndup(base, suffix-base);
 }
index ad007f6963f8bb2d0b87f8255469fa62d7d267bf..9d044067031d3235b7713909a4637992f120736e 100644 (file)
@@ -392,5 +392,8 @@ strstartswith(const char *str, const char *prefix)
        return prefixlen > 0 ? strneq(str, prefix, strlen(prefix)) : false;
 }
 
+const char *
+safe_basename(const char *filename);
+
 char *
 trunkname(const char *filename);
index f31b2e07c7f0e828286662680a8580273b7cd3d1..dbd5803174a22098e92193d8af4ad1514a7defb1 100644 (file)
@@ -1292,6 +1292,35 @@ START_TEST(strneq_test)
 }
 END_TEST
 
+START_TEST(basename_test)
+{
+       struct test {
+               const char *path;
+               const char *expected;
+       } tests[] = {
+               { "a", "a" },
+               { "foo.c", "foo.c" },
+               { "foo", "foo" },
+               { "/path/to/foo.h", "foo.h" },
+               { "../bar.foo", "bar.foo" },
+               { "./bar.foo.baz", "bar.foo.baz" },
+               { "./", NULL },
+               { "/", NULL },
+               { "/bar/", NULL },
+               { "/bar", "bar" },
+               { "", NULL },
+       };
+       struct test *t;
+
+       ARRAY_FOR_EACH(tests, t) {
+               const char *result = safe_basename(t->path);
+               if (t->expected == NULL)
+                       ck_assert(result == NULL);
+               else
+                       ck_assert_str_eq(result, t->expected);
+       }
+}
+END_TEST
 START_TEST(trunkname_test)
 {
        struct test {
@@ -1300,6 +1329,7 @@ START_TEST(trunkname_test)
        } tests[] = {
                { "foo.c", "foo" },
                { "/path/to/foo.h", "foo" },
+               { "/path/to/foo", "foo" },
                { "../bar.foo", "bar" },
                { "./bar.foo.baz", "bar.foo" },
                { "./", "" },
@@ -1362,6 +1392,7 @@ litest_utils_suite(void)
        tcase_add_test(tc, streq_test);
        tcase_add_test(tc, strneq_test);
        tcase_add_test(tc, trunkname_test);
+       tcase_add_test(tc, basename_test);
 
        suite_add_tcase(s, tc);