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