1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2024 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') {
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 int result = 0;
1452
1453 if (!text || !*text) {
1454 return -1;
1455 }
1456
1457 while (*fmt) {
1458 if (*fmt == ' ') {
1459 while (SDL_isspace((unsigned char)*text)) {
1460 ++text;
1461 }
1462 ++fmt;
1463 continue;
1464 }
1465 if (*fmt == '%') {
1466 bool done = false;
1467 long count = 0;
1468 int radix = 10;
1469 enum
1470 {
1471 DO_SHORT,
1472 DO_INT,
1473 DO_LONG,
1474 DO_LONGLONG,
1475 DO_SIZE_T
1476 } inttype = DO_INT;
1477 size_t advance;
1478 bool suppress = false;
1479
1480 ++fmt;
1481 if (*fmt == '%') {
1482 if (*text == '%') {
1483 ++text;
1484 ++fmt;
1485 continue;
1486 }
1487 break;
1488 }
1489 if (*fmt == '*') {
1490 suppress = true;
1491 ++fmt;
1492 }
1493 fmt += SDL_ScanLong(fmt, 0, 10, &count);
1494
1495 if (*fmt == 'c') {
1496 if (!count) {
1497 count = 1;
1498 }
1499 if (suppress) {
1500 while (count--) {
1501 ++text;
1502 }
1503 } else {
1504 char *valuep = va_arg(ap, char *);
1505 while (count--) {
1506 *valuep++ = *text++;
1507 }
1508 ++result;
1509 }
1510 continue;
1511 }
1512
1513 while (SDL_isspace((unsigned char)*text)) {
1514 ++text;
1515 }
1516
1517 // FIXME: implement more of the format specifiers
1518 while (!done) {
1519 switch (*fmt) {
1520 case '*':
1521 suppress = true;
1522 break;
1523 case 'h':
1524 if (inttype == DO_INT) {
1525 inttype = DO_SHORT;
1526 } else if (inttype > DO_SHORT) {
1527 ++inttype;
1528 }
1529 break;
1530 case 'l':
1531 if (inttype < DO_LONGLONG) {
1532 ++inttype;
1533 }
1534 break;
1535 case 'I':
1536 if (SDL_strncmp(fmt, "I64", 3) == 0) {
1537 fmt += 2;
1538 inttype = DO_LONGLONG;
1539 }
1540 break;
1541 case 'z':
1542 inttype = DO_SIZE_T;
1543 break;
1544 case 'i':
1545 {
1546 int index = 0;
1547 if (text[index] == '-') {
1548 ++index;
1549 }
1550 if (text[index] == '0') {
1551 if (SDL_tolower((unsigned char)text[index + 1]) == 'x') {
1552 radix = 16;
1553 } else {
1554 radix = 8;
1555 }
1556 }
1557 }
1558 SDL_FALLTHROUGH;
1559 case 'd':
1560 if (inttype == DO_LONGLONG) {
1561 long long value = 0;
1562 advance = SDL_ScanLongLong(text, count, radix, &value);
1563 text += advance;
1564 if (advance && !suppress) {
1565 Sint64 *valuep = va_arg(ap, Sint64 *);
1566 *valuep = value;
1567 ++result;
1568 }
1569 } else if (inttype == DO_SIZE_T) {
1570 long long value = 0;
1571 advance = SDL_ScanLongLong(text, count, radix, &value);
1572 text += advance;
1573 if (advance && !suppress) {
1574 size_t *valuep = va_arg(ap, size_t *);
1575 *valuep = (size_t)value;
1576 ++result;
1577 }
1578 } else {
1579 long value = 0;
1580 advance = SDL_ScanLong(text, count, radix, &value);
1581 text += advance;
1582 if (advance && !suppress) {
1583 switch (inttype) {
1584 case DO_SHORT:
1585 {
1586 short *valuep = va_arg(ap, short *);
1587 *valuep = (short)value;
1588 } break;
1589 case DO_INT:
1590 {
1591 int *valuep = va_arg(ap, int *);
1592 *valuep = (int)value;
1593 } break;
1594 case DO_LONG:
1595 {
1596 long *valuep = va_arg(ap, long *);
1597 *valuep = value;
1598 } break;
1599 case DO_LONGLONG:
1600 case DO_SIZE_T:
1601 // Handled above
1602 break;
1603 }
1604 ++result;
1605 }
1606 }
1607 done = true;
1608 break;
1609 case 'o':
1610 if (radix == 10) {
1611 radix = 8;
1612 }
1613 SDL_FALLTHROUGH;
1614 case 'x':
1615 case 'X':
1616 if (radix == 10) {
1617 radix = 16;
1618 }
1619 SDL_FALLTHROUGH;
1620 case 'u':
1621 if (inttype == DO_LONGLONG) {
1622 unsigned long long value = 0;
1623 advance = SDL_ScanUnsignedLongLong(text, count, radix, &value);
1624 text += advance;
1625 if (advance && !suppress) {
1626 Uint64 *valuep = va_arg(ap, Uint64 *);
1627 *valuep = value;
1628 ++result;
1629 }
1630 } else if (inttype == DO_SIZE_T) {
1631 unsigned long long value = 0;
1632 advance = SDL_ScanUnsignedLongLong(text, count, radix, &value);
1633 text += advance;
1634 if (advance && !suppress) {
1635 size_t *valuep = va_arg(ap, size_t *);
1636 *valuep = (size_t)value;
1637 ++result;
1638 }
1639 } else {
1640 unsigned long value = 0;
1641 advance = SDL_ScanUnsignedLong(text, count, radix, &value);
1642 text += advance;
1643 if (advance && !suppress) {
1644 switch (inttype) {
1645 case DO_SHORT:
1646 {
1647 short *valuep = va_arg(ap, short *);
1648 *valuep = (short)value;
1649 } break;
1650 case DO_INT:
1651 {
1652 int *valuep = va_arg(ap, int *);
1653 *valuep = (int)value;
1654 } break;
1655 case DO_LONG:
1656 {
1657 long *valuep = va_arg(ap, long *);
1658 *valuep = value;
1659 } break;
1660 case DO_LONGLONG:
1661 case DO_SIZE_T:
1662 // Handled above
1663 break;
1664 }
1665 ++result;
1666 }
1667 }
1668 done = true;
1669 break;
1670 case 'p':
1671 {
1672 uintptr_t value = 0;
1673 advance = SDL_ScanUintPtrT(text, &value);
1674 text += advance;
1675 if (advance && !suppress) {
1676 void **valuep = va_arg(ap, void **);
1677 *valuep = (void *)value;
1678 ++result;
1679 }
1680 }
1681 done = true;
1682 break;
1683 case 'f':
1684 {
1685 double value = 0.0;
1686 advance = SDL_ScanFloat(text, &value);
1687 text += advance;
1688 if (advance && !suppress) {
1689 float *valuep = va_arg(ap, float *);
1690 *valuep = (float)value;
1691 ++result;
1692 }
1693 }
1694 done = true;
1695 break;
1696 case 's':
1697 if (suppress) {
1698 while (!SDL_isspace((unsigned char)*text)) {
1699 ++text;
1700 if (count) {
1701 if (--count == 0) {
1702 break;
1703 }
1704 }
1705 }
1706 } else {
1707 char *valuep = va_arg(ap, char *);
1708 while (!SDL_isspace((unsigned char)*text)) {
1709 *valuep++ = *text++;
1710 if (count) {
1711 if (--count == 0) {
1712 break;
1713 }
1714 }
1715 }
1716 *valuep = '\0';
1717 ++result;
1718 }
1719 done = true;
1720 break;
1721 case '[':
1722 {
1723 const char *set = fmt + 1;
1724 while (*fmt && *fmt != ']') {
1725 ++fmt;
1726 }
1727 if (*fmt) {
1728 size_t set_len = (fmt - set);
1729 if (suppress) {
1730 while (CharacterMatchesSet(*text, set, set_len)) {
1731 ++text;
1732 if (count) {
1733 if (--count == 0) {
1734 break;
1735 }
1736 }
1737 }
1738 } else {
1739 bool had_match = false;
1740 char *valuep = va_arg(ap, char *);
1741 while (CharacterMatchesSet(*text, set, set_len)) {
1742 had_match = true;
1743 *valuep++ = *text++;
1744 if (count) {
1745 if (--count == 0) {
1746 break;
1747 }
1748 }
1749 }
1750 *valuep = '\0';
1751 if (had_match) {
1752 ++result;
1753 }
1754 }
1755 }
1756 }
1757 done = true;
1758 break;
1759 default:
1760 done = true;
1761 break;
1762 }
1763 ++fmt;
1764 }
1765 continue;
1766 }
1767 if (*text == *fmt) {
1768 ++text;
1769 ++fmt;
1770 continue;
1771 }
1772 // Text didn't match format specifier
1773 break;
1774 }
1775
1776 return result;
1777}
1778#endif // HAVE_VSSCANF
1779
1780int SDL_snprintf(SDL_OUT_Z_CAP(maxlen) char *text, size_t maxlen, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
1781{
1782 va_list ap;
1783 int result;
1784
1785 va_start(ap, fmt);
1786 result = SDL_vsnprintf(text, maxlen, fmt, ap);
1787 va_end(ap);
1788
1789 return result;
1790}
1791
1792int SDL_swprintf(SDL_OUT_Z_CAP(maxlen) wchar_t *text, size_t maxlen, SDL_PRINTF_FORMAT_STRING const wchar_t *fmt, ...)
1793{
1794 va_list ap;
1795 int result;
1796
1797 va_start(ap, fmt);
1798 result = SDL_vswprintf(text, maxlen, fmt, ap);
1799 va_end(ap);
1800
1801 return result;
1802}
1803
1804#if defined(HAVE_LIBC) && defined(__WATCOMC__)
1805// _vsnprintf() doesn't ensure nul termination
1806int SDL_vsnprintf(SDL_OUT_Z_CAP(maxlen) char *text, size_t maxlen, const char *fmt, va_list ap)
1807{
1808 int result;
1809 if (!fmt) {
1810 fmt = "";
1811 }
1812 result = _vsnprintf(text, maxlen, fmt, ap);
1813 if (maxlen > 0) {
1814 text[maxlen - 1] = '\0';
1815 }
1816 if (result < 0) {
1817 result = (int)maxlen;
1818 }
1819 return result;
1820}
1821#elif defined(HAVE_VSNPRINTF)
1822int SDL_vsnprintf(SDL_OUT_Z_CAP(maxlen) char *text, size_t maxlen, const char *fmt, va_list ap)
1823{
1824 if (!fmt) {
1825 fmt = "";
1826 }
1827 return vsnprintf(text, maxlen, fmt, ap);
1828}
1829#else
1830#define TEXT_AND_LEN_ARGS (length < maxlen) ? &text[length] : NULL, (length < maxlen) ? (maxlen - length) : 0
1831
1832// FIXME: implement more of the format specifiers
1833typedef enum
1834{
1835 SDL_CASE_NOCHANGE,
1836 SDL_CASE_LOWER,
1837 SDL_CASE_UPPER
1838} SDL_letter_case;
1839
1840typedef struct
1841{
1842 bool left_justify;
1843 bool force_sign;
1844 bool force_type; // for now: used only by float printer, ignored otherwise.
1845 bool pad_zeroes;
1846 SDL_letter_case force_case;
1847 int width;
1848 int radix;
1849 int precision;
1850} SDL_FormatInfo;
1851
1852static size_t SDL_PrintString(char *text, size_t maxlen, SDL_FormatInfo *info, const char *string)
1853{
1854 const char fill = (info && info->pad_zeroes) ? '0' : ' ';
1855 size_t width = 0;
1856 size_t filllen = 0;
1857 size_t length = 0;
1858 size_t slen, sz;
1859
1860 if (!string) {
1861 string = "(null)";
1862 }
1863
1864 sz = SDL_strlen(string);
1865 if (info && info->width > 0 && (size_t)info->width > sz) {
1866 width = info->width - sz;
1867 if (info->precision >= 0 && (size_t)info->precision < sz) {
1868 width += sz - (size_t)info->precision;
1869 }
1870
1871 filllen = SDL_min(width, maxlen);
1872 if (!info->left_justify) {
1873 SDL_memset(text, fill, filllen);
1874 text += filllen;
1875 maxlen -= filllen;
1876 length += width;
1877 filllen = 0;
1878 }
1879 }
1880
1881 SDL_strlcpy(text, string, maxlen);
1882 length += sz;
1883
1884 if (filllen > 0) {
1885 SDL_memset(text + sz, fill, filllen);
1886 length += width;
1887 }
1888
1889 if (info) {
1890 if (info->precision >= 0 && (size_t)info->precision < sz) {
1891 slen = (size_t)info->precision;
1892 if (slen < maxlen) {
1893 text[slen] = '\0';
1894 }
1895 length -= (sz - slen);
1896 }
1897 if (maxlen > 1) {
1898 if (info->force_case == SDL_CASE_LOWER) {
1899 SDL_strlwr(text);
1900 } else if (info->force_case == SDL_CASE_UPPER) {
1901 SDL_strupr(text);
1902 }
1903 }
1904 }
1905 return length;
1906}
1907
1908static size_t SDL_PrintStringW(char *text, size_t maxlen, SDL_FormatInfo *info, const wchar_t *wide_string)
1909{
1910 size_t length = 0;
1911 if (wide_string) {
1912 char *string = SDL_iconv_string("UTF-8", "WCHAR_T", (char *)(wide_string), (SDL_wcslen(wide_string) + 1) * sizeof(*wide_string));
1913 length = SDL_PrintString(TEXT_AND_LEN_ARGS, info, string);
1914 SDL_free(string);
1915 } else {
1916 length = SDL_PrintString(TEXT_AND_LEN_ARGS, info, NULL);
1917 }
1918 return length;
1919}
1920
1921static void SDL_IntPrecisionAdjust(char *num, size_t maxlen, SDL_FormatInfo *info)
1922{ // left-pad num with zeroes.
1923 size_t sz, pad, have_sign;
1924
1925 if (!info) {
1926 return;
1927 }
1928
1929 have_sign = 0;
1930 if (*num == '-' || *num == '+') {
1931 have_sign = 1;
1932 ++num;
1933 --maxlen;
1934 }
1935 sz = SDL_strlen(num);
1936 if (info->precision > 0 && sz < (size_t)info->precision) {
1937 pad = (size_t)info->precision - sz;
1938 if (pad + sz + 1 <= maxlen) { // otherwise ignore the precision
1939 SDL_memmove(num + pad, num, sz + 1);
1940 SDL_memset(num, '0', pad);
1941 }
1942 }
1943 info->precision = -1; // so that SDL_PrintString() doesn't make a mess.
1944
1945 if (info->pad_zeroes && info->width > 0 && (size_t)info->width > sz + have_sign) {
1946 /* handle here: spaces are added before the sign
1947 but zeroes must be placed _after_ the sign. */
1948 // sz hasn't changed: we ignore pad_zeroes if a precision is given.
1949 pad = (size_t)info->width - sz - have_sign;
1950 if (pad + sz + 1 <= maxlen) {
1951 SDL_memmove(num + pad, num, sz + 1);
1952 SDL_memset(num, '0', pad);
1953 }
1954 info->width = 0; // so that SDL_PrintString() doesn't make a mess.
1955 }
1956}
1957
1958static size_t SDL_PrintLong(char *text, size_t maxlen, SDL_FormatInfo *info, long value)
1959{
1960 char num[130], *p = num;
1961
1962 if (info->force_sign && value >= 0L) {
1963 *p++ = '+';
1964 }
1965
1966 SDL_ltoa(value, p, info ? info->radix : 10);
1967 SDL_IntPrecisionAdjust(num, sizeof(num), info);
1968 return SDL_PrintString(text, maxlen, info, num);
1969}
1970
1971static size_t SDL_PrintUnsignedLong(char *text, size_t maxlen, SDL_FormatInfo *info, unsigned long value)
1972{
1973 char num[130];
1974
1975 SDL_ultoa(value, num, info ? info->radix : 10);
1976 SDL_IntPrecisionAdjust(num, sizeof(num), info);
1977 return SDL_PrintString(text, maxlen, info, num);
1978}
1979
1980static size_t SDL_PrintLongLong(char *text, size_t maxlen, SDL_FormatInfo *info, long long value)
1981{
1982 char num[130], *p = num;
1983
1984 if (info->force_sign && value >= (Sint64)0) {
1985 *p++ = '+';
1986 }
1987
1988 SDL_lltoa(value, p, info ? info->radix : 10);
1989 SDL_IntPrecisionAdjust(num, sizeof(num), info);
1990 return SDL_PrintString(text, maxlen, info, num);
1991}
1992
1993static size_t SDL_PrintUnsignedLongLong(char *text, size_t maxlen, SDL_FormatInfo *info, unsigned long long value)
1994{
1995 char num[130];
1996
1997 SDL_ulltoa(value, num, 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_PrintFloat(char *text, size_t maxlen, SDL_FormatInfo *info, double arg, bool g)
2003{
2004 char num[327];
2005 size_t length = 0;
2006 size_t integer_length;
2007 int precision = info->precision;
2008
2009 // This isn't especially accurate, but hey, it's easy. :)
2010 unsigned long long value;
2011
2012 if (arg < 0.0 || (arg == 0.0 && 1.0 / arg < 0.0)) { // additional check for signed zero
2013 num[length++] = '-';
2014 arg = -arg;
2015 } else if (info->force_sign) {
2016 num[length++] = '+';
2017 }
2018 value = (unsigned long long)arg;
2019 integer_length = SDL_PrintUnsignedLongLong(&num[length], sizeof(num) - length, NULL, value);
2020 length += integer_length;
2021 arg -= value;
2022 if (precision < 0) {
2023 precision = 6;
2024 }
2025 if (g) {
2026 // The precision includes the integer portion
2027 precision -= SDL_min((int)integer_length, precision);
2028 }
2029 if (info->force_type || precision > 0) {
2030 const char decimal_separator = '.';
2031 double integer_value;
2032
2033 SDL_assert(length < sizeof(num));
2034 num[length++] = decimal_separator;
2035 while (precision > 1) {
2036 arg *= 10.0;
2037 arg = SDL_modf(arg, &integer_value);
2038 SDL_assert(length < sizeof(num));
2039 num[length++] = '0' + (char)integer_value;
2040 --precision;
2041 }
2042 if (precision == 1) {
2043 arg *= 10.0;
2044 integer_value = SDL_round(arg);
2045 if (integer_value == 10.0) {
2046 // Carry the one...
2047 size_t i;
2048
2049 for (i = length; i--; ) {
2050 if (num[i] == decimal_separator) {
2051 continue;
2052 }
2053 if (num[i] == '9') {
2054 num[i] = '0';
2055 if (i == 0 || num[i - 1] == '-' || num[i - 1] == '+') {
2056 SDL_memmove(&num[i+1], &num[i], length - i);
2057 num[i] = '1';
2058 ++length;
2059 break;
2060 }
2061 } else {
2062 ++num[i];
2063 break;
2064 }
2065 }
2066 SDL_assert(length < sizeof(num));
2067 num[length++] = '0';
2068 } else {
2069 SDL_assert(length < sizeof(num));
2070 num[length++] = '0' + (char)integer_value;
2071 }
2072 }
2073
2074 if (g) {
2075 // Trim trailing zeroes and decimal separator
2076 size_t i;
2077
2078 for (i = length - 1; num[i] != decimal_separator; --i) {
2079 if (num[i] == '0') {
2080 --length;
2081 } else {
2082 break;
2083 }
2084 }
2085 if (num[i] == decimal_separator) {
2086 --length;
2087 }
2088 }
2089 }
2090 num[length] = '\0';
2091
2092 info->precision = -1;
2093 length = SDL_PrintString(text, maxlen, info, num);
2094
2095 if (info->width > 0 && (size_t)info->width > length) {
2096 const char fill = info->pad_zeroes ? '0' : ' ';
2097 size_t width = info->width - length;
2098 size_t filllen, movelen;
2099
2100 filllen = SDL_min(width, maxlen);
2101 movelen = SDL_min(length, (maxlen - filllen));
2102 SDL_memmove(&text[filllen], text, movelen);
2103 SDL_memset(text, fill, filllen);
2104 length += width;
2105 }
2106 return length;
2107}
2108
2109static size_t SDL_PrintPointer(char *text, size_t maxlen, SDL_FormatInfo *info, const void *value)
2110{
2111 char num[130];
2112 size_t length;
2113
2114 if (!value) {
2115 return SDL_PrintString(text, maxlen, info, NULL);
2116 }
2117
2118 SDL_ulltoa((unsigned long long)(uintptr_t)value, num, 16);
2119 length = SDL_PrintString(text, maxlen, info, "0x");
2120 return length + SDL_PrintString(TEXT_AND_LEN_ARGS, info, num);
2121}
2122
2123// NOLINTNEXTLINE(readability-non-const-parameter)
2124int SDL_vsnprintf(SDL_OUT_Z_CAP(maxlen) char *text, size_t maxlen, SDL_PRINTF_FORMAT_STRING const char *fmt, va_list ap)
2125{
2126 size_t length = 0;
2127
2128 if (!text) {
2129 maxlen = 0;
2130 }
2131 if (!fmt) {
2132 fmt = "";
2133 }
2134 while (*fmt) {
2135 if (*fmt == '%') {
2136 bool done = false;
2137 bool check_flag;
2138 SDL_FormatInfo info;
2139 enum
2140 {
2141 DO_INT,
2142 DO_LONG,
2143 DO_LONGLONG,
2144 DO_SIZE_T
2145 } inttype = DO_INT;
2146
2147 SDL_zero(info);
2148 info.radix = 10;
2149 info.precision = -1;
2150
2151 check_flag = true;
2152 while (check_flag) {
2153 ++fmt;
2154 switch (*fmt) {
2155 case '-':
2156 info.left_justify = true;
2157 break;
2158 case '+':
2159 info.force_sign = true;
2160 break;
2161 case '#':
2162 info.force_type = true;
2163 break;
2164 case '0':
2165 info.pad_zeroes = true;
2166 break;
2167 default:
2168 check_flag = false;
2169 break;
2170 }
2171 }
2172
2173 if (*fmt >= '0' && *fmt <= '9') {
2174 info.width = SDL_strtol(fmt, (char **)&fmt, 0);
2175 } else if (*fmt == '*') {
2176 ++fmt;
2177 info.width = va_arg(ap, int);
2178 }
2179
2180 if (*fmt == '.') {
2181 ++fmt;
2182 if (*fmt >= '0' && *fmt <= '9') {
2183 info.precision = SDL_strtol(fmt, (char **)&fmt, 0);
2184 } else if (*fmt == '*') {
2185 ++fmt;
2186 info.precision = va_arg(ap, int);
2187 } else {
2188 info.precision = 0;
2189 }
2190 if (info.precision < 0) {
2191 info.precision = 0;
2192 }
2193 }
2194
2195 while (!done) {
2196 switch (*fmt) {
2197 case '%':
2198 if (length < maxlen) {
2199 text[length] = '%';
2200 }
2201 ++length;
2202 done = true;
2203 break;
2204 case 'c':
2205 // char is promoted to int when passed through (...)
2206 if (length < maxlen) {
2207 text[length] = (char)va_arg(ap, int);
2208 }
2209 ++length;
2210 done = true;
2211 break;
2212 case 'h':
2213 // short is promoted to int when passed through (...)
2214 break;
2215 case 'l':
2216 if (inttype < DO_LONGLONG) {
2217 ++inttype;
2218 }
2219 break;
2220 case 'I':
2221 if (SDL_strncmp(fmt, "I64", 3) == 0) {
2222 fmt += 2;
2223 inttype = DO_LONGLONG;
2224 }
2225 break;
2226 case 'z':
2227 inttype = DO_SIZE_T;
2228 break;
2229 case 'i':
2230 case 'd':
2231 if (info.precision >= 0) {
2232 info.pad_zeroes = false;
2233 }
2234 switch (inttype) {
2235 case DO_INT:
2236 length += SDL_PrintLong(TEXT_AND_LEN_ARGS, &info,
2237 (long)va_arg(ap, int));
2238 break;
2239 case DO_LONG:
2240 length += SDL_PrintLong(TEXT_AND_LEN_ARGS, &info,
2241 va_arg(ap, long));
2242 break;
2243 case DO_LONGLONG:
2244 length += SDL_PrintLongLong(TEXT_AND_LEN_ARGS, &info,
2245 va_arg(ap, long long));
2246 break;
2247 case DO_SIZE_T:
2248 length += SDL_PrintLongLong(TEXT_AND_LEN_ARGS, &info,
2249 va_arg(ap, size_t));
2250 break;
2251 }
2252 done = true;
2253 break;
2254 case 'p':
2255 info.force_case = SDL_CASE_LOWER;
2256 length += SDL_PrintPointer(TEXT_AND_LEN_ARGS, &info, va_arg(ap, void *));
2257 done = true;
2258 break;
2259 case 'x':
2260 info.force_case = SDL_CASE_LOWER;
2261 SDL_FALLTHROUGH;
2262 case 'X':
2263 if (info.force_case == SDL_CASE_NOCHANGE) {
2264 info.force_case = SDL_CASE_UPPER;
2265 }
2266 if (info.radix == 10) {
2267 info.radix = 16;
2268 }
2269 SDL_FALLTHROUGH;
2270 case 'o':
2271 if (info.radix == 10) {
2272 info.radix = 8;
2273 }
2274 SDL_FALLTHROUGH;
2275 case 'u':
2276 info.force_sign = false;
2277 if (info.precision >= 0) {
2278 info.pad_zeroes = false;
2279 }
2280 switch (inttype) {
2281 case DO_INT:
2282 length += SDL_PrintUnsignedLong(TEXT_AND_LEN_ARGS, &info,
2283 va_arg(ap, unsigned int));
2284 break;
2285 case DO_LONG:
2286 length += SDL_PrintUnsignedLong(TEXT_AND_LEN_ARGS, &info,
2287 va_arg(ap, unsigned long));
2288 break;
2289 case DO_LONGLONG:
2290 length += SDL_PrintUnsignedLongLong(TEXT_AND_LEN_ARGS, &info,
2291 va_arg(ap, unsigned long long));
2292 break;
2293 case DO_SIZE_T:
2294 length += SDL_PrintUnsignedLongLong(TEXT_AND_LEN_ARGS, &info,
2295 va_arg(ap, size_t));
2296 break;
2297 }
2298 done = true;
2299 break;
2300 case 'f':
2301 length += SDL_PrintFloat(TEXT_AND_LEN_ARGS, &info, va_arg(ap, double), false);
2302 done = true;
2303 break;
2304 case 'g':
2305 length += SDL_PrintFloat(TEXT_AND_LEN_ARGS, &info, va_arg(ap, double), true);
2306 done = true;
2307 break;
2308 case 'S':
2309 info.pad_zeroes = false;
2310 length += SDL_PrintStringW(TEXT_AND_LEN_ARGS, &info, va_arg(ap, wchar_t *));
2311 done = true;
2312 break;
2313 case 's':
2314 info.pad_zeroes = false;
2315 if (inttype > DO_INT) {
2316 length += SDL_PrintStringW(TEXT_AND_LEN_ARGS, &info, va_arg(ap, wchar_t *));
2317 } else {
2318 length += SDL_PrintString(TEXT_AND_LEN_ARGS, &info, va_arg(ap, char *));
2319 }
2320 done = true;
2321 break;
2322 default:
2323 done = true;
2324 break;
2325 }
2326 ++fmt;
2327 }
2328 } else {
2329 if (length < maxlen) {
2330 text[length] = *fmt;
2331 }
2332 ++fmt;
2333 ++length;
2334 }
2335 }
2336 if (length < maxlen) {
2337 text[length] = '\0';
2338 } else if (maxlen > 0) {
2339 text[maxlen - 1] = '\0';
2340 }
2341 return (int)length;
2342}
2343
2344#undef TEXT_AND_LEN_ARGS
2345#endif // HAVE_VSNPRINTF
2346
2347int SDL_vswprintf(SDL_OUT_Z_CAP(maxlen) wchar_t *text, size_t maxlen, const wchar_t *fmt, va_list ap)
2348{
2349 char *text_utf8 = NULL, *fmt_utf8 = NULL;
2350 int result;
2351
2352 if (fmt) {
2353 fmt_utf8 = SDL_iconv_string("UTF-8", "WCHAR_T", (const char *)fmt, (SDL_wcslen(fmt) + 1) * sizeof(wchar_t));
2354 if (!fmt_utf8) {
2355 return -1;
2356 }
2357 }
2358
2359 if (!maxlen) {
2360 // We still need to generate the text to find the final text length
2361 maxlen = 1024;
2362 }
2363 text_utf8 = (char *)SDL_malloc(maxlen * 4);
2364 if (!text_utf8) {
2365 SDL_free(fmt_utf8);
2366 return -1;
2367 }
2368
2369 result = SDL_vsnprintf(text_utf8, maxlen * 4, fmt_utf8, ap);
2370
2371 if (result >= 0) {
2372 wchar_t *text_wchar = (wchar_t *)SDL_iconv_string("WCHAR_T", "UTF-8", text_utf8, SDL_strlen(text_utf8) + 1);
2373 if (text_wchar) {
2374 if (text) {
2375 SDL_wcslcpy(text, text_wchar, maxlen);
2376 }
2377 result = (int)SDL_wcslen(text_wchar);
2378 SDL_free(text_wchar);
2379 } else {
2380 result = -1;
2381 }
2382 }
2383
2384 SDL_free(text_utf8);
2385 SDL_free(fmt_utf8);
2386
2387 return result;
2388}
2389
2390int SDL_asprintf(char **strp, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
2391{
2392 va_list ap;
2393 int result;
2394
2395 va_start(ap, fmt);
2396 result = SDL_vasprintf(strp, fmt, ap);
2397 va_end(ap);
2398
2399 return result;
2400}
2401
2402int SDL_vasprintf(char **strp, SDL_PRINTF_FORMAT_STRING const char *fmt, va_list ap)
2403{
2404 int result;
2405 int size = 100; // Guess we need no more than 100 bytes
2406 char *p, *np;
2407 va_list aq;
2408
2409 *strp = NULL;
2410
2411 p = (char *)SDL_malloc(size);
2412 if (!p) {
2413 return -1;
2414 }
2415
2416 while (1) {
2417 // Try to print in the allocated space
2418 va_copy(aq, ap);
2419 result = SDL_vsnprintf(p, size, fmt, aq);
2420 va_end(aq);
2421
2422 // Check error code
2423 if (result < 0) {
2424 SDL_free(p);
2425 return result;
2426 }
2427
2428 // If that worked, return the string
2429 if (result < size) {
2430 *strp = p;
2431 return result;
2432 }
2433
2434 // Else try again with more space
2435 size = result + 1; // Precisely what is needed
2436
2437 np = (char *)SDL_realloc(p, size);
2438 if (!np) {
2439 SDL_free(p);
2440 return -1;
2441 } else {
2442 p = np;
2443 }
2444 }
2445}
2446
2447char * SDL_strpbrk(const char *str, const char *breakset)
2448{
2449#ifdef HAVE_STRPBRK
2450 return strpbrk(str, breakset);
2451#else
2452
2453 for (; *str; str++) {
2454 const char *b;
2455
2456 for (b = breakset; *b; b++) {
2457 if (*str == *b) {
2458 return (char *) str;
2459 }
2460 }
2461 }
2462 return NULL;
2463#endif
2464}