1 /*
2  * Copyright © 2005-2020 Rich Felker
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files (the
6  * "Software"), to deal in the Software without restriction, including
7  * without limitation the rights to use, copy, modify, merge, publish,
8  * distribute, sublicense, and/or sell copies of the Software, and to
9  * permit persons to whom the Software is furnished to do so, subject to
10  * the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23 
24 #define _GNU_SOURCE
25 #include <stdio.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <limits.h>
29 #include <math.h>
30 
31 #pragma GCC diagnostic ignored "-Wpragmas"
32 #pragma GCC diagnostic ignored "-Wunknown-warning-option"
33 #pragma GCC diagnostic ignored "-Wformat-extra-args"
34 #pragma GCC diagnostic ignored "-Wformat"
35 #pragma GCC diagnostic ignored "-Wformat-truncation"
36 
37 #define DISABLE_SLOW_TESTS
38 
39 #define TEST(r, f, x, m) ( \
40 ((r) = (f)) == (x) || \
41 (printf(__FILE__ ":%d: %s failed (" m ")\n", __LINE__, #f, r, x), err++, 0) )
42 
43 #define TEST_S(s, x, m) ( \
44 !strcmp((s),(x)) || \
45 (printf(__FILE__ ":%d: [%s] != [%s] (%s)\n", __LINE__, s, x, m), err++, 0) )
46 
47 static const struct {
48 	const char *fmt;
49 	int i;
50 	const char *expect;
51 } int_tests[] = {
52 	/* width, precision, alignment */
53 	{ "%04d", 12, "0012" },
54 	{ "%.3d", 12, "012" },
55 	{ "%3d", 12, " 12" },
56 	{ "%-3d", 12, "12 " },
57 	{ "%+3d", 12, "+12" },
58 	{ "%+-5d", 12, "+12  " },
59 	{ "%+- 5d", 12, "+12  " },
60 	{ "%- 5d", 12, " 12  " },
61 	{ "% d", 12, " 12" },
62 	{ "%0-5d", 12, "12   " },
63 	{ "%-05d", 12, "12   " },
64 
65 	/* ...explicit precision of 0 shall be no characters. */
66 	{ "%.0d", 0, "" },
67 	{ "%.0o", 0, "" },
68 	{ "%#.0d", 0, "" },
69 #ifdef TINY_STDIO
70 	{ "%#.0o", 0, "" },
71 #endif
72 	{ "%#.0x", 0, "" },
73 
74 	/* ...but it still has to honor width and flags. */
75 	{ "%2.0u", 0, "  " },
76 	{ "%02.0u", 0, "  " },
77 	{ "%2.0d", 0, "  " },
78 	{ "%02.0d", 0, "  " },
79 	{ "% .0d", 0, " " },
80 	{ "%+.0d", 0, "+" },
81 
82 	/* hex: test alt form and case */
83 	{ "%x", 63, "3f" },
84 	{ "%#x", 63, "0x3f" },
85 	{ "%X", 63, "3F" },
86 
87 	/* octal: test alt form */
88 	{ "%o", 15, "17" },
89 	{ "%#o", 15, "017" },
90 
91 	{ NULL, 0.0, NULL }
92 };
93 
94 static const struct {
95 	const char *fmt;
96 	double f;
97 	const char *expect;
98 } fp_tests[] = {
99 	/* basic form, handling of exponent/precision for 0 */
100 	{ "%e", 0.0, "0.000000e+00" },
101 	{ "%f", 0.0, "0.000000" },
102 	{ "%g", 0.0, "0" },
103 	{ "%#g", 0.0, "0.00000" },
104 
105 	/* rounding */
106 	{ "%f", 1.1, "1.100000" },
107 	{ "%f", 1.2, "1.200000" },
108 	{ "%f", 1.3, "1.300000" },
109 	{ "%f", 1.4, "1.400000" },
110 	{ "%f", 1.5, "1.500000" },
111 	{ "%.4f", 1.06125, "1.0613" },
112 	{ "%.2f", 1.375, "1.38" },
113 	{ "%.1f", 1.375, "1.4" },
114 	{ "%.15f", 1.1, "1.100000000000000" },
115 #ifdef TINY_STDIO
116 	{ "%.16f", 1.1, "1.1000000000000000" },
117 	{ "%.17f", 1.1, "1.10000000000000000" },
118 #else
119         /* legacy stdio adds non-zero digits beyond needed precision */
120 	{ "%.16f", 1.1, "1.1000000000000001" },
121 	{ "%.17f", 1.1, "1.10000000000000009" },
122 #endif
123 	{ "%.2e", 1500001.0, "1.50e+06" },
124 	{ "%.2e", 1505000.0, "1.50e+06" },
125 	{ "%.2e", 1505000.00000095367431640625, "1.51e+06" },
126 	{ "%.2e", 1505001.0, "1.51e+06" },
127 	{ "%.2e", 1506000.0, "1.51e+06" },
128 
129 	/* correctness in DBL_DIG places */
130 	{ "%.15g", 1.23456789012345, "1.23456789012345" },
131 
132 	/* correct choice of notation for %g */
133 	{ "%g", 0.0001, "0.0001" },
134 	{ "%g", 0.00001, "1e-05" },
135 	{ "%g", 123456, "123456" },
136 	{ "%g", 1234567, "1.23457e+06" },
137 	{ "%.7g", 1234567, "1234567" },
138 	{ "%.7g", 12345678, "1.234568e+07" },
139 	{ "%.8g", 0.1, "0.1" },
140 	{ "%.9g", 0.1, "0.1" },
141 	{ "%.10g", 0.1, "0.1" },
142 	{ "%.11g", 0.1, "0.1" },
143 
144 	/* pi in double precision, printed to a few extra places */
145 	{ "%.15f", M_PI, "3.141592653589793" },
146 #ifdef TINY_STDIO
147 	{ "%.18f", M_PI, "3.141592653589793000" },
148 #else
149         /* legacy stdio adds non-zero digits beyond needed precision */
150 	{ "%.18f", M_PI, "3.141592653589793116" },
151 #endif
152 
153 	/* exact conversion of large integers */
154 #ifdef TINY_STDIO
155 	{ "%.0f", 340282366920938463463374607431768211456.0,
156 	         "340282366920938500000000000000000000000" },
157 #else
158         /* legacy stdio adds non-zero digits beyond needed precision */
159 	{ "%.0f", 340282366920938463463374607431768211456.0,
160 	         "340282366920938463463374607431768211456" },
161 #endif
162 
163 	{ NULL, 0.0, NULL }
164 };
165 
test_snprintf(void)166 int test_snprintf(void)
167 {
168 	int i, j, k;
169 	int err=0;
170 	char b[2000];
171 
172 	TEST(i, snprintf(0, 0, "%d", 123456), 6, "length returned %d != %d");
173 	TEST(i, snprintf(0, 0, "%.4s", "hello"), 4, "length returned %d != %d");
174 	TEST(i, snprintf(b, 0, "%.0s", "goodbye"), 0, "length returned %d != %d");
175 
176 	strcpy(b, "xxxxxxxx");
177 	TEST(i, snprintf(b, 4, "%d", 123456), 6, "length returned %d != %d");
178 	TEST_S(b, "123", "incorrect output");
179 	TEST(i, b[5], 'x', "buffer overrun");
180 
181 #if defined(TINY_STDIO) || (!defined(NO_FLOATING_POINT) && (!defined(_WANT_IO_LONG_DOUBLE) || defined(_LDBL_EQ_DBL)))
182 	/* Perform ascii arithmetic to test printing tiny doubles */
183 	TEST(i, snprintf(b, sizeof b, "%.1022f", 0x1p-1021), 1024, "%d != %d");
184 	b[1] = '0';
185 	for (i=0; i<1021; i++) {
186 		for (k=0, j=1023; j>0; j--) {
187 			if (b[j]<'5') b[j]+=b[j]-'0'+k, k=0;
188 			else b[j]+=b[j]-'0'-10+k, k=1;
189 		}
190 	}
191 	TEST(i, b[1], '1', "'%c' != '%c'");
192 // I have no idea what this is testing, but it's not sensible
193 //	for (j=2; b[j]=='0'; j++);
194 //	TEST(i, j, 1024, "%d != %d");
195 #endif
196 
197 
198 #ifndef DISABLE_SLOW_TESTS
199 	errno = 0;
200 	TEST(i, snprintf(NULL, 0, "%.*u", 2147483647, 0), 2147483647, "cannot print max length %d");
201 // picolibc doesn't check this condition
202 //	TEST(i, snprintf(NULL, 0, "%.*u ", 2147483647, 0), -1, "integer overflow %d");
203 //	TEST(i, errno, EOVERFLOW, "after overflow: %d != %d");
204 #endif
205 	for (j=0; int_tests[j].fmt; j++) {
206                 TEST(i, snprintf(b, sizeof b, int_tests[j].fmt, int_tests[j].i), (int) strlen(b), "%d != %d");
207 		TEST_S(b, int_tests[j].expect, "bad integer conversion");
208 	}
209 
210         (void) fp_tests;
211         (void) k;
212 #if !defined(NO_FLOATING_POINT) && defined(_IO_FLOAT_EXACT)
213 	for (j=0; fp_tests[j].fmt; j++) {
214 		TEST(i, snprintf(b, sizeof b, fp_tests[j].fmt, fp_tests[j].f), (int) strlen(b), "%d != %d");
215 		TEST_S(b, fp_tests[j].expect, "bad floating point conversion");
216 	}
217 
218 	TEST(i, snprintf(0, 0, "%.4a", 1.0), 11, "%d != %d");
219 #endif
220 
221 	return err;
222 }
223 
224 #define TEST_NAME snprintf
225 #include "testcase.h"
226