summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/stdlib/SDL_string.c
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
committer3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
commit5a079a2d114f96d4847d1ee305d5b7c16eeec50e (patch)
tree8926ab44f168acf787d8e19608857b3af0f82758 /contrib/SDL-3.2.8/src/stdlib/SDL_string.c
Initial commit
Diffstat (limited to 'contrib/SDL-3.2.8/src/stdlib/SDL_string.c')
-rw-r--r--contrib/SDL-3.2.8/src/stdlib/SDL_string.c2515
1 files changed, 2515 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/stdlib/SDL_string.c b/contrib/SDL-3.2.8/src/stdlib/SDL_string.c
new file mode 100644
index 0000000..007719e
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/stdlib/SDL_string.c
@@ -0,0 +1,2515 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23// This file contains portable string manipulation functions for SDL
24
25#include "SDL_vacopy.h"
26
27#ifdef SDL_PLATFORM_VITA
28#include <psp2/kernel/clib.h>
29#endif
30
31#include "SDL_sysstdlib.h"
32
33#include "SDL_casefolding.h"
34
35#if defined(__SIZEOF_WCHAR_T__)
36#define SDL_SIZEOF_WCHAR_T __SIZEOF_WCHAR_T__
37#elif defined(SDL_PLATFORM_WINDOWS)
38#define SDL_SIZEOF_WCHAR_T 2
39#else // assume everything else is UTF-32 (add more tests if compiler-assert fails below!)
40#define SDL_SIZEOF_WCHAR_T 4
41#endif
42SDL_COMPILE_TIME_ASSERT(sizeof_wchar_t, sizeof(wchar_t) == SDL_SIZEOF_WCHAR_T);
43
44
45char *SDL_UCS4ToUTF8(Uint32 codepoint, char *dst)
46{
47 if (!dst) {
48 return NULL; // I guess...?
49 } else if (codepoint > 0x10FFFF) { // Outside the range of Unicode codepoints (also, larger than can be encoded in 4 bytes of UTF-8!).
50 codepoint = SDL_INVALID_UNICODE_CODEPOINT;
51 } else if ((codepoint >= 0xD800) && (codepoint <= 0xDFFF)) { // UTF-16 surrogate values are illegal in UTF-8.
52 codepoint = SDL_INVALID_UNICODE_CODEPOINT;
53 }
54
55 Uint8 *p = (Uint8 *)dst;
56 if (codepoint <= 0x7F) {
57 *p = (Uint8)codepoint;
58 ++dst;
59 } else if (codepoint <= 0x7FF) {
60 p[0] = 0xC0 | (Uint8)((codepoint >> 6) & 0x1F);
61 p[1] = 0x80 | (Uint8)(codepoint & 0x3F);
62 dst += 2;
63 } else if (codepoint <= 0xFFFF) {
64 p[0] = 0xE0 | (Uint8)((codepoint >> 12) & 0x0F);
65 p[1] = 0x80 | (Uint8)((codepoint >> 6) & 0x3F);
66 p[2] = 0x80 | (Uint8)(codepoint & 0x3F);
67 dst += 3;
68 } else {
69 SDL_assert(codepoint <= 0x10FFFF);
70 p[0] = 0xF0 | (Uint8)((codepoint >> 18) & 0x07);
71 p[1] = 0x80 | (Uint8)((codepoint >> 12) & 0x3F);
72 p[2] = 0x80 | (Uint8)((codepoint >> 6) & 0x3F);
73 p[3] = 0x80 | (Uint8)(codepoint & 0x3F);
74 dst += 4;
75 }
76
77 return dst;
78}
79
80
81// this expects `from` and `to` to be UTF-32 encoding!
82int SDL_CaseFoldUnicode(Uint32 from, Uint32 *to)
83{
84 // !!! FIXME: since the hashtable is static, maybe we should binary
85 // !!! FIXME: search it instead of walking the whole bucket.
86
87 if (from < 128) { // low-ASCII, easy!
88 if ((from >= 'A') && (from <= 'Z')) {
89 *to = 'a' + (from - 'A');
90 return 1;
91 }
92 } else if (from <= 0xFFFF) { // the Basic Multilingual Plane.
93 const Uint8 hash = ((from ^ (from >> 8)) & 0xFF);
94 const Uint16 from16 = (Uint16) from;
95
96 // see if it maps to a single char (most common)...
97 {
98 const CaseFoldHashBucket1_16 *bucket = &case_fold_hash1_16[hash];
99 const int count = (int) bucket->count;
100 for (int i = 0; i < count; i++) {
101 const CaseFoldMapping1_16 *mapping = &bucket->list[i];
102 if (mapping->from == from16) {
103 *to = mapping->to0;
104 return 1;
105 }
106 }
107 }
108
109 // see if it folds down to two chars...
110 {
111 const CaseFoldHashBucket2_16 *bucket = &case_fold_hash2_16[hash & 15];
112 const int count = (int) bucket->count;
113 for (int i = 0; i < count; i++) {
114 const CaseFoldMapping2_16 *mapping = &bucket->list[i];
115 if (mapping->from == from16) {
116 to[0] = mapping->to0;
117 to[1] = mapping->to1;
118 return 2;
119 }
120 }
121 }
122
123 // okay, maybe it's _three_ characters!
124 {
125 const CaseFoldHashBucket3_16 *bucket = &case_fold_hash3_16[hash & 3];
126 const int count = (int) bucket->count;
127 for (int i = 0; i < count; i++) {
128 const CaseFoldMapping3_16 *mapping = &bucket->list[i];
129 if (mapping->from == from16) {
130 to[0] = mapping->to0;
131 to[1] = mapping->to1;
132 to[2] = mapping->to2;
133 return 3;
134 }
135 }
136 }
137
138 } else { // codepoint that doesn't fit in 16 bits.
139 const Uint8 hash = ((from ^ (from >> 8)) & 0xFF);
140 const CaseFoldHashBucket1_32 *bucket = &case_fold_hash1_32[hash & 15];
141 const int count = (int) bucket->count;
142 for (int i = 0; i < count; i++) {
143 const CaseFoldMapping1_32 *mapping = &bucket->list[i];
144 if (mapping->from == from) {
145 *to = mapping->to0;
146 return 1;
147 }
148 }
149 }
150
151 // Not found...there's no folding needed for this codepoint.
152 *to = from;
153 return 1;
154}
155
156#define UNICODE_STRCASECMP(bits, slen1, slen2, update_slen1, update_slen2) \
157 Uint32 folded1[3], folded2[3]; \
158 int head1 = 0, tail1 = 0, head2 = 0, tail2 = 0; \
159 while (true) { \
160 Uint32 cp1, cp2; \
161 if (head1 != tail1) { \
162 cp1 = folded1[tail1++]; \
163 } else { \
164 const Uint##bits *str1start = (const Uint##bits *) str1; \
165 head1 = SDL_CaseFoldUnicode(StepUTF##bits(&str1, slen1), folded1); \
166 update_slen1; \
167 cp1 = folded1[0]; \
168 tail1 = 1; \
169 } \
170 if (head2 != tail2) { \
171 cp2 = folded2[tail2++]; \
172 } else { \
173 const Uint##bits *str2start = (const Uint##bits *) str2; \
174 head2 = SDL_CaseFoldUnicode(StepUTF##bits(&str2, slen2), folded2); \
175 update_slen2; \
176 cp2 = folded2[0]; \
177 tail2 = 1; \
178 } \
179 if (cp1 < cp2) { \
180 return -1; \
181 } else if (cp1 > cp2) { \
182 return 1; \
183 } else if (cp1 == 0) { \
184 break; /* complete match. */ \
185 } \
186 } \
187 return 0
188
189
190static Uint32 StepUTF8(const char **_str, const size_t slen)
191{
192 /*
193 * From rfc3629, the UTF-8 spec:
194 * https://www.ietf.org/rfc/rfc3629.txt
195 *
196 * Char. number range | UTF-8 octet sequence
197 * (hexadecimal) | (binary)
198 * --------------------+---------------------------------------------
199 * 0000 0000-0000 007F | 0xxxxxxx
200 * 0000 0080-0000 07FF | 110xxxxx 10xxxxxx
201 * 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
202 * 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
203 */
204
205 const Uint8 *str = (const Uint8 *) *_str;
206 const Uint32 octet = (Uint32) (slen ? *str : 0);
207
208 if (octet == 0) { // null terminator, end of string.
209 return 0; // don't advance `*_str`.
210 } else if ((octet & 0x80) == 0) { // 0xxxxxxx: one byte codepoint.
211 (*_str)++;
212 return octet;
213 } else if (((octet & 0xE0) == 0xC0) && (slen >= 2)) { // 110xxxxx 10xxxxxx: two byte codepoint.
214 const Uint8 str1 = str[1];
215 if ((str1 & 0xC0) == 0x80) { // If trailing bytes aren't 10xxxxxx, sequence is bogus.
216 const Uint32 result = ((octet & 0x1F) << 6) | (str1 & 0x3F);
217 if (result >= 0x0080) { // rfc3629 says you can't use overlong sequences for smaller values.
218 *_str += 2;
219 return result;
220 }
221 }
222 } else if (((octet & 0xF0) == 0xE0) && (slen >= 3)) { // 1110xxxx 10xxxxxx 10xxxxxx: three byte codepoint.
223 const Uint8 str1 = str[1];
224 const Uint8 str2 = str[2];
225 if (((str1 & 0xC0) == 0x80) && ((str2 & 0xC0) == 0x80)) { // If trailing bytes aren't 10xxxxxx, sequence is bogus.
226 const Uint32 octet2 = ((Uint32) (str1 & 0x3F)) << 6;
227 const Uint32 octet3 = ((Uint32) (str2 & 0x3F));
228 const Uint32 result = ((octet & 0x0F) << 12) | octet2 | octet3;
229 if (result >= 0x800) { // rfc3629 says you can't use overlong sequences for smaller values.
230 if ((result < 0xD800) || (result > 0xDFFF)) { // UTF-16 surrogate values are illegal in UTF-8.
231 *_str += 3;
232 return result;
233 }
234 }
235 }
236 } else if (((octet & 0xF8) == 0xF0) && (slen >= 4)) { // 11110xxxx 10xxxxxx 10xxxxxx 10xxxxxx: four byte codepoint.
237 const Uint8 str1 = str[1];
238 const Uint8 str2 = str[2];
239 const Uint8 str3 = str[3];
240 if (((str1 & 0xC0) == 0x80) && ((str2 & 0xC0) == 0x80) && ((str3 & 0xC0) == 0x80)) { // If trailing bytes aren't 10xxxxxx, sequence is bogus.
241 const Uint32 octet2 = ((Uint32) (str1 & 0x1F)) << 12;
242 const Uint32 octet3 = ((Uint32) (str2 & 0x3F)) << 6;
243 const Uint32 octet4 = ((Uint32) (str3 & 0x3F));
244 const Uint32 result = ((octet & 0x07) << 18) | octet2 | octet3 | octet4;
245 if (result >= 0x10000) { // rfc3629 says you can't use overlong sequences for smaller values.
246 *_str += 4;
247 return result;
248 }
249 }
250 }
251
252 // bogus byte, skip ahead, return a REPLACEMENT CHARACTER.
253 (*_str)++;
254 return SDL_INVALID_UNICODE_CODEPOINT;
255}
256
257Uint32 SDL_StepUTF8(const char **pstr, size_t *pslen)
258{
259 if (!pslen) {
260 return StepUTF8(pstr, 4); // 4 == max codepoint size.
261 }
262 const char *origstr = *pstr;
263 const Uint32 result = StepUTF8(pstr, *pslen);
264 *pslen -= (size_t) (*pstr - origstr);
265 return result;
266}
267
268Uint32 SDL_StepBackUTF8(const char *start, const char **pstr)
269{
270 if (!pstr || *pstr <= start) {
271 return 0;
272 }
273
274 // Step back over the previous UTF-8 character
275 const char *str = *pstr;
276 do {
277 if (str == start) {
278 break;
279 }
280 --str;
281 } while ((*str & 0xC0) == 0x80);
282
283 size_t length = (*pstr - str);
284 *pstr = str;
285 return StepUTF8(&str, length);
286}
287
288#if (SDL_SIZEOF_WCHAR_T == 2)
289static Uint32 StepUTF16(const Uint16 **_str, const size_t slen)
290{
291 const Uint16 *str = *_str;
292 Uint32 cp = (Uint32) *(str++);
293 if (cp == 0) {
294 return 0; // don't advance string pointer.
295 } else if ((cp >= 0xDC00) && (cp <= 0xDFFF)) {
296 cp = SDL_INVALID_UNICODE_CODEPOINT; // Orphaned second half of surrogate pair
297 } else if ((cp >= 0xD800) && (cp <= 0xDBFF)) { // start of surrogate pair!
298 const Uint32 pair = (Uint32) *str;
299 if ((pair == 0) || ((pair < 0xDC00) || (pair > 0xDFFF))) {
300 cp = SDL_INVALID_UNICODE_CODEPOINT;
301 } else {
302 str++; // eat the other surrogate.
303 cp = 0x10000 + (((cp - 0xD800) << 10) | (pair - 0xDC00));
304 }
305 }
306
307 *_str = str;
308 return (cp > 0x10FFFF) ? SDL_INVALID_UNICODE_CODEPOINT : cp;
309}
310#elif (SDL_SIZEOF_WCHAR_T == 4)
311static Uint32 StepUTF32(const Uint32 **_str, const size_t slen)
312{
313 if (!slen) {
314 return 0;
315 }
316
317 const Uint32 *str = *_str;
318 const Uint32 cp = *str;
319 if (cp == 0) {
320 return 0; // don't advance string pointer.
321 }
322
323 (*_str)++;
324 return (cp > 0x10FFFF) ? SDL_INVALID_UNICODE_CODEPOINT : cp;
325}
326#endif
327
328#define UTF8_IsLeadByte(c) ((c) >= 0xC0 && (c) <= 0xF4)
329#define UTF8_IsTrailingByte(c) ((c) >= 0x80 && (c) <= 0xBF)
330
331static size_t UTF8_GetTrailingBytes(unsigned char c)
332{
333 if (c >= 0xC0 && c <= 0xDF) {
334 return 1;
335 } else if (c >= 0xE0 && c <= 0xEF) {
336 return 2;
337 } else if (c >= 0xF0 && c <= 0xF4) {
338 return 3;
339 }
340
341 return 0;
342}
343
344#if !defined(HAVE_VSSCANF) || !defined(HAVE_STRTOL) || !defined(HAVE_STRTOUL) || !defined(HAVE_STRTOLL) || !defined(HAVE_STRTOULL) || !defined(HAVE_STRTOD)
345/**
346 * Parses an unsigned long long and returns the unsigned value and sign bit.
347 *
348 * Positive values are clamped to ULLONG_MAX.
349 * The result `value == 0 && negative` indicates negative overflow
350 * and might need to be handled differently depending on whether a
351 * signed or unsigned integer is being parsed.
352 */
353static size_t SDL_ScanUnsignedLongLongInternal(const char *text, int count, int radix, unsigned long long *valuep, bool *negativep)
354{
355 const unsigned long long ullong_max = ~0ULL;
356
357 const char *text_start = text;
358 const char *number_start = text_start;
359 unsigned long long value = 0;
360 bool negative = false;
361 bool overflow = false;
362
363 if (radix == 0 || (radix >= 2 && radix <= 36)) {
364 while (SDL_isspace(*text)) {
365 ++text;
366 }
367 if (*text == '-' || *text == '+') {
368 negative = *text == '-';
369 ++text;
370 }
371 if ((radix == 0 || radix == 16) && *text == '0' && text[1] != '\0') {
372 ++text;
373 if (*text == 'x' || *text == 'X') {
374 radix = 16;
375 ++text;
376 } else if (radix == 0) {
377 radix = 8;
378 }
379 } else if (radix == 0) {
380 radix = 10;
381 }
382 number_start = text;
383 do {
384 unsigned long long digit;
385 if (*text >= '0' && *text <= '9') {
386 digit = *text - '0';
387 } else if (radix > 10) {
388 if (*text >= 'A' && *text < 'A' + (radix - 10)) {
389 digit = 10 + (*text - 'A');
390 } else if (*text >= 'a' && *text < 'a' + (radix - 10)) {
391 digit = 10 + (*text - 'a');
392 } else {
393 break;
394 }
395 } else {
396 break;
397 }
398 if (value != 0 && radix > ullong_max / value) {
399 overflow = true;
400 } else {
401 value *= radix;
402 if (digit > ullong_max - value) {
403 overflow = true;
404 } else {
405 value += digit;
406 }
407 }
408 ++text;
409 } while (count == 0 || (text - text_start) != count);
410 }
411 if (text == number_start) {
412 if (radix == 16 && text > text_start && (*(text - 1) == 'x' || *(text - 1) == 'X')) {
413 // the string was "0x"; consume the '0' but not the 'x'
414 --text;
415 } else {
416 // no number was parsed, and thus no characters were consumed
417 text = text_start;
418 }
419 }
420 if (overflow) {
421 if (negative) {
422 value = 0;
423 } else {
424 value = ullong_max;
425 }
426 } else if (value == 0) {
427 negative = false;
428 }
429 *valuep = value;
430 *negativep = negative;
431 return text - text_start;
432}
433#endif
434
435#ifndef HAVE_WCSTOL
436// SDL_ScanUnsignedLongLongInternalW assumes that wchar_t can be converted to int without truncating bits
437SDL_COMPILE_TIME_ASSERT(wchar_t_int, sizeof(wchar_t) <= sizeof(int));
438
439/**
440 * Parses an unsigned long long and returns the unsigned value and sign bit.
441 *
442 * Positive values are clamped to ULLONG_MAX.
443 * The result `value == 0 && negative` indicates negative overflow
444 * and might need to be handled differently depending on whether a
445 * signed or unsigned integer is being parsed.
446 */
447static size_t SDL_ScanUnsignedLongLongInternalW(const wchar_t *text, int count, int radix, unsigned long long *valuep, bool *negativep)
448{
449 const unsigned long long ullong_max = ~0ULL;
450
451 const wchar_t *text_start = text;
452 const wchar_t *number_start = text_start;
453 unsigned long long value = 0;
454 bool negative = false;
455 bool overflow = false;
456
457 if (radix == 0 || (radix >= 2 && radix <= 36)) {
458 while (SDL_isspace(*text)) {
459 ++text;
460 }
461 if (*text == '-' || *text == '+') {
462 negative = *text == '-';
463 ++text;
464 }
465 if ((radix == 0 || radix == 16) && *text == '0') {
466 ++text;
467 if (*text == 'x' || *text == 'X') {
468 radix = 16;
469 ++text;
470 } else if (radix == 0) {
471 radix = 8;
472 }
473 } else if (radix == 0) {
474 radix = 10;
475 }
476 number_start = text;
477 do {
478 unsigned long long digit;
479 if (*text >= '0' && *text <= '9') {
480 digit = *text - '0';
481 } else if (radix > 10) {
482 if (*text >= 'A' && *text < 'A' + (radix - 10)) {
483 digit = 10 + (*text - 'A');
484 } else if (*text >= 'a' && *text < 'a' + (radix - 10)) {
485 digit = 10 + (*text - 'a');
486 } else {
487 break;
488 }
489 } else {
490 break;
491 }
492 if (value != 0 && radix > ullong_max / value) {
493 overflow = true;
494 } else {
495 value *= radix;
496 if (digit > ullong_max - value) {
497 overflow = true;
498 } else {
499 value += digit;
500 }
501 }
502 ++text;
503 } while (count == 0 || (text - text_start) != count);
504 }
505 if (text == number_start) {
506 if (radix == 16 && text > text_start && (*(text - 1) == 'x' || *(text - 1) == 'X')) {
507 // the string was "0x"; consume the '0' but not the 'x'
508 --text;
509 } else {
510 // no number was parsed, and thus no characters were consumed
511 text = text_start;
512 }
513 }
514 if (overflow) {
515 if (negative) {
516 value = 0;
517 } else {
518 value = ullong_max;
519 }
520 } else if (value == 0) {
521 negative = false;
522 }
523 *valuep = value;
524 *negativep = negative;
525 return text - text_start;
526}
527#endif
528
529#if !defined(HAVE_VSSCANF) || !defined(HAVE_STRTOL)
530static size_t SDL_ScanLong(const char *text, int count, int radix, long *valuep)
531{
532 const unsigned long long_max = (~0UL) >> 1;
533 unsigned long long value;
534 bool negative;
535 size_t len = SDL_ScanUnsignedLongLongInternal(text, count, radix, &value, &negative);
536 if (negative) {
537 const unsigned long abs_long_min = long_max + 1;
538 if (value == 0 || value > abs_long_min) {
539 value = 0ULL - abs_long_min;
540 } else {
541 value = 0ULL - value;
542 }
543 } else if (value > long_max) {
544 value = long_max;
545 }
546 *valuep = (long)value;
547 return len;
548}
549#endif
550
551#ifndef HAVE_WCSTOL
552static size_t SDL_ScanLongW(const wchar_t *text, int count, int radix, long *valuep)
553{
554 const unsigned long long_max = (~0UL) >> 1;
555 unsigned long long value;
556 bool negative;
557 size_t len = SDL_ScanUnsignedLongLongInternalW(text, count, radix, &value, &negative);
558 if (negative) {
559 const unsigned long abs_long_min = long_max + 1;
560 if (value == 0 || value > abs_long_min) {
561 value = 0ULL - abs_long_min;
562 } else {
563 value = 0ULL - value;
564 }
565 } else if (value > long_max) {
566 value = long_max;
567 }
568 *valuep = (long)value;
569 return len;
570}
571#endif
572
573#if !defined(HAVE_VSSCANF) || !defined(HAVE_STRTOUL)
574static size_t SDL_ScanUnsignedLong(const char *text, int count, int radix, unsigned long *valuep)
575{
576 const unsigned long ulong_max = ~0UL;
577 unsigned long long value;
578 bool negative;
579 size_t len = SDL_ScanUnsignedLongLongInternal(text, count, radix, &value, &negative);
580 if (negative) {
581 if (value == 0 || value > ulong_max) {
582 value = ulong_max;
583 } else if (value == ulong_max) {
584 value = 1;
585 } else {
586 value = 0ULL - value;
587 }
588 } else if (value > ulong_max) {
589 value = ulong_max;
590 }
591 *valuep = (unsigned long)value;
592 return len;
593}
594#endif
595
596#ifndef HAVE_VSSCANF
597static size_t SDL_ScanUintPtrT(const char *text, uintptr_t *valuep)
598{
599 const uintptr_t uintptr_max = ~(uintptr_t)0;
600 unsigned long long value;
601 bool negative;
602 size_t len = SDL_ScanUnsignedLongLongInternal(text, 0, 16, &value, &negative);
603 if (negative) {
604 if (value == 0 || value > uintptr_max) {
605 value = uintptr_max;
606 } else if (value == uintptr_max) {
607 value = 1;
608 } else {
609 value = 0ULL - value;
610 }
611 } else if (value > uintptr_max) {
612 value = uintptr_max;
613 }
614 *valuep = (uintptr_t)value;
615 return len;
616}
617#endif
618
619#if !defined(HAVE_VSSCANF) || !defined(HAVE_STRTOLL)
620static size_t SDL_ScanLongLong(const char *text, int count, int radix, long long *valuep)
621{
622 const unsigned long long llong_max = (~0ULL) >> 1;
623 unsigned long long value;
624 bool negative;
625 size_t len = SDL_ScanUnsignedLongLongInternal(text, count, radix, &value, &negative);
626 if (negative) {
627 const unsigned long long abs_llong_min = llong_max + 1;
628 if (value == 0 || value > abs_llong_min) {
629 value = 0ULL - abs_llong_min;
630 } else {
631 value = 0ULL - value;
632 }
633 } else if (value > llong_max) {
634 value = llong_max;
635 }
636 *valuep = value;
637 return len;
638}
639#endif
640
641#if !defined(HAVE_VSSCANF) || !defined(HAVE_STRTOULL) || !defined(HAVE_STRTOD)
642static size_t SDL_ScanUnsignedLongLong(const char *text, int count, int radix, unsigned long long *valuep)
643{
644 const unsigned long long ullong_max = ~0ULL;
645 bool negative;
646 size_t len = SDL_ScanUnsignedLongLongInternal(text, count, radix, valuep, &negative);
647 if (negative) {
648 if (*valuep == 0) {
649 *valuep = ullong_max;
650 } else {
651 *valuep = 0ULL - *valuep;
652 }
653 }
654 return len;
655}
656#endif
657
658#if !defined(HAVE_VSSCANF) || !defined(HAVE_STRTOD)
659static size_t SDL_ScanFloat(const char *text, double *valuep)
660{
661 const char *text_start = text;
662 const char *number_start = text_start;
663 double value = 0.0;
664 bool negative = false;
665
666 while (SDL_isspace(*text)) {
667 ++text;
668 }
669 if (*text == '-' || *text == '+') {
670 negative = *text == '-';
671 ++text;
672 }
673 number_start = text;
674 if (SDL_isdigit(*text)) {
675 value += SDL_strtoull(text, (char **)(&text), 10);
676 if (*text == '.') {
677 double denom = 10;
678 ++text;
679 while (SDL_isdigit(*text)) {
680 value += (double)(*text - '0') / denom;
681 denom *= 10;
682 ++text;
683 }
684 }
685 }
686 if (text == number_start) {
687 // no number was parsed, and thus no characters were consumed
688 text = text_start;
689 } else if (negative) {
690 value = -value;
691 }
692 *valuep = value;
693 return text - text_start;
694}
695#endif
696
697int SDL_memcmp(const void *s1, const void *s2, size_t len)
698{
699#ifdef SDL_PLATFORM_VITA
700 /*
701 Using memcmp on NULL is UB per POSIX / C99 7.21.1/2.
702 But, both linux and bsd allow that, with an exception:
703 zero length strings are always identical, so NULLs are never dereferenced.
704 sceClibMemcmp on PSVita doesn't allow that, so we check ourselves.
705 */
706 if (len == 0) {
707 return 0;
708 }
709 return sceClibMemcmp(s1, s2, len);
710#elif defined(HAVE_MEMCMP)
711 return memcmp(s1, s2, len);
712#else
713 char *s1p = (char *)s1;
714 char *s2p = (char *)s2;
715 while (len--) {
716 if (*s1p != *s2p) {
717 return *s1p - *s2p;
718 }
719 ++s1p;
720 ++s2p;
721 }
722 return 0;
723#endif // HAVE_MEMCMP
724}
725
726size_t SDL_strlen(const char *string)
727{
728#ifdef HAVE_STRLEN
729 return strlen(string);
730#else
731 size_t len = 0;
732 while (*string++) {
733 ++len;
734 }
735 return len;
736#endif // HAVE_STRLEN
737}
738
739size_t SDL_strnlen(const char *string, size_t maxlen)
740{
741#ifdef HAVE_STRNLEN
742 return strnlen(string, maxlen);
743#else
744 size_t len = 0;
745 while (len < maxlen && *string++) {
746 ++len;
747 }
748 return len;
749#endif // HAVE_STRNLEN
750}
751
752size_t SDL_wcslen(const wchar_t *string)
753{
754#ifdef HAVE_WCSLEN
755 return wcslen(string);
756#else
757 size_t len = 0;
758 while (*string++) {
759 ++len;
760 }
761 return len;
762#endif // HAVE_WCSLEN
763}
764
765size_t SDL_wcsnlen(const wchar_t *string, size_t maxlen)
766{
767#ifdef HAVE_WCSNLEN
768 return wcsnlen(string, maxlen);
769#else
770 size_t len = 0;
771 while (len < maxlen && *string++) {
772 ++len;
773 }
774 return len;
775#endif // HAVE_WCSNLEN
776}
777
778size_t SDL_wcslcpy(SDL_OUT_Z_CAP(maxlen) wchar_t *dst, const wchar_t *src, size_t maxlen)
779{
780#ifdef HAVE_WCSLCPY
781 return wcslcpy(dst, src, maxlen);
782#else
783 size_t srclen = SDL_wcslen(src);
784 if (maxlen > 0) {
785 size_t len = SDL_min(srclen, maxlen - 1);
786 SDL_memcpy(dst, src, len * sizeof(wchar_t));
787 dst[len] = '\0';
788 }
789 return srclen;
790#endif // HAVE_WCSLCPY
791}
792
793size_t SDL_wcslcat(SDL_INOUT_Z_CAP(maxlen) wchar_t *dst, const wchar_t *src, size_t maxlen)
794{
795#ifdef HAVE_WCSLCAT
796 return wcslcat(dst, src, maxlen);
797#else
798 size_t dstlen = SDL_wcslen(dst);
799 size_t srclen = SDL_wcslen(src);
800 if (dstlen < maxlen) {
801 SDL_wcslcpy(dst + dstlen, src, maxlen - dstlen);
802 }
803 return dstlen + srclen;
804#endif // HAVE_WCSLCAT
805}
806
807wchar_t *SDL_wcsdup(const wchar_t *string)
808{
809 size_t len = ((SDL_wcslen(string) + 1) * sizeof(wchar_t));
810 wchar_t *newstr = (wchar_t *)SDL_malloc(len);
811 if (newstr) {
812 SDL_memcpy(newstr, string, len);
813 }
814 return newstr;
815}
816
817wchar_t *SDL_wcsnstr(const wchar_t *haystack, const wchar_t *needle, size_t maxlen)
818{
819 size_t length = SDL_wcslen(needle);
820 if (length == 0) {
821 return (wchar_t *)haystack;
822 }
823 while (maxlen >= length && *haystack) {
824 if (maxlen >= length && SDL_wcsncmp(haystack, needle, length) == 0) {
825 return (wchar_t *)haystack;
826 }
827 ++haystack;
828 --maxlen;
829 }
830 return NULL;
831}
832
833wchar_t *SDL_wcsstr(const wchar_t *haystack, const wchar_t *needle)
834{
835#ifdef HAVE_WCSSTR
836 return SDL_const_cast(wchar_t *, wcsstr(haystack, needle));
837#else
838 return SDL_wcsnstr(haystack, needle, SDL_wcslen(haystack));
839#endif // HAVE_WCSSTR
840}
841
842int SDL_wcscmp(const wchar_t *str1, const wchar_t *str2)
843{
844#ifdef HAVE_WCSCMP
845 return wcscmp(str1, str2);
846#else
847 while (*str1 && *str2) {
848 if (*str1 != *str2) {
849 break;
850 }
851 ++str1;
852 ++str2;
853 }
854 return *str1 - *str2;
855#endif // HAVE_WCSCMP
856}
857
858int SDL_wcsncmp(const wchar_t *str1, const wchar_t *str2, size_t maxlen)
859{
860#ifdef HAVE_WCSNCMP
861 return wcsncmp(str1, str2, maxlen);
862#else
863 while (*str1 && *str2 && maxlen) {
864 if (*str1 != *str2) {
865 break;
866 }
867 ++str1;
868 ++str2;
869 --maxlen;
870 }
871 if (!maxlen) {
872 return 0;
873 }
874 return *str1 - *str2;
875
876#endif // HAVE_WCSNCMP
877}
878
879int SDL_wcscasecmp(const wchar_t *wstr1, const wchar_t *wstr2)
880{
881#if (SDL_SIZEOF_WCHAR_T == 2)
882 const Uint16 *str1 = (const Uint16 *) wstr1;
883 const Uint16 *str2 = (const Uint16 *) wstr2;
884 UNICODE_STRCASECMP(16, 2, 2, (void) str1start, (void) str2start); // always NULL-terminated, no need to adjust lengths.
885#elif (SDL_SIZEOF_WCHAR_T == 4)
886 const Uint32 *str1 = (const Uint32 *) wstr1;
887 const Uint32 *str2 = (const Uint32 *) wstr2;
888 UNICODE_STRCASECMP(32, 1, 1, (void) str1start, (void) str2start); // always NULL-terminated, no need to adjust lengths.
889#else
890 #error Unexpected wchar_t size
891 return -1;
892#endif
893}
894
895int SDL_wcsncasecmp(const wchar_t *wstr1, const wchar_t *wstr2, size_t maxlen)
896{
897 size_t slen1 = maxlen;
898 size_t slen2 = maxlen;
899
900#if (SDL_SIZEOF_WCHAR_T == 2)
901 const Uint16 *str1 = (const Uint16 *) wstr1;
902 const Uint16 *str2 = (const Uint16 *) wstr2;
903 UNICODE_STRCASECMP(16, slen1, slen2, slen1 -= (size_t) (str1 - str1start), slen2 -= (size_t) (str2 - str2start));
904#elif (SDL_SIZEOF_WCHAR_T == 4)
905 const Uint32 *str1 = (const Uint32 *) wstr1;
906 const Uint32 *str2 = (const Uint32 *) wstr2;
907 UNICODE_STRCASECMP(32, slen1, slen2, slen1 -= (size_t) (str1 - str1start), slen2 -= (size_t) (str2 - str2start));
908#else
909 #error Unexpected wchar_t size
910 return -1;
911#endif
912}
913
914long SDL_wcstol(const wchar_t *string, wchar_t **endp, int base)
915{
916#ifdef HAVE_WCSTOL
917 return wcstol(string, endp, base);
918#else
919 long value = 0;
920 size_t len = SDL_ScanLongW(string, 0, base, &value);
921 if (endp) {
922 *endp = (wchar_t *)string + len;
923 }
924 return value;
925#endif // HAVE_WCSTOL
926}
927
928size_t SDL_strlcpy(SDL_OUT_Z_CAP(maxlen) char *dst, const char *src, size_t maxlen)
929{
930#ifdef HAVE_STRLCPY
931 return strlcpy(dst, src, maxlen);
932#else
933 size_t srclen = SDL_strlen(src);
934 if (maxlen > 0) {
935 size_t len = SDL_min(srclen, maxlen - 1);
936 SDL_memcpy(dst, src, len);
937 dst[len] = '\0';
938 }
939 return srclen;
940#endif // HAVE_STRLCPY
941}
942
943size_t SDL_utf8strlcpy(SDL_OUT_Z_CAP(dst_bytes) char *dst, const char *src, size_t dst_bytes)
944{
945 size_t bytes = 0;
946
947 if (dst_bytes > 0) {
948 size_t src_bytes = SDL_strlen(src);
949 size_t i = 0;
950 size_t trailing_bytes = 0;
951
952 bytes = SDL_min(src_bytes, dst_bytes - 1);
953 if (bytes) {
954 unsigned char c = (unsigned char)src[bytes - 1];
955 if (UTF8_IsLeadByte(c)) {
956 --bytes;
957 } else if (UTF8_IsTrailingByte(c)) {
958 for (i = bytes - 1; i != 0; --i) {
959 c = (unsigned char)src[i];
960 trailing_bytes = UTF8_GetTrailingBytes(c);
961 if (trailing_bytes) {
962 if ((bytes - i) != (trailing_bytes + 1)) {
963 bytes = i;
964 }
965
966 break;
967 }
968 }
969 }
970 SDL_memcpy(dst, src, bytes);
971 }
972 dst[bytes] = '\0';
973 }
974
975 return bytes;
976}
977
978size_t SDL_utf8strlen(const char *str)
979{
980 size_t result = 0;
981 while (SDL_StepUTF8(&str, NULL)) {
982 result++;
983 }
984 return result;
985}
986
987size_t SDL_utf8strnlen(const char *str, size_t bytes)
988{
989 size_t result = 0;
990 while (SDL_StepUTF8(&str, &bytes)) {
991 result++;
992 }
993 return result;
994}
995
996size_t SDL_strlcat(SDL_INOUT_Z_CAP(maxlen) char *dst, const char *src, size_t maxlen)
997{
998#ifdef HAVE_STRLCAT
999 return strlcat(dst, src, maxlen);
1000#else
1001 size_t dstlen = SDL_strlen(dst);
1002 size_t srclen = SDL_strlen(src);
1003 if (dstlen < maxlen) {
1004 SDL_strlcpy(dst + dstlen, src, maxlen - dstlen);
1005 }
1006 return dstlen + srclen;
1007#endif // HAVE_STRLCAT
1008}
1009
1010char *SDL_strdup(const char *string)
1011{
1012 size_t len = SDL_strlen(string) + 1;
1013 char *newstr = (char *)SDL_malloc(len);
1014 if (newstr) {
1015 SDL_memcpy(newstr, string, len);
1016 }
1017 return newstr;
1018}
1019
1020char *SDL_strndup(const char *string, size_t maxlen)
1021{
1022 size_t len = SDL_strnlen(string, maxlen);
1023 char *newstr = (char *)SDL_malloc(len + 1);
1024 if (newstr) {
1025 SDL_memcpy(newstr, string, len);
1026 newstr[len] = '\0';
1027 }
1028 return newstr;
1029}
1030
1031char *SDL_strrev(char *string)
1032{
1033#ifdef HAVE__STRREV
1034 return _strrev(string);
1035#else
1036 size_t len = SDL_strlen(string);
1037 char *a = &string[0];
1038 char *b = &string[len - 1];
1039 len /= 2;
1040 while (len--) {
1041 const char c = *a; // NOLINT(clang-analyzer-core.uninitialized.Assign)
1042 *a++ = *b;
1043 *b-- = c;
1044 }
1045 return string;
1046#endif // HAVE__STRREV
1047}
1048
1049char *SDL_strupr(char *string)
1050{
1051 char *bufp = string;
1052 while (*bufp) {
1053 *bufp = (char)SDL_toupper((unsigned char)*bufp);
1054 ++bufp;
1055 }
1056 return string;
1057}
1058
1059char *SDL_strlwr(char *string)
1060{
1061 char *bufp = string;
1062 while (*bufp) {
1063 *bufp = (char)SDL_tolower((unsigned char)*bufp);
1064 ++bufp;
1065 }
1066 return string;
1067}
1068
1069char *SDL_strchr(const char *string, int c)
1070{
1071#ifdef HAVE_STRCHR
1072 return SDL_const_cast(char *, strchr(string, c));
1073#elif defined(HAVE_INDEX)
1074 return SDL_const_cast(char *, index(string, c));
1075#else
1076 while (*string) {
1077 if (*string == c) {
1078 return (char *)string;
1079 }
1080 ++string;
1081 }
1082 if (c == '\0') {
1083 return (char *)string;
1084 }
1085 return NULL;
1086#endif // HAVE_STRCHR
1087}
1088
1089char *SDL_strrchr(const char *string, int c)
1090{
1091#ifdef HAVE_STRRCHR
1092 return SDL_const_cast(char *, strrchr(string, c));
1093#elif defined(HAVE_RINDEX)
1094 return SDL_const_cast(char *, rindex(string, c));
1095#else
1096 const char *bufp = string + SDL_strlen(string);
1097 while (bufp >= string) {
1098 if (*bufp == c) {
1099 return (char *)bufp;
1100 }
1101 --bufp;
1102 }
1103 return NULL;
1104#endif // HAVE_STRRCHR
1105}
1106
1107char *SDL_strnstr(const char *haystack, const char *needle, size_t maxlen)
1108{
1109#ifdef HAVE_STRNSTR
1110 return SDL_const_cast(char *, strnstr(haystack, needle, maxlen));
1111#else
1112 size_t length = SDL_strlen(needle);
1113 if (length == 0) {
1114 return (char *)haystack;
1115 }
1116 while (maxlen >= length && *haystack) {
1117 if (SDL_strncmp(haystack, needle, length) == 0) {
1118 return (char *)haystack;
1119 }
1120 ++haystack;
1121 --maxlen;
1122 }
1123 return NULL;
1124#endif // HAVE_STRSTR
1125}
1126
1127char *SDL_strstr(const char *haystack, const char *needle)
1128{
1129#ifdef HAVE_STRSTR
1130 return SDL_const_cast(char *, strstr(haystack, needle));
1131#else
1132 return SDL_strnstr(haystack, needle, SDL_strlen(haystack));
1133#endif // HAVE_STRSTR
1134}
1135
1136char *SDL_strcasestr(const char *haystack, const char *needle)
1137{
1138 const size_t length = SDL_strlen(needle);
1139 do {
1140 if (SDL_strncasecmp(haystack, needle, length) == 0) {
1141 return (char *)haystack;
1142 }
1143 } while (SDL_StepUTF8(&haystack, NULL)); // move ahead by a full codepoint at a time, regardless of bytes.
1144
1145 return NULL;
1146}
1147
1148#if !defined(HAVE__LTOA) || !defined(HAVE__I64TOA) || \
1149 !defined(HAVE__ULTOA) || !defined(HAVE__UI64TOA)
1150static const char ntoa_table[] = {
1151 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
1152 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
1153 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
1154 'U', 'V', 'W', 'X', 'Y', 'Z'
1155};
1156#endif // ntoa() conversion table
1157
1158char *SDL_itoa(int value, char *string, int radix)
1159{
1160#ifdef HAVE_ITOA
1161 return itoa(value, string, radix);
1162#else
1163 return SDL_ltoa((long)value, string, radix);
1164#endif // HAVE_ITOA
1165}
1166
1167char *SDL_uitoa(unsigned int value, char *string, int radix)
1168{
1169#ifdef HAVE__UITOA
1170 return _uitoa(value, string, radix);
1171#else
1172 return SDL_ultoa((unsigned long)value, string, radix);
1173#endif // HAVE__UITOA
1174}
1175
1176char *SDL_ltoa(long value, char *string, int radix)
1177{
1178#ifdef HAVE__LTOA
1179 return _ltoa(value, string, radix);
1180#else
1181 char *bufp = string;
1182
1183 if (value < 0) {
1184 *bufp++ = '-';
1185 SDL_ultoa(-value, bufp, radix);
1186 } else {
1187 SDL_ultoa(value, bufp, radix);
1188 }
1189
1190 return string;
1191#endif // HAVE__LTOA
1192}
1193
1194char *SDL_ultoa(unsigned long value, char *string, int radix)
1195{
1196#ifdef HAVE__ULTOA
1197 return _ultoa(value, string, radix);
1198#else
1199 char *bufp = string;
1200
1201 if (value) {
1202 while (value > 0) {
1203 *bufp++ = ntoa_table[value % radix];
1204 value /= radix;
1205 }
1206 } else {
1207 *bufp++ = '0';
1208 }
1209 *bufp = '\0';
1210
1211 // The numbers went into the string backwards. :)
1212 SDL_strrev(string);
1213
1214 return string;
1215#endif // HAVE__ULTOA
1216}
1217
1218char *SDL_lltoa(long long value, char *string, int radix)
1219{
1220#ifdef HAVE__I64TOA
1221 return _i64toa(value, string, radix);
1222#else
1223 char *bufp = string;
1224
1225 if (value < 0) {
1226 *bufp++ = '-';
1227 SDL_ulltoa(-value, bufp, radix);
1228 } else {
1229 SDL_ulltoa(value, bufp, radix);
1230 }
1231
1232 return string;
1233#endif // HAVE__I64TOA
1234}
1235
1236char *SDL_ulltoa(unsigned long long value, char *string, int radix)
1237{
1238#ifdef HAVE__UI64TOA
1239 return _ui64toa(value, string, radix);
1240#else
1241 char *bufp = string;
1242
1243 if (value) {
1244 while (value > 0) {
1245 *bufp++ = ntoa_table[value % radix];
1246 value /= radix;
1247 }
1248 } else {
1249 *bufp++ = '0';
1250 }
1251 *bufp = '\0';
1252
1253 // The numbers went into the string backwards. :)
1254 SDL_strrev(string);
1255
1256 return string;
1257#endif // HAVE__UI64TOA
1258}
1259
1260int SDL_atoi(const char *string)
1261{
1262#ifdef HAVE_ATOI
1263 return atoi(string);
1264#else
1265 return SDL_strtol(string, NULL, 10);
1266#endif // HAVE_ATOI
1267}
1268
1269double SDL_atof(const char *string)
1270{
1271#ifdef HAVE_ATOF
1272 return atof(string);
1273#else
1274 return SDL_strtod(string, NULL);
1275#endif // HAVE_ATOF
1276}
1277
1278long SDL_strtol(const char *string, char **endp, int base)
1279{
1280#ifdef HAVE_STRTOL
1281 return strtol(string, endp, base);
1282#else
1283 long value = 0;
1284 size_t len = SDL_ScanLong(string, 0, base, &value);
1285 if (endp) {
1286 *endp = (char *)string + len;
1287 }
1288 return value;
1289#endif // HAVE_STRTOL
1290}
1291
1292unsigned long SDL_strtoul(const char *string, char **endp, int base)
1293{
1294#ifdef HAVE_STRTOUL
1295 return strtoul(string, endp, base);
1296#else
1297 unsigned long value = 0;
1298 size_t len = SDL_ScanUnsignedLong(string, 0, base, &value);
1299 if (endp) {
1300 *endp = (char *)string + len;
1301 }
1302 return value;
1303#endif // HAVE_STRTOUL
1304}
1305
1306long long SDL_strtoll(const char *string, char **endp, int base)
1307{
1308#ifdef HAVE_STRTOLL
1309 return strtoll(string, endp, base);
1310#else
1311 long long value = 0;
1312 size_t len = SDL_ScanLongLong(string, 0, base, &value);
1313 if (endp) {
1314 *endp = (char *)string + len;
1315 }
1316 return value;
1317#endif // HAVE_STRTOLL
1318}
1319
1320unsigned long long SDL_strtoull(const char *string, char **endp, int base)
1321{
1322#ifdef HAVE_STRTOULL
1323 return strtoull(string, endp, base);
1324#else
1325 unsigned long long value = 0;
1326 size_t len = SDL_ScanUnsignedLongLong(string, 0, base, &value);
1327 if (endp) {
1328 *endp = (char *)string + len;
1329 }
1330 return value;
1331#endif // HAVE_STRTOULL
1332}
1333
1334double SDL_strtod(const char *string, char **endp)
1335{
1336#ifdef HAVE_STRTOD
1337 return strtod(string, endp);
1338#else
1339 double value;
1340 size_t len = SDL_ScanFloat(string, &value);
1341 if (endp) {
1342 *endp = (char *)string + len;
1343 }
1344 return value;
1345#endif // HAVE_STRTOD
1346}
1347
1348int SDL_strcmp(const char *str1, const char *str2)
1349{
1350#ifdef HAVE_STRCMP
1351 return strcmp(str1, str2);
1352#else
1353 int result;
1354
1355 while (1) {
1356 result = ((unsigned char)*str1 - (unsigned char)*str2);
1357 if (result != 0 || (*str1 == '\0' /* && *str2 == '\0'*/)) {
1358 break;
1359 }
1360 ++str1;
1361 ++str2;
1362 }
1363 return result;
1364#endif // HAVE_STRCMP
1365}
1366
1367int SDL_strncmp(const char *str1, const char *str2, size_t maxlen)
1368{
1369#ifdef HAVE_STRNCMP
1370 return strncmp(str1, str2, maxlen);
1371#else
1372 int result = 0;
1373
1374 while (maxlen) {
1375 result = (int)(unsigned char)*str1 - (unsigned char)*str2;
1376 if (result != 0 || *str1 == '\0' /* && *str2 == '\0'*/) {
1377 break;
1378 }
1379 ++str1;
1380 ++str2;
1381 --maxlen;
1382 }
1383 return result;
1384#endif // HAVE_STRNCMP
1385}
1386
1387int SDL_strcasecmp(const char *str1, const char *str2)
1388{
1389 UNICODE_STRCASECMP(8, 4, 4, (void) str1start, (void) str2start); // always NULL-terminated, no need to adjust lengths.
1390}
1391
1392int SDL_strncasecmp(const char *str1, const char *str2, size_t maxlen)
1393{
1394 size_t slen1 = maxlen;
1395 size_t slen2 = maxlen;
1396 UNICODE_STRCASECMP(8, slen1, slen2, slen1 -= (size_t) (str1 - ((const char *) str1start)), slen2 -= (size_t) (str2 - ((const char *) str2start)));
1397}
1398
1399int SDL_sscanf(const char *text, SDL_SCANF_FORMAT_STRING const char *fmt, ...)
1400{
1401 int rc;
1402 va_list ap;
1403 va_start(ap, fmt);
1404 rc = SDL_vsscanf(text, fmt, ap);
1405 va_end(ap);
1406 return rc;
1407}
1408
1409#ifdef HAVE_VSSCANF
1410int SDL_vsscanf(const char *text, const char *fmt, va_list ap)
1411{
1412 return vsscanf(text, fmt, ap);
1413}
1414#else
1415static bool CharacterMatchesSet(char c, const char *set, size_t set_len)
1416{
1417 bool invert = false;
1418 bool result = false;
1419
1420 if (*set == '^') {
1421 invert = true;
1422 ++set;
1423 --set_len;
1424 }
1425 while (set_len > 0 && !result) {
1426 if (set_len >= 3 && set[1] == '-') {
1427 char low_char = SDL_min(set[0], set[2]);
1428 char high_char = SDL_max(set[0], set[2]);
1429 if (c >= low_char && c <= high_char) {
1430 result = true;
1431 }
1432 set += 3;
1433 set_len -= 3;
1434 } else {
1435 if (c == *set) {
1436 result = true;
1437 }
1438 ++set;
1439 --set_len;
1440 }
1441 }
1442 if (invert) {
1443 result = !result;
1444 }
1445 return result;
1446}
1447
1448// NOLINTNEXTLINE(readability-non-const-parameter)
1449int SDL_vsscanf(const char *text, SDL_SCANF_FORMAT_STRING const char *fmt, va_list ap)
1450{
1451 const char *start = text;
1452 int result = 0;
1453
1454 if (!text || !*text) {
1455 return -1;
1456 }
1457
1458 while (*fmt) {
1459 if (*fmt == ' ') {
1460 while (SDL_isspace((unsigned char)*text)) {
1461 ++text;
1462 }
1463 ++fmt;
1464 continue;
1465 }
1466 if (*fmt == '%') {
1467 bool done = false;
1468 long count = 0;
1469 int radix = 10;
1470 enum
1471 {
1472 DO_SHORT,
1473 DO_INT,
1474 DO_LONG,
1475 DO_LONGLONG,
1476 DO_SIZE_T
1477 } inttype = DO_INT;
1478 size_t advance;
1479 bool suppress = false;
1480
1481 ++fmt;
1482 if (*fmt == '%') {
1483 if (*text == '%') {
1484 ++text;
1485 ++fmt;
1486 continue;
1487 }
1488 break;
1489 }
1490 if (*fmt == '*') {
1491 suppress = true;
1492 ++fmt;
1493 }
1494 fmt += SDL_ScanLong(fmt, 0, 10, &count);
1495
1496 if (*fmt == 'c') {
1497 if (!count) {
1498 count = 1;
1499 }
1500 if (suppress) {
1501 while (count--) {
1502 ++text;
1503 }
1504 } else {
1505 char *valuep = va_arg(ap, char *);
1506 while (count--) {
1507 *valuep++ = *text++;
1508 }
1509 ++result;
1510 }
1511 continue;
1512 }
1513
1514 while (SDL_isspace((unsigned char)*text)) {
1515 ++text;
1516 }
1517
1518 // FIXME: implement more of the format specifiers
1519 while (!done) {
1520 switch (*fmt) {
1521 case '*':
1522 suppress = true;
1523 break;
1524 case 'h':
1525 if (inttype == DO_INT) {
1526 inttype = DO_SHORT;
1527 } else if (inttype > DO_SHORT) {
1528 ++inttype;
1529 }
1530 break;
1531 case 'l':
1532 if (inttype < DO_LONGLONG) {
1533 ++inttype;
1534 }
1535 break;
1536 case 'I':
1537 if (SDL_strncmp(fmt, "I64", 3) == 0) {
1538 fmt += 2;
1539 inttype = DO_LONGLONG;
1540 }
1541 break;
1542 case 'z':
1543 inttype = DO_SIZE_T;
1544 break;
1545 case 'i':
1546 {
1547 int index = 0;
1548 if (text[index] == '-') {
1549 ++index;
1550 }
1551 if (text[index] == '0') {
1552 if (SDL_tolower((unsigned char)text[index + 1]) == 'x') {
1553 radix = 16;
1554 } else {
1555 radix = 8;
1556 }
1557 }
1558 }
1559 SDL_FALLTHROUGH;
1560 case 'd':
1561 if (inttype == DO_LONGLONG) {
1562 long long value = 0;
1563 advance = SDL_ScanLongLong(text, count, radix, &value);
1564 text += advance;
1565 if (advance && !suppress) {
1566 Sint64 *valuep = va_arg(ap, Sint64 *);
1567 *valuep = value;
1568 ++result;
1569 }
1570 } else if (inttype == DO_SIZE_T) {
1571 long long value = 0;
1572 advance = SDL_ScanLongLong(text, count, radix, &value);
1573 text += advance;
1574 if (advance && !suppress) {
1575 size_t *valuep = va_arg(ap, size_t *);
1576 *valuep = (size_t)value;
1577 ++result;
1578 }
1579 } else {
1580 long value = 0;
1581 advance = SDL_ScanLong(text, count, radix, &value);
1582 text += advance;
1583 if (advance && !suppress) {
1584 switch (inttype) {
1585 case DO_SHORT:
1586 {
1587 short *valuep = va_arg(ap, short *);
1588 *valuep = (short)value;
1589 } break;
1590 case DO_INT:
1591 {
1592 int *valuep = va_arg(ap, int *);
1593 *valuep = (int)value;
1594 } break;
1595 case DO_LONG:
1596 {
1597 long *valuep = va_arg(ap, long *);
1598 *valuep = value;
1599 } break;
1600 case DO_LONGLONG:
1601 case DO_SIZE_T:
1602 // Handled above
1603 break;
1604 }
1605 ++result;
1606 }
1607 }
1608 done = true;
1609 break;
1610 case 'o':
1611 if (radix == 10) {
1612 radix = 8;
1613 }
1614 SDL_FALLTHROUGH;
1615 case 'x':
1616 case 'X':
1617 if (radix == 10) {
1618 radix = 16;
1619 }
1620 SDL_FALLTHROUGH;
1621 case 'u':
1622 if (inttype == DO_LONGLONG) {
1623 unsigned long long value = 0;
1624 advance = SDL_ScanUnsignedLongLong(text, count, radix, &value);
1625 text += advance;
1626 if (advance && !suppress) {
1627 Uint64 *valuep = va_arg(ap, Uint64 *);
1628 *valuep = value;
1629 ++result;
1630 }
1631 } else if (inttype == DO_SIZE_T) {
1632 unsigned long long value = 0;
1633 advance = SDL_ScanUnsignedLongLong(text, count, radix, &value);
1634 text += advance;
1635 if (advance && !suppress) {
1636 size_t *valuep = va_arg(ap, size_t *);
1637 *valuep = (size_t)value;
1638 ++result;
1639 }
1640 } else {
1641 unsigned long value = 0;
1642 advance = SDL_ScanUnsignedLong(text, count, radix, &value);
1643 text += advance;
1644 if (advance && !suppress) {
1645 switch (inttype) {
1646 case DO_SHORT:
1647 {
1648 short *valuep = va_arg(ap, short *);
1649 *valuep = (short)value;
1650 } break;
1651 case DO_INT:
1652 {
1653 int *valuep = va_arg(ap, int *);
1654 *valuep = (int)value;
1655 } break;
1656 case DO_LONG:
1657 {
1658 long *valuep = va_arg(ap, long *);
1659 *valuep = value;
1660 } break;
1661 case DO_LONGLONG:
1662 case DO_SIZE_T:
1663 // Handled above
1664 break;
1665 }
1666 ++result;
1667 }
1668 }
1669 done = true;
1670 break;
1671 case 'p':
1672 {
1673 uintptr_t value = 0;
1674 advance = SDL_ScanUintPtrT(text, &value);
1675 text += advance;
1676 if (advance && !suppress) {
1677 void **valuep = va_arg(ap, void **);
1678 *valuep = (void *)value;
1679 ++result;
1680 }
1681 }
1682 done = true;
1683 break;
1684 case 'f':
1685 {
1686 double value = 0.0;
1687 advance = SDL_ScanFloat(text, &value);
1688 text += advance;
1689 if (advance && !suppress) {
1690 float *valuep = va_arg(ap, float *);
1691 *valuep = (float)value;
1692 ++result;
1693 }
1694 }
1695 done = true;
1696 break;
1697 case 's':
1698 if (suppress) {
1699 while (!SDL_isspace((unsigned char)*text)) {
1700 ++text;
1701 if (count) {
1702 if (--count == 0) {
1703 break;
1704 }
1705 }
1706 }
1707 } else {
1708 char *valuep = va_arg(ap, char *);
1709 while (!SDL_isspace((unsigned char)*text)) {
1710 *valuep++ = *text++;
1711 if (count) {
1712 if (--count == 0) {
1713 break;
1714 }
1715 }
1716 }
1717 *valuep = '\0';
1718 ++result;
1719 }
1720 done = true;
1721 break;
1722 case 'n':
1723 switch (inttype) {
1724 case DO_SHORT:
1725 {
1726 short *valuep = va_arg(ap, short *);
1727 *valuep = (short)(text - start);
1728 } break;
1729 case DO_INT:
1730 {
1731 int *valuep = va_arg(ap, int *);
1732 *valuep = (int)(text - start);
1733 } break;
1734 case DO_LONG:
1735 {
1736 long *valuep = va_arg(ap, long *);
1737 *valuep = (long)(text - start);
1738 } break;
1739 case DO_LONGLONG:
1740 {
1741 long long *valuep = va_arg(ap, long long *);
1742 *valuep = (long long)(text - start);
1743 } break;
1744 case DO_SIZE_T:
1745 {
1746 size_t *valuep = va_arg(ap, size_t *);
1747 *valuep = (size_t)(text - start);
1748 } break;
1749 }
1750 done = true;
1751 break;
1752 case '[':
1753 {
1754 const char *set = fmt + 1;
1755 while (*fmt && *fmt != ']') {
1756 ++fmt;
1757 }
1758 if (*fmt) {
1759 size_t set_len = (fmt - set);
1760 if (suppress) {
1761 while (CharacterMatchesSet(*text, set, set_len)) {
1762 ++text;
1763 if (count) {
1764 if (--count == 0) {
1765 break;
1766 }
1767 }
1768 }
1769 } else {
1770 bool had_match = false;
1771 char *valuep = va_arg(ap, char *);
1772 while (CharacterMatchesSet(*text, set, set_len)) {
1773 had_match = true;
1774 *valuep++ = *text++;
1775 if (count) {
1776 if (--count == 0) {
1777 break;
1778 }
1779 }
1780 }
1781 *valuep = '\0';
1782 if (had_match) {
1783 ++result;
1784 }
1785 }
1786 }
1787 }
1788 done = true;
1789 break;
1790 default:
1791 done = true;
1792 break;
1793 }
1794 ++fmt;
1795 }
1796 continue;
1797 }
1798 if (*text == *fmt) {
1799 ++text;
1800 ++fmt;
1801 continue;
1802 }
1803 // Text didn't match format specifier
1804 break;
1805 }
1806
1807 return result;
1808}
1809#endif // HAVE_VSSCANF
1810
1811int SDL_snprintf(SDL_OUT_Z_CAP(maxlen) char *text, size_t maxlen, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
1812{
1813 va_list ap;
1814 int result;
1815
1816 va_start(ap, fmt);
1817 result = SDL_vsnprintf(text, maxlen, fmt, ap);
1818 va_end(ap);
1819
1820 return result;
1821}
1822
1823int SDL_swprintf(SDL_OUT_Z_CAP(maxlen) wchar_t *text, size_t maxlen, SDL_PRINTF_FORMAT_STRING const wchar_t *fmt, ...)
1824{
1825 va_list ap;
1826 int result;
1827
1828 va_start(ap, fmt);
1829 result = SDL_vswprintf(text, maxlen, fmt, ap);
1830 va_end(ap);
1831
1832 return result;
1833}
1834
1835#if defined(HAVE_LIBC) && defined(__WATCOMC__)
1836// _vsnprintf() doesn't ensure nul termination
1837int SDL_vsnprintf(SDL_OUT_Z_CAP(maxlen) char *text, size_t maxlen, const char *fmt, va_list ap)
1838{
1839 int result;
1840 if (!fmt) {
1841 fmt = "";
1842 }
1843 result = _vsnprintf(text, maxlen, fmt, ap);
1844 if (maxlen > 0) {
1845 text[maxlen - 1] = '\0';
1846 }
1847 if (result < 0) {
1848 result = (int)maxlen;
1849 }
1850 return result;
1851}
1852#elif defined(HAVE_VSNPRINTF)
1853int SDL_vsnprintf(SDL_OUT_Z_CAP(maxlen) char *text, size_t maxlen, const char *fmt, va_list ap)
1854{
1855 if (!fmt) {
1856 fmt = "";
1857 }
1858 return vsnprintf(text, maxlen, fmt, ap);
1859}
1860#else
1861#define TEXT_AND_LEN_ARGS (length < maxlen) ? &text[length] : NULL, (length < maxlen) ? (maxlen - length) : 0
1862
1863// FIXME: implement more of the format specifiers
1864typedef enum
1865{
1866 SDL_CASE_NOCHANGE,
1867 SDL_CASE_LOWER,
1868 SDL_CASE_UPPER
1869} SDL_letter_case;
1870
1871typedef struct
1872{
1873 bool left_justify;
1874 bool force_sign;
1875 bool force_type; // for now: used only by float printer, ignored otherwise.
1876 bool pad_zeroes;
1877 SDL_letter_case force_case;
1878 int width;
1879 int radix;
1880 int precision;
1881} SDL_FormatInfo;
1882
1883static size_t SDL_PrintString(char *text, size_t maxlen, SDL_FormatInfo *info, const char *string)
1884{
1885 const char fill = (info && info->pad_zeroes) ? '0' : ' ';
1886 size_t width = 0;
1887 size_t filllen = 0;
1888 size_t length = 0;
1889 size_t slen, sz;
1890
1891 if (!string) {
1892 string = "(null)";
1893 }
1894
1895 sz = SDL_strlen(string);
1896 if (info && info->width > 0 && (size_t)info->width > sz) {
1897 width = info->width - sz;
1898 if (info->precision >= 0 && (size_t)info->precision < sz) {
1899 width += sz - (size_t)info->precision;
1900 }
1901
1902 filllen = SDL_min(width, maxlen);
1903 if (!info->left_justify) {
1904 SDL_memset(text, fill, filllen);
1905 text += filllen;
1906 maxlen -= filllen;
1907 length += width;
1908 filllen = 0;
1909 }
1910 }
1911
1912 SDL_strlcpy(text, string, maxlen);
1913 length += sz;
1914
1915 if (filllen > 0) {
1916 SDL_memset(text + sz, fill, filllen);
1917 length += width;
1918 }
1919
1920 if (info) {
1921 if (info->precision >= 0 && (size_t)info->precision < sz) {
1922 slen = (size_t)info->precision;
1923 if (slen < maxlen) {
1924 text[slen] = '\0';
1925 }
1926 length -= (sz - slen);
1927 }
1928 if (maxlen > 1) {
1929 if (info->force_case == SDL_CASE_LOWER) {
1930 SDL_strlwr(text);
1931 } else if (info->force_case == SDL_CASE_UPPER) {
1932 SDL_strupr(text);
1933 }
1934 }
1935 }
1936 return length;
1937}
1938
1939static size_t SDL_PrintStringW(char *text, size_t maxlen, SDL_FormatInfo *info, const wchar_t *wide_string)
1940{
1941 size_t length = 0;
1942 if (wide_string) {
1943 char *string = SDL_iconv_string("UTF-8", "WCHAR_T", (char *)(wide_string), (SDL_wcslen(wide_string) + 1) * sizeof(*wide_string));
1944 length = SDL_PrintString(TEXT_AND_LEN_ARGS, info, string);
1945 SDL_free(string);
1946 } else {
1947 length = SDL_PrintString(TEXT_AND_LEN_ARGS, info, NULL);
1948 }
1949 return length;
1950}
1951
1952static void SDL_IntPrecisionAdjust(char *num, size_t maxlen, SDL_FormatInfo *info)
1953{ // left-pad num with zeroes.
1954 size_t sz, pad, have_sign;
1955
1956 if (!info) {
1957 return;
1958 }
1959
1960 have_sign = 0;
1961 if (*num == '-' || *num == '+') {
1962 have_sign = 1;
1963 ++num;
1964 --maxlen;
1965 }
1966 sz = SDL_strlen(num);
1967 if (info->precision > 0 && sz < (size_t)info->precision) {
1968 pad = (size_t)info->precision - sz;
1969 if (pad + sz + 1 <= maxlen) { // otherwise ignore the precision
1970 SDL_memmove(num + pad, num, sz + 1);
1971 SDL_memset(num, '0', pad);
1972 }
1973 }
1974 info->precision = -1; // so that SDL_PrintString() doesn't make a mess.
1975
1976 if (info->pad_zeroes && info->width > 0 && (size_t)info->width > sz + have_sign) {
1977 /* handle here: spaces are added before the sign
1978 but zeroes must be placed _after_ the sign. */
1979 // sz hasn't changed: we ignore pad_zeroes if a precision is given.
1980 pad = (size_t)info->width - sz - have_sign;
1981 if (pad + sz + 1 <= maxlen) {
1982 SDL_memmove(num + pad, num, sz + 1);
1983 SDL_memset(num, '0', pad);
1984 }
1985 info->width = 0; // so that SDL_PrintString() doesn't make a mess.
1986 }
1987}
1988
1989static size_t SDL_PrintLong(char *text, size_t maxlen, SDL_FormatInfo *info, long value)
1990{
1991 char num[130], *p = num;
1992
1993 if (info->force_sign && value >= 0L) {
1994 *p++ = '+';
1995 }
1996
1997 SDL_ltoa(value, p, info ? info->radix : 10);
1998 SDL_IntPrecisionAdjust(num, sizeof(num), info);
1999 return SDL_PrintString(text, maxlen, info, num);
2000}
2001
2002static size_t SDL_PrintUnsignedLong(char *text, size_t maxlen, SDL_FormatInfo *info, unsigned long value)
2003{
2004 char num[130];
2005
2006 SDL_ultoa(value, num, info ? info->radix : 10);
2007 SDL_IntPrecisionAdjust(num, sizeof(num), info);
2008 return SDL_PrintString(text, maxlen, info, num);
2009}
2010
2011static size_t SDL_PrintLongLong(char *text, size_t maxlen, SDL_FormatInfo *info, long long value)
2012{
2013 char num[130], *p = num;
2014
2015 if (info->force_sign && value >= (Sint64)0) {
2016 *p++ = '+';
2017 }
2018
2019 SDL_lltoa(value, p, info ? info->radix : 10);
2020 SDL_IntPrecisionAdjust(num, sizeof(num), info);
2021 return SDL_PrintString(text, maxlen, info, num);
2022}
2023
2024static size_t SDL_PrintUnsignedLongLong(char *text, size_t maxlen, SDL_FormatInfo *info, unsigned long long value)
2025{
2026 char num[130];
2027
2028 SDL_ulltoa(value, num, info ? info->radix : 10);
2029 SDL_IntPrecisionAdjust(num, sizeof(num), info);
2030 return SDL_PrintString(text, maxlen, info, num);
2031}
2032
2033static size_t SDL_PrintFloat(char *text, size_t maxlen, SDL_FormatInfo *info, double arg, bool g)
2034{
2035 char num[327];
2036 size_t length = 0;
2037 size_t integer_length;
2038 int precision = info->precision;
2039
2040 // This isn't especially accurate, but hey, it's easy. :)
2041 unsigned long long value;
2042
2043 if (arg < 0.0 || (arg == 0.0 && 1.0 / arg < 0.0)) { // additional check for signed zero
2044 num[length++] = '-';
2045 arg = -arg;
2046 } else if (info->force_sign) {
2047 num[length++] = '+';
2048 }
2049 value = (unsigned long long)arg;
2050 integer_length = SDL_PrintUnsignedLongLong(&num[length], sizeof(num) - length, NULL, value);
2051 length += integer_length;
2052 arg -= value;
2053 if (precision < 0) {
2054 precision = 6;
2055 }
2056 if (g) {
2057 // The precision includes the integer portion
2058 precision -= SDL_min((int)integer_length, precision);
2059 }
2060 if (info->force_type || precision > 0) {
2061 const char decimal_separator = '.';
2062 double integer_value;
2063
2064 SDL_assert(length < sizeof(num));
2065 num[length++] = decimal_separator;
2066 while (precision > 1) {
2067 arg *= 10.0;
2068 arg = SDL_modf(arg, &integer_value);
2069 SDL_assert(length < sizeof(num));
2070 num[length++] = '0' + (char)integer_value;
2071 --precision;
2072 }
2073 if (precision == 1) {
2074 arg *= 10.0;
2075 integer_value = SDL_round(arg);
2076 if (integer_value == 10.0) {
2077 // Carry the one...
2078 size_t i;
2079
2080 for (i = length; i--; ) {
2081 if (num[i] == decimal_separator) {
2082 continue;
2083 }
2084 if (num[i] == '9') {
2085 num[i] = '0';
2086 if (i == 0 || num[i - 1] == '-' || num[i - 1] == '+') {
2087 SDL_memmove(&num[i+1], &num[i], length - i);
2088 num[i] = '1';
2089 ++length;
2090 break;
2091 }
2092 } else {
2093 ++num[i];
2094 break;
2095 }
2096 }
2097 SDL_assert(length < sizeof(num));
2098 num[length++] = '0';
2099 } else {
2100 SDL_assert(length < sizeof(num));
2101 num[length++] = '0' + (char)integer_value;
2102 }
2103 }
2104
2105 if (g) {
2106 // Trim trailing zeroes and decimal separator
2107 size_t i;
2108
2109 for (i = length - 1; num[i] != decimal_separator; --i) {
2110 if (num[i] == '0') {
2111 --length;
2112 } else {
2113 break;
2114 }
2115 }
2116 if (num[i] == decimal_separator) {
2117 --length;
2118 }
2119 }
2120 }
2121 num[length] = '\0';
2122
2123 info->precision = -1;
2124 length = SDL_PrintString(text, maxlen, info, num);
2125
2126 if (info->width > 0 && (size_t)info->width > length) {
2127 const char fill = info->pad_zeroes ? '0' : ' ';
2128 size_t width = info->width - length;
2129 size_t filllen, movelen;
2130
2131 filllen = SDL_min(width, maxlen);
2132 movelen = SDL_min(length, (maxlen - filllen));
2133 SDL_memmove(&text[filllen], text, movelen);
2134 SDL_memset(text, fill, filllen);
2135 length += width;
2136 }
2137 return length;
2138}
2139
2140static size_t SDL_PrintPointer(char *text, size_t maxlen, SDL_FormatInfo *info, const void *value)
2141{
2142 char num[130];
2143 size_t length;
2144
2145 if (!value) {
2146 return SDL_PrintString(text, maxlen, info, NULL);
2147 }
2148
2149 SDL_ulltoa((unsigned long long)(uintptr_t)value, num, 16);
2150 length = SDL_PrintString(text, maxlen, info, "0x");
2151 return length + SDL_PrintString(TEXT_AND_LEN_ARGS, info, num);
2152}
2153
2154// NOLINTNEXTLINE(readability-non-const-parameter)
2155int SDL_vsnprintf(SDL_OUT_Z_CAP(maxlen) char *text, size_t maxlen, SDL_PRINTF_FORMAT_STRING const char *fmt, va_list ap)
2156{
2157 size_t length = 0;
2158
2159 if (!text) {
2160 maxlen = 0;
2161 }
2162 if (!fmt) {
2163 fmt = "";
2164 }
2165 while (*fmt) {
2166 if (*fmt == '%') {
2167 bool done = false;
2168 bool check_flag;
2169 SDL_FormatInfo info;
2170 enum
2171 {
2172 DO_INT,
2173 DO_LONG,
2174 DO_LONGLONG,
2175 DO_SIZE_T
2176 } inttype = DO_INT;
2177
2178 SDL_zero(info);
2179 info.radix = 10;
2180 info.precision = -1;
2181
2182 check_flag = true;
2183 while (check_flag) {
2184 ++fmt;
2185 switch (*fmt) {
2186 case '-':
2187 info.left_justify = true;
2188 break;
2189 case '+':
2190 info.force_sign = true;
2191 break;
2192 case '#':
2193 info.force_type = true;
2194 break;
2195 case '0':
2196 info.pad_zeroes = true;
2197 break;
2198 default:
2199 check_flag = false;
2200 break;
2201 }
2202 }
2203
2204 if (*fmt >= '0' && *fmt <= '9') {
2205 info.width = SDL_strtol(fmt, (char **)&fmt, 0);
2206 } else if (*fmt == '*') {
2207 ++fmt;
2208 info.width = va_arg(ap, int);
2209 }
2210
2211 if (*fmt == '.') {
2212 ++fmt;
2213 if (*fmt >= '0' && *fmt <= '9') {
2214 info.precision = SDL_strtol(fmt, (char **)&fmt, 0);
2215 } else if (*fmt == '*') {
2216 ++fmt;
2217 info.precision = va_arg(ap, int);
2218 } else {
2219 info.precision = 0;
2220 }
2221 if (info.precision < 0) {
2222 info.precision = 0;
2223 }
2224 }
2225
2226 while (!done) {
2227 switch (*fmt) {
2228 case '%':
2229 if (length < maxlen) {
2230 text[length] = '%';
2231 }
2232 ++length;
2233 done = true;
2234 break;
2235 case 'c':
2236 // char is promoted to int when passed through (...)
2237 if (length < maxlen) {
2238 text[length] = (char)va_arg(ap, int);
2239 }
2240 ++length;
2241 done = true;
2242 break;
2243 case 'h':
2244 // short is promoted to int when passed through (...)
2245 break;
2246 case 'l':
2247 if (inttype < DO_LONGLONG) {
2248 ++inttype;
2249 }
2250 break;
2251 case 'I':
2252 if (SDL_strncmp(fmt, "I64", 3) == 0) {
2253 fmt += 2;
2254 inttype = DO_LONGLONG;
2255 }
2256 break;
2257 case 'z':
2258 inttype = DO_SIZE_T;
2259 break;
2260 case 'i':
2261 case 'd':
2262 if (info.precision >= 0) {
2263 info.pad_zeroes = false;
2264 }
2265 switch (inttype) {
2266 case DO_INT:
2267 length += SDL_PrintLong(TEXT_AND_LEN_ARGS, &info,
2268 (long)va_arg(ap, int));
2269 break;
2270 case DO_LONG:
2271 length += SDL_PrintLong(TEXT_AND_LEN_ARGS, &info,
2272 va_arg(ap, long));
2273 break;
2274 case DO_LONGLONG:
2275 length += SDL_PrintLongLong(TEXT_AND_LEN_ARGS, &info,
2276 va_arg(ap, long long));
2277 break;
2278 case DO_SIZE_T:
2279 length += SDL_PrintLongLong(TEXT_AND_LEN_ARGS, &info,
2280 va_arg(ap, size_t));
2281 break;
2282 }
2283 done = true;
2284 break;
2285 case 'p':
2286 info.force_case = SDL_CASE_LOWER;
2287 length += SDL_PrintPointer(TEXT_AND_LEN_ARGS, &info, va_arg(ap, void *));
2288 done = true;
2289 break;
2290 case 'x':
2291 info.force_case = SDL_CASE_LOWER;
2292 SDL_FALLTHROUGH;
2293 case 'X':
2294 if (info.force_case == SDL_CASE_NOCHANGE) {
2295 info.force_case = SDL_CASE_UPPER;
2296 }
2297 if (info.radix == 10) {
2298 info.radix = 16;
2299 }
2300 SDL_FALLTHROUGH;
2301 case 'o':
2302 if (info.radix == 10) {
2303 info.radix = 8;
2304 }
2305 SDL_FALLTHROUGH;
2306 case 'u':
2307 info.force_sign = false;
2308 if (info.precision >= 0) {
2309 info.pad_zeroes = false;
2310 }
2311 switch (inttype) {
2312 case DO_INT:
2313 length += SDL_PrintUnsignedLong(TEXT_AND_LEN_ARGS, &info,
2314 va_arg(ap, unsigned int));
2315 break;
2316 case DO_LONG:
2317 length += SDL_PrintUnsignedLong(TEXT_AND_LEN_ARGS, &info,
2318 va_arg(ap, unsigned long));
2319 break;
2320 case DO_LONGLONG:
2321 length += SDL_PrintUnsignedLongLong(TEXT_AND_LEN_ARGS, &info,
2322 va_arg(ap, unsigned long long));
2323 break;
2324 case DO_SIZE_T:
2325 length += SDL_PrintUnsignedLongLong(TEXT_AND_LEN_ARGS, &info,
2326 va_arg(ap, size_t));
2327 break;
2328 }
2329 done = true;
2330 break;
2331 case 'f':
2332 length += SDL_PrintFloat(TEXT_AND_LEN_ARGS, &info, va_arg(ap, double), false);
2333 done = true;
2334 break;
2335 case 'g':
2336 length += SDL_PrintFloat(TEXT_AND_LEN_ARGS, &info, va_arg(ap, double), true);
2337 done = true;
2338 break;
2339 case 'S':
2340 info.pad_zeroes = false;
2341 length += SDL_PrintStringW(TEXT_AND_LEN_ARGS, &info, va_arg(ap, wchar_t *));
2342 done = true;
2343 break;
2344 case 's':
2345 info.pad_zeroes = false;
2346 if (inttype > DO_INT) {
2347 length += SDL_PrintStringW(TEXT_AND_LEN_ARGS, &info, va_arg(ap, wchar_t *));
2348 } else {
2349 length += SDL_PrintString(TEXT_AND_LEN_ARGS, &info, va_arg(ap, char *));
2350 }
2351 done = true;
2352 break;
2353 default:
2354 done = true;
2355 break;
2356 }
2357 ++fmt;
2358 }
2359 } else {
2360 if (length < maxlen) {
2361 text[length] = *fmt;
2362 }
2363 ++fmt;
2364 ++length;
2365 }
2366 }
2367 if (length < maxlen) {
2368 text[length] = '\0';
2369 } else if (maxlen > 0) {
2370 text[maxlen - 1] = '\0';
2371 }
2372 return (int)length;
2373}
2374
2375#undef TEXT_AND_LEN_ARGS
2376#endif // HAVE_VSNPRINTF
2377
2378int SDL_vswprintf(SDL_OUT_Z_CAP(maxlen) wchar_t *text, size_t maxlen, const wchar_t *fmt, va_list ap)
2379{
2380 char *fmt_utf8 = NULL;
2381 if (fmt) {
2382 fmt_utf8 = SDL_iconv_string("UTF-8", "WCHAR_T", (const char *)fmt, (SDL_wcslen(fmt) + 1) * sizeof(wchar_t));
2383 if (!fmt_utf8) {
2384 return -1;
2385 }
2386 }
2387
2388 char tinybuf[64]; // for really small strings, calculate it once.
2389
2390 // generate the text to find the final text length
2391 va_list aq;
2392 va_copy(aq, ap);
2393 const int utf8len = SDL_vsnprintf(tinybuf, sizeof (tinybuf), fmt_utf8, aq);
2394 va_end(aq);
2395
2396 if (utf8len < 0) {
2397 SDL_free(fmt_utf8);
2398 return -1;
2399 }
2400
2401 bool isstack = false;
2402 char *smallbuf = NULL;
2403 char *utf8buf;
2404 int result;
2405
2406 if (utf8len < sizeof (tinybuf)) { // whole thing fit in the stack buffer, just use that copy.
2407 utf8buf = tinybuf;
2408 } else { // didn't fit in the stack buffer, allocate the needed space and run it again.
2409 utf8buf = smallbuf = SDL_small_alloc(char, utf8len + 1, &isstack);
2410 if (!smallbuf) {
2411 SDL_free(fmt_utf8);
2412 return -1; // oh well.
2413 }
2414 const int utf8len2 = SDL_vsnprintf(smallbuf, utf8len + 1, fmt_utf8, ap);
2415 if (utf8len2 > utf8len) {
2416 SDL_free(fmt_utf8);
2417 return SDL_SetError("Formatted output changed between two runs"); // race condition on the parameters, and we no longer have room...yikes.
2418 }
2419 }
2420
2421 SDL_free(fmt_utf8);
2422
2423 wchar_t *wbuf = (wchar_t *)SDL_iconv_string("WCHAR_T", "UTF-8", utf8buf, utf8len + 1);
2424 if (wbuf) {
2425 if (text) {
2426 SDL_wcslcpy(text, wbuf, maxlen);
2427 }
2428 result = (int)SDL_wcslen(wbuf);
2429 SDL_free(wbuf);
2430 } else {
2431 result = -1;
2432 }
2433
2434 if (smallbuf != NULL) {
2435 SDL_small_free(smallbuf, isstack);
2436 }
2437
2438 return result;
2439}
2440
2441int SDL_asprintf(char **strp, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
2442{
2443 va_list ap;
2444 int result;
2445
2446 va_start(ap, fmt);
2447 result = SDL_vasprintf(strp, fmt, ap);
2448 va_end(ap);
2449
2450 return result;
2451}
2452
2453int SDL_vasprintf(char **strp, SDL_PRINTF_FORMAT_STRING const char *fmt, va_list ap)
2454{
2455 int result;
2456 int size = 100; // Guess we need no more than 100 bytes
2457 char *p, *np;
2458 va_list aq;
2459
2460 *strp = NULL;
2461
2462 p = (char *)SDL_malloc(size);
2463 if (!p) {
2464 return -1;
2465 }
2466
2467 while (1) {
2468 // Try to print in the allocated space
2469 va_copy(aq, ap);
2470 result = SDL_vsnprintf(p, size, fmt, aq);
2471 va_end(aq);
2472
2473 // Check error code
2474 if (result < 0) {
2475 SDL_free(p);
2476 return result;
2477 }
2478
2479 // If that worked, return the string
2480 if (result < size) {
2481 *strp = p;
2482 return result;
2483 }
2484
2485 // Else try again with more space
2486 size = result + 1; // Precisely what is needed
2487
2488 np = (char *)SDL_realloc(p, size);
2489 if (!np) {
2490 SDL_free(p);
2491 return -1;
2492 } else {
2493 p = np;
2494 }
2495 }
2496}
2497
2498char * SDL_strpbrk(const char *str, const char *breakset)
2499{
2500#ifdef HAVE_STRPBRK
2501 return strpbrk(str, breakset);
2502#else
2503
2504 for (; *str; str++) {
2505 const char *b;
2506
2507 for (b = breakset; *b; b++) {
2508 if (*str == *b) {
2509 return (char *) str;
2510 }
2511 }
2512 }
2513 return NULL;
2514#endif
2515}