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