Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/sdl/stdlib/SDL_string.c
9903 views
1
/*
2
Simple DirectMedia Layer
3
Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
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
42
SDL_COMPILE_TIME_ASSERT(sizeof_wchar_t, sizeof(wchar_t) == SDL_SIZEOF_WCHAR_T);
43
44
45
char *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!
82
int 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
190
static 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
257
Uint32 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
268
Uint32 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)
289
static 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)
311
static 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
331
static 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
*/
353
static 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
435
SDL_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
*/
445
static 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)
526
static 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
548
static 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)
570
static 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
593
static 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)
616
static 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)
638
static 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)
655
static 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
693
int 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
722
size_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
735
size_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
748
size_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
761
size_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
774
size_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
789
size_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
803
wchar_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
813
wchar_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
829
wchar_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
838
int 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
854
int 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
875
int 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
891
int 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
910
long 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
924
size_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
939
size_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
974
size_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
983
size_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
992
size_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
1006
char *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
1016
char *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
1027
char *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
1045
char *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
1055
char *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
1065
char *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
1085
char *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
1103
char *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
1123
char *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
1132
char *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)
1146
static 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
1154
char *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
1163
char *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
1172
char *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
1190
char *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
1214
char *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
1232
char *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
1256
int 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
1265
double 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
1274
long 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
1288
unsigned 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
1302
long 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
1316
unsigned 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
1330
double 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
1344
int 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
1363
int 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
1383
int 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
1388
int 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
1395
int 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
1406
int SDL_vsscanf(const char *text, const char *fmt, va_list ap)
1407
{
1408
return vsscanf(text, fmt, ap);
1409
}
1410
#else
1411
static 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)
1445
int 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
1807
int 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
1819
int 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
1833
int 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)
1849
int 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
1860
typedef enum
1861
{
1862
SDL_CASE_NOCHANGE,
1863
SDL_CASE_LOWER,
1864
SDL_CASE_UPPER
1865
} SDL_letter_case;
1866
1867
typedef 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
1879
static 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
1935
static 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
1948
static 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
1985
static 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
1998
static 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
2007
static 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
2020
static 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
2029
static 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
2136
static 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)
2151
int 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
2374
int 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
2437
int 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
2449
int 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
2494
char * 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
2513