SDL-1.2.14
[sdl_omap.git] / src / stdlib / SDL_string.c
1 /*
2     SDL - Simple DirectMedia Layer
3     Copyright (C) 1997-2009 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)
33 static 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)
73 static 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
104 static 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)
136 static 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)
176 static 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)
208 static 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
243 void *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
276 void *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
288 void *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
302 int 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
318 size_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
329 size_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
342 size_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
354 char *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
366 char *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
382 char *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
394 char *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
406 char *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
419 char *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
433 char *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)
448 static 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
457 char *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
487 char *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
509 long 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
531 unsigned 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
555 char *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
585 char *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
607 Sint64 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
629 Uint64 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
653 double 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
667 int 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
680 int 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)
697 int 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)
714 int 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
732 int 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
999 int 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
1013 static 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 }
1027 static 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
1042 static 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 }
1056 static 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 */
1071 static 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 }
1107 static 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 }
1115 int 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