1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2026 Oxide Computer Company 14 */ 15 16 /* 17 * Test the implementation of strtonum(), strtonumx(), strtounum() and 18 * strtounumx() 19 */ 20 21 #include <errno.h> 22 #include <limits.h> 23 #include <stdbool.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 28 bool fail = false; 29 30 static void 31 runtestx(const char *nstr, long long minval, long long maxval, 32 int base, long long expval, bool expect_pass) 33 { 34 const char *errstr; 35 char key[0x100]; 36 long long val; 37 int n; 38 39 if (base == -1) { 40 val = strtonum(nstr, minval, maxval, &errstr); 41 n = snprintf(key, sizeof (key), "strtonum(%s)", nstr); 42 } else { 43 val = strtonumx(nstr, minval, maxval, &errstr, base); 44 n = snprintf(key, sizeof (key), "strtonumx(%s, %d)", 45 nstr, base); 46 } 47 if (n < 0 || (size_t)n >= sizeof (key)) { 48 fprintf(stderr, "FAIL could not build key for '%s'\n", nstr); 49 fail = true; 50 return; 51 } 52 53 if (expect_pass) { 54 if (errstr != NULL) { 55 fprintf(stderr, "FAIL %s was rejected: " 56 "%s [%lld,%lld]\n", 57 key, errstr, minval, maxval); 58 fail = true; 59 } else { 60 fprintf(stdout, "PASS %s was accepted: " 61 "[%lld,%lld] = %lld\n", 62 key, minval, maxval, val); 63 if (val != expval) { 64 fprintf(stderr, "FAIL %s returned wrong value: " 65 "[%lld,%lld] = %lld vs. %lld\n", key, 66 minval, maxval, val, expval); 67 fail = true; 68 } 69 } 70 } else { 71 if (errstr == NULL) { 72 fprintf(stderr, "FAIL %s was accepted: " 73 "[%lld,%lld] = %lld\n", 74 key, minval, maxval, val); 75 fail = true; 76 } else { 77 fprintf(stdout, "PASS %s was rejected: " 78 "%s [%lld,%lld]\n", 79 key, errstr, minval, maxval); 80 } 81 } 82 } 83 84 static void 85 runtest(const char *nstr, long long minval, long long maxval, long long expval, 86 bool expect_pass) 87 { 88 runtestx(nstr, minval, maxval, -1, expval, expect_pass); 89 runtestx(nstr, minval, maxval, 10, expval, expect_pass); 90 runtestx(nstr, minval, maxval, 0, expval, expect_pass); 91 } 92 93 static void 94 runtestux(const char *nstr, unsigned long long minval, 95 unsigned long long maxval, int base, unsigned long long expval, 96 bool expect_pass, const char *experr) 97 { 98 const char *errstr; 99 char key[0x100]; 100 unsigned long long val; 101 int n; 102 103 if (base == -1) { 104 val = strtounum(nstr, minval, maxval, &errstr); 105 n = snprintf(key, sizeof (key), "strtounum(%s)", nstr); 106 } else { 107 val = strtounumx(nstr, minval, maxval, &errstr, base); 108 n = snprintf(key, sizeof (key), "strtounumx(%s, %d)", 109 nstr, base); 110 } 111 if (n < 0 || (size_t)n >= sizeof (key)) { 112 fprintf(stderr, "FAIL could not build key for '%s'\n", nstr); 113 fail = true; 114 return; 115 } 116 117 if (expect_pass) { 118 if (errstr != NULL) { 119 fprintf(stderr, "FAIL %s was rejected: " 120 "%s [%llu,%llu]\n", 121 key, errstr, minval, maxval); 122 fail = true; 123 } else { 124 fprintf(stdout, "PASS %s was accepted: " 125 "[%llu,%llu] = %llu\n", 126 key, minval, maxval, val); 127 if (val != expval) { 128 fprintf(stderr, "FAIL %s returned wrong value: " 129 "[%llu,%llu] = %llu vs. %llu\n", key, 130 minval, maxval, val, expval); 131 fail = true; 132 } 133 } 134 } else { 135 if (errstr == NULL) { 136 fprintf(stderr, "FAIL %s was accepted: " 137 "[%llu,%llu] = %llu\n", 138 key, minval, maxval, val); 139 fail = true; 140 } else if (experr != NULL && strcmp(errstr, experr) != 0) { 141 fprintf(stderr, "FAIL %s wrong error: " 142 "'%s' vs. '%s' [%llu,%llu]\n", 143 key, errstr, experr, minval, maxval); 144 fail = true; 145 } else { 146 fprintf(stdout, "PASS %s was rejected: " 147 "%s [%llu,%llu]\n", 148 key, errstr, minval, maxval); 149 } 150 } 151 } 152 153 static void 154 runtestu(const char *nstr, unsigned long long minval, 155 unsigned long long maxval, unsigned long long expval, bool expect_pass, 156 const char *experr) 157 { 158 runtestux(nstr, minval, maxval, -1, expval, expect_pass, experr); 159 runtestux(nstr, minval, maxval, 10, expval, expect_pass, experr); 160 runtestux(nstr, minval, maxval, 0, expval, expect_pass, experr); 161 } 162 163 /* 164 * The unsigned functions must restore the caller's errno on success and 165 * set it appropriately on failure. 166 */ 167 static void 168 runtest_errno(void) 169 { 170 const char *errstr; 171 unsigned long long val; 172 173 errno = EDOM; 174 val = strtounum("5", 0, 10, &errstr); 175 if (errstr != NULL || errno != EDOM || val != 5) { 176 fprintf(stderr, "FAIL errno/value wrong on success: " 177 "errno %d vs. %d, value %llu vs. 5\n", errno, EDOM, val); 178 fail = true; 179 } else { 180 fprintf(stdout, "PASS errno preserved on success\n"); 181 } 182 183 errno = 0; 184 val = strtounum("11", 0, 10, &errstr); 185 if (errstr == NULL || errno != ERANGE || val != 0) { 186 fprintf(stderr, "FAIL errno/value wrong on range error: " 187 "errno %d, value %llu\n", errno, val); 188 fail = true; 189 } else { 190 fprintf(stdout, "PASS errno set to ERANGE on range error\n"); 191 } 192 193 errno = 0; 194 val = strtounum("zebra", 0, 10, &errstr); 195 if (errstr == NULL || errno != EINVAL || val != 0) { 196 fprintf(stderr, "FAIL errno/value wrong on invalid input: " 197 "errno %d, value %llu\n", errno, val); 198 fail = true; 199 } else { 200 fprintf(stdout, "PASS errno set to EINVAL on invalid input\n"); 201 } 202 } 203 204 int 205 main(void) 206 { 207 runtest("1", 0, 100, 1, true); 208 runtest("0", -3, 7, 0, true); 209 runtest("0", 2, 10, 0, false); 210 runtest("0", 2, LLONG_MAX, 0, false); 211 runtest("-2", 0, LLONG_MAX, 0, false); 212 runtest("0", -5, LLONG_MAX, 0, true); 213 runtest("-3", -3, LLONG_MAX, -3, true); 214 runtest("-2", 10, -1, 0, false); 215 runtest("-2", -10, -1, -2, true); 216 runtest("-20", -10, -1, 0, false); 217 runtest("20", -10, -1, 0, false); 218 219 runtest("-9223372036854775808", LLONG_MIN, LLONG_MAX, LLONG_MIN, true); 220 runtest("-9223372036854775809", LLONG_MIN, LLONG_MAX, 0, false); 221 222 runtest("9223372036854775807", LLONG_MIN, LLONG_MAX, LLONG_MAX, true); 223 runtest("9223372036854775808", LLONG_MIN, LLONG_MAX, 0, false); 224 225 for (int base = 2; base <= 36; base++) 226 runtestx("1", 0, 100, base, 1, true); 227 228 runtestx("1", 0, 100, -2, 0, false); 229 runtestx("1", 0, 100, 1, 0, false); 230 runtestx("1", 0, 100, 37, 0, false); 231 232 runtestx("0x1234", 0, LLONG_MAX, 16, 0x1234, true); 233 runtestx("0x1234", 0, LLONG_MAX, 0, 0x1234, true); 234 runtestx("0x1234", 0, LLONG_MAX, 10, 0, false); 235 236 runtestx("AZbc123", 0, LLONG_MAX, 36, 23903176539, true); 237 runtestx("AZbc123", 0, LLONG_MAX, 35, 0, false); 238 runtestx("AYBC123", 0, LLONG_MAX, 35, 20185422673, true); 239 runtestx("AYBC123", 0, LLONG_MAX, 34, 0, false); 240 241 runtestx("01234", 0, LLONG_MAX, 8, 01234, true); 242 runtestx("01234", 0, LLONG_MAX, 0, 01234, true); 243 runtestx("01234", 0, LLONG_MAX, 10, 1234, true); 244 245 /* Unsigned variants */ 246 runtestu("1", 0, 100, 1, true, NULL); 247 runtestu("0", 0, 100, 0, true, NULL); 248 runtestu("+42", 0, 100, 42, true, NULL); 249 runtestu("0", 2, 10, 0, false, "too small"); 250 runtestu("11", 0, 10, 0, false, "too large"); 251 runtestu("5", 10, 2, 0, false, "invalid"); 252 runtestu("", 0, 100, 0, false, "invalid"); 253 runtestu("zebra", 0, 100, 0, false, "invalid"); 254 255 /* Values beyond the signed 64-bit range must work */ 256 runtestu("9223372036854775808", 0, ULLONG_MAX, 257 9223372036854775808ULL, true, NULL); 258 runtestu("18446744073709551615", 0, ULLONG_MAX, ULLONG_MAX, true, 259 NULL); 260 runtestu("18446744073709551616", 0, ULLONG_MAX, 0, false, 261 "too large"); 262 runtestu("18446744073709551615", 0, 100, 0, false, "too large"); 263 264 /* Negative input is always rejected, never wrapped */ 265 runtestu("-1", 0, ULLONG_MAX, 0, false, "too small"); 266 runtestu("-0", 0, ULLONG_MAX, 0, false, "too small"); 267 runtestu(" -5", 0, ULLONG_MAX, 0, false, "too small"); 268 runtestu("-18446744073709551615", 0, ULLONG_MAX, 0, false, 269 "too small"); 270 271 /* Malformed minus-prefixed input is invalid, not out of range */ 272 runtestu("-", 0, ULLONG_MAX, 0, false, "invalid"); 273 runtestu(" -", 0, ULLONG_MAX, 0, false, "invalid"); 274 runtestu("-xyz", 0, ULLONG_MAX, 0, false, "invalid"); 275 runtestu("- 5", 0, ULLONG_MAX, 0, false, "invalid"); 276 runtestu("--5", 0, ULLONG_MAX, 0, false, "invalid"); 277 runtestu("-5x", 0, ULLONG_MAX, 0, false, "invalid"); 278 279 /* Base handling */ 280 for (int base = 2; base <= 36; base++) 281 runtestux("1", 0, 100, base, 1, true, NULL); 282 283 runtestux("1", 0, 100, -2, 0, false, NULL); 284 runtestux("1", 0, 100, 1, 0, false, NULL); 285 runtestux("1", 0, 100, 37, 0, false, NULL); 286 287 runtestux("0x20000000", 0, ULLONG_MAX, 0, 0x20000000ULL, true, NULL); 288 runtestux("0x20000000", 0, ULLONG_MAX, 16, 0x20000000ULL, true, NULL); 289 runtestux("0xffffffffffffffff", 0, ULLONG_MAX, 16, ULLONG_MAX, true, 290 NULL); 291 runtestux("0x1234", 0, ULLONG_MAX, 10, 0, false, "invalid"); 292 runtestux("01234", 0, ULLONG_MAX, 0, 01234, true, NULL); 293 294 runtest_errno(); 295 296 if (fail) { 297 printf("\nOverall status: FAIL\n"); 298 return (EXIT_FAILURE); 299 } 300 301 printf("\nOverall status: PASS\n"); 302 return (EXIT_SUCCESS); 303 } 304