From f8520383849e3f9c014adcf7039d6be37d5f29b6 Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Wed, 27 Nov 2024 19:41:37 -0500
Subject: [PATCH] filesystem: Added SDL_GetCurrentDirectory().
Fixes #11531.
---
include/SDL3/SDL_filesystem.h | 16 +++++++++++
src/dynapi/SDL_dynapi.sym | 1 +
src/dynapi/SDL_dynapi_overrides.h | 1 +
src/dynapi/SDL_dynapi_procs.h | 1 +
src/filesystem/SDL_filesystem.c | 7 +++--
src/filesystem/SDL_sysfilesystem.h | 1 +
src/filesystem/dummy/SDL_sysfilesystem.c | 6 ++++
src/filesystem/posix/SDL_sysfsops.c | 32 ++++++++++++++++++++++
src/filesystem/windows/SDL_sysfilesystem.c | 28 +++++++++++++++++++
test/testfilesystem.c | 10 +++++++
10 files changed, 101 insertions(+), 2 deletions(-)
diff --git a/include/SDL3/SDL_filesystem.h b/include/SDL3/SDL_filesystem.h
index bf403e28ddf01..11a57015770de 100644
--- a/include/SDL3/SDL_filesystem.h
+++ b/include/SDL3/SDL_filesystem.h
@@ -447,6 +447,22 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetPathInfo(const char *path, SDL_PathInfo
*/
extern SDL_DECLSPEC char ** SDLCALL SDL_GlobDirectory(const char *path, const char *pattern, SDL_GlobFlags flags, int *count);
+/**
+ * Get what the system believes is the "current working directory."
+ *
+ * For systems without a concept of a current working directory, this will
+ * still attempt to provide something reasonable.
+ *
+ * SDL does not provide a means to _change_ the current working directory;
+ * for platforms without this concept, this would cause surprises with file
+ * access outside of SDL.
+ *
+ * \returns a UTF-8 string of the current working directory in
+ * platform-dependent notation. NULL if there's a problem. This
+ * should be freed with SDL_free() when it is no longer needed.
+ */
+extern SDL_DECLSPEC char * SDLCALL SDL_GetCurrentDirectory(void);
+
/* Ends C function definitions when using C++ */
#ifdef __cplusplus
}
diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym
index e786d37002e76..c750acc421634 100644
--- a/src/dynapi/SDL_dynapi.sym
+++ b/src/dynapi/SDL_dynapi.sym
@@ -1186,6 +1186,7 @@ SDL3_0.0.0 {
SDL_CancelGPUCommandBuffer;
SDL_SaveFile_IO;
SDL_SaveFile;
+ SDL_GetCurrentDirectory;
# extra symbols go here (don't modify this line)
local: *;
};
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index 215f471510348..cac3f0b7dddd8 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -1211,3 +1211,4 @@
#define SDL_CancelGPUCommandBuffer SDL_CancelGPUCommandBuffer_REAL
#define SDL_SaveFile_IO SDL_SaveFile_IO_REAL
#define SDL_SaveFile SDL_SaveFile_REAL
+#define SDL_GetCurrentDirectory SDL_GetCurrentDirectory_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index 05849f6ec3e33..36a8ad0cf8255 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -1217,3 +1217,4 @@ SDL_DYNAPI_PROC(SDL_Sandbox,SDL_GetSandbox,(void),(),return)
SDL_DYNAPI_PROC(bool,SDL_CancelGPUCommandBuffer,(SDL_GPUCommandBuffer *a),(a),return)
SDL_DYNAPI_PROC(bool,SDL_SaveFile_IO,(SDL_IOStream *a,const void *b,size_t c,bool d),(a,b,c,d),return)
SDL_DYNAPI_PROC(bool,SDL_SaveFile,(const char *a,const void *b,size_t c),(a,b,c),return)
+SDL_DYNAPI_PROC(char*,SDL_GetCurrentDirectory,(void),(),return)
diff --git a/src/filesystem/SDL_filesystem.c b/src/filesystem/SDL_filesystem.c
index d13b6ea8d93b2..46cb74b7f8277 100644
--- a/src/filesystem/SDL_filesystem.c
+++ b/src/filesystem/SDL_filesystem.c
@@ -495,10 +495,13 @@ const char *SDL_GetUserFolder(SDL_Folder folder)
char *SDL_GetPrefPath(const char *org, const char *app)
{
- char *path = SDL_SYS_GetPrefPath(org, app);
- return path;
+ return SDL_SYS_GetPrefPath(org, app);
}
+char *SDL_GetCurrentDirectory(void)
+{
+ return SDL_SYS_GetCurrentDirectory();
+}
void SDL_InitFilesystem(void)
{
diff --git a/src/filesystem/SDL_sysfilesystem.h b/src/filesystem/SDL_sysfilesystem.h
index e0fe9c604bb97..cec031f4c8c7d 100644
--- a/src/filesystem/SDL_sysfilesystem.h
+++ b/src/filesystem/SDL_sysfilesystem.h
@@ -26,6 +26,7 @@
extern char *SDL_SYS_GetBasePath(void);
extern char *SDL_SYS_GetPrefPath(const char *org, const char *app);
extern char *SDL_SYS_GetUserFolder(SDL_Folder folder);
+extern char *SDL_SYS_GetCurrentDirectory(void);
extern bool SDL_SYS_EnumerateDirectory(const char *path, const char *dirname, SDL_EnumerateDirectoryCallback cb, void *userdata);
extern bool SDL_SYS_RemovePath(const char *path);
diff --git a/src/filesystem/dummy/SDL_sysfilesystem.c b/src/filesystem/dummy/SDL_sysfilesystem.c
index 0c74b5986b41d..4aebe95881913 100644
--- a/src/filesystem/dummy/SDL_sysfilesystem.c
+++ b/src/filesystem/dummy/SDL_sysfilesystem.c
@@ -45,4 +45,10 @@ char *SDL_SYS_GetUserFolder(SDL_Folder folder)
return NULL;
}
+char *SDL_SYS_GetCurrentDirectory(void)
+{
+ SDL_Unsupported();
+ return NULL;
+}
+
#endif // SDL_FILESYSTEM_DUMMY || SDL_FILESYSTEM_DISABLED
diff --git a/src/filesystem/posix/SDL_sysfsops.c b/src/filesystem/posix/SDL_sysfsops.c
index db197ed05af72..ec2cc15c6f78a 100644
--- a/src/filesystem/posix/SDL_sysfsops.c
+++ b/src/filesystem/posix/SDL_sysfsops.c
@@ -33,6 +33,7 @@
#include <errno.h>
#include <dirent.h>
#include <sys/stat.h>
+#include <unistd.h>
bool SDL_SYS_EnumerateDirectory(const char *path, const char *dirname, SDL_EnumerateDirectoryCallback cb, void *userdata)
{
@@ -185,5 +186,36 @@ bool SDL_SYS_GetPathInfo(const char *path, SDL_PathInfo *info)
return true;
}
+// Note that this isn't actually part of filesystem, not fsops, but everything that uses posix fsops uses this implementation, even with separate filesystem code.
+char *SDL_SYS_GetCurrentDirectory(void)
+{
+ size_t buflen = 64;
+ char *buf = NULL;
+
+ while (true) {
+ void *ptr = SDL_realloc(buf, buflen);
+ if (!ptr) {
+ SDL_free(buf);
+ return NULL;
+ }
+ buf = (char *) ptr;
+
+ if (getcwd(buf, buflen) != NULL) {
+ break; // we got it!
+ }
+
+ if (errno == ERANGE) {
+ buflen *= 2; // try again with a bigger buffer.
+ continue;
+ }
+
+ SDL_free(buf);
+ SDL_SetError("getcwd failed: %s", strerror(errno));
+ return NULL;
+ }
+
+ return buf;
+}
+
#endif // SDL_FSOPS_POSIX
diff --git a/src/filesystem/windows/SDL_sysfilesystem.c b/src/filesystem/windows/SDL_sysfilesystem.c
index 6db77d455f4b2..3b627adad874c 100644
--- a/src/filesystem/windows/SDL_sysfilesystem.c
+++ b/src/filesystem/windows/SDL_sysfilesystem.c
@@ -345,4 +345,32 @@ char *SDL_SYS_GetUserFolder(SDL_Folder folder)
}
return result;
}
+
+char *SDL_SYS_GetCurrentDirectory(void)
+{
+ WCHAR *wstr = NULL;
+ DWORD buflen = 0;
+ while (true) {
+ const DWORD bw = GetCurrentDirectoryW(buflen, wstr);
+ if (bw == 0) {
+ WIN_SetError("GetCurrentDirectoryW failed");
+ return NULL;
+ } else if (bw < buflen) {
+ break; // we got it!
+ }
+
+ void *ptr = SDL_realloc(wstr, bw * sizeof (WCHAR));
+ if (!ptr) {
+ SDL_free(wstr);
+ return NULL;
+ }
+ wstr = (WCHAR *) ptr;
+ buflen = bw;
+ }
+
+ char *retval = WIN_StringToUTF8W(wstr);
+ SDL_free(wstr);
+ return retval;
+}
+
#endif // SDL_FILESYSTEM_WINDOWS
diff --git a/test/testfilesystem.c b/test/testfilesystem.c
index 3a068a0f2b863..76087998bb6ac 100644
--- a/test/testfilesystem.c
+++ b/test/testfilesystem.c
@@ -62,6 +62,7 @@ int main(int argc, char *argv[])
{
SDLTest_CommonState *state;
char *pref_path;
+ char *curdir;
const char *base_path;
/* Initialize test framework */
@@ -106,6 +107,15 @@ int main(int argc, char *argv[])
}
SDL_free(pref_path);
+ curdir = SDL_GetCurrentDirectory();
+ if (!curdir) {
+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't find current directory: %s\n",
+ SDL_GetError());
+ } else {
+ SDL_Log("current directory: '%s'\n", curdir);
+ }
+ SDL_free(curdir);
+
if (base_path) {
char **globlist;
SDL_IOStream *stream;