SDL: Add SDL_strpbrk

From baa1a5e2f472ad062ef618843dce916e226791aa Mon Sep 17 00:00:00 2001
From: Anonymous Maarten <[EMAIL REDACTED]>
Date: Mon, 2 Sep 2024 14:13:54 +0200
Subject: [PATCH] Add SDL_strpbrk

---
 CMakeLists.txt                                |  2 +-
 cmake/PreseedEmscriptenCache.cmake            |  1 +
 cmake/PreseedMSVCCache.cmake                  |  1 +
 include/SDL3/SDL_stdinc.h                     | 17 +++++++
 include/build_config/SDL_build_config.h.cmake |  1 +
 include/build_config/SDL_build_config_ios.h   |  1 +
 include/build_config/SDL_build_config_macos.h |  1 +
 .../build_config/SDL_build_config_windows.h   |  1 +
 include/build_config/SDL_build_config_winrt.h |  1 +
 include/build_config/SDL_build_config_xbox.h  |  1 +
 src/dynapi/SDL_dynapi.sym                     |  1 +
 src/dynapi/SDL_dynapi_overrides.h             |  1 +
 src/dynapi/SDL_dynapi_procs.h                 |  1 +
 src/stdlib/SDL_string.c                       | 19 +++++++
 test/testautomation_stdlib.c                  | 51 +++++++++++++++++--
 15 files changed, 96 insertions(+), 4 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 37e6647a6d799..5716da4cefbe3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -979,7 +979,7 @@ if(SDL_LIBC)
     pow powf putenv
     realloc rindex round roundf
     scalbn scalbnf setenv sin sinf sqr sqrt sqrtf sscanf strchr
-    strcmp strlcat strlcpy strlen strncmp strnlen
+    strcmp strlcat strlcpy strlen strncmp strnlen strpbrk
     strrchr strstr strnstr strtod strtok_r strtol strtoll strtoul strtoull
     tan tanf trunc truncf
     unsetenv
diff --git a/cmake/PreseedEmscriptenCache.cmake b/cmake/PreseedEmscriptenCache.cmake
index 261e7ea677e96..5a34ab5ac21f4 100644
--- a/cmake/PreseedEmscriptenCache.cmake
+++ b/cmake/PreseedEmscriptenCache.cmake
@@ -98,6 +98,7 @@ if(EMSCRIPTEN)
     set(LIBC_HAS_STRNCMP                                 "1"   CACHE INTERNAL "Have symbol strncmp")
     set(LIBC_HAS_STRNLEN                                 "1"   CACHE INTERNAL "Have symbol strnlen")
     set(LIBC_HAS_STRNSTR                                 ""    CACHE INTERNAL "Have symbol strnstr")
+    set(LIBC_HAS_STRPBRK                                 "1"   CACHE INTERNAL "Have symbol strpbrk")
     set(LIBC_HAS_STRRCHR                                 "1"   CACHE INTERNAL "Have symbol strrchr")
     set(LIBC_HAS_STRSTR                                  "1"   CACHE INTERNAL "Have symbol strstr")
     set(LIBC_HAS_STRTOD                                  "1"   CACHE INTERNAL "Have symbol strtod")
diff --git a/cmake/PreseedMSVCCache.cmake b/cmake/PreseedMSVCCache.cmake
index 76d3fa4b8b5a5..17495aa480aba 100644
--- a/cmake/PreseedMSVCCache.cmake
+++ b/cmake/PreseedMSVCCache.cmake
@@ -111,6 +111,7 @@ if(MSVC)
     set(LIBC_HAS_STRNCMP                                 "1"   CACHE INTERNAL "Have symbol strncmp")
     set(LIBC_HAS_STRNLEN                                 "1"   CACHE INTERNAL "Have symbol strnlen")
     set(LIBC_HAS_STRNSTR                                 ""    CACHE INTERNAL "Have symbol strnstr")
