src/stdlib/SDL_string.c
author Ozkan Sezer <sezeroz@gmail.com>
Sat, 29 Sep 2018 01:24:10 +0300
changeset 12254 f1ac9de30ee1
parent 12253 2dbf011db466
child 12256 9f3949b8ec40
permissions -rw-r--r--
SDL_vsnprintf: implemented '+' flag for signed integers printing.

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