diff --git a/CMakeLists.txt b/CMakeLists.txt index fab3c11e2..8af562830 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1065,7 +1065,7 @@ if(SDL_LIBC) realloc rindex round roundf scalbn scalbnf setenv sin sinf sqr sqrt sqrtf sscanf strchr strcmp strlcat strlcpy strlen strncmp strnlen - strrchr strstr strtod strtok_r strtol strtoll strtoul strtoull + strrchr strstr strnstr strtod strtok_r strtol strtoll strtoul strtoull tan tanf trunc truncf unsetenv vsnprintf vsscanf diff --git a/include/SDL3/SDL_stdinc.h b/include/SDL3/SDL_stdinc.h index 50d81825c..a257513f3 100644 --- a/include/SDL3/SDL_stdinc.h +++ b/include/SDL3/SDL_stdinc.h @@ -546,6 +546,7 @@ extern DECLSPEC size_t SDLCALL SDL_wcslcpy(SDL_OUT_Z_CAP(maxlen) wchar_t *dst, c extern DECLSPEC size_t SDLCALL SDL_wcslcat(SDL_INOUT_Z_CAP(maxlen) wchar_t *dst, const wchar_t *src, size_t maxlen); extern DECLSPEC wchar_t *SDLCALL SDL_wcsdup(const wchar_t *wstr); extern DECLSPEC wchar_t *SDLCALL SDL_wcsstr(const wchar_t *haystack, const wchar_t *needle); +extern DECLSPEC wchar_t *SDLCALL SDL_wcsnstr(const wchar_t *haystack, const wchar_t *needle, size_t maxlen); extern DECLSPEC int SDLCALL SDL_wcscmp(const wchar_t *str1, const wchar_t *str2); extern DECLSPEC int SDLCALL SDL_wcsncmp(const wchar_t *str1, const wchar_t *str2, size_t maxlen); @@ -566,6 +567,7 @@ extern DECLSPEC char *SDLCALL SDL_strlwr(char *str); extern DECLSPEC char *SDLCALL SDL_strchr(const char *str, int c); extern DECLSPEC char *SDLCALL SDL_strrchr(const char *str, int c); extern DECLSPEC char *SDLCALL SDL_strstr(const char *haystack, const char *needle); +extern DECLSPEC char *SDLCALL SDL_strnstr(const char *haystack, const char *needle, size_t maxlen); extern DECLSPEC char *SDLCALL SDL_strcasestr(const char *haystack, const char *needle); extern DECLSPEC char *SDLCALL SDL_strtok_r(char *s1, const char *s2, char **saveptr); extern DECLSPEC size_t SDLCALL SDL_utf8strlen(const char *str); diff --git a/include/build_config/SDL_build_config.h.cmake b/include/build_config/SDL_build_config.h.cmake index 4c5f9b31c..b725a98e1 100644 --- a/include/build_config/SDL_build_config.h.cmake +++ b/include/build_config/SDL_build_config.h.cmake @@ -116,6 +116,7 @@ #cmakedefine HAVE_STRCHR 1 #cmakedefine HAVE_STRRCHR 1 #cmakedefine HAVE_STRSTR 1 +#cmakedefine HAVE_STRNSTR 1 #cmakedefine HAVE_STRTOK_R 1 #cmakedefine HAVE_ITOA 1 #cmakedefine HAVE__LTOA 1 diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index e007e8d4a..61bf30691 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -960,6 +960,8 @@ SDL3_0.0.0 { SDL_GetGamepadMappings; SDL_GetTouchDevices; SDL_GetTouchDeviceName; + SDL_strnstr; + SDL_wcsnstr; # extra symbols go here (don't modify this line) local: *; }; diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index 9c9696d44..bc03b3178 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -985,3 +985,5 @@ #define SDL_GetGamepadMappings SDL_GetGamepadMappings_REAL #define SDL_GetTouchDevices SDL_GetTouchDevices_REAL #define SDL_GetTouchDeviceName SDL_GetTouchDeviceName_REAL +#define SDL_strnstr SDL_strnstr_REAL +#define SDL_wcsnstr SDL_wcsnstr_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index e055c8d06..02d9aebae 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -1010,3 +1010,5 @@ SDL_DYNAPI_PROC(SDL_Renderer*,SDL_CreateRendererWithProperties,(SDL_PropertiesID SDL_DYNAPI_PROC(char**,SDL_GetGamepadMappings,(int *a),(a),return) SDL_DYNAPI_PROC(SDL_TouchID*,SDL_GetTouchDevices,(int *a),(a),return) SDL_DYNAPI_PROC(const char*,SDL_GetTouchDeviceName,(SDL_TouchID a),(a),return) +SDL_DYNAPI_PROC(char*,SDL_strnstr,(const char *a, const char *b, size_t c),(a,b,c),return) +SDL_DYNAPI_PROC(wchar_t*,SDL_wcsnstr,(const wchar_t *a, const wchar_t *b, size_t c),(a,b,c),return) diff --git a/src/stdlib/SDL_string.c b/src/stdlib/SDL_string.c index cdc51ef8d..8570d4fa1 100644 --- a/src/stdlib/SDL_string.c +++ b/src/stdlib/SDL_string.c @@ -468,19 +468,28 @@ wchar_t *SDL_wcsdup(const wchar_t *string) return newstr; } +wchar_t *SDL_wcsnstr(const wchar_t *haystack, const wchar_t *needle, size_t maxlen) +{ + size_t length = SDL_wcslen(needle); + if (length == 0) { + return (wchar_t *)haystack; + } + while (maxlen >= length && *haystack) { + if (maxlen >= length && SDL_wcsncmp(haystack, needle, length) == 0) { + return (wchar_t *)haystack; + } + ++haystack; + --maxlen; + } + return NULL; +} + wchar_t *SDL_wcsstr(const wchar_t *haystack, const wchar_t *needle) { #ifdef HAVE_WCSSTR return SDL_const_cast(wchar_t *, wcsstr(haystack, needle)); #else - size_t length = SDL_wcslen(needle); - while (*haystack) { - if (SDL_wcsncmp(haystack, needle, length) == 0) { - return (wchar_t *)haystack; - } - ++haystack; - } - return NULL; + return SDL_wcsnstr(haystack, needle, SDL_wcslen(haystack)); #endif /* HAVE_WCSSTR */ } @@ -821,19 +830,32 @@ char *SDL_strrchr(const char *string, int c) #endif /* HAVE_STRRCHR */ } +char *SDL_strnstr(const char *haystack, const char *needle, size_t maxlen) +{ +#ifdef HAVE_STRNSTR + return SDL_const_cast(char *, strnstr(haystack, needle, maxlen)); +#else + size_t length = SDL_strlen(needle); + if (length == 0) { + return (char *)haystack; + } + while (maxlen >= length && *haystack) { + if (SDL_strncmp(haystack, needle, length) == 0) { + return (char *)haystack; + } + ++haystack; + --maxlen; + } + return NULL; +#endif /* HAVE_STRSTR */ +} + char *SDL_strstr(const char *haystack, const char *needle) { #ifdef HAVE_STRSTR return SDL_const_cast(char *, strstr(haystack, needle)); #else - size_t length = SDL_strlen(needle); - while (*haystack) { - if (SDL_strncmp(haystack, needle, length) == 0) { - return (char *)haystack; - } - ++haystack; - } - return NULL; + return SDL_strnstr(haystack, needle, SDL_strlen(haystack)); #endif /* HAVE_STRSTR */ } diff --git a/test/testautomation_stdlib.c b/test/testautomation_stdlib.c index eab676d79..d4abe2a2b 100644 --- a/test/testautomation_stdlib.c +++ b/test/testautomation_stdlib.c @@ -60,6 +60,68 @@ static int stdlib_strlcpy(void *arg) return TEST_COMPLETED; } +/** + * Call to SDL_strstr + */ +static int stdlib_strstr(void *arg) +{ + char *result; + const char *text = "abcdef"; + const char *expected; + + result = SDL_strstr(text, ""); + expected = text; + SDLTest_AssertPass("Call to SDL_strstr(text, \"\")"); + SDLTest_AssertCheck(result == expected, "Check result, expected: %s, got: %s", expected, result); + + result = SDL_strstr(text, "abc"); + expected = text; + SDLTest_AssertPass("Call to SDL_strstr(text, \"abc\")"); + SDLTest_AssertCheck(result == expected, "Check result, expected: %s, got: %s", expected, result); + + result = SDL_strstr(text, "bcd"); + expected = text+1; + SDLTest_AssertPass("Call to SDL_strstr(text, \"bcd\")"); + SDLTest_AssertCheck(result == expected, "Check result, expected: %s, got: %s", expected, result); + + result = SDL_strstr(text, "xyz"); + expected = NULL; + SDLTest_AssertPass("Call to SDL_strstr(text, \"xyz\")"); + SDLTest_AssertCheck(result == expected, "Check result, expected: (null), got: %s", result); + + result = SDL_strnstr(text, "", SDL_strlen(text)); + expected = text; + SDLTest_AssertPass("Call to SDL_strnstr(text, \"\", SDL_strlen(text))"); + SDLTest_AssertCheck(result == expected, "Check result, expected: %s, got: %s", expected, result); + + result = SDL_strnstr(text, "abc", SDL_strlen(text)); + expected = text; + SDLTest_AssertPass("Call to SDL_strnstr(text, \"abc\", SDL_strlen(text))"); + SDLTest_AssertCheck(result == expected, "Check result, expected: %s, got: %s", expected, result); + + result = SDL_strnstr(text, "bcd", SDL_strlen(text)); + expected = text+1; + SDLTest_AssertPass("Call to SDL_strnstr(text, \"bcd\", SDL_strlen(text))"); + SDLTest_AssertCheck(result == expected, "Check result, expected: %s, got: %s", expected, result); + + result = SDL_strnstr(text, "bcd", 3); + expected = NULL; + SDLTest_AssertPass("Call to SDL_strnstr(text, \"bcd\", 3)"); + SDLTest_AssertCheck(result == expected, "Check result, expected: (null), got: %s", result); + + result = SDL_strnstr(text, "xyz", 3); + expected = NULL; + SDLTest_AssertPass("Call to SDL_strnstr(text, \"xyz\", 3)"); + SDLTest_AssertCheck(result == expected, "Check result, expected: (null), got: %s", result); + + result = SDL_strnstr(text, "xyz", SDL_strlen(text)*100000); + expected = NULL; + SDLTest_AssertPass("Call to SDL_strnstr(text, \"xyz\", SDL_strlen(text)*100000)"); + SDLTest_AssertCheck(result == expected, "Check result, expected: (null), got: %s", result); + + return TEST_COMPLETED; +} + #if defined(HAVE_WFORMAT) || defined(HAVE_WFORMAT_EXTRA_ARGS) #pragma GCC diagnostic push #ifdef HAVE_WFORMAT @@ -930,22 +992,26 @@ static const SDLTest_TestCaseReference stdlibTest2 = { }; static const SDLTest_TestCaseReference stdlibTest3 = { - stdlib_snprintf, "stdlib_snprintf", "Call to SDL_snprintf", TEST_ENABLED + stdlib_strstr, "stdlib_strstr", "Call to SDL_strstr", TEST_ENABLED }; static const SDLTest_TestCaseReference stdlibTest4 = { - stdlib_swprintf, "stdlib_swprintf", "Call to SDL_swprintf", TEST_ENABLED + stdlib_snprintf, "stdlib_snprintf", "Call to SDL_snprintf", TEST_ENABLED }; static const SDLTest_TestCaseReference stdlibTest5 = { - stdlib_getsetenv, "stdlib_getsetenv", "Call to SDL_getenv and SDL_setenv", TEST_ENABLED + stdlib_swprintf, "stdlib_swprintf", "Call to SDL_swprintf", TEST_ENABLED }; static const SDLTest_TestCaseReference stdlibTest6 = { - stdlib_sscanf, "stdlib_sscanf", "Call to SDL_sscanf", TEST_ENABLED + stdlib_getsetenv, "stdlib_getsetenv", "Call to SDL_getenv and SDL_setenv", TEST_ENABLED }; static const SDLTest_TestCaseReference stdlibTest7 = { + stdlib_sscanf, "stdlib_sscanf", "Call to SDL_sscanf", TEST_ENABLED +}; + +static const SDLTest_TestCaseReference stdlibTest8 = { stdlib_aligned_alloc, "stdlib_aligned_alloc", "Call to SDL_aligned_alloc", TEST_ENABLED }; @@ -962,6 +1028,7 @@ static const SDLTest_TestCaseReference *stdlibTests[] = { &stdlibTest5, &stdlibTest6, &stdlibTest7, + &stdlibTest8, &stdlibTestOverflow, NULL };