summaryrefslogtreecommitdiff
path: root/mkdir_r.c
diff options
context:
space:
mode:
authorXavier Del Campo Romero <xavier.delcampo@orain.io>2020-09-23 12:23:08 +0200
committerXavier Del Campo Romero <xavier.delcampo@orain.io>2020-09-23 12:29:30 +0200
commit3de04d477f7fcf5fb1c5b217c53ef5d6683b5966 (patch)
tree50ec69f1c971164b6683b36956da57a8848d6a82 /mkdir_r.c
parent09282eefd060f910ca2e70dc7c84788a106c215e (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.c95
1 files changed, 54 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 <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);
-}