2011-03-23 02:34:40 +03:00
/*
* Convert integer string representation to an integer .
* If an integer doesn ' t fit into specified type , - E is returned .
*
* Integer starts with optional sign .
* kstrtou * ( ) functions do not accept sign " - " .
*
* Radix 0 means autodetection : leading " 0x " implies radix 16 ,
* leading " 0 " implies radix 8 , otherwise radix is 10.
* Autodetection hints work after optional sign , but not before .
*
* If - E is returned , result is not touched .
*/
# include <linux/ctype.h>
# include <linux/errno.h>
# include <linux/kernel.h>
# include <linux/math64.h>
2011-11-17 06:29:17 +04:00
# include <linux/export.h>
2011-03-23 02:34:40 +03:00
# include <linux/types.h>
2011-05-25 04:13:31 +04:00
# include <asm/uaccess.h>
2011-11-01 04:12:28 +04:00
# include "kstrtox.h"
2011-03-23 02:34:40 +03:00
2011-11-01 04:12:28 +04:00
const char * _parse_integer_fixup_radix ( const char * s , unsigned int * base )
2011-03-23 02:34:40 +03:00
{
2011-11-01 04:12:28 +04:00
if ( * base = = 0 ) {
2011-03-23 02:34:40 +03:00
if ( s [ 0 ] = = ' 0 ' ) {
if ( _tolower ( s [ 1 ] ) = = ' x ' & & isxdigit ( s [ 2 ] ) )
2011-11-01 04:12:28 +04:00
* base = 16 ;
2011-03-23 02:34:40 +03:00
else
2011-11-01 04:12:28 +04:00
* base = 8 ;
2011-03-23 02:34:40 +03:00
} else
2011-11-01 04:12:28 +04:00
* base = 10 ;
2011-03-23 02:34:40 +03:00
}
2011-11-01 04:12:28 +04:00
if ( * base = = 16 & & s [ 0 ] = = ' 0 ' & & _tolower ( s [ 1 ] ) = = ' x ' )
2011-03-23 02:34:40 +03:00
s + = 2 ;
2011-11-01 04:12:28 +04:00
return s ;
}
/*
* Convert non - negative integer string representation in explicitly given radix
* to an integer .
* Return number of characters consumed maybe or - ed with overflow bit .
* If overflow occurs , result integer ( incorrect ) is still returned .
*
* Don ' t you dare use this function .
*/
2012-02-09 19:48:20 +04:00
unsigned int _parse_integer ( const char * s , unsigned int base , unsigned long long * p )
2011-11-01 04:12:28 +04:00
{
2012-02-09 19:48:20 +04:00
unsigned long long res ;
2011-11-01 04:12:28 +04:00
unsigned int rv ;
int overflow ;
2011-03-23 02:34:40 +03:00
2012-02-09 19:48:20 +04:00
res = 0 ;
2011-11-01 04:12:28 +04:00
rv = 0 ;
overflow = 0 ;
2011-03-23 02:34:40 +03:00
while ( * s ) {
unsigned int val ;
if ( ' 0 ' < = * s & & * s < = ' 9 ' )
val = * s - ' 0 ' ;
else if ( ' a ' < = _tolower ( * s ) & & _tolower ( * s ) < = ' f ' )
val = _tolower ( * s ) - ' a ' + 10 ;
2011-04-15 02:22:02 +04:00
else
2011-11-01 04:12:28 +04:00
break ;
2011-03-23 02:34:40 +03:00
if ( val > = base )
2011-11-01 04:12:28 +04:00
break ;
2012-02-09 19:48:20 +04:00
/*
* Check for overflow only if we are within range of
* it in the max base we support ( 16 )
*/
if ( unlikely ( res & ( ~ 0ull < < 60 ) ) ) {
if ( res > div_u64 ( ULLONG_MAX - val , base ) )
overflow = 1 ;
}
res = res * base + val ;
2011-11-01 04:12:28 +04:00
rv + + ;
2011-03-23 02:34:40 +03:00
s + + ;
}
2012-02-09 19:48:20 +04:00
* p = res ;
2011-11-01 04:12:28 +04:00
if ( overflow )
rv | = KSTRTOX_OVERFLOW ;
return rv ;
}
static int _kstrtoull ( const char * s , unsigned int base , unsigned long long * res )
{
unsigned long long _res ;
unsigned int rv ;
s = _parse_integer_fixup_radix ( s , & base ) ;
rv = _parse_integer ( s , base , & _res ) ;
if ( rv & KSTRTOX_OVERFLOW )
return - ERANGE ;
rv & = ~ KSTRTOX_OVERFLOW ;
if ( rv = = 0 )
return - EINVAL ;
s + = rv ;
if ( * s = = ' \n ' )
s + + ;
if ( * s )
2011-03-23 02:34:40 +03:00
return - EINVAL ;
2011-11-01 04:12:28 +04:00
* res = _res ;
2011-03-23 02:34:40 +03:00
return 0 ;
}
int kstrtoull ( const char * s , unsigned int base , unsigned long long * res )
{
if ( s [ 0 ] = = ' + ' )
s + + ;
return _kstrtoull ( s , base , res ) ;
}
EXPORT_SYMBOL ( kstrtoull ) ;
int kstrtoll ( const char * s , unsigned int base , long long * res )
{
unsigned long long tmp ;
int rv ;
if ( s [ 0 ] = = ' - ' ) {
rv = _kstrtoull ( s + 1 , base , & tmp ) ;
if ( rv < 0 )
return rv ;
if ( ( long long ) ( - tmp ) > = 0 )
return - ERANGE ;
* res = - tmp ;
} else {
rv = kstrtoull ( s , base , & tmp ) ;
if ( rv < 0 )
return rv ;
if ( ( long long ) tmp < 0 )
return - ERANGE ;
* res = tmp ;
}
return 0 ;
}
EXPORT_SYMBOL ( kstrtoll ) ;
/* Internal, do not use. */
int _kstrtoul ( const char * s , unsigned int base , unsigned long * res )
{
unsigned long long tmp ;
int rv ;
rv = kstrtoull ( s , base , & tmp ) ;
if ( rv < 0 )
return rv ;
if ( tmp ! = ( unsigned long long ) ( unsigned long ) tmp )
return - ERANGE ;
* res = tmp ;
return 0 ;
}
EXPORT_SYMBOL ( _kstrtoul ) ;
/* Internal, do not use. */
int _kstrtol ( const char * s , unsigned int base , long * res )
{
long long tmp ;
int rv ;
rv = kstrtoll ( s , base , & tmp ) ;
if ( rv < 0 )
return rv ;
if ( tmp ! = ( long long ) ( long ) tmp )
return - ERANGE ;
* res = tmp ;
return 0 ;
}
EXPORT_SYMBOL ( _kstrtol ) ;
int kstrtouint ( const char * s , unsigned int base , unsigned int * res )
{
unsigned long long tmp ;
int rv ;
rv = kstrtoull ( s , base , & tmp ) ;
if ( rv < 0 )
return rv ;
if ( tmp ! = ( unsigned long long ) ( unsigned int ) tmp )
return - ERANGE ;
* res = tmp ;
return 0 ;
}
EXPORT_SYMBOL ( kstrtouint ) ;
int kstrtoint ( const char * s , unsigned int base , int * res )
{
long long tmp ;
int rv ;
rv = kstrtoll ( s , base , & tmp ) ;
if ( rv < 0 )
return rv ;
if ( tmp ! = ( long long ) ( int ) tmp )
return - ERANGE ;
* res = tmp ;
return 0 ;
}
EXPORT_SYMBOL ( kstrtoint ) ;
int kstrtou16 ( const char * s , unsigned int base , u16 * res )
{
unsigned long long tmp ;
int rv ;
rv = kstrtoull ( s , base , & tmp ) ;
if ( rv < 0 )
return rv ;
if ( tmp ! = ( unsigned long long ) ( u16 ) tmp )
return - ERANGE ;
* res = tmp ;
return 0 ;
}
EXPORT_SYMBOL ( kstrtou16 ) ;
int kstrtos16 ( const char * s , unsigned int base , s16 * res )
{
long long tmp ;
int rv ;
rv = kstrtoll ( s , base , & tmp ) ;
if ( rv < 0 )
return rv ;
if ( tmp ! = ( long long ) ( s16 ) tmp )
return - ERANGE ;
* res = tmp ;
return 0 ;
}
EXPORT_SYMBOL ( kstrtos16 ) ;
int kstrtou8 ( const char * s , unsigned int base , u8 * res )
{
unsigned long long tmp ;
int rv ;
rv = kstrtoull ( s , base , & tmp ) ;
if ( rv < 0 )
return rv ;
if ( tmp ! = ( unsigned long long ) ( u8 ) tmp )
return - ERANGE ;
* res = tmp ;
return 0 ;
}
EXPORT_SYMBOL ( kstrtou8 ) ;
int kstrtos8 ( const char * s , unsigned int base , s8 * res )
{
long long tmp ;
int rv ;
rv = kstrtoll ( s , base , & tmp ) ;
if ( rv < 0 )
return rv ;
if ( tmp ! = ( long long ) ( s8 ) tmp )
return - ERANGE ;
* res = tmp ;
return 0 ;
}
EXPORT_SYMBOL ( kstrtos8 ) ;
2011-05-25 04:13:31 +04:00
# define kstrto_from_user(f, g, type) \
int f ( const char __user * s , size_t count , unsigned int base , type * res ) \
{ \
/* sign, base 2 representation, newline, terminator */ \
char buf [ 1 + sizeof ( type ) * 8 + 1 + 1 ] ; \
\
count = min ( count , sizeof ( buf ) - 1 ) ; \
if ( copy_from_user ( buf , s , count ) ) \
return - EFAULT ; \
buf [ count ] = ' \0 ' ; \
return g ( buf , base , res ) ; \
} \
EXPORT_SYMBOL ( f )
kstrto_from_user ( kstrtoull_from_user , kstrtoull , unsigned long long ) ;
kstrto_from_user ( kstrtoll_from_user , kstrtoll , long long ) ;
kstrto_from_user ( kstrtoul_from_user , kstrtoul , unsigned long ) ;
kstrto_from_user ( kstrtol_from_user , kstrtol , long ) ;
kstrto_from_user ( kstrtouint_from_user , kstrtouint , unsigned int ) ;
kstrto_from_user ( kstrtoint_from_user , kstrtoint , int ) ;
kstrto_from_user ( kstrtou16_from_user , kstrtou16 , u16 ) ;
kstrto_from_user ( kstrtos16_from_user , kstrtos16 , s16 ) ;
kstrto_from_user ( kstrtou8_from_user , kstrtou8 , u8 ) ;
kstrto_from_user ( kstrtos8_from_user , kstrtos8 , s8 ) ;