+    set(LIBC_HAS_STRPBRK                                 "1"   CACHE INTERNAL "Have symbol strpbrk")
     set(LIBC_HAS_STRRCHR                                 "1"   CACHE INTERNAL "Have symbol strrchr")
     set(LIBC_HAS_STRSTR                                  "1"   CACHE INTERNAL "Have symbol strstr")
     set(LIBC_HAS_STRTOD                                  "1"   CACHE INTERNAL "Have symbol strtod")
diff --git a/include/SDL3/SDL_stdinc.h b/include/SDL3/SDL_stdinc.h
index 646bbd788420b..2a36d4426e0ab 100644
--- a/include/SDL3/SDL_stdinc.h
+++ b/include/SDL3/SDL_stdinc.h
@@ -1297,6 +1297,22 @@ extern SDL_DECLSPEC int SDLCALL SDL_strcasecmp(const char *str1, const char *str
  */
 extern SDL_DECLSPEC int SDLCALL SDL_strncasecmp(const char *str1, const char *str2, size_t maxlen);
 
+/**
+ * Searches a string for the first occurence of any character contained in a
+ * breakset, and returns a pointer from the string to that character.
+ *
+ * \param str The null-terminated string to be searched.
+ * \param breakset A null-terminated string containing the list of characters
+ *        to look for.
+ * \returns A pointer to the location, in str, of the first occurence of a
+ *          character present in the breakset, or NULL if none is found.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.0.0.
+ */
+extern SDL_DECLSPEC char * SDLCALL SDL_strpbrk(const char * SDL_RESTRICT str, const char * SDL_RESTRICT breakset);
+
 /**
  * The Unicode REPLACEMENT CHARACTER codepoint.
  *
@@ -3079,6 +3095,7 @@ size_t wcslcat(wchar_t *dst, const wchar_t *src, size_t size);
 #define SDL_wcsncmp wcsncmp
 #define SDL_strcasecmp strcasecmp
 #define SDL_strncasecmp strncasecmp
+#define SDL_strpbrk strpbrk
 #define SDL_sscanf sscanf
 #define SDL_vsscanf vsscanf
 #define SDL_snprintf snprintf
diff --git a/include/build_config/SDL_build_config.h.cmake b/include/build_config/SDL_build_config.h.cmake
index ea05ee31c633c..e117409bb6ead 100644
--- a/include/build_config/SDL_build_config.h.cmake
+++ b/include/build_config/SDL_build_config.h.cmake
@@ -100,6 +100,7 @@
 #cmakedefine HAVE_STRNLEN 1
 #cmakedefine HAVE_STRLCPY 1
 #cmakedefine HAVE_STRLCAT 1
+#cmakedefine HAVE_STRPBRK 1
 #cmakedefine HAVE__STRREV 1
 #cmakedefine HAVE__STRUPR 1
 #cmakedefine HAVE__STRLWR 1
diff --git a/include/build_config/SDL_build_config_ios.h b/include/build_config/SDL_build_config_ios.h
index b283858e833d8..fe87d84e3ecbb 100644
--- a/include/build_config/SDL_build_config_ios.h
+++ b/include/build_config/SDL_build_config_ios.h
@@ -76,6 +76,7 @@
 #define HAVE_ATOF   1
 #define HAVE_STRCMP 1
 #define HAVE_STRNCMP    1
+#define HAVE_STRPBRK    1
 #define HAVE_STRCASESTR 1
 #define HAVE_VSSCANF 1
 #define HAVE_VSNPRINTF  1
diff --git a/include/build_config/SDL_build_config_macos.h b/include/build_config/SDL_build_config_macos.h
index 61fc8e8414fa3..40a8895d7cd42 100644
--- a/include/build_config/SDL_build_config_macos.h
+++ b/include/build_config/SDL_build_config_macos.h
@@ -68,6 +68,7 @@
 #define HAVE_STRLEN 1
 #define HAVE_STRLCPY    1
 #define HAVE_STRLCAT    1
+#define HAVE_STRPBRK    1
 #define HAVE_STRCHR 1
 #define HAVE_STRRCHR    1
 #define HAVE_STRSTR 1
diff --git a/include/build_config/SDL_build_config_windows.h b/include/build_config/SDL_build_config_windows.h
index 47723fb28594e..7729521963b99 100644
--- a/include/build_config/SDL_build_config_windows.h
+++ b/include/build_config/SDL_build_config_windows.h
@@ -160,6 +160,7 @@ typedef unsigned int uintptr_t;
 #define HAVE_ATOF 1
 #define HAVE_STRCMP 1
 #define HAVE_STRNCMP 1
+#define HAVE_STRPBRK 1
 #define HAVE__WCSDUP 1
 #define HAVE_SSCANF 1
 #define HAVE_VSSCANF 1
diff --git a/include/build_config/SDL_build_config_winrt.h b/include/build_config/SDL_build_config_winrt.h
index 57d1dea420af0..7ee64a66f8e1e 100644
--- a/include/build_config/SDL_build_config_winrt.h
+++ b/include/build_config/SDL_build_config_winrt.h
@@ -85,6 +85,7 @@
 #define HAVE__STRREV 1
 #define HAVE__STRUPR 1
 #define HAVE_STRCHR 1
+#define HAVE_STRPBRK 1
 #define HAVE_STRRCHR 1
 #define HAVE_STRSTR 1
 #define HAVE_STRTOL 1
diff --git a/include/build_config/SDL_build_config_xbox.h b/include/build_config/SDL_build_config_xbox.h
index 2ad0eb7d4e043..ae80b5ef64f5a 100644
--- a/include/build_config/SDL_build_config_xbox.h
+++ b/include/build_config/SDL_build_config_xbox.h
@@ -82,6 +82,7 @@
 #define HAVE_MEMMOVE 1
 #define HAVE_MEMCMP 1
 #define HAVE_STRLEN 1
+#define HAVE_STRPBRK 1
 #define HAVE__STRREV 1
 /* These functions have security warnings, so we won't use them */
 /* #undef HAVE__STRUPR */
diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym
index 35ef0041f165f..6c6aa7695c216 100644
--- a/src/dynapi/SDL_dynapi.sym
+++ b/src/dynapi/SDL_dynapi.sym
@@ -1144,6 +1144,7 @@ SDL3_0.0.0 {
     SDL_wcsnstr;
     SDL_wcsstr;
     SDL_wcstol;
+    SDL_strpbrk;
     # 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 d04960d01066a..f0e1118c69755 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -1169,3 +1169,4 @@
 #define SDL_wcsnstr SDL_wcsnstr_REAL
 #define SDL_wcsstr SDL_wcsstr_REAL
 #define SDL_wcstol SDL_wcstol_REAL
+#define SDL_strpbrk SDL_strpbrk_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index 0d1ed3b05e8d0..b4c4e3062011a 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -1175,3 +1175,4 @@ SDL_DYNAPI_PROC(size_t,SDL_wcsnlen,(const wchar_t *a, size_t b),(a,b),return)
 SDL_DYNAPI_PROC(wchar_t*,SDL_wcsnstr,(const wchar_t *a, const wchar_t *b, size_t c),(a,b,c),return)
 SDL_DYNAPI_PROC(wchar_t*,SDL_wcsstr,(const wchar_t *a, const wchar_t *b),(a,b),return)
 SDL_DYNAPI_PROC(long,SDL_wcstol,(const wchar_t *a, wchar_t **b, int c),(a,b,c),return)
+SDL_DYNAPI_PROC(char*,SDL_strpbrk,(const char *a, const char *b),(a,b),return)
diff --git a/src/stdlib/SDL_string.c b/src/stdlib/SDL_string.c
index 0b7dad4460003..53fc7fd9b145b 100644
--- a/src/stdlib/SDL_string.c
+++ b/src/stdlib/SDL_string.c
@@ -2401,3 +2401,22 @@ int SDL_vasprintf(char **strp, SDL_PRINTF_FORMAT_STRING const char *fmt, va_list
         }
     }
 }
