sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_string.c (51805B)


      1 /*
      2   Simple DirectMedia Layer
      3   Copyright (C) 1997-2020 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 #if defined(__clang_analyzer__)
     22 #define SDL_DISABLE_ANALYZE_MACROS 1
     23 #endif
     24 
     25 #include "../SDL_internal.h"
     26 
     27 /* This file contains portable string manipulation functions for SDL */
     28 
     29 #include "SDL_stdinc.h"
     30 
     31 #if !defined(HAVE_VSSCANF) || !defined(HAVE_STRTOL) || !defined(HAVE_STRTOUL)  || !defined(HAVE_STRTOLL) || !defined(HAVE_STRTOULL) || !defined(HAVE_STRTOD)
     32 #define SDL_isupperhex(X)   (((X) >= 'A') && ((X) <= 'F'))
     33 #define SDL_islowerhex(X)   (((X) >= 'a') && ((X) <= 'f'))
     34 #endif
     35 
     36 #define UTF8_IsLeadByte(c) ((c) >= 0xC0 && (c) <= 0xF4)
     37 #define UTF8_IsTrailingByte(c) ((c) >= 0x80 && (c) <= 0xBF)
     38 
     39 static int UTF8_TrailingBytes(unsigned char c)
     40 {
     41     if (c >= 0xC0 && c <= 0xDF)
     42         return 1;
     43     else if (c >= 0xE0 && c <= 0xEF)
     44         return 2;
     45     else if (c >= 0xF0 && c <= 0xF4)
     46         return 3;
     47     else
     48         return 0;
     49 }
     50 
     51 #if !defined(HAVE_VSSCANF) || !defined(HAVE_STRTOL)
     52 static size_t
     53 SDL_ScanLong(const char *text, int radix, long *valuep)
     54 {
     55     const char *textstart = text;
     56     long value = 0;
     57     SDL_bool negative = SDL_FALSE;
     58 
     59     if (*text == '-') {
     60         negative = SDL_TRUE;
     61         ++text;
     62     }
     63     if (radix == 16 && SDL_strncmp(text, "0x", 2) == 0) {
     64         text += 2;
     65     }
     66     for (;;) {
     67         int v;
     68         if (SDL_isdigit((unsigned char) *text)) {
     69             v = *text - '0';
     70         } else if (radix == 16 && SDL_isupperhex(*text)) {
     71             v = 10 + (*text - 'A');
     72         } else if (radix == 16 && SDL_islowerhex(*text)) {
     73             v = 10 + (*text - 'a');
     74         } else {
     75             break;
     76         }
     77         value *= radix;
     78         value += v;
     79         ++text;
     80     }
     81     if (valuep && text > textstart) {
     82         if (negative && value) {
     83             *valuep = -value;
     84         } else {
     85             *valuep = value;
     86         }
     87     }
     88     return (text - textstart);
     89 }
     90 #endif
     91 
     92 #if !defined(HAVE_VSSCANF) || !defined(HAVE_STRTOUL) || !defined(HAVE_STRTOD)
     93 static size_t
     94 SDL_ScanUnsignedLong(const char *text, int radix, unsigned long *valuep)
     95 {
     96     const char *textstart = text;
     97     unsigned long value = 0;
     98 
     99     if (radix == 16 && SDL_strncmp(text, "0x", 2) == 0) {
    100         text += 2;
    101     }
    102     for (;;) {
    103         int v;
    104         if (SDL_isdigit((unsigned char) *text)) {
    105             v = *text - '0';
    106         } else if (radix == 16 && SDL_isupperhex(*text)) {
    107             v = 10 + (*text - 'A');
    108         } else if (radix == 16 && SDL_islowerhex(*text)) {
    109             v = 10 + (*text - 'a');
    110         } else {
    111             break;
    112         }
    113         value *= radix;
    114         value += v;
    115         ++text;
    116     }
    117     if (valuep && text > textstart) {
    118         *valuep = value;
    119     }
    120     return (text - textstart);
    121 }
    122 #endif
    123 
    124 #ifndef HAVE_VSSCANF
    125 static size_t
    126 SDL_ScanUintPtrT(const char *text, int radix, uintptr_t * valuep)
    127 {
    128     const char *textstart = text;
    129     uintptr_t value = 0;
    130 
    131     if (radix == 16 && SDL_strncmp(text, "0x", 2) == 0) {
    132         text += 2;
    133     }
    134     for (;;) {
    135         int v;
    136         if (SDL_isdigit((unsigned char) *text)) {
    137             v = *text - '0';
    138         } else if (radix == 16 && SDL_isupperhex(*text)) {
    139             v = 10 + (*text - 'A');
    140         } else if (radix == 16 && SDL_islowerhex(*text)) {
    141             v = 10 + (*text - 'a');
    142         } else {
    143             break;
    144         }
    145         value *= radix;
    146         value += v;
    147         ++text;
    148     }
    149     if (valuep && text > textstart) {
    150         *valuep = value;
    151     }
    152     return (text - textstart);
    153 }
    154 #endif
    155 
    156 #if !defined(HAVE_VSSCANF) || !defined(HAVE_STRTOLL)
    157 static size_t
    158 SDL_ScanLongLong(const char *text, int radix, Sint64 * valuep)
    159 {
    160     const char *textstart = text;
    161     Sint64 value = 0;
    162     SDL_bool negative = SDL_FALSE;
    163 
    164     if (*text == '-') {
    165         negative = SDL_TRUE;
    166         ++text;
    167     }
    168     if (radix == 16 && SDL_strncmp(text, "0x", 2) == 0) {
    169         text += 2;
    170     }
    171     for (;;) {
    172         int v;
    173         if (SDL_isdigit((unsigned char) *text)) {
    174             v = *text - '0';
    175         } else if (radix == 16 && SDL_isupperhex(*text)) {
    176             v = 10 + (*text - 'A');
    177         } else if (radix == 16 && SDL_islowerhex(*text)) {
    178             v = 10 + (*text - 'a');
    179         } else {
    180             break;
    181         }
    182         value *= radix;
    183         value += v;
    184         ++text;
    185     }
    186     if (valuep && text > textstart) {
    187         if (negative && value) {
    188             *valuep = -value;
    189         } else {
    190             *valuep = value;
    191         }
    192     }
    193     return (text - textstart);
    194 }
    195 #endif
    196 
    197 #if !defined(HAVE_VSSCANF) || !defined(HAVE_STRTOULL)
    198 static size_t
    199 SDL_ScanUnsignedLongLong(const char *text, int radix, Uint64 * valuep)
    200 {
    201     const char *textstart = text;
    202     Uint64 value = 0;
    203 
    204     if (radix == 16 && SDL_strncmp(text, "0x", 2) == 0) {
    205         text += 2;
    206     }
    207     for (;;) {
    208         int v;
    209         if (SDL_isdigit((unsigned char) *text)) {
    210             v = *text - '0';
    211         } else if (radix == 16 && SDL_isupperhex(*text)) {
    212             v = 10 + (*text - 'A');
    213         } else if (radix == 16 && SDL_islowerhex(*text)) {
    214             v = 10 + (*text - 'a');
    215         } else {
    216             break;
    217         }
    218         value *= radix;
    219         value += v;
    220         ++text;
    221     }
    222     if (valuep && text > textstart) {
    223         *valuep = value;
    224     }
    225     return (text - textstart);
    226 }
    227 #endif
    228 
    229 #if !defined(HAVE_VSSCANF) || !defined(HAVE_STRTOD)
    230 static size_t
    231 SDL_ScanFloat(const char *text, double *valuep)
    232 {
    233     const char *textstart = text;
    234     unsigned long lvalue = 0;
    235     double value = 0.0;
    236     SDL_bool negative = SDL_FALSE;
    237 
    238     if (*text == '-') {
    239         negative = SDL_TRUE;
    240         ++text;
    241     }
    242     text += SDL_ScanUnsignedLong(text, 10, &lvalue);
    243     value += lvalue;
    244     if (*text == '.') {
    245         int mult = 10;
    246         ++text;
    247         while (SDL_isdigit((unsigned char) *text)) {
    248             lvalue = *text - '0';
    249             value += (double) lvalue / mult;
    250             mult *= 10;
    251             ++text;
    252         }
    253     }
    254     if (valuep && text > textstart) {
    255         if (negative && value) {
    256             *valuep = -value;
    257         } else {
    258             *valuep = value;
    259         }
    260     }
    261     return (text - textstart);
    262 }
    263 #endif
    264 
    265 void *
    266 SDL_memset(SDL_OUT_BYTECAP(len) void *dst, int c, size_t len)
    267 {
    268 #if defined(HAVE_MEMSET)
    269     return memset(dst, c, len);
    270 #else
    271     size_t left;
    272     Uint32 *dstp4;
    273     Uint8 *dstp1 = (Uint8 *) dst;
    274     Uint8 value1;
    275     Uint32 value4;
    276 
    277     /* The value used in memset() is a byte, passed as an int */
    278     c &= 0xff;
    279 
    280     /* The destination pointer needs to be aligned on a 4-byte boundary to
    281      * execute a 32-bit set. Set first bytes manually if needed until it is
    282      * aligned. */
    283     value1 = (Uint8)c;
    284     while ((intptr_t)dstp1 & 0x3) {
    285         if (len--) {
    286             *dstp1++ = value1;
    287         } else {
    288             return dst;
    289         }
    290     }
    291 
    292     value4 = (c | (c << 8) | (c << 16) | (c << 24));
    293     dstp4 = (Uint32 *) dstp1;
    294     left = (len % 4);
    295     len /= 4;
    296     while (len--) {
    297         *dstp4++ = value4;
    298     }
    299 
    300     dstp1 = (Uint8 *) dstp4;
    301     switch (left) {
    302     case 3:
    303         *dstp1++ = value1;
    304     case 2:
    305         *dstp1++ = value1;
    306     case 1:
    307         *dstp1++ = value1;
    308     }
    309 
    310     return dst;
    311 #endif /* HAVE_MEMSET */
    312 }
    313 
    314 void *
    315 SDL_memcpy(SDL_OUT_BYTECAP(len) void *dst, SDL_IN_BYTECAP(len) const void *src, size_t len)
    316 {
    317 #ifdef __GNUC__
    318     /* Presumably this is well tuned for speed.
    319        On my machine this is twice as fast as the C code below.
    320      */
    321     return __builtin_memcpy(dst, src, len);
    322 #elif defined(HAVE_MEMCPY)
    323     return memcpy(dst, src, len);
    324 #elif defined(HAVE_BCOPY)
    325     bcopy(src, dst, len);
    326     return dst;
    327 #else
    328     /* GCC 4.9.0 with -O3 will generate movaps instructions with the loop
    329        using Uint32* pointers, so we need to make sure the pointers are
    330        aligned before we loop using them.
    331      */
    332     if (((intptr_t)src & 0x3) || ((intptr_t)dst & 0x3)) {
    333         /* Do an unaligned byte copy */
    334         Uint8 *srcp1 = (Uint8 *)src;
    335         Uint8 *dstp1 = (Uint8 *)dst;
    336 
    337         while (len--) {
    338             *dstp1++ = *srcp1++;
    339         }
    340     } else {
    341         size_t left = (len % 4);
    342         Uint32 *srcp4, *dstp4;
    343         Uint8 *srcp1, *dstp1;
    344 
    345         srcp4 = (Uint32 *) src;
    346         dstp4 = (Uint32 *) dst;
    347         len /= 4;
    348         while (len--) {
    349             *dstp4++ = *srcp4++;
    350         }
    351 
    352         srcp1 = (Uint8 *) srcp4;
    353         dstp1 = (Uint8 *) dstp4;
    354         switch (left) {
    355         case 3:
    356             *dstp1++ = *srcp1++;
    357         case 2:
    358             *dstp1++ = *srcp1++;
    359         case 1:
    360             *dstp1++ = *srcp1++;
    361         }
    362     }
    363     return dst;
    364 #endif /* __GNUC__ */
    365 }
    366 
    367 void *
    368 SDL_memmove(SDL_OUT_BYTECAP(len) void *dst, SDL_IN_BYTECAP(len) const void *src, size_t len)
    369 {
    370 #if defined(HAVE_MEMMOVE)
    371     return memmove(dst, src, len);
    372 #else
    373     char *srcp = (char *) src;
    374     char *dstp = (char *) dst;
    375 
    376     if (src < dst) {
    377         srcp += len - 1;
    378         dstp += len - 1;
    379         while (len--) {
    380             *dstp-- = *srcp--;
    381         }
    382     } else {
    383         while (len--) {
    384             *dstp++ = *srcp++;
    385         }
    386     }
    387     return dst;
    388 #endif /* HAVE_MEMMOVE */
    389 }
    390 
    391 int
    392 SDL_memcmp(const void *s1, const void *s2, size_t len)
    393 {
    394 #if defined(HAVE_MEMCMP)
    395     return memcmp(s1, s2, len);
    396 #else
    397     char *s1p = (char *) s1;
    398     char *s2p = (char *) s2;
    399     while (len--) {
    400         if (*s1p != *s2p) {
    401             return (*s1p - *s2p);
    402         }
    403         ++s1p;
    404         ++s2p;
    405     }
    406     return 0;
    407 #endif /* HAVE_MEMCMP */
    408 }
    409 
    410 size_t
    411 SDL_strlen(const char *string)
    412 {
    413 #if defined(HAVE_STRLEN)
    414     return strlen(string);
    415 #else
    416     size_t len = 0;
    417     while (*string++) {
    418         ++len;
    419     }
    420     return len;
    421 #endif /* HAVE_STRLEN */
    422 }
    423 
    424 size_t
    425 SDL_wcslen(const wchar_t * string)
    426 {
    427 #if defined(HAVE_WCSLEN)
    428     return wcslen(string);
    429 #else
    430     size_t len = 0;
    431     while (*string++) {
    432         ++len;
    433     }
    434     return len;
    435 #endif /* HAVE_WCSLEN */
    436 }
    437 
    438 size_t
    439 SDL_wcslcpy(SDL_OUT_Z_CAP(maxlen) wchar_t *dst, const wchar_t *src, size_t maxlen)
    440 {
    441 #if defined(HAVE_WCSLCPY)
    442     return wcslcpy(dst, src, maxlen);
    443 #else
    444     size_t srclen = SDL_wcslen(src);
    445     if (maxlen > 0) {
    446         size_t len = SDL_min(srclen, maxlen - 1);
    447         SDL_memcpy(dst, src, len * sizeof(wchar_t));
    448         dst[len] = '\0';
    449     }
    450     return srclen;
    451 #endif /* HAVE_WCSLCPY */
    452 }
    453 
    454 size_t
    455 SDL_wcslcat(SDL_INOUT_Z_CAP(maxlen) wchar_t *dst, const wchar_t *src, size_t maxlen)
    456 {
    457 #if defined(HAVE_WCSLCAT)
    458     return wcslcat(dst, src, maxlen);
    459 #else
    460     size_t dstlen = SDL_wcslen(dst);
    461     size_t srclen = SDL_wcslen(src);
    462     if (dstlen < maxlen) {
    463         SDL_wcslcpy(dst + dstlen, src, maxlen - dstlen);
    464     }
    465     return dstlen + srclen;
    466 #endif /* HAVE_WCSLCAT */
    467 }
    468 
    469 wchar_t *
    470 SDL_wcsdup(const wchar_t *string)
    471 {
    472     size_t len = ((SDL_wcslen(string) + 1) * sizeof(wchar_t));
    473     wchar_t *newstr = (wchar_t *)SDL_malloc(len);
    474     if (newstr) {
    475         SDL_memcpy(newstr, string, len);
    476     }
    477     return newstr;
    478 }
    479 
    480 wchar_t *
    481 SDL_wcsstr(const wchar_t *haystack, const wchar_t *needle)
    482 {
    483 #if defined(HAVE_WCSSTR)
    484     return SDL_const_cast(wchar_t*,wcsstr(haystack, needle));
    485 #else
    486     size_t length = SDL_wcslen(needle);
    487     while (*haystack) {
    488         if (SDL_wcsncmp(haystack, needle, length) == 0) {
    489             return (wchar_t *)haystack;
    490         }
    491         ++haystack;
    492     }
    493     return NULL;
    494 #endif /* HAVE_WCSSTR */
    495 }
    496 
    497 int
    498 SDL_wcscmp(const wchar_t *str1, const wchar_t *str2)
    499 {
    500 #if defined(HAVE_WCSCMP)
    501     return wcscmp(str1, str2);
    502 #else
    503     while (*str1 && *str2) {
    504         if (*str1 != *str2)
    505             break;
    506         ++str1;
    507         ++str2;
    508     }
    509     return (int)(*str1 - *str2);
    510 #endif /* HAVE_WCSCMP */
    511 }
    512 
    513 int
    514 SDL_wcsncmp(const wchar_t *str1, const wchar_t *str2, size_t maxlen)
    515 {
    516 #if defined(HAVE_WCSNCMP)
    517     return wcsncmp(str1, str2, maxlen);
    518 #else
    519     while (*str1 && *str2 && maxlen) {
    520         if (*str1 != *str2)
    521             break;
    522         ++str1;
    523         ++str2;
    524         --maxlen;
    525     }
    526     if (!maxlen) {
    527         return 0;
    528     }
    529     return (int) (*str1 - *str2);
    530 
    531 #endif /* HAVE_WCSNCMP */
    532 }
    533 
    534 int
    535 SDL_wcscasecmp(const wchar_t *str1, const wchar_t *str2)
    536 {
    537 #if defined(HAVE_WCSCASECMP)
    538     return wcscasecmp(str1, str2);
    539 #elif defined(HAVE__WCSICMP)
    540     return _wcsicmp(str1, str2);
    541 #else
    542     wchar_t a = 0;
    543     wchar_t b = 0;
    544     while (*str1 && *str2) {
    545         /* FIXME: This doesn't actually support wide characters */
    546         if (*str1 >= 0x80 || *str2 >= 0x80) {
    547             a = *str1;
    548             b = *str2;
    549         } else {
    550             a = SDL_toupper((unsigned char) *str1);
    551             b = SDL_toupper((unsigned char) *str2);
    552         }
    553         if (a != b)
    554             break;
    555         ++str1;
    556         ++str2;
    557     }
    558 
    559     /* FIXME: This doesn't actually support wide characters */
    560     if (*str1 >= 0x80 || *str2 >= 0x80) {
    561         a = *str1;
    562         b = *str2;
    563     } else {
    564         a = SDL_toupper((unsigned char) *str1);
    565         b = SDL_toupper((unsigned char) *str2);
    566     }
    567     return (int) ((unsigned int) a - (unsigned int) b);
    568 #endif /* HAVE__WCSICMP */
    569 }
    570 
    571 int
    572 SDL_wcsncasecmp(const wchar_t *str1, const wchar_t *str2, size_t maxlen)
    573 {
    574 #if defined(HAVE_WCSNCASECMP)
    575     return wcsncasecmp(str1, str2, maxlen);
    576 #elif defined(HAVE__WCSNICMP)
    577     return _wcsnicmp(str1, str2, maxlen);
    578 #else
    579     wchar_t a = 0;
    580     wchar_t b = 0;
    581     while (*str1 && *str2 && maxlen) {
    582         /* FIXME: This doesn't actually support wide characters */
    583         if (*str1 >= 0x80 || *str2 >= 0x80) {
    584             a = *str1;
    585             b = *str2;
    586         } else {
    587             a = SDL_toupper((unsigned char) *str1);
    588             b = SDL_toupper((unsigned char) *str2);
    589         }
    590         if (a != b)
    591             break;
    592         ++str1;
    593         ++str2;
    594         --maxlen;
    595     }
    596 
    597     if (maxlen == 0) {
    598         return 0;
    599     } else {
    600         /* FIXME: This doesn't actually support wide characters */
    601         if (*str1 >= 0x80 || *str2 >= 0x80) {
    602             a = *str1;
    603             b = *str2;
    604         } else {
    605             a = SDL_toupper((unsigned char) *str1);
    606             b = SDL_toupper((unsigned char) *str2);
    607         }
    608         return (int) ((unsigned int) a - (unsigned int) b);
    609     }
    610 #endif /* HAVE__WCSNICMP */
    611 }
    612 
    613 size_t
    614 SDL_strlcpy(SDL_OUT_Z_CAP(maxlen) char *dst, const char *src, size_t maxlen)
    615 {
    616 #if defined(HAVE_STRLCPY)
    617     return strlcpy(dst, src, maxlen);
    618 #else
    619     size_t srclen = SDL_strlen(src);
    620     if (maxlen > 0) {
    621         size_t len = SDL_min(srclen, maxlen - 1);
    622         SDL_memcpy(dst, src, len);
    623         dst[len] = '\0';
    624     }
    625     return srclen;
    626 #endif /* HAVE_STRLCPY */
    627 }
    628 
    629 size_t
    630 SDL_utf8strlcpy(SDL_OUT_Z_CAP(dst_bytes) char *dst, const char *src, size_t dst_bytes)
    631 {
    632     size_t src_bytes = SDL_strlen(src);
    633     size_t bytes = SDL_min(src_bytes, dst_bytes - 1);
    634     size_t i = 0;
    635     char trailing_bytes = 0;
    636     if (bytes)
    637     {
    638         unsigned char c = (unsigned char)src[bytes - 1];
    639         if (UTF8_IsLeadByte(c))
    640             --bytes;
    641         else if (UTF8_IsTrailingByte(c))
    642         {
    643             for (i = bytes - 1; i != 0; --i)
    644             {
    645                 c = (unsigned char)src[i];
    646                 trailing_bytes = UTF8_TrailingBytes(c);
    647                 if (trailing_bytes)
    648                 {
    649                     if (bytes - i != trailing_bytes + 1)
    650                         bytes = i;
    651 
    652                     break;
    653                 }
    654             }
    655         }
    656         SDL_memcpy(dst, src, bytes);
    657     }
    658     dst[bytes] = '\0';
    659     return bytes;
    660 }
    661 
    662 size_t
    663 SDL_utf8strlen(const char *str)
    664 {
    665     size_t retval = 0;
    666     const char *p = str;
    667     char ch;
    668 
    669     while ((ch = *(p++)) != 0) {
    670         /* if top two bits are 1 and 0, it's a continuation byte. */
    671         if ((ch & 0xc0) != 0x80) {
    672             retval++;
    673         }
    674     }
    675     
    676     return retval;
    677 }
    678 
    679 size_t
    680 SDL_strlcat(SDL_INOUT_Z_CAP(maxlen) char *dst, const char *src, size_t maxlen)
    681 {
    682 #if defined(HAVE_STRLCAT)
    683     return strlcat(dst, src, maxlen);
    684 #else
    685     size_t dstlen = SDL_strlen(dst);
    686     size_t srclen = SDL_strlen(src);
    687     if (dstlen < maxlen) {
    688         SDL_strlcpy(dst + dstlen, src, maxlen - dstlen);
    689     }
    690     return dstlen + srclen;
    691 #endif /* HAVE_STRLCAT */
    692 }
    693 
    694 char *
    695 SDL_strdup(const char *string)
    696 {
    697     size_t len = SDL_strlen(string) + 1;
    698     char *newstr = (char *)SDL_malloc(len);
    699     if (newstr) {
    700         SDL_memcpy(newstr, string, len);
    701     }
    702     return newstr;
    703 }
    704 
    705 char *
    706 SDL_strrev(char *string)
    707 {
    708 #if defined(HAVE__STRREV)
    709     return _strrev(string);
    710 #else
    711     size_t len = SDL_strlen(string);
    712     char *a = &string[0];
    713     char *b = &string[len - 1];
    714     len /= 2;
    715     while (len--) {
    716         char c = *a;
    717         *a++ = *b;
    718         *b-- = c;
    719     }
    720     return string;
    721 #endif /* HAVE__STRREV */
    722 }
    723 
    724 char *
    725 SDL_strupr(char *string)
    726 {
    727 #if defined(HAVE__STRUPR)
    728     return _strupr(string);
    729 #else
    730     char *bufp = string;
    731     while (*bufp) {
    732         *bufp = SDL_toupper((unsigned char) *bufp);
    733         ++bufp;
    734     }
    735     return string;
    736 #endif /* HAVE__STRUPR */
    737 }
    738 
    739 char *
    740 SDL_strlwr(char *string)
    741 {
    742 #if defined(HAVE__STRLWR)
    743     return _strlwr(string);
    744 #else
    745     char *bufp = string;
    746     while (*bufp) {
    747         *bufp = SDL_tolower((unsigned char) *bufp);
    748         ++bufp;
    749     }
    750     return string;
    751 #endif /* HAVE__STRLWR */
    752 }
    753 
    754 char *
    755 SDL_strchr(const char *string, int c)
    756 {
    757 #ifdef HAVE_STRCHR
    758     return SDL_const_cast(char*,strchr(string, c));
    759 #elif defined(HAVE_INDEX)
    760     return SDL_const_cast(char*,index(string, c));
    761 #else
    762     while (*string) {
    763         if (*string == c) {
    764             return (char *) string;
    765         }
    766         ++string;
    767     }
    768     return NULL;
    769 #endif /* HAVE_STRCHR */
    770 }
    771 
    772 char *
    773 SDL_strrchr(const char *string, int c)
    774 {
    775 #ifdef HAVE_STRRCHR
    776     return SDL_const_cast(char*,strrchr(string, c));
    777 #elif defined(HAVE_RINDEX)
    778     return SDL_const_cast(char*,rindex(string, c));
    779 #else
    780     const char *bufp = string + SDL_strlen(string) - 1;
    781     while (bufp >= string) {
    782         if (*bufp == c) {
    783             return (char *) bufp;
    784         }
    785         --bufp;
    786     }
    787     return NULL;
    788 #endif /* HAVE_STRRCHR */
    789 }
    790 
    791 char *
    792 SDL_strstr(const char *haystack, const char *needle)
    793 {
    794 #if defined(HAVE_STRSTR)
    795     return SDL_const_cast(char*,strstr(haystack, needle));
    796 #else
    797     size_t length = SDL_strlen(needle);
    798     while (*haystack) {
    799         if (SDL_strncmp(haystack, needle, length) == 0) {
    800             return (char *) haystack;
    801         }
    802         ++haystack;
    803     }
    804     return NULL;
    805 #endif /* HAVE_STRSTR */
    806 }
    807 
    808 #if !defined(HAVE__LTOA) || !defined(HAVE__I64TOA) || \
    809     !defined(HAVE__ULTOA) || !defined(HAVE__UI64TOA)
    810 static const char ntoa_table[] = {
    811     '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
    812     'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
    813     'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
    814     'U', 'V', 'W', 'X', 'Y', 'Z'
    815 };
    816 #endif /* ntoa() conversion table */
    817 
    818 char *
    819 SDL_itoa(int value, char *string, int radix)
    820 {
    821 #ifdef HAVE_ITOA
    822     return itoa(value, string, radix);
    823 #else
    824     return SDL_ltoa((long)value, string, radix);
    825 #endif /* HAVE_ITOA */
    826 }
    827 
    828 char *
    829 SDL_uitoa(unsigned int value, char *string, int radix)
    830 {
    831 #ifdef HAVE__UITOA
    832     return _uitoa(value, string, radix);
    833 #else
    834     return SDL_ultoa((unsigned long)value, string, radix);
    835 #endif /* HAVE__UITOA */
    836 }
    837 
    838 char *
    839 SDL_ltoa(long value, char *string, int radix)
    840 {
    841 #if defined(HAVE__LTOA)
    842     return _ltoa(value, string, radix);
    843 #else
    844     char *bufp = string;
    845 
    846     if (value < 0) {
    847         *bufp++ = '-';
    848         SDL_ultoa(-value, bufp, radix);
    849     } else {
    850         SDL_ultoa(value, bufp, radix);
    851     }
    852 
    853     return string;
    854 #endif /* HAVE__LTOA */
    855 }
    856 
    857 char *
    858 SDL_ultoa(unsigned long value, char *string, int radix)
    859 {
    860 #if defined(HAVE__ULTOA)
    861     return _ultoa(value, string, radix);
    862 #else
    863     char *bufp = string;
    864 
    865     if (value) {
    866         while (value > 0) {
    867             *bufp++ = ntoa_table[value % radix];
    868             value /= radix;
    869         }
    870     } else {
    871         *bufp++ = '0';
    872     }
    873     *bufp = '\0';
    874 
    875     /* The numbers went into the string backwards. :) */
    876     SDL_strrev(string);
    877 
    878     return string;
    879 #endif /* HAVE__ULTOA */
    880 }
    881 
    882 char *
    883 SDL_lltoa(Sint64 value, char *string, int radix)
    884 {
    885 #if defined(HAVE__I64TOA)
    886     return _i64toa(value, string, radix);
    887 #else
    888     char *bufp = string;
    889 
    890     if (value < 0) {
    891         *bufp++ = '-';
    892         SDL_ulltoa(-value, bufp, radix);
    893     } else {
    894         SDL_ulltoa(value, bufp, radix);
    895     }
    896 
    897     return string;
    898 #endif /* HAVE__I64TOA */
    899 }
    900 
    901 char *
    902 SDL_ulltoa(Uint64 value, char *string, int radix)
    903 {
    904 #if defined(HAVE__UI64TOA)
    905     return _ui64toa(value, string, radix);
    906 #else
    907     char *bufp = string;
    908 
    909     if (value) {
    910         while (value > 0) {
    911             *bufp++ = ntoa_table[value % radix];
    912             value /= radix;
    913         }
    914     } else {
    915         *bufp++ = '0';
    916     }
    917     *bufp = '\0';
    918 
    919     /* The numbers went into the string backwards. :) */
    920     SDL_strrev(string);
    921 
    922     return string;
    923 #endif /* HAVE__UI64TOA */
    924 }
    925 
    926 int SDL_atoi(const char *string)
    927 {
    928 #ifdef HAVE_ATOI
    929     return atoi(string);
    930 #else
    931     return SDL_strtol(string, NULL, 0);
    932 #endif /* HAVE_ATOI */
    933 }
    934 
    935 double SDL_atof(const char *string)
    936 {
    937 #ifdef HAVE_ATOF
    938     return atof(string);
    939 #else
    940     return SDL_strtod(string, NULL);
    941 #endif /* HAVE_ATOF */
    942 }
    943 
    944 long
    945 SDL_strtol(const char *string, char **endp, int base)
    946 {
    947 #if defined(HAVE_STRTOL)
    948     return strtol(string, endp, base);
    949 #else
    950     size_t len;
    951     long value = 0;
    952 
    953     if (!base) {
    954         if ((SDL_strlen(string) > 2) && (SDL_strncmp(string, "0x", 2) == 0)) {
    955             base = 16;
    956         } else {
    957             base = 10;
    958         }
    959     }
    960 
    961     len = SDL_ScanLong(string, base, &value);
    962     if (endp) {
    963         *endp = (char *) string + len;
    964     }
    965     return value;
    966 #endif /* HAVE_STRTOL */
    967 }
    968 
    969 unsigned long
    970 SDL_strtoul(const char *string, char **endp, int base)
    971 {
    972 #if defined(HAVE_STRTOUL)
    973     return strtoul(string, endp, base);
    974 #else
    975     size_t len;
    976     unsigned long value = 0;
    977 
    978     if (!base) {
    979         if ((SDL_strlen(string) > 2) && (SDL_strncmp(string, "0x", 2) == 0)) {
    980             base = 16;
    981         } else {
    982             base = 10;
    983         }
    984     }
    985 
    986     len = SDL_ScanUnsignedLong(string, base, &value);
    987     if (endp) {
    988         *endp = (char *) string + len;
    989     }
    990     return value;
    991 #endif /* HAVE_STRTOUL */
    992 }
    993 
    994 Sint64
    995 SDL_strtoll(const char *string, char **endp, int base)
    996 {
    997 #if defined(HAVE_STRTOLL)
    998     return strtoll(string, endp, base);
    999 #else
   1000     size_t len;
   1001     Sint64 value = 0;
   1002 
   1003     if (!base) {
   1004         if ((SDL_strlen(string) > 2) && (SDL_strncmp(string, "0x", 2) == 0)) {
   1005             base = 16;
   1006         } else {
   1007             base = 10;
   1008         }
   1009     }
   1010 
   1011     len = SDL_ScanLongLong(string, base, &value);
   1012     if (endp) {
   1013         *endp = (char *) string + len;
   1014     }
   1015     return value;
   1016 #endif /* HAVE_STRTOLL */
   1017 }
   1018 
   1019 Uint64
   1020 SDL_strtoull(const char *string, char **endp, int base)
   1021 {
   1022 #if defined(HAVE_STRTOULL)
   1023     return strtoull(string, endp, base);
   1024 #else
   1025     size_t len;
   1026     Uint64 value = 0;
   1027 
   1028     if (!base) {
   1029         if ((SDL_strlen(string) > 2) && (SDL_strncmp(string, "0x", 2) == 0)) {
   1030             base = 16;
   1031         } else {
   1032             base = 10;
   1033         }
   1034     }
   1035 
   1036     len = SDL_ScanUnsignedLongLong(string, base, &value);
   1037     if (endp) {
   1038         *endp = (char *) string + len;
   1039     }
   1040     return value;
   1041 #endif /* HAVE_STRTOULL */
   1042 }
   1043 
   1044 double
   1045 SDL_strtod(const char *string, char **endp)
   1046 {
   1047 #if defined(HAVE_STRTOD)
   1048     return strtod(string, endp);
   1049 #else
   1050     size_t len;
   1051     double value = 0.0;
   1052 
   1053     len = SDL_ScanFloat(string, &value);
   1054     if (endp) {
   1055         *endp = (char *) string + len;
   1056     }
   1057     return value;
   1058 #endif /* HAVE_STRTOD */
   1059 }
   1060 
   1061 int
   1062 SDL_strcmp(const char *str1, const char *str2)
   1063 {
   1064 #if defined(HAVE_STRCMP)
   1065     return strcmp(str1, str2);
   1066 #else
   1067     while (*str1 && *str2) {
   1068         if (*str1 != *str2)
   1069             break;
   1070         ++str1;
   1071         ++str2;
   1072     }
   1073     return (int)((unsigned char) *str1 - (unsigned char) *str2);
   1074 #endif /* HAVE_STRCMP */
   1075 }
   1076 
   1077 int
   1078 SDL_strncmp(const char *str1, const char *str2, size_t maxlen)
   1079 {
   1080 #if defined(HAVE_STRNCMP)
   1081     return strncmp(str1, str2, maxlen);
   1082 #else
   1083     while (*str1 && *str2 && maxlen) {
   1084         if (*str1 != *str2)
   1085             break;
   1086         ++str1;
   1087         ++str2;
   1088         --maxlen;
   1089     }
   1090     if (!maxlen) {
   1091         return 0;
   1092     }
   1093     return (int) ((unsigned char) *str1 - (unsigned char) *str2);
   1094 #endif /* HAVE_STRNCMP */
   1095 }
   1096 
   1097 int
   1098 SDL_strcasecmp(const char *str1, const char *str2)
   1099 {
   1100 #ifdef HAVE_STRCASECMP
   1101     return strcasecmp(str1, str2);
   1102 #elif defined(HAVE__STRICMP)
   1103     return _stricmp(str1, str2);
   1104 #else
   1105     char a = 0;
   1106     char b = 0;
   1107     while (*str1 && *str2) {
   1108         a = SDL_toupper((unsigned char) *str1);
   1109         b = SDL_toupper((unsigned char) *str2);
   1110         if (a != b)
   1111             break;
   1112         ++str1;
   1113         ++str2;
   1114     }
   1115     a = SDL_toupper(*str1);
   1116     b = SDL_toupper(*str2);
   1117     return (int) ((unsigned char) a - (unsigned char) b);
   1118 #endif /* HAVE_STRCASECMP */
   1119 }
   1120 
   1121 int
   1122 SDL_strncasecmp(const char *str1, const char *str2, size_t maxlen)
   1123 {
   1124 #ifdef HAVE_STRNCASECMP
   1125     return strncasecmp(str1, str2, maxlen);
   1126 #elif defined(HAVE__STRNICMP)
   1127     return _strnicmp(str1, str2, maxlen);
   1128 #else
   1129     char a = 0;
   1130     char b = 0;
   1131     while (*str1 && *str2 && maxlen) {
   1132         a = SDL_tolower((unsigned char) *str1);
   1133         b = SDL_tolower((unsigned char) *str2);
   1134         if (a != b)
   1135             break;
   1136         ++str1;
   1137         ++str2;
   1138         --maxlen;
   1139     }
   1140     if (maxlen == 0) {
   1141         return 0;
   1142     } else {
   1143         a = SDL_tolower((unsigned char) *str1);
   1144         b = SDL_tolower((unsigned char) *str2);
   1145         return (int) ((unsigned char) a - (unsigned char) b);
   1146     }
   1147 #endif /* HAVE_STRNCASECMP */
   1148 }
   1149 
   1150 int
   1151 SDL_sscanf(const char *text, SDL_SCANF_FORMAT_STRING const char *fmt, ...)
   1152 {
   1153     int rc;
   1154     va_list ap;
   1155     va_start(ap, fmt);
   1156     rc = SDL_vsscanf(text, fmt, ap);
   1157     va_end(ap);
   1158     return rc;
   1159 }
   1160 
   1161 #ifdef HAVE_VSSCANF
   1162 int
   1163 SDL_vsscanf(const char *text, const char *fmt, va_list ap)
   1164 {
   1165     return vsscanf(text, fmt, ap);
   1166 }
   1167 #else
   1168 int
   1169 SDL_vsscanf(const char *text, const char *fmt, va_list ap)
   1170 {
   1171     int retval = 0;
   1172 
   1173     if (!text || !*text) {
   1174         return -1;
   1175     }
   1176 
   1177     while (*fmt) {
   1178         if (*fmt == ' ') {
   1179             while (SDL_isspace((unsigned char) *text)) {
   1180                 ++text;
   1181             }
   1182             ++fmt;
   1183             continue;
   1184         }
   1185         if (*fmt == '%') {
   1186             SDL_bool done = SDL_FALSE;
   1187             long count = 0;
   1188             int radix = 10;
   1189             enum
   1190             {
   1191                 DO_SHORT,
   1192                 DO_INT,
   1193                 DO_LONG,
   1194                 DO_LONGLONG
   1195             } inttype = DO_INT;
   1196             size_t advance;
   1197             SDL_bool suppress = SDL_FALSE;
   1198 
   1199             ++fmt;
   1200             if (*fmt == '%') {
   1201                 if (*text == '%') {
   1202                     ++text;
   1203                     ++fmt;
   1204                     continue;
   1205                 }
   1206                 break;
   1207             }
   1208             if (*fmt == '*') {
   1209                 suppress = SDL_TRUE;
   1210                 ++fmt;
   1211             }
   1212             fmt += SDL_ScanLong(fmt, 10, &count);
   1213 
   1214             if (*fmt == 'c') {
   1215                 if (!count) {
   1216                     count = 1;
   1217                 }
   1218                 if (suppress) {
   1219                     while (count--) {
   1220                         ++text;
   1221                     }
   1222                 } else {
   1223                     char *valuep = va_arg(ap, char *);
   1224                     while (count--) {
   1225                         *valuep++ = *text++;
   1226                     }
   1227                     ++retval;
   1228                 }
   1229                 continue;
   1230             }
   1231 
   1232             while (SDL_isspace((unsigned char) *text)) {
   1233                 ++text;
   1234             }
   1235 
   1236             /* FIXME: implement more of the format specifiers */
   1237             while (!done) {
   1238                 switch (*fmt) {
   1239                 case '*':
   1240                     suppress = SDL_TRUE;
   1241                     break;
   1242                 case 'h':
   1243                     if (inttype > DO_SHORT) {
   1244                         ++inttype;
   1245                     }
   1246                     break;
   1247                 case 'l':
   1248                     if (inttype < DO_LONGLONG) {
   1249                         ++inttype;
   1250                     }
   1251                     break;
   1252                 case 'I':
   1253                     if (SDL_strncmp(fmt, "I64", 3) == 0) {
   1254                         fmt += 2;
   1255                         inttype = DO_LONGLONG;
   1256                     }
   1257                     break;
   1258                 case 'i':
   1259                     {
   1260                         int index = 0;
   1261                         if (text[index] == '-') {
   1262                             ++index;
   1263                         }
   1264                         if (text[index] == '0') {
   1265                             if (SDL_tolower((unsigned char) text[index + 1]) == 'x') {
   1266                                 radix = 16;
   1267                             } else {
   1268                                 radix = 8;
   1269                             }
   1270                         }
   1271                     }
   1272                     /* Fall through to %d handling */
   1273                 case 'd':
   1274                     if (inttype == DO_LONGLONG) {
   1275                         Sint64 value;
   1276                         advance = SDL_ScanLongLong(text, radix, &value);
   1277                         text += advance;
   1278                         if (advance && !suppress) {
   1279                             Sint64 *valuep = va_arg(ap, Sint64 *);
   1280                             *valuep = value;
   1281                             ++retval;
   1282                         }
   1283                     } else {
   1284                         long value;
   1285                         advance = SDL_ScanLong(text, radix, &value);
   1286                         text += advance;
   1287                         if (advance && !suppress) {
   1288                             switch (inttype) {
   1289                             case DO_SHORT:
   1290                                 {
   1291                                     short *valuep = va_arg(ap, short *);
   1292                                     *valuep = (short) value;
   1293                                 }
   1294                                 break;
   1295                             case DO_INT:
   1296                                 {
   1297                                     int *valuep = va_arg(ap, int *);
   1298                                     *valuep = (int) value;
   1299                                 }
   1300                                 break;
   1301                             case DO_LONG:
   1302                                 {
   1303                                     long *valuep = va_arg(ap, long *);
   1304                                     *valuep = value;
   1305                                 }
   1306                                 break;
   1307                             case DO_LONGLONG:
   1308                                 /* Handled above */
   1309                                 break;
   1310                             }
   1311                             ++retval;
   1312                         }
   1313                     }
   1314                     done = SDL_TRUE;
   1315                     break;
   1316                 case 'o':
   1317                     if (radix == 10) {
   1318                         radix = 8;
   1319                     }
   1320                     /* Fall through to unsigned handling */
   1321                 case 'x':
   1322                 case 'X':
   1323                     if (radix == 10) {
   1324                         radix = 16;
   1325                     }
   1326                     /* Fall through to unsigned handling */
   1327                 case 'u':
   1328                     if (inttype == DO_LONGLONG) {
   1329                         Uint64 value = 0;
   1330                         advance = SDL_ScanUnsignedLongLong(text, radix, &value);
   1331                         text += advance;
   1332                         if (advance && !suppress) {
   1333                             Uint64 *valuep = va_arg(ap, Uint64 *);
   1334                             *valuep = value;
   1335                             ++retval;
   1336                         }
   1337                     } else {
   1338                         unsigned long value = 0;
   1339                         advance = SDL_ScanUnsignedLong(text, radix, &value);
   1340                         text += advance;
   1341                         if (advance && !suppress) {
   1342                             switch (inttype) {
   1343                             case DO_SHORT:
   1344                                 {
   1345                                     short *valuep = va_arg(ap, short *);
   1346                                     *valuep = (short) value;
   1347                                 }
   1348                                 break;
   1349                             case DO_INT:
   1350                                 {
   1351                                     int *valuep = va_arg(ap, int *);
   1352                                     *valuep = (int) value;
   1353                                 }
   1354                                 break;
   1355                             case DO_LONG:
   1356                                 {
   1357                                     long *valuep = va_arg(ap, long *);
   1358                                     *valuep = value;
   1359                                 }
   1360                                 break;
   1361                             case DO_LONGLONG:
   1362                                 /* Handled above */
   1363                                 break;
   1364                             }
   1365                             ++retval;
   1366                         }
   1367                     }
   1368                     done = SDL_TRUE;
   1369                     break;
   1370                 case 'p':
   1371                     {
   1372                         uintptr_t value = 0;
   1373                         advance = SDL_ScanUintPtrT(text, 16, &value);
   1374                         text += advance;
   1375                         if (advance && !suppress) {
   1376                             void **valuep = va_arg(ap, void **);
   1377                             *valuep = (void *) value;
   1378                             ++retval;
   1379                         }
   1380                     }
   1381                     done = SDL_TRUE;
   1382                     break;
   1383                 case 'f':
   1384                     {
   1385                         double value;
   1386                         advance = SDL_ScanFloat(text, &value);
   1387                         text += advance;
   1388                         if (advance && !suppress) {
   1389                             float *valuep = va_arg(ap, float *);
   1390                             *valuep = (float) value;
   1391                             ++retval;
   1392                         }
   1393                     }
   1394                     done = SDL_TRUE;
   1395                     break;
   1396                 case 's':
   1397                     if (suppress) {
   1398                         while (!SDL_isspace((unsigned char) *text)) {
   1399                             ++text;
   1400                             if (count) {
   1401                                 if (--count == 0) {
   1402                                     break;
   1403                                 }
   1404                             }
   1405                         }
   1406                     } else {
   1407                         char *valuep = va_arg(ap, char *);
   1408                         while (!SDL_isspace((unsigned char) *text)) {
   1409                             *valuep++ = *text++;
   1410                             if (count) {
   1411                                 if (--count == 0) {
   1412                                     break;
   1413                                 }
   1414                             }
   1415                         }
   1416                         *valuep = '\0';
   1417                         ++retval;
   1418                     }
   1419                     done = SDL_TRUE;
   1420                     break;
   1421                 default:
   1422                     done = SDL_TRUE;
   1423                     break;
   1424                 }
   1425                 ++fmt;
   1426             }
   1427             continue;
   1428         }
   1429         if (*text == *fmt) {
   1430             ++text;
   1431             ++fmt;
   1432             continue;
   1433         }
   1434         /* Text didn't match format specifier */
   1435         break;
   1436     }
   1437 
   1438     return retval;
   1439 }
   1440 #endif /* HAVE_VSSCANF */
   1441 
   1442 int
   1443 SDL_snprintf(SDL_OUT_Z_CAP(maxlen) char *text, size_t maxlen, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
   1444 {
   1445     va_list ap;
   1446     int retval;
   1447 
   1448     va_start(ap, fmt);
   1449     retval = SDL_vsnprintf(text, maxlen, fmt, ap);
   1450     va_end(ap);
   1451 
   1452     return retval;
   1453 }
   1454 
   1455 #if defined(HAVE_LIBC) && defined(__WATCOMC__)
   1456 /* _vsnprintf() doesn't ensure nul termination */
   1457 int SDL_vsnprintf(SDL_OUT_Z_CAP(maxlen) char *text, size_t maxlen, const char *fmt, va_list ap)
   1458 {
   1459     int retval;
   1460     if (!fmt) fmt = "";
   1461     retval = _vsnprintf(text, maxlen, fmt, ap);
   1462     if (maxlen > 0) text[maxlen-1] = '\0';
   1463     if (retval < 0) retval = (int) maxlen;
   1464     return retval;
   1465 }
   1466 #elif defined(HAVE_VSNPRINTF)
   1467 int SDL_vsnprintf(SDL_OUT_Z_CAP(maxlen) char *text, size_t maxlen, const char *fmt, va_list ap)
   1468 {
   1469     if (!fmt) {
   1470         fmt = "";
   1471     }
   1472     return vsnprintf(text, maxlen, fmt, ap);
   1473 }
   1474 #else
   1475  /* FIXME: implement more of the format specifiers */
   1476 typedef enum
   1477 {
   1478     SDL_CASE_NOCHANGE,
   1479     SDL_CASE_LOWER,
   1480     SDL_CASE_UPPER
   1481 } SDL_letter_case;
   1482 
   1483 typedef struct
   1484 {
   1485     SDL_bool left_justify; /* for now: ignored. */
   1486     SDL_bool force_sign;
   1487     SDL_bool force_type;   /* for now: used only by float printer, ignored otherwise. */
   1488     SDL_bool pad_zeroes;
   1489     SDL_letter_case force_case;
   1490     int width;
   1491     int radix;
   1492     int precision;
   1493 } SDL_FormatInfo;
   1494 
   1495 static size_t
   1496 SDL_PrintString(char *text, size_t maxlen, SDL_FormatInfo *info, const char *string)
   1497 {
   1498     size_t length = 0;
   1499     size_t slen, sz;
   1500 
   1501     if (string == NULL) {
   1502         string = "(null)";
   1503     }
   1504 
   1505     sz = SDL_strlen(string);
   1506     if (info && info->width > 0 && (size_t)info->width > sz) {
   1507         const char fill = info->pad_zeroes ? '0' : ' ';
   1508         size_t width = info->width - sz;
   1509         size_t filllen;
   1510 
   1511         if (info->precision >= 0 && (size_t)info->precision < sz)
   1512             width += sz - (size_t)info->precision;
   1513 
   1514         filllen = SDL_min(width, maxlen);
   1515         SDL_memset(text, fill, filllen);
   1516         text += filllen;
   1517         length += filllen;
   1518         maxlen -= filllen;
   1519     }
   1520 
   1521     slen = SDL_strlcpy(text, string, maxlen);
   1522     length += SDL_min(slen, maxlen);
   1523 
   1524     if (info) {
   1525         if (info->precision >= 0 && (size_t)info->precision < sz) {
   1526             slen = (size_t)info->precision;
   1527             if (slen < maxlen) {
   1528                 text[slen] = 0;
   1529                 length -= (sz - slen);
   1530             }
   1531         }
   1532         if (info->force_case == SDL_CASE_LOWER) {
   1533             SDL_strlwr(text);
   1534         } else if (info->force_case == SDL_CASE_UPPER) {
   1535             SDL_strupr(text);
   1536         }
   1537     }
   1538     return length;
   1539 }
   1540 
   1541 static void
   1542 SDL_IntPrecisionAdjust(char *num, size_t maxlen, SDL_FormatInfo *info)
   1543 {/* left-pad num with zeroes. */
   1544     size_t sz, pad, have_sign;
   1545 
   1546     if (!info)
   1547         return;
   1548 
   1549     have_sign = 0;
   1550     if (*num == '-' || *num == '+') {
   1551         have_sign = 1;
   1552         ++num;
   1553         --maxlen;
   1554     }
   1555     sz = SDL_strlen(num);
   1556     if (info->precision > 0 && sz < (size_t)info->precision) {
   1557         pad = (size_t)info->precision - sz;
   1558         if (pad + sz + 1 <= maxlen) { /* otherwise ignore the precision */
   1559             SDL_memmove(num + pad, num, sz + 1);
   1560             SDL_memset(num, '0', pad);
   1561         }
   1562     }
   1563     info->precision = -1;/* so that SDL_PrintString() doesn't make a mess. */
   1564 
   1565     if (info->pad_zeroes && info->width > 0 && (size_t)info->width > sz + have_sign) {
   1566     /* handle here: spaces are added before the sign
   1567        but zeroes must be placed _after_ the sign. */
   1568     /* sz hasn't changed: we ignore pad_zeroes if a precision is given. */
   1569         pad = (size_t)info->width - sz - have_sign;
   1570         if (pad + sz + 1 <= maxlen) {
   1571             SDL_memmove(num + pad, num, sz + 1);
   1572             SDL_memset(num, '0', pad);
   1573         }
   1574         info->width = 0; /* so that SDL_PrintString() doesn't make a mess. */
   1575     }
   1576 }
   1577 
   1578 static size_t
   1579 SDL_PrintLong(char *text, size_t maxlen, SDL_FormatInfo *info, long value)
   1580 {
   1581     char num[130], *p = num;
   1582 
   1583     if (info->force_sign && value >= 0L) {
   1584         *p++ = '+';
   1585     }
   1586 
   1587     SDL_ltoa(value, p, info ? info->radix : 10);
   1588     SDL_IntPrecisionAdjust(num, maxlen, info);
   1589     return SDL_PrintString(text, maxlen, info, num);
   1590 }
   1591 
   1592 static size_t
   1593 SDL_PrintUnsignedLong(char *text, size_t maxlen, SDL_FormatInfo *info, unsigned long value)
   1594 {
   1595     char num[130];
   1596 
   1597     SDL_ultoa(value, num, info ? info->radix : 10);
   1598     SDL_IntPrecisionAdjust(num, maxlen, info);
   1599     return SDL_PrintString(text, maxlen, info, num);
   1600 }
   1601 
   1602 static size_t
   1603 SDL_PrintLongLong(char *text, size_t maxlen, SDL_FormatInfo *info, Sint64 value)
   1604 {
   1605     char num[130], *p = num;
   1606 
   1607     if (info->force_sign && value >= (Sint64)0) {
   1608         *p++ = '+';
   1609     }
   1610 
   1611     SDL_lltoa(value, p, info ? info->radix : 10);
   1612     SDL_IntPrecisionAdjust(num, maxlen, info);
   1613     return SDL_PrintString(text, maxlen, info, num);
   1614 }
   1615 
   1616 static size_t
   1617 SDL_PrintUnsignedLongLong(char *text, size_t maxlen, SDL_FormatInfo *info, Uint64 value)
   1618 {
   1619     char num[130];
   1620 
   1621     SDL_ulltoa(value, num, info ? info->radix : 10);
   1622     SDL_IntPrecisionAdjust(num, maxlen, info);
   1623     return SDL_PrintString(text, maxlen, info, num);
   1624 }
   1625 
   1626 static size_t
   1627 SDL_PrintFloat(char *text, size_t maxlen, SDL_FormatInfo *info, double arg)
   1628 {
   1629     int width;
   1630     size_t len;
   1631     size_t left = maxlen;
   1632     char *textstart = text;
   1633 
   1634     if (arg) {
   1635         /* This isn't especially accurate, but hey, it's easy. :) */
   1636         unsigned long value;
   1637 
   1638         if (arg < 0) {
   1639             if (left > 1) {
   1640                 *text = '-';
   1641                 --left;
   1642             }
   1643             ++text;
   1644             arg = -arg;
   1645         } else if (info->force_sign) {
   1646             if (left > 1) {
   1647                 *text = '+';
   1648                 --left;
   1649             }
   1650             ++text;
   1651         }
   1652         value = (unsigned long) arg;
   1653         len = SDL_PrintUnsignedLong(text, left, NULL, value);
   1654         if (len >= left) {
   1655             text += (left > 1) ? left - 1 : 0;
   1656             left = SDL_min(left, 1);
   1657         } else {
   1658             text += len;
   1659             left -= len;
   1660         }
   1661         arg -= value;
   1662         if (info->precision < 0) {
   1663             info->precision = 6;
   1664         }
   1665         if (info->force_type || info->precision > 0) {
   1666             int mult = 10;
   1667             if (left > 1) {
   1668                 *text = '.';
   1669                 --left;
   1670             }
   1671             ++text;
   1672             while (info->precision-- > 0) {
   1673                 value = (unsigned long) (arg * mult);
   1674                 len = SDL_PrintUnsignedLong(text, left, NULL, value);
   1675                 if (len >= left) {
   1676                     text += (left > 1) ? left - 1 : 0;
   1677                     left = SDL_min(left, 1);
   1678                 } else {
   1679                     text += len;
   1680                     left -= len;
   1681                 }
   1682                 arg -= (double) value / mult;
   1683                 mult *= 10;
   1684             }
   1685         }
   1686     } else {
   1687         if (left > 1) {
   1688             *text = '0';
   1689             --left;
   1690         }
   1691         ++text;
   1692         if (info->force_type) {
   1693             if (left > 1) {
   1694                 *text = '.';
   1695                 --left;
   1696             }
   1697             ++text;
   1698         }
   1699     }
   1700 
   1701     width = info->width - (int)(text - textstart);
   1702     if (width > 0) {
   1703         const char fill = info->pad_zeroes ? '0' : ' ';
   1704         char *end = text+left-1;
   1705         len = (text - textstart);
   1706         for (len = (text - textstart); len--; ) {
   1707             if ((textstart+len+width) < end) {
   1708                 *(textstart+len+width) = *(textstart+len);
   1709             }
   1710         }
   1711         len = (size_t)width;
   1712         if (len >= left) {
   1713             text += (left > 1) ? left - 1 : 0;
   1714             left = SDL_min(left, 1);
   1715         } else {
   1716             text += len;
   1717             left -= len;
   1718         }
   1719 
   1720         if (end != textstart) {
   1721             const size_t filllen = SDL_min(len, ((size_t) (end - textstart)) - 1);
   1722             SDL_memset(textstart, fill, filllen);
   1723         }
   1724     }
   1725 
   1726     return (text - textstart);
   1727 }
   1728 
   1729 int
   1730 SDL_vsnprintf(SDL_OUT_Z_CAP(maxlen) char *text, size_t maxlen, const char *fmt, va_list ap)
   1731 {
   1732     size_t left = maxlen;
   1733     char *textstart = text;
   1734 
   1735     if (!fmt) {
   1736         fmt = "";
   1737     }
   1738     while (*fmt && left > 1) {
   1739         if (*fmt == '%') {
   1740             SDL_bool done = SDL_FALSE;
   1741             size_t len = 0;
   1742             SDL_bool check_flag;
   1743             SDL_FormatInfo info;
   1744             enum
   1745             {
   1746                 DO_INT,
   1747                 DO_LONG,
   1748                 DO_LONGLONG
   1749             } inttype = DO_INT;
   1750 
   1751             SDL_zero(info);
   1752             info.radix = 10;
   1753             info.precision = -1;
   1754 
   1755             check_flag = SDL_TRUE;
   1756             while (check_flag) {
   1757                 ++fmt;
   1758                 switch (*fmt) {
   1759                 case '-':
   1760                     info.left_justify = SDL_TRUE;
   1761                     break;
   1762                 case '+':
   1763                     info.force_sign = SDL_TRUE;
   1764                     break;
   1765                 case '#':
   1766                     info.force_type = SDL_TRUE;
   1767                     break;
   1768                 case '0':
   1769                     info.pad_zeroes = SDL_TRUE;
   1770                     break;
   1771                 default:
   1772                     check_flag = SDL_FALSE;
   1773                     break;
   1774                 }
   1775             }
   1776 
   1777             if (*fmt >= '0' && *fmt <= '9') {
   1778                 info.width = SDL_strtol(fmt, (char **)&fmt, 0);
   1779             }
   1780             else if (*fmt == '*') {
   1781                 ++fmt;
   1782                 info.width = va_arg(ap, int);
   1783             }
   1784 
   1785             if (*fmt == '.') {
   1786                 ++fmt;
   1787                 if (*fmt >= '0' && *fmt <= '9') {
   1788                     info.precision = SDL_strtol(fmt, (char **)&fmt, 0);
   1789                 } else if (*fmt == '*') {
   1790                     ++fmt;
   1791                     info.precision = va_arg(ap, int);
   1792                 } else {
   1793                     info.precision = 0;
   1794                 }
   1795                 if (info.precision < 0) {
   1796                     info.precision = 0;
   1797                 }
   1798             }
   1799 
   1800             while (!done) {
   1801                 switch (*fmt) {
   1802                 case '%':
   1803                     if (left > 1) {
   1804                         *text = '%';
   1805                     }
   1806                     len = 1;
   1807                     done = SDL_TRUE;
   1808                     break;
   1809                 case 'c':
   1810                     /* char is promoted to int when passed through (...) */
   1811                     if (left > 1) {
   1812                         *text = (char) va_arg(ap, int);
   1813                     }
   1814                     len = 1;
   1815                     done = SDL_TRUE;
   1816                     break;
   1817                 case 'h':
   1818                     /* short is promoted to int when passed through (...) */
   1819                     break;
   1820                 case 'l':
   1821                     if (inttype < DO_LONGLONG) {
   1822                         ++inttype;
   1823                     }
   1824                     break;
   1825                 case 'I':
   1826                     if (SDL_strncmp(fmt, "I64", 3) == 0) {
   1827                         fmt += 2;
   1828                         inttype = DO_LONGLONG;
   1829                     }
   1830                     break;
   1831                 case 'i':
   1832                 case 'd':
   1833                     if (info.precision >= 0) {
   1834                         info.pad_zeroes = SDL_FALSE;
   1835                     }
   1836                     switch (inttype) {
   1837                     case DO_INT:
   1838                         len = SDL_PrintLong(text, left, &info,
   1839                                             (long) va_arg(ap, int));
   1840                         break;
   1841                     case DO_LONG:
   1842                         len = SDL_PrintLong(text, left, &info,
   1843                                             va_arg(ap, long));
   1844                         break;
   1845                     case DO_LONGLONG:
   1846                         len = SDL_PrintLongLong(text, left, &info,
   1847                                                 va_arg(ap, Sint64));
   1848                         break;
   1849                     }
   1850                     done = SDL_TRUE;
   1851                     break;
   1852                 case 'p':
   1853                 case 'x':
   1854                     info.force_case = SDL_CASE_LOWER;
   1855                     /* Fall through to 'X' handling */
   1856                 case 'X':
   1857                     if (info.force_case == SDL_CASE_NOCHANGE) {
   1858                         info.force_case = SDL_CASE_UPPER;
   1859                     }
   1860                     if (info.radix == 10) {
   1861                         info.radix = 16;
   1862                     }
   1863                     if (*fmt == 'p') {
   1864                         inttype = DO_LONG;
   1865                     }
   1866                     /* Fall through to unsigned handling */
   1867                 case 'o':
   1868                     if (info.radix == 10) {
   1869                         info.radix = 8;
   1870                     }
   1871                     /* Fall through to unsigned handling */
   1872                 case 'u':
   1873                     info.force_sign = SDL_FALSE;
   1874                     if (info.precision >= 0) {
   1875                         info.pad_zeroes = SDL_FALSE;
   1876                     }
   1877                     switch (inttype) {
   1878                     case DO_INT:
   1879                         len = SDL_PrintUnsignedLong(text, left, &info,
   1880                                                     (unsigned long)
   1881                                                     va_arg(ap, unsigned int));
   1882                         break;
   1883                     case DO_LONG:
   1884                         len = SDL_PrintUnsignedLong(text, left, &info,
   1885                                                     va_arg(ap, unsigned long));
   1886                         break;
   1887                     case DO_LONGLONG:
   1888                         len = SDL_PrintUnsignedLongLong(text, left, &info,
   1889                                                         va_arg(ap, Uint64));
   1890                         break;
   1891                     }
   1892                     done = SDL_TRUE;
   1893                     break;
   1894                 case 'f':
   1895                     len = SDL_PrintFloat(text, left, &info, va_arg(ap, double));
   1896                     done = SDL_TRUE;
   1897                     break;
   1898                 case 'S':
   1899                     {
   1900                         /* In practice this is used on Windows for WCHAR strings */
   1901                         wchar_t *wide_arg = va_arg(ap, wchar_t *);
   1902                         if (wide_arg) {
   1903                             char *arg = SDL_iconv_string("UTF-8", "UTF-16LE", (char *)(wide_arg), (SDL_wcslen(wide_arg)+1)*sizeof(*wide_arg));
   1904                             info.pad_zeroes = SDL_FALSE;
   1905                             len = SDL_PrintString(text, left, &info, arg);
   1906                             SDL_free(arg);
   1907                         } else {
   1908                             info.pad_zeroes = SDL_FALSE;
   1909                             len = SDL_PrintString(text, left, &info, NULL);
   1910                         }
   1911                         done = SDL_TRUE;
   1912                     }
   1913                     break;
   1914                 case 's':
   1915                     info.pad_zeroes = SDL_FALSE;
   1916                     len = SDL_PrintString(text, left, &info, va_arg(ap, char *));
   1917                     done = SDL_TRUE;
   1918                     break;
   1919                 default:
   1920                     done = SDL_TRUE;
   1921                     break;
   1922                 }
   1923                 ++fmt;
   1924             }
   1925             if (len >= left) {
   1926                 text += (left > 1) ? left - 1 : 0;
   1927                 left = SDL_min(left, 1);
   1928             } else {
   1929                 text += len;
   1930                 left -= len;
   1931             }
   1932         } else {
   1933             *text++ = *fmt++;
   1934             --left;
   1935         }
   1936     }
   1937     if (left > 0) {
   1938         *text = '\0';
   1939     }
   1940     return (int)(text - textstart);
   1941 }
   1942 #endif /* HAVE_VSNPRINTF */
   1943 
   1944 /* vi: set ts=4 sw=4 expandtab: */