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
check(struct test * t,intmax_t rv,char * end,int rstatus)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
check_errno(int e)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);
ATF_TC_HEAD(strtoi_base,tc)110 ATF_TC_HEAD(strtoi_base, tc)
111 {
112           atf_tc_set_md_var(tc, "descr", "Test strtoi(3) with different bases");
113 }
114 
ATF_TC_BODY(strtoi_base,tc)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);
ATF_TC_HEAD(strtoi_case,tc)202 ATF_TC_HEAD(strtoi_case, tc)
203 {
204           atf_tc_set_md_var(tc, "descr", "Case insensitivity with strtoi(3)");
205 }
206 
ATF_TC_BODY(strtoi_case,tc)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);
ATF_TC_HEAD(strtoi_range,tc)247 ATF_TC_HEAD(strtoi_range, tc)
248 {
249           atf_tc_set_md_var(tc, "descr", "Test ERANGE from strtoi(3)");
250 }
251 
ATF_TC_BODY(strtoi_range,tc)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);
ATF_TC_HEAD(strtoi_range_trail,tc)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 
ATF_TC_BODY(strtoi_range_trail,tc)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);
ATF_TC_HEAD(strtoi_signed,tc)335 ATF_TC_HEAD(strtoi_signed, tc)
336 {
337           atf_tc_set_md_var(tc, "descr", "A basic test of strtoi(3)");
338 }
339 
ATF_TC_BODY(strtoi_signed,tc)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 
ATF_TP_ADD_TCS(tp)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