path-util: add path_extract_directory(), to match path_extract_filename()

These two together are a lot like dirname() + basename() but have the
benefit that they return clear errors when one passes a special case
path to them where the extraction doesn't make sense, i.e. "", "/",
"foo", "foo/" and so on.

Sooner or later we should probably port all our uses of
dirname()/basename() over to this, to catch these special cases more
safely.
This commit is contained in:
Lennart Poettering
2021-03-02 15:07:11 +01:00
parent 7e40042b55
commit 8dcb891c19
3 changed files with 102 additions and 0 deletions
+42
View File
@@ -846,6 +846,48 @@ int path_extract_filename(const char *p, char **ret) {
return 0;
}
int path_extract_directory(const char *p, char **ret) {
_cleanup_free_ char *a = NULL;
const char *c;
/* The inverse of path_extract_filename(), i.e. returns the directory path prefix. Returns:
*
* -EINVAL → if the passed in path is not a valid path
* -EDESTADDRREQ → if no directory was specified in the passed in path, i.e. only a filename was passed
* -EADDRNOTAVAIL → if the passed in parameter had no filename but did have a directory, i.e. the root dir itself was specified
* -ENOMEM → no memory (surprise!)
*
* This function guarantees to return a fully valid path, i.e. one that passes path_is_valid().
*/
if (!path_is_valid(p))
return -EINVAL;
/* Special case the root dir, because otherwise for an input of "///" last_path_component() returns
* the pointer to the last slash only, which might be seen as a valid path below. */
if (path_equal(p, "/"))
return -EADDRNOTAVAIL;
c = last_path_component(p);
/* Delete trailing slashes, but keep one */
while (c > p+1 && c[-1] == '/')
c--;
if (p == c) /* No path whatsoever? Then return a recognizable error */
return -EDESTADDRREQ;
a = strndup(p, c - p);
if (!a)
return -ENOMEM;
if (!path_is_valid(a))
return -EINVAL;
*ret = TAKE_PTR(a);
return 0;
}
bool filename_is_valid(const char *p) {
const char *e;
+1
View File
@@ -147,6 +147,7 @@ int fsck_exists(const char *fstype);
char* dirname_malloc(const char *path);
const char *last_path_component(const char *path);
int path_extract_filename(const char *p, char **ret);
int path_extract_directory(const char *p, char **ret);
bool filename_is_valid(const char *p) _pure_;
bool path_is_valid(const char *p) _pure_;