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