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 | |
39 | static int UTF8_TrailingBytes(unsigned char c) |
40 | { |
41 | if (c >= 0xC0 && c <= 0xDF) |
42 | return 1; |
43 | else if (c >= 0xE0 && c <= 0xEF) |
44 | return 2; |
45 | else if (c >= 0xF0 && c <= 0xF4) |
46 | return 3; |
47 | else |
48 | return 0; |
49 | } |
50 | |
51 | #if !defined(HAVE_VSSCANF) || !defined(HAVE_STRTOL) |
52 | static size_t |
53 | SDL_ScanLong(const char *text, int radix, long *valuep) |
54 | { |
55 | const char *textstart = text; |
56 | long value = 0; |
57 | SDL_bool negative = SDL_FALSE; |
58 | |
59 | if (*text == '-') { |
60 | negative = SDL_TRUE; |
61 | ++text; |
62 | } |
63 | if (radix == 16 && SDL_strncmp(text, "0x" , 2) == 0) { |
64 | text += 2; |
65 | } |
66 | for (;;) { |
67 | int v; |
68 | if (SDL_isdigit((unsigned char) *text)) { |
69 | v = *text - '0'; |
70 | } else if (radix == 16 && SDL_isupperhex(*text)) { |
71 | v = 10 + (*text - 'A'); |
72 | } else if (radix == 16 && SDL_islowerhex(*text)) { |
73 | v = 10 + (*text - 'a'); |
74 | } else { |
75 | break; |
76 | } |
77 | value *= radix; |
78 | value += v; |
79 | ++text; |
80 | } |
81 | if (valuep && text > textstart) { |
82 | if (negative && value) { |
83 | *valuep = -value; |
84 | } else { |
85 | *valuep = value; |
86 | } |
87 | } |
88 | return (text - textstart); |
89 | } |
90 | #endif |
91 | |
92 | #if !defined(HAVE_VSSCANF) || !defined(HAVE_STRTOUL) || !defined(HAVE_STRTOD) |
93 | static size_t |
94 | SDL_ScanUnsignedLong(const char *text, int radix, unsigned long *valuep) |
95 | { |
96 | const char *textstart = text; |
97 | unsigned long value = 0; |
98 | |
99 | if (radix == 16 && SDL_strncmp(text, "0x" , 2) == 0) { |
100 | text += 2; |
101 | } |
102 | for (;;) { |
103 | int v; |
104 | if (SDL_isdigit((unsigned char) *text)) { |
105 | v = *text - '0'; |
106 | } else if (radix == 16 && SDL_isupperhex(*text)) { |
107 | v = 10 + (*text - 'A'); |
108 | } else if (radix == 16 && SDL_islowerhex(*text)) { |
109 | v = 10 + (*text - 'a'); |
110 | } else { |
111 | break; |
112 | } |
113 | value *= radix; |
114 | value += v; |
115 | ++text; |
116 | } |
117 | if (valuep && text > textstart) { |
118 | *valuep = value; |
119 | } |
120 | return (text - textstart); |
121 | } |
122 | #endif |
123 | |
124 | #ifndef HAVE_VSSCANF |
125 | static size_t |
126 | SDL_ScanUintPtrT(const char *text, int radix, uintptr_t * valuep) |
127 | { |
128 | const char *textstart = text; |
129 | uintptr_t value = 0; |
130 | |
131 | if (radix == 16 && SDL_strncmp(text, "0x" , 2) == 0) { |
132 | text += 2; |
133 | } |
134 | for (;;) { |
135 | int v; |
136 | if (SDL_isdigit((unsigned char) *text)) { |
137 | v = *text - '0'; |
138 | } else if (radix == 16 && SDL_isupperhex(*text)) { |
139 | v = 10 + (*text - 'A'); |
140 | } else if (radix == 16 && SDL_islowerhex(*text)) { |
141 | v = 10 + (*text - 'a'); |
142 | } else { |
143 | break; |
144 | } |
145 | value *= radix; |
146 | value += v; |
147 | ++text; |
148 | } |
149 | if (valuep && text > textstart) { |
150 | *valuep = value; |
151 | } |
152 | return (text - textstart); |
153 | } |
154 | #endif |
155 | |
156 | #if !defined(HAVE_VSSCANF) || !defined(HAVE_STRTOLL) |
157 | static size_t |
158 | SDL_ScanLongLong(const char *text, int radix, Sint64 * valuep) |
159 | { |
160 | const char *textstart = text; |
161 | Sint64 value = 0; |
162 | SDL_bool negative = SDL_FALSE; |
163 | |
164 | if (*text == '-') { |
165 | negative = SDL_TRUE; |
166 | ++text; |
167 | } |
168 | if (radix == 16 && SDL_strncmp(text, "0x" , 2) == 0) { |
169 | text += 2; |
170 | } |
171 | for (;;) { |
172 | int v; |
173 | if (SDL_isdigit((unsigned char) *text)) { |
174 | v = *text - '0'; |
175 | } else if (radix == 16 && SDL_isupperhex(*text)) { |
176 | v = 10 + (*text - 'A'); |
177 | } else if (radix == 16 && SDL_islowerhex(*text)) { |
178 | v = 10 + (*text - 'a'); |
179 | } else { |
180 | break; |
181 | } |
182 | value *= radix; |
183 | value += v; |
184 | ++text; |
185 | } |
186 | if (valuep && text > textstart) { |
187 | if (negative && value) { |
188 | *valuep = -value; |
189 | } else { |
190 | *valuep = value; |
191 | } |
192 | } |
193 | return (text - textstart); |
194 | } |
195 | #endif |
196 | |
197 | #if !defined(HAVE_VSSCANF) || !defined(HAVE_STRTOULL) |
198 | static size_t |
199 | SDL_ScanUnsignedLongLong(const char *text, int radix, Uint64 * valuep) |
200 | { |
201 | const char *textstart = text; |
202 | Uint64 value = 0; |
203 | |
204 | if (radix == 16 && SDL_strncmp(text, "0x" , 2) == 0) { |
205 | text += 2; |
206 | } |
207 | for (;;) { |
208 | int v; |
209 | if (SDL_isdigit((unsigned char) *text)) { |
210 | v = *text - '0'; |
211 | } else if (radix == 16 && SDL_isupperhex(*text)) { |
212 | v = 10 + (*text - 'A'); |
213 | } else if (radix == 16 && SDL_islowerhex(*text)) { |
214 | v = 10 + (*text - 'a'); |
215 | } else { |
216 | break; |
217 | } |
218 | value *= radix; |
219 | value += v; |
220 | ++text; |
221 | } |
222 | if (valuep && text > textstart) { |
223 | *valuep = value; |
224 | } |
225 | return (text - textstart); |
226 | } |
227 | #endif |
228 | |
229 | #if !defined(HAVE_VSSCANF) || !defined(HAVE_STRTOD) |
230 | static size_t |
231 | SDL_ScanFloat(const char *text, double *valuep) |
232 | { |
233 | const char *textstart = text; |
234 | unsigned long lvalue = 0; |
235 | double value = 0.0; |
236 | SDL_bool negative = SDL_FALSE; |
237 | |
238 | if (*text == '-') { |
239 | negative = SDL_TRUE; |
240 | ++text; |
241 | } |
242 | text += SDL_ScanUnsignedLong(text, 10, &lvalue); |
243 | value += lvalue; |
244 | if (*text == '.') { |
245 | int mult = 10; |
246 | ++text; |
247 | while (SDL_isdigit((unsigned char) *text)) { |
248 | lvalue = *text - '0'; |
249 | value += (double) lvalue / mult; |
250 | mult *= 10; |
251 | ++text; |
252 | } |
253 | } |
254 | if (valuep && text > textstart) { |
255 | if (negative && value) { |
256 | *valuep = -value; |
257 | } else { |
258 | *valuep = value; |
259 | } |
260 | } |
261 | return (text - textstart); |
262 | } |
263 | #endif |
264 | |
265 | void * |
266 | SDL_memset(SDL_OUT_BYTECAP(len) void *dst, int c, size_t len) |
267 | { |
268 | #if defined(HAVE_MEMSET) |
269 | return memset(dst, c, len); |
270 | #else |
271 | size_t left; |
272 | Uint32 *dstp4; |
273 | Uint8 *dstp1 = (Uint8 *) dst; |
274 | 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 | |
314 | void * |
315 | SDL_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 | |
367 | void * |
368 | SDL_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 | |
391 | int |
392 | SDL_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 | |
410 | size_t |
411 | SDL_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 | |
424 | size_t |
425 | SDL_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 | |
438 | size_t |
439 | SDL_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 | |
454 | size_t |
455 | SDL_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 | |
469 | wchar_t * |
470 | SDL_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 | |
480 | wchar_t * |
481 | SDL_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 | |
497 | int |
498 | SDL_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 | |
513 | int |
514 | SDL_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 | |
534 | int |
535 | SDL_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 | |
571 | int |
572 | SDL_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 | |
613 | size_t |
614 | SDL_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 | |
629 | size_t |
630 | SDL_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 | |
662 | size_t |
663 | SDL_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 | |
679 | size_t |
680 | SDL_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 | |
694 | char * |
695 | SDL_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 | |
705 | char * |
706 | SDL_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 | |
724 | char * |
725 | SDL_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 | |
739 | char * |
740 | SDL_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 | |
754 | char * |
755 | SDL_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 | |
772 | char * |
773 | SDL_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 | |
791 | char * |
792 | SDL_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) |
810 | static 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 | |
818 | char * |
819 | SDL_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 | |
828 | char * |
829 | SDL_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 | |
838 | char * |
839 | SDL_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 | |
857 | char * |
858 | SDL_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 | |
882 | char * |
883 | SDL_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 | |
901 | char * |
902 | SDL_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 | |
926 | int 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 | |
935 | double 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 | |
944 | long |
945 | SDL_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 | |
969 | unsigned long |
970 | SDL_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 | |
994 | Sint64 |
995 | SDL_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 | |
1019 | Uint64 |
1020 | SDL_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 | |
1044 | double |
1045 | SDL_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 | |
1061 | int |
1062 | SDL_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 | |
1077 | int |
1078 | SDL_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 | |
1097 | int |
1098 | SDL_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 | |
1121 | int |
1122 | SDL_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 | |
1150 | int |
1151 | SDL_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 |
1162 | int |
1163 | SDL_vsscanf(const char *text, const char *fmt, va_list ap) |
1164 | { |
1165 | return vsscanf(text, fmt, ap); |
1166 | } |
1167 | #else |
1168 | int |
1169 | SDL_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 | |
1442 | int |
1443 | SDL_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 */ |
1457 | int 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) |
1467 | int 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 */ |
1476 | typedef enum |
1477 | { |
1478 | SDL_CASE_NOCHANGE, |
1479 | SDL_CASE_LOWER, |
1480 | SDL_CASE_UPPER |
1481 | } SDL_letter_case; |
1482 | |
1483 | typedef 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 | |
1495 | static size_t |
1496 | SDL_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 | |
1541 | static void |
1542 | SDL_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 | |
1578 | static size_t |
1579 | SDL_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 | |
1592 | static size_t |
1593 | SDL_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 | |
1602 | static size_t |
1603 | SDL_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 | |
1616 | static size_t |
1617 | SDL_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 | |
1626 | static size_t |
1627 | SDL_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 | |
1729 | int |
1730 | SDL_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 | |