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