diff options
| author | Xavier Del Campo Romero <xavier.delcampo@orain.io> | 2020-09-23 12:23:08 +0200 |
|---|---|---|
| committer | Xavier Del Campo Romero <xavier.delcampo@orain.io> | 2020-09-23 12:29:30 +0200 |
| commit | 3de04d477f7fcf5fb1c5b217c53ef5d6683b5966 (patch) | |
| tree | 50ec69f1c971164b6683b36956da57a8848d6a82 /mkdir_r.c | |
| parent | 09282eefd060f910ca2e70dc7c84788a106c215e (diff) | |
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.
Diffstat (limited to 'mkdir_r.c')
| -rw-r--r-- | mkdir_r.c | 95 |
1 files changed, 54 insertions, 41 deletions
@@ -17,70 +17,83 @@ #include <string.h> #include <stdlib.h> #include <fcntl.h> +#include <unistd.h> #include <sys/stat.h> #include <sys/types.h> #include <dirent.h> #include <errno.h> #include <stdio.h> +#include <errno.h> -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); -} |
