|  | @@ -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_pointer, "null");
 | 
	
		
			
				|  |  | +        length = sprintf((char*)number_buffer, "null");
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      else if ((fabs(floor(d) - d) <= DBL_EPSILON) && (fabs(d) < 1.0e60))
 | 
	
		
			
				|  |  |      {
 | 
	
		
			
				|  |  |          /* integer */
 | 
	
		
			
				|  |  | -        length = sprintf((char*)output_pointer, "%.0f", d);
 | 
	
		
			
				|  |  | +        length = sprintf((char*)number_buffer, "%.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_pointer, "%e", d);
 | 
	
		
			
				|  |  | +        length = sprintf((char*)number_buffer, "%e", d);
 | 
	
		
			
				|  |  |          trim_zeroes = false; /* don't remove zeroes in engineering notation */
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      else
 | 
	
		
			
				|  |  |      {
 | 
	
		
			
				|  |  | -        length = sprintf((char*)output_pointer, "%f", d);
 | 
	
		
			
				|  |  | +        length = sprintf((char*)number_buffer, "%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;
 | 
	
		
			
				|  |  |  }
 |