* mkdirp: initial commit
authorUrban Wallasch <urban.wallasch@freenet.de>
Sun, 10 Nov 2019 22:23:20 +0000 (23:23 +0100)
committerUrban Wallasch <urban.wallasch@freenet.de>
Sun, 10 Nov 2019 22:23:20 +0000 (23:23 +0100)
* updated README.html

README.html
mkdirp/mkdirp.c [new file with mode: 0644]
mkdirp/mkdirp.h [new file with mode: 0644]
mkdirp/mkdirp_test.c [new file with mode: 0644]
mkdirp/run_test.sh [new file with mode: 0755]

index edf36669a96593fa7b4f99c288d8874562c9eec7..661cf0eb6f94fb9db4b2597eba2b4835be77c735 100644 (file)
@@ -9,6 +9,13 @@ dict
 standard C dictionary to store data associated with unique string keys
 </td></tr>
 
+<tr><td><a
+href="/?p=oddbits.git;a=tree;f=mkdirp;hb=HEAD"><b>
+mkdirp
+</b></a></td><td>-</td><td>
+create a directory and missing parents
+</td></tr>
+
 <tr><td><a
 href="/?p=oddbits.git;a=tree;f=net;hb=HEAD"><b>
 net
diff --git a/mkdirp/mkdirp.c b/mkdirp/mkdirp.c
new file mode 100644 (file)
index 0000000..502b7f6
--- /dev/null
@@ -0,0 +1,42 @@
+#include <errno.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "mkdirp.h"
+
+
+/* mkdirp - create a directory and missing parents */
+
+int mkdirp(const char *pathname, mode_t mode) {
+    if (!pathname || !*pathname) {
+        errno = ENOENT;
+        return -1;
+    }
+
+    int err = 0;
+    char *p;
+    char path[strlen(pathname) + 1];
+    struct stat sb;
+
+    if (stat(pathname, &sb) == 0 && S_ISDIR(sb.st_mode))
+        return 0;
+
+    mode |= S_IRWXU;
+    strcpy(path, pathname);
+    p = path + 1;
+    do {
+        p = strchr(p, '/');
+        if (p)
+            *p = '\0';
+        if (stat(path, &sb) != 0 || !S_ISDIR(sb.st_mode))
+            err = mkdir(path, mode);
+        if (p)
+            *p++ = '/';
+    } while (!err && p && *p);
+
+    return err;
+}
+
diff --git a/mkdirp/mkdirp.h b/mkdirp/mkdirp.h
new file mode 100644 (file)
index 0000000..9ea4184
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * mkdirp.h
+ *
+ * Copyright (c) 2019, Urban Wallasch
+ * BSD 3-Clause License, see LICENSE file for more details.
+ *
+ * Create a directory and its parents, if they do not already exist.
+ *
+ */
+
+
+#ifndef MKDIRP_H_INCLUDED
+#define MKDIRP_H_INCLUDED
+
+#ifdef cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+
+
+/*
+ * mkdirp - create a directory and missing parents
+ *
+ * The mkdirp() function creates the directory pathname with the specified
+ * mode, if it does not already exist.  If necessary, any missing parent
+ * directories are created as part of the process.  The file mode of each
+ * created directory is ((mode | 0700) & ~umask & 0777).  The modes of
+ * already existing directories are left unchanged.
+ *
+ * The mkdirp() function returns 0, if the specified directory already
+ * exists or was successfully created, or -1 if an error occurred, in
+ * which case errno is set appropriately.
+ */
+extern int mkdirp(const char *pathname, mode_t mode);
+
+
+#ifdef cplusplus
+}
+#endif
+
+#endif /* ndef MKDIRP_H_INCLUDED */
diff --git a/mkdirp/mkdirp_test.c b/mkdirp/mkdirp_test.c
new file mode 100644 (file)
index 0000000..df77419
--- /dev/null
@@ -0,0 +1,45 @@
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "mkdirp.h"
+
+
+int main(void) {
+    struct {
+        const char *d;
+        int m;
+        int expect;
+    } t[] = {
+        { "", 0, -1 },
+        { "/", 0, 0 },
+        { "A_test", 0755, 0 },
+        { "A_test/a", 0644, 0 },
+        { "B_test/", 0, 0 },
+        { "B_test/b/", 0, 0 },
+        { "/C", 0, -1 },
+        { "/D/", 0, -1 },
+        { "C_test/E", 0, 0 },
+        { "C_test/F/", 0, 0 },
+        { "./C_test//////..///C_test//././././././//X/////   ", 0777, 0 },
+        { "/tmp/G_test", 0, 0 },
+        { "/tmp/H_test/", 0, 0 },
+        { "/tmp/I_test/bar/baz/zonk/", 0, 0 },
+        { "/tmp/I_test/bar/baz/borg", 0, 0 },
+        { NULL, 0, -1 }
+    };
+    int i = 0, err;
+
+    do {
+        err = mkdirp(t[i].d, t[i].m);
+        assert( err == t[i].expect );
+        if (err)
+            fprintf(stderr, "mkdir_p(\"%s\", 0%03o) : %s\n", t[i].d, t[i].m, strerror(errno));
+        else
+            fprintf(stderr, "mkdir_p(\"%s\", 0%03o) : Ok\n", t[i].d, t[i].m);
+    } while (t[i++].d);
+
+    return 0;
+}
+
diff --git a/mkdirp/run_test.sh b/mkdirp/run_test.sh
new file mode 100755 (executable)
index 0000000..6d380c6
--- /dev/null
@@ -0,0 +1,9 @@
+#!/bin/bash
+EXEC=mkdirp_test
+SRC="mkdirp.c mkdirp_test.c"
+OPTS="-Wall -Wextra -Werror"
+
+CMD="gcc $OPTS -o $EXEC $SRC"
+echo $CMD
+$CMD && "./$EXEC"
+rm -rf  [ABC]_test  /tmp/[GHI]_test