2008-08-31 19:13:54 +04:00
/*
* Helpers for formatting and printing strings
*
* Copyright 31 August 2008 James Bottomley
2013-05-01 02:27:30 +04:00
* Copyright ( C ) 2013 , Intel Corporation
2008-08-31 19:13:54 +04:00
*/
# include <linux/kernel.h>
# include <linux/math64.h>
2011-11-17 06:29:17 +04:00
# include <linux/export.h>
2013-05-01 02:27:30 +04:00
# include <linux/ctype.h>
2014-10-14 02:55:16 +04:00
# include <linux/errno.h>
# include <linux/string.h>
2008-08-31 19:13:54 +04:00
# include <linux/string_helpers.h>
/**
* string_get_size - get the size in the specified units
* @ size : The size to be converted
* @ units : units to use ( powers of 1000 or 1024 )
* @ buf : buffer to format to
* @ len : length of buffer
*
* This function returns a string formatted to 3 significant figures
* giving the size in the required units . Returns 0 on success or
* error on failure . @ buf is always zero terminated .
*
*/
int string_get_size ( u64 size , const enum string_size_units units ,
char * buf , int len )
{
2014-08-07 03:09:31 +04:00
static const char * const units_10 [ ] = {
" B " , " kB " , " MB " , " GB " , " TB " , " PB " , " EB " , " ZB " , " YB " , NULL
} ;
static const char * const units_2 [ ] = {
" B " , " KiB " , " MiB " , " GiB " , " TiB " , " PiB " , " EiB " , " ZiB " , " YiB " ,
NULL
} ;
static const char * const * const units_str [ ] = {
[ STRING_UNITS_10 ] = units_10 ,
2008-08-31 19:13:54 +04:00
[ STRING_UNITS_2 ] = units_2 ,
} ;
2012-05-30 02:07:32 +04:00
static const unsigned int divisor [ ] = {
2008-08-31 19:13:54 +04:00
[ STRING_UNITS_10 ] = 1000 ,
[ STRING_UNITS_2 ] = 1024 ,
} ;
int i , j ;
u64 remainder = 0 , sf_cap ;
char tmp [ 8 ] ;
tmp [ 0 ] = ' \0 ' ;
2008-10-14 22:34:21 +04:00
i = 0 ;
if ( size > = divisor [ units ] ) {
while ( size > = divisor [ units ] & & units_str [ units ] [ i ] ) {
remainder = do_div ( size , divisor [ units ] ) ;
i + + ;
}
2008-08-31 19:13:54 +04:00
2008-10-14 22:34:21 +04:00
sf_cap = size ;
for ( j = 0 ; sf_cap * 10 < 1000 ; j + + )
sf_cap * = 10 ;
2008-08-31 19:13:54 +04:00
2008-10-14 22:34:21 +04:00
if ( j ) {
remainder * = 1000 ;
do_div ( remainder , divisor [ units ] ) ;
snprintf ( tmp , sizeof ( tmp ) , " .%03lld " ,
( unsigned long long ) remainder ) ;
tmp [ j + 1 ] = ' \0 ' ;
}
2008-08-31 19:13:54 +04:00
}
2008-10-14 22:34:21 +04:00
snprintf ( buf , len , " %lld%s %s " , ( unsigned long long ) size ,
2008-08-31 19:13:54 +04:00
tmp , units_str [ units ] [ i ] ) ;
return 0 ;
}
EXPORT_SYMBOL ( string_get_size ) ;
2013-05-01 02:27:30 +04:00
static bool unescape_space ( char * * src , char * * dst )
{
char * p = * dst , * q = * src ;
switch ( * q ) {
case ' n ' :
* p = ' \n ' ;
break ;
case ' r ' :
* p = ' \r ' ;
break ;
case ' t ' :
* p = ' \t ' ;
break ;
case ' v ' :
* p = ' \v ' ;
break ;
case ' f ' :
* p = ' \f ' ;
break ;
default :
return false ;
}
* dst + = 1 ;
* src + = 1 ;
return true ;
}
static bool unescape_octal ( char * * src , char * * dst )
{
char * p = * dst , * q = * src ;
u8 num ;
if ( isodigit ( * q ) = = 0 )
return false ;
num = ( * q + + ) & 7 ;
while ( num < 32 & & isodigit ( * q ) & & ( q - * src < 3 ) ) {
num < < = 3 ;
num + = ( * q + + ) & 7 ;
}
* p = num ;
* dst + = 1 ;
* src = q ;
return true ;
}
static bool unescape_hex ( char * * src , char * * dst )
{
char * p = * dst , * q = * src ;
int digit ;
u8 num ;
if ( * q + + ! = ' x ' )
return false ;
num = digit = hex_to_bin ( * q + + ) ;
if ( digit < 0 )
return false ;
digit = hex_to_bin ( * q ) ;
if ( digit > = 0 ) {
q + + ;
num = ( num < < 4 ) | digit ;
}
* p = num ;
* dst + = 1 ;
* src = q ;
return true ;
}
static bool unescape_special ( char * * src , char * * dst )
{
char * p = * dst , * q = * src ;
switch ( * q ) {
case ' \" ' :
* p = ' \" ' ;
break ;
case ' \\ ' :
* p = ' \\ ' ;
break ;
case ' a ' :
* p = ' \a ' ;
break ;
case ' e ' :
* p = ' \e ' ;
break ;
default :
return false ;
}
* dst + = 1 ;
* src + = 1 ;
return true ;
}
2014-10-14 02:55:11 +04:00
/**
* string_unescape - unquote characters in the given string
* @ src : source buffer ( escaped )
* @ dst : destination buffer ( unescaped )
* @ size : size of the destination buffer ( 0 to unlimit )
* @ flags : combination of the flags ( bitwise OR ) :
* % UNESCAPE_SPACE :
* ' \f ' - form feed
* ' \n ' - new line
* ' \r ' - carriage return
* ' \t ' - horizontal tab
* ' \v ' - vertical tab
* % UNESCAPE_OCTAL :
* ' \ NNN ' - byte with octal value NNN ( 1 to 3 digits )
* % UNESCAPE_HEX :
* ' \ xHH ' - byte with hexadecimal value HH ( 1 to 2 digits )
* % UNESCAPE_SPECIAL :
* ' \" ' - double quote
* ' \\ ' - backslash
* ' \a ' - alert ( BEL )
* ' \e ' - escape
* % UNESCAPE_ANY :
* all previous together
*
* Description :
* The function unquotes characters in the given string .
*
* Because the size of the output will be the same as or less than the size of
* the input , the transformation may be performed in place .
*
* Caller must provide valid source and destination pointers . Be aware that
* destination buffer will always be NULL - terminated . Source string must be
* NULL - terminated as well .
*
* Return :
* The amount of the characters processed to the destination buffer excluding
* trailing ' \0 ' is returned .
*/
2013-05-01 02:27:30 +04:00
int string_unescape ( char * src , char * dst , size_t size , unsigned int flags )
{
char * out = dst ;
while ( * src & & - - size ) {
if ( src [ 0 ] = = ' \\ ' & & src [ 1 ] ! = ' \0 ' & & size > 1 ) {
src + + ;
size - - ;
if ( flags & UNESCAPE_SPACE & &
unescape_space ( & src , & out ) )
continue ;
if ( flags & UNESCAPE_OCTAL & &
unescape_octal ( & src , & out ) )
continue ;
if ( flags & UNESCAPE_HEX & &
unescape_hex ( & src , & out ) )
continue ;
if ( flags & UNESCAPE_SPECIAL & &
unescape_special ( & src , & out ) )
continue ;
* out + + = ' \\ ' ;
}
* out + + = * src + + ;
}
* out = ' \0 ' ;
return out - dst ;
}
EXPORT_SYMBOL ( string_unescape ) ;
2014-10-14 02:55:16 +04:00
static int escape_passthrough ( unsigned char c , char * * dst , size_t * osz )
{
char * out = * dst ;
if ( * osz < 1 )
return - ENOMEM ;
* out + + = c ;
* dst = out ;
* osz - = 1 ;
return 1 ;
}
static int escape_space ( unsigned char c , char * * dst , size_t * osz )
{
char * out = * dst ;
unsigned char to ;
if ( * osz < 2 )
return - ENOMEM ;
switch ( c ) {
case ' \n ' :
to = ' n ' ;
break ;
case ' \r ' :
to = ' r ' ;
break ;
case ' \t ' :
to = ' t ' ;
break ;
case ' \v ' :
to = ' v ' ;
break ;
case ' \f ' :
to = ' f ' ;
break ;
default :
return 0 ;
}
* out + + = ' \\ ' ;
* out + + = to ;
* dst = out ;
* osz - = 2 ;
return 1 ;
}
static int escape_special ( unsigned char c , char * * dst , size_t * osz )
{
char * out = * dst ;
unsigned char to ;
if ( * osz < 2 )
return - ENOMEM ;
switch ( c ) {
case ' \\ ' :
to = ' \\ ' ;
break ;
case ' \a ' :
to = ' a ' ;
break ;
case ' \e ' :
to = ' e ' ;
break ;
default :
return 0 ;
}
* out + + = ' \\ ' ;
* out + + = to ;
* dst = out ;
* osz - = 2 ;
return 1 ;
}
static int escape_null ( unsigned char c , char * * dst , size_t * osz )
{
char * out = * dst ;
if ( * osz < 2 )
return - ENOMEM ;
if ( c )
return 0 ;
* out + + = ' \\ ' ;
* out + + = ' 0 ' ;
* dst = out ;
* osz - = 2 ;
return 1 ;
}
static int escape_octal ( unsigned char c , char * * dst , size_t * osz )
{
char * out = * dst ;
if ( * osz < 4 )
return - ENOMEM ;
* out + + = ' \\ ' ;
* out + + = ( ( c > > 6 ) & 0x07 ) + ' 0 ' ;
* out + + = ( ( c > > 3 ) & 0x07 ) + ' 0 ' ;
* out + + = ( ( c > > 0 ) & 0x07 ) + ' 0 ' ;
* dst = out ;
* osz - = 4 ;
return 1 ;
}
static int escape_hex ( unsigned char c , char * * dst , size_t * osz )
{
char * out = * dst ;
if ( * osz < 4 )
return - ENOMEM ;
* out + + = ' \\ ' ;
* out + + = ' x ' ;
* out + + = hex_asc_hi ( c ) ;
* out + + = hex_asc_lo ( c ) ;
* dst = out ;
* osz - = 4 ;
return 1 ;
}
/**
* string_escape_mem - quote characters in the given memory buffer
* @ src : source buffer ( unescaped )
* @ isz : source buffer size
* @ dst : destination buffer ( escaped )
* @ osz : destination buffer size
* @ flags : combination of the flags ( bitwise OR ) :
* % ESCAPE_SPACE :
* ' \f ' - form feed
* ' \n ' - new line
* ' \r ' - carriage return
* ' \t ' - horizontal tab
* ' \v ' - vertical tab
* % ESCAPE_SPECIAL :
* ' \\ ' - backslash
* ' \a ' - alert ( BEL )
* ' \e ' - escape
* % ESCAPE_NULL :
* ' \0 ' - null
* % ESCAPE_OCTAL :
* ' \ NNN ' - byte with octal value NNN ( 3 digits )
* % ESCAPE_ANY :
* all previous together
* % ESCAPE_NP :
* escape only non - printable characters ( checked by isprint )
* % ESCAPE_ANY_NP :
* all previous together
* % ESCAPE_HEX :
* ' \ xHH ' - byte with hexadecimal value HH ( 2 digits )
* @ esc : NULL - terminated string of characters any of which , if found in
* the source , has to be escaped
*
* Description :
* The process of escaping byte buffer includes several parts . They are applied
* in the following sequence .
* 1. The character is matched to the printable class , if asked , and in
* case of match it passes through to the output .
* 2. The character is not matched to the one from @ esc string and thus
* must go as is to the output .
* 3. The character is checked if it falls into the class given by @ flags .
* % ESCAPE_OCTAL and % ESCAPE_HEX are going last since they cover any
* character . Note that they actually can ' t go together , otherwise
* % ESCAPE_HEX will be ignored .
*
* Caller must provide valid source and destination pointers . Be aware that
* destination buffer will not be NULL - terminated , thus caller have to append
* it if needs .
*
* Return :
* The amount of the characters processed to the destination buffer , or
* % - ENOMEM if the size of buffer is not enough to put an escaped character is
* returned .
*
* Even in the case of error @ dst pointer will be updated to point to the byte
* after the last processed character .
*/
int string_escape_mem ( const char * src , size_t isz , char * * dst , size_t osz ,
unsigned int flags , const char * esc )
{
char * out = * dst , * p = out ;
bool is_dict = esc & & * esc ;
int ret = 0 ;
while ( isz - - ) {
unsigned char c = * src + + ;
/*
* Apply rules in the following sequence :
* - the character is printable , when @ flags has
* % ESCAPE_NP bit set
* - the @ esc string is supplied and does not contain a
* character under question
* - the character doesn ' t fall into a class of symbols
* defined by given @ flags
* In these cases we just pass through a character to the
* output buffer .
*/
if ( ( flags & ESCAPE_NP & & isprint ( c ) ) | |
( is_dict & & ! strchr ( esc , c ) ) ) {
/* do nothing */
} else {
if ( flags & ESCAPE_SPACE ) {
ret = escape_space ( c , & p , & osz ) ;
if ( ret < 0 )
break ;
if ( ret > 0 )
continue ;
}
if ( flags & ESCAPE_SPECIAL ) {
ret = escape_special ( c , & p , & osz ) ;
if ( ret < 0 )
break ;
if ( ret > 0 )
continue ;
}
if ( flags & ESCAPE_NULL ) {
ret = escape_null ( c , & p , & osz ) ;
if ( ret < 0 )
break ;
if ( ret > 0 )
continue ;
}
/* ESCAPE_OCTAL and ESCAPE_HEX always go last */
if ( flags & ESCAPE_OCTAL ) {
ret = escape_octal ( c , & p , & osz ) ;
if ( ret < 0 )
break ;
continue ;
}
if ( flags & ESCAPE_HEX ) {
ret = escape_hex ( c , & p , & osz ) ;
if ( ret < 0 )
break ;
continue ;
}
}
ret = escape_passthrough ( c , & p , & osz ) ;
if ( ret < 0 )
break ;
}
* dst = p ;
if ( ret < 0 )
return ret ;
return p - out ;
}
EXPORT_SYMBOL ( string_escape_mem ) ;