+
+char * SDL_strpbrk(const char *str, const char *breakset)
+{
+#ifdef HAVE_STRPBRK
+    return strpbrk(str, breakset);
+#else
+
+    for (; *str; str++) {
+        const char *b;
+
+        for (b = breakset; *b; b++) {
+            if (*str == *b) {
+                return (char *) str;
+            }
+        }
+    }
+    return NULL;
+#endif
+}
diff --git a/test/testautomation_stdlib.c b/test/testautomation_stdlib.c
index be87103ec876e..832987b616182 100644
--- a/test/testautomation_stdlib.c
+++ b/test/testautomation_stdlib.c
@@ -1188,6 +1188,46 @@ stdlib_iconv(void *arg)
     return TEST_COMPLETED;
 }
 
+
+static int
+stdlib_strpbrk(void *arg)
+{
+    struct {
+        const char *input;
+        const char *accept;
+        int expected[3]; /* negative if NULL */
+    } test_cases[] = {
+        { "",               "",             { -1, -1, -1  } },
+        { "abc",            "",             { -1, -1, -1  } },
+        { "Abc",            "a",            { -1, -1, -1  } },
+        { "abc",            "a",            {  0, -1, -1  } },
+        { "abcbd",          "bbbb",         {  1,  3, -1  } },
+        { "a;b;c",          ";",            {  1,  3, -1  } },
+        { "a;b;c",          ",",            { -1, -1, -1  } },
+        { "a:bbbb;c",       ";:",           {  1,  6, -1  } },
+        { "Hello\tS DL\n",   " \t\r\n",     {  5,  7,  10 } },
+    };
+    int i;
+
+    for (i = 0; i < SDL_arraysize(test_cases); i++) {
+        int j;
+        const char *input = test_cases[i].input;
+
+        for (j = 0; j < SDL_arraysize(test_cases[i].expected); j++) {
+            char *result;
+
+            SDLTest_AssertPass("About to call SDL_strpbrk(\"%s\", \"%s\")", input, test_cases[i].accept);
+            result = SDL_strpbrk(input, test_cases[i].accept);
+            if (test_cases[i].expected[j] < 0) {
+                SDLTest_AssertCheck(result == NULL, "Expected NULL, got %p", result);
+            } else {
+                SDLTest_AssertCheck(result == test_cases[i].input + test_cases[i].expected[j], "Expected %p, got %p", test_cases[i].input + test_cases[i].expected[j], result);
+                input = test_cases[i].input + test_cases[i].expected[j] + 1;
+            }
+        }
+    }
+    return TEST_COMPLETED;
+}
 /* ================= Test References ================== */
 
 /* Standard C routine test cases */
@@ -1227,8 +1267,12 @@ static const SDLTest_TestCaseReference stdlibTestOverflow = {
     stdlib_overflow, "stdlib_overflow", "Overflow detection", TEST_ENABLED
 };
 
-static const SDLTest_TestCaseReference stdlibIconv = {
-    stdlib_iconv, "stdlib_iconv", "Calls to iconv", TEST_ENABLED
+static const SDLTest_TestCaseReference stdlibTest_iconv = {
+    stdlib_iconv, "stdlib_iconv", "Calls to SDL_iconv", TEST_ENABLED
+};
+
+static const SDLTest_TestCaseReference stdlibTest_strpbrk = {
+    stdlib_strpbrk, "stdlib_strpbrk", "Calls to SDL_strpbrk", TEST_ENABLED
 };
 
 /* Sequence of Standard C routine test cases */
@@ -1242,7 +1286,8 @@ static const SDLTest_TestCaseReference *stdlibTests[] = {
     &stdlibTest_sscanf,
     &stdlibTest_aligned_alloc,
     &stdlibTestOverflow,
-    &stdlibIconv,
+    &stdlibTest_iconv,
+    &stdlibTest_strpbrk,
     NULL
 };