xref: /netbsd-src/tests/lib/libc/stdlib/t_strtoi.c (revision 5ed3745558946d55fda3f071139ba4ab25c67c4e)
1 /*	$NetBSD: t_strtoi.c,v 1.5 2024/07/24 09:26:06 kre Exp $	*/
2 
3 /*-
4  * Copyright (c) 2015 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Jukka Ruohonen.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * Created by Kamil Rytarowski, based on ID:
34  * NetBSD: t_strtol.c,v 1.5 2011/06/14 02:45:58 jruoho Exp
35  */
36 
37 #include <sys/cdefs.h>
38 __RCSID("$NetBSD: t_strtoi.c,v 1.5 2024/07/24 09:26:06 kre Exp $");
39 
40 #include <atf-c.h>
41 #include <errno.h>
42 #include <inttypes.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <limits.h>
46 
47 struct test {
48 	const char	*str;
49 	intmax_t	 res;
50 	int		 base;
51 	const char	*end;
52 	intmax_t	 lo;
53 	intmax_t	 hi;
54 	int		 rstatus;
55 };
56 
57 static void	check(struct test *, intmax_t, char *, int);
58 
59 static void
60 check(struct test *t, intmax_t rv, char *end, int rstatus)
61 {
62 
63 	if (rv != t->res)
64 		atf_tc_fail_nonfatal("strtoi(\"%s\", &end, %d, %jd, %jd, "
65 		    "&rstatus) failed (rv = %jd)", t->str, t->base,
66 		    t->lo, t->hi, rv);
67 
68 	if (rstatus != t->rstatus) {
69 		char *emsg;
70 
71 		if (rstatus != 0) {
72 			emsg = strerror(rstatus);
73 			if (emsg != NULL) {
74 				emsg = strdup(emsg);
75 				if (emsg == NULL) {
76 					atf_tc_fail("Out of Memory");
77 					return;
78 				}
79 			}
80 		} else
81 			emsg = NULL;
82 
83 		atf_tc_fail_nonfatal("strtoi(\"%s\", &end, %d, %jd, %jd, &rstatus)"
84 		    " failed (rstatus: %d %s%s%sexpected %d%s%s%s)",
85 		    t->str, t->base, t->lo, t->hi, rstatus, rstatus ? "('" : "",
86 		    emsg != NULL ? emsg : "", rstatus ? "') " : "", t->rstatus,
87 		    t->rstatus ? " ('" : "", t->rstatus ? strerror(t->rstatus)
88 		    : "", t->rstatus ? "')" : "");
89 
90 		free(emsg);
91 	}
92 
93 	if ((t->end != NULL && strcmp(t->end, end) != 0) ||
94 	    (t->end == NULL && *end != '\0'))
95 		atf_tc_fail_nonfatal("invalid end pointer ('%s') from "
96 		    "strtoi(\"%s\", &end, %d, %jd, %jd, &rstatus), "
97 		    "expected '%s'", end, t->str, t->base, t->lo, t->hi,
98 		     t->end != NULL ? t->end : "\\0");
99 }
100 
101 static void
102 check_errno(int e)
103 {
104 	if (e != 0)
105 		atf_tc_fail("strtoi(3) changed errno to %d ('%s')",
106 			            e, strerror(e));
107 }
108 
109 ATF_TC(strtoi_base);
110 ATF_TC_HEAD(strtoi_base, tc)
111 {
112 	atf_tc_set_md_var(tc, "descr", "Test strtoi(3) with different bases");
113 }
114 
115 ATF_TC_BODY(strtoi_base, tc)
116 {
117 	struct test t[] = {
118 		{ "123456789",                  123456789,	0,	NULL,
119 		  INTMAX_MIN,	INTMAX_MAX,	0	},
120 		{ "111010110111100110100010101",123456789,	2,	NULL,
121 		  INTMAX_MIN,	INTMAX_MAX,	0	},
122 		{ "22121022020212200",          123456789,	3,	NULL,
123 		  INTMAX_MIN,	INTMAX_MAX,	0	},
124 		{ "13112330310111",	        123456789,	4,	NULL,
125 		  INTMAX_MIN,	INTMAX_MAX,	0	},
126 		{ "223101104124",               123456789,	5,	NULL,
127 		  INTMAX_MIN,	INTMAX_MAX,	0	},
128 		{ "20130035113",                123456789,	6,	NULL,
129 		  INTMAX_MIN,	INTMAX_MAX,	0	},
130 		{ "3026236221",	                123456789,	7,	NULL,
131 		  INTMAX_MIN,	INTMAX_MAX,	0	},
132 		{ "726746425",                  123456789,	8,	NULL,
133 		  INTMAX_MIN,	INTMAX_MAX,	0	},
134 		{ "277266780",                  123456789,	9,	NULL,
135 		  INTMAX_MIN,	INTMAX_MAX,	0	},
136 		{ "123456789",                  123456789,	10,	NULL,
137 		  INTMAX_MIN,	INTMAX_MAX,	0	},
138 		{ "63762A05",                   123456789,	11,	NULL,
139 		  INTMAX_MIN,	INTMAX_MAX,	0	},
140 		{ "35418A99",                   123456789,	12,	NULL,
141 		  INTMAX_MIN,	INTMAX_MAX,	0	},
142 		{ "1C767471",                   123456789,	13,	NULL,
143 		  INTMAX_MIN,	INTMAX_MAX,	0	},
144 		{ "12579781",                   123456789,	14,	NULL,
145 		  INTMAX_MIN,	INTMAX_MAX,	0	},
146 		{ "AC89BC9",                    123456789,	15,	NULL,
147 		  INTMAX_MIN,	INTMAX_MAX,	0	},
148 		{ "75BCD15",                    123456789,	16,	NULL,
149 		  INTMAX_MIN,	INTMAX_MAX,	0	},
150 		{ "1234567",                       342391,	8,	NULL,
151 		  INTMAX_MIN,	INTMAX_MAX,	0	},
152 		{ "01234567",                      342391,	0,	NULL,
153 		  INTMAX_MIN,	INTMAX_MAX,	0	},
154 		{ "0123456789",                 123456789,	10,	NULL,
155 		  INTMAX_MIN,	INTMAX_MAX,	0	},
156 		{ "0x75bcd15",                  123456789,	0,	NULL,
157 		  INTMAX_MIN,	INTMAX_MAX,	0	},
158 	};
159 	struct test f[] = {
160 		{ "1",                                  0,	1,	"1",
161 		  INTMAX_MIN,	INTMAX_MAX,	EINVAL	},
162 		{ "2",                                  0,	-1,	"2",
163 		  INTMAX_MIN,	INTMAX_MAX,	EINVAL	},
164 		{ "3",                                  0,	37,	"3",
165 		  INTMAX_MIN,	INTMAX_MAX,	EINVAL	},
166 		{ "4",                                  0,	-1,	"4",
167 		  INTMAX_MIN,	INTMAX_MAX,	EINVAL	},
168 		{ "0x",                                  0,	 0,	"x",
169 		  INTMAX_MIN,	INTMAX_MAX,	ENOTSUP	},
170 	};
171 
172 	intmax_t rv;
173 	char *end;
174 	int e;
175 	size_t i;
176 
177 	for (i = 0; i < __arraycount(t); i++) {
178 
179 		errno = 0;
180 		rv = strtoi(t[i].str, &end, t[i].base, t[i].lo, t[i].hi, &e);
181 
182 		check_errno(errno);
183 
184 		check(&t[i], rv, end, e);
185 	}
186 
187 	for (i = 0; i < __arraycount(f); i++) {
188 
189 		end = NULL;
190 		errno = 0;
191 		e = -99;
192 
193 		rv = strtoi(f[i].str, &end, f[i].base, f[i].lo, f[i].hi, &e);
194 
195 		check_errno(errno);
196 
197 		check(&f[i], rv, end, e);
198 	}
199 }
200 
201 ATF_TC(strtoi_case);
202 ATF_TC_HEAD(strtoi_case, tc)
203 {
204 	atf_tc_set_md_var(tc, "descr", "Case insensitivity with strtoi(3)");
205 }
206 
207 ATF_TC_BODY(strtoi_case, tc)
208 {
209 	struct test t[] = {
210 		{ "abcd",	0xabcd,	16,	NULL,
211 		  INTMAX_MIN,	INTMAX_MAX,	0	},
212 		{ "     dcba",	0xdcba,	16,	NULL,
213 		  INTMAX_MIN,	INTMAX_MAX,	0	},
214 		{ "abcd dcba",	0xabcd,	16,	" dcba",
215 		  INTMAX_MIN,	INTMAX_MAX,	ENOTSUP	},
216 		{ "abc0x123",	0xabc0, 16,	"x123",
217 		  INTMAX_MIN,	INTMAX_MAX,	ENOTSUP	},
218 		{ "abcd\0x123",	0xabcd, 16,	"\0x123",
219 		  INTMAX_MIN,	INTMAX_MAX,	0	},
220 		{ "ABCD",	0xabcd, 16,	NULL,
221 		  INTMAX_MIN,	INTMAX_MAX,	0	},
222 		{ "aBcD",	0xabcd, 16,	NULL,
223 		  INTMAX_MIN,	INTMAX_MAX,	0	},
224 		{ "0xABCD",	0xabcd, 16,	NULL,
225 		  INTMAX_MIN,	INTMAX_MAX,	0	},
226 		{ "0xABCDX",	0xabcd, 16,	"X",
227 		  INTMAX_MIN,	INTMAX_MAX,	ENOTSUP},
228 	};
229 
230 	intmax_t rv;
231 	char *end;
232 	int e;
233 	size_t i;
234 
235 	for (i = 0; i < __arraycount(t); i++) {
236 
237 		errno = 0;
238 		rv = strtoi(t[i].str, &end, t[i].base, t[i].lo, t[i].hi, &e);
239 
240 		check_errno(errno);
241 
242 		check(&t[i], rv, end, e);
243 	}
244 }
245 
246 ATF_TC(strtoi_range);
247 ATF_TC_HEAD(strtoi_range, tc)
248 {
249 	atf_tc_set_md_var(tc, "descr", "Test ERANGE from strtoi(3)");
250 }
251 
252 ATF_TC_BODY(strtoi_range, tc)
253 {
254 	struct test t[] = {
255 #if INTMAX_MAX == 0x7fffffffffffffff
256 		{ "1000000000000000000000", INTMAX_MAX, 8, NULL,
257 		  INTMAX_MIN,	INTMAX_MAX,	ERANGE },
258 		{ "9223372036854775808",    INTMAX_MAX, 10, NULL,
259 		  INTMAX_MIN,	INTMAX_MAX,	ERANGE },
260 		{ "8000000000000000",       INTMAX_MAX, 16, NULL,
261 		  INTMAX_MIN,	INTMAX_MAX,	ERANGE },
262 #else
263 #error extend this test to your platform!
264 #endif
265 		{ "10",		 1,	10,	NULL,
266 		  -1,	 1,	ERANGE			},
267 		{ "10",		11,	10,	NULL,
268 		  11,	20,	ERANGE			},
269 		{ "7",		 7,	0,	NULL,
270 		   7,	 7,	0			},
271 		{ "6",		 7,	0,	NULL,
272 		   7,	 7,	ERANGE			},
273 		{ "8",		 7,	0,	NULL,
274 		   7,	 7,	ERANGE			},
275 		{ "7x",		 7,	0,	"x",
276 		   7,	 7,	ENOTSUP			},
277 		{ "8x",		 7,	0,	"x",
278 		   7,	 7,	ERANGE			},
279 		{ "Z",		11,	10,	"Z",
280 		  11,	20,	ECANCELED		},
281 	};
282 
283 	intmax_t rv;
284 	char *end;
285 	int e;
286 	size_t i;
287 
288 	for (i = 0; i < __arraycount(t); i++) {
289 
290 		errno = 0;
291 		rv = strtoi(t[i].str, &end, t[i].base, t[i].lo, t[i].hi, &e);
292 
293 		if (errno != 0)
294 			atf_tc_fail("Range test %zd set errno=%d", i, errno);
295 		check_errno(errno);
296 
297 		check(&t[i], rv, end, e);
298 	}
299 }
300 
301 ATF_TC(strtoi_range_trail);
302 ATF_TC_HEAD(strtoi_range_trail, tc)
303 {
304 	atf_tc_set_md_var(tc, "descr", "Test ERANGE from strtoi(3) "
305 		"with trailing characters");
306 }
307 
308 ATF_TC_BODY(strtoi_range_trail, tc)
309 {
310 	struct test t[] = {
311 		{ "11x",    9, 10, "x",  0, 9, ERANGE },
312 		{ " -3y",  -2, 10, "y", -2, 1, ERANGE },
313 		{ "11111z", 9, 10, "z",  0, 9, ERANGE },
314 		{ "+0xAq",  9, 16, "q",  0, 9, ERANGE },
315 		{ "-0xBAr", 0, 16, "r",  0, 9, ERANGE },
316 	};
317 
318 	intmax_t rv;
319 	char *end;
320 	int e;
321 	size_t i;
322 
323 	for (i = 0; i < __arraycount(t); i++) {
324 
325 		errno = 0;
326 		rv = strtoi(t[i].str, &end, t[i].base, t[i].lo, t[i].hi, &e);
327 
328 		check_errno(errno);
329 
330 		check(&t[i], rv, end, e);
331 	}
332 }
333 
334 ATF_TC(strtoi_signed);
335 ATF_TC_HEAD(strtoi_signed, tc)
336 {
337 	atf_tc_set_md_var(tc, "descr", "A basic test of strtoi(3)");
338 }
339 
340 ATF_TC_BODY(strtoi_signed, tc)
341 {
342 	struct test t[] = {
343 		{ "1",		 1, 0, NULL,
344 		  INTMAX_MIN,	INTMAX_MAX,	0 },
345 		{ " 2",		 2, 0, NULL,
346 		  INTMAX_MIN,	INTMAX_MAX,	0 },
347 		{ "  3",	 3, 0, NULL,
348 		  INTMAX_MIN,	INTMAX_MAX,	0 },
349 		{ " -3",	-3, 0, NULL,
350 		  INTMAX_MIN,	INTMAX_MAX,	0 },
351 		{ "--1",	 0, 0, "--1",
352 		  INTMAX_MIN,	INTMAX_MAX,	ECANCELED },
353 		{ "+-2",	 0, 0, "+-2",
354 		  INTMAX_MIN,	INTMAX_MAX,	ECANCELED },
355 		{ "++3",	 0, 0, "++3",
356 		  INTMAX_MIN,	INTMAX_MAX,	ECANCELED },
357 		{ "+9",		 9, 0, NULL,
358 		  INTMAX_MIN,	INTMAX_MAX,	0 },
359 		{ "+123",      123, 0, NULL,
360 		  INTMAX_MIN,	INTMAX_MAX,	0 },
361 		{ "-1 3",       -1, 0, " 3",
362 		  INTMAX_MIN,	INTMAX_MAX,	ENOTSUP },
363 		{ "-1.3",       -1, 0, ".3",
364 		  INTMAX_MIN,	INTMAX_MAX,	ENOTSUP },
365 		{ "-  3",        0, 0, "-  3",
366 		  INTMAX_MIN,	INTMAX_MAX,	ECANCELED },
367 		{ "+33.",       33, 0, ".",
368 		  INTMAX_MIN,	INTMAX_MAX,	ENOTSUP },
369 		{ "30x0",       30, 0, "x0",
370 		  INTMAX_MIN,	INTMAX_MAX,	ENOTSUP },
371 	};
372 
373 	intmax_t rv;
374 	char *end;
375 	int e;
376 	size_t i;
377 
378 	for (i = 0; i < __arraycount(t); i++) {
379 
380 		errno = 0;
381 		rv = strtoi(t[i].str, &end, t[i].base, t[i].lo, t[i].hi, &e);
382 
383 		check_errno(errno);
384 
385 		check(&t[i], rv, end, e);
386 	}
387 }
388 
389 ATF_TP_ADD_TCS(tp)
390 {
391 
392 	ATF_TP_ADD_TC(tp, strtoi_base);
393 	ATF_TP_ADD_TC(tp, strtoi_case);
394 	ATF_TP_ADD_TC(tp, strtoi_range);
395 	ATF_TP_ADD_TC(tp, strtoi_range_trail);
396 	ATF_TP_ADD_TC(tp, strtoi_signed);
397 
398 	return atf_no_error();
399 }
400