SDL: Add trailing path separator to SDL_GetUserFolder()

From b9d3d746a0d71102009db9fea57a73fa6d470750 Mon Sep 17 00:00:00 2001
From: Semphris <[EMAIL REDACTED]>
Date: Tue, 30 Apr 2024 16:57:17 -0400
Subject: [PATCH] Add trailing path separator to SDL_GetUserFolder()

---
 include/SDL3/SDL_filesystem.h                 |  3 +++
 src/filesystem/cocoa/SDL_sysfilesystem.m      |  9 ++++++--
 src/filesystem/emscripten/SDL_sysfilesystem.c | 13 ++++++++++-
 src/filesystem/haiku/SDL_sysfilesystem.cc     | 23 +++++++++++++++----
 src/filesystem/unix/SDL_sysfilesystem.c       | 11 +++++++++
 src/filesystem/windows/SDL_sysfilesystem.c    | 13 +++++++++++
 src/filesystem/winrt/SDL_sysfilesystem.cpp    |  2 ++
 test/testdialog.c                             | 15 ++++++------
 8 files changed, 74 insertions(+), 15 deletions(-)

diff --git a/include/SDL3/SDL_filesystem.h b/include/SDL3/SDL_filesystem.h
index 9cefc75555c7d..6b72c0a87b49e 100644
--- a/include/SDL3/SDL_filesystem.h
+++ b/include/SDL3/SDL_filesystem.h
@@ -224,6 +224,9 @@ typedef enum SDL_Folder
  * Note that the function is expensive, and should be called once at the
  * beginning of the execution and kept for as long as needed.
  *
+ * The returned path is guaranteed to end with a path separator ('\\' on
+ * Windows, '/' on most other platforms).
+ *
  * The returned value is owned by the caller and should be freed with
  * SDL_free().
  *
diff --git a/src/filesystem/cocoa/SDL_sysfilesystem.m b/src/filesystem/cocoa/SDL_sysfilesystem.m
index aef0217d2aca6..fb261d769916f 100644
--- a/src/filesystem/cocoa/SDL_sysfilesystem.m
+++ b/src/filesystem/cocoa/SDL_sysfilesystem.m
@@ -209,11 +209,17 @@
             return NULL;
         }
 
-        retval = SDL_strdup(base);
+        retval = SDL_malloc(SDL_strlen(base) + 2);
         if (retval == NULL) {
             return NULL;
         }
 
+        if (SDL_snprintf(retval, SDL_strlen(base) + 2, "%s/", base) < 0) {
+            SDL_SetError("Couldn't snprintf folder path for Cocoa: %s", base);
+            SDL_free(retval);
+            return NULL;
+        }
+
         for (ptr = retval + 1; *ptr; ptr++) {
             if (*ptr == '/') {
                 *ptr = '\0';
@@ -221,7 +227,6 @@
                 *ptr = '/';
             }
         }
-        mkdir(retval, 0700);
 
         return retval;
 #endif /* SDL_PLATFORM_TVOS */
diff --git a/src/filesystem/emscripten/SDL_sysfilesystem.c b/src/filesystem/emscripten/SDL_sysfilesystem.c
index 69069e937fd4b..b2ae8c529b992 100644
--- a/src/filesystem/emscripten/SDL_sysfilesystem.c
+++ b/src/filesystem/emscripten/SDL_sysfilesystem.c
@@ -97,7 +97,18 @@ char *SDL_GetUserFolder(SDL_Folder folder)
         return NULL;
     }
 
-    return SDL_strdup(home);
+    char *retval = SDL_malloc(SDL_strlen(home) + 2);
+    if (!retval) {
+        return NULL;
+    }
+
+    if (SDL_snprintf(retval, SDL_strlen(home) + 2, "%s/", home) < 0) {
+        SDL_SetError("Couldn't snprintf home path for Emscripten: %s", home);
+        SDL_free(retval);
+        return NULL;
+    }
+
+    return retval;
 }
 
 #endif /* SDL_FILESYSTEM_EMSCRIPTEN */
