#include "stdlib_p.h" #include #include #include #include int __strto_int(struct __scanf_input *input, int base, long *outl, long long *outll, bool use_unsigned) { /* Skip initial whitespace */ while(isspace(__scanf_peek(input))) __scanf_in(input); /* Accept a sign character */ bool negative = false; int sign = __scanf_peek(input); if(sign == '-') negative = true; if(sign == '-' || sign == '+') __scanf_in(input); /* Use unsigned variables as only these have defined overflow */ unsigned long xl = 0; unsigned long long xll = 0; int errno_value = 0; bool valid = false; /* Read prefixes and determine base */ if(__scanf_peek(input) == '0') { __scanf_in(input); if((base == 0 || base == 16) && tolower(__scanf_peek(input)) == 'x') { __scanf_in(input); base = 16; } /* If we don't consume the x then count the 0 as a digit */ else valid = true; if(base == 0) base = 8; } if(base == 0) base = 10; /* Read digits */ while(1) { int v = -1; int c = __scanf_peek(input); if(isdigit(c)) v = c - '0'; if(islower(c)) v = c - 'a' + 10; if(v == -1 || v >= base) break; /* The value is valid as long as there is at least one digit */ valid = true; /* (x = base*x + v) but with overflow checks */ /* TODO: strto_int: We might fail to represent [L]LONG_MIN */ if(outl) { if(__builtin_umull_overflow(xl, base, &xl)) errno_value = ERANGE; if(__builtin_uaddl_overflow(xl, v, &xl)) errno_value = ERANGE; } if(outll) { if(__builtin_umulll_overflow(xll, base, &xll)) errno_value = ERANGE; if(__builtin_uaddll_overflow(xll, v, &xll)) errno_value = ERANGE; } __scanf_in(input); } /* Handle sign and range */ if(negative) { /* Only -0 can be represented as unsigned */ if(outl && use_unsigned && xl != 0) errno_value = ERANGE; if(outll && use_unsigned && xll != 0) errno_value = ERANGE; /* Handle signed range. Don't use -[L]LONG_MIN, it overflows */ if(outl && !use_unsigned && xl > LONG_MAX * 1ul + 1) errno_value = ERANGE; if(outll && !use_unsigned && xll > LLONG_MAX * 1ull + 1) errno_value = ERANGE; xl = -xl; xll = -xll; } else { if(outl && !use_unsigned && xl > LONG_MAX * 1ul) errno_value = ERANGE; if(outll && !use_unsigned && xll > LLONG_MAX * 1ull) errno_value = ERANGE; } /* Handle overflows */ if(outl && errno_value == ERANGE) { if(use_unsigned) xl = ULONG_MAX; else xl = negative ? LONG_MIN : LONG_MAX; } if(outll && errno_value == ERANGE) { if(use_unsigned) xll = ULLONG_MAX; else xll = negative ? LLONG_MIN : LLONG_MAX; } if(outl) *outl = xl; if(outll) *outll = xll; return valid ? errno_value : EINVAL; }