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 - -static int mkdir_r_lvl(const char *const path, int level) -{ - int ret = -1; - char *dir = NULL; - DIR *d = NULL; - - if (!strlen(path)) - goto exit; - - const char *c = path; - int cur_level = 0; - - while (cur_level <= level) - { - for (; *c && *c != '\\' && *c != '/'; c++) - ; - - if (*c) - c++; - - cur_level++; - } - - const size_t sz = c - path + 1; - - dir = malloc(sz); - - if (!dir) - goto exit; - - memcpy(dir, path, sz - 1); - dir[sz - 1] = '\0'; - - d = opendir(dir); - - if (!d && mkdir(dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)) - { - perror("mkdir"); - goto exit; - } - - if (*c) - mkdir_r_lvl(path, cur_level); - - /* No more levels left. */ - ret = 0; - -exit: - if (dir) - free(dir); - - if (d) - closedir(d); - - return ret; -} +#include int mkdir_r(const char *const path) { - return mkdir_r_lvl(path, 0); + int ret = -1; + char *dup = NULL; + + if (!path || !*path) + { + errno = EINVAL; + goto exit; + } + + const size_t sz = strlen(path) + 1; + + dup = malloc(sz); + + if (!dup) + { + errno = ENOMEM; + goto exit; + } + + memcpy(dup, path, sz); + char *c = dup; + const char *dir = dup; + int exit_fl = 0; + + for (c = dup, dir = dup; !exit_fl; c++) + { + 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; + } + } + + ret = 0; + +exit: + if (dup) + free(dup); + + return ret; } 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);