diff options
| -rw-r--r-- | mkdir_r.c | 95 | ||||
| -rw-r--r-- | mkdir_r.h | 5 |
2 files changed, 59 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); -} @@ -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); |
