From 69803253100111f870c068300d336edac19783b4 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Mon, 24 Feb 2025 18:49:31 -0800
Subject: [PATCH] Added support for the "%n" sscanf format specifier
---
src/stdlib/SDL_string.c | 31 ++++++++++++++++++++++
test/testautomation_stdlib.c | 50 +++++++++++++++++++++---------------
2 files changed, 61 insertions(+), 20 deletions(-)
diff --git a/src/stdlib/SDL_string.c b/src/stdlib/SDL_string.c
index 39c49e46173fd..007719ef796f1 100644
--- a/src/stdlib/SDL_string.c
+++ b/src/stdlib/SDL_string.c
@@ -1448,6 +1448,7 @@ static bool CharacterMatchesSet(char c, const char *set, size_t set_len)
// NOLINTNEXTLINE(readability-non-const-parameter)
int SDL_vsscanf(const char *text, SDL_SCANF_FORMAT_STRING const char *fmt, va_list ap)
{
+ const char *start = text;
int result = 0;
if (!text || !*text) {
@@ -1718,6 +1719,36 @@ int SDL_vsscanf(const char *text, SDL_SCANF_FORMAT_STRING const char *fmt, va_li
}
done = true;
break;
+ case 'n':
+ switch (inttype) {
+ case DO_SHORT:
+ {
+ short *valuep = va_arg(ap, short *);
+ *valuep = (short)(text - start);
+ } break;
+ case DO_INT:
+ {
+ int *valuep = va_arg(ap, int *);
+ *valuep = (int)(text - start);
+ } break;
+ case DO_LONG:
+ {
+ long *valuep = va_arg(ap, long *);
+ *valuep = (long)(text - start);
+ } break;
+ case DO_LONGLONG:
+ {
+ long long *valuep = va_arg(ap, long long *);
+ *valuep = (long long)(text - start);
+ } break;
+ case DO_SIZE_T:
+ {
+ size_t *valuep = va_arg(ap, size_t *);
+ *valuep = (size_t)(text - start);
+ } break;
+ }
+ done = true;
+ break;
case '[':
{
const char *set = fmt + 1;
diff --git a/test/testautomation_stdlib.c b/test/testautomation_stdlib.c
index 3073510604a04..bd7e17a5a7d04 100644
--- a/test/testautomation_stdlib.c
+++ b/test/testautomation_stdlib.c
@@ -731,6 +731,7 @@ static int SDLCALL stdlib_getsetenv(void *arg)
#endif
#define FMT_PRILLd "%" SDL_PRILLd
+#define FMT_PRILLdn "%" SDL_PRILLd "%" SDL_PRILL_PREFIX "n"
#define FMT_PRILLu "%" SDL_PRILLu
/**
@@ -740,11 +741,12 @@ static int SDLCALL stdlib_sscanf(void *arg)
{
int output;
int result;
+ int length;
int expected_output;
int expected_result;
- short short_output, expected_short_output;
- long long_output, expected_long_output;
- long long long_long_output, expected_long_long_output;
+ short short_output, expected_short_output, short_length;
+ long long_output, expected_long_output, long_length;
+ long long long_long_output, expected_long_long_output, long_long_length;
size_t size_output, expected_size_output;
void *ptr_output, *expected_ptr_output;
char text[128], text2[128];
@@ -764,43 +766,51 @@ static int SDLCALL stdlib_sscanf(void *arg)
SDLTest_AssertCheck(expected_result == result, "Check return value, expected: %i, got: %i", expected_result, result);
output = 123;
+ length = 0;
expected_output = 2;
expected_result = 1;
- result = SDL_sscanf("2", "%i", &output);
- SDLTest_AssertPass("Call to SDL_sscanf(\"2\", \"%%i\", &output)");
+ result = SDL_sscanf("2", "%i%n", &output, &length);
+ SDLTest_AssertPass("Call to SDL_sscanf(\"2\", \"%%i%%n\", &output, &length)");
SDLTest_AssertCheck(expected_output == output, "Check output, expected: %i, got: %i", expected_output, output);
SDLTest_AssertCheck(expected_result == result, "Check return value, expected: %i, got: %i", expected_result, result);
+ SDLTest_AssertCheck(length == 1, "Check length, expected: 1, got: %i", length);
output = 123;
+ length = 0;
expected_output = 0xa;
expected_result = 1;
- result = SDL_sscanf("aa", "%1x", &output);
- SDLTest_AssertPass("Call to SDL_sscanf(\"aa\", \"%%1x\", &output)");
+ result = SDL_sscanf("aa", "%1x%n", &output, &length);
+ SDLTest_AssertPass("Call to SDL_sscanf(\"aa\", \"%%1x%%n\", &output, &length)");
SDLTest_AssertCheck(expected_output == output, "Check output, expected: %i, got: %i", expected_output, output);
SDLTest_AssertCheck(expected_result == result, "Check return value, expected: %i, got: %i", expected_result, result);
+ SDLTest_AssertCheck(length == 1, "Check length, expected: 1, got: %i", length);
-#define SIZED_TEST_CASE(type, var, format_specifier) \
+#define SIZED_TEST_CASE(type, var, printf_specifier, scanf_specifier) \
var##_output = 123; \
+ var##_length = 0; \
expected_##var##_output = (type)(((unsigned type)(~0)) >> 1); \
expected_result = 1; \
- result = SDL_snprintf(text, sizeof(text), format_specifier, expected_##var##_output); \
- result = SDL_sscanf(text, format_specifier, &var##_output); \
- SDLTest_AssertPass("Call to SDL_sscanf(\"%s\", \"%s\", &output)", text, #format_specifier); \
- SDLTest_AssertCheck(expected_##var##_output == var##_output, "Check output, expected: " format_specifier ", got: " format_specifier, expected_##var##_output, var##_output); \
+ result = SDL_snprintf(text, sizeof(text), printf_specifier, expected_##var##_output); \
+ result = SDL_sscanf(text, scanf_specifier, &var##_output, &var##_length); \
+ SDLTest_AssertPass("Call to SDL_sscanf(\"%s\", %s, &output, &length)", text, #scanf_specifier); \
+ SDLTest_AssertCheck(expected_##var##_output == var##_output, "Check output, expected: " printf_specifier ", got: " printf_specifier, expected_##var##_output, var##_output); \
SDLTest_AssertCheck(expected_result == result, "Check return value, expected: %i, got: %i", expected_result, result); \
+ SDLTest_AssertCheck(var##_length == (type)SDL_strlen(text), "Check length, expected: %i, got: %i", (int)SDL_strlen(text), (int)var##_length); \
\
var##_output = 123; \
+ var##_length = 0; \
expected_##var##_output = ~(type)(((unsigned type)(~0)) >> 1); \
expected_result = 1; \
- result = SDL_snprintf(text, sizeof(text), format_specifier, expected_##var##_output); \
- result = SDL_sscanf(text, format_specifier, &var##_output); \
- SDLTest_AssertPass("Call to SDL_sscanf(\"%s\", \"%s\", &output)", text, #format_specifier); \
- SDLTest_AssertCheck(expected_##var##_output == var##_output, "Check output, expected: " format_specifier ", got: " format_specifier, expected_##var##_output, var##_output); \
- SDLTest_AssertCheck(expected_result == result, "Check return value, expected: %i, got: %i", expected_result, result);
+ result = SDL_snprintf(text, sizeof(text), printf_specifier, expected_##var##_output); \
+ result = SDL_sscanf(text, scanf_specifier, &var##_output, &var##_length); \
+ SDLTest_AssertPass("Call to SDL_sscanf(\"%s\", %s, &output, &length)", text, #scanf_specifier); \
+ SDLTest_AssertCheck(expected_##var##_output == var##_output, "Check output, expected: " printf_specifier ", got: " printf_specifier, expected_##var##_output, var##_output); \
+ SDLTest_AssertCheck(expected_result == result, "Check return value, expected: %i, got: %i", expected_result, result); \
+ SDLTest_AssertCheck(var##_length == (type)SDL_strlen(text), "Check length, expected: %i, got: %i", (int)SDL_strlen(text), (int)var##_length); \
- SIZED_TEST_CASE(short, short, "%hd")
- SIZED_TEST_CASE(long, long, "%ld")
- SIZED_TEST_CASE(long long, long_long, FMT_PRILLd)
+ SIZED_TEST_CASE(short, short, "%hd", "%hd%hn")
+ SIZED_TEST_CASE(long, long, "%ld", "%ld%ln")
+ SIZED_TEST_CASE(long long, long_long, FMT_PRILLd, FMT_PRILLdn)
size_output = 123;
expected_size_output = ~((size_t)0);