Use strchr() when searching for a single character
[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     size_t length;
80
81     ret = snprintf(buffer, size, "%.17g", value);
82     if(ret < 0)
83         return -1;
84
85     length = (size_t)ret;
86     if(length >= size)
87         return -1;
88
89 #if JSON_HAVE_LOCALECONV
90     from_locale(buffer);
91 #endif
92
93     /* Make sure there's a dot or 'e' in the output. Otherwise
94        a real is converted to an integer when decoding */
95     if(strchr(buffer, '.') == NULL &&
96        strchr(buffer, 'e') == NULL)
97     {
98         if(length + 2 >= size) {
99             /* No space to append ".0" */
100             return -1;
101         }
102         buffer[length] = '.';
103         buffer[length + 1] = '0';
104         length += 2;
105     }
106
107     return (int)length;
108 }