blob: 7c7f48bec328473e702ffbd09139c38f498a9e44 [file] [log] [blame]
/*
* 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 <linux/ctype.h>
#include <linux/errno.h>
#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/math64.h>
#include <linux/parse-integer.h>
#include <asm/bug.h>
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);