xref: /NextBSD/contrib/jansson/src/strconv.c (revision 33da5adc555b3bc29986eeadca03829e4ad06b1e)
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 
to_locale(strbuffer_t * strbuffer)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 
from_locale(char * buffer)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 
jsonp_strtod(strbuffer_t * strbuffer,double * out)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 
jsonp_dtostr(char * buffer,size_t size,double value,int precision)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 }
139