Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

tools/nolibc: implement strtol() and friends

The implementation always works on uintmax_t values.

This is inefficient when only 32bit are needed.
However for all functions this only happens for strtol() on 32bit
platforms.

Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
Acked-by: Willy Tarreau <w@1wt.eu>
Link: https://lore.kernel.org/r/20240425-nolibc-strtol-v1-2-bfeef7846902@weissschuh.net

+170
+109
tools/include/nolibc/stdlib.h
··· 438 438 return itoa_buffer; 439 439 } 440 440 441 + static __attribute__((unused)) 442 + uintmax_t __strtox(const char *nptr, char **endptr, int base, intmax_t lower_limit, uintmax_t upper_limit) 443 + { 444 + const char signed_ = lower_limit != 0; 445 + unsigned char neg = 0, overflow = 0; 446 + uintmax_t val = 0, limit, old_val; 447 + char c; 448 + 449 + if (base < 0 || base > 36) { 450 + SET_ERRNO(EINVAL); 451 + goto out; 452 + } 453 + 454 + while (isspace(*nptr)) 455 + nptr++; 456 + 457 + if (*nptr == '+') { 458 + nptr++; 459 + } else if (*nptr == '-') { 460 + neg = 1; 461 + nptr++; 462 + } 463 + 464 + if (signed_ && neg) 465 + limit = -(uintmax_t)lower_limit; 466 + else 467 + limit = upper_limit; 468 + 469 + if ((base == 0 || base == 16) && 470 + (strncmp(nptr, "0x", 2) == 0 || strncmp(nptr, "0X", 2) == 0)) { 471 + base = 16; 472 + nptr += 2; 473 + } else if (base == 0 && strncmp(nptr, "0", 1) == 0) { 474 + base = 8; 475 + nptr += 1; 476 + } else if (base == 0) { 477 + base = 10; 478 + } 479 + 480 + while (*nptr) { 481 + c = *nptr; 482 + 483 + if (c >= '0' && c <= '9') 484 + c -= '0'; 485 + else if (c >= 'a' && c <= 'z') 486 + c = c - 'a' + 10; 487 + else if (c >= 'A' && c <= 'Z') 488 + c = c - 'A' + 10; 489 + else 490 + goto out; 491 + 492 + if (c >= base) 493 + goto out; 494 + 495 + nptr++; 496 + old_val = val; 497 + val *= base; 498 + val += c; 499 + 500 + if (val > limit || val < old_val) 501 + overflow = 1; 502 + } 503 + 504 + out: 505 + if (overflow) { 506 + SET_ERRNO(ERANGE); 507 + val = limit; 508 + } 509 + if (endptr) 510 + *endptr = (char *)nptr; 511 + return neg ? -val : val; 512 + } 513 + 514 + static __attribute__((unused)) 515 + long strtol(const char *nptr, char **endptr, int base) 516 + { 517 + return __strtox(nptr, endptr, base, LONG_MIN, LONG_MAX); 518 + } 519 + 520 + static __attribute__((unused)) 521 + unsigned long strtoul(const char *nptr, char **endptr, int base) 522 + { 523 + return __strtox(nptr, endptr, base, 0, ULONG_MAX); 524 + } 525 + 526 + static __attribute__((unused)) 527 + long long strtoll(const char *nptr, char **endptr, int base) 528 + { 529 + return __strtox(nptr, endptr, base, LLONG_MIN, LLONG_MAX); 530 + } 531 + 532 + static __attribute__((unused)) 533 + unsigned long long strtoull(const char *nptr, char **endptr, int base) 534 + { 535 + return __strtox(nptr, endptr, base, 0, ULLONG_MAX); 536 + } 537 + 538 + static __attribute__((unused)) 539 + intmax_t strtoimax(const char *nptr, char **endptr, int base) 540 + { 541 + return __strtox(nptr, endptr, base, INTMAX_MIN, INTMAX_MAX); 542 + } 543 + 544 + static __attribute__((unused)) 545 + uintmax_t strtoumax(const char *nptr, char **endptr, int base) 546 + { 547 + return __strtox(nptr, endptr, base, 0, UINTMAX_MAX); 548 + } 549 + 441 550 /* make sure to include all global symbols */ 442 551 #include "nolibc.h" 443 552
+61
tools/testing/selftests/nolibc/nolibc-test.c
··· 621 621 return 0; 622 622 } 623 623 624 + #define EXPECT_STRTOX(cond, func, input, base, expected, chars, expected_errno) \ 625 + do { if (!(cond)) result(llen, SKIPPED); else ret += expect_strtox(llen, func, input, base, expected, chars, expected_errno); } while (0) 626 + 627 + static __attribute__((unused)) 628 + int expect_strtox(int llen, void *func, const char *input, int base, intmax_t expected, int expected_chars, int expected_errno) 629 + { 630 + char *endptr; 631 + int actual_errno, actual_chars; 632 + intmax_t r; 633 + 634 + errno = 0; 635 + if (func == strtol) { 636 + r = strtol(input, &endptr, base); 637 + } else if (func == strtoul) { 638 + r = strtoul(input, &endptr, base); 639 + } else { 640 + result(llen, FAIL); 641 + return 1; 642 + } 643 + actual_errno = errno; 644 + actual_chars = endptr - input; 645 + 646 + llen += printf(" %lld = %lld", (long long)expected, (long long)r); 647 + if (r != expected) { 648 + result(llen, FAIL); 649 + return 1; 650 + } 651 + if (expected_chars == -1) { 652 + if (*endptr != '\0') { 653 + result(llen, FAIL); 654 + return 1; 655 + } 656 + } else if (expected_chars != actual_chars) { 657 + result(llen, FAIL); 658 + return 1; 659 + } 660 + if (actual_errno != expected_errno) { 661 + result(llen, FAIL); 662 + return 1; 663 + } 664 + 665 + result(llen, OK); 666 + return 0; 667 + } 668 + 624 669 /* declare tests based on line numbers. There must be exactly one test per line. */ 625 670 #define CASE_TEST(name) \ 626 671 case __LINE__: llen += printf("%d %s", test, #name); ··· 1188 1143 CASE_TEST(limit_ptrdiff_min); EXPECT_EQ(1, PTRDIFF_MIN, sizeof(long) == 8 ? (ptrdiff_t) 0x8000000000000000LL : (ptrdiff_t) 0x80000000); break; 1189 1144 CASE_TEST(limit_ptrdiff_max); EXPECT_EQ(1, PTRDIFF_MAX, sizeof(long) == 8 ? (ptrdiff_t) 0x7fffffffffffffffLL : (ptrdiff_t) 0x7fffffff); break; 1190 1145 CASE_TEST(limit_size_max); EXPECT_EQ(1, SIZE_MAX, sizeof(long) == 8 ? (size_t) 0xffffffffffffffffULL : (size_t) 0xffffffffU); break; 1146 + CASE_TEST(strtol_simple); EXPECT_STRTOX(1, strtol, "35", 10, 35, -1, 0); break; 1147 + CASE_TEST(strtol_positive); EXPECT_STRTOX(1, strtol, "+35", 10, 35, -1, 0); break; 1148 + CASE_TEST(strtol_negative); EXPECT_STRTOX(1, strtol, "-35", 10, -35, -1, 0); break; 1149 + CASE_TEST(strtol_hex_auto); EXPECT_STRTOX(1, strtol, "0xFF", 0, 255, -1, 0); break; 1150 + CASE_TEST(strtol_base36); EXPECT_STRTOX(1, strtol, "12yZ", 36, 50507, -1, 0); break; 1151 + CASE_TEST(strtol_cutoff); EXPECT_STRTOX(1, strtol, "1234567890", 8, 342391, 7, 0); break; 1152 + CASE_TEST(strtol_octal_auto); EXPECT_STRTOX(1, strtol, "011", 0, 9, -1, 0); break; 1153 + CASE_TEST(strtol_hex_00); EXPECT_STRTOX(1, strtol, "0x00", 16, 0, -1, 0); break; 1154 + CASE_TEST(strtol_hex_FF); EXPECT_STRTOX(1, strtol, "FF", 16, 255, -1, 0); break; 1155 + CASE_TEST(strtol_hex_ff); EXPECT_STRTOX(1, strtol, "ff", 16, 255, -1, 0); break; 1156 + CASE_TEST(strtol_hex_prefix); EXPECT_STRTOX(1, strtol, "0xFF", 16, 255, -1, 0); break; 1157 + CASE_TEST(strtol_trailer); EXPECT_STRTOX(1, strtol, "35foo", 10, 35, 2, 0); break; 1158 + CASE_TEST(strtol_overflow); EXPECT_STRTOX(1, strtol, "0x8000000000000000", 16, LONG_MAX, -1, ERANGE); break; 1159 + CASE_TEST(strtol_underflow); EXPECT_STRTOX(1, strtol, "-0x8000000000000001", 16, LONG_MIN, -1, ERANGE); break; 1160 + CASE_TEST(strtoul_negative); EXPECT_STRTOX(1, strtoul, "-0x1", 16, ULONG_MAX, 4, 0); break; 1161 + CASE_TEST(strtoul_overflow); EXPECT_STRTOX(1, strtoul, "0x10000000000000000", 16, ULONG_MAX, -1, ERANGE); break; 1191 1162 1192 1163 case __LINE__: 1193 1164 return ret; /* must be last */