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
26static 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
42static 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
59int 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
81int 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