#include #include #include #include #include #include #define DIVLIMIT (UINTMAX_MAX / 10) #define MODLIMIT (UINTMAX_MAX % 10) static int digit_value(int c, int base) { assert('Z' - 'A' == 25); assert('z' - 'a' == 25); if ('0' <= c && c <= '9' && c - '0' < base) return (c - '0'); if ('A' <= c && c <= 'Z' && 10 + (c - 'A') < base) return 10 + (c - 'A'); if ('a' <= c && c <= 'z' && 10 + (c - 'a') < base) return 10 + (c - 'a'); return -1; } int strtounum(const char *s, int base, uintmax_t *result, size_t *consumed) { uintmax_t n; size_t con; int digit; assert(s != NULL); assert(2 <= base && base <= 36); con = 0; while (*s != '\0' && isspace((unsigned char)(*s))) { s++; con++; } if (*s == '\0') { errno = EINVAL; return -1; } n = 0; for (n = 0; (digit = digit_value(*s, base)) != -1; s++, con++) { if (n > DIVLIMIT || (n == DIVLIMIT && (unsigned int)digit > MODLIMIT)) { errno = ERANGE; return -1; } n = base * n + digit; } if (*s != '\0') { errno = EINVAL; return -1; } if (result != NULL) *result = n; if (consumed != NULL) *consumed = con; return 0; } #if defined(TEST_STRTOUNUM) int main(void) { int result; uintmax_t n; size_t con; assert(CHAR_BIT == 8); result = strtounum("1234567", 10, &n, &con); assert(result == 0); assert(n == 1234567UL); assert(con == 7); result = strtounum("12345a", 10, &n, &con); assert(result == -1); assert(errno == EINVAL); result = strtounum("abcd", 36, &n, &con); assert(result == 0); assert(n == ((((UINTMAX_C(0) + 10) * 36 + 11) * 36 + 12) * 36 + 13)); assert(con == 4); result = strtounum("1100101", 2, &n, &con); assert(result == 0); assert(n == 0x65); assert(con == 7); result = strtounum("012", 2, &n, &con); assert(result == -1); assert(errno == EINVAL); if (sizeof(uintmax_t) == 8) { /* 2^64 - 1 */ result = strtounum("18446744073709551615", 10, &n, &con); assert(result == 0); assert(n == UINTMAX_MAX); assert(con == 20); /* 2^64 */ result = strtounum("18446744073709551616", 10, &n, &con); assert(result == -1); assert(errno == ERANGE); /* A much too big number */ result = strtounum("1111111111111111111111111", 10, &n, &con); assert(result == -1); assert(errno == ERANGE); } else { assert(!"Unsupported bit width of type uintmax_t."); } return 0; } #endif