src/stdlib/SDL_string.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 28 Aug 2017 01:42:18 -0700
changeset 11386 d5c2d689bf6d
parent 11265 9152aa462a82
child 11451 b96f291a772e
permissions -rw-r--r--
Fixed build when Wayland is dynamically loaded
     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 #if defined(HAVE_STRDUP)
   565     return strdup(string);
   566 #else
   567     size_t len = SDL_strlen(string) + 1;
   568     char *newstr = SDL_malloc(len);
   569     if (newstr) {
   570         SDL_strlcpy(newstr, string, len);
   571     }
   572     return newstr;
   573 #endif /* HAVE_STRDUP */
   574 }
   575 
   576 char *
   577 SDL_strrev(char *string)
   578 {
   579 #if defined(HAVE__STRREV)
   580     return _strrev(string);
   581 #else
   582     size_t len = SDL_strlen(string);
   583     char *a = &string[0];
   584     char *b = &string[len - 1];
   585     len /= 2;
   586     while (len--) {
   587         char c = *a;
   588         *a++ = *b;
   589         *b-- = c;
   590     }
   591     return string;
   592 #endif /* HAVE__STRREV */
   593 }
   594 
   595 char *
   596 SDL_strupr(char *string)
   597 {
   598 #if defined(HAVE__STRUPR)
   599     return _strupr(string);
   600 #else
   601     char *bufp = string;
   602     while (*bufp) {
   603         *bufp = SDL_toupper((unsigned char) *bufp);
   604         ++bufp;
   605     }
   606     return string;
   607 #endif /* HAVE__STRUPR */
   608 }
   609 
   610 char *
   611 SDL_strlwr(char *string)
   612 {
   613 #if defined(HAVE__STRLWR)
   614     return _strlwr(string);
   615 #else
   616     char *bufp = string;
   617     while (*bufp) {
   618         *bufp = SDL_tolower((unsigned char) *bufp);
   619         ++bufp;
   620     }
   621     return string;
   622 #endif /* HAVE__STRLWR */
   623 }
   624 
   625 char *
   626 SDL_strchr(const char *string, int c)
   627 {
   628 #ifdef HAVE_STRCHR
   629     return SDL_const_cast(char*,strchr(string, c));
   630 #elif defined(HAVE_INDEX)
   631     return SDL_const_cast(char*,index(string, c));
   632 #else
   633     while (*string) {
   634         if (*string == c) {
   635             return (char *) string;
   636         }
   637         ++string;
   638     }
   639     return NULL;
   640 #endif /* HAVE_STRCHR */
   641 }
   642 
   643 char *
   644 SDL_strrchr(const char *string, int c)
   645 {
   646 #ifdef HAVE_STRRCHR
   647     return SDL_const_cast(char*,strrchr(string, c));
   648 #elif defined(HAVE_RINDEX)
   649     return SDL_const_cast(char*,rindex(string, c));
   650 #else
   651     const char *bufp = string + SDL_strlen(string) - 1;
   652     while (bufp >= string) {
   653         if (*bufp == c) {
   654             return (char *) bufp;
   655         }
   656         --bufp;
   657     }
   658     return NULL;
   659 #endif /* HAVE_STRRCHR */
   660 }
   661 
   662 char *
   663 SDL_strstr(const char *haystack, const char *needle)
   664 {
   665 #if defined(HAVE_STRSTR)
   666     return SDL_const_cast(char*,strstr(haystack, needle));
   667 #else
   668     size_t length = SDL_strlen(needle);
   669     while (*haystack) {
   670         if (SDL_strncmp(haystack, needle, length) == 0) {
   671             return (char *) haystack;
   672         }
   673         ++haystack;
   674     }
   675     return NULL;
   676 #endif /* HAVE_STRSTR */
   677 }
   678 
   679 #if !defined(HAVE__LTOA) || !defined(HAVE__I64TOA) || \
   680     !defined(HAVE__ULTOA) || !defined(HAVE__UI64TOA)
   681 static const char ntoa_table[] = {
   682     '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
   683     'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
   684     'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
   685     'U', 'V', 'W', 'X', 'Y', 'Z'
   686 };
   687 #endif /* ntoa() conversion table */
   688 
   689 char *
   690 SDL_itoa(int value, char *string, int radix)
   691 {
   692 #ifdef HAVE_ITOA
   693     return itoa(value, string, radix);
   694 #else
   695     return SDL_ltoa((long)value, string, radix);
   696 #endif /* HAVE_ITOA */
   697 }
   698 
   699 char *
   700 SDL_uitoa(unsigned int value, char *string, int radix)
   701 {
   702 #ifdef HAVE__UITOA
   703     return _uitoa(value, string, radix);
   704 #else
   705     return SDL_ultoa((unsigned long)value, string, radix);
   706 #endif /* HAVE__UITOA */
   707 }
   708 
   709 char *
   710 SDL_ltoa(long value, char *string, int radix)
   711 {
   712 #if defined(HAVE__LTOA)
   713     return _ltoa(value, string, radix);
   714 #else
   715     char *bufp = string;
   716 
   717     if (value < 0) {
   718         *bufp++ = '-';
   719         SDL_ultoa(-value, bufp, radix);
   720     } else {
   721         SDL_ultoa(value, bufp, radix);
   722     }
   723 
   724     return string;
   725 #endif /* HAVE__LTOA */
   726 }
   727 
   728 char *
   729 SDL_ultoa(unsigned long value, char *string, int radix)
   730 {
   731 #if defined(HAVE__ULTOA)
   732     return _ultoa(value, string, radix);
   733 #else
   734     char *bufp = string;
   735 
   736     if (value) {
   737         while (value > 0) {
   738             *bufp++ = ntoa_table[value % radix];
   739             value /= radix;
   740         }
   741     } else {
   742         *bufp++ = '0';
   743     }
   744     *bufp = '\0';
   745 
   746     /* The numbers went into the string backwards. :) */
   747     SDL_strrev(string);
   748 
   749     return string;
   750 #endif /* HAVE__ULTOA */
   751 }
   752 
   753 char *
   754 SDL_lltoa(Sint64 value, char *string, int radix)
   755 {
   756 #if defined(HAVE__I64TOA)
   757     return _i64toa(value, string, radix);
   758 #else
   759     char *bufp = string;
   760 
   761     if (value < 0) {
   762         *bufp++ = '-';
   763         SDL_ulltoa(-value, bufp, radix);
   764     } else {
   765         SDL_ulltoa(value, bufp, radix);
   766     }
   767 
   768     return string;
   769 #endif /* HAVE__I64TOA */
   770 }
   771 
   772 char *
   773 SDL_ulltoa(Uint64 value, char *string, int radix)
   774 {
   775 #if defined(HAVE__UI64TOA)
   776     return _ui64toa(value, string, radix);
   777 #else
   778     char *bufp = string;
   779 
   780     if (value) {
   781         while (value > 0) {
   782             *bufp++ = ntoa_table[value % radix];
   783             value /= radix;
   784         }
   785     } else {
   786         *bufp++ = '0';
   787     }
   788     *bufp = '\0';
   789 
   790     /* The numbers went into the string backwards. :) */
   791     SDL_strrev(string);
   792 
   793     return string;
   794 #endif /* HAVE__UI64TOA */
   795 }
   796 
   797 int SDL_atoi(const char *string)
   798 {
   799 #ifdef HAVE_ATOI
   800     return atoi(string);
   801 #else
   802     return SDL_strtol(string, NULL, 0);
   803 #endif /* HAVE_ATOI */
   804 }
   805 
   806 double SDL_atof(const char *string)
   807 {
   808 #ifdef HAVE_ATOF
   809     return (double) atof(string);
   810 #else
   811     return SDL_strtod(string, NULL);
   812 #endif /* HAVE_ATOF */
   813 }
   814 
   815 long
   816 SDL_strtol(const char *string, char **endp, int base)
   817 {
   818 #if defined(HAVE_STRTOL)
   819     return strtol(string, endp, base);
   820 #else
   821     size_t len;
   822     long value = 0;
   823 
   824     if (!base) {
   825         if ((SDL_strlen(string) > 2) && (SDL_strncmp(string, "0x", 2) == 0)) {
   826             base = 16;
   827         } else {
   828             base = 10;
   829         }
   830     }
   831 
   832     len = SDL_ScanLong(string, base, &value);
   833     if (endp) {
   834         *endp = (char *) string + len;
   835     }
   836     return value;
   837 #endif /* HAVE_STRTOL */
   838 }
   839 
   840 unsigned long
   841 SDL_strtoul(const char *string, char **endp, int base)
   842 {
   843 #if defined(HAVE_STRTOUL)
   844     return strtoul(string, endp, base);
   845 #else
   846     size_t len;
   847     unsigned long value = 0;
   848 
   849     if (!base) {
   850         if ((SDL_strlen(string) > 2) && (SDL_strncmp(string, "0x", 2) == 0)) {
   851             base = 16;
   852         } else {
   853             base = 10;
   854         }
   855     }
   856 
   857     len = SDL_ScanUnsignedLong(string, base, &value);
   858     if (endp) {
   859         *endp = (char *) string + len;
   860     }
   861     return value;
   862 #endif /* HAVE_STRTOUL */
   863 }
   864 
   865 Sint64
   866 SDL_strtoll(const char *string, char **endp, int base)
   867 {
   868 #if defined(HAVE_STRTOLL)
   869     return strtoll(string, endp, base);
   870 #else
   871     size_t len;
   872     Sint64 value = 0;
   873 
   874     if (!base) {
   875         if ((SDL_strlen(string) > 2) && (SDL_strncmp(string, "0x", 2) == 0)) {
   876             base = 16;
   877         } else {
   878             base = 10;
   879         }
   880     }
   881 
   882     len = SDL_ScanLongLong(string, base, &value);
   883     if (endp) {
   884         *endp = (char *) string + len;
   885     }
   886     return value;
   887 #endif /* HAVE_STRTOLL */
   888 }
   889 
   890 Uint64
   891 SDL_strtoull(const char *string, char **endp, int base)
   892 {
   893 #if defined(HAVE_STRTOULL)
   894     return strtoull(string, endp, base);
   895 #else
   896     size_t len;
   897     Uint64 value = 0;
   898 
   899     if (!base) {
   900         if ((SDL_strlen(string) > 2) && (SDL_strncmp(string, "0x", 2) == 0)) {
   901             base = 16;
   902         } else {
   903             base = 10;
   904         }
   905     }
   906 
   907     len = SDL_ScanUnsignedLongLong(string, base, &value);
   908     if (endp) {
   909         *endp = (char *) string + len;
   910     }
   911     return value;
   912 #endif /* HAVE_STRTOULL */
   913 }
   914 
   915 double
   916 SDL_strtod(const char *string, char **endp)
   917 {
   918 #if defined(HAVE_STRTOD)
   919     return strtod(string, endp);
   920 #else
   921     size_t len;
   922     double value = 0.0;
   923 
   924     len = SDL_ScanFloat(string, &value);
   925     if (endp) {
   926         *endp = (char *) string + len;
   927     }
   928     return value;
   929 #endif /* HAVE_STRTOD */
   930 }
   931 
   932 int
   933 SDL_strcmp(const char *str1, const char *str2)
   934 {
   935 #if defined(HAVE_STRCMP)
   936     return strcmp(str1, str2);
   937 #else
   938     while (*str1 && *str2) {
   939         if (*str1 != *str2)
   940             break;
   941         ++str1;
   942         ++str2;
   943     }
   944     return (int)((unsigned char) *str1 - (unsigned char) *str2);
   945 #endif /* HAVE_STRCMP */
   946 }
   947 
   948 int
   949 SDL_strncmp(const char *str1, const char *str2, size_t maxlen)
   950 {
   951 #if defined(HAVE_STRNCMP)
   952     return strncmp(str1, str2, maxlen);
   953 #else
   954     while (*str1 && *str2 && maxlen) {
   955         if (*str1 != *str2)
   956             break;
   957         ++str1;
   958         ++str2;
   959         --maxlen;
   960     }
   961     if (!maxlen) {
   962         return 0;
   963     }
   964     return (int) ((unsigned char) *str1 - (unsigned char) *str2);
   965 #endif /* HAVE_STRNCMP */
   966 }
   967 
   968 int
   969 SDL_strcasecmp(const char *str1, const char *str2)
   970 {
   971 #ifdef HAVE_STRCASECMP
   972     return strcasecmp(str1, str2);
   973 #elif defined(HAVE__STRICMP)
   974     return _stricmp(str1, str2);
   975 #else
   976     char a = 0;
   977     char b = 0;
   978     while (*str1 && *str2) {
   979         a = SDL_toupper((unsigned char) *str1);
   980         b = SDL_toupper((unsigned char) *str2);
   981         if (a != b)
   982             break;
   983         ++str1;
   984         ++str2;
   985     }
   986     a = SDL_toupper(*str1);
   987     b = SDL_toupper(*str2);
   988     return (int) ((unsigned char) a - (unsigned char) b);
   989 #endif /* HAVE_STRCASECMP */
   990 }
   991 
   992 int
   993 SDL_strncasecmp(const char *str1, const char *str2, size_t maxlen)
   994 {
   995 #ifdef HAVE_STRNCASECMP
   996     return strncasecmp(str1, str2, maxlen);
   997 #elif defined(HAVE__STRNICMP)
   998     return _strnicmp(str1, str2, maxlen);
   999 #else
  1000     char a = 0;
  1001     char b = 0;
  1002     while (*str1 && *str2 && maxlen) {
  1003         a = SDL_tolower((unsigned char) *str1);
  1004         b = SDL_tolower((unsigned char) *str2);
  1005         if (a != b)
  1006             break;
  1007         ++str1;
  1008         ++str2;
  1009         --maxlen;
  1010     }
  1011     if (maxlen == 0) {
  1012         return 0;
  1013     } else {
  1014         a = SDL_tolower((unsigned char) *str1);
  1015         b = SDL_tolower((unsigned char) *str2);
  1016         return (int) ((unsigned char) a - (unsigned char) b);
  1017     }
  1018 #endif /* HAVE_STRNCASECMP */
  1019 }
  1020 
  1021 int
  1022 SDL_sscanf(const char *text, SDL_SCANF_FORMAT_STRING const char *fmt, ...)
  1023 {
  1024     int rc;
  1025     va_list ap;
  1026     va_start(ap, fmt);
  1027     rc = SDL_vsscanf(text, fmt, ap);
  1028     va_end(ap);
  1029     return rc;
  1030 }
  1031 
  1032 #ifdef HAVE_VSSCANF
  1033 int
  1034 SDL_vsscanf(const char *text, const char *fmt, va_list ap)
  1035 {
  1036     return vsscanf(text, fmt, ap);
  1037 }
  1038 #else
  1039 int
  1040 SDL_vsscanf(const char *text, const char *fmt, va_list ap)
  1041 {
  1042     int retval = 0;
  1043 
  1044     if (!text || !*text) {
  1045         return -1;
  1046     }
  1047 
  1048     while (*fmt) {
  1049         if (*fmt == ' ') {
  1050             while (SDL_isspace((unsigned char) *text)) {
  1051                 ++text;
  1052             }
  1053             ++fmt;
  1054             continue;
  1055         }
  1056         if (*fmt == '%') {
  1057             SDL_bool done = SDL_FALSE;
  1058             long count = 0;
  1059             int radix = 10;
  1060             enum
  1061             {
  1062                 DO_SHORT,
  1063                 DO_INT,
  1064                 DO_LONG,
  1065                 DO_LONGLONG
  1066             } inttype = DO_INT;
  1067             size_t advance;
  1068             SDL_bool suppress = SDL_FALSE;
  1069 
  1070             ++fmt;
  1071             if (*fmt == '%') {
  1072                 if (*text == '%') {
  1073                     ++text;
  1074                     ++fmt;
  1075                     continue;
  1076                 }
  1077                 break;
  1078             }
  1079             if (*fmt == '*') {
  1080                 suppress = SDL_TRUE;
  1081                 ++fmt;
  1082             }
  1083             fmt += SDL_ScanLong(fmt, 10, &count);
  1084 
  1085             if (*fmt == 'c') {
  1086                 if (!count) {
  1087                     count = 1;
  1088                 }
  1089                 if (suppress) {
  1090                     while (count--) {
  1091                         ++text;
  1092                     }
  1093                 } else {
  1094                     char *valuep = va_arg(ap, char *);
  1095                     while (count--) {
  1096                         *valuep++ = *text++;
  1097                     }
  1098                     ++retval;
  1099                 }
  1100                 continue;
  1101             }
  1102 
  1103             while (SDL_isspace((unsigned char) *text)) {
  1104                 ++text;
  1105             }
  1106 
  1107             /* FIXME: implement more of the format specifiers */
  1108             while (!done) {
  1109                 switch (*fmt) {
  1110                 case '*':
  1111                     suppress = SDL_TRUE;
  1112                     break;
  1113                 case 'h':
  1114                     if (inttype > DO_SHORT) {
  1115                         ++inttype;
  1116                     }
  1117                     break;
  1118                 case 'l':
  1119                     if (inttype < DO_LONGLONG) {
  1120                         ++inttype;
  1121                     }
  1122                     break;
  1123                 case 'I':
  1124                     if (SDL_strncmp(fmt, "I64", 3) == 0) {
  1125                         fmt += 2;
  1126                         inttype = DO_LONGLONG;
  1127                     }
  1128                     break;
  1129                 case 'i':
  1130                     {
  1131                         int index = 0;
  1132                         if (text[index] == '-') {
  1133                             ++index;
  1134                         }
  1135                         if (text[index] == '0') {
  1136                             if (SDL_tolower((unsigned char) text[index + 1]) == 'x') {
  1137                                 radix = 16;
  1138                             } else {
  1139                                 radix = 8;
  1140                             }
  1141                         }
  1142                     }
  1143                     /* Fall through to %d handling */
  1144                 case 'd':
  1145                     if (inttype == DO_LONGLONG) {
  1146                         Sint64 value;
  1147                         advance = SDL_ScanLongLong(text, radix, &value);
  1148                         text += advance;
  1149                         if (advance && !suppress) {
  1150                             Sint64 *valuep = va_arg(ap, Sint64 *);
  1151                             *valuep = value;
  1152                             ++retval;
  1153                         }
  1154                     } else {
  1155                         long value;
  1156                         advance = SDL_ScanLong(text, radix, &value);
  1157                         text += advance;
  1158                         if (advance && !suppress) {
  1159                             switch (inttype) {
  1160                             case DO_SHORT:
  1161                                 {
  1162                                     short *valuep = va_arg(ap, short *);
  1163                                     *valuep = (short) value;
  1164                                 }
  1165                                 break;
  1166                             case DO_INT:
  1167                                 {
  1168                                     int *valuep = va_arg(ap, int *);
  1169                                     *valuep = (int) value;
  1170                                 }
  1171                                 break;
  1172                             case DO_LONG:
  1173                                 {
  1174                                     long *valuep = va_arg(ap, long *);
  1175                                     *valuep = value;
  1176                                 }
  1177                                 break;
  1178                             case DO_LONGLONG:
  1179                                 /* Handled above */
  1180                                 break;
  1181                             }
  1182                             ++retval;
  1183                         }
  1184                     }
  1185                     done = SDL_TRUE;
  1186                     break;
  1187                 case 'o':
  1188                     if (radix == 10) {
  1189                         radix = 8;
  1190                     }
  1191                     /* Fall through to unsigned handling */
  1192                 case 'x':
  1193                 case 'X':
  1194                     if (radix == 10) {
  1195                         radix = 16;
  1196                     }
  1197                     /* Fall through to unsigned handling */
  1198                 case 'u':
  1199                     if (inttype == DO_LONGLONG) {
  1200                         Uint64 value;
  1201                         advance = SDL_ScanUnsignedLongLong(text, radix, &value);
  1202                         text += advance;
  1203                         if (advance && !suppress) {
  1204                             Uint64 *valuep = va_arg(ap, Uint64 *);
  1205                             *valuep = value;
  1206                             ++retval;
  1207                         }
  1208                     } else {
  1209                         unsigned long value;
  1210                         advance = SDL_ScanUnsignedLong(text, radix, &value);
  1211                         text += advance;
  1212                         if (advance && !suppress) {
  1213                             switch (inttype) {
  1214                             case DO_SHORT:
  1215                                 {
  1216                                     short *valuep = va_arg(ap, short *);
  1217                                     *valuep = (short) value;
  1218                                 }
  1219                                 break;
  1220                             case DO_INT:
  1221                                 {
  1222                                     int *valuep = va_arg(ap, int *);
  1223                                     *valuep = (int) value;
  1224                                 }
  1225                                 break;
  1226                             case DO_LONG:
  1227                                 {
  1228                                     long *valuep = va_arg(ap, long *);
  1229                                     *valuep = value;
  1230                                 }
  1231                                 break;
  1232                             case DO_LONGLONG:
  1233                                 /* Handled above */
  1234                                 break;
  1235                             }
  1236                             ++retval;
  1237                         }
  1238                     }
  1239                     done = SDL_TRUE;
  1240                     break;
  1241                 case 'p':
  1242                     {
  1243                         uintptr_t value;
  1244                         advance = SDL_ScanUintPtrT(text, 16, &value);
  1245                         text += advance;
  1246                         if (advance && !suppress) {
  1247                             void **valuep = va_arg(ap, void **);
  1248                             *valuep = (void *) value;
  1249                             ++retval;
  1250                         }
  1251                     }
  1252                     done = SDL_TRUE;
  1253                     break;
  1254                 case 'f':
  1255                     {
  1256                         double value;
  1257                         advance = SDL_ScanFloat(text, &value);
  1258                         text += advance;
  1259                         if (advance && !suppress) {
  1260                             float *valuep = va_arg(ap, float *);
  1261                             *valuep = (float) value;
  1262                             ++retval;
  1263                         }
  1264                     }
  1265                     done = SDL_TRUE;
  1266                     break;
  1267                 case 's':
  1268                     if (suppress) {
  1269                         while (!SDL_isspace((unsigned char) *text)) {
  1270                             ++text;
  1271                             if (count) {
  1272                                 if (--count == 0) {
  1273                                     break;
  1274                                 }
  1275                             }
  1276                         }
  1277                     } else {
  1278                         char *valuep = va_arg(ap, char *);
  1279                         while (!SDL_isspace((unsigned char) *text)) {
  1280                             *valuep++ = *text++;
  1281                             if (count) {
  1282                                 if (--count == 0) {
  1283                                     break;
  1284                                 }
  1285                             }
  1286                         }
  1287                         *valuep = '\0';
  1288                         ++retval;
  1289                     }
  1290                     done = SDL_TRUE;
  1291                     break;
  1292                 default:
  1293                     done = SDL_TRUE;
  1294                     break;
  1295                 }
  1296                 ++fmt;
  1297             }
  1298             continue;
  1299         }
  1300         if (*text == *fmt) {
  1301             ++text;
  1302             ++fmt;
  1303             continue;
  1304         }
  1305         /* Text didn't match format specifier */
  1306         break;
  1307     }
  1308 
  1309     return retval;
  1310 }
  1311 #endif /* HAVE_VSSCANF */
  1312 
  1313 int
  1314 SDL_snprintf(SDL_OUT_Z_CAP(maxlen) char *text, size_t maxlen, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
  1315 {
  1316     va_list ap;
  1317     int retval;
  1318 
  1319     va_start(ap, fmt);
  1320     retval = SDL_vsnprintf(text, maxlen, fmt, ap);
  1321     va_end(ap);
  1322 
  1323     return retval;
  1324 }
  1325 
  1326 #ifdef HAVE_VSNPRINTF
  1327 int SDL_vsnprintf(SDL_OUT_Z_CAP(maxlen) char *text, size_t maxlen, const char *fmt, va_list ap)
  1328 {
  1329     if (!fmt) {
  1330         fmt = "";
  1331     }
  1332     return vsnprintf(text, maxlen, fmt, ap);
  1333 }
  1334 #else
  1335  /* FIXME: implement more of the format specifiers */
  1336 typedef enum
  1337 {
  1338     SDL_CASE_NOCHANGE,
  1339     SDL_CASE_LOWER,
  1340     SDL_CASE_UPPER
  1341 } SDL_letter_case;
  1342 
  1343 typedef struct
  1344 {
  1345     SDL_bool left_justify;
  1346     SDL_bool force_sign;
  1347     SDL_bool force_type;
  1348     SDL_bool pad_zeroes;
  1349     SDL_letter_case force_case;
  1350     int width;
  1351     int radix;
  1352     int precision;
  1353 } SDL_FormatInfo;
  1354 
  1355 static size_t
  1356 SDL_PrintString(char *text, size_t maxlen, SDL_FormatInfo *info, const char *string)
  1357 {
  1358     size_t length = 0;
  1359     size_t slen;
  1360 
  1361     if (string == NULL) {
  1362         string = "(null)";
  1363     }
  1364 
  1365     if (info && info->width && (size_t)info->width > SDL_strlen(string)) {
  1366         char fill = info->pad_zeroes ? '0' : ' ';
  1367         size_t width = info->width - SDL_strlen(string);
  1368         while (width-- > 0 && maxlen > 0) {
  1369             *text++ = fill;
  1370             ++length;
  1371             --maxlen;
  1372         }
  1373     }
  1374 
  1375     slen = SDL_strlcpy(text, string, maxlen);
  1376     length += SDL_min(slen, maxlen);
  1377 
  1378     if (info) {
  1379         if (info->force_case == SDL_CASE_LOWER) {
  1380             SDL_strlwr(text);
  1381         } else if (info->force_case == SDL_CASE_UPPER) {
  1382             SDL_strupr(text);
  1383         }
  1384     }
  1385     return length;
  1386 }
  1387 
  1388 static size_t
  1389 SDL_PrintLong(char *text, size_t maxlen, SDL_FormatInfo *info, long value)
  1390 {
  1391     char num[130];
  1392 
  1393     SDL_ltoa(value, num, info ? info->radix : 10);
  1394     return SDL_PrintString(text, maxlen, info, num);
  1395 }
  1396 
  1397 static size_t
  1398 SDL_PrintUnsignedLong(char *text, size_t maxlen, SDL_FormatInfo *info, unsigned long value)
  1399 {
  1400     char num[130];
  1401 
  1402     SDL_ultoa(value, num, info ? info->radix : 10);
  1403     return SDL_PrintString(text, maxlen, info, num);
  1404 }
  1405 
  1406 static size_t
  1407 SDL_PrintLongLong(char *text, size_t maxlen, SDL_FormatInfo *info, Sint64 value)
  1408 {
  1409     char num[130];
  1410 
  1411     SDL_lltoa(value, num, info ? info->radix : 10);
  1412     return SDL_PrintString(text, maxlen, info, num);
  1413 }
  1414 
  1415 static size_t
  1416 SDL_PrintUnsignedLongLong(char *text, size_t maxlen, SDL_FormatInfo *info, Uint64 value)
  1417 {
  1418     char num[130];
  1419 
  1420     SDL_ulltoa(value, num, info ? info->radix : 10);
  1421     return SDL_PrintString(text, maxlen, info, num);
  1422 }
  1423 
  1424 static size_t
  1425 SDL_PrintFloat(char *text, size_t maxlen, SDL_FormatInfo *info, double arg)
  1426 {
  1427     int width;
  1428     size_t len;
  1429     size_t left = maxlen;
  1430     char *textstart = text;
  1431 
  1432     if (arg) {
  1433         /* This isn't especially accurate, but hey, it's easy. :) */
  1434         unsigned long value;
  1435 
  1436         if (arg < 0) {
  1437             if (left > 1) {
  1438                 *text = '-';
  1439                 --left;
  1440             }
  1441             ++text;
  1442             arg = -arg;
  1443         } else if (info->force_sign) {
  1444             if (left > 1) {
  1445                 *text = '+';
  1446                 --left;
  1447             }
  1448             ++text;
  1449         }
  1450         value = (unsigned long) arg;
  1451         len = SDL_PrintUnsignedLong(text, left, NULL, value);
  1452         if (len >= left) {
  1453             text += (left > 1) ? left - 1 : 0;
  1454             left = SDL_min(left, 1);
  1455         } else {
  1456             text += len;
  1457             left -= len;
  1458         }
  1459         arg -= value;
  1460         if (info->precision < 0) {
  1461             info->precision = 6;
  1462         }
  1463         if (info->force_type || info->precision > 0) {
  1464             int mult = 10;
  1465             if (left > 1) {
  1466                 *text = '.';
  1467                 --left;
  1468             }
  1469             ++text;
  1470             while (info->precision-- > 0) {
  1471                 value = (unsigned long) (arg * mult);
  1472                 len = SDL_PrintUnsignedLong(text, left, NULL, value);
  1473                 if (len >= left) {
  1474                     text += (left > 1) ? left - 1 : 0;
  1475                     left = SDL_min(left, 1);
  1476                 } else {
  1477                     text += len;
  1478                     left -= len;
  1479                 }
  1480                 arg -= (double) value / mult;
  1481                 mult *= 10;
  1482             }
  1483         }
  1484     } else {
  1485         if (left > 1) {
  1486             *text = '0';
  1487             --left;
  1488         }
  1489         ++text;
  1490         if (info->force_type) {
  1491             if (left > 1) {
  1492                 *text = '.';
  1493                 --left;
  1494             }
  1495             ++text;
  1496         }
  1497     }
  1498 
  1499     width = info->width - (int)(text - textstart);
  1500     if (width > 0) {
  1501         char fill = info->pad_zeroes ? '0' : ' ';
  1502         char *end = text+left-1;
  1503         len = (text - textstart);
  1504         for (len = (text - textstart); len--; ) {
  1505             if ((textstart+len+width) < end) {
  1506                 *(textstart+len+width) = *(textstart+len);
  1507             }
  1508         }
  1509         len = (size_t)width;
  1510         if (len >= left) {
  1511             text += (left > 1) ? left - 1 : 0;
  1512             left = SDL_min(left, 1);
  1513         } else {
  1514             text += len;
  1515             left -= len;
  1516         }
  1517         while (len--) {
  1518             if (textstart+len < end) {
  1519                 textstart[len] = fill;
  1520             }
  1521         }
  1522     }
  1523 
  1524     return (text - textstart);
  1525 }
  1526 
  1527 int
  1528 SDL_vsnprintf(SDL_OUT_Z_CAP(maxlen) char *text, size_t maxlen, const char *fmt, va_list ap)
  1529 {
  1530     size_t left = maxlen;
  1531     char *textstart = text;
  1532 
  1533     if (!fmt) {
  1534         fmt = "";
  1535     }
  1536     while (*fmt && left > 1) {
  1537         if (*fmt == '%') {
  1538             SDL_bool done = SDL_FALSE;
  1539             size_t len = 0;
  1540             SDL_bool check_flag;
  1541             SDL_FormatInfo info;
  1542             enum
  1543             {
  1544                 DO_INT,
  1545                 DO_LONG,
  1546                 DO_LONGLONG
  1547             } inttype = DO_INT;
  1548 
  1549             SDL_zero(info);
  1550             info.radix = 10;
  1551             info.precision = -1;
  1552 
  1553             check_flag = SDL_TRUE;
  1554             while (check_flag) {
  1555                 ++fmt;
  1556                 switch (*fmt) {
  1557                 case '-':
  1558                     info.left_justify = SDL_TRUE;
  1559                     break;
  1560                 case '+':
  1561                     info.force_sign = SDL_TRUE;
  1562                     break;
  1563                 case '#':
  1564                     info.force_type = SDL_TRUE;
  1565                     break;
  1566                 case '0':
  1567                     info.pad_zeroes = SDL_TRUE;
  1568                     break;
  1569                 default:
  1570                     check_flag = SDL_FALSE;
  1571                     break;
  1572                 }
  1573             }
  1574 
  1575             if (*fmt >= '0' && *fmt <= '9') {
  1576                 info.width = SDL_strtol(fmt, (char **)&fmt, 0);
  1577             }
  1578 
  1579             if (*fmt == '.') {
  1580                 ++fmt;
  1581                 if (*fmt >= '0' && *fmt <= '9') {
  1582                     info.precision = SDL_strtol(fmt, (char **)&fmt, 0);
  1583                 } else {
  1584                     info.precision = 0;
  1585                 }
  1586             }
  1587 
  1588             while (!done) {
  1589                 switch (*fmt) {
  1590                 case '%':
  1591                     if (left > 1) {
  1592                         *text = '%';
  1593                     }
  1594                     len = 1;
  1595                     done = SDL_TRUE;
  1596                     break;
  1597                 case 'c':
  1598                     /* char is promoted to int when passed through (...) */
  1599                     if (left > 1) {
  1600                         *text = (char) va_arg(ap, int);
  1601                     }
  1602                     len = 1;
  1603                     done = SDL_TRUE;
  1604                     break;
  1605                 case 'h':
  1606                     /* short is promoted to int when passed through (...) */
  1607                     break;
  1608                 case 'l':
  1609                     if (inttype < DO_LONGLONG) {
  1610                         ++inttype;
  1611                     }
  1612                     break;
  1613                 case 'I':
  1614                     if (SDL_strncmp(fmt, "I64", 3) == 0) {
  1615                         fmt += 2;
  1616                         inttype = DO_LONGLONG;
  1617                     }
  1618                     break;
  1619                 case 'i':
  1620                 case 'd':
  1621                     switch (inttype) {
  1622                     case DO_INT:
  1623                         len = SDL_PrintLong(text, left, &info,
  1624                                             (long) va_arg(ap, int));
  1625                         break;
  1626                     case DO_LONG:
  1627                         len = SDL_PrintLong(text, left, &info,
  1628                                             va_arg(ap, long));
  1629                         break;
  1630                     case DO_LONGLONG:
  1631                         len = SDL_PrintLongLong(text, left, &info,
  1632                                                 va_arg(ap, Sint64));
  1633                         break;
  1634                     }
  1635                     done = SDL_TRUE;
  1636                     break;
  1637                 case 'p':
  1638                 case 'x':
  1639                     info.force_case = SDL_CASE_LOWER;
  1640                     /* Fall through to 'X' handling */
  1641                 case 'X':
  1642                     if (info.force_case == SDL_CASE_NOCHANGE) {
  1643                         info.force_case = SDL_CASE_UPPER;
  1644                     }
  1645                     if (info.radix == 10) {
  1646                         info.radix = 16;
  1647                     }
  1648                     if (*fmt == 'p') {
  1649                         inttype = DO_LONG;
  1650                     }
  1651                     /* Fall through to unsigned handling */
  1652                 case 'o':
  1653                     if (info.radix == 10) {
  1654                         info.radix = 8;
  1655                     }
  1656                     /* Fall through to unsigned handling */
  1657                 case 'u':
  1658                     info.pad_zeroes = SDL_TRUE;
  1659                     switch (inttype) {
  1660                     case DO_INT:
  1661                         len = SDL_PrintUnsignedLong(text, left, &info,
  1662                                                     (unsigned long)
  1663                                                     va_arg(ap, unsigned int));
  1664                         break;
  1665                     case DO_LONG:
  1666                         len = SDL_PrintUnsignedLong(text, left, &info,
  1667                                                     va_arg(ap, unsigned long));
  1668                         break;
  1669                     case DO_LONGLONG:
  1670                         len = SDL_PrintUnsignedLongLong(text, left, &info,
  1671                                                         va_arg(ap, Uint64));
  1672                         break;
  1673                     }
  1674                     done = SDL_TRUE;
  1675                     break;
  1676                 case 'f':
  1677                     len = SDL_PrintFloat(text, left, &info, va_arg(ap, double));
  1678                     done = SDL_TRUE;
  1679                     break;
  1680                 case 'S':
  1681                     {
  1682                         /* In practice this is used on Windows for WCHAR strings */
  1683                         wchar_t *wide_arg = va_arg(ap, wchar_t *);
  1684                         char *arg = SDL_iconv_string("UTF-8", "UTF-16LE", (char *)(wide_arg), (SDL_wcslen(wide_arg)+1)*sizeof(*wide_arg));
  1685                         len = SDL_PrintString(text, left, &info, arg);
  1686                         SDL_free(arg);
  1687                         done = SDL_TRUE;
  1688                     }
  1689                     break;
  1690                 case 's':
  1691                     len = SDL_PrintString(text, left, &info, va_arg(ap, char *));
  1692                     done = SDL_TRUE;
  1693                     break;
  1694                 default:
  1695                     done = SDL_TRUE;
  1696                     break;
  1697                 }
  1698                 ++fmt;
  1699             }
  1700             if (len >= left) {
  1701                 text += (left > 1) ? left - 1 : 0;
  1702                 left = SDL_min(left, 1);
  1703             } else {
  1704                 text += len;
  1705                 left -= len;
  1706             }
  1707         } else {
  1708             *text++ = *fmt++;
  1709             --left;
  1710         }
  1711     }
  1712     if (left > 0) {
  1713         *text = '\0';
  1714     }
  1715     return (int)(text - textstart);
  1716 }
  1717 #endif /* HAVE_VSNPRINTF */
  1718 
  1719 /* vi: set ts=4 sw=4 expandtab: */