summaryrefslogtreecommitdiff
path: root/lib/util
diff options
context:
space:
mode:
Diffstat (limited to 'lib/util')
-rw-r--r--lib/util/Makemodule.am1
-rw-r--r--lib/util/mkdir_p.c170
2 files changed, 171 insertions, 0 deletions
diff --git a/lib/util/Makemodule.am b/lib/util/Makemodule.am
index 9578fb2..bc3d618 100644
--- a/lib/util/Makemodule.am
+++ b/lib/util/Makemodule.am
@@ -9,6 +9,7 @@ libutil_a_SOURCES += include/util/threadpool.h
libutil_a_SOURCES += include/util/w32threadwrap.h
libutil_a_SOURCES += lib/util/threadpool_serial.c
libutil_a_SOURCES += lib/util/is_memory_zero.c
+libutil_a_SOURCES += lib/util/mkdir_p.c
libutil_a_CFLAGS = $(AM_CFLAGS)
libutil_a_CPPFLAGS = $(AM_CPPFLAGS)
diff --git a/lib/util/mkdir_p.c b/lib/util/mkdir_p.c
new file mode 100644
index 0000000..993d8ec
--- /dev/null
+++ b/lib/util/mkdir_p.c
@@ -0,0 +1,170 @@
+/* SPDX-License-Identifier: LGPL-3.0-or-later */
+/*
+ * mkdir_p.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "util/util.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#ifdef _WIN32
+/*
+ Supported paths:
+ - <letter>:\ <absolute path>
+ - \\<server>\<share>\ <absolute path>
+ - \\?\<letter>:\ <absolute path>
+ - \\?\UNC\<server>\<share>\ <absolute path>
+ - Relative path not starting with '\'
+ */
+
+static WCHAR *skip_unc_path(WCHAR *ptr)
+{
+ /* server */
+ if (*ptr == '\0' || *ptr == '\\')
+ return NULL;
+
+ while (*ptr != '\0' && *ptr != '\\')
+ ++ptr;
+
+ if (*(ptr++) != '\\')
+ return NULL;
+
+ /* share */
+ if (*ptr == '\0' || *ptr == '\\')
+ return NULL;
+
+ while (*ptr != '\0' && *ptr != '\\')
+ ++ptr;
+
+ return (*ptr == '\\') ? (ptr + 1) : ptr;
+}
+
+static WCHAR *skip_prefix(WCHAR *ptr)
+{
+ if (isalpha(ptr[0]) && ptr[1] == ':' && ptr[2] == '\\')
+ return ptr + 3;
+
+ if (ptr[0] == '\\' && ptr[1] == '\\') {
+ if (ptr[2] == '?') {
+ if (ptr[3] != '\\')
+ return NULL;
+
+ ptr += 4;
+
+ if ((ptr[0] == 'u' || ptr[0] == 'U') &&
+ (ptr[1] == 'n' || ptr[1] == 'N') &&
+ (ptr[2] == 'c' || ptr[2] == 'C') &&
+ ptr[3] == '\\') {
+ ptr += 4;
+
+ return skip_unc_path(ptr);
+ }
+
+ if (isalpha(ptr[0]) && ptr[1] == ':' && ptr[2] == '\\')
+ return ptr + 3;
+
+ return NULL;
+ }
+
+ return skip_unc_path(ptr);
+ }
+
+ if (ptr[0] == '\\')
+ return NULL;
+
+ return ptr;
+}
+
+int mkdir_p(const char *path)
+{
+ WCHAR *wpath, *ptr, *end;
+ DWORD error;
+ bool done;
+
+
+ wpath = path_to_windows(path);
+ if (wpath == NULL)
+ return -1;
+
+ ptr = skip_prefix(wpath);
+ if (ptr == NULL) {
+ fprintf(stderr, "Illegal or unsupported path: %s\n", path);
+ goto fail;
+ }
+
+ while (*ptr != '\0') {
+ if (*ptr == '\\') {
+ ++ptr;
+ continue;
+ }
+
+ for (end = ptr; *end != '\0' && *end != '\\'; ++end)
+ ++end;
+
+ if (*end == '\\') {
+ done = false;
+ *end = '\0';
+ } else {
+ done = true;
+ }
+
+ if (!CreateDirectoryW(wpath, NULL)) {
+ error = GetLastError();
+
+ if (error != ERROR_ALREADY_EXISTS) {
+ fprintf(stderr, "Creating %s: %ld\n",
+ path, error);
+ goto fail;
+ }
+ }
+
+ if (!done)
+ *end = '\\';
+
+ ptr = done ? end : (end + 1);
+ }
+
+ free(wpath);
+ return 0;
+fail:
+ free(wpath);
+ return -1;
+}
+#else
+int mkdir_p(const char *path)
+{
+ size_t i, len;
+ char *buffer;
+
+ while (path[0] == '/' && path[1] == '/')
+ ++path;
+
+ if (*path == '\0' || (path[0] == '/' && path[1] == '\0'))
+ return 0;
+
+ len = strlen(path) + 1;
+ buffer = alloca(len);
+
+ for (i = 0; i < len; ++i) {
+ if (i > 0 && (path[i] == '/' || path[i] == '\0')) {
+ buffer[i] = '\0';
+
+ if (mkdir(buffer, 0755) != 0) {
+ if (errno != EEXIST) {
+ fprintf(stderr, "mkdir %s: %s\n",
+ buffer, strerror(errno));
+ return -1;
+ }
+ }
+ }
+
+ buffer[i] = path[i];
+ }
+
+ return 0;
+}
+#endif