1#include <assert.h> 2#include <errno.h> 3#include <stdio.h> 4#include <string.h> 5#include <math.h> 6#include "jansson_private.h" 7#include "strbuffer.h" 8 9/* need jansson_private_config.h to get the correct snprintf */ 10#ifdef HAVE_CONFIG_H 11#include <jansson_private_config.h> 12#endif 13 14#if JSON_HAVE_LOCALECONV 15#include <locale.h> 16 17/* 18 - This code assumes that the decimal separator is exactly one 19 character. 20 21 - If setlocale() is called by another thread between the call to 22 localeconv() and the call to sprintf() or strtod(), the result may 23 be wrong. setlocale() is not thread-safe and should not be used 24 this way. Multi-threaded programs should use uselocale() instead. 25*/ 26 27static void to_locale(strbuffer_t *strbuffer) 28{ 29 const char *point; 30 char *pos; 31 32 point = localeconv()->decimal_point; 33 if(*point == '.') { 34 /* No conversion needed */ 35 return; 36 } 37 38 pos = strchr(strbuffer->value, '.'); 39 if(pos) 40 *pos = *point; 41} 42 43static void from_locale(char *buffer) 44{ 45 const char *point; 46 char *pos; 47 48 point = localeconv()->decimal_point; 49 if(*point == '.') { 50 /* No conversion needed */ 51 return; 52 } 53 54 pos = strchr(buffer, *point); 55 if(pos) 56 *pos = '.'; 57} 58#endif 59 60int jsonp_strtod(strbuffer_t *strbuffer, double *out) 61{ 62 double value; 63 char *end; 64 65#if JSON_HAVE_LOCALECONV 66 to_locale(strbuffer); 67#endif 68 69 errno = 0; 70 value = strtod(strbuffer->value, &end); 71 assert(end == strbuffer->value + strbuffer->length); 72 73 if((value == HUGE_VAL || value == -HUGE_VAL) && errno == ERANGE) { 74 /* Overflow */ 75 return -1; 76 } 77 78 *out = value; 79 return 0; 80} 81 82int jsonp_dtostr(char *buffer, size_t size, double value, int precision) 83{ 84 int ret; 85 char *start, *end; 86 size_t length; 87 88 if (precision == 0) 89 precision = 17; 90 91 ret = snprintf(buffer, size, "%.*g", precision, value); 92 if(ret < 0) 93 return -1; 94 95 length = (size_t)ret; 96 if(length >= size) 97 return -1; 98 99#if JSON_HAVE_LOCALECONV 100 from_locale(buffer); 101#endif 102 103 /* Make sure there's a dot or 'e' in the output. Otherwise 104 a real is converted to an integer when decoding */ 105 if(strchr(buffer, '.') == NULL && 106 strchr(buffer, 'e') == NULL) 107 { 108 if(length + 3 >= size) { 109 /* No space to append ".0" */ 110 return -1; 111 } 112 buffer[length] = '.'; 113 buffer[length + 1] = '0'; 114 buffer[length + 2] = '\0'; 115 length += 2; 116 } 117 118 /* Remove leading '+' from positive exponent. Also remove leading 119 zeros from exponents (added by some printf() implementations) */ 120 start = strchr(buffer, 'e'); 121 if(start) { 122 start++; 123 end = start + 1; 124 125 if(*start == '-') 126 start++; 127 128 while(*end == '0') 129 end++; 130 131 if(end != start) { 132 memmove(start, end, length - (size_t)(end - buffer)); 133 length -= (size_t)(end - start); 134 } 135 } 136 137 return (int)length; 138} 139