From 3de04d477f7fcf5fb1c5b217c53ef5d6683b5966 Mon Sep 17 00:00:00 2001 From: Xavier Del Campo Romero Date: Wed, 23 Sep 2020 12:23:08 +0200 Subject: Move away from recursion-based algorithm Previous implementation could not be realiable for embedded applications since it used recursion and allocated a buffer for each folder in the tree. Now, one single allocation is made, duplicating the original string so it can be tokenized. A check which ensures each element from the tree is a directory has also been added. On the other hand, errno is now set when standard functions don't. --- mkdir_r.c | 95 ++++++++++++++++++++++++++++++++++++--------------------------- mkdir_r.h | 5 ++++ 2 files changed, 59 insertions(+), 41 deletions(-) diff --git a/mkdir_r.c b/mkdir_r.c index 178a250..b759e05 100644 --- a/mkdir_r.c +++ b/mkdir_r.c @@ -17,70 +17,83 @@ #include #include #include +#include #include #include #include #include #include +#include -static int mkdir_r_lvl(const char *const path, int level) +int mkdir_r(const char *const path) { int ret = -1; - char *dir = NULL; - DIR *d = NULL; - - if (!strlen(path)) - goto exit; - - const char *c = path; - int cur_level = 0; + char *dup = NULL; - while (cur_level <= level) + if (!path || !*path) { - for (; *c && *c != '\\' && *c != '/'; c++) - ; - - if (*c) - c++; - - cur_level++; + errno = EINVAL; + goto exit; } - const size_t sz = c - path + 1; + const size_t sz = strlen(path) + 1; - dir = malloc(sz); + dup = malloc(sz); - if (!dir) + if (!dup) + { + errno = ENOMEM; goto exit; + } - memcpy(dir, path, sz - 1); - dir[sz - 1] = '\0'; - - d = opendir(dir); + memcpy(dup, path, sz); + char *c = dup; + const char *dir = dup; + int exit_fl = 0; - if (!d && mkdir(dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)) + for (c = dup, dir = dup; !exit_fl; c++) { - perror("mkdir"); - goto exit; + switch (*c) + { + case '\0': + exit_fl = 1; + /* Fall through. */ + case '/': + { + const char cb = *c; + + *c = '\0'; + + if (!*dir) + /* Path starting with delimiter character. */ + ; + else if (!access(dir, 0)) + { + struct stat sb; + + if (stat(dir, &sb)) + goto exit; + else if (!(sb.st_mode & S_IFDIR)) + { + errno = ENOTDIR; + goto exit; + } + } + else if (mkdir(dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)) + goto exit; + + dir = dup; + *c = cb; + } + break; + } } - if (*c) - mkdir_r_lvl(path, cur_level); - - /* No more levels left. */ ret = 0; exit: - if (dir) - free(dir); - - if (d) - closedir(d); + if (dup) + free(dup); return ret; } - -int mkdir_r(const char *const path) -{ - return mkdir_r_lvl(path, 0); -} diff --git a/mkdir_r.h b/mkdir_r.h index 5e36f73..fb40373 100644 --- a/mkdir_r.h +++ b/mkdir_r.h @@ -20,6 +20,11 @@ * Recursive directory creation. * @param path Directory path * @return 0 if successful, -1 otherwise. + * @note Sets @c errno to @c ENOMEM if internal dynamic allocation fails. + * @note Sets @c errno to @c EINVAL if an invalid or empty directory path is + * given. + * @note Sets @c errno to @c ENOTDIR if one of the elements from @c path is not + * a directory. */ int mkdir_r(const char *const path); -- cgit v1.2.3