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