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