path-util: add helper for extracting last filename from path

This commit is contained in:
Lennart Poettering 2018-10-26 16:07:35 +02:00
parent 77e0a1b5e0
commit a60c8eee10
3 changed files with 72 additions and 0 deletions

View File

@ -769,6 +769,37 @@ const char *last_path_component(const char *path) {
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;

View File

@ -133,6 +133,7 @@ int parse_path_argument_and_warn(const char *path, bool suppress_root, char **ar
char* dirname_malloc(const char *path);
const char *last_path_component(const char *path);
int path_extract_filename(const char *p, char **ret);
bool filename_is_valid(const char *p) _pure_;
bool path_is_valid(const char *p) _pure_;

View File

@ -425,6 +425,45 @@ static void test_last_path_component(void) {
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;
@ -543,6 +582,7 @@ int main(int argc, char **argv) {
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();