xref: /netbsd-src/tests/lib/libc/stdio/t_printf.c (revision c5c7b2d6d453b69d79e02ad032b1384f5e3b6012)
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