src/stdlib/SDL_string.c
author Sam Lantinga <slouken@libsdl.org>
Thu, 12 Oct 2017 13:44:28 -0700
changeset 11610 6dea196ecbcb
parent 11451 b96f291a772e
child 11811 5d94cb6b24d3
permissions -rw-r--r--
Added functions to query and set the SDL memory allocation functions:
SDL_GetMemoryFunctions()
SDL_SetMemoryFunctions()
SDL_GetNumAllocations()
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2017 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     Uint32 value4 = (c | (c << 8) | (c << 16) | (c << 24));
   275     Uint8 value1 = (Uint8) c;
   276 
   277     /* The destination pointer needs to be aligned on a 4-byte boundary to
   278      * execute a 32-bit set. Set first bytes manually if needed until it is
   279      * aligned. */
   280     while ((intptr_t)dstp1 & 0x3) {
   281         if (len--) {
   282             *dstp1++ = value1;
   283         } else {
   284             return dst;
   285         }
   286     }
   287 
   288     dstp4 = (Uint32 *) dstp1;
   289     left = (len % 4);
   290     len /= 4;
   291     while (len--) {
   292         *dstp4++ = value4;
   293     }
   294 
   295     dstp1 = (Uint8 *) dstp4;
   296     switch (left) {
   297     case 3:
   298         *dstp1++ = value1;
   299     case 2:
   300         *dstp1++ = value1;
   301     case 1:
   302         *dstp1++ = value1;
   303     }
   304 
   305     return dst;
   306 #endif /* HAVE_MEMSET */
   307 }
   308 
   309 void *
   310 SDL_memcpy(SDL_OUT_BYTECAP(len) void *dst, SDL_IN_BYTECAP(len) const void *src, size_t len)
   311 {
   312 #ifdef __GNUC__
   313     /* Presumably this is well tuned for speed.
   314        On my machine this is twice as fast as the C code below.
   315      */
   316     return __builtin_memcpy(dst, src, len);
   317 #elif defined(HAVE_MEMCPY)
   318     return memcpy(dst, src, len);
   319 #elif defined(HAVE_BCOPY)
   320     bcopy(src, dst, len);
   321     return dst;
   322 #else
   323     /* GCC 4.9.0 with -O3 will generate movaps instructions with the loop
   324        using Uint32* pointers, so we need to make sure the pointers are
   325        aligned before we loop using them.
   326      */
   327     if (((intptr_t)src & 0x3) || ((intptr_t)dst & 0x3)) {
   328         /* Do an unaligned byte copy */
   329         Uint8 *srcp1 = (Uint8 *)src;
   330         Uint8 *dstp1 = (Uint8 *)dst;
   331 
   332         while (len--) {
   333             *dstp1++ = *srcp1++;
   334         }
   335     } else {
   336         size_t left = (len % 4);
   337         Uint32 *srcp4, *dstp4;
   338         Uint8 *srcp1, *dstp1;
   339 
   340         srcp4 = (Uint32 *) src;
   341         dstp4 = (Uint32 *) dst;
   342         len /= 4;
   343         while (len--) {
   344             *dstp4++ = *srcp4++;
   345         }
   346 
   347         srcp1 = (Uint8 *) srcp4;
   348         dstp1 = (Uint8 *) dstp4;
   349         switch (left) {
   350         case 3:
   351             *dstp1++ = *srcp1++;
   352         case 2:
   353             *dstp1++ = *srcp1++;
   354         case 1:
   355             *dstp1++ = *srcp1++;
   356         }
   357     }
   358     return dst;
   359 #endif /* __GNUC__ */
   360 }
   361 
   362 void *
   363 SDL_memmove(SDL_OUT_BYTECAP(len) void *dst, SDL_IN_BYTECAP(len) const void *src, size_t len)
   364 {
   365 #if defined(HAVE_MEMMOVE)
   366     return memmove(dst, src, len);
   367 #else
   368     char *srcp = (char *) src;
   369     char *dstp = (char *) dst;
   370 
   371     if (src < dst) {
   372         srcp += len - 1;
   373         dstp += len - 1;
   374         while (len--) {
   375             *dstp-- = *srcp--;
   376         }
   377     } else {
   378         while (len--) {
   379             *dstp++ = *srcp++;
   380         }
   381     }
   382     return dst;
   383 #endif /* HAVE_MEMMOVE */
   384 }
   385 
   386 int
   387 SDL_memcmp(const void *s1, const void *s2, size_t len)
   388 {
   389 #if defined(HAVE_MEMCMP)
   390     return memcmp(s1, s2, len);
   391 #else
   392     char *s1p = (char *) s1;
   393     char *s2p = (char *) s2;
   394     while (len--) {
   395         if (*s1p != *s2p) {
   396             return (*s1p - *s2p);
   397         }
   398         ++s1p;
   399         ++s2p;
   400     }
   401     return 0;
   402 #endif /* HAVE_MEMCMP */
   403 }
   404 
   405 size_t
   406 SDL_strlen(const char *string)
   407 {
   408 #if defined(HAVE_STRLEN)
   409     return strlen(string);
   410 #else
   411     size_t len = 0;
   412     while (*string++) {
   413         ++len;
   414     }
   415     return len;
   416 #endif /* HAVE_STRLEN */
   417 }
   418 
   419 size_t
   420 SDL_wcslen(const wchar_t * string)
   421 {
   422 #if defined(HAVE_WCSLEN)
   423     return wcslen(string);
   424 #else
   425     size_t len = 0;
   426     while (*string++) {
   427         ++len;
   428     }
   429     return len;
   430 #endif /* HAVE_WCSLEN */
   431 }
   432 
   433 size_t
   434 SDL_wcslcpy(SDL_OUT_Z_CAP(maxlen) wchar_t *dst, const wchar_t *src, size_t maxlen)
   435 {
   436 #if defined(HAVE_WCSLCPY)
   437     return wcslcpy(dst, src, maxlen);
   438 #else
   439     size_t srclen = SDL_wcslen(src);
   440     if (maxlen > 0) {
   441         size_t len = SDL_min(srclen, maxlen - 1);
   442         SDL_memcpy(dst, src, len * sizeof(wchar_t));
   443         dst[len] = '\0';
   444     }
   445     return srclen;
   446 #endif /* HAVE_WCSLCPY */
   447 }
   448 
   449 size_t
   450 SDL_wcslcat(SDL_INOUT_Z_CAP(maxlen) wchar_t *dst, const wchar_t *src, size_t maxlen)
   451 {
   452 #if defined(HAVE_WCSLCAT)
   453     return wcslcat(dst, src, maxlen);
   454 #else
   455     size_t dstlen = SDL_wcslen(dst);
   456     size_t srclen = SDL_wcslen(src);
   457     if (dstlen < maxlen) {
   458         SDL_wcslcpy(dst + dstlen, src, maxlen - dstlen);
   459     }
   460     return dstlen + srclen;
   461 #endif /* HAVE_WCSLCAT */
   462 }
   463 
   464 int
   465 SDL_wcscmp(const wchar_t *str1, const wchar_t *str2)
   466 {
   467 #if defined(HAVE_WCSCMP)
   468     return wcscmp(str1, str2);
   469 #else
   470     while (*str1 && *str2) {
   471         if (*str1 != *str2)
   472             break;
   473         ++str1;
   474         ++str2;
   475     }
   476     return (int)(*str1 - *str2);
   477 #endif /* HAVE_WCSCMP */
   478 }
   479 
   480 size_t
   481 SDL_strlcpy(SDL_OUT_Z_CAP(maxlen) char *dst, const char *src, size_t maxlen)
   482 {
   483 #if defined(HAVE_STRLCPY)
   484     return strlcpy(dst, src, maxlen);
   485 #else
   486     size_t srclen = SDL_strlen(src);
   487     if (maxlen > 0) {
   488         size_t len = SDL_min(srclen, maxlen - 1);
   489         SDL_memcpy(dst, src, len);
   490         dst[len] = '\0';
   491     }
   492     return srclen;
   493 #endif /* HAVE_STRLCPY */
   494 }
   495 
   496 size_t
   497 SDL_utf8strlcpy(SDL_OUT_Z_CAP(dst_bytes) char *dst, const char *src, size_t dst_bytes)
   498 {
   499     size_t src_bytes = SDL_strlen(src);
   500     size_t bytes = SDL_min(src_bytes, dst_bytes - 1);
   501     size_t i = 0;
   502     char trailing_bytes = 0;
   503     if (bytes)
   504     {
   505         unsigned char c = (unsigned char)src[bytes - 1];
   506         if (UTF8_IsLeadByte(c))
   507             --bytes;
   508         else if (UTF8_IsTrailingByte(c))
   509         {
   510             for (i = bytes - 1; i != 0; --i)
   511             {
   512                 c = (unsigned char)src[i];
   513                 trailing_bytes = UTF8_TrailingBytes(c);
   514                 if (trailing_bytes)
   515                 {
   516                     if (bytes - i != trailing_bytes + 1)
   517                         bytes = i;
   518 
   519                     break;
   520                 }
   521             }
   522         }
   523         SDL_memcpy(dst, src, bytes);
   524     }
   525     dst[bytes] = '\0';
   526     return bytes;
   527 }
   528 
   529 size_t
   530 SDL_utf8strlen(const char *str)
   531 {
   532     size_t retval = 0;
   533     const char *p = str;
   534     char ch;
   535 
   536     while ((ch = *(p++))) {
   537         /* if top two bits are 1 and 0, it's a continuation byte. */
   538         if ((ch & 0xc0) != 0x80) {
   539             retval++;
   540         }
   541     }
   542     
   543     return retval;
   544 }
   545 
   546 size_t
   547 SDL_strlcat(SDL_INOUT_Z_CAP(maxlen) char *dst, const char *src, size_t maxlen)
   548 {
   549 #if defined(HAVE_STRLCAT)
   550     return strlcat(dst, src, maxlen);
   551 #else
   552     size_t dstlen = SDL_strlen(dst);
   553     size_t srclen = SDL_strlen(src);
   554     if (dstlen < maxlen) {
   555         SDL_strlcpy(dst + dstlen, src, maxlen - dstlen);
   556     }
   557     return dstlen + srclen;
   558 #endif /* HAVE_STRLCAT */
   559 }
   560 
   561 char *
   562 SDL_strdup(const char *string)
   563 {
   564     size_t len = SDL_strlen(string) + 1;
   565     char *newstr = SDL_malloc(len);
   566     if (newstr) {
   567         SDL_strlcpy(newstr, string, len);
   568     }
   569     return newstr;
   570 }
   571 
   572 char *
   573 SDL_strrev(char *string)
   574 {
   575 #if defined(HAVE__STRREV)
   576     return _strrev(string);
   577 #else
   578     size_t len = SDL_strlen(string);
   579     char *a = &string[0];
   580     char *b = &string[len - 1];
   581     len /= 2;
   582     while (len--) {
   583         char c = *a;
   584         *a++ = *b;
   585         *b-- = c;
   586     }
   587     return string;
   588 #endif /* HAVE__STRREV */
   589 }
   590 
   591 char *
   592 SDL_strupr(char *string)
   593 {
   594 #if defined(HAVE__STRUPR)
   595     return _strupr(string);
   596 #else
   597     char *bufp = string;
   598     while (*bufp) {
   599         *bufp = SDL_toupper((unsigned char) *bufp);
   600         ++bufp;
   601     }
   602     return string;
   603 #endif /* HAVE__STRUPR */
   604 }
   605 
   606 char *
   607 SDL_strlwr(char *string)
   608 {
   609 #if defined(HAVE__STRLWR)
   610     return _strlwr(string);
   611 #else
   612     char *bufp = string;
   613     while (*bufp) {
   614         *bufp = SDL_tolower((unsigned char) *bufp);
   615         ++bufp;
   616     }
   617     return string;
   618 #endif /* HAVE__STRLWR */
   619 }
   620 
   621 char *
   622 SDL_strchr(const char *string, int c)
   623 {
   624 #ifdef HAVE_STRCHR
   625     return SDL_const_cast(char*,strchr(string, c));
   626 #elif defined(HAVE_INDEX)
   627     return SDL_const_cast(char*,index(string, c));
   628 #else
   629     while (*string) {
   630         if (*string == c) {
   631             return (char *) string;
   632         }
   633         ++string;
   634     }
   635     return NULL;
   636 #endif /* HAVE_STRCHR */
   637 }
   638 
   639 char *
   640 SDL_strrchr(const char *string, int c)
   641 {
   642 #ifdef HAVE_STRRCHR
   643     return SDL_const_cast(char*,strrchr(string, c));
   644 #elif defined(HAVE_RINDEX)
   645     return SDL_const_cast(char*,rindex(string, c));
   646 #else
   647     const char *bufp = string + SDL_strlen(string) - 1;
   648     while (bufp >= string) {
   649         if (*bufp == c) {
   650             return (char *) bufp;
   651         }
   652         --bufp;
   653     }
   654     return NULL;
   655 #endif /* HAVE_STRRCHR */
   656 }
   657 
   658 char *
   659 SDL_strstr(const char *haystack, const char *needle)
   660 {
   661 #if defined(HAVE_STRSTR)
   662     return SDL_const_cast(char*,strstr(haystack, needle));
   663 #else
   664     size_t length = SDL_strlen(needle);
   665     while (*haystack) {
   666         if (SDL_strncmp(haystack, needle, length) == 0) {
   667             return (char *) haystack;
   668         }
   669         ++haystack;
   670     }
   671     return NULL;
   672 #endif /* HAVE_STRSTR */
   673 }
   674 
   675 #if !defined(HAVE__LTOA) || !defined(HAVE__I64TOA) || \
   676     !defined(HAVE__ULTOA) || !defined(HAVE__UI64TOA)
   677 static const char ntoa_table[] = {
   678     '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
   679     'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
   680     'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
   681     'U', 'V', 'W', 'X', 'Y', 'Z'
   682 };
   683 #endif /* ntoa() conversion table */
   684 
   685 char *
   686 SDL_itoa(int value, char *string, int radix)
   687 {
   688 #ifdef HAVE_ITOA
   689     return itoa(value, string, radix);
   690 #else
   691     return SDL_ltoa((long)value, string, radix);
   692 #endif /* HAVE_ITOA */
   693 }
   694 
   695 char *
   696 SDL_uitoa(unsigned int value, char *string, int radix)
   697 {
   698 #ifdef HAVE__UITOA
   699     return _uitoa(value, string, radix);
   700 #else
   701     return SDL_ultoa((unsigned long)value, string, radix);
   702 #endif /* HAVE__UITOA */
   703 }
   704 
   705 char *
   706 SDL_ltoa(long value, char *string, int radix)
   707 {
   708 #if defined(HAVE__LTOA)
   709     return _ltoa(value, string, radix);
   710 #else
   711     char *bufp = string;
   712 
   713     if (value < 0) {
   714         *bufp++ = '-';
   715         SDL_ultoa(-value, bufp, radix);
   716     } else {
   717         SDL_ultoa(value, bufp, radix);
   718     }
   719 
   720     return string;
   721 #endif /* HAVE__LTOA */
   722 }
   723 
   724 char *
   725 SDL_ultoa(unsigned long value, char *string, int radix)
   726 {
   727 #if defined(HAVE__ULTOA)
   728     return _ultoa(value, string, radix);
   729 #else
   730     char *bufp = string;
   731 
   732     if (value) {
   733         while (value > 0) {
   734             *bufp++ = ntoa_table[value % radix];
   735             value /= radix;
   736         }
   737     } else {
   738         *bufp++ = '0';
   739     }
   740     *bufp = '\0';
   741 
   742     /* The numbers went into the string backwards. :) */
   743     SDL_strrev(string);
   744 
   745     return string;
   746 #endif /* HAVE__ULTOA */
   747 }
   748 
   749 char *
   750 SDL_lltoa(Sint64 value, char *string, int radix)
   751 {
   752 #if defined(HAVE__I64TOA)
   753     return _i64toa(value, string, radix);
   754 #else
   755     char *bufp = string;
   756 
   757     if (value < 0) {
   758         *bufp++ = '-';
   759         SDL_ulltoa(-value, bufp, radix);
   760     } else {
   761         SDL_ulltoa(value, bufp, radix);
   762     }
   763 
   764     return string;
   765 #endif /* HAVE__I64TOA */
   766 }
   767 
   768 char *
   769 SDL_ulltoa(Uint64 value, char *string, int radix)
   770 {
   771 #if defined(HAVE__UI64TOA)
   772     return _ui64toa(value, string, radix);
   773 #else
   774     char *bufp = string;
   775 
   776     if (value) {
   777         while (value > 0) {
   778             *bufp++ = ntoa_table[value % radix];
   779             value /= radix;
   780         }
   781     } else {
   782         *bufp++ = '0';
   783     }
   784     *bufp = '\0';
   785 
   786     /* The numbers went into the string backwards. :) */
   787     SDL_strrev(string);
   788 
   789     return string;
   790 #endif /* HAVE__UI64TOA */
   791 }
   792 
   793 int SDL_atoi(const char *string)
   794 {
   795 #ifdef HAVE_ATOI
   796     return atoi(string);
   797 #else
   798     return SDL_strtol(string, NULL, 0);
   799 #endif /* HAVE_ATOI */
   800 }
   801 
   802 double SDL_atof(const char *string)
   803 {
   804 #ifdef HAVE_ATOF
   805     return (double) atof(string);
   806 #else
   807     return SDL_strtod(string, NULL);
   808 #endif /* HAVE_ATOF */
   809 }
   810 
   811 long
   812 SDL_strtol(const char *string, char **endp, int base)
   813 {
   814 #if defined(HAVE_STRTOL)
   815     return strtol(string, endp, base);
   816 #else
   817     size_t len;
   818     long value = 0;
   819 
   820     if (!base) {
   821         if ((SDL_strlen(string) > 2) && (SDL_strncmp(string, "0x", 2) == 0)) {
   822             base = 16;
   823         } else {
   824             base = 10;
   825         }
   826     }
   827 
   828     len = SDL_ScanLong(string, base, &value);
   829     if (endp) {
   830         *endp = (char *) string + len;
   831     }
   832     return value;
   833 #endif /* HAVE_STRTOL */
   834 }
   835 
   836 unsigned long
   837 SDL_strtoul(const char *string, char **endp, int base)
   838 {
   839 #if defined(HAVE_STRTOUL)
   840     return strtoul(string, endp, base);
   841 #else
   842     size_t len;
   843     unsigned long value = 0;
   844 
   845     if (!base) {
   846         if ((SDL_strlen(string) > 2) && (SDL_strncmp(string, "0x", 2) == 0)) {
   847             base = 16;
   848         } else {
   849             base = 10;
   850         }
   851     }
   852 
   853     len = SDL_ScanUnsignedLong(string, base, &value);
   854     if (endp) {
   855         *endp = (char *) string + len;
   856     }
   857     return value;
   858 #endif /* HAVE_STRTOUL */
   859 }
   860 
   861 Sint64
   862 SDL_strtoll(const char *string, char **endp, int base)
   863 {
   864 #if defined(HAVE_STRTOLL)
   865     return strtoll(string, endp, base);
   866 #else
   867     size_t len;
   868     Sint64 value = 0;
   869 
   870     if (!base) {
   871         if ((SDL_strlen(string) > 2) && (SDL_strncmp(string, "0x", 2) == 0)) {
   872             base = 16;
   873         } else {
   874             base = 10;
   875         }
   876     }
   877 
   878     len = SDL_ScanLongLong(string, base, &value);
   879     if (endp) {
   880         *endp = (char *) string + len;
   881     }
   882     return value;
   883 #endif /* HAVE_STRTOLL */
   884 }
   885 
   886 Uint64
   887 SDL_strtoull(const char *string, char **endp, int base)
   888 {
   889 #if defined(HAVE_STRTOULL)
   890     return strtoull(string, endp, base);
   891 #else
   892     size_t len;
   893     Uint64 value = 0;
   894 
   895     if (!base) {
   896         if ((SDL_strlen(string) > 2) && (SDL_strncmp(string, "0x", 2) == 0)) {
   897             base = 16;
   898         } else {
   899             base = 10;
   900         }
   901     }
   902 
   903     len = SDL_ScanUnsignedLongLong(string, base, &value);
   904     if (endp) {
   905         *endp = (char *) string + len;
   906     }
   907     return value;
   908 #endif /* HAVE_STRTOULL */
   909 }
   910 
   911 double
   912 SDL_strtod(const char *string, char **endp)
   913 {
   914 #if defined(HAVE_STRTOD)
   915     return strtod(string, endp);
   916 #else
   917     size_t len;
   918     double value = 0.0;
   919 
   920     len = SDL_ScanFloat(string, &value);
   921     if (endp) {
   922         *endp = (char *) string + len;
   923     }
   924     return value;
   925 #endif /* HAVE_STRTOD */
   926 }
   927 
   928 int
   929 SDL_strcmp(const char *str1, const char *str2)
   930 {
   931 #if defined(HAVE_STRCMP)
   932     return strcmp(str1, str2);
   933 #else
   934     while (*str1 && *str2) {
   935         if (*str1 != *str2)
   936             break;
   937         ++str1;
   938         ++str2;
   939     }
   940     return (int)((unsigned char) *str1 - (unsigned char) *str2);
   941 #endif /* HAVE_STRCMP */
   942 }
   943 
   944 int
   945 SDL_strncmp(const char *str1, const char *str2, size_t maxlen)
   946 {
   947 #if defined(HAVE_STRNCMP)
   948     return strncmp(str1, str2, maxlen);
   949 #else
   950     while (*str1 && *str2 && maxlen) {
   951         if (*str1 != *str2)
   952             break;
   953         ++str1;
   954         ++str2;
   955         --maxlen;
   956     }
   957     if (!maxlen) {
   958         return 0;
   959     }
   960     return (int) ((unsigned char) *str1 - (unsigned char) *str2);
   961 #endif /* HAVE_STRNCMP */
   962 }
   963 
   964 int
   965 SDL_strcasecmp(const char *str1, const char *str2)
   966 {
   967 #ifdef HAVE_STRCASECMP
   968     return strcasecmp(str1, str2);
   969 #elif defined(HAVE__STRICMP)
   970     return _stricmp(str1, str2);
   971 #else
   972     char a = 0;
   973     char b = 0;
   974     while (*str1 && *str2) {
   975         a = SDL_toupper((unsigned char) *str1);
   976         b = SDL_toupper((unsigned char) *str2);
   977         if (a != b)
   978             break;
   979         ++str1;
   980         ++str2;
   981     }
   982     a = SDL_toupper(*str1);
   983     b = SDL_toupper(*str2);
   984     return (int) ((unsigned char) a - (unsigned char) b);
   985 #endif /* HAVE_STRCASECMP */
   986 }
   987 
   988 int
   989 SDL_strncasecmp(const char *str1, const char *str2, size_t maxlen)
   990 {
   991 #ifdef HAVE_STRNCASECMP
   992     return strncasecmp(str1, str2, maxlen);
   993 #elif defined(HAVE__STRNICMP)
   994     return _strnicmp(str1, str2, maxlen);
   995 #else
   996     char a = 0;
   997     char b = 0;
   998     while (*str1 && *str2 && maxlen) {
   999         a = SDL_tolower((unsigned char) *str1);
  1000         b = SDL_tolower((unsigned char) *str2);
  1001         if (a != b)
  1002             break;
  1003         ++str1;
  1004         ++str2;
  1005         --maxlen;
  1006     }
  1007     if (maxlen == 0) {
  1008         return 0;
  1009     } else {
  1010         a = SDL_tolower((unsigned char) *str1);
  1011         b = SDL_tolower((unsigned char) *str2);
  1012         return (int) ((unsigned char) a - (unsigned char) b);
  1013     }
  1014 #endif /* HAVE_STRNCASECMP */
  1015 }
  1016 
  1017 int
  1018 SDL_sscanf(const char *text, SDL_SCANF_FORMAT_STRING const char *fmt, ...)
  1019 {
  1020     int rc;
  1021     va_list ap;
  1022     va_start(ap, fmt);
  1023     rc = SDL_vsscanf(text, fmt, ap);
  1024     va_end(ap);
  1025     return rc;
  1026 }
  1027 
  1028 #ifdef HAVE_VSSCANF
  1029 int
  1030 SDL_vsscanf(const char *text, const char *fmt, va_list ap)
  1031 {
  1032     return vsscanf(text, fmt, ap);
  1033 }
  1034 #else
  1035 int
  1036 SDL_vsscanf(const char *text, const char *fmt, va_list ap)
  1037 {
  1038     int retval = 0;
  1039 
  1040     if (!text || !*text) {
  1041         return -1;
  1042     }
  1043 
  1044     while (*fmt) {
  1045         if (*fmt == ' ') {
  1046             while (SDL_isspace((unsigned char) *text)) {
  1047                 ++text;
  1048             }
  1049             ++fmt;
  1050             continue;
  1051         }
  1052         if (*fmt == '%') {
  1053             SDL_bool done = SDL_FALSE;
  1054             long count = 0;
  1055             int radix = 10;
  1056             enum
  1057             {
  1058                 DO_SHORT,
  1059                 DO_INT,
  1060                 DO_LONG,
  1061                 DO_LONGLONG
  1062             } inttype = DO_INT;
  1063             size_t advance;
  1064             SDL_bool suppress = SDL_FALSE;
  1065 
  1066             ++fmt;
  1067             if (*fmt == '%') {
  1068                 if (*text == '%') {
  1069                     ++text;
  1070                     ++fmt;
  1071                     continue;
  1072                 }
  1073                 break;
  1074             }
  1075             if (*fmt == '*') {
  1076                 suppress = SDL_TRUE;
  1077                 ++fmt;
  1078             }
  1079             fmt += SDL_ScanLong(fmt, 10, &count);
  1080 
  1081             if (*fmt == 'c') {
  1082                 if (!count) {
  1083                     count = 1;
  1084                 }
  1085                 if (suppress) {
  1086                     while (count--) {
  1087                         ++text;
  1088                     }
  1089                 } else {
  1090                     char *valuep = va_arg(ap, char *);
  1091                     while (count--) {
  1092                         *valuep++ = *text++;
  1093                     }
  1094                     ++retval;
  1095                 }
  1096                 continue;
  1097             }
  1098 
  1099             while (SDL_isspace((unsigned char) *text)) {
  1100                 ++text;
  1101             }
  1102 
  1103             /* FIXME: implement more of the format specifiers */
  1104             while (!done) {
  1105                 switch (*fmt) {
  1106                 case '*':
  1107                     suppress = SDL_TRUE;
  1108                     break;
  1109                 case 'h':
  1110                     if (inttype > DO_SHORT) {
  1111                         ++inttype;
  1112                     }
  1113                     break;
  1114                 case 'l':
  1115                     if (inttype < DO_LONGLONG) {
  1116                         ++inttype;
  1117                     }
  1118                     break;
  1119                 case 'I':
  1120                     if (SDL_strncmp(fmt, "I64", 3) == 0) {
  1121                         fmt += 2;
  1122                         inttype = DO_LONGLONG;
  1123                     }
  1124                     break;
  1125                 case 'i':
  1126                     {
  1127                         int index = 0;
  1128                         if (text[index] == '-') {
  1129                             ++index;
  1130                         }
  1131                         if (text[index] == '0') {
  1132                             if (SDL_tolower((unsigned char) text[index + 1]) == 'x') {
  1133                                 radix = 16;
  1134                             } else {
  1135                                 radix = 8;
  1136                             }
  1137                         }
  1138                     }
  1139                     /* Fall through to %d handling */
  1140                 case 'd':
  1141                     if (inttype == DO_LONGLONG) {
  1142                         Sint64 value;
  1143                         advance = SDL_ScanLongLong(text, radix, &value);
  1144                         text += advance;
  1145                         if (advance && !suppress) {
  1146                             Sint64 *valuep = va_arg(ap, Sint64 *);
  1147                             *valuep = value;
  1148                             ++retval;
  1149                         }
  1150                     } else {
  1151                         long value;
  1152                         advance = SDL_ScanLong(text, radix, &value);
  1153                         text += advance;
  1154                         if (advance && !suppress) {
  1155                             switch (inttype) {
  1156                             case DO_SHORT:
  1157                                 {
  1158                                     short *valuep = va_arg(ap, short *);
  1159                                     *valuep = (short) value;
  1160                                 }
  1161                                 break;
  1162                             case DO_INT:
  1163                                 {
  1164                                     int *valuep = va_arg(ap, int *);
  1165                                     *valuep = (int) value;
  1166                                 }
  1167                                 break;
  1168                             case DO_LONG:
  1169                                 {
  1170                                     long *valuep = va_arg(ap, long *);
  1171                                     *valuep = value;
  1172                                 }
  1173                                 break;
  1174                             case DO_LONGLONG:
  1175                                 /* Handled above */
  1176                                 break;
  1177                             }
  1178                             ++retval;
  1179                         }
  1180                     }
  1181                     done = SDL_TRUE;
  1182                     break;
  1183                 case 'o':
  1184                     if (radix == 10) {
  1185                         radix = 8;
  1186                     }
  1187                     /* Fall through to unsigned handling */
  1188                 case 'x':
  1189                 case 'X':
  1190                     if (radix == 10) {
  1191                         radix = 16;
  1192                     }
  1193                     /* Fall through to unsigned handling */
  1194                 case 'u':
  1195                     if (inttype == DO_LONGLONG) {
  1196                         Uint64 value = 0;
  1197                         advance = SDL_ScanUnsignedLongLong(text, radix, &value);
  1198                         text += advance;
  1199                         if (advance && !suppress) {
  1200                             Uint64 *valuep = va_arg(ap, Uint64 *);
  1201                             *valuep = value;
  1202                             ++retval;
  1203                         }
  1204                     } else {
  1205                         unsigned long value = 0;
  1206                         advance = SDL_ScanUnsignedLong(text, radix, &value);
  1207                         text += advance;
  1208                         if (advance && !suppress) {
  1209                             switch (inttype) {
  1210                             case DO_SHORT:
  1211                                 {
  1212                                     short *valuep = va_arg(ap, short *);
  1213                                     *valuep = (short) value;
  1214                                 }
  1215                                 break;
  1216                             case DO_INT:
  1217                                 {
  1218                                     int *valuep = va_arg(ap, int *);
  1219                                     *valuep = (int) value;
  1220                                 }
  1221                                 break;
  1222                             case DO_LONG:
  1223                                 {
  1224                                     long *valuep = va_arg(ap, long *);
  1225                                     *valuep = value;
  1226                                 }
  1227                                 break;
  1228                             case DO_LONGLONG:
  1229                                 /* Handled above */
  1230                                 break;
  1231                             }
  1232                             ++retval;
  1233                         }
  1234                     }
  1235                     done = SDL_TRUE;
  1236                     break;
  1237                 case 'p':
  1238                     {
  1239                         uintptr_t value = 0;
  1240                         advance = SDL_ScanUintPtrT(text, 16, &value);
  1241                         text += advance;
  1242                         if (advance && !suppress) {
  1243                             void **valuep = va_arg(ap, void **);
  1244                             *valuep = (void *) value;
  1245                             ++retval;
  1246                         }
  1247                     }
  1248                     done = SDL_TRUE;
  1249                     break;
  1250                 case 'f':
  1251                     {
  1252                         double value;
  1253                         advance = SDL_ScanFloat(text, &value);
  1254                         text += advance;
  1255                         if (advance && !suppress) {
  1256                             float *valuep = va_arg(ap, float *);
  1257                             *valuep = (float) value;
  1258                             ++retval;
  1259                         }
  1260                     }
  1261                     done = SDL_TRUE;
  1262                     break;
  1263                 case 's':
  1264                     if (suppress) {
  1265                         while (!SDL_isspace((unsigned char) *text)) {
  1266                             ++text;
  1267                             if (count) {
  1268                                 if (--count == 0) {
  1269                                     break;
  1270                                 }
  1271                             }
  1272                         }
  1273                     } else {
  1274                         char *valuep = va_arg(ap, char *);
  1275                         while (!SDL_isspace((unsigned char) *text)) {
  1276                             *valuep++ = *text++;
  1277                             if (count) {
  1278                                 if (--count == 0) {
  1279                                     break;
  1280                                 }
  1281                             }
  1282                         }
  1283                         *valuep = '\0';
  1284                         ++retval;
  1285                     }
  1286                     done = SDL_TRUE;
  1287                     break;
  1288                 default:
  1289                     done = SDL_TRUE;
  1290                     break;
  1291                 }
  1292                 ++fmt;
  1293             }
  1294             continue;
  1295         }
  1296         if (*text == *fmt) {
  1297             ++text;
  1298             ++fmt;
  1299             continue;
  1300         }
  1301         /* Text didn't match format specifier */
  1302         break;
  1303     }
  1304 
  1305     return retval;
  1306 }
  1307 #endif /* HAVE_VSSCANF */
  1308 
  1309 int
  1310 SDL_snprintf(SDL_OUT_Z_CAP(maxlen) char *text, size_t maxlen, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
  1311 {
  1312     va_list ap;
  1313     int retval;
  1314 
  1315     va_start(ap, fmt);
  1316     retval = SDL_vsnprintf(text, maxlen, fmt, ap);
  1317     va_end(ap);
  1318 
  1319     return retval;
  1320 }
  1321 
  1322 #ifdef HAVE_VSNPRINTF
  1323 int SDL_vsnprintf(SDL_OUT_Z_CAP(maxlen) char *text, size_t maxlen, const char *fmt, va_list ap)
  1324 {
  1325     if (!fmt) {
  1326         fmt = "";
  1327     }
  1328     return vsnprintf(text, maxlen, fmt, ap);
  1329 }
  1330 #else
  1331  /* FIXME: implement more of the format specifiers */
  1332 typedef enum
  1333 {
  1334     SDL_CASE_NOCHANGE,
  1335     SDL_CASE_LOWER,
  1336     SDL_CASE_UPPER
  1337 } SDL_letter_case;
  1338 
  1339 typedef struct
  1340 {
  1341     SDL_bool left_justify;
  1342     SDL_bool force_sign;
  1343     SDL_bool force_type;
  1344     SDL_bool pad_zeroes;
  1345     SDL_letter_case force_case;
  1346     int width;
  1347     int radix;
  1348     int precision;
  1349 } SDL_FormatInfo;
  1350 
  1351 static size_t
  1352 SDL_PrintString(char *text, size_t maxlen, SDL_FormatInfo *info, const char *string)
  1353 {
  1354     size_t length = 0;
  1355     size_t slen;
  1356 
  1357     if (string == NULL) {
  1358         string = "(null)";
  1359     }
  1360 
  1361     if (info && info->width && (size_t)info->width > SDL_strlen(string)) {
  1362         char fill = info->pad_zeroes ? '0' : ' ';
  1363         size_t width = info->width - SDL_strlen(string);
  1364         while (width-- > 0 && maxlen > 0) {
  1365             *text++ = fill;
  1366             ++length;
  1367             --maxlen;
  1368         }
  1369     }
  1370 
  1371     slen = SDL_strlcpy(text, string, maxlen);
  1372     length += SDL_min(slen, maxlen);
  1373 
  1374     if (info) {
  1375         if (info->force_case == SDL_CASE_LOWER) {
  1376             SDL_strlwr(text);
  1377         } else if (info->force_case == SDL_CASE_UPPER) {
  1378             SDL_strupr(text);
  1379         }
  1380     }
  1381     return length;
  1382 }
  1383 
  1384 static size_t
  1385 SDL_PrintLong(char *text, size_t maxlen, SDL_FormatInfo *info, long value)
  1386 {
  1387     char num[130];
  1388 
  1389     SDL_ltoa(value, num, info ? info->radix : 10);
  1390     return SDL_PrintString(text, maxlen, info, num);
  1391 }
  1392 
  1393 static size_t
  1394 SDL_PrintUnsignedLong(char *text, size_t maxlen, SDL_FormatInfo *info, unsigned long value)
  1395 {
  1396     char num[130];
  1397 
  1398     SDL_ultoa(value, num, info ? info->radix : 10);
  1399     return SDL_PrintString(text, maxlen, info, num);
  1400 }
  1401 
  1402 static size_t
  1403 SDL_PrintLongLong(char *text, size_t maxlen, SDL_FormatInfo *info, Sint64 value)
  1404 {
  1405     char num[130];
  1406 
  1407     SDL_lltoa(value, num, info ? info->radix : 10);
  1408     return SDL_PrintString(text, maxlen, info, num);
  1409 }
  1410 
  1411 static size_t
  1412 SDL_PrintUnsignedLongLong(char *text, size_t maxlen, SDL_FormatInfo *info, Uint64 value)
  1413 {
  1414     char num[130];
  1415 
  1416     SDL_ulltoa(value, num, info ? info->radix : 10);
  1417     return SDL_PrintString(text, maxlen, info, num);
  1418 }
  1419 
  1420 static size_t
  1421 SDL_PrintFloat(char *text, size_t maxlen, SDL_FormatInfo *info, double arg)
  1422 {
  1423     int width;
  1424     size_t len;
  1425     size_t left = maxlen;
  1426     char *textstart = text;
  1427 
  1428     if (arg) {
  1429         /* This isn't especially accurate, but hey, it's easy. :) */
  1430         unsigned long value;
  1431 
  1432         if (arg < 0) {
  1433             if (left > 1) {
  1434                 *text = '-';
  1435                 --left;
  1436             }
  1437             ++text;
  1438             arg = -arg;
  1439         } else if (info->force_sign) {
  1440             if (left > 1) {
  1441                 *text = '+';
  1442                 --left;
  1443             }
  1444             ++text;
  1445         }
  1446         value = (unsigned long) arg;
  1447         len = SDL_PrintUnsignedLong(text, left, NULL, value);
  1448         if (len >= left) {
  1449             text += (left > 1) ? left - 1 : 0;
  1450             left = SDL_min(left, 1);
  1451         } else {
  1452             text += len;
  1453             left -= len;
  1454         }
  1455         arg -= value;
  1456         if (info->precision < 0) {
  1457             info->precision = 6;
  1458         }
  1459         if (info->force_type || info->precision > 0) {
  1460             int mult = 10;
  1461             if (left > 1) {
  1462                 *text = '.';
  1463                 --left;
  1464             }
  1465             ++text;
  1466             while (info->precision-- > 0) {
  1467                 value = (unsigned long) (arg * mult);
  1468                 len = SDL_PrintUnsignedLong(text, left, NULL, value);
  1469                 if (len >= left) {
  1470                     text += (left > 1) ? left - 1 : 0;
  1471                     left = SDL_min(left, 1);
  1472                 } else {
  1473                     text += len;
  1474                     left -= len;
  1475                 }
  1476                 arg -= (double) value / mult;
  1477                 mult *= 10;
  1478             }
  1479         }
  1480     } else {
  1481         if (left > 1) {
  1482             *text = '0';
  1483             --left;
  1484         }
  1485         ++text;
  1486         if (info->force_type) {
  1487             if (left > 1) {
  1488                 *text = '.';
  1489                 --left;
  1490             }
  1491             ++text;
  1492         }
  1493     }
  1494 
  1495     width = info->width - (int)(text - textstart);
  1496     if (width > 0) {
  1497         char fill = info->pad_zeroes ? '0' : ' ';
  1498         char *end = text+left-1;
  1499         len = (text - textstart);
  1500         for (len = (text - textstart); len--; ) {
  1501             if ((textstart+len+width) < end) {
  1502                 *(textstart+len+width) = *(textstart+len);
  1503             }
  1504         }
  1505         len = (size_t)width;
  1506         if (len >= left) {
  1507             text += (left > 1) ? left - 1 : 0;
  1508             left = SDL_min(left, 1);
  1509         } else {
  1510             text += len;
  1511             left -= len;
  1512         }
  1513         while (len--) {
  1514             if (textstart+len < end) {
  1515                 textstart[len] = fill;
  1516             }
  1517         }
  1518     }
  1519 
  1520     return (text - textstart);
  1521 }
  1522 
  1523 int
  1524 SDL_vsnprintf(SDL_OUT_Z_CAP(maxlen) char *text, size_t maxlen, const char *fmt, va_list ap)
  1525 {
  1526     size_t left = maxlen;
  1527     char *textstart = text;
  1528 
  1529     if (!fmt) {
  1530         fmt = "";
  1531     }
  1532     while (*fmt && left > 1) {
  1533         if (*fmt == '%') {
  1534             SDL_bool done = SDL_FALSE;
  1535             size_t len = 0;
  1536             SDL_bool check_flag;
  1537             SDL_FormatInfo info;
  1538             enum
  1539             {
  1540                 DO_INT,
  1541                 DO_LONG,
  1542                 DO_LONGLONG
  1543             } inttype = DO_INT;
  1544 
  1545             SDL_zero(info);
  1546             info.radix = 10;
  1547             info.precision = -1;
  1548 
  1549             check_flag = SDL_TRUE;
  1550             while (check_flag) {
  1551                 ++fmt;
  1552                 switch (*fmt) {
  1553                 case '-':
  1554                     info.left_justify = SDL_TRUE;
  1555                     break;
  1556                 case '+':
  1557                     info.force_sign = SDL_TRUE;
  1558                     break;
  1559                 case '#':
  1560                     info.force_type = SDL_TRUE;
  1561                     break;
  1562                 case '0':
  1563                     info.pad_zeroes = SDL_TRUE;
  1564                     break;
  1565                 default:
  1566                     check_flag = SDL_FALSE;
  1567                     break;
  1568                 }
  1569             }
  1570 
  1571             if (*fmt >= '0' && *fmt <= '9') {
  1572                 info.width = SDL_strtol(fmt, (char **)&fmt, 0);
  1573             }
  1574 
  1575             if (*fmt == '.') {
  1576                 ++fmt;
  1577                 if (*fmt >= '0' && *fmt <= '9') {
  1578                     info.precision = SDL_strtol(fmt, (char **)&fmt, 0);
  1579                 } else {
  1580                     info.precision = 0;
  1581                 }
  1582             }
  1583 
  1584             while (!done) {
  1585                 switch (*fmt) {
  1586                 case '%':
  1587                     if (left > 1) {
  1588                         *text = '%';
  1589                     }
  1590                     len = 1;
  1591                     done = SDL_TRUE;
  1592                     break;
  1593                 case 'c':
  1594                     /* char is promoted to int when passed through (...) */
  1595                     if (left > 1) {
  1596                         *text = (char) va_arg(ap, int);
  1597                     }
  1598                     len = 1;
  1599                     done = SDL_TRUE;
  1600                     break;
  1601                 case 'h':
  1602                     /* short is promoted to int when passed through (...) */
  1603                     break;
  1604                 case 'l':
  1605                     if (inttype < DO_LONGLONG) {
  1606                         ++inttype;
  1607                     }
  1608                     break;
  1609                 case 'I':
  1610                     if (SDL_strncmp(fmt, "I64", 3) == 0) {
  1611                         fmt += 2;
  1612                         inttype = DO_LONGLONG;
  1613                     }
  1614                     break;
  1615                 case 'i':
  1616                 case 'd':
  1617                     switch (inttype) {
  1618                     case DO_INT:
  1619                         len = SDL_PrintLong(text, left, &info,
  1620                                             (long) va_arg(ap, int));
  1621                         break;
  1622                     case DO_LONG:
  1623                         len = SDL_PrintLong(text, left, &info,
  1624                                             va_arg(ap, long));
  1625                         break;
  1626                     case DO_LONGLONG:
  1627                         len = SDL_PrintLongLong(text, left, &info,
  1628                                                 va_arg(ap, Sint64));
  1629                         break;
  1630                     }
  1631                     done = SDL_TRUE;
  1632                     break;
  1633                 case 'p':
  1634                 case 'x':
  1635                     info.force_case = SDL_CASE_LOWER;
  1636                     /* Fall through to 'X' handling */
  1637                 case 'X':
  1638                     if (info.force_case == SDL_CASE_NOCHANGE) {
  1639                         info.force_case = SDL_CASE_UPPER;
  1640                     }
  1641                     if (info.radix == 10) {
  1642                         info.radix = 16;
  1643                     }
  1644                     if (*fmt == 'p') {
  1645                         inttype = DO_LONG;
  1646                     }
  1647                     /* Fall through to unsigned handling */
  1648                 case 'o':
  1649                     if (info.radix == 10) {
  1650                         info.radix = 8;
  1651                     }
  1652                     /* Fall through to unsigned handling */
  1653                 case 'u':
  1654                     info.pad_zeroes = SDL_TRUE;
  1655                     switch (inttype) {
  1656                     case DO_INT:
  1657                         len = SDL_PrintUnsignedLong(text, left, &info,
  1658                                                     (unsigned long)
  1659                                                     va_arg(ap, unsigned int));
  1660                         break;
  1661                     case DO_LONG:
  1662                         len = SDL_PrintUnsignedLong(text, left, &info,
  1663                                                     va_arg(ap, unsigned long));
  1664                         break;
  1665                     case DO_LONGLONG:
  1666                         len = SDL_PrintUnsignedLongLong(text, left, &info,
  1667                                                         va_arg(ap, Uint64));
  1668                         break;
  1669                     }
  1670                     done = SDL_TRUE;
  1671                     break;
  1672                 case 'f':
  1673                     len = SDL_PrintFloat(text, left, &info, va_arg(ap, double));
  1674                     done = SDL_TRUE;
  1675                     break;
  1676                 case 'S':
  1677                     {
  1678                         /* In practice this is used on Windows for WCHAR strings */
  1679                         wchar_t *wide_arg = va_arg(ap, wchar_t *);
  1680                         char *arg = SDL_iconv_string("UTF-8", "UTF-16LE", (char *)(wide_arg), (SDL_wcslen(wide_arg)+1)*sizeof(*wide_arg));
  1681                         len = SDL_PrintString(text, left, &info, arg);
  1682                         SDL_free(arg);
  1683                         done = SDL_TRUE;
  1684                     }
  1685                     break;
  1686                 case 's':
  1687                     len = SDL_PrintString(text, left, &info, va_arg(ap, char *));
  1688                     done = SDL_TRUE;
  1689                     break;
  1690                 default:
  1691                     done = SDL_TRUE;
  1692                     break;
  1693                 }
  1694                 ++fmt;
  1695             }
  1696             if (len >= left) {
  1697                 text += (left > 1) ? left - 1 : 0;
  1698                 left = SDL_min(left, 1);
  1699             } else {
  1700                 text += len;
  1701                 left -= len;
  1702             }
  1703         } else {
  1704             *text++ = *fmt++;
  1705             --left;
  1706         }
  1707     }
  1708     if (left > 0) {
  1709         *text = '\0';
  1710     }
  1711     return (int)(text - textstart);
  1712 }
  1713 #endif /* HAVE_VSNPRINTF */
  1714 
  1715 /* vi: set ts=4 sw=4 expandtab: */