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