SDL: stdlib: Use new parser for scanf %p specifier

From 61bc856b043fedafa542a1a44dfc4e9af630f1cd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Carl=20=C3=85stholm?= <[EMAIL REDACTED]>
Date: Thu, 12 Sep 2024 00:26:36 +0200
Subject: [PATCH] stdlib: Use new parser for scanf %p specifier

---
 src/stdlib/SDL_string.c      | 45 +++++++++++++-----------------------
 test/testautomation_stdlib.c | 10 ++++++++
 2 files changed, 26 insertions(+), 29 deletions(-)

diff --git a/src/stdlib/SDL_string.c b/src/stdlib/SDL_string.c
index 75239d77263fc..355fce16f87de 100644
--- a/src/stdlib/SDL_string.c
+++ b/src/stdlib/SDL_string.c
@@ -305,11 +305,6 @@ static Uint32 StepUTF32(const Uint32 **_str, const size_t slen)
 }
 #endif
 
-#if !defined(HAVE_VSSCANF) || !defined(HAVE_STRTOL) || !defined(HAVE_WCSTOL) || !defined(HAVE_STRTOUL) || !defined(HAVE_STRTOD) || !defined(HAVE_STRTOLL) || !defined(HAVE_STRTOULL)
-#define SDL_isupperhex(X) (((X) >= 'A') && ((X) <= 'F'))
-#define SDL_islowerhex(X) (((X) >= 'a') && ((X) <= 'f'))
-#endif
-
 #define UTF8_IsLeadByte(c)     ((c) >= 0xC0 && (c) <= 0xF4)
 #define UTF8_IsTrailingByte(c) ((c) >= 0x80 && (c) <= 0xBF)
 
@@ -534,33 +529,25 @@ static size_t SDL_ScanUnsignedLong(const char *text, int count, int radix, unsig
 #endif
 
 #ifndef HAVE_VSSCANF
-static size_t SDL_ScanUintPtrT(const char *text, int radix, uintptr_t *valuep)
+static size_t SDL_ScanUintPtrT(const char *text, uintptr_t *valuep)
 {
-    const char *textstart = text;
-    uintptr_t value = 0;
-
-    if (radix == 16 && SDL_strncmp(text, "0x", 2) == 0) {
-        text += 2;
-    }
-    for (;;) {
-        int v;
-        if (SDL_isdigit((unsigned char)*text)) {
-            v = *text - '0';
-        } else if (radix == 16 && SDL_isupperhex(*text)) {
-            v = 10 + (*text - 'A');
-        } else if (radix == 16 && SDL_islowerhex(*text)) {
-            v = 10 + (*text - 'a');
+    const uintptr_t uintptr_max = ~(uintptr_t)0;
+    unsigned long long value;
+    bool negative;
+    size_t len = SDL_ScanUnsignedLongLongInternal(text, 0, 16, &value, &negative);
+    if (negative) {
+        if (value == 0 || value > uintptr_max) {
+            value = uintptr_max;
+        } else if (value == uintptr_max) {
+            value = 1;
         } else {
-            break;
+            value = 0ULL - value;
         }
-        value *= radix;
-        value += v;
-        ++text;
-    }
-    if (valuep && text > textstart) {
-        *valuep = value;
+    } else if (value > uintptr_max) {
+        value = uintptr_max;
     }
-    return text - textstart;
+    *valuep = value;
+    return len;
 }
 #endif
 
@@ -1616,7 +1603,7 @@ int SDL_vsscanf(const char *text, SDL_SCANF_FORMAT_STRING const char *fmt, va_li
                 case 'p':
                 {
                     uintptr_t value = 0;
-                    advance = SDL_ScanUintPtrT(text, 16, &value);
+                    advance = SDL_ScanUintPtrT(text, &value);
                     text += advance;
                     if (advance && !suppress) {
                         void **valuep = va_arg(ap, void **);
diff --git a/test/testautomation_stdlib.c b/test/testautomation_stdlib.c
index 0f5f5bd60d9bb..28d19750b79d3 100644
--- a/test/testautomation_stdlib.c
+++ b/test/testautomation_stdlib.c
@@ -726,6 +726,7 @@ static int SDLCALL stdlib_sscanf(void *arg)
     long long_output, expected_long_output;
     long long long_long_output, expected_long_long_output;
     size_t size_output, expected_size_output;
+    uintptr_t uintptr_output, expected_uintptr_output;
     char text[128], text2[128];
 
     expected_output = output = 123;
@@ -790,6 +791,15 @@ static int SDLCALL stdlib_sscanf(void *arg)
     SDLTest_AssertCheck(expected_size_output == size_output, "Check output, expected: %zu, got: %zu", expected_size_output, size_output);
     SDLTest_AssertCheck(expected_result == result, "Check return value, expected: %i, got: %i", expected_result, result);
 
+    uintptr_output = 123;
+    expected_uintptr_output = 0x1234567;
+    expected_result = 1;
+    result = SDL_snprintf(text, sizeof(text), "%p", expected_uintptr_output);
+    result = SDL_sscanf(text, "%p", &uintptr_output);
+    SDLTest_AssertPass("Call to SDL_sscanf(\"%s\", \"%%p\", &output)", text);
+    SDLTest_AssertCheck(expected_uintptr_output == uintptr_output, "Check output, expected: %p, got: %p", expected_uintptr_output, uintptr_output);
+    SDLTest_AssertCheck(expected_result == result, "Check return value, expected: %i, got: %i", expected_result, result);
+
     expected_result = 1;
     text[0] = '\0';
     result = SDL_sscanf("abc def", "%s", text);