From 7d29ce8e31dbd063ed61b2aa0ce604677923b362 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Tue, 26 May 2026 15:23:31 -0400 Subject: [PATCH] filesystem: Added SDL_GetExeName(). core/unix had a more-limited copy of filesystem/unix's implementation, called SDL_GetExeName(). Replace that with a real implementation in filesystem, and allow each platform to implement it as appropriate. Implemented for Unix and Windows; most implementations are currently FIXME stubs at the moment. Reference Issue #15692. --- src/SDL_internal.h | 3 + src/SDL_utils.c | 1 - src/core/linux/SDL_fcitx.c | 1 + src/core/unix/SDL_appid.c | 34 +--------- src/core/unix/SDL_appid.h | 1 - src/core/windows/SDL_windows.c | 38 +++++++++++ src/core/windows/SDL_windows.h | 5 +- src/filesystem/SDL_filesystem.c | 18 ++++-- src/filesystem/SDL_sysfilesystem.h | 1 + src/filesystem/android/SDL_sysfilesystem.c | 5 ++ src/filesystem/cocoa/SDL_sysfilesystem.m | 6 ++ src/filesystem/dos/SDL_sysfilesystem.c | 6 ++ src/filesystem/dummy/SDL_sysfilesystem.c | 6 ++ src/filesystem/emscripten/SDL_sysfilesystem.c | 5 ++ src/filesystem/gdk/SDL_sysfilesystem.cpp | 8 +++ src/filesystem/haiku/SDL_sysfilesystem.cc | 5 ++ src/filesystem/n3ds/SDL_sysfilesystem.c | 5 ++ src/filesystem/ngage/SDL_sysfilesystem.c | 6 ++ src/filesystem/ps2/SDL_sysfilesystem.c | 5 ++ src/filesystem/psp/SDL_sysfilesystem.c | 5 ++ src/filesystem/riscos/SDL_sysfilesystem.c | 6 ++ src/filesystem/unix/SDL_sysfilesystem.c | 53 ++++++++++------ src/filesystem/vita/SDL_sysfilesystem.c | 5 ++ src/filesystem/windows/SDL_sysfilesystem.c | 63 +++++++------------ 24 files changed, 191 insertions(+), 100 deletions(-) diff --git a/src/SDL_internal.h b/src/SDL_internal.h index d8c3b4320f..b25e917917 100644 --- a/src/SDL_internal.h +++ b/src/SDL_internal.h @@ -267,6 +267,9 @@ extern "C" { anything calling it without an extremely good reason. */ extern SDL_NORETURN void SDL_ExitProcess(int exitcode); +// Get just the process's binary name, no path. Calculates and caches the string on first call. String lives until SDL_Quit(). This is not a public API right now! +extern const char *SDL_GetExeName(void); + #ifdef HAVE_LIBC #define SDL_abort() abort() #else diff --git a/src/SDL_utils.c b/src/SDL_utils.c index 153803adc1..50bfc8f5d5 100644 --- a/src/SDL_utils.c +++ b/src/SDL_utils.c @@ -625,4 +625,3 @@ void SDL_DebugLogBackend(const char *subsystem, const char *backend) { SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "SDL chose %s backend '%s'", subsystem, backend); } - diff --git a/src/core/linux/SDL_fcitx.c b/src/core/linux/SDL_fcitx.c index d47b44e86f..a13b640db5 100644 --- a/src/core/linux/SDL_fcitx.c +++ b/src/core/linux/SDL_fcitx.c @@ -54,6 +54,7 @@ typedef struct FcitxClient static FcitxClient fcitx_client; +// !!! FIXME: should this just be dumped for src/core/unix's SDL_GetAppID()? static const char *GetAppName(void) { const char *exe_name = SDL_GetExeName(); diff --git a/src/core/unix/SDL_appid.c b/src/core/unix/SDL_appid.c index afe157f2cd..69070b3242 100644 --- a/src/core/unix/SDL_appid.c +++ b/src/core/unix/SDL_appid.c @@ -24,39 +24,6 @@ #include "SDL_appid.h" #include -const char *SDL_GetExeName(void) -{ - static const char *proc_name = NULL; - - // TODO: Use a fallback if BSD has no mounted procfs (OpenBSD has no procfs at all) - if (!proc_name) { -#if defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_FREEBSD) || defined (SDL_PLATFORM_NETBSD) || defined(SDL_PLATFORM_HURD) - static char linkfile[1024]; - int linksize; - -#if defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_HURD) - const char *proc_path = "/proc/self/exe"; -#elif defined(SDL_PLATFORM_FREEBSD) - const char *proc_path = "/proc/curproc/file"; -#elif defined(SDL_PLATFORM_NETBSD) - const char *proc_path = "/proc/curproc/exe"; -#endif - linksize = readlink(proc_path, linkfile, sizeof(linkfile) - 1); - if (linksize > 0) { - linkfile[linksize] = '\0'; - proc_name = SDL_strrchr(linkfile, '/'); - if (proc_name) { - ++proc_name; - } else { - proc_name = linkfile; - } - } -#endif - } - - return proc_name; -} - const char *SDL_GetAppID(void) { const char *id_str = SDL_GetAppMetadataProperty(SDL_PROP_APP_METADATA_IDENTIFIER_STRING); @@ -79,3 +46,4 @@ const char *SDL_GetAppID(void) return id_str; } + diff --git a/src/core/unix/SDL_appid.h b/src/core/unix/SDL_appid.h index 9ed45c31bc..5f8eb932e6 100644 --- a/src/core/unix/SDL_appid.h +++ b/src/core/unix/SDL_appid.h @@ -24,7 +24,6 @@ freely, subject to the following restrictions: #ifndef SDL_appid_h_ #define SDL_appid_h_ -extern const char *SDL_GetExeName(void); extern const char *SDL_GetAppID(void); #endif // SDL_appid_h_ diff --git a/src/core/windows/SDL_windows.c b/src/core/windows/SDL_windows.c index 0be19bd2ec..3f51cbd5fa 100644 --- a/src/core/windows/SDL_windows.c +++ b/src/core/windows/SDL_windows.c @@ -730,4 +730,42 @@ const char *WIN_CheckDefaultArgcArgv(int *pargc, char ***pargv, void **pallocate return NULL; // no error string. } +char *WIN_GetModulePath(HMODULE handle) +{ + DWORD buflen = 128; + WCHAR *path = NULL; + DWORD len = 0; + + while (true) { + void *ptr = SDL_realloc(path, buflen * sizeof(WCHAR)); + if (!ptr) { + SDL_free(path); + return NULL; + } + + path = (WCHAR *)ptr; + + len = GetModuleFileNameW(handle, path, buflen); + // if it truncated, then len >= buflen - 1 + // if there was enough room (or failure), len < buflen - 1 + if (len < (buflen - 1)) { + break; + } + + // buffer too small? Try again. + buflen *= 2; + } + + char *retval = NULL; + if (len == 0) { + WIN_SetError("Couldn't locate module"); + } else { + retval = WIN_StringToUTF8W(path); + } + + SDL_free(path); + + return retval; +} + #endif // defined(SDL_PLATFORM_WINDOWS) diff --git a/src/core/windows/SDL_windows.h b/src/core/windows/SDL_windows.h index 0a7c785493..e102f200c3 100644 --- a/src/core/windows/SDL_windows.h +++ b/src/core/windows/SDL_windows.h @@ -223,7 +223,10 @@ extern SDL_AudioFormat SDL_WaveFormatExToSDLFormat(WAVEFORMATEX *waveformat); extern int WIN_WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWCH lpWideCharStr, int cchWideChar, LPSTR lpMultiByteStr, int cbMultiByte, LPCCH lpDefaultChar, LPBOOL lpUsedDefaultChar); // parse out command lines from OS if argv is NULL, otherwise pass through unchanged. `*pallocated` must be HeapFree'd by caller if not NULL on successful return. Returns NULL on success, error string on problems. -const char *WIN_CheckDefaultArgcArgv(int *pargc, char ***pargv, void **pallocated); +extern const char *WIN_CheckDefaultArgcArgv(int *pargc, char ***pargv, void **pallocated); + +// Does all the win32 tapdancing to make GetModuleFileName work. Returns a SDL_malloc'd UTF-8 string, or NULL on failure. +extern char *WIN_GetModulePath(HMODULE handle); // Ends C function definitions when using C++ #ifdef __cplusplus diff --git a/src/filesystem/SDL_filesystem.c b/src/filesystem/SDL_filesystem.c index bfce6a3292..bde66cdae5 100644 --- a/src/filesystem/SDL_filesystem.c +++ b/src/filesystem/SDL_filesystem.c @@ -518,6 +518,14 @@ const char *SDL_GetUserFolder(SDL_Folder folder) return CachedUserFolders[idx]; } +static char *CachedExeName = NULL; +const char *SDL_GetExeName(void) +{ + if (!CachedExeName) { + CachedExeName = SDL_SYS_GetExeName(); + } + return CachedExeName; +} char *SDL_GetPrefPath(const char *org, const char *app) { @@ -545,10 +553,12 @@ void SDL_InitFilesystem(void) void SDL_QuitFilesystem(void) { - if (CachedBasePath) { - SDL_free(CachedBasePath); - CachedBasePath = NULL; - } + SDL_free(CachedBasePath); + CachedBasePath = NULL; + + SDL_free(CachedExeName); + CachedExeName = NULL; + for (int i = 0; i < SDL_arraysize(CachedUserFolders); i++) { if (CachedUserFolders[i]) { SDL_free(CachedUserFolders[i]); diff --git a/src/filesystem/SDL_sysfilesystem.h b/src/filesystem/SDL_sysfilesystem.h index 660c712480..4f1cf1d5cc 100644 --- a/src/filesystem/SDL_sysfilesystem.h +++ b/src/filesystem/SDL_sysfilesystem.h @@ -24,6 +24,7 @@ // return a string that we can SDL_free(). It will be cached at the higher level. extern char *SDL_SYS_GetBasePath(void); +extern char *SDL_SYS_GetExeName(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); diff --git a/src/filesystem/android/SDL_sysfilesystem.c b/src/filesystem/android/SDL_sysfilesystem.c index 1f50abf165..a6d0708841 100644 --- a/src/filesystem/android/SDL_sysfilesystem.c +++ b/src/filesystem/android/SDL_sysfilesystem.c @@ -34,6 +34,11 @@ char *SDL_SYS_GetBasePath(void) return SDL_strdup("assets://"); } +char *SDL_SYS_GetExeName(void) +{ + return NULL; // !!! FIXME: probably just use the Linux path? +} + char *SDL_SYS_GetPrefPath(const char *org, const char *app) { const char *path = SDL_GetAndroidInternalStoragePath(); diff --git a/src/filesystem/cocoa/SDL_sysfilesystem.m b/src/filesystem/cocoa/SDL_sysfilesystem.m index 26ee7cc102..2995df4e5d 100644 --- a/src/filesystem/cocoa/SDL_sysfilesystem.m +++ b/src/filesystem/cocoa/SDL_sysfilesystem.m @@ -63,6 +63,12 @@ char *SDL_SYS_GetBasePath(void) } } +char *SDL_SYS_GetExeName(void) +{ + SDL_Unsupported(); // !!! FIXME + return NULL; +} + char *SDL_SYS_GetPrefPath(const char *org, const char *app) { @autoreleasepool { diff --git a/src/filesystem/dos/SDL_sysfilesystem.c b/src/filesystem/dos/SDL_sysfilesystem.c index fbc897dae7..244430c111 100644 --- a/src/filesystem/dos/SDL_sysfilesystem.c +++ b/src/filesystem/dos/SDL_sysfilesystem.c @@ -104,6 +104,12 @@ char *SDL_SYS_GetBasePath(void) return retval; } +char *SDL_SYS_GetExeName(void) +{ + SDL_Unsupported(); // !!! FIXME: Move most of SDL_SYS_GetBasePath to a separate function and reuse it here. + return NULL; +} + char *SDL_SYS_GetPrefPath(const char *org, const char *app) { char *result = NULL; diff --git a/src/filesystem/dummy/SDL_sysfilesystem.c b/src/filesystem/dummy/SDL_sysfilesystem.c index 53bc50f7f9..2df8ec4f2c 100644 --- a/src/filesystem/dummy/SDL_sysfilesystem.c +++ b/src/filesystem/dummy/SDL_sysfilesystem.c @@ -33,6 +33,12 @@ char *SDL_SYS_GetBasePath(void) return NULL; } +char *SDL_SYS_GetExeName(void) +{ + SDL_Unsupported(); + return NULL; +} + char *SDL_SYS_GetPrefPath(const char *org, const char *app) { SDL_Unsupported(); diff --git a/src/filesystem/emscripten/SDL_sysfilesystem.c b/src/filesystem/emscripten/SDL_sysfilesystem.c index 3156af5cd5..97f4ac8989 100644 --- a/src/filesystem/emscripten/SDL_sysfilesystem.c +++ b/src/filesystem/emscripten/SDL_sysfilesystem.c @@ -37,6 +37,11 @@ char *SDL_SYS_GetBasePath(void) return SDL_strdup("/"); } +char *SDL_SYS_GetExeName(void) +{ + return NULL; // no EXE name on this system. +} + char *SDL_SYS_GetPrefPath(const char *org, const char *app) { #ifdef SDL_EMSCRIPTEN_PERSISTENT_PATH_STRING diff --git a/src/filesystem/gdk/SDL_sysfilesystem.cpp b/src/filesystem/gdk/SDL_sysfilesystem.cpp index 83abfada54..8554026088 100644 --- a/src/filesystem/gdk/SDL_sysfilesystem.cpp +++ b/src/filesystem/gdk/SDL_sysfilesystem.cpp @@ -38,6 +38,8 @@ SDL_SYS_GetBasePath(void) { /* NOTE: This function is a UTF8 version of the Win32 SDL_GetBasePath()! * The GDK actually _recommends_ the 'A' functions over the 'W' functions :o + * + * !!! FIXME: But can we use WIN_GetModulePath anyhow? (or change WIN_GetModulePath to use GetModuleFileNameA when built for GDK?) */ DWORD buflen = 128; CHAR *path = NULL; @@ -82,6 +84,12 @@ SDL_SYS_GetBasePath(void) return path; } +char *SDL_SYS_GetExeName(void) +{ + SDL_Unsupported(); // !!! FIXME: use WIN_GetModulePath + return NULL; +} + char *SDL_SYS_GetPrefPath(const char *org, const char *app) { XUserHandle user = NULL; diff --git a/src/filesystem/haiku/SDL_sysfilesystem.cc b/src/filesystem/haiku/SDL_sysfilesystem.cc index 48bfb1ed6a..3d00c565b5 100644 --- a/src/filesystem/haiku/SDL_sysfilesystem.cc +++ b/src/filesystem/haiku/SDL_sysfilesystem.cc @@ -64,6 +64,11 @@ char *SDL_SYS_GetBasePath(void) return result; } +char *SDL_SYS_GetExeName(void) +{ + SDL_Unsupported(); // !!! FIXME: Move most of SDL_SYS_GetBasePath to a separate function and reuse it here. + return NULL; +} char *SDL_SYS_GetPrefPath(const char *org, const char *app) { diff --git a/src/filesystem/n3ds/SDL_sysfilesystem.c b/src/filesystem/n3ds/SDL_sysfilesystem.c index 66dba9ddaf..8a129c7194 100644 --- a/src/filesystem/n3ds/SDL_sysfilesystem.c +++ b/src/filesystem/n3ds/SDL_sysfilesystem.c @@ -40,6 +40,11 @@ char *SDL_SYS_GetBasePath(void) return base_path; } +char *SDL_SYS_GetExeName(void) +{ + return NULL; // there isn't an "exe name" on this platform. +} + char *SDL_SYS_GetPrefPath(const char *org, const char *app) { char *pref_path = NULL; diff --git a/src/filesystem/ngage/SDL_sysfilesystem.c b/src/filesystem/ngage/SDL_sysfilesystem.c index a7d5ea8959..73b8ddc0c7 100644 --- a/src/filesystem/ngage/SDL_sysfilesystem.c +++ b/src/filesystem/ngage/SDL_sysfilesystem.c @@ -30,6 +30,12 @@ char *SDL_SYS_GetBasePath(void) return base_path; } +char *SDL_SYS_GetExeName(void) +{ + SDL_Unsupported(); // !!! FIXME: see code in NGAGE_GetAppPath + return NULL; +} + char *SDL_SYS_GetPrefPath(const char *org, const char *app) { char *pref_path = NULL; diff --git a/src/filesystem/ps2/SDL_sysfilesystem.c b/src/filesystem/ps2/SDL_sysfilesystem.c index 87b44c9bf0..ddc3c68399 100644 --- a/src/filesystem/ps2/SDL_sysfilesystem.c +++ b/src/filesystem/ps2/SDL_sysfilesystem.c @@ -46,6 +46,11 @@ char *SDL_SYS_GetBasePath(void) return result; } +char *SDL_SYS_GetExeName(void) +{ + return NULL; // no EXE name on this system. +} + // Do a recursive mkdir of parents folders static void recursive_mkdir(const char *dir) { diff --git a/src/filesystem/psp/SDL_sysfilesystem.c b/src/filesystem/psp/SDL_sysfilesystem.c index 18cd9d95ed..dd828cf523 100644 --- a/src/filesystem/psp/SDL_sysfilesystem.c +++ b/src/filesystem/psp/SDL_sysfilesystem.c @@ -46,6 +46,11 @@ char *SDL_SYS_GetBasePath(void) return result; } +char *SDL_SYS_GetExeName(void) +{ + return NULL; // no EXE name on this system. +} + char *SDL_SYS_GetPrefPath(const char *org, const char *app) { char *result = NULL; diff --git a/src/filesystem/riscos/SDL_sysfilesystem.c b/src/filesystem/riscos/SDL_sysfilesystem.c index d52b22ad43..2a5ac9263f 100644 --- a/src/filesystem/riscos/SDL_sysfilesystem.c +++ b/src/filesystem/riscos/SDL_sysfilesystem.c @@ -152,6 +152,12 @@ char *SDL_SYS_GetBasePath(void) return result; } +char *SDL_SYS_GetExeName(void) +{ + SDL_Unsupported(); // !!! FIXME: see code in SDL_SYS_GetBasePath. + return NULL; +} + char *SDL_SYS_GetPrefPath(const char *org, const char *app) { char *canon, *dir, *result; diff --git a/src/filesystem/unix/SDL_sysfilesystem.c b/src/filesystem/unix/SDL_sysfilesystem.c index 9cba8db2eb..8c9259b5f1 100644 --- a/src/filesystem/unix/SDL_sysfilesystem.c +++ b/src/filesystem/unix/SDL_sysfilesystem.c @@ -122,7 +122,7 @@ static char *search_path_for_binary(const char *bin) } #endif -char *SDL_SYS_GetBasePath(void) +static char *GetExePath(void) { char *result = NULL; @@ -235,27 +235,42 @@ char *SDL_SYS_GetBasePath(void) /* If we had access to argv[0] here, we could check it for a path, or troll through $PATH looking for it, too. */ - if (result) { // chop off filename. - char *ptr = SDL_strrchr(result, '/'); - if (ptr) { - *(ptr + 1) = '\0'; - } else { // shouldn't happen, but just in case... - SDL_free(result); - result = NULL; - } - } - - if (result) { - // try to shrink buffer... - char *ptr = (char *)SDL_realloc(result, SDL_strlen(result) + 1); - if (ptr) { - result = ptr; // oh well if it failed. - } - } - return result; } +char *SDL_SYS_GetBasePath(void) +{ + char *path = GetExePath(); + if (!path) { + return NULL; + } + + char *ptr = SDL_strrchr(path, '/'); + SDL_assert(ptr != NULL); // Should have been an absolute path. + + ptr[1] = '\0'; // chop off filename, leave '/'. + + ptr = (char *) SDL_realloc(path, ((size_t) (ptr - path)) + 2); // try to shrink this allocation down a little. + return ptr ? ptr : path; // return shrunk buffer if shrink worked out, unchanged original buffer if not. +} + + +char *SDL_SYS_GetExeName(void) +{ + char *path = GetExePath(); + if (!path) { + return NULL; + } + + char *ptr = SDL_strrchr(path, '/'); + SDL_assert(ptr != NULL); // Should have been an absolute path. + const size_t slen = SDL_strlen(ptr); // counts null terminator because we're still sitting on path separator. + SDL_memmove(path, ptr + 1, slen); // move filename string to start of SDL_realloc'd region. + ptr = (char *) SDL_realloc(path, slen); // try to shrink this allocation down a little. + return ptr ? ptr : path; // return shrunk buffer if shrink worked out, unchanged original buffer if not. +} + + char *SDL_SYS_GetPrefPath(const char *org, const char *app) { /* diff --git a/src/filesystem/vita/SDL_sysfilesystem.c b/src/filesystem/vita/SDL_sysfilesystem.c index a23fcdcad6..4910000acb 100644 --- a/src/filesystem/vita/SDL_sysfilesystem.c +++ b/src/filesystem/vita/SDL_sysfilesystem.c @@ -41,6 +41,11 @@ char *SDL_SYS_GetBasePath(void) return SDL_strdup("app0:/"); } +char *SDL_SYS_GetExeName(void) +{ + return NULL; // no EXE name on this system. +} + char *SDL_SYS_GetPrefPath(const char *org, const char *app) { const char *envr = "ux0:/data/"; diff --git a/src/filesystem/windows/SDL_sysfilesystem.c b/src/filesystem/windows/SDL_sysfilesystem.c index 48b6d1d68f..bc397a1117 100644 --- a/src/filesystem/windows/SDL_sysfilesystem.c +++ b/src/filesystem/windows/SDL_sysfilesystem.c @@ -45,51 +45,32 @@ DEFINE_GUID(SDL_FOLDERID_Videos, 0x18989B1D, 0x99B5, 0x455B, 0x84, 0x1C, 0xAB, 0 char *SDL_SYS_GetBasePath(void) { - DWORD buflen = 128; - WCHAR *path = NULL; - char *result = NULL; - DWORD len = 0; - int i; - - while (true) { - void *ptr = SDL_realloc(path, buflen * sizeof(WCHAR)); - if (!ptr) { - SDL_free(path); - return NULL; - } - - path = (WCHAR *)ptr; - - len = GetModuleFileNameW(NULL, path, buflen); - // if it truncated, then len >= buflen - 1 - // if there was enough room (or failure), len < buflen - 1 - if (len < buflen - 1) { - break; - } - - // buffer too small? Try again. - buflen *= 2; + char *path = WIN_GetModulePath(NULL); // look up full path of the current process's EXE file. + if (!path) { + return NULL; // error message was already set. } - if (len == 0) { - SDL_free(path); - WIN_SetError("Couldn't locate our .exe"); - return NULL; + char *ptr = SDL_strrchr(path, '\\'); + SDL_assert(ptr != NULL); // Should have been an absolute path. + + ptr[1] = '\0'; // chop off filename, leave '\\'. + + ptr = (char *) SDL_realloc(path, ((size_t) (ptr - path)) + 2); // try to shrink this allocation down a little. + return ptr ? ptr : path; // return shrunk buffer if shrink worked out, unchanged original buffer if not. +} + +char *SDL_SYS_GetExeName(void) +{ + char *path = WIN_GetModulePath(NULL); // look up full path of the current process's EXE file. + if (!path) { + return NULL; // error message was already set. } - for (i = len - 1; i > 0; i--) { - if (path[i] == '\\') { - break; - } - } - - SDL_assert(i > 0); // Should have been an absolute path. - path[i + 1] = '\0'; // chop off filename. - - result = WIN_StringToUTF8W(path); - SDL_free(path); - - return result; + char *ptr = SDL_strrchr(path, '\\'); + const size_t slen = SDL_strlen(ptr); // counts null terminator because we're still sitting on path separator. + SDL_memmove(path, ptr + 1, slen); // move filename string to start of SDL_realloc'd region. + ptr = (char *) SDL_realloc(path, slen); // try to shrink this allocation down a little. + return ptr ? ptr : path; // return shrunk buffer if shrink worked out, unchanged original buffer if not. } char *SDL_SYS_GetPrefPath(const char *org, const char *app)