1/*
2    SDL - Simple DirectMedia Layer
3    Copyright (C) 1997-2012 Sam Lantinga
4
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with this library; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
19    Sam Lantinga
20    slouken@libsdl.org
21*/
22#include "SDL_config.h"
23
24/* This file contains portable string manipulation functions for SDL */
25
26#include "SDL_stdinc.h"
27
28
29#define SDL_isupperhex(X)   (((X) >= 'A') && ((X) <= 'F'))
30#define SDL_islowerhex(X)   (((X) >= 'a') && ((X) <= 'f'))
31
32#if !defined(HAVE_SSCANF) || !defined(HAVE_STRTOL)
33static size_t SDL_ScanLong(const char *text, int radix, long *valuep)
34{
35    const char *textstart = text;
36    long value = 0;
37    SDL_bool negative = SDL_FALSE;
38
39    if ( *text == '-' ) {
40        negative = SDL_TRUE;
41        ++text;
42    }
43    if ( radix == 16 && SDL_strncmp(text, "0x", 2) == 0 ) {
44        text += 2;
45    }
46    for ( ; ; ) {
47        int v;
48        if ( SDL_isdigit((unsigned char) *text) ) {
49            v = *text - '0';
50        } else if ( radix == 16 && SDL_isupperhex(*text) ) {
51            v = 10 + (*text - 'A');
52        } else if ( radix == 16 && SDL_islowerhex(*text) ) {
53            v = 10 + (*text - 'a');
54        } else {
55            break;
56        }
57        value *= radix;
58        value += v;
59        ++text;
60    }
61    if ( valuep ) {
62        if ( negative && value ) {
63            *valuep = -value;
64        } else {
65            *valuep = value;
66        }
67    }
68    return (text - textstart);
69}
70#endif
71
72#if !defined(HAVE_SSCANF) || !defined(HAVE_STRTOUL) || !defined(HAVE_STRTOD)
73static size_t SDL_ScanUnsignedLong(const char *text, int radix, unsigned long *valuep)
74{
75    const char *textstart = text;
76    unsigned long value = 0;
77
78    if ( radix == 16 && SDL_strncmp(text, "0x", 2) == 0 ) {
79        text += 2;
80    }
81    for ( ; ; ) {
82        int v;
83        if ( SDL_isdigit((unsigned char) *text) ) {
84            v = *text - '0';
85        } else if ( radix == 16 && SDL_isupperhex(*text) ) {
86            v = 10 + (*text - 'A');
87        } else if ( radix == 16 && SDL_islowerhex(*text) ) {
88            v = 10 + (*text - 'a');
89        } else {
90            break;
91        }
92        value *= radix;
93        value += v;
94        ++text;
95    }
96    if ( valuep ) {
97        *valuep = value;
98    }
99    return (text - textstart);
100}
101#endif
102
103#ifndef HAVE_SSCANF
104static size_t SDL_ScanUintPtrT(const char *text, int radix, uintptr_t *valuep)
105{
106    const char *textstart = text;
107    uintptr_t value = 0;
108
109    if ( radix == 16 && SDL_strncmp(text, "0x", 2) == 0 ) {
110        text += 2;
111    }
112    for ( ; ; ) {
113        int v;
114        if ( SDL_isdigit((unsigned char) *text) ) {
115            v = *text - '0';
116        } else if ( radix == 16 && SDL_isupperhex(*text) ) {
117            v = 10 + (*text - 'A');
118        } else if ( radix == 16 && SDL_islowerhex(*text) ) {
119            v = 10 + (*text - 'a');
120        } else {
121            break;
122        }
123        value *= radix;
124        value += v;
125        ++text;
126    }
127    if ( valuep ) {
128        *valuep = value;
129    }
130    return (text - textstart);
131}
132#endif
133
134#ifdef SDL_HAS_64BIT_TYPE
135#if !defined(HAVE_SSCANF) || !defined(HAVE_STRTOLL)
136static size_t SDL_ScanLongLong(const char *text, int radix, Sint64 *valuep)
137{
138    const char *textstart = text;
139    Sint64 value = 0;
140    SDL_bool negative = SDL_FALSE;
141
142    if ( *text == '-' ) {
143        negative = SDL_TRUE;
144        ++text;
145    }
146    if ( radix == 16 && SDL_strncmp(text, "0x", 2) == 0 ) {
147        text += 2;
148    }
149    for ( ; ; ) {
150        int v;
151        if ( SDL_isdigit((unsigned char) *text) ) {
152            v = *text - '0';
153        } else if ( radix == 16 && SDL_isupperhex(*text) ) {
154            v = 10 + (*text - 'A');
155        } else if ( radix == 16 && SDL_islowerhex(*text) ) {
156            v = 10 + (*text - 'a');
157        } else {
158            break;
159        }
160        value *= radix;
161        value += v;
162        ++text;
163    }
164    if ( valuep ) {
165        if ( negative && value ) {
166            *valuep = -value;
167        } else {
168            *valuep = value;
169        }
170    }
171    return (text - textstart);
172}
173#endif
174
175#if !defined(HAVE_SSCANF) || !defined(HAVE_STRTOULL)
176static size_t SDL_ScanUnsignedLongLong(const char *text, int radix, Uint64 *valuep)
177{
178    const char *textstart = text;
179    Uint64 value = 0;
180
181    if ( radix == 16 && SDL_strncmp(text, "0x", 2) == 0 ) {
182        text += 2;
183    }
184    for ( ; ; ) {
185        int v;
186        if ( SDL_isdigit((unsigned char) *text) ) {
187            v = *text - '0';
188        } else if ( radix == 16 && SDL_isupperhex(*text) ) {
189            v = 10 + (*text - 'A');
190        } else if ( radix == 16 && SDL_islowerhex(*text) ) {
191            v = 10 + (*text - 'a');
192        } else {
193            break;
194        }
195        value *= radix;
196        value += v;
197        ++text;
198    }
199    if ( valuep ) {
200        *valuep = value;
201    }
202    return (text - textstart);
203}
204#endif
205#endif /* SDL_HAS_64BIT_TYPE */
206
207#if !defined(HAVE_SSCANF) || !defined(HAVE_STRTOD)
208static size_t SDL_ScanFloat(const char *text, double *valuep)
209{
210    const char *textstart = text;
211    unsigned long lvalue = 0;
212    double value = 0.0;
213    SDL_bool negative = SDL_FALSE;
214
215    if ( *text == '-' ) {
216        negative = SDL_TRUE;
217        ++text;
218    }
219    text += SDL_ScanUnsignedLong(text, 10, &lvalue);
220    value += lvalue;
221    if ( *text == '.' ) {
222        int mult = 10;
223        ++text;
224        while ( SDL_isdigit((unsigned char) *text) ) {
225            lvalue = *text - '0';
226            value += (double)lvalue / mult;
227            mult *= 10;
228            ++text;
229        }
230    }
231    if ( valuep ) {
232        if ( negative && value ) {
233            *valuep = -value;
234        } else {
235            *valuep = value;
236        }
237    }
238    return (text - textstart);
239}
240#endif
241
242#ifndef SDL_memset
243void *SDL_memset(void *dst, int c, size_t len)
244{
245    size_t left = (len % 4);
246    if ( len >= 4 ) {
247        Uint32 value = 0;
248        Uint32 *dstp = (Uint32 *)dst;
249        int i;
250        for (i = 0; i < 4; ++i) {
251            value <<= 8;
252            value |= c;
253        }
254        len /= 4;
255        while ( len-- ) {
256            *dstp++ = value;
257        }
258    }
259    if ( left > 0 ) {
260        Uint8 value = (Uint8)c;
261        Uint8 *dstp = (Uint8 *)dst;
262	switch(left) {
263	case 3:
264            *dstp++ = value;
265	case 2:
266            *dstp++ = value;
267	case 1:
268            *dstp++ = value;
269        }
270    }
271    return dst;
272}
273#endif
274
275#ifndef SDL_memcpy
276void *SDL_memcpy(void *dst, const void *src, size_t len)
277{
278    char *srcp = (char *)src;
279    char *dstp = (char *)dst;
280    while ( len-- ) {
281        *dstp++ = *srcp++;
282    }
283    return dst;
284}
285#endif
286
287#ifndef SDL_revcpy
288void *SDL_revcpy(void *dst, const void *src, size_t len)
289{
290    char *srcp = (char *)src;
291    char *dstp = (char *)dst;
292    srcp += len-1;
293    dstp += len-1;
294    while ( len-- ) {
295        *dstp-- = *srcp--;
296    }
297    return dst;
298}
299#endif
300
301#ifndef SDL_memcmp
302int SDL_memcmp(const void *s1, const void *s2, size_t len)
303{
304    char *s1p = (char *)s1;
305    char *s2p = (char *)s2;
306    while ( len-- ) {
307        if ( *s1p != *s2p ) {
308            return (*s1p - *s2p);
309    }
310    ++s1p;
311    ++s2p;
312    }
313    return 0;
314}
315#endif
316
317#ifndef HAVE_STRLEN
318size_t SDL_strlen(const char *string)
319{
320    size_t len = 0;
321    while ( *string++ ) {
322        ++len;
323    }
324    return len;
325}
326#endif
327
328#ifndef HAVE_STRLCPY
329size_t SDL_strlcpy(char *dst, const char *src, size_t maxlen)
330{
331    size_t srclen = SDL_strlen(src);
332    if ( maxlen > 0 ) {
333        size_t len = SDL_min(srclen, maxlen-1);
334        SDL_memcpy(dst, src, len);
335        dst[len] = '\0';
336    }
337    return srclen;
338}
339#endif
340
341#ifndef HAVE_STRLCAT
342size_t SDL_strlcat(char *dst, const char *src, size_t maxlen)
343{
344    size_t dstlen = SDL_strlen(dst);
345    size_t srclen = SDL_strlen(src);
346    if ( dstlen < maxlen ) {
347        SDL_strlcpy(dst+dstlen, src, maxlen-dstlen);
348    }
349    return dstlen+srclen;
350}
351#endif
352
353#ifndef HAVE_STRDUP
354char *SDL_strdup(const char *string)
355{
356    size_t len = SDL_strlen(string)+1;
357    char *newstr = SDL_malloc(len);
358    if ( newstr ) {
359        SDL_strlcpy(newstr, string, len);
360    }
361    return newstr;
362}
363#endif
364
365#ifndef HAVE__STRREV
366char *SDL_strrev(char *string)
367{
368    size_t len = SDL_strlen(string);
369    char *a = &string[0];
370    char *b = &string[len-1];
371    len /= 2;
372    while ( len-- ) {
373        char c = *a;
374        *a++ = *b;
375        *b-- = c;
376    }
377    return string;
378}
379#endif
380
381#ifndef HAVE__STRUPR
382char *SDL_strupr(char *string)
383{
384    char *bufp = string;
385    while ( *bufp ) {
386        *bufp = SDL_toupper((unsigned char) *bufp);
387	++bufp;
388    }
389    return string;
390}
391#endif
392
393#ifndef HAVE__STRLWR
394char *SDL_strlwr(char *string)
395{
396    char *bufp = string;
397    while ( *bufp ) {
398        *bufp = SDL_tolower((unsigned char) *bufp);
399	++bufp;
400    }
401    return string;
402}
403#endif
404
405#ifndef HAVE_STRCHR
406char *SDL_strchr(const char *string, int c)
407{
408    while ( *string ) {
409        if ( *string == c ) {
410            return (char *)string;
411        }
412	++string;
413    }
414    return NULL;
415}
416#endif
417
418#ifndef HAVE_STRRCHR
419char *SDL_strrchr(const char *string, int c)
420{
421    const char *bufp = string + SDL_strlen(string) - 1;
422    while ( bufp >= string ) {
423        if ( *bufp == c ) {
424            return (char *)bufp;
425        }
426	--bufp;
427    }
428    return NULL;
429}
430#endif
431
432#ifndef HAVE_STRSTR
433char *SDL_strstr(const char *haystack, const char *needle)
434{
435    size_t length = SDL_strlen(needle);
436    while ( *haystack ) {
437        if ( SDL_strncmp(haystack, needle, length) == 0 ) {
438            return (char *)haystack;
439        }
440	++haystack;
441    }
442    return NULL;
443}
444#endif
445
446#if !defined(HAVE__LTOA)  || !defined(HAVE__I64TOA) || \
447    !defined(HAVE__ULTOA) || !defined(HAVE__UI64TOA)
448static const char ntoa_table[] = {
449    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
450    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
451    'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
452    'U', 'V', 'W', 'X', 'Y', 'Z'
453};
454#endif /* ntoa() conversion table */
455
456#ifndef HAVE__LTOA
457char *SDL_ltoa(long value, char *string, int radix)
458{
459    char *bufp = string;
460
461    if ( value < 0 ) {
462        *bufp++ = '-';
463        value = -value;
464    }
465    if ( value ) {
466        while ( value > 0 ) {
467            *bufp++ = ntoa_table[value % radix];
468            value /= radix;
469        }
470    } else {
471        *bufp++ = '0';
472    }
473    *bufp = '\0';
474
475    /* The numbers went into the string backwards. :) */
476    if ( *string == '-' ) {
477        SDL_strrev(string+1);
478    } else {
479        SDL_strrev(string);
480    }
481
482    return string;
483}
484#endif
485
486#ifndef HAVE__ULTOA
487char *SDL_ultoa(unsigned long value, char *string, int radix)
488{
489    char *bufp = string;
490
491    if ( value ) {
492        while ( value > 0 ) {
493            *bufp++ = ntoa_table[value % radix];
494            value /= radix;
495        }
496    } else {
497        *bufp++ = '0';
498    }
499    *bufp = '\0';
500
501    /* The numbers went into the string backwards. :) */
502    SDL_strrev(string);
503
504    return string;
505}
506#endif
507
508#ifndef HAVE_STRTOL
509long SDL_strtol(const char *string, char **endp, int base)
510{
511    size_t len;
512    long value;
513
514    if ( !base ) {
515        if ( (SDL_strlen(string) > 2) && (SDL_strncmp(string, "0x", 2) == 0) ) {
516            base = 16;
517        } else {
518            base = 10;
519        }
520    }
521
522    len = SDL_ScanLong(string, base, &value);
523    if ( endp ) {
524        *endp = (char *)string + len;
525    }
526    return value;
527}
528#endif
529
530#ifndef HAVE_STRTOUL
531unsigned long SDL_strtoul(const char *string, char **endp, int base)
532{
533    size_t len;
534    unsigned long value;
535
536    if ( !base ) {
537        if ( (SDL_strlen(string) > 2) && (SDL_strncmp(string, "0x", 2) == 0) ) {
538            base = 16;
539        } else {
540            base = 10;
541        }
542    }
543
544    len = SDL_ScanUnsignedLong(string, base, &value);
545    if ( endp ) {
546        *endp = (char *)string + len;
547    }
548    return value;
549}
550#endif
551
552#ifdef SDL_HAS_64BIT_TYPE
553
554#ifndef HAVE__I64TOA
555char *SDL_lltoa(Sint64 value, char *string, int radix)
556{
557    char *bufp = string;
558
559    if ( value < 0 ) {
560        *bufp++ = '-';
561        value = -value;
562    }
563    if ( value ) {
564        while ( value > 0 ) {
565            *bufp++ = ntoa_table[value % radix];
566            value /= radix;
567        }
568    } else {
569        *bufp++ = '0';
570    }
571    *bufp = '\0';
572
573    /* The numbers went into the string backwards. :) */
574    if ( *string == '-' ) {
575        SDL_strrev(string+1);
576    } else {
577        SDL_strrev(string);
578    }
579
580    return string;
581}
582#endif
583
584#ifndef HAVE__UI64TOA
585char *SDL_ulltoa(Uint64 value, char *string, int radix)
586{
587    char *bufp = string;
588
589    if ( value ) {
590        while ( value > 0 ) {
591            *bufp++ = ntoa_table[value % radix];
592            value /= radix;
593        }
594    } else {
595        *bufp++ = '0';
596    }
597    *bufp = '\0';
598
599    /* The numbers went into the string backwards. :) */
600    SDL_strrev(string);
601
602    return string;
603}
604#endif
605
606#ifndef HAVE_STRTOLL
607Sint64 SDL_strtoll(const char *string, char **endp, int base)
608{
609    size_t len;
610    Sint64 value;
611
612    if ( !base ) {
613        if ( (SDL_strlen(string) > 2) && (SDL_strncmp(string, "0x", 2) == 0) ) {
614            base = 16;
615        } else {
616            base = 10;
617        }
618    }
619
620    len = SDL_ScanLongLong(string, base, &value);
621    if ( endp ) {
622        *endp = (char *)string + len;
623    }
624    return value;
625}
626#endif
627
628#ifndef HAVE_STRTOULL
629Uint64 SDL_strtoull(const char *string, char **endp, int base)
630{
631    size_t len;
632    Uint64 value;
633
634    if ( !base ) {
635        if ( (SDL_strlen(string) > 2) && (SDL_strncmp(string, "0x", 2) == 0) ) {
636            base = 16;
637        } else {
638            base = 10;
639        }
640    }
641
642    len = SDL_ScanUnsignedLongLong(string, base, &value);
643    if ( endp ) {
644        *endp = (char *)string + len;
645    }
646    return value;
647}
648#endif
649
650#endif /* SDL_HAS_64BIT_TYPE */
651
652#ifndef HAVE_STRTOD
653double SDL_strtod(const char *string, char **endp)
654{
655    size_t len;
656    double value;
657
658    len = SDL_ScanFloat(string, &value);
659    if ( endp ) {
660        *endp = (char *)string + len;
661    }
662    return value;
663}
664#endif
665
666#ifndef HAVE_STRCMP
667int SDL_strcmp(const char *str1, const char *str2)
668{
669    while (*str1 && *str2) {
670        if ( *str1 != *str2 )
671            break;
672        ++str1;
673        ++str2;
674    }
675    return (int)((unsigned char)*str1 - (unsigned char)*str2);
676}
677#endif
678
679#ifndef HAVE_STRNCMP
680int SDL_strncmp(const char *str1, const char *str2, size_t maxlen)
681{
682    while ( *str1 && *str2 && maxlen ) {
683        if ( *str1 != *str2 )
684            break;
685        ++str1;
686        ++str2;
687        --maxlen;
688    }
689    if ( ! maxlen ) {
690        return 0;
691    }
692    return (int)((unsigned char)*str1 - (unsigned char)*str2);
693}
694#endif
695
696#if !defined(HAVE_STRCASECMP) && !defined(HAVE__STRICMP)
697int SDL_strcasecmp(const char *str1, const char *str2)
698{
699    char a = 0;
700    char b = 0;
701    while ( *str1 && *str2 ) {
702        a = SDL_tolower((unsigned char) *str1);
703        b = SDL_tolower((unsigned char) *str2);
704        if ( a != b )
705            break;
706        ++str1;
707        ++str2;
708    }
709    return (int)((unsigned char)a - (unsigned char)b);
710}
711#endif
712
713#if !defined(HAVE_STRNCASECMP) && !defined(HAVE__STRNICMP)
714int SDL_strncasecmp(const char *str1, const char *str2, size_t maxlen)
715{
716    char a = 0;
717    char b = 0;
718    while ( *str1 && *str2 && maxlen ) {
719        a = SDL_tolower((unsigned char) *str1);
720        b = SDL_tolower((unsigned char) *str2);
721        if ( a != b )
722            break;
723        ++str1;
724        ++str2;
725        --maxlen;
726    }
727    return (int)((unsigned char)a - (unsigned char)b);
728}
729#endif
730
731#ifndef HAVE_SSCANF
732int SDL_sscanf(const char *text, const char *fmt, ...)
733{
734    va_list ap;
735    int retval = 0;
736
737    va_start(ap, fmt);
738    while ( *fmt ) {
739        if ( *fmt == ' ' ) {
740            while ( SDL_isspace((unsigned char) *text) ) {
741                ++text;
742            }
743            ++fmt;
744            continue;
745        }
746        if ( *fmt == '%' ) {
747            SDL_bool done = SDL_FALSE;
748            long count = 0;
749            int radix = 10;
750            enum {
751                DO_SHORT,
752                DO_INT,
753                DO_LONG,
754                DO_LONGLONG
755            } inttype = DO_INT;
756            SDL_bool suppress = SDL_FALSE;
757
758            ++fmt;
759            if ( *fmt == '%' ) {
760                if ( *text == '%' ) {
761                    ++text;
762                    ++fmt;
763                    continue;
764                }
765                break;
766            }
767            if ( *fmt == '*' ) {
768                suppress = SDL_TRUE;
769                ++fmt;
770            }
771            fmt += SDL_ScanLong(fmt, 10, &count);
772
773            if ( *fmt == 'c' ) {
774                if ( ! count ) {
775                    count = 1;
776                }
777                if ( suppress ) {
778                    while ( count-- ) {
779                        ++text;
780                    }
781                } else {
782                    char *valuep = va_arg(ap, char*);
783                    while ( count-- ) {
784                        *valuep++ = *text++;
785                    }
786                    ++retval;
787                }
788                continue;
789            }
790
791            while ( SDL_isspace((unsigned char) *text) ) {
792                ++text;
793            }
794
795            /* FIXME: implement more of the format specifiers */
796            while (!done) {
797                switch(*fmt) {
798                    case '*':
799                        suppress = SDL_TRUE;
800                        break;
801                    case 'h':
802                        if ( inttype > DO_SHORT ) {
803                            ++inttype;
804                        }
805                        break;
806                    case 'l':
807                        if ( inttype < DO_LONGLONG ) {
808                            ++inttype;
809                        }
810                        break;
811                    case 'I':
812                        if ( SDL_strncmp(fmt, "I64", 3) == 0 ) {
813                            fmt += 2;
814                            inttype = DO_LONGLONG;
815                        }
816                        break;
817                    case 'i':
818                        {
819                            int index = 0;
820                            if ( text[index] == '-' ) {
821                                ++index;
822                            }
823                            if ( text[index] == '0' ) {
824                                if ( SDL_tolower((unsigned char) text[index+1]) == 'x' ) {
825                                    radix = 16;
826                                } else {
827                                    radix = 8;
828                                }
829                            }
830                        }
831                        /* Fall through to %d handling */
832                    case 'd':
833#ifdef SDL_HAS_64BIT_TYPE
834                        if ( inttype == DO_LONGLONG ) {
835                            Sint64 value;
836                            text += SDL_ScanLongLong(text, radix, &value);
837                            if ( ! suppress ) {
838                                Sint64 *valuep = va_arg(ap, Sint64*);
839                                *valuep = value;
840                                ++retval;
841                            }
842                        }
843                        else
844#endif /* SDL_HAS_64BIT_TYPE */
845                        {
846                            long value;
847                            text += SDL_ScanLong(text, radix, &value);
848                            if ( ! suppress ) {
849                                switch (inttype) {
850                                    case DO_SHORT:
851                                        { short* valuep = va_arg(ap, short*);
852                                            *valuep = (short)value;
853                                        }
854                                        break;
855                                    case DO_INT:
856                                        { int* valuep = va_arg(ap, int*);
857                                            *valuep = (int)value;
858                                        }
859                                        break;
860                                    case DO_LONG:
861                                        { long* valuep = va_arg(ap, long*);
862                                            *valuep = value;
863                                        }
864                                        break;
865                                    case DO_LONGLONG:
866                                        /* Handled above */
867                                        break;
868                                }
869                                ++retval;
870                            }
871                        }
872                        done = SDL_TRUE;
873                        break;
874                    case 'o':
875                        if ( radix == 10 ) {
876                            radix = 8;
877                        }
878                        /* Fall through to unsigned handling */
879                    case 'x':
880                    case 'X':
881                        if ( radix == 10 ) {
882                            radix = 16;
883                        }
884                        /* Fall through to unsigned handling */
885                    case 'u':
886#ifdef SDL_HAS_64BIT_TYPE
887                        if ( inttype == DO_LONGLONG ) {
888                            Uint64 value;
889                            text += SDL_ScanUnsignedLongLong(text, radix, &value);
890                            if ( ! suppress ) {
891                                Uint64 *valuep = va_arg(ap, Uint64*);
892                                *valuep = value;
893                                ++retval;
894                            }
895                        }
896                        else
897#endif /* SDL_HAS_64BIT_TYPE */
898                        {
899                            unsigned long value;
900                            text += SDL_ScanUnsignedLong(text, radix, &value);
901                            if ( ! suppress ) {
902                                switch (inttype) {
903                                    case DO_SHORT:
904                                        { short* valuep = va_arg(ap, short*);
905                                            *valuep = (short)value;
906                                        }
907                                        break;
908                                    case DO_INT:
909                                        { int* valuep = va_arg(ap, int*);
910                                            *valuep = (int)value;
911                                        }
912                                        break;
913                                    case DO_LONG:
914                                        { long* valuep = va_arg(ap, long*);
915                                            *valuep = value;
916                                        }
917                                        break;
918                                    case DO_LONGLONG:
919                                        /* Handled above */
920                                        break;
921                                }
922                                ++retval;
923                            }
924                        }
925                        done = SDL_TRUE;
926                        break;
927                    case 'p':
928                        {
929                            uintptr_t value;
930                            text += SDL_ScanUintPtrT(text, 16, &value);
931                            if ( ! suppress ) {
932                                void** valuep = va_arg(ap, void**);
933                                *valuep = (void*)value;
934                                ++retval;
935                            }
936                        }
937                        done = SDL_TRUE;
938                        break;
939                    case 'f':
940                        {
941                            double value;
942                            text += SDL_ScanFloat(text, &value);
943                            if ( ! suppress ) {
944                                float* valuep = va_arg(ap, float*);
945                                *valuep = (float)value;
946                                ++retval;
947                            }
948                        }
949                        done = SDL_TRUE;
950                        break;
951                    case 's':
952                        if ( suppress ) {
953                            while ( !SDL_isspace((unsigned char) *text) ) {
954                                ++text;
955                                if ( count ) {
956                                    if ( --count == 0 ) {
957                                        break;
958                                    }
959                                }
960                            }
961                        } else {
962                            char *valuep = va_arg(ap, char*);
963                            while ( !SDL_isspace((unsigned char) *text) ) {
964                                *valuep++ = *text++;
965                                if ( count ) {
966                                    if ( --count == 0 ) {
967                                        break;
968                                    }
969                                }
970                            }
971                            *valuep = '\0';
972                            ++retval;
973                        }
974                        done = SDL_TRUE;
975                        break;
976                    default:
977                        done = SDL_TRUE;
978                        break;
979                }
980                ++fmt;
981            }
982            continue;
983        }
984        if ( *text == *fmt ) {
985            ++text;
986            ++fmt;
987            continue;
988        }
989        /* Text didn't match format specifier */
990        break;
991    }
992    va_end(ap);
993
994    return retval;
995}
996#endif
997
998#ifndef HAVE_SNPRINTF
999int SDL_snprintf(char *text, size_t maxlen, const char *fmt, ...)
1000{
1001    va_list ap;
1002    int retval;
1003
1004    va_start(ap, fmt);
1005    retval = SDL_vsnprintf(text, maxlen, fmt, ap);
1006    va_end(ap);
1007
1008    return retval;
1009}
1010#endif
1011
1012#ifndef HAVE_VSNPRINTF
1013static size_t SDL_PrintLong(char *text, long value, int radix, size_t maxlen)
1014{
1015    char num[130];
1016    size_t size;
1017
1018    SDL_ltoa(value, num, radix);
1019    size = SDL_strlen(num);
1020    if ( size >= maxlen ) {
1021        size = maxlen-1;
1022    }
1023    SDL_strlcpy(text, num, size+1);
1024
1025    return size;
1026}
1027static size_t SDL_PrintUnsignedLong(char *text, unsigned long value, int radix, size_t maxlen)
1028{
1029    char num[130];
1030    size_t size;
1031
1032    SDL_ultoa(value, num, radix);
1033    size = SDL_strlen(num);
1034    if ( size >= maxlen ) {
1035        size = maxlen-1;
1036    }
1037    SDL_strlcpy(text, num, size+1);
1038
1039    return size;
1040}
1041#ifdef SDL_HAS_64BIT_TYPE
1042static size_t SDL_PrintLongLong(char *text, Sint64 value, int radix, size_t maxlen)
1043{
1044    char num[130];
1045    size_t size;
1046
1047    SDL_lltoa(value, num, radix);
1048    size = SDL_strlen(num);
1049    if ( size >= maxlen ) {
1050        size = maxlen-1;
1051    }
1052    SDL_strlcpy(text, num, size+1);
1053
1054    return size;
1055}
1056static size_t SDL_PrintUnsignedLongLong(char *text, Uint64 value, int radix, size_t maxlen)
1057{
1058    char num[130];
1059    size_t size;
1060
1061    SDL_ulltoa(value, num, radix);
1062    size = SDL_strlen(num);
1063    if ( size >= maxlen ) {
1064        size = maxlen-1;
1065    }
1066    SDL_strlcpy(text, num, size+1);
1067
1068    return size;
1069}
1070#endif /* SDL_HAS_64BIT_TYPE */
1071static size_t SDL_PrintFloat(char *text, double arg, size_t maxlen)
1072{
1073    char *textstart = text;
1074    if ( arg ) {
1075        /* This isn't especially accurate, but hey, it's easy. :) */
1076        const double precision = 0.00000001;
1077        size_t len;
1078        unsigned long value;
1079
1080        if ( arg < 0 ) {
1081            *text++ = '-';
1082            --maxlen;
1083            arg = -arg;
1084        }
1085        value = (unsigned long)arg;
1086        len = SDL_PrintUnsignedLong(text, value, 10, maxlen);
1087        text += len;
1088        maxlen -= len;
1089        arg -= value;
1090        if ( arg > precision && maxlen ) {
1091            int mult = 10;
1092            *text++ = '.';
1093            while ( (arg > precision) && maxlen ) {
1094                value = (unsigned long)(arg * mult);
1095                len = SDL_PrintUnsignedLong(text, value, 10, maxlen);
1096                text += len;
1097                maxlen -= len;
1098                arg -= (double)value / mult;
1099                mult *= 10;
1100            }
1101        }
1102    } else {
1103        *text++ = '0';
1104    }
1105    return (text - textstart);
1106}
1107static size_t SDL_PrintString(char *text, const char *string, size_t maxlen)
1108{
1109    char *textstart = text;
1110    while ( *string && maxlen-- ) {
1111        *text++ = *string++;
1112    }
1113    return (text - textstart);
1114}
1115int SDL_vsnprintf(char *text, size_t maxlen, const char *fmt, va_list ap)
1116{
1117    char *textstart = text;
1118    if ( maxlen <= 0 ) {
1119        return 0;
1120    }
1121    --maxlen; /* For the trailing '\0' */
1122    while ( *fmt && maxlen ) {
1123        if ( *fmt == '%' ) {
1124            SDL_bool done = SDL_FALSE;
1125            size_t len = 0;
1126            SDL_bool do_lowercase = SDL_FALSE;
1127            int radix = 10;
1128            enum {
1129                DO_INT,
1130                DO_LONG,
1131                DO_LONGLONG
1132            } inttype = DO_INT;
1133
1134            ++fmt;
1135            /* FIXME: implement more of the format specifiers */
1136            while ( *fmt == '.' || (*fmt >= '0' && *fmt <= '9') ) {
1137                ++fmt;
1138            }
1139            while (!done) {
1140                switch(*fmt) {
1141                    case '%':
1142                        *text = '%';
1143                        len = 1;
1144                        done = SDL_TRUE;
1145                        break;
1146                    case 'c':
1147                        /* char is promoted to int when passed through (...) */
1148                        *text = (char)va_arg(ap, int);
1149                        len = 1;
1150                        done = SDL_TRUE;
1151                        break;
1152                    case 'h':
1153                        /* short is promoted to int when passed through (...) */
1154                        break;
1155                    case 'l':
1156                        if ( inttype < DO_LONGLONG ) {
1157                            ++inttype;
1158                        }
1159                        break;
1160                    case 'I':
1161                        if ( SDL_strncmp(fmt, "I64", 3) == 0 ) {
1162                            fmt += 2;
1163                            inttype = DO_LONGLONG;
1164                        }
1165                        break;
1166                    case 'i':
1167                    case 'd':
1168                        switch (inttype) {
1169                            case DO_INT:
1170                                len = SDL_PrintLong(text, (long)va_arg(ap, int), radix, maxlen);
1171                                break;
1172                            case DO_LONG:
1173                                len = SDL_PrintLong(text, va_arg(ap, long), radix, maxlen);
1174                                break;
1175                            case DO_LONGLONG:
1176#ifdef SDL_HAS_64BIT_TYPE
1177                                len = SDL_PrintLongLong(text, va_arg(ap, Sint64), radix, maxlen);
1178#else
1179                                len = SDL_PrintLong(text, va_arg(ap, long), radix, maxlen);
1180#endif
1181                                break;
1182                        }
1183                        done = SDL_TRUE;
1184                        break;
1185                    case 'p':
1186                    case 'x':
1187                        do_lowercase = SDL_TRUE;
1188                        /* Fall through to 'X' handling */
1189                    case 'X':
1190                        if ( radix == 10 ) {
1191                            radix = 16;
1192                        }
1193                        if ( *fmt == 'p' ) {
1194                            inttype = DO_LONG;
1195                        }
1196                        /* Fall through to unsigned handling */
1197                    case 'o':
1198                        if ( radix == 10 ) {
1199                            radix = 8;
1200                        }
1201                        /* Fall through to unsigned handling */
1202                    case 'u':
1203                        switch (inttype) {
1204                            case DO_INT:
1205                                len = SDL_PrintUnsignedLong(text, (unsigned long)va_arg(ap, unsigned int), radix, maxlen);
1206                                break;
1207                            case DO_LONG:
1208                                len = SDL_PrintUnsignedLong(text, va_arg(ap, unsigned long), radix, maxlen);
1209                                break;
1210                            case DO_LONGLONG:
1211#ifdef SDL_HAS_64BIT_TYPE
1212                                len = SDL_PrintUnsignedLongLong(text, va_arg(ap, Uint64), radix, maxlen);
1213#else
1214                                len = SDL_PrintUnsignedLong(text, va_arg(ap, unsigned long), radix, maxlen);
1215#endif
1216                                break;
1217                        }
1218                        if ( do_lowercase ) {
1219                            SDL_strlwr(text);
1220                        }
1221                        done = SDL_TRUE;
1222                        break;
1223                    case 'f':
1224                        len = SDL_PrintFloat(text, va_arg(ap, double), maxlen);
1225                        done = SDL_TRUE;
1226                        break;
1227                    case 's':
1228                        len = SDL_PrintString(text, va_arg(ap, char*), maxlen);
1229                        done = SDL_TRUE;
1230                        break;
1231                    default:
1232                        done = SDL_TRUE;
1233                        break;
1234                }
1235                ++fmt;
1236            }
1237            text += len;
1238            maxlen -= len;
1239        } else {
1240            *text++ = *fmt++;
1241            --maxlen;
1242        }
1243    }
1244    *text = '\0';
1245
1246    return (text - textstart);
1247}
1248#endif
1249