@ -31,6 +31,7 @@
# include <float.h>
# include <limits.h>
# include <ctype.h>
# include <locale.h>
# pragma GCC visibility pop
# include "cJSON.h"
@ -177,19 +178,63 @@ CJSON_PUBLIC(void) cJSON_Delete(cJSON *c)
}
}
/* get the decimal point character of the current locale */
static unsigned char get_decimal_point ( void )
{
struct lconv * lconv = localeconv ( ) ;
return ( unsigned char ) lconv - > decimal_point [ 0 ] ;
}
/* Parse the input text to generate a number, and populate the result into item. */
static const unsigned char * parse_number ( cJSON * const item , const unsigned char * const input )
{
double number = 0 ;
unsigned char * after_end = NULL ;
unsigned char number_c_string [ 64 ] ;
unsigned char decimal_point = get_decimal_point ( ) ;
size_t i = 0 ;
if ( input = = NULL )
{
return NULL ;
}
number = strtod ( ( const char * ) input , ( char * * ) & after_end ) ;
if ( input = = after_end )
/* copy the number into a temporary buffer and replace '.' with the decimal point
* of the current locale ( for strtod ) */
for ( i = 0 ; ( i < ( sizeof ( number_c_string ) - 1 ) ) & & ( input [ i ] ! = ' \0 ' ) ; i + + )
{
switch ( input [ i ] )
{
case ' 0 ' :
case ' 1 ' :
case ' 2 ' :
case ' 3 ' :
case ' 4 ' :
case ' 5 ' :
case ' 6 ' :
case ' 7 ' :
case ' 8 ' :
case ' 9 ' :
case ' + ' :
case ' - ' :
case ' e ' :
case ' E ' :
number_c_string [ i ] = input [ i ] ;
break ;
case ' . ' :
number_c_string [ i ] = decimal_point ;
break ;
default :
goto loop_end ;
}
}
loop_end :
number_c_string [ i ] = ' \0 ' ;
number = strtod ( ( const char * ) number_c_string , ( char * * ) & after_end ) ;
if ( number_c_string = = after_end )
{
return NULL ; /* parse_error */
}
@ -212,7 +257,7 @@ static const unsigned char *parse_number(cJSON * const item, const unsigned char
item - > type = cJSON_Number ;
return after_end ;
return input + ( after_end - number_c_string ) ;
}
/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */
@ -336,34 +381,24 @@ static void update_offset(printbuffer * const buffer)
}
/* Removes trailing zeroes from the end of a printed number */
static cJSON_bool trim_trailing_zeroes ( printbuffer * const buffer )
static int trim_trailing_zeroes ( const unsigned char * const number , int length , const unsigned char decimal_point )
{
size_t offset = 0 ;
unsigned char * content = NULL ;
if ( ( buffer = = NULL ) | | ( buffer - > buffer = = NULL ) | | ( buffer - > offset < 1 ) )
if ( ( number = = NULL ) | | ( length < = 0 ) )
{
return false ;
return - 1 ;
}
offset = buffer - > offset - 1 ;
content = buffer - > buffer ;
while ( ( offset > 0 ) & & ( content [ offset ] = = ' 0 ' ) )
while ( ( length > 0 ) & & ( number [ length - 1 ] = = ' 0 ' ) )
{
offset - - ;
length - - ;
}
if ( ( offset > 0 ) & & ( content [ offset ] = = ' . ' ) )
if ( ( length > 0 ) & & ( number [ length - 1 ] = = decimal_point ) )
{
offset - - ;
/* remove trailing decimal_point */
length - - ;
}
offset + + ;
content [ offset ] = ' \0 ' ;
buffer - > offset = offset ;
return true ;
return length ;
}
/* Render the number nicely from the given item into a string. */
@ -372,53 +407,74 @@ static cJSON_bool print_number(const cJSON * const item, printbuffer * const out
unsigned char * output_pointer = NULL ;
double d = item - > valuedouble ;
int length = 0 ;
cJSON_bool trim_zeroes = true ; /* should at the end be removed? */
size_t i = 0 ;
cJSON_bool trim_zeroes = true ; /* should zeroes at the end be removed? */
unsigned char number_buffer [ 64 ] ; /* temporary buffer to print the number into */
unsigned char decimal_point = get_decimal_point ( ) ;
if ( output_buffer = = NULL )
{
return false ;
}
/* This is a nice tradeoff. */
output_pointer = ensure ( output_buffer , 64 , hooks ) ;
if ( output_pointer = = NULL )
{
return false ;
}
/* This checks for NaN and Infinity */
if ( ( d * 0 ) ! = 0 )
{
length = sprintf ( ( char * ) output_point er, " null " ) ;
length = sprintf ( ( char * ) number_buffer , " null " ) ;
}
else if ( ( fabs ( floor ( d ) - d ) < = DBL_EPSILON ) & & ( fabs ( d ) < 1.0e60 ) )
{
/* integer */
length = sprintf ( ( char * ) output_point er, " %.0f " , d ) ;
length = sprintf ( ( char * ) number_buff er, " %.0f " , d ) ;
trim_zeroes = false ; /* don't remove zeroes for "big integers" */
}
else if ( ( fabs ( d ) < 1.0e-6 ) | | ( fabs ( d ) > 1.0e9 ) )
{
length = sprintf ( ( char * ) output_point er, " %e " , d ) ;
length = sprintf ( ( char * ) number_buff er, " %e " , d ) ;
trim_zeroes = false ; /* don't remove zeroes in engineering notation */
}
else
{
length = sprintf ( ( char * ) output_point er, " %f " , d ) ;
length = sprintf ( ( char * ) number_buff er, " %f " , d ) ;
}
/* sprintf failed */
if ( length < 0 )
/* sprintf failed or buffer overrun occured */
if ( ( length < 0 ) | | ( length > ( int ) ( sizeof ( number_buffer ) - 1 ) ) )
{
return false ;
}
output_buffer - > offset + = ( size_t ) length ;
if ( trim_zeroes )
{
return trim_trailing_zeroes ( output_buffer ) ;
length = trim_trailing_zeroes ( number_buffer , length , decimal_point ) ;
if ( length < = 0 )
{
return false ;
}
}
/* reserve appropriate space in the output */
output_pointer = ensure ( output_buffer , ( size_t ) length , hooks ) ;
if ( output_pointer = = NULL )
{
return false ;
}
/* copy the printed number to the output and replace locale
* dependent decimal point with ' . ' */
for ( i = 0 ; i < ( ( size_t ) length ) ; i + + )
{
if ( number_buffer [ i ] = = decimal_point )
{
output_pointer [ i ] = ' . ' ;
continue ;
}
output_pointer [ i ] = number_buffer [ i ] ;
}
output_pointer [ i ] = ' \0 ' ;
output_buffer - > offset + = ( size_t ) length ;
return true ;
}