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
49 #ifndef __PICOLIBC__
50 # define printf_float(x) ((double) (x))
51 #elif defined(TINY_STDIO)
52 # if defined(PICOLIBC_MINIMAL_PRINTF_SCANF)
53 # define NO_FLOATING_POINT
54 # define NO_POS_ARGS
55 # if !defined(_WANT_MINIMAL_IO_LONG_LONG) && __SIZEOF_LONG_LONG__ > __SIZEOF_LONG__
56 # define NO_LONG_LONG
57 # endif
58 # ifndef _WANT_IO_C99_FORMATS
59 # define NO_C99_FORMATS
60 # endif
61 # elif defined(PICOLIBC_INTEGER_PRINTF_SCANF)
62 # define NO_FLOATING_POINT
63 # ifndef _WANT_IO_POS_ARGS
64 # define NO_POS_ARGS
65 # endif
66 # if !defined(_WANT_IO_LONG_LONG) && __SIZEOF_LONG_LONG__ > __SIZEOF_LONG__
67 # define NO_LONG_LONG
68 # endif
69 # ifndef _WANT_IO_C99_FORMATS
70 # define NO_C99_FORMATS
71 # endif
72 # ifdef _WANT_IO_PERCENT_B
73 # define BINARY_FORMAT
74 # endif
75 # elif defined(PICOLIBC_LONG_LONG_PRINTF_SCANF)
76 # define NO_FLOATING_POINT
77 # ifndef _WANT_IO_POS_ARGS
78 # define NO_POS_ARGS
79 # endif
80 # ifndef _WANT_IO_C99_FORMATS
81 # define NO_C99_FORMATS
82 # endif
83 # ifdef _WANT_IO_PERCENT_B
84 # define BINARY_FORMAT
85 # endif
86 # elif defined(PICOLIBC_FLOAT_PRINTF_SCANF) || defined(PICOLIBC_DOUBLE_PRINTF_SCANF)
87 # ifdef _WANT_IO_PERCENT_B
88 # define BINARY_FORMAT
89 # endif
90 # endif
91 #else
92 #define printf_float(x) ((double) (x))
93
94 #ifndef _WIDE_ORIENT
95 #define NO_WIDE_IO
96 #endif
97
98 #ifndef _WANT_IO_POS_ARGS
99 #define NO_POS_ARGS
100 #endif
101 #if !defined(_WANT_IO_C99_FORMATS) || defined(_NANO_FORMATTED_IO)
102 # define NO_C99_FORMATS
103 #endif
104
105 #if __SIZEOF_DOUBLE__ != 8
106 #define NO_FLOATING_POINT
107 #endif
108
109 #ifndef _WANT_IO_LONG_LONG
110 #define NO_LONG_LONG
111 #endif
112
113 #ifdef _NANO_FORMATTED_IO
114
115 #ifndef NO_FLOATING_POINT
116 extern int _printf_float();
117 extern int _scanf_float();
118
119 int (*_reference_printf_float)() = _printf_float;
120 int (*_reference_scanf_float)() = _scanf_float;
121 #endif
122 #endif
123 #endif
124
125 #if !defined(NO_FLOATING_POINT)
126 static const double test_vals[] = { 1.234567, 1.1, M_PI };
127 #endif
128
129 int
check_vsnprintf(char * str,size_t size,const char * format,...)130 check_vsnprintf(char *str, size_t size, const char *format, ...)
131 {
132 int i;
133 va_list ap;
134
135 va_start(ap, format);
136 i = vsnprintf(str, size, format, ap);
137 va_end(ap);
138 return i;
139 }
140
141 #if defined(__PICOLIBC__) && !defined(TINYSTDIO)
142 #define LEGACY_NEWLIB
143 #endif
144
145 #ifndef NO_WIDE_IO
146 static struct {
147 const wchar_t *str;
148 const wchar_t *fmt;
149 int expect;
150 } wtest[] = {
151 { .str = L"foo\n", .fmt = L"foo\nbar", .expect = -1 },
152 { .str = L"foo\n", .fmt = L"foo bar", .expect = -1 },
153 { .str = L"foo\n", .fmt = L"foo %d", .expect = -1 },
154 { .str = L"foo\n", .fmt = L"foo\n%d", .expect = -1 },
155 { .str = L"foon", .fmt = L"foonbar", .expect = -1 },
156 { .str = L"foon", .fmt = L"foon%d", .expect = -1 },
157 { .str = L"foo ", .fmt = L"foo bar", .expect = -1 },
158 { .str = L"foo ", .fmt = L"foo %d", .expect = -1 },
159 { .str = L"foo\t", .fmt = L"foo\tbar", .expect = -1 },
160 { .str = L"foo\t", .fmt = L"foo bar", .expect = -1 },
161 { .str = L"foo\t", .fmt = L"foo %d", .expect = -1 },
162 { .str = L"foo\t", .fmt = L"foo\t%d", .expect = -1 },
163 { .str = L"foo", .fmt = L"foo", .expect = 0 },
164 #ifndef LEGACY_NEWLIB
165 { .str = L"foon", .fmt = L"foo bar", .expect = 0 },
166 { .str = L"foon", .fmt = L"foo %d", .expect = 0 },
167 { .str = L"foo ", .fmt = L"fooxbar", .expect = 0 },
168 { .str = L"foo ", .fmt = L"foox%d", .expect = 0 },
169 { .str = L"foo bar", .fmt = L"foon", .expect = 0 },
170 #endif
171 { .str = L"foo bar", .fmt = L"foo bar", .expect = 0 },
172 { .str = L"foo bar", .fmt = L"foo %d", .expect = 0 },
173 #ifndef LEGACY_NEWLIB
174 { .str = L"foo bar", .fmt = L"foon%d", .expect = 0 },
175 #endif
176 { .str = L"foo (nil)", .fmt = L"foo %4p", .expect = 0},
177 { .str = L"foo ", .fmt = L"foo %n", .expect = 0 },
178 { .str = L"foo%bar1", .fmt = L"foo%%bar%d", 1 },
179 { }
180 };
181 #endif
182
183 int
main(void)184 main(void)
185 {
186 int x = -35;
187 int y;
188 char buf[256];
189 int errors = 0;
190
191 #if 0
192 double a;
193
194 printf ("hello world\n");
195 for (x = 1; x < 20; x++) {
196 printf("%.*f\n", x, 9.99999999999);
197 }
198
199 for (a = 1e-10; a < 1e10; a *= 10.0) {
200 printf("g format: %10.3g %10.3g\n", 1.2345678 * a, 1.1 * a);
201 fflush(stdout);
202 }
203 for (a = 1e-10; a < 1e10; a *= 10.0) {
204 printf("f format: %10.3f %10.3f\n", 1.2345678 * a, 1.1 * a);
205 fflush(stdout);
206 }
207 for (a = 1e-10; a < 1e10; a *= 10.0) {
208 printf("e format: %10.3e %10.3e\n", 1.2345678 * a, 1.1 * a);
209 fflush(stdout);
210 }
211 printf ("%g\n", exp(11));
212 #endif
213
214 #ifndef NO_WIDE_IO
215 unsigned wt;
216 for (wt = 0; wtest[wt].str; wt++) {
217 void *extra;
218 int wtr = swscanf(wtest[wt].str, wtest[wt].fmt, &extra);
219 if (wtr != wtest[wt].expect) {
220 wprintf(L"%d str %ls fmt %ls expected %d got %d\n", wt,
221 wtest[wt].str, wtest[wt].fmt, wtest[wt].expect, wtr);
222 ++errors;
223 }
224 }
225 wprintf(L"hello world %g\n", 1.0);
226 #endif
227
228 #if !defined(NO_FLOATING_POINT)
229 printf("checking floating point\n");
230 sprintf(buf, "%g", printf_float(0.0f));
231 if (strcmp(buf, "0") != 0) {
232 printf("0: wanted \"0\" got \"%s\"\n", buf);
233 errors++;
234 fflush(stdout);
235 }
236 #endif
237
238 #ifndef NO_POS_ARGS
239 printf("checking pos args\n");
240 x = y = 0;
241 int r = sscanf("3 4", "%2$d %1$d", &x, &y);
242 if (x != 4 || y != 3 || r != 2) {
243 printf("pos: wanted %d %d (ret %d) got %d %d (ret %d)\n", 4, 3, 2, x, y, r);
244 errors++;
245 fflush(stdout);
246 }
247 #endif
248
249 /*
250 * test snprintf and vsnprintf to make sure they don't
251 * overwrite the specified buffer length (even if that is
252 * zero)
253 */
254 for (x = 0; x <= 6; x++) {
255 for (y = 0; y < 2; y++) {
256 char tbuf[10] = "xxxxxxxxx";
257 const char ref[10] = "xxxxxxxxx";
258 const char *name = (y == 0 ? "snprintf" : "vsnprintf");
259 int i = (y == 0 ? snprintf : check_vsnprintf) (tbuf, x, "%s", "123");
260 int y = x <= 4 ? x : 4;
261 if (i != 3) {
262 printf("%s(tbuf, %d, \"%%s\", \"123\") return %d instead of %d\n", name,
263 x, i, 3);
264 errors++;
265 }
266 int l = strlen(tbuf);
267 if (y > 0 && l != y - 1) {
268 printf("%s: returned buffer len want %d got %d\n", name, y - 1, l);
269 errors++;
270 }
271 if (y > 0 && strncmp(tbuf, "123", y - 1) != 0) {
272 #pragma GCC diagnostic ignored "-Wpragmas"
273 #pragma GCC diagnostic ignored "-Wunknown-warning-option"
274 #pragma GCC diagnostic ignored "-Wstringop-truncation"
275 strncpy(buf, "123", y - 1);
276 buf[y-1] = '\0';
277 printf("%s: returned buffer want %s got %s\n", name, buf, tbuf);
278 errors++;
279 }
280 if (memcmp(tbuf + y, ref + y, sizeof(tbuf) - y) != 0) {
281 printf("%s: tail of buf mangled %s\n", name, tbuf + y);
282 errors++;
283 }
284 }
285 }
286
287 #define FMT(prefix,conv) "%" prefix conv
288
289 #define VERIFY_BOTH(prefix, oconv, iconv) do { \
290 int __n; \
291 sprintf(buf, FMT(prefix, oconv), v); \
292 __n = sscanf(buf, FMT(prefix, iconv), &r); \
293 if (v != r || __n != 1) { \
294 printf("\t%3d: " prefix " " oconv " wanted " FMT(prefix, oconv) " got " FMT(prefix, oconv) "\n", x, v, r); \
295 errors++; \
296 fflush(stdout); \
297 } \
298 } while(0)
299
300 #define VERIFY(prefix, conv) VERIFY_BOTH(prefix, conv, conv)
301
302 #ifdef BINARY_FORMAT
303 printf("checking binary format\n");
304 #define VERIFY_BINARY(prefix) VERIFY(prefix, "b")
305 #pragma GCC diagnostic ignored "-Wformat"
306 #pragma GCC diagnostic ignored "-Wformat-extra-args"
307 #else
308 #define VERIFY_BINARY(prefix)
309 #endif
310
311 #define CHECK_RT(type, prefix) do { \
312 for (x = 0; x < (int) (sizeof(type) * 8); x++) { \
313 type v = (type) (0x123456789abcdef0LL >> (64 - sizeof(type) * 8)) >> x; \
314 type r = ~v; \
315 VERIFY(prefix, "d"); \
316 r = ~v; \
317 VERIFY(prefix, "u"); \
318 r = ~v; \
319 VERIFY(prefix, "x"); \
320 r = ~v; \
321 VERIFY(prefix, "o"); \
322 r = ~v; \
323 VERIFY_BINARY(prefix); \
324 } \
325 } while(0)
326
327 #ifndef _NANO_FORMATTED_IO
328 CHECK_RT(unsigned char, "hh");
329 #endif
330 CHECK_RT(unsigned short, "h");
331 CHECK_RT(unsigned int, "");
332 CHECK_RT(unsigned long, "l");
333 #ifndef NO_LONG_LONG
334 printf("checking long long\n");
335 CHECK_RT(unsigned long long, "ll");
336 #endif
337 #ifndef NO_C99_FORMATS
338 printf("checking c99 formats\n");
339 #ifdef NO_LONG_LONG
340 if (sizeof(intmax_t) <= sizeof(long))
341 #endif
342 {
343 CHECK_RT(intmax_t, "j");
344 }
345 CHECK_RT(size_t, "z");
346 CHECK_RT(ptrdiff_t, "t");
347 #endif
348
349 {
350 static int i_addr = 12;
351 void *v = &i_addr;
352 void *r = (void *) -1;
353 VERIFY("", "p");
354 }
355 #if !defined(NO_FLOATING_POINT)
356 #ifdef PICOLIBC_FLOAT_PRINTF_SCANF
357 #define float_type float
358 #define pow(a,b) powf((float) a, (float) b)
359 #define fabs(a) fabsf(a)
360 #define scanf_format "%f"
361 #if (defined(TINY_STDIO) && !defined(_IO_FLOAT_EXACT))
362 #define ERROR_MAX 1e-6
363 #else
364 #define ERROR_MAX 0
365 #endif
366 #else
367 #define float_type double
368 #define scanf_format "%lf"
369 #if defined(TINY_STDIO) && !defined(_IO_FLOAT_EXACT)
370 # if __SIZEOF_DOUBLE__ == 4
371 # define ERROR_MAX 1e-6
372 # else
373 # define ERROR_MAX 1e-14
374 # endif
375 #else
376 #if (!defined(TINY_STDIO) && defined(_WANT_IO_LONG_DOUBLE))
377 /* __ldtoa is really broken */
378 #define ERROR_MAX 1e-5
379 #else
380 #define ERROR_MAX 0
381 #endif
382 #endif
383 #endif
384 for (x = -37; x <= 37; x++)
385 {
386 float_type r;
387 unsigned t;
388 for (t = 0; t < sizeof(test_vals)/sizeof(test_vals[0]); t++) {
389
390 float_type v = (float_type) test_vals[t] * pow(10.0, (float_type) x);
391 float_type e;
392
393 sprintf(buf, "%.55f", printf_float(v));
394 sscanf(buf, scanf_format, &r);
395 e = fabs(v-r) / v;
396 if (e > (float_type) ERROR_MAX) {
397 printf("\tf %3d: wanted %.7e got %.7e (error %.7e, buf %s)\n", x,
398 printf_float(v), printf_float(r), printf_float(e), buf);
399 errors++;
400 fflush(stdout);
401 }
402
403
404 sprintf(buf, "%.20e", printf_float(v));
405 sscanf(buf, scanf_format, &r);
406 e = fabs(v-r) / v;
407 if (e > (float_type) ERROR_MAX)
408 {
409 printf("\te %3d: wanted %.7e got %.7e (error %.7e, buf %s)\n", x,
410 printf_float(v), printf_float(r), printf_float(e), buf);
411 errors++;
412 fflush(stdout);
413 }
414
415
416 sprintf(buf, "%.20g", printf_float(v));
417 sscanf(buf, scanf_format, &r);
418 e = fabs(v-r) / v;
419 if (e > (float_type) ERROR_MAX)
420 {
421 printf("\tg %3d: wanted %.7e got %.7e (error %.7e, buf %s)\n", x,
422 printf_float(v), printf_float(r), printf_float(e), buf);
423 errors++;
424 fflush(stdout);
425 }
426
427 #ifndef NO_C99_FORMATS
428 sprintf(buf, "%.20a", printf_float(v));
429 sscanf(buf, scanf_format, &r);
430 e = fabs(v-r) / v;
431 if (e > (float_type) ERROR_MAX)
432 {
433 printf("\ta %3d: wanted %.7e got %.7e (error %.7e, buf %s)\n", x,
434 printf_float(v), printf_float(r), printf_float(e), buf);
435 errors++;
436 fflush(stdout);
437 }
438 #endif
439
440 }
441 #ifndef NO_C99_FORMATS
442 sprintf(buf, "0x0.0p%+d", x);
443 sscanf(buf, scanf_format, &r);
444 if (r != (float_type) 0.0)
445 {
446 printf("\tg %3d: wanted 0.0 got %.7e (buf %s)\n", x,
447 printf_float(r), buf);
448 errors++;
449 fflush(stdout);
450 }
451
452 sprintf(buf, "0x1p%+d", x);
453 sscanf(buf, scanf_format, &r);
454 if (r != (float_type) ldexp(1.0, x))
455 {
456 printf("\tg %3d: wanted 1 got %.7e (buf %s)\n", x,
457 printf_float(r), buf);
458 errors++;
459 fflush(stdout);
460 }
461 #endif
462 }
463 #endif
464 fflush(stdout);
465 return errors;
466 }
467