1 /* $NetBSD: t_printf.c,v 1.18 2024/05/11 14:39:53 riastradh Exp $ */
2
3 /*-
4 * Copyright (c) 2010 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/types.h>
30 #include <sys/resource.h>
31
32 #include <atf-c.h>
33 #include <errno.h>
34 #include <float.h>
35 #include <math.h>
36 #include <stdint.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <time.h>
41
42 ATF_TC(snprintf_c99);
ATF_TC_HEAD(snprintf_c99,tc)43 ATF_TC_HEAD(snprintf_c99, tc)
44 {
45
46 atf_tc_set_md_var(tc, "descr",
47 "Test printf(3) C99 conformance (PR lib/22019)");
48 }
49
ATF_TC_BODY(snprintf_c99,tc)50 ATF_TC_BODY(snprintf_c99, tc)
51 {
52 char s[4];
53
54 (void)memset(s, '\0', sizeof(s));
55 (void)snprintf(s, sizeof(s), "%#.o", 0);
56 (void)printf("printf = %#.o\n", 0);
57 (void)fprintf(stderr, "snprintf = %s", s);
58
59 ATF_REQUIRE(strlen(s) == 1);
60 ATF_REQUIRE(s[0] == '0');
61 }
62
63 ATF_TC(snprintf_dotzero);
ATF_TC_HEAD(snprintf_dotzero,tc)64 ATF_TC_HEAD(snprintf_dotzero, tc)
65 {
66
67 atf_tc_set_md_var(tc, "descr",
68 "PR lib/32951: %%.0f formats (0.0,0.5] to \"0.\"");
69 }
70
ATF_TC_BODY(snprintf_dotzero,tc)71 ATF_TC_BODY(snprintf_dotzero, tc)
72 {
73 char s[4];
74
75 ATF_CHECK(snprintf(s, sizeof(s), "%.0f", 0.1) == 1);
76 ATF_REQUIRE_STREQ(s, "0");
77 }
78
79 ATF_TC(snprintf_posarg);
ATF_TC_HEAD(snprintf_posarg,tc)80 ATF_TC_HEAD(snprintf_posarg, tc)
81 {
82
83 atf_tc_set_md_var(tc, "descr", "test for positional arguments");
84 }
85
ATF_TC_BODY(snprintf_posarg,tc)86 ATF_TC_BODY(snprintf_posarg, tc)
87 {
88 char s[16];
89
90 ATF_CHECK(snprintf(s, sizeof(s), "%1$d", -23) == 3);
91 ATF_REQUIRE_STREQ(s, "-23");
92 }
93
94 ATF_TC(snprintf_posarg_width);
ATF_TC_HEAD(snprintf_posarg_width,tc)95 ATF_TC_HEAD(snprintf_posarg_width, tc)
96 {
97
98 atf_tc_set_md_var(tc, "descr", "test for positional arguments with "
99 "field width");
100 }
101
ATF_TC_BODY(snprintf_posarg_width,tc)102 ATF_TC_BODY(snprintf_posarg_width, tc)
103 {
104 char s[16];
105
106 ATF_CHECK(snprintf(s, sizeof(s), "%1$*2$d", -23, 4) == 4);
107 ATF_REQUIRE_STREQ(s, " -23");
108 }
109
110 ATF_TC(snprintf_posarg_error);
ATF_TC_HEAD(snprintf_posarg_error,tc)111 ATF_TC_HEAD(snprintf_posarg_error, tc)
112 {
113
114 atf_tc_set_md_var(tc, "descr", "test for positional arguments out "
115 "of bounds");
116 }
117
ATF_TC_BODY(snprintf_posarg_error,tc)118 ATF_TC_BODY(snprintf_posarg_error, tc)
119 {
120 char s[16], fmt[32];
121
122 snprintf(fmt, sizeof(fmt), "%%%zu$d", SIZE_MAX / sizeof(size_t));
123
124 ATF_CHECK(snprintf(s, sizeof(s), fmt, -23) == -1);
125 }
126
127 ATF_TC(snprintf_float);
ATF_TC_HEAD(snprintf_float,tc)128 ATF_TC_HEAD(snprintf_float, tc)
129 {
130
131 atf_tc_set_md_var(tc, "descr", "test that floating conversions don't"
132 " leak memory");
133 }
134
ATF_TC_BODY(snprintf_float,tc)135 ATF_TC_BODY(snprintf_float, tc)
136 {
137 union {
138 double d;
139 uint64_t bits;
140 } u;
141 uint32_t ul, uh;
142 time_t now;
143 char buf[1000];
144 struct rlimit rl;
145
146 rl.rlim_cur = rl.rlim_max = 1 * 1024 * 1024;
147 ATF_CHECK(setrlimit(RLIMIT_AS, &rl) != -1);
148 rl.rlim_cur = rl.rlim_max = 1 * 1024 * 1024;
149 ATF_CHECK(setrlimit(RLIMIT_DATA, &rl) != -1);
150
151 time(&now);
152 srand(now);
153 for (size_t i = 0; i < 10000; i++) {
154 ul = rand();
155 uh = rand();
156 u.bits = (uint64_t)uh << 32 | ul;
157 ATF_CHECK(snprintf(buf, sizeof buf, " %.2f", u.d) != -1);
158 }
159 }
160
161 ATF_TC(sprintf_zeropad);
ATF_TC_HEAD(sprintf_zeropad,tc)162 ATF_TC_HEAD(sprintf_zeropad, tc)
163 {
164 atf_tc_set_md_var(tc, "descr",
165 "Test output format zero padding (PR lib/44113)");
166 }
167
ATF_TC_BODY(sprintf_zeropad,tc)168 ATF_TC_BODY(sprintf_zeropad, tc)
169 {
170 char str[1024];
171
172 ATF_CHECK(sprintf(str, "%010f", 0.0) == 10);
173 ATF_REQUIRE_STREQ(str, "000.000000");
174
175 /* ieeefp */
176 #ifndef __vax__
177 /* printf(3) should ignore zero padding for nan/inf */
178 ATF_CHECK(sprintf(str, "%010f", NAN) == 10);
179 ATF_REQUIRE_STREQ(str, " nan");
180 ATF_CHECK(sprintf(str, "%010f", INFINITY) == 10);
181 ATF_REQUIRE_STREQ(str, " inf");
182 #endif
183 }
184
185 ATF_TC(snprintf_double_a);
ATF_TC_HEAD(snprintf_double_a,tc)186 ATF_TC_HEAD(snprintf_double_a, tc)
187 {
188 atf_tc_set_md_var(tc, "descr", "Test printf a format");
189 }
190
ATF_TC_BODY(snprintf_double_a,tc)191 ATF_TC_BODY(snprintf_double_a, tc)
192 {
193 char buf[1000];
194
195 snprintf(buf, sizeof buf, "%.3a", (double)10.6);
196 ATF_CHECK_MSG((strcmp(buf, "0x1.533p+3") == 0 ||
197 strcmp(buf, "0x2.a66p+2") == 0 ||
198 strcmp(buf, "0x5.4cdp+1") == 0 ||
199 strcmp(buf, "0xa.99ap+0") == 0),
200 "buf=%s", buf);
201
202 snprintf(buf, sizeof buf, "%a", (double)0.125);
203 ATF_CHECK_MSG((strcmp(buf, "0x1p-3") == 0 ||
204 strcmp(buf, "0x2p-4") == 0 ||
205 strcmp(buf, "0x4p-5") == 0 ||
206 strcmp(buf, "0x8p-6") == 0),
207 "buf=%s", buf);
208 }
209
210 ATF_TC(snprintf_long_double_a);
ATF_TC_HEAD(snprintf_long_double_a,tc)211 ATF_TC_HEAD(snprintf_long_double_a, tc)
212 {
213 atf_tc_set_md_var(tc, "descr", "Test printf La format");
214 }
215
ATF_TC_BODY(snprintf_long_double_a,tc)216 ATF_TC_BODY(snprintf_long_double_a, tc)
217 {
218 char buf[1000];
219
220 snprintf(buf, sizeof buf, "%.3La", 10.6L);
221 ATF_CHECK_MSG((strcmp(buf, "0x1.533p+3") == 0 ||
222 strcmp(buf, "0x2.a66p+2") == 0 ||
223 strcmp(buf, "0x5.4cdp+1") == 0 ||
224 strcmp(buf, "0xa.99ap+0") == 0),
225 "buf=%s", buf);
226
227 snprintf(buf, sizeof buf, "%La", 0.125L);
228 ATF_CHECK_MSG((strcmp(buf, "0x1p-3") == 0 ||
229 strcmp(buf, "0x2p-4") == 0 ||
230 strcmp(buf, "0x4p-5") == 0 ||
231 strcmp(buf, "0x8p-6") == 0),
232 "buf=%s", buf);
233
234 /*
235 * Test case adapted from:
236 *
237 * https://mail-index.netbsd.org/tech-userlevel/2020/04/11/msg012329.html
238 */
239 #if LDBL_MAX_EXP >= 16384 && LDBL_MANT_DIG >= 64
240 snprintf(buf, sizeof buf, "%La", -0xc.ecececececececep+3788L);
241 ATF_CHECK_MSG((strcmp(buf, "-0x1.9d9d9d9d9d9d9d9cp+3791") == 0 ||
242 strcmp(buf, "-0x3.3b3b3b3b3b3b3b38p+3790") == 0 ||
243 strcmp(buf, "-0x6.7676767676767674p+3789") == 0 ||
244 strcmp(buf, "-0xc.ecececececececep+3788") == 0),
245 "buf=%s", buf);
246 #endif
247
248 #if LDBL_MAX_EXP >= 16384 && LDBL_MANT_DIG >= 113
249 snprintf(buf, sizeof buf, "%La",
250 -0x1.cecececececececececececececep+3791L);
251 ATF_CHECK_MSG((strcmp(buf,
252 "-0x1.cecececececececececececececep+3791") == 0 ||
253 strcmp(buf, "-0x3.3333333333333338p+3790") == 0 ||
254 strcmp(buf, "-0x6.767676767676767p+3789") == 0 ||
255 strcmp(buf, "-0xc.ecececececececep+3788") == 0),
256 "buf=%s", buf);
257 #endif
258 }
259
260 /* is "long double" and "double" different? */
261 #if (__LDBL_MANT_DIG__ != __DBL_MANT_DIG__) || \
262 (__LDBL_MAX_EXP__ != __DBL_MAX_EXP__)
263 #define WIDE_DOUBLE
264 #endif
265
266 #ifndef WIDE_DOUBLE
267 ATF_TC(pr57250_fix);
ATF_TC_HEAD(pr57250_fix,tc)268 ATF_TC_HEAD(pr57250_fix, tc)
269 {
270 atf_tc_set_md_var(tc, "descr", "Test for PR57250");
271 }
272
ATF_TC_BODY(pr57250_fix,tc)273 ATF_TC_BODY(pr57250_fix, tc)
274 {
275 char *eptr;
276 char buf[1000];
277 long double ld;
278
279 errno = 0;
280 ld = strtold("1e309", &eptr);
281 ATF_CHECK(errno != 0);
282 ld = (double)ld;
283 ATF_CHECK(isfinite(ld) == 0);
284 snprintf(buf, sizeof buf, "%Lf\n", ld);
285 ATF_REQUIRE_STREQ(buf, "inf\n");
286 }
287 #endif
288
289
ATF_TP_ADD_TCS(tp)290 ATF_TP_ADD_TCS(tp)
291 {
292
293 ATF_TP_ADD_TC(tp, snprintf_c99);
294 ATF_TP_ADD_TC(tp, snprintf_dotzero);
295 ATF_TP_ADD_TC(tp, snprintf_posarg);
296 ATF_TP_ADD_TC(tp, snprintf_posarg_width);
297 ATF_TP_ADD_TC(tp, snprintf_posarg_error);
298 ATF_TP_ADD_TC(tp, snprintf_float);
299 ATF_TP_ADD_TC(tp, sprintf_zeropad);
300 ATF_TP_ADD_TC(tp, snprintf_double_a);
301 ATF_TP_ADD_TC(tp, snprintf_long_double_a);
302 #ifndef WIDE_DOUBLE
303 ATF_TP_ADD_TC(tp, pr57250_fix);
304 #endif
305
306 return atf_no_error();
307 }
308