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