From 4194a902e8d038fb5285d92dbaa72683a28cb31e Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Tue, 24 Oct 2023 16:41:19 -0700
Subject: [PATCH] Added support for "%[]" sscanf syntax
Fixes https://github.com/libsdl-org/SDL/issues/8423
(cherry picked from commit 39a961ba41430788774f1b8c0e6d63f7308116e7)
---
src/stdlib/SDL_string.c | 71 ++++++++++++++++++++++++++++++++
test/testautomation_stdlib.c | 78 +++++++++++++++++++++++++++++++++++-
2 files changed, 148 insertions(+), 1 deletion(-)
diff --git a/src/stdlib/SDL_string.c b/src/stdlib/SDL_string.c
index 7bbaad1170ff..ce0ec397fadb 100644
--- a/src/stdlib/SDL_string.c
+++ b/src/stdlib/SDL_string.c
@@ -1114,6 +1114,39 @@ int SDL_vsscanf(const char *text, const char *fmt, va_list ap)
return vsscanf(text, fmt, ap);
}
#else
+static SDL_bool CharacterMatchesSet(char c, const char *set, size_t set_len)
+{
+ SDL_bool invert = SDL_FALSE;
+ SDL_bool result = SDL_FALSE;
+
+ if (*set == '^') {
+ invert = SDL_TRUE;
+ ++set;
+ --set_len;
+ }
+ while (set_len > 0 && !result) {
+ if (set_len >= 3 && set[1] == '-') {
+ char low_char = SDL_min(set[0], set[2]);
+ char high_char = SDL_max(set[0], set[2]);
+ if (c >= low_char && c <= high_char) {
+ result = SDL_TRUE;
+ }
+ set += 3;
+ set_len -= 3;
+ } else {
+ if (c == *set) {
+ result = SDL_TRUE;
+ }
+ ++set;
+ --set_len;
+ }
+ }
+ if (invert) {
+ result = result ? SDL_FALSE : SDL_TRUE;
+ }
+ return result;
+}
+
/* NOLINTNEXTLINE(readability-non-const-parameter) */
int SDL_vsscanf(const char *text, const char *fmt, va_list ap)
{
@@ -1387,6 +1420,44 @@ int SDL_vsscanf(const char *text, const char *fmt, va_list ap)
}
done = SDL_TRUE;
break;
+ case '[':
+ {
+ const char *set = fmt + 1;
+ while (*fmt && *fmt != ']') {
+ ++fmt;
+ }
+ if (*fmt) {
+ size_t set_len = (fmt - set);
+ if (suppress) {
+ while (CharacterMatchesSet(*text, set, set_len)) {
+ ++text;
+ if (count) {
+ if (--count == 0) {
+ break;
+ }
+ }
+ }
+ } else {
+ SDL_bool had_match = SDL_FALSE;
+ char *valuep = va_arg(ap, char *);
+ while (CharacterMatchesSet(*text, set, set_len)) {
+ had_match = SDL_TRUE;
+ *valuep++ = *text++;
+ if (count) {
+ if (--count == 0) {
+ break;
+ }
+ }
+ }
+ *valuep = '\0';
+ if (had_match) {
+ ++retval;
+ }
+ }
+ }
+ }
+ done = SDL_TRUE;
+ break;
default:
done = SDL_TRUE;
break;
diff --git a/test/testautomation_stdlib.c b/test/testautomation_stdlib.c
index af014b7c2c51..5b681fcc5e1f 100644
--- a/test/testautomation_stdlib.c
+++ b/test/testautomation_stdlib.c
@@ -351,7 +351,7 @@ int 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;
- char text[128];
+ char text[128], text2[128];
expected_output = output = 123;
expected_result = -1;
@@ -415,6 +415,82 @@ int 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);
+ expected_result = 1;
+ text[0] = '\0';
+ result = SDL_sscanf("abc def", "%s", text);
+ SDLTest_AssertPass("Call to SDL_sscanf(\"abc def\", \"%%s\", text)");
+ SDLTest_AssertCheck(SDL_strcmp(text, "abc") == 0, "Check output, expected: \"abc\", got: \"%s\"", text);
+ 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);
+ SDLTest_AssertPass("Call to SDL_sscanf(\"abc,def\", \"%%s\", text)");
+ SDLTest_AssertCheck(SDL_strcmp(text, "abc,def") == 0, "Check output, expected: \"abc\", got: \"%s\"", text);
+ 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", "%[cba]", text);
+ SDLTest_AssertPass("Call to SDL_sscanf(\"abc,def\", \"%%[cba]\", text)");
+ SDLTest_AssertCheck(SDL_strcmp(text, "abc") == 0, "Check output, expected: \"abc\", got: \"%s\"", text);
+ 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", "%[a-z]", text);
+ SDLTest_AssertPass("Call to SDL_sscanf(\"abc,def\", \"%%[z-a]\", text)");
+ SDLTest_AssertCheck(SDL_strcmp(text, "abc") == 0, "Check output, expected: \"abc\", got: \"%s\"", text);
+ 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", "%[^,]", text);
+ SDLTest_AssertPass("Call to SDL_sscanf(\"abc,def\", \"%%[^,]\", text)");
+ SDLTest_AssertCheck(SDL_strcmp(text, "abc") == 0, "Check output, expected: \"abc\", got: \"%s\"", text);
+ SDLTest_AssertCheck(expected_result == result, "Check return value, expected: %i, got: %i", expected_result, result);
+
+ expected_result = 0;
+ text[0] = '\0';
+ result = SDL_sscanf("abc,def", "%[A-Z]", text);
+ SDLTest_AssertPass("Call to SDL_sscanf(\"abc,def\", \"%%[A-Z]\", text)");
+ SDLTest_AssertCheck(SDL_strcmp(text, "") == 0, "Check output, expected: \"\", got: \"%s\"", text);
+ SDLTest_AssertCheck(expected_result == result, "Check return value, expected: %i, got: %i", expected_result, result);
+
+ expected_result = 2;
+ text[0] = '\0';
+ text2[0] = '\0';
+ result = SDL_sscanf("abc,def", "%[abc],%[def]", text, text2);
+ SDLTest_AssertPass("Call to SDL_sscanf(\"abc,def\", \"%%[abc],%%[def]\", text)");
+ SDLTest_AssertCheck(SDL_strcmp(text, "abc") == 0, "Check output, expected: \"abc\", got: \"%s\"", text);
+ SDLTest_AssertCheck(SDL_strcmp(text2, "def") == 0, "Check output, expected: \"def\", got: \"%s\"", text2);
+ SDLTest_AssertCheck(expected_result == result, "Check return value, expected: %i, got: %i", expected_result, result);
+
+ expected_result = 2;
+ text[0] = '\0';
+ text2[0] = '\0';
+ result = SDL_sscanf("abc,def", "%[abc]%*[,]%[def]", text, text2);
+ SDLTest_AssertPass("Call to SDL_sscanf(\"abc,def\", \"%%[abc]%%*[,]%%[def]\", text)");
+ SDLTest_AssertCheck(SDL_strcmp(text, "abc") == 0, "Check output, expected: \"abc\", got: \"%s\"", text);
+ SDLTest_AssertCheck(SDL_strcmp(text2, "def") == 0, "Check output, expected: \"def\", got: \"%s\"", text2);
+ SDLTest_AssertCheck(expected_result == result, "Check return value, expected: %i, got: %i", expected_result, result);
+
+ expected_result = 2;
+ text[0] = '\0';
+ text2[0] = '\0';
+ result = SDL_sscanf("abc def", "%[abc] %[def]", text, text2);
+ SDLTest_AssertPass("Call to SDL_sscanf(\"abc def\", \"%%[abc] %%[def]\", text)");
+ SDLTest_AssertCheck(SDL_strcmp(text, "abc") == 0, "Check output, expected: \"abc\", got: \"%s\"", text);
+ SDLTest_AssertCheck(SDL_strcmp(text2, "def") == 0, "Check output, expected: \"def\", got: \"%s\"", text2);
+ SDLTest_AssertCheck(expected_result == result, "Check return value, expected: %i, got: %i", expected_result, result);
+
+ expected_result = 1;
+ text[0] = '\0';
+ result = SDL_sscanf("abc123XYZ", "%[a-zA-Z0-9]", text);
+ SDLTest_AssertPass("Call to SDL_sscanf(\"abc123XYZ\", \"%%[a-zA-Z0-9]\", text)");
+ SDLTest_AssertCheck(SDL_strcmp(text, "abc123XYZ") == 0, "Check output, expected: \"abc123XYZ\", got: \"%s\"", text);
+ SDLTest_AssertCheck(expected_result == result, "Check return value, expected: %i, got: %i", expected_result, result);
+
return TEST_COMPLETED;
}