From 428f2f35bee9b3abca4afb12b6b8257f10302389 Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Fri, 27 Sep 2024 15:27:55 -0400
Subject: [PATCH] filesystem: SDL_CreateDirectory should make parent
directories.
Fixes #10502.
---
include/SDL3/SDL_filesystem.h | 7 ++++-
src/filesystem/SDL_filesystem.c | 53 +++++++++++++++++++++++++++++++--
2 files changed, 57 insertions(+), 3 deletions(-)
diff --git a/include/SDL3/SDL_filesystem.h b/include/SDL3/SDL_filesystem.h
index 673638540785a..b4a1e5e128aa9 100644
--- a/include/SDL3/SDL_filesystem.h
+++ b/include/SDL3/SDL_filesystem.h
@@ -249,7 +249,12 @@ typedef Uint32 SDL_GlobFlags;
#define SDL_GLOB_CASEINSENSITIVE (1u << 0)
/**
- * Create a directory.
+ * Create a directory, and any missing parent directories.
+ *
+ * This reports success if `path` already exists as a directory.
+ *
+ * If parent directories are missing, it will also create them. Note that
+ * if this fails, it will not remove any parent directories it already made.
*
* \param path the path of the directory to create.
* \returns true on success or false on failure; call SDL_GetError() for more
diff --git a/src/filesystem/SDL_filesystem.c b/src/filesystem/SDL_filesystem.c
index 1ceb007f42ceb..05f184d35965b 100644
--- a/src/filesystem/SDL_filesystem.c
+++ b/src/filesystem/SDL_filesystem.c
@@ -55,11 +55,60 @@ bool SDL_CopyFile(const char *oldpath, const char *newpath)
bool SDL_CreateDirectory(const char *path)
{
- // TODO: Recursively create subdirectories
if (!path) {
return SDL_InvalidParamError("path");
}
- return SDL_SYS_CreateDirectory(path);
+
+ bool retval = SDL_SYS_CreateDirectory(path);
+ if (!retval && *path) { // maybe we're missing parent directories?
+ char *parents = SDL_strdup(path);
+ if (!parents) {
+ return false; // oh well.
+ }
+
+ // in case there was a separator at the end of the path and it was
+ // upsetting something, chop it off.
+ const size_t slen = SDL_strlen(parents);
+ #ifdef SDL_PLATFORM_WINDOWS
+ if ((parents[slen - 1] == '/') || (parents[slen - 1] == '\\'))
+ #else
+ if (parents[slen - 1] == '/')
+ #endif
+ {
+ parents[slen - 1] = '\0';
+ retval = SDL_SYS_CreateDirectory(parents);
+ }
+
+ if (!retval) {
+ for (char *ptr = parents; *ptr; ptr++) {
+ const char ch = *ptr;
+ #ifdef SDL_PLATFORM_WINDOWS
+ const bool issep = (ch == '/') || (ch == '\\');
+ if (issep && ((ptr - parents) == 2) && (parents[1] == ':')) {
+ continue; // it's just the drive letter, skip it.
+ }
+ #else
+ const bool issep = (ch == '/');
+ #endif
+
+ if (issep) {
+ *ptr = '\0';
+ // (this does not fail if the path already exists as a directory.)
+ retval = SDL_SYS_CreateDirectory(parents);
+ if (!retval) { // still failing when making parents? Give up.
+ break;
+ }
+ *ptr = ch;
+ }
+ }
+
+ // last chance: did it work this time?
+ retval = SDL_SYS_CreateDirectory(parents);
+ }
+
+ SDL_free(parents);
+ }
+ return retval;
}
bool SDL_EnumerateDirectory(const char *path, SDL_EnumerateDirectoryCallback callback, void *userdata)