diff --git a/src/filesystem/haiku/SDL_sysfilesystem.cc b/src/filesystem/haiku/SDL_sysfilesystem.cc
index 893e24a6d28a5..0e91b06b00b51 100644
--- a/src/filesystem/haiku/SDL_sysfilesystem.cc
+++ b/src/filesystem/haiku/SDL_sysfilesystem.cc
@@ -107,15 +107,30 @@ char *SDL_GetUserFolder(SDL_Folder folder)
 
     switch (folder) {
     case SDL_FOLDER_HOME:
-        return SDL_strdup(home);
+        retval = (char *) SDL_malloc(SDL_strlen(home) + 2);
+        if (!retval) {
+            return NULL;
+        }
+
+        if (SDL_snprintf(retval, SDL_strlen(home) + 2, "%s/", home) < 0) {
+            SDL_SetError("Couldn't snprintf home path for Haiku: %s", home);
+            SDL_free(retval);
+            return NULL;
+        }
+
+        return retval;
 
         /* TODO: Is Haiku's desktop folder always ~/Desktop/ ? */
     case SDL_FOLDER_DESKTOP:
         retval = (char *) SDL_malloc(SDL_strlen(home) + 10);
+        if (!retval) {
+            return NULL;
+        }
 
-        if (retval) {
-            SDL_strlcpy(retval, home, SDL_strlen(home) + 10);
-            SDL_strlcat(retval, "/Desktop/", SDL_strlen(home) + 10);
+        if (SDL_snprintf(retval, SDL_strlen(home) + 10, "%s/Desktop/", home) < 0) {
+            SDL_SetError("Couldn't snprintf desktop path for Haiku: %s/Desktop/", home);
+            SDL_free(retval);
+            return NULL;
         }
 
         return retval;
diff --git a/src/filesystem/unix/SDL_sysfilesystem.c b/src/filesystem/unix/SDL_sysfilesystem.c
index 72e061a0d9961..308950edd70a7 100644
--- a/src/filesystem/unix/SDL_sysfilesystem.c
+++ b/src/filesystem/unix/SDL_sysfilesystem.c
@@ -514,6 +514,7 @@ char *SDL_GetUserFolder(SDL_Folder folder)
 {
     const char *param = NULL;
     char *retval;
+    char *newretval;
 
     /* According to `man xdg-user-dir`, the possible values are:
         DESKTOP
@@ -594,6 +595,16 @@ char *SDL_GetUserFolder(SDL_Folder folder)
         return NULL;
     }
 
+    newretval = (char *) SDL_realloc(retval, SDL_strlen(retval) + 2);
+
+    if (!newretval) {
+        SDL_free(retval);
+        return NULL;
+    }
+
+    retval = newretval;
+    SDL_strlcat(retval, "/", SDL_strlen(retval) + 2);
+
     return retval;
 }
 
diff --git a/src/filesystem/windows/SDL_sysfilesystem.c b/src/filesystem/windows/SDL_sysfilesystem.c
index f9e2361610a94..e321b7dc02574 100644
--- a/src/filesystem/windows/SDL_sysfilesystem.c
+++ b/src/filesystem/windows/SDL_sysfilesystem.c
@@ -322,6 +322,19 @@ char *SDL_GetUserFolder(SDL_Folder folder)
         }
     }
 
+    if (retval) {
+        char *newretval = (char *) SDL_realloc(retval, SDL_strlen(retval) + 2);
+
+        if (!newretval) {
+            SDL_free(retval);
+            retval = NULL; /* will be returned */
+            goto done;
+        }
+
+        retval = newretval;
+        SDL_strlcat(retval, "\\", SDL_strlen(retval) + 2);
+    }
+
 done:
     if (lib) {
         FreeLibrary(lib);
diff --git a/src/filesystem/winrt/SDL_sysfilesystem.cpp b/src/filesystem/winrt/SDL_sysfilesystem.cpp
index 0a6fcdc76acd1..f29bc348a7925 100644
--- a/src/filesystem/winrt/SDL_sysfilesystem.cpp
+++ b/src/filesystem/winrt/SDL_sysfilesystem.cpp
@@ -250,6 +250,8 @@ char *SDL_GetUserFolder(SDL_Folder folder)
             return NULL;
     };
 
+    wpath += L"\\";
+
     return WIN_StringToUTF8(wpath.c_str());
 }
 
diff --git a/test/testdialog.c b/test/testdialog.c
index e5492eb663354..378cc6d6ce14b 100644
--- a/test/testdialog.c
+++ b/test/testdialog.c
@@ -54,7 +54,6 @@ int main(int argc, char *argv[]) {
     const SDL_FRect open_folder_rect = { 370, 50, 220, 140 };
     int i;
     char *initial_path = NULL;
-    char path_with_trailing_slash[2048];
 
     /* Initialize test framework */
     state = SDLTest_CommonCreateState(argv, 0);
@@ -94,10 +93,6 @@ int main(int argc, char *argv[]) {
 
     if (!initial_path) {
         SDL_Log("Will not use an initial path, couldn't get the home directory path: %s\n", SDL_GetError());
-        path_with_trailing_slash[0] = '\0';
-    } else {
-        SDL_snprintf(path_with_trailing_slash, sizeof(path_with_trailing_slash), "%s/", initial_path);
-        SDL_free(initial_path);
     }
 
     while (1) {
@@ -119,11 +114,11 @@ int main(int argc, char *argv[]) {
                  * - Nonzero if the user is allowed to choose multiple entries (not for SDL_ShowSaveFileDialog)
                  */
                 if (SDL_PointInRectFloat(&p, &open_file_rect)) {
-                    SDL_ShowOpenFileDialog(callback, NULL, w, filters, path_with_trailing_slash, 1);
+                    SDL_ShowOpenFileDialog(callback, NULL, w, filters, initial_path, 1);
                 } else if (SDL_PointInRectFloat(&p, &open_folder_rect)) {
-                    SDL_ShowOpenFolderDialog(callback, NULL, w, path_with_trailing_slash, 1);
+                    SDL_ShowOpenFolderDialog(callback, NULL, w, initial_path, 1);
                 } else if (SDL_PointInRectFloat(&p, &save_file_rect)) {
-                    SDL_ShowSaveFileDialog(callback, NULL, w, filters, path_with_trailing_slash);
+                    SDL_ShowSaveFileDialog(callback, NULL, w, filters, initial_path);
                 }
             }
         }
@@ -152,6 +147,10 @@ int main(int argc, char *argv[]) {
         SDL_RenderPresent(r);
     }
 
+    if (initial_path) {
+        SDL_free(initial_path);
+    }
+
     SDLTest_CleanupTextDrawing();
     SDL_DestroyRenderer(r);
     SDL_DestroyWindow(w);