1 /*
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright © 2019 Keith Packard
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
17 *
18 * 3. Neither the name of the copyright holder nor the names of its
19 * contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
33 * OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35
36 #define _DEFAULT_SOURCE
37 #include <stdio.h>
38 #include <math.h>
39 #include <unistd.h>
40 #include <stdint.h>
41 #include <errno.h>
42 #include <sys/stat.h>
43 #include <string.h>
44 #include <stdlib.h>
45 #include <stdarg.h>
46 #include <stddef.h>
47 #include <wchar.h>
48 #include <locale.h>
49 #include <limits.h>
50
51 #ifndef __PICOLIBC__
52 # define printf_float(x) ((double) (x))
53 #elif defined(TINY_STDIO)
54 # if defined(PICOLIBC_MINIMAL_PRINTF_SCANF)
55 # define NO_FLOATING_POINT
56 # define NO_POS_ARGS
57 # define NO_MULTI_BYTE
58 # if !defined(_WANT_MINIMAL_IO_LONG_LONG) && __SIZEOF_LONG_LONG__ > __SIZEOF_LONG__
59 # define NO_LONG_LONG
60 # endif
61 # ifndef _WANT_IO_C99_FORMATS
62 # define NO_C99_FORMATS
63 # endif
64 # elif defined(PICOLIBC_INTEGER_PRINTF_SCANF)
65 # define NO_FLOATING_POINT
66 # define NO_MULTI_BYTE
67 # ifndef _WANT_IO_POS_ARGS
68 # define NO_POS_ARGS
69 # endif
70 # if !defined(_WANT_IO_LONG_LONG) && __SIZEOF_LONG_LONG__ > __SIZEOF_LONG__
71 # define NO_LONG_LONG
72 # endif
73 # ifndef _WANT_IO_C99_FORMATS
74 # define NO_C99_FORMATS
75 # endif
76 # ifdef _WANT_IO_PERCENT_B
77 # define BINARY_FORMAT
78 # endif
79 # elif defined(PICOLIBC_LONG_LONG_PRINTF_SCANF)
80 # define NO_FLOATING_POINT
81 # define NO_MULTI_BYTE
82 # ifndef _WANT_IO_POS_ARGS
83 # define NO_POS_ARGS
84 # endif
85 # ifndef _WANT_IO_C99_FORMATS
86 # define NO_C99_FORMATS
87 # endif
88 # ifdef _WANT_IO_PERCENT_B
89 # define BINARY_FORMAT
90 # endif
91 # elif defined(PICOLIBC_FLOAT_PRINTF_SCANF)
92 # define NO_MULTI_BYTE
93 # ifndef _IO_FLOAT_EXACT
94 # define NO_FLOAT_EXACT
95 # endif
96 # ifdef _WANT_IO_PERCENT_B
97 # define BINARY_FORMAT
98 # endif
99 # elif defined(PICOLIBC_DOUBLE_PRINTF_SCANF)
100 # ifndef _HAS_IO_MBCHAR
101 # define NO_MULTI_BYTE
102 # endif
103 # ifndef _IO_FLOAT_EXACT
104 # define NO_FLOAT_EXACT
105 # endif
106 # ifdef _WANT_IO_PERCENT_B
107 # define BINARY_FORMAT
108 # endif
109 # endif
110 #else
111 #define printf_float(x) ((double) (x))
112
113 #ifndef _WIDE_ORIENT
114 #define NO_WIDE_IO
115 #endif
116
117 #ifndef _WANT_IO_POS_ARGS
118 #define NO_POS_ARGS
119 #endif
120 #if !defined(_WANT_IO_C99_FORMATS) || defined(_NANO_FORMATTED_IO)
121 # define NO_C99_FORMATS
122 #endif
123
124 #if __SIZEOF_DOUBLE__ != 8
125 #define NO_FLOATING_POINT
126 #endif
127
128 #ifndef _WANT_IO_LONG_LONG
129 #define NO_LONG_LONG
130 #endif
131
132 #ifdef _NANO_FORMATTED_IO
133
134 #ifndef NO_FLOATING_POINT
135 extern int _printf_float();
136 extern int _scanf_float();
137
138 int (*_reference_printf_float)() = _printf_float;
139 int (*_reference_scanf_float)() = _scanf_float;
140 #endif
141 #endif
142 #endif
143
144 #if !defined(NO_FLOATING_POINT)
145 static const double test_vals[] = { 1.234567, 1.1, M_PI };
146 #endif
147
148 static int
check_vsnprintf(char * str,size_t size,const char * format,...)149 check_vsnprintf(char *str, size_t size, const char *format, ...)
150 {
151 int i;
152 va_list ap;
153
154 va_start(ap, format);
155 i = vsnprintf(str, size, format, ap);
156 va_end(ap);
157 return i;
158 }
159
160 #if !defined(NO_FLOATING_POINT)
161 #ifdef PICOLIBC_FLOAT_PRINTF_SCANF
162 #define float_type float
163 #define pow(a,b) powf((float) (a), (float) (b))
164 #define nextafter(a,b) nextafterf((float)(a), (float)(b))
165 #define fabs(a) fabsf(a)
166 #define scanf_format "%f"
167 #if (defined(TINY_STDIO) && !defined(_IO_FLOAT_EXACT))
168 #define ERROR_MAX 1e-6
169 #else
170 #define ERROR_MAX 0
171 #endif
172 #else
173 #define float_type double
174 #define scanf_format "%lf"
175 #if defined(TINY_STDIO) && !defined(_IO_FLOAT_EXACT)
176 # if __SIZEOF_DOUBLE__ == 4
177 # define ERROR_MAX 1e-6
178 # else
179 # define ERROR_MAX 1e-14
180 # endif
181 #else
182 #if (!defined(TINY_STDIO) && defined(_WANT_IO_LONG_DOUBLE))
183 /* __ldtoa is really broken */
184 #define ERROR_MAX 1e-5
185 #else
186 #define ERROR_MAX 0
187 #endif
188 #endif
189 #endif
190 #endif
191
192 #if defined(__PICOLIBC__) && !defined(TINYSTDIO)
193 #define LEGACY_NEWLIB
194 #endif
195
196 #ifndef NO_WIDE_IO
197 static struct {
198 const wchar_t *str;
199 const wchar_t *fmt;
200 int expect;
201 } wtest[] = {
202 { .str = L"foo\n", .fmt = L"foo\nbar", .expect = -1 },
203 { .str = L"foo\n", .fmt = L"foo bar", .expect = -1 },
204 { .str = L"foo\n", .fmt = L"foo %d", .expect = -1 },
205 { .str = L"foo\n", .fmt = L"foo\n%d", .expect = -1 },
206 { .str = L"foon", .fmt = L"foonbar", .expect = -1 },
207 { .str = L"foon", .fmt = L"foon%d", .expect = -1 },
208 { .str = L"foo ", .fmt = L"foo bar", .expect = -1 },
209 { .str = L"foo ", .fmt = L"foo %d", .expect = -1 },
210 { .str = L"foo\t", .fmt = L"foo\tbar", .expect = -1 },
211 { .str = L"foo\t", .fmt = L"foo bar", .expect = -1 },
212 { .str = L"foo\t", .fmt = L"foo %d", .expect = -1 },
213 { .str = L"foo\t", .fmt = L"foo\t%d", .expect = -1 },
214 { .str = L"foo", .fmt = L"foo", .expect = 0 },
215 #ifndef LEGACY_NEWLIB
216 { .str = L"foon", .fmt = L"foo bar", .expect = 0 },
217 { .str = L"foon", .fmt = L"foo %d", .expect = 0 },
218 { .str = L"foo ", .fmt = L"fooxbar", .expect = 0 },
219 { .str = L"foo ", .fmt = L"foox%d", .expect = 0 },
220 { .str = L"foo bar", .fmt = L"foon", .expect = 0 },
221 #endif
222 { .str = L"foo bar", .fmt = L"foo bar", .expect = 0 },
223 { .str = L"foo bar", .fmt = L"foo %d", .expect = 0 },
224 #ifndef LEGACY_NEWLIB
225 { .str = L"foo bar", .fmt = L"foon%d", .expect = 0 },
226 #endif
227 { .str = L"foo (nil)", .fmt = L"foo %4p", .expect = 0},
228 { .str = L"foo ", .fmt = L"foo %n", .expect = 0 },
229 { .str = L"foo%bar1", .fmt = L"foo%%bar%d", 1 },
230 { }
231 };
232 #endif
233
234 #ifndef NO_FLOATING_POINT
235
236 static float_type
int_exp10(int n)237 int_exp10(int n)
238 {
239 float_type a = 1.0;
240 int sign = n < 0;
241
242 if (sign)
243 n = -n;
244 while (n--)
245 a *= (float_type) 10.0;
246 if (sign)
247 a = 1/a;
248 return a;
249 }
250
251 #ifndef NO_FLOAT_EXACT
252
253 static int
check_float(const char * test_name,float_type a,const char * buf,const char * head,int zeros,const char * tail)254 check_float(const char *test_name, float_type a, const char *buf, const char *head, int zeros, const char *tail)
255 {
256 int z;
257 int o;
258
259 if (strncmp(buf, head, strlen(head)) != 0)
260 goto fail;
261
262 o = strlen(head);
263 for (z = 0; z < zeros; z++) {
264 if (buf[o] != '0')
265 goto fail;
266 o++;
267 }
268
269 if (strcmp(buf + o, tail) != 0)
270 goto fail;
271 return 0;
272
273 fail:
274 printf("float mismatch test %s: %a %.17e \"%s\" isn't in the form \"%s\", %d zeros, \"%s\"\n",
275 test_name, printf_float(a), printf_float(a), buf, head, zeros, tail);
276 return 1;
277 }
278
279 #endif
280 #endif
281
282 int
main(void)283 main(void)
284 {
285 int x = -35;
286 int y;
287 char buf[256];
288 int errors = 0;
289
290 #if !defined(__PICOLIBC__) || defined(_MB_CAPABLE)
291 if (!setlocale(LC_CTYPE, "C.UTF-8")) {
292 printf("setlocale(LC_CTYPE, \"C.UTF-8\") failed\n");
293 return 1;
294 }
295 #endif
296 #if 0
297 double a;
298
299 printf ("hello world\n");
300 for (x = 1; x < 20; x++) {
301 printf("%.*f\n", x, 9.99999999999);
302 }
303
304 for (a = 1e-10; a < 1e10; a *= 10.0) {
305 printf("g format: %10.3g %10.3g\n", 1.2345678 * a, 1.1 * a);
306 fflush(stdout);
307 }
308 for (a = 1e-10; a < 1e10; a *= 10.0) {
309 printf("f format: %10.3f %10.3f\n", 1.2345678 * a, 1.1 * a);
310 fflush(stdout);
311 }
312 for (a = 1e-10; a < 1e10; a *= 10.0) {
313 printf("e format: %10.3e %10.3e\n", 1.2345678 * a, 1.1 * a);
314 fflush(stdout);
315 }
316 printf ("%g\n", exp(11));
317 #endif
318
319 #ifndef NO_WIDE_IO
320 unsigned wt;
321 for (wt = 0; wtest[wt].str; wt++) {
322 void *extra;
323 int wtr = swscanf(wtest[wt].str, wtest[wt].fmt, &extra);
324 if (wtr != wtest[wt].expect) {
325 printf("%d str %ls fmt %ls expected %d got %d\n", wt,
326 wtest[wt].str, wtest[wt].fmt, wtest[wt].expect, wtr);
327 ++errors;
328 }
329 }
330 #ifndef NO_MULTI_BYTE
331 {
332 wchar_t c;
333 char test_val[] = "㌰";
334 int i = sscanf(test_val, "%lc", &c);
335 if (i != 1 || c != L'㌰') {
336 printf("%d: %lc != %s or %d != 1\n", __LINE__, (wint_t) c, test_val, i);
337 ++errors;
338 }
339 wchar_t wtest_val[] = L"㌰";
340 char c_mb[MB_LEN_MAX+1] = {0};
341 i = swscanf(wtest_val, L"%c", c_mb);
342 if (i != 1 || strcmp(c_mb, test_val) != 0) {
343 printf("%d: %s != %s or %d != 1\n", __LINE__, c_mb, test_val, i);
344 ++errors;
345 }
346 }
347 #endif
348 #endif
349
350 #if !defined(NO_FLOATING_POINT)
351 printf("checking floating point\n");
352 sprintf(buf, "%g", printf_float(0.0f));
353 if (strcmp(buf, "0") != 0) {
354 printf("0: wanted \"0\" got \"%s\"\n", buf);
355 errors++;
356 fflush(stdout);
357 }
358 #endif
359
360 #ifndef NO_POS_ARGS
361 printf("checking pos args\n");
362 x = y = 0;
363 int r = sscanf("3 4", "%2$d %1$d", &x, &y);
364 if (x != 4 || y != 3 || r != 2) {
365 printf("pos: wanted %d %d (ret %d) got %d %d (ret %d)\n", 4, 3, 2, x, y, r);
366 errors++;
367 fflush(stdout);
368 }
369 #endif
370
371 /*
372 * test snprintf and vsnprintf to make sure they don't
373 * overwrite the specified buffer length (even if that is
374 * zero)
375 */
376 for (x = 0; x <= 6; x++) {
377 for (y = 0; y < 2; y++) {
378 char tbuf[10] = "xxxxxxxxx";
379 const char ref[10] = "xxxxxxxxx";
380 const char *name = (y == 0 ? "snprintf" : "vsnprintf");
381 int i = (y == 0 ? snprintf : check_vsnprintf) (tbuf, x, "%s", "123");
382 int y = x <= 4 ? x : 4;
383 if (i != 3) {
384 printf("%s(tbuf, %d, \"%%s\", \"123\") return %d instead of %d\n", name,
385 x, i, 3);
386 errors++;
387 }
388 int l = strlen(tbuf);
389 if (y > 0 && l != y - 1) {
390 printf("%s: returned buffer len want %d got %d\n", name, y - 1, l);
391 errors++;
392 }
393 if (y > 0 && strncmp(tbuf, "123", y - 1) != 0) {
394 #pragma GCC diagnostic ignored "-Wpragmas"
395 #pragma GCC diagnostic ignored "-Wunknown-warning-option"
396 #pragma GCC diagnostic ignored "-Wstringop-truncation"
397 strncpy(buf, "123", y - 1);
398 buf[y-1] = '\0';
399 printf("%s: returned buffer want %s got %s\n", name, buf, tbuf);
400 errors++;
401 }
402 if (memcmp(tbuf + y, ref + y, sizeof(tbuf) - y) != 0) {
403 printf("%s: tail of buf mangled %s\n", name, tbuf + y);
404 errors++;
405 }
406 }
407 }
408
409 #define FMT(prefix,conv) "%" prefix conv
410
411 #define VERIFY_BOTH(prefix, oconv, iconv) do { \
412 int __n; \
413 sprintf(buf, FMT(prefix, oconv), v); \
414 __n = sscanf(buf, FMT(prefix, iconv), &r); \
415 if (v != r || __n != 1) { \
416 printf("\t%3d: " prefix " " oconv " wanted " FMT(prefix, oconv) " got " FMT(prefix, oconv) "\n", x, v, r); \
417 errors++; \
418 fflush(stdout); \
419 } \
420 } while(0)
421
422 #define VERIFY(prefix, conv) VERIFY_BOTH(prefix, conv, conv)
423
424 #ifdef BINARY_FORMAT
425 printf("checking binary format\n");
426 #define VERIFY_BINARY(prefix) VERIFY(prefix, "b")
427 #pragma GCC diagnostic ignored "-Wformat"
428 #pragma GCC diagnostic ignored "-Wformat-extra-args"
429 #else
430 #define VERIFY_BINARY(prefix)
431 #endif
432
433 #define CHECK_RT(type, prefix) do { \
434 for (x = 0; x < (int) (sizeof(type) * 8); x++) { \
435 type v = (type) (0x123456789abcdef0LL >> (64 - sizeof(type) * 8)) >> x; \
436 type r = ~v; \
437 VERIFY(prefix, "d"); \
438 r = ~v; \
439 VERIFY(prefix, "u"); \
440 r = ~v; \
441 VERIFY(prefix, "x"); \
442 r = ~v; \
443 VERIFY(prefix, "o"); \
444 r = ~v; \
445 VERIFY_BINARY(prefix); \
446 } \
447 } while(0)
448
449 #ifndef _NANO_FORMATTED_IO
450 CHECK_RT(unsigned char, "hh");
451 #endif
452 CHECK_RT(unsigned short, "h");
453 CHECK_RT(unsigned int, "");
454 CHECK_RT(unsigned long, "l");
455 #ifndef NO_LONG_LONG
456 printf("checking long long\n");
457 CHECK_RT(unsigned long long, "ll");
458 #endif
459 #ifndef NO_C99_FORMATS
460 printf("checking c99 formats\n");
461 #ifdef NO_LONG_LONG
462 if (sizeof(intmax_t) <= sizeof(long))
463 #endif
464 {
465 CHECK_RT(intmax_t, "j");
466 }
467 CHECK_RT(size_t, "z");
468 CHECK_RT(ptrdiff_t, "t");
469 #endif
470
471 {
472 static int i_addr = 12;
473 void *v = &i_addr;
474 void *r = (void *) -1;
475 VERIFY("", "p");
476 }
477 #if !defined(NO_FLOATING_POINT)
478
479 #ifndef NO_FLOAT_EXACT
480 for (x = 0; x < 37; x++) {
481 float_type m, n, a, b, c;
482 float_type pow_val = int_exp10(x);
483
484 m = 1 / pow_val;
485 n = m / (float_type) 2.0;
486
487 a = n;
488 b = n;
489 c = m;
490
491 /* Make sure the values are on the right side of the rounding value */
492 for (y = 0; y < 5; y++) {
493 a = nextafter(a, 2.0);
494 b = nextafter(b, 0.0);
495 c = nextafter(c, 0.0);
496 }
497
498 /*
499 * A value greater than 5 just past the last digit
500 * rounds up to 0.0...1
501 */
502 sprintf(buf, "%.*f", x, printf_float(a));
503 if (x == 0)
504 errors += check_float(">= 0.5", a, buf, "1", x, "");
505 else
506 errors += check_float(">= 0.5", a, buf, "0.", x-1, "1");
507
508 /*
509 * A value greater than 5 in the last digit
510 * rounds to 0.0...5
511 */
512 sprintf(buf, "%.*f", x+1, printf_float(a));
513 errors += check_float(">= 0.5", a, buf, "0.", x, "5");
514
515 /*
516 * A value less than 5 just past the last digit
517 * rounds down to 0.0...0
518 */
519 sprintf(buf, "%.*f", x, printf_float(b));
520 if (x == 0)
521 errors += check_float("< 0.5", b, buf, "0", x, "");
522 else
523 errors += check_float("< 0.5", b, buf, "0.", x, "");
524
525 /*
526 * A value less than 5 in the last digit
527 * rounds to 0.0...5
528 */
529 sprintf(buf, "%.*f", x+1, printf_float(b));
530 errors += check_float("< 0.5", b, buf, "0.", x, "5");
531
532 /*
533 * A value less than 1 in the last digit
534 * rounds to 0.0...1
535 */
536 sprintf(buf, "%.*f", x, printf_float(c));
537 if (x == 0)
538 errors += check_float("< 1", c, buf, "1", x, "");
539 else
540 errors += check_float("< 1", c, buf, "0.", x-1, "1");
541 }
542 #endif
543
544 for (x = -37; x <= 37; x++)
545 {
546 float_type r;
547 unsigned t;
548 for (t = 0; t < sizeof(test_vals)/sizeof(test_vals[0]); t++) {
549
550 float_type v = (float_type) test_vals[t] * int_exp10(x);
551 float_type e;
552
553 sprintf(buf, "%.55f", printf_float(v));
554 sscanf(buf, scanf_format, &r);
555 e = fabs(v-r) / v;
556 if (e > (float_type) ERROR_MAX) {
557 printf("\tf %3d: wanted %.7e %a got %.7e %a (error %.7e %a, buf %s)\n", x,
558 printf_float(v), printf_float(v),
559 printf_float(r), printf_float(r),
560 printf_float(e), printf_float(e),
561 buf);
562 errors++;
563 fflush(stdout);
564 }
565
566
567 sprintf(buf, "%.20e", printf_float(v));
568 sscanf(buf, scanf_format, &r);
569 e = fabs(v-r) / v;
570 if (e > (float_type) ERROR_MAX)
571 {
572 printf("\te %3d: wanted %.7e got %.7e (error %.7e, buf %s)\n", x,
573 printf_float(v), printf_float(r), printf_float(e), buf);
574 errors++;
575 fflush(stdout);
576 }
577
578
579 sprintf(buf, "%.20g", printf_float(v));
580 sscanf(buf, scanf_format, &r);
581 e = fabs(v-r) / v;
582 if (e > (float_type) ERROR_MAX)
583 {
584 printf("\tg %3d: wanted %.7e got %.7e (error %.7e, buf %s)\n", x,
585 printf_float(v), printf_float(r), printf_float(e), buf);
586 errors++;
587 fflush(stdout);
588 }
589
590 #ifndef NO_C99_FORMATS
591 sprintf(buf, "%.20a", printf_float(v));
592 sscanf(buf, scanf_format, &r);
593 e = fabs(v-r) / v;
594 if (e > (float_type) ERROR_MAX)
595 {
596 printf("\ta %3d: wanted %.7e got %.7e (error %.7e, buf %s)\n", x,
597 printf_float(v), printf_float(r), printf_float(e), buf);
598 errors++;
599 fflush(stdout);
600 }
601 #endif
602
603 }
604 #ifndef NO_C99_FORMATS
605 sprintf(buf, "0x0.0p%+d", x);
606 sscanf(buf, scanf_format, &r);
607 if (r != (float_type) 0.0)
608 {
609 printf("\tg %3d: wanted 0.0 got %.7e (buf %s)\n", x,
610 printf_float(r), buf);
611 errors++;
612 fflush(stdout);
613 }
614
615 sprintf(buf, "0x1p%+d", x);
616 sscanf(buf, scanf_format, &r);
617 if (r != (float_type) ldexp(1.0, x))
618 {
619 printf("\tg %3d: wanted 1 got %.7e (buf %s)\n", x,
620 printf_float(r), buf);
621 errors++;
622 fflush(stdout);
623 }
624 #endif
625 }
626 #endif
627 fflush(stdout);
628 return errors;
629 }
630