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