0
|
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 |
|
27 static 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 |
|
43 static 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 |
|
60 int 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 |
|
82 int 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 } |