diff --git a/src/mkdirp.c b/src/mkdirp.c new file mode 100644 index 0000000..7deed44 --- /dev/null +++ b/src/mkdirp.c @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include +#include + +int uc_mkdir_p(const char *full_path, mode_t mode) +{ + struct stat st; + int rc = 0; + char *path; + char *p; + + if (full_path == NULL || full_path[0] == 0) { + + errno = EINVAL; + return -1; + } + + rc = stat(full_path, &st); + if (rc == 0) { //already exists + if (!S_ISDIR(st.st_mode)) { + errno = ENOTDIR; + return -1; + } + if (st.st_mode != (mode | S_IFDIR) && chmod(full_path, mode) != 0) { + return -1; + } + } + + path = strdup(full_path); + if (path == NULL) { + return -1; + } + + //skip leading / + for (p = path; *p == '/'; p++) + ; + + for (;;) { + p = strchr(p, '/'); + if (p == NULL) { + break; + } + + *p = 0; + + rc = stat(path, &st); + if (rc != 0) { //doesn't exist + if (mkdir(path, mode) != 0) { + free(path); + return -1; + } + } else if (!S_ISDIR(st.st_mode)) { + errno = ENOTDIR; + free(path); + return -1; + } else { //dir already exist + + if (st.st_mode != (mode | S_IFDIR) && chmod(path, mode) != 0) { + free(path); + return -1; + } + } + + //in case of multiple / , e.g. foo//bar + *p = '/'; + for (; *p == '/'; p++) + ; + } + + //last part, in case full_path does not end with a / + rc = stat(path, &st); + if (rc != 0) { + rc = mkdir(path, mode); + } + + free(path); + + return rc; +} + + +int main(int argc, char *argv[]) +{ + uc_mkdir_p("a/b//c/d", 0777); +}