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 #include #include +#include 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;