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