blob: 19f25dab97d5690119cde3374d4c6ef7f08aeb91 [file] [log] [blame]
Martin v. Löwis737ea822004-06-08 18:52:54 +00001/* -*- Mode: C; c-file-style: "python" -*- */
2
3#include <Python.h>
4#include <locale.h>
5
6/* ascii character tests (as opposed to locale tests) */
7#define ISSPACE(c) ((c) == ' ' || (c) == '\f' || (c) == '\n' || \
8 (c) == '\r' || (c) == '\t' || (c) == '\v')
9#define ISDIGIT(c) ((c) >= '0' && (c) <= '9')
10#define ISXDIGIT(c) (ISDIGIT(c) || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F'))
11
12
13/**
14 * PyOS_ascii_strtod:
15 * @nptr: the string to convert to a numeric value.
16 * @endptr: if non-%NULL, it returns the character after
17 * the last character used in the conversion.
18 *
19 * Converts a string to a #gdouble value.
20 * This function behaves like the standard strtod() function
21 * does in the C locale. It does this without actually
22 * changing the current locale, since that would not be
23 * thread-safe.
24 *
25 * This function is typically used when reading configuration
26 * files or other non-user input that should be locale independent.
27 * To handle input from the user you should normally use the
28 * locale-sensitive system strtod() function.
29 *
30 * If the correct value would cause overflow, plus or minus %HUGE_VAL
31 * is returned (according to the sign of the value), and %ERANGE is
32 * stored in %errno. If the correct value would cause underflow,
33 * zero is returned and %ERANGE is stored in %errno.
34 *
35 * This function resets %errno before calling strtod() so that
36 * you can reliably detect overflow and underflow.
37 *
38 * Return value: the #gdouble value.
39 **/
40double
Neal Norwitze7214a12005-12-18 05:03:17 +000041PyOS_ascii_strtod(const char *nptr, char **endptr)
Martin v. Löwis737ea822004-06-08 18:52:54 +000042{
43 char *fail_pos;
44 double val;
45 struct lconv *locale_data;
46 const char *decimal_point;
47 int decimal_point_len;
48 const char *p, *decimal_point_pos;
49 const char *end = NULL; /* Silence gcc */
50
Martin v. Löwis737ea822004-06-08 18:52:54 +000051 assert(nptr != NULL);
52
53 fail_pos = NULL;
54
55 locale_data = localeconv();
56 decimal_point = locale_data->decimal_point;
57 decimal_point_len = strlen(decimal_point);
58
59 assert(decimal_point_len != 0);
60
61 decimal_point_pos = NULL;
62 if (decimal_point[0] != '.' ||
63 decimal_point[1] != 0)
64 {
65 p = nptr;
66 /* Skip leading space */
67 while (ISSPACE(*p))
68 p++;
69
70 /* Skip leading optional sign */
71 if (*p == '+' || *p == '-')
72 p++;
73
Neal Norwitze7214a12005-12-18 05:03:17 +000074 while (ISDIGIT(*p))
75 p++;
76
77 if (*p == '.')
Martin v. Löwis737ea822004-06-08 18:52:54 +000078 {
Neal Norwitze7214a12005-12-18 05:03:17 +000079 decimal_point_pos = p++;
Martin v. Löwis737ea822004-06-08 18:52:54 +000080
Martin v. Löwis737ea822004-06-08 18:52:54 +000081 while (ISDIGIT(*p))
82 p++;
83
Neal Norwitze7214a12005-12-18 05:03:17 +000084 if (*p == 'e' || *p == 'E')
85 p++;
86 if (*p == '+' || *p == '-')
87 p++;
88 while (ISDIGIT(*p))
89 p++;
90 end = p;
Martin v. Löwis737ea822004-06-08 18:52:54 +000091 }
Neal Norwitze7214a12005-12-18 05:03:17 +000092 /* For the other cases, we need not convert the decimal point */
Martin v. Löwis737ea822004-06-08 18:52:54 +000093 }
94
Neal Norwitze7214a12005-12-18 05:03:17 +000095 /* Set errno to zero, so that we can distinguish zero results
96 and underflows */
Martin v. Löwis737ea822004-06-08 18:52:54 +000097 errno = 0;
98
99 if (decimal_point_pos)
100 {
101 char *copy, *c;
102
Neal Norwitze7214a12005-12-18 05:03:17 +0000103 /* We need to convert the '.' to the locale specific decimal point */
Martin v. Löwis737ea822004-06-08 18:52:54 +0000104 copy = malloc(end - nptr + 1 + decimal_point_len);
105
106 c = copy;
107 memcpy(c, nptr, decimal_point_pos - nptr);
108 c += decimal_point_pos - nptr;
109 memcpy(c, decimal_point, decimal_point_len);
110 c += decimal_point_len;
111 memcpy(c, decimal_point_pos + 1, end - (decimal_point_pos + 1));
112 c += end - (decimal_point_pos + 1);
113 *c = 0;
114
115 val = strtod(copy, &fail_pos);
116
117 if (fail_pos)
118 {
119 if (fail_pos > decimal_point_pos)
120 fail_pos = (char *)nptr + (fail_pos - copy) - (decimal_point_len - 1);
121 else
122 fail_pos = (char *)nptr + (fail_pos - copy);
123 }
124
125 free(copy);
126
127 }
Neal Norwitze7214a12005-12-18 05:03:17 +0000128 else {
129 unsigned i = 0;
130 if (nptr[i] == '-')
131 i++;
132 if (nptr[i] == '0' && (nptr[i+1] == 'x' || nptr[i+1] == 'X'))
133 fail_pos = nptr;
134 else
135 val = strtod(nptr, &fail_pos);
136 }
Martin v. Löwis737ea822004-06-08 18:52:54 +0000137
138 if (endptr)
139 *endptr = fail_pos;
140
141 return val;
142}
143
144
145/**
146 * PyOS_ascii_formatd:
147 * @buffer: A buffer to place the resulting string in
148 * @buf_len: The length of the buffer.
149 * @format: The printf()-style format to use for the
150 * code to use for converting.
151 * @d: The #gdouble to convert
152 *
153 * Converts a #gdouble to a string, using the '.' as
154 * decimal point. To format the number you pass in
155 * a printf()-style format string. Allowed conversion
156 * specifiers are 'e', 'E', 'f', 'F', 'g' and 'G'.
157 *
158 * Return value: The pointer to the buffer with the converted string.
159 **/
160char *
161PyOS_ascii_formatd(char *buffer,
162 int buf_len,
163 const char *format,
164 double d)
165{
166 struct lconv *locale_data;
167 const char *decimal_point;
168 int decimal_point_len;
169 char *p;
170 int rest_len;
171 char format_char;
172
173/* g_return_val_if_fail (buffer != NULL, NULL); */
174/* g_return_val_if_fail (format[0] == '%', NULL); */
175/* g_return_val_if_fail (strpbrk (format + 1, "'l%") == NULL, NULL); */
176
177 format_char = format[strlen(format) - 1];
178
179/* g_return_val_if_fail (format_char == 'e' || format_char == 'E' || */
180/* format_char == 'f' || format_char == 'F' || */
181/* format_char == 'g' || format_char == 'G', */
182/* NULL); */
183
184 if (format[0] != '%')
185 return NULL;
186
187 if (strpbrk(format + 1, "'l%"))
188 return NULL;
189
190 if (!(format_char == 'e' || format_char == 'E' ||
191 format_char == 'f' || format_char == 'F' ||
192 format_char == 'g' || format_char == 'G'))
193 return NULL;
194
195
196 PyOS_snprintf(buffer, buf_len, format, d);
197
198 locale_data = localeconv();
199 decimal_point = locale_data->decimal_point;
200 decimal_point_len = strlen(decimal_point);
201
202 assert(decimal_point_len != 0);
203
204 if (decimal_point[0] != '.' ||
205 decimal_point[1] != 0)
206 {
207 p = buffer;
208
209 if (*p == '+' || *p == '-')
210 p++;
211
212 while (isdigit((unsigned char)*p))
213 p++;
214
215 if (strncmp(p, decimal_point, decimal_point_len) == 0)
216 {
217 *p = '.';
218 p++;
219 if (decimal_point_len > 1) {
220 rest_len = strlen(p + (decimal_point_len - 1));
221 memmove(p, p + (decimal_point_len - 1),
222 rest_len);
223 p[rest_len] = 0;
224 }
225 }
226 }
227
228 return buffer;
229}
230
231double
232PyOS_ascii_atof(const char *nptr)
233{
234 return PyOS_ascii_strtod(nptr, NULL);
235}