@ -21,6 +21,31 @@
extern int do_reset ( cmd_tbl_t * cmdtp , int flag , int argc , char * argv [ ] ) ;
# endif
# ifdef CONFIG_SYS_64BIT_VSPRINTF
# define NUM_TYPE long long
# else
# define NUM_TYPE long
# endif
# define noinline __attribute__((noinline))
# define do_div(n, base) ({ \
unsigned int __res ; \
__res = ( ( unsigned NUM_TYPE ) n ) % base ; \
n = ( ( unsigned NUM_TYPE ) n ) / base ; \
__res ; \
} )
const char hex_asc [ ] = " 0123456789abcdef " ;
# define hex_asc_lo(x) hex_asc[((x) & 0x0f)]
# define hex_asc_hi(x) hex_asc[((x) & 0xf0) >> 4]
static inline char * pack_hex_byte ( char * buf , u8 byte )
{
* buf + + = hex_asc_hi ( byte ) ;
* buf + + = hex_asc_lo ( byte ) ;
return buf ;
}
unsigned long simple_strtoul ( const char * cp , char * * endp , unsigned int base )
{
unsigned long result = 0 , value ;
@ -120,52 +145,132 @@ static int skip_atoi(const char **s)
return i ;
}
/* Decimal conversion is by far the most typical, and is used
* for / proc and / sys data . This directly impacts e . g . top performance
* with many processes running . We optimize it for speed
* using code from
* http : //www.cs.uiowa.edu/~jones/bcd/decimal.html
* ( with permission from the author , Douglas W . Jones ) . */
/* Formats correctly any integer in [0,99999].
* Outputs from one to five digits depending on input .
* On i386 gcc 4.1 .2 - O2 : ~ 250 bytes of code . */
static char * put_dec_trunc ( char * buf , unsigned q )
{
unsigned d3 , d2 , d1 , d0 ;
d1 = ( q > > 4 ) & 0xf ;
d2 = ( q > > 8 ) & 0xf ;
d3 = ( q > > 12 ) ;
d0 = 6 * ( d3 + d2 + d1 ) + ( q & 0xf ) ;
q = ( d0 * 0xcd ) > > 11 ;
d0 = d0 - 10 * q ;
* buf + + = d0 + ' 0 ' ; /* least significant digit */
d1 = q + 9 * d3 + 5 * d2 + d1 ;
if ( d1 ! = 0 ) {
q = ( d1 * 0xcd ) > > 11 ;
d1 = d1 - 10 * q ;
* buf + + = d1 + ' 0 ' ; /* next digit */
d2 = q + 2 * d2 ;
if ( ( d2 ! = 0 ) | | ( d3 ! = 0 ) ) {
q = ( d2 * 0xd ) > > 7 ;
d2 = d2 - 10 * q ;
* buf + + = d2 + ' 0 ' ; /* next digit */
d3 = q + 4 * d3 ;
if ( d3 ! = 0 ) {
q = ( d3 * 0xcd ) > > 11 ;
d3 = d3 - 10 * q ;
* buf + + = d3 + ' 0 ' ; /* next digit */
if ( q ! = 0 )
* buf + + = q + ' 0 ' ; /* most sign. digit */
}
}
}
return buf ;
}
/* Same with if's removed. Always emits five digits */
static char * put_dec_full ( char * buf , unsigned q )
{
/* BTW, if q is in [0,9999], 8-bit ints will be enough, */
/* but anyway, gcc produces better code with full-sized ints */
unsigned d3 , d2 , d1 , d0 ;
d1 = ( q > > 4 ) & 0xf ;
d2 = ( q > > 8 ) & 0xf ;
d3 = ( q > > 12 ) ;
/* Possible ways to approx. divide by 10 */
/* gcc -O2 replaces multiply with shifts and adds */
// (x * 0xcd) >> 11: 11001101 - shorter code than * 0x67 (on i386)
// (x * 0x67) >> 10: 1100111
// (x * 0x34) >> 9: 110100 - same
// (x * 0x1a) >> 8: 11010 - same
// (x * 0x0d) >> 7: 1101 - same, shortest code (on i386)
d0 = 6 * ( d3 + d2 + d1 ) + ( q & 0xf ) ;
q = ( d0 * 0xcd ) > > 11 ;
d0 = d0 - 10 * q ;
* buf + + = d0 + ' 0 ' ;
d1 = q + 9 * d3 + 5 * d2 + d1 ;
q = ( d1 * 0xcd ) > > 11 ;
d1 = d1 - 10 * q ;
* buf + + = d1 + ' 0 ' ;
d2 = q + 2 * d2 ;
q = ( d2 * 0xd ) > > 7 ;
d2 = d2 - 10 * q ;
* buf + + = d2 + ' 0 ' ;
d3 = q + 4 * d3 ;
q = ( d3 * 0xcd ) > > 11 ; /* - shorter code */
/* q = (d3 * 0x67) >> 10; - would also work */
d3 = d3 - 10 * q ;
* buf + + = d3 + ' 0 ' ;
* buf + + = q + ' 0 ' ;
return buf ;
}
/* No inlining helps gcc to use registers better */
static noinline char * put_dec ( char * buf , unsigned NUM_TYPE num )
{
while ( 1 ) {
unsigned rem ;
if ( num < 100000 )
return put_dec_trunc ( buf , num ) ;
rem = do_div ( num , 100000 ) ;
buf = put_dec_full ( buf , rem ) ;
}
}
# define ZEROPAD 1 /* pad with zero */
# define SIGN 2 /* unsigned/signed long */
# define PLUS 4 /* show plus */
# define SPACE 8 /* space if plus */
# define LEFT 16 /* left justified */
# define SPECIAL 32 /* 0x */
# define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */
# define SMALL 32 /* Must be 32 == 0x20 */
# define SPECIAL 64 /* 0x */
# ifdef CONFIG_SYS_64BIT_VSPRINTF
# define do_div(n,base) ({ \
unsigned int __res ; \
__res = ( ( unsigned long long ) n ) % base ; \
n = ( ( unsigned long long ) n ) / base ; \
__res ; \
} )
# else
# define do_div(n,base) ({ \
int __res ; \
__res = ( ( unsigned long ) n ) % base ; \
n = ( ( unsigned long ) n ) / base ; \
__res ; \
} )
# endif
# ifdef CONFIG_SYS_64BIT_VSPRINTF
static char * number ( char * str , long long num , unsigned int base , int size , int precision , int type )
# else
static char * number ( char * str , long num , unsigned int base , int size , int precision , int type )
# endif
static char * number ( char * buf , unsigned NUM_TYPE num , int base , int size , int precision , int type )
{
char c , sign , tmp [ 66 ] ;
const char * digits = " 0123456789abcdefghijklmnopqrstuvwxyz " ;
/* we are called with base 8, 10 or 16, only, thus don't need "G..." */
static const char digits [ 16 ] = " 0123456789ABCDEF " ; /* "GHIJKLMNOPQRSTUVWXYZ"; */
char tmp [ 66 ] ;
char sign ;
char locase ;
int need_pfx = ( ( type & SPECIAL ) & & base ! = 10 ) ;
int i ;
if ( type & LARGE )
digits = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ " ;
/* locase = 0 or 0x20. ORing digits or letters with 'locase'
* produces same digits or ( maybe lowercased ) letters */
locase = ( type & SMALL ) ;
if ( type & LEFT )
type & = ~ ZEROPAD ;
if ( base < 2 | | base > 36 )
return 0 ;
c = ( type & ZEROPAD ) ? ' 0 ' : ' ' ;
sign = 0 ;
if ( type & SIGN ) {
if ( num < 0 ) {
if ( ( signed NUM_TYPE ) num < 0 ) {
sign = ' - ' ;
num = - num ;
num = - ( signed NUM_TYPE ) num ;
size - - ;
} else if ( type & PLUS ) {
sign = ' + ' ;
@ -175,68 +280,231 @@ static char * number(char * str, long num, unsigned int base, int size, int prec
size - - ;
}
}
if ( type & SPECIAL ) {
if ( need_pfx ) {
size - - ;
if ( base = = 16 )
size - = 2 ;
else if ( base = = 8 )
size - - ;
}
/* generate full string in tmp[], in reverse order */
i = 0 ;
if ( num = = 0 )
tmp [ i + + ] = ' 0 ' ;
else while ( num ! = 0 )
tmp [ i + + ] = digits [ do_div ( num , base ) ] ;
tmp [ i + + ] = ' 0 ' ;
/* Generic code, for any base:
else do {
tmp [ i + + ] = ( digits [ do_div ( num , base ) ] | locase ) ;
} while ( num ! = 0 ) ;
*/
else if ( base ! = 10 ) { /* 8 or 16 */
int mask = base - 1 ;
int shift = 3 ;
if ( base = = 16 ) shift = 4 ;
do {
tmp [ i + + ] = ( digits [ ( ( unsigned char ) num ) & mask ] | locase ) ;
num > > = shift ;
} while ( num ) ;
} else { /* base 10 */
i = put_dec ( tmp , num ) - tmp ;
}
/* printing 100 using %2d gives "100", not "00" */
if ( i > precision )
precision = i ;
/* leading space padding */
size - = precision ;
if ( ! ( type & ( ZEROPAD + LEFT ) ) )
while ( size - - > 0 )
* str + + = ' ' ;
if ( ! ( type & ( ZEROPAD + LEFT ) ) )
while ( - - size > = 0 )
* buf + + = ' ' ;
/* sign */
if ( sign )
* str + + = sign ;
if ( type & SPECIAL ) {
if ( base = = 8 )
* str + + = ' 0 ' ;
else if ( base = = 16 ) {
* str + + = ' 0 ' ;
* str + + = digits [ 33 ] ;
}
* buf + + = sign ;
/* "0x" / "0" prefix */
if ( need_pfx ) {
* buf + + = ' 0 ' ;
if ( base = = 16 )
* buf + + = ( ' X ' | locase ) ;
}
/* zero or space padding */
if ( ! ( type & LEFT ) ) {
char c = ( type & ZEROPAD ) ? ' 0 ' : ' ' ;
while ( - - size > = 0 )
* buf + + = c ;
}
if ( ! ( type & LEFT ) )
while ( size - - > 0 )
* str + + = c ;
while ( i < precision - - )
* str + + = ' 0 ' ;
while ( i - - > 0 )
* str + + = tmp [ i ] ;
while ( size - - > 0 )
* str + + = ' ' ;
return str ;
/* hmm even more zero padding? */
while ( i < = - - precision )
* buf + + = ' 0 ' ;
/* actual digits of result */
while ( - - i > = 0 )
* buf + + = tmp [ i ] ;
/* trailing space padding */
while ( - - size > = 0 )
* buf + + = ' ' ;
return buf ;
}
/* Forward decl. needed for IP address printing stuff... */
int sprintf ( char * buf , const char * fmt , . . . ) ;
static char * string ( char * buf , char * s , int field_width , int precision , int flags )
{
int len , i ;
int vsprintf ( char * buf , const char * fmt , va_list args )
if ( s = = 0 )
s = " <NULL> " ;
len = strnlen ( s , precision ) ;
if ( ! ( flags & LEFT ) )
while ( len < field_width - - )
* buf + + = ' ' ;
for ( i = 0 ; i < len ; + + i )
* buf + + = * s + + ;
while ( len < field_width - - )
* buf + + = ' ' ;
return buf ;
}
# ifdef CONFIG_CMD_NET
static char * mac_address_string ( char * buf , u8 * addr , int field_width ,
int precision , int flags )
{
int len ;
# ifdef CONFIG_SYS_64BIT_VSPRINTF
unsigned long long num ;
# else
unsigned long num ;
char mac_addr [ 6 * 3 ] ; /* (6 * 2 hex digits), 5 colons and trailing zero */
char * p = mac_addr ;
int i ;
for ( i = 0 ; i < 6 ; i + + ) {
p = pack_hex_byte ( p , addr [ i ] ) ;
if ( ! ( flags & SPECIAL ) & & i ! = 5 )
* p + + = ' : ' ;
}
* p = ' \0 ' ;
return string ( buf , mac_addr , field_width , precision , flags & ~ SPECIAL ) ;
}
static char * ip6_addr_string ( char * buf , u8 * addr , int field_width ,
int precision , int flags )
{
char ip6_addr [ 8 * 5 ] ; /* (8 * 4 hex digits), 7 colons and trailing zero */
char * p = ip6_addr ;
int i ;
for ( i = 0 ; i < 8 ; i + + ) {
p = pack_hex_byte ( p , addr [ 2 * i ] ) ;
p = pack_hex_byte ( p , addr [ 2 * i + 1 ] ) ;
if ( ! ( flags & SPECIAL ) & & i ! = 7 )
* p + + = ' : ' ;
}
* p = ' \0 ' ;
return string ( buf , ip6_addr , field_width , precision , flags & ~ SPECIAL ) ;
}
static char * ip4_addr_string ( char * buf , u8 * addr , int field_width ,
int precision , int flags )
{
char ip4_addr [ 4 * 4 ] ; /* (4 * 3 decimal digits), 3 dots and trailing zero */
char temp [ 3 ] ; /* hold each IP quad in reverse order */
char * p = ip4_addr ;
int i , digits ;
for ( i = 0 ; i < 4 ; i + + ) {
digits = put_dec_trunc ( temp , addr [ i ] ) - temp ;
/* reverse the digits in the quad */
while ( digits - - )
* p + + = temp [ digits ] ;
if ( i ! = 3 )
* p + + = ' . ' ;
}
* p = ' \0 ' ;
return string ( buf , ip4_addr , field_width , precision , flags & ~ SPECIAL ) ;
}
# endif
int i , base ;
char * str ;
const char * s ;
/*
* Show a ' % p ' thing . A kernel extension is that the ' % p ' is followed
* by an extra set of alphanumeric characters that are extended format
* specifiers .
*
* Right now we handle :
*
* - ' M ' For a 6 - byte MAC address , it prints the address in the
* usual colon - separated hex notation
* - ' I ' [ 46 ] for IPv4 / IPv6 addresses printed in the usual way ( dot - separated
* decimal for v4 and colon separated network - order 16 bit hex for v6 )
* - ' i ' [ 46 ] for ' raw ' IPv4 / IPv6 addresses , IPv6 omits the colons , IPv4 is
* currently the same
*
* Note : The difference between ' S ' and ' F ' is that on ia64 and ppc64
* function pointers are really function descriptors , which contain a
* pointer to the real address .
*/
static char * pointer ( const char * fmt , char * buf , void * ptr , int field_width , int precision , int flags )
{
if ( ! ptr )
return string ( buf , " (null) " , field_width, precision, flags) ;
# ifdef CONFIG_CMD_NET
switch ( * fmt ) {
case ' m ' :
flags | = SPECIAL ;
/* Fallthrough */
case ' M ' :
return mac_address_string ( buf , ptr , field_width , precision , flags ) ;
case ' i ' :
flags | = SPECIAL ;
/* Fallthrough */
case ' I ' :
if ( fmt [ 1 ] = = ' 6 ' )
return ip6_addr_string ( buf , ptr , field_width , precision , flags ) ;
if ( fmt [ 1 ] = = ' 4 ' )
return ip4_addr_string ( buf , ptr , field_width , precision , flags ) ;
flags & = ~ SPECIAL ;
break ;
}
# endif
flags | = SMALL ;
if ( field_width = = - 1 ) {
field_width = 2 * sizeof ( void * ) ;
flags | = ZEROPAD ;
}
return number ( buf , ( unsigned long ) ptr , 16 , field_width , precision , flags ) ;
}
/**
* vsprintf - Format a string and place it in a buffer
* @ buf : The buffer to place the result into
* @ fmt : The format string to use
* @ args : Arguments for the format string
*
* This function follows C99 vsprintf , but has some extensions :
* % pS output the name of a text symbol
* % pF output the name of a function pointer
* % pR output the address range in a struct resource
*
* The function returns the number of characters written
* into @ buf .
*
* Call this function if you are already dealing with a va_list .
* You probably want sprintf ( ) instead .
*/
int vsprintf ( char * buf , const char * fmt , va_list args )
{
unsigned NUM_TYPE num ;
int base ;
char * str ;
int flags ; /* flags to number() */
int field_width ; /* width of output field */
int precision ; /* min. # of digits for integers; max
number of chars for from string */
int qualifier ; /* 'h', 'l', or 'q' for integer fields */
int qualifier ; /* 'h', 'l', or 'L' for integer fields */
/* 'z' support added 23/7/1999 S.H. */
/* 'z' changed to 'Z' --davidm 1/25/99 */
/* 't' added for ptrdiff_t */
str = buf ;
for ( str = buf ; * fmt ; + + fmt ) {
for ( ; * fmt ; + + fmt ) {
if ( * fmt ! = ' % ' ) {
* str + + = * fmt ;
continue ;
@ -252,7 +520,7 @@ int vsprintf(char *buf, const char *fmt, va_list args)
case ' ' : flags | = SPACE ; goto repeat ;
case ' # ' : flags | = SPECIAL ; goto repeat ;
case ' 0 ' : flags | = ZEROPAD ; goto repeat ;
}
}
/* get field width */
field_width = - 1 ;
@ -286,14 +554,13 @@ int vsprintf(char *buf, const char *fmt, va_list args)
/* get the conversion qualifier */
qualifier = - 1 ;
if ( * fmt = = ' h ' | | * fmt = = ' l ' | | * fmt = = ' L ' | |
* fmt = = ' Z ' | | * fmt = = ' z ' | | * fmt = = ' t ' | |
* fmt = = ' q ' ) {
* fmt = = ' Z ' | | * fmt = = ' z ' | | * fmt = = ' t ' ) {
qualifier = * fmt ;
if ( qualifier = = ' l ' & & * ( fmt + 1 ) = = ' l ' ) {
qualifier = ' q ' ;
+ + fmt ;
if ( qualifier = = ' l ' & & * fmt = = ' l ' ) {
qualifier = ' L ' ;
+ + fmt ;
}
+ + fmt ;
}
/* default base */
@ -310,32 +577,18 @@ int vsprintf(char *buf, const char *fmt, va_list args)
continue ;
case ' s ' :
s = va_arg ( args , char * ) ;
if ( ! s )
s = " <NULL> " ;
len = strnlen ( s , precision ) ;
if ( ! ( flags & LEFT ) )
while ( len < field_width - - )
* str + + = ' ' ;
for ( i = 0 ; i < len ; + + i )
* str + + = * s + + ;
while ( len < field_width - - )
* str + + = ' ' ;
str = string ( str , va_arg ( args , char * ) , field_width , precision , flags ) ;
continue ;
case ' p ' :
if ( field_width = = - 1 ) {
field_width = 2 * sizeof ( void * ) ;
flags | = ZEROPAD ;
}
str = number ( str ,
( unsigned long ) va_arg ( args , void * ) , 16 ,
field_width , precision , flags ) ;
str = pointer ( fmt + 1 , str ,
va_arg ( args , void * ) ,
field_width , precision , flags ) ;
/* Skip all alphanumeric pointer suffixes */
while ( isalnum ( fmt [ 1 ] ) )
fmt + + ;
continue ;
case ' n ' :
if ( qualifier = = ' l ' ) {
long * ip = va_arg ( args , long * ) ;
@ -355,9 +608,9 @@ int vsprintf(char *buf, const char *fmt, va_list args)
base = 8 ;
break ;
case ' X ' :
flags | = LARGE ;
case ' x ' :
flags | = SMALL ;
case ' X ' :
base = 16 ;
break ;
@ -376,12 +629,14 @@ int vsprintf(char *buf, const char *fmt, va_list args)
continue ;
}
# ifdef CONFIG_SYS_64BIT_VSPRINTF
if ( qualifier = = ' q ' ) /* "quad" for 64 bit variables */
if ( qualifier = = ' L ' ) /* "quad" for 64 bit variables */
num = va_arg ( args , unsigned long long ) ;
else
# endif
if ( qualifier = = ' l ' ) {
num = va_arg ( args , unsigned long ) ;
if ( flags & SIGN )
num = ( signed long ) num ;
} else if ( qualifier = = ' Z ' | | qualifier = = ' z ' ) {
num = va_arg ( args , size_t ) ;
} else if ( qualifier = = ' t ' ) {
@ -389,17 +644,29 @@ int vsprintf(char *buf, const char *fmt, va_list args)
} else if ( qualifier = = ' h ' ) {
num = ( unsigned short ) va_arg ( args , int ) ;
if ( flags & SIGN )
num = ( short ) num ;
} else if ( flags & SIGN )
num = va_arg ( args , int ) ;
else
num = ( signed short ) num ;
} else {
num = va_arg ( args , unsigned int ) ;
if ( flags & SIGN )
num = ( signed int ) num ;
}
str = number ( str , num , base , field_width , precision , flags ) ;
}
* str = ' \0 ' ;
return str - buf ;
}
/**
* sprintf - Format a string and place it in a buffer
* @ buf : The buffer to place the result into
* @ fmt : The format string to use
* @ . . . : Arguments for the format string
*
* The function returns the number of characters written
* into @ buf .
*
* See the vsprintf ( ) documentation for format string extensions over C99 .
*/
int sprintf ( char * buf , const char * fmt , . . . )
{
va_list args ;