/* * See parse_integer(). * * Individual dispatch functions in this file aren't supposed to be used * directly and thus aren't advertised and documented despited being exported. * * Do not use any function in this file for any reason. */ #include #include #include #include #include #include #include const char *_parse_integer_fixup_radix(const char *s, unsigned int *base) { if (*base == 0) { if (s[0] == '0') { if (_tolower(s[1]) == 'x' && isxdigit(s[2])) *base = 16; else *base = 8; } else *base = 10; } if (*base == 16 && s[0] == '0' && _tolower(s[1]) == 'x') s += 2; BUG_ON(*base < 2 || *base > 16); return s; } static int ___parse_integer(const char *s, unsigned int base, unsigned long long *val) { const char *s0 = s, *sd; unsigned long long acc; s = sd = _parse_integer_fixup_radix(s0, &base); acc = 0; while (*s) { unsigned int d; if ('0' <= *s && *s <= '9') d = *s - '0'; else if ('a' <= _tolower(*s) && _tolower(*s) <= 'f') d = _tolower(*s) - 'a' + 10; else break; if (d >= base) break; /* Overflow can't happen early enough. */ if ((acc >> 60) && acc > div_u64(ULLONG_MAX - d, base)) return -ERANGE; acc = acc * base + d; s++; } /* At least one digit has to be converted. */ if (s == sd) return -EINVAL; *val = acc; /* Radix 1 is not supported otherwise returned length can overflow. */ return s - s0; } static int __parse_integer(const char *s, unsigned int base, unsigned long long *val) { unsigned long long tmp; int rv; rv = ___parse_integer(s, base & ~PARSE_INTEGER_NEWLINE, &tmp); if (rv < 0) return rv; if (base & PARSE_INTEGER_NEWLINE) { /* Accept "integer\0" or "integer\n\0" */ s += rv; if (*s == '\n') s++; if (*s) return -EINVAL; } *val = tmp; return rv; } int _parse_integer_ull(const char *s, unsigned int base, unsigned long long *val) { char sign; int rv; sign = 0; if (*s == '-') return -EINVAL; else if (*s == '+') sign = *s++; rv = __parse_integer(s, base, val); if (rv < 0) return rv; if (base & PARSE_INTEGER_NEWLINE) return 0; return rv + !!sign; } EXPORT_SYMBOL(_parse_integer_ull); int _parse_integer_ll(const char *s, unsigned int base, long long *val) { unsigned long long tmp; char sign; int rv; sign = 0; if (*s == '-' || *s == '+') sign = *s++; rv = __parse_integer(s, base, &tmp); if (rv < 0) return rv; if (sign == '-') { if ((long long)-tmp > 0) return -ERANGE; *val = -tmp; } else { if ((long long)tmp < 0) return -ERANGE; *val = tmp; } if (base & PARSE_INTEGER_NEWLINE) return 0; return rv + !!sign; } EXPORT_SYMBOL(_parse_integer_ll); int _parse_integer_u(const char *s, unsigned int base, unsigned int *val) { unsigned long long tmp; int rv; rv = _parse_integer_ull(s, base, &tmp); if (rv < 0) return rv; if (tmp != (unsigned int)tmp) return -ERANGE; *val = tmp; return rv; } EXPORT_SYMBOL(_parse_integer_u); int _parse_integer_i(const char *s, unsigned int base, int *val) { long long tmp; int rv; rv = _parse_integer_ll(s, base, &tmp); if (rv < 0) return rv; if (tmp != (int)tmp) return -ERANGE; *val = tmp; return rv; } EXPORT_SYMBOL(_parse_integer_i); int _parse_integer_us(const char *s, unsigned int base, unsigned short *val) { unsigned long long tmp; int rv; rv = _parse_integer_ull(s, base, &tmp); if (rv < 0) return rv; if (tmp != (unsigned short)tmp) return -ERANGE; *val = tmp; return rv; } EXPORT_SYMBOL(_parse_integer_us); int _parse_integer_s(const char *s, unsigned int base, short *val) { long long tmp; int rv; rv = _parse_integer_ll(s, base, &tmp); if (rv < 0) return rv; if (tmp != (short)tmp) return -ERANGE; *val = tmp; return rv; } EXPORT_SYMBOL(_parse_integer_s); int _parse_integer_uc(const char *s, unsigned int base, unsigned char *val) { unsigned long long tmp; int rv; rv = _parse_integer_ull(s, base, &tmp); if (rv < 0) return rv; if (tmp != (unsigned char)tmp) return -ERANGE; *val = tmp; return rv; } EXPORT_SYMBOL(_parse_integer_uc); int _parse_integer_sc(const char *s, unsigned int base, signed char *val) { long long tmp; int rv; rv = _parse_integer_ll(s, base, &tmp); if (rv < 0) return rv; if (tmp != (signed char)tmp) return -ERANGE; *val = tmp; return rv; } EXPORT_SYMBOL(_parse_integer_sc);