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