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