return path + k;
}
+int path_extract_filename(const char *p, char **ret) {
+ _cleanup_free_ char *a = NULL;
+ const char *c, *e = NULL, *q;
+
+ /* Extracts the filename part (i.e. right-most component) from a path, i.e. string that passes
+ * filename_is_valid(). A wrapper around last_path_component(), but eats up trailing slashes. */
+
+ if (!p)
+ return -EINVAL;
+
+ c = last_path_component(p);
+
+ for (q = c; *q != 0; q++)
+ if (*q != '/')
+ e = q + 1;
+
+ if (!e) /* no valid character? */
+ return -EINVAL;
+
+ a = strndup(c, e - c);
+ if (!a)
+ return -ENOMEM;
+
+ if (!filename_is_valid(a))
+ return -EINVAL;
+
+ *ret = TAKE_PTR(a);
+
+ return 0;
+}
+
bool filename_is_valid(const char *p) {
const char *e;
assert_se(streq(last_path_component("/a/"), "a/"));
}
+static void test_path_extract_filename_one(const char *input, const char *output, int ret) {
+ _cleanup_free_ char *k = NULL;
+ int r;
+
+ r = path_extract_filename(input, &k);
+ log_info("%s → %s/%s [expected: %s/%s]", strnull(input), strnull(k), strerror(-r), strnull(output), strerror(-ret));
+ assert_se(streq_ptr(k, output));
+ assert_se(r == ret);
+}
+
+static void test_path_extract_filename(void) {
+ test_path_extract_filename_one(NULL, NULL, -EINVAL);
+ test_path_extract_filename_one("a/b/c", "c", 0);
+ test_path_extract_filename_one("a/b/c/", "c", 0);
+ test_path_extract_filename_one("/", NULL, -EINVAL);
+ test_path_extract_filename_one("//", NULL, -EINVAL);
+ test_path_extract_filename_one("///", NULL, -EINVAL);
+ test_path_extract_filename_one(".", NULL, -EINVAL);
+ test_path_extract_filename_one("./.", NULL, -EINVAL);
+ test_path_extract_filename_one("././", NULL, -EINVAL);
+ test_path_extract_filename_one("././/", NULL, -EINVAL);
+ test_path_extract_filename_one("/foo/a", "a", 0);
+ test_path_extract_filename_one("/foo/a/", "a", 0);
+ test_path_extract_filename_one("", NULL, -EINVAL);
+ test_path_extract_filename_one("a", "a", 0);
+ test_path_extract_filename_one("a/", "a", 0);
+ test_path_extract_filename_one("/a", "a", 0);
+ test_path_extract_filename_one("/a/", "a", 0);
+ test_path_extract_filename_one("/////////////a/////////////", "a", 0);
+ test_path_extract_filename_one("xx/.", NULL, -EINVAL);
+ test_path_extract_filename_one("xx/..", NULL, -EINVAL);
+ test_path_extract_filename_one("..", NULL, -EINVAL);
+ test_path_extract_filename_one("/..", NULL, -EINVAL);
+ test_path_extract_filename_one("../", NULL, -EINVAL);
+ test_path_extract_filename_one(".", NULL, -EINVAL);
+ test_path_extract_filename_one("/.", NULL, -EINVAL);
+ test_path_extract_filename_one("./", NULL, -EINVAL);
+}
+
static void test_filename_is_valid(void) {
char foo[FILENAME_MAX+2];
int i;
test_prefix_root();
test_file_in_same_dir();
test_last_path_component();
+ test_path_extract_filename();
test_filename_is_valid();
test_hidden_or_backup_file();
test_skip_dev_prefix();