From 9b348ef6e8a63eff49104c8e658d7851b3ec4b02 Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Mon, 24 Nov 2025 15:16:42 -0500
Subject: [PATCH] android: Better parsing of command line arguments.
Reference Issue #14510.
---
src/core/android/SDL_android.c | 75 +++++++++++++++++++---------------
1 file changed, 43 insertions(+), 32 deletions(-)
diff --git a/src/core/android/SDL_android.c b/src/core/android/SDL_android.c
index 7623d8bae01ab..0bd7813e3eb35 100644
--- a/src/core/android/SDL_android.c
+++ b/src/core/android/SDL_android.c
@@ -768,9 +768,6 @@ JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)(JNIEnv *env
checkJNIReady();
}
-// SDL main function prototype
-typedef int (*SDL_main_func)(int argc, char *argv[]);
-
static int run_count = 0;
static bool allow_recreate_activity;
static bool allow_recreate_activity_set;
@@ -840,47 +837,61 @@ JNIEXPORT int JNICALL SDL_JAVA_INTERFACE(nativeRunMain)(JNIEnv *env, jclass cls,
function_name = (*env)->GetStringUTFChars(env, function, NULL);
SDL_main = (SDL_main_func)dlsym(library_handle, function_name);
if (SDL_main) {
- int i;
- int argc;
- int len;
- char **argv;
- bool isstack;
-
- // Prepare the arguments.
- len = (*env)->GetArrayLength(env, array);
- argv = SDL_small_alloc(char *, 1 + len + 1, &isstack); // !!! FIXME: check for NULL
- argc = 0;
- /* Use the name "app_process" so PHYSFS_platformCalcBaseDir() works.
- https://github.com/love2d/love-android/issues/24
- */
- argv[argc++] = SDL_strdup("app_process");
- for (i = 0; i < len; ++i) {
- char *arg = NULL;
+ // Use the name "app_process" for argv[0] so PHYSFS_platformCalcBaseDir() works.
+ // https://github.com/love2d/love-android/issues/24
+ // (note that PhysicsFS hasn't used argv on Android in a long time, but we'll keep this for compat at least for SDL3's lifetime. --ryan.)
+ const char *argv0 = "app_process";
+ const int len = (*env)->GetArrayLength(env, array); // argv elements, not counting argv[0].
+
+ size_t total_alloc_len = (SDL_strlen(argv0) + 1) + ((len + 2) * sizeof (char *)); // len+2 to allocate an array that also holds argv0 and a NULL terminator.
+ for (int i = 0; i < len; ++i) {
+ total_alloc_len++; // null terminator.
jstring string = (*env)->GetObjectArrayElement(env, array, i);
if (string) {
const char *utf = (*env)->GetStringUTFChars(env, string, 0);
if (utf) {
- arg = SDL_strdup(utf);
+ total_alloc_len += SDL_strlen(utf) + 1;
(*env)->ReleaseStringUTFChars(env, string, utf);
}
(*env)->DeleteLocalRef(env, string);
}
- if (arg == NULL) {
- arg = SDL_strdup("");
- }
- argv[argc++] = arg;
}
- argv[argc] = NULL;
- // Run the application.
- status = SDL_main(argc, argv);
+ void *args = malloc(total_alloc_len); // This should NOT be SDL_malloc()
+ if (!args) { // uhoh.
+ __android_log_print(ANDROID_LOG_ERROR, "SDL", "nativeRunMain(): Out of memory parsing command line!");
+ } else {
+ size_t remain = total_alloc_len - (sizeof (char *) * (len + 2));
+ int argc = 0;
+ char **argv = (char **) args;
+ char *ptr = (char *) &argv[len + 2];
+ size_t cpy = SDL_strlcpy(ptr, argv0, remain) + 1;
+ argv[argc++] = ptr;
+ SDL_assert(cpy <= remain); remain -= cpy; ptr += cpy;
+ for (int i = 0; i < len; ++i) {
+ jstring string = (*env)->GetObjectArrayElement(env, array, i);
+ const char *utf = string ? (*env)->GetStringUTFChars(env, string, 0) : NULL;
+ cpy = SDL_strlcpy(ptr, utf ? utf : "", remain) + 1;
+ if (cpy < remain) {
+ argv[argc++] = ptr;
+ remain -= cpy;
+ ptr += cpy;
+ }
+ if (utf) {
+ (*env)->ReleaseStringUTFChars(env, string, utf);
+ }
+ if (string) {
+ (*env)->DeleteLocalRef(env, string);
+ }
+ }
+ argv[argc] = NULL;
- // Release the arguments.
- for (i = 0; i < argc; ++i) {
- SDL_free(argv[i]);
- }
- SDL_small_free(argv, isstack);
+ // Run the application.
+ status = SDL_RunApp(argc, argv, SDL_main, NULL);
+ // Release the arguments.
+ free(args); // This should NOT be SDL_free()
+ }
} else {
__android_log_print(ANDROID_LOG_ERROR, "SDL", "nativeRunMain(): Couldn't find function %s in library %s", function_name, library_file);
}