Atlas - SDL_string.c

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