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 #pragma GCC diagnostic ignored "-Woverflow"
37 
38 #define DISABLE_SLOW_TESTS
39 
40 #define TEST(r, f, x, m) ( \
41 ((r) = (f)) == (x) || \
42 (printf(__FILE__ ":%d: %s failed (" m ")\n", __LINE__, #f, r, x), err++, 0) )
43 
44 #define TEST_S(s, x, m) ( \
45 !strcmp((s),(x)) || \
46 (printf(__FILE__ ":%d: [%s] != [%s] (%s)\n", __LINE__, s, x, m), err++, 0) )
47 
48 static const struct {
49 	const char *fmt;
50 	int i;
51 	const char *expect;
52 } int_tests[] = {
53 	/* width, precision, alignment */
54 	{ "%04d", 12, "0012" },
55 	{ "%.3d", 12, "012" },
56 	{ "%3d", 12, " 12" },
57 	{ "%-3d", 12, "12 " },
58 	{ "%+3d", 12, "+12" },
59 	{ "%+-5d", 12, "+12  " },
60 	{ "%+- 5d", 12, "+12  " },
61 	{ "%- 5d", 12, " 12  " },
62 	{ "% d", 12, " 12" },
63 	{ "%0-5d", 12, "12   " },
64 	{ "%-05d", 12, "12   " },
65 
66 	/* ...explicit precision of 0 shall be no characters. */
67 	{ "%.0d", 0, "" },
68 	{ "%.0o", 0, "" },
69 	{ "%#.0d", 0, "" },
70 #ifdef TINY_STDIO
71 	{ "%#.0o", 0, "" },
72 #endif
73 	{ "%#.0x", 0, "" },
74 
75 	/* ...but it still has to honor width and flags. */
76 	{ "%2.0u", 0, "  " },
77 	{ "%02.0u", 0, "  " },
78 	{ "%2.0d", 0, "  " },
79 	{ "%02.0d", 0, "  " },
80 	{ "% .0d", 0, " " },
81 	{ "%+.0d", 0, "+" },
82 
83 	/* hex: test alt form and case */
84 	{ "%x", 63, "3f" },
85 	{ "%#x", 63, "0x3f" },
86 	{ "%X", 63, "3F" },
87 
88 	/* octal: test alt form */
89 	{ "%o", 15, "17" },
90 	{ "%#o", 15, "017" },
91 
92 	{ NULL, 0.0, NULL }
93 };
94 
95 static const struct {
96 	const char *fmt;
97 	double f;
98 	const char *expect;
99 } fp_tests[] = {
100 	/* basic form, handling of exponent/precision for 0 */
101 	{ "%e", 0.0, "0.000000e+00" },
102 	{ "%f", 0.0, "0.000000" },
103 	{ "%g", 0.0, "0" },
104 	{ "%#g", 0.0, "0.00000" },
105 
106 	/* rounding */
107 	{ "%f", 1.1, "1.100000" },
108 	{ "%f", 1.2, "1.200000" },
109 	{ "%f", 1.3, "1.300000" },
110 	{ "%f", 1.4, "1.400000" },
111 	{ "%f", 1.5, "1.500000" },
112 	{ "%.4f", 1.06125, "1.0613" },
113 	{ "%.2f", 1.375, "1.38" },
114 	{ "%.1f", 1.375, "1.4" },
115 	{ "%.15f", 1.1, "1.100000000000000" },
116 #ifdef TINY_STDIO
117 	{ "%.16f", 1.1, "1.1000000000000000" },
118 	{ "%.17f", 1.1, "1.10000000000000000" },
119 #else
120         /* legacy stdio adds non-zero digits beyond needed precision */
121 	{ "%.16f", 1.1, "1.1000000000000001" },
122 	{ "%.17f", 1.1, "1.10000000000000009" },
123 #endif
124 	{ "%.2e", 1500001.0, "1.50e+06" },
125 	{ "%.2e", 1505000.0, "1.50e+06" },
126 	{ "%.2e", 1505000.00000095367431640625, "1.51e+06" },
127 	{ "%.2e", 1505001.0, "1.51e+06" },
128 	{ "%.2e", 1506000.0, "1.51e+06" },
129 
130 	/* correctness in DBL_DIG places */
131 	{ "%.15g", 1.23456789012345, "1.23456789012345" },
132 
133 	/* correct choice of notation for %g */
134 	{ "%g", 0.0001, "0.0001" },
135 	{ "%g", 0.00001, "1e-05" },
136 	{ "%g", 123456, "123456" },
137 	{ "%g", 1234567, "1.23457e+06" },
138 	{ "%.7g", 1234567, "1234567" },
139 	{ "%.7g", 12345678, "1.234568e+07" },
140 	{ "%.8g", 0.1, "0.1" },
141 	{ "%.9g", 0.1, "0.1" },
142 	{ "%.10g", 0.1, "0.1" },
143 	{ "%.11g", 0.1, "0.1" },
144 
145 	/* pi in double precision, printed to a few extra places */
146 	{ "%.15f", M_PI, "3.141592653589793" },
147 #ifdef TINY_STDIO
148 	{ "%.18f", M_PI, "3.141592653589793000" },
149 #else
150         /* legacy stdio adds non-zero digits beyond needed precision */
151 	{ "%.18f", M_PI, "3.141592653589793116" },
152 #endif
153 
154 	/* exact conversion of large integers */
155 #ifdef TINY_STDIO
156 	{ "%.0f", 340282366920938463463374607431768211456.0,
157 	         "340282366920938500000000000000000000000" },
158 #else
159         /* legacy stdio adds non-zero digits beyond needed precision */
160 	{ "%.0f", 340282366920938463463374607431768211456.0,
161 	         "340282366920938463463374607431768211456" },
162 #endif
163 
164 	{ NULL, 0.0, NULL }
165 };
166 
test_snprintf(void)167 int test_snprintf(void)
168 {
169 	int i, j, k;
170 	int err=0;
171 	char b[2000];
172 
173 	TEST(i, snprintf(0, 0, "%ld", 123456l), 6, "length returned %d != %d");
174 	TEST(i, snprintf(0, 0, "%.4s", "hello"), 4, "length returned %d != %d");
175 	TEST(i, snprintf(b, 0, "%.0s", "goodbye"), 0, "length returned %d != %d");
176 
177 	strcpy(b, "xxxxxxxx");
178 	TEST(i, snprintf(b, 4, "%ld", 123456l), 6, "length returned %d != %d");
179 	TEST_S(b, "123", "incorrect output");
180 	TEST(i, b[5], 'x', "buffer overrun");
181 
182 #if __SIZEOF_DOUBLE__ == 8
183 #if defined(TINY_STDIO) || (!defined(NO_FLOATING_POINT) && (!defined(_WANT_IO_LONG_DOUBLE) || defined(_LDBL_EQ_DBL)))
184 	/* Perform ascii arithmetic to test printing tiny doubles */
185 	TEST(i, snprintf(b, sizeof b, "%.1022f", 0x1p-1021), 1024, "%d != %d");
186 	b[1] = '0';
187 	for (i=0; i<1021; i++) {
188 		for (k=0, j=1023; j>0; j--) {
189 			if (b[j]<'5') b[j]+=b[j]-'0'+k, k=0;
190 			else b[j]+=b[j]-'0'-10+k, k=1;
191 		}
192 	}
193 	TEST(i, b[1], '1', "'%c' != '%c'");
194 // I have no idea what this is testing, but it's not sensible
195 //	for (j=2; b[j]=='0'; j++);
196 //	TEST(i, j, 1024, "%d != %d");
197 #endif
198 #endif
199 
200 
201 #ifndef DISABLE_SLOW_TESTS
202 	errno = 0;
203 	TEST(i, snprintf(NULL, 0, "%.*u", 2147483647, 0), 2147483647, "cannot print max length %d");
204 // picolibc doesn't check this condition
205 //	TEST(i, snprintf(NULL, 0, "%.*u ", 2147483647, 0), -1, "integer overflow %d");
206 //	TEST(i, errno, EOVERFLOW, "after overflow: %d != %d");
207 #endif
208 	for (j=0; int_tests[j].fmt; j++) {
209                 TEST(i, snprintf(b, sizeof b, int_tests[j].fmt, int_tests[j].i), (int) strlen(b), "%d != %d");
210 		TEST_S(b, int_tests[j].expect, "bad integer conversion");
211 	}
212 
213         (void) fp_tests;
214         (void) k;
215 #if !defined(NO_FLOATING_POINT) && defined(_IO_FLOAT_EXACT) && __SIZEOF_DOUBLE__ == 8
216 	for (j=0; fp_tests[j].fmt; j++) {
217 		TEST(i, snprintf(b, sizeof b, fp_tests[j].fmt, fp_tests[j].f), (int) strlen(b), "%d != %d");
218 		TEST_S(b, fp_tests[j].expect, "bad floating point conversion");
219 	}
220 
221 	TEST(i, snprintf(0, 0, "%.4a", 1.0), 11, "%d != %d");
222 #endif
223 
224 	return err;
225 }
226 
227 #define TEST_NAME snprintf
228 #include "testcase.h"
229