blob: 14fb84ba715b8c5e9d59cfe7fc871316ce10e9d3 [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.
Thomas Wouters4d70c3d2006-06-08 14:42:34 +000034 * If memory allocation fails, %ENOMEM is stored in %errno.
Martin v. Löwis737ea822004-06-08 18:52:54 +000035 *
36 * This function resets %errno before calling strtod() so that
37 * you can reliably detect overflow and underflow.
38 *
39 * Return value: the #gdouble value.
40 **/
41double
Neal Norwitze7214a12005-12-18 05:03:17 +000042PyOS_ascii_strtod(const char *nptr, char **endptr)
Martin v. Löwis737ea822004-06-08 18:52:54 +000043{
44 char *fail_pos;
Neal Norwitz0e7a0ed2005-12-18 05:37:36 +000045 double val = -1.0;
Martin v. Löwis737ea822004-06-08 18:52:54 +000046 struct lconv *locale_data;
47 const char *decimal_point;
Neal Norwitzd39d8612006-01-08 01:03:36 +000048 size_t decimal_point_len;
Martin v. Löwis737ea822004-06-08 18:52:54 +000049 const char *p, *decimal_point_pos;
50 const char *end = NULL; /* Silence gcc */
Christian Heimesfaf2f632008-01-06 16:59:19 +000051 const char *digits_pos = NULL;
52 int negate = 0;
Martin v. Löwis737ea822004-06-08 18:52:54 +000053
Martin v. Löwis737ea822004-06-08 18:52:54 +000054 assert(nptr != NULL);
55
56 fail_pos = NULL;
57
58 locale_data = localeconv();
59 decimal_point = locale_data->decimal_point;
60 decimal_point_len = strlen(decimal_point);
61
62 assert(decimal_point_len != 0);
63
64 decimal_point_pos = NULL;
Christian Heimesfaf2f632008-01-06 16:59:19 +000065
66 /* We process any leading whitespace and the optional sign manually,
67 then pass the remainder to the system strtod. This ensures that
68 the result of an underflow has the correct sign. (bug #1725) */
69
70 p = nptr;
71 /* Skip leading space */
72 while (ISSPACE(*p))
73 p++;
74
75 /* Process leading sign, if present */
76 if (*p == '-') {
77 negate = 1;
78 p++;
79 } else if (*p == '+') {
80 p++;
81 }
82
83 /* What's left should begin with a digit, a decimal point, or one of
84 the letters i, I, n, N. It should not begin with 0x or 0X */
85 if ((!ISDIGIT(*p) &&
86 *p != '.' && *p != 'i' && *p != 'I' && *p != 'n' && *p != 'N')
87 ||
88 (*p == '0' && (p[1] == 'x' || p[1] == 'X')))
89 {
90 if (endptr)
91 *endptr = (char*)nptr;
92 errno = EINVAL;
93 return val;
94 }
95 digits_pos = p;
96
Martin v. Löwis737ea822004-06-08 18:52:54 +000097 if (decimal_point[0] != '.' ||
98 decimal_point[1] != 0)
99 {
Neal Norwitze7214a12005-12-18 05:03:17 +0000100 while (ISDIGIT(*p))
101 p++;
102
103 if (*p == '.')
Martin v. Löwis737ea822004-06-08 18:52:54 +0000104 {
Neal Norwitze7214a12005-12-18 05:03:17 +0000105 decimal_point_pos = p++;
Martin v. Löwis737ea822004-06-08 18:52:54 +0000106
Martin v. Löwis737ea822004-06-08 18:52:54 +0000107 while (ISDIGIT(*p))
108 p++;
109
Neal Norwitze7214a12005-12-18 05:03:17 +0000110 if (*p == 'e' || *p == 'E')
111 p++;
112 if (*p == '+' || *p == '-')
113 p++;
114 while (ISDIGIT(*p))
115 p++;
116 end = p;
Martin v. Löwis737ea822004-06-08 18:52:54 +0000117 }
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000118 else if (strncmp(p, decimal_point, decimal_point_len) == 0)
119 {
120 /* Python bug #1417699 */
Christian Heimesfaf2f632008-01-06 16:59:19 +0000121 if (endptr)
122 *endptr = (char*)nptr;
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000123 errno = EINVAL;
124 return val;
125 }
Neal Norwitze7214a12005-12-18 05:03:17 +0000126 /* For the other cases, we need not convert the decimal point */
Martin v. Löwis737ea822004-06-08 18:52:54 +0000127 }
128
Neal Norwitze7214a12005-12-18 05:03:17 +0000129 /* Set errno to zero, so that we can distinguish zero results
130 and underflows */
Martin v. Löwis737ea822004-06-08 18:52:54 +0000131 errno = 0;
132
133 if (decimal_point_pos)
134 {
135 char *copy, *c;
136
Neal Norwitze7214a12005-12-18 05:03:17 +0000137 /* We need to convert the '.' to the locale specific decimal point */
Christian Heimesfaf2f632008-01-06 16:59:19 +0000138 copy = (char *)PyMem_MALLOC(end - digits_pos +
139 1 + decimal_point_len);
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000140 if (copy == NULL) {
141 if (endptr)
142 *endptr = (char *)nptr;
143 errno = ENOMEM;
144 return val;
145 }
Martin v. Löwis737ea822004-06-08 18:52:54 +0000146
147 c = copy;
Christian Heimesfaf2f632008-01-06 16:59:19 +0000148 memcpy(c, digits_pos, decimal_point_pos - digits_pos);
149 c += decimal_point_pos - digits_pos;
Martin v. Löwis737ea822004-06-08 18:52:54 +0000150 memcpy(c, decimal_point, decimal_point_len);
151 c += decimal_point_len;
152 memcpy(c, decimal_point_pos + 1, end - (decimal_point_pos + 1));
153 c += end - (decimal_point_pos + 1);
154 *c = 0;
155
156 val = strtod(copy, &fail_pos);
157
158 if (fail_pos)
159 {
160 if (fail_pos > decimal_point_pos)
Christian Heimesfaf2f632008-01-06 16:59:19 +0000161 fail_pos = (char *)digits_pos +
162 (fail_pos - copy) -
163 (decimal_point_len - 1);
Martin v. Löwis737ea822004-06-08 18:52:54 +0000164 else
Christian Heimesfaf2f632008-01-06 16:59:19 +0000165 fail_pos = (char *)digits_pos +
166 (fail_pos - copy);
Martin v. Löwis737ea822004-06-08 18:52:54 +0000167 }
168
Thomas Wouters477c8d52006-05-27 19:21:47 +0000169 PyMem_FREE(copy);
Martin v. Löwis737ea822004-06-08 18:52:54 +0000170
171 }
Neal Norwitze7214a12005-12-18 05:03:17 +0000172 else {
Christian Heimesfaf2f632008-01-06 16:59:19 +0000173 val = strtod(digits_pos, &fail_pos);
Neal Norwitze7214a12005-12-18 05:03:17 +0000174 }
Martin v. Löwis737ea822004-06-08 18:52:54 +0000175
Christian Heimesfaf2f632008-01-06 16:59:19 +0000176 if (fail_pos == digits_pos)
177 fail_pos = (char *)nptr;
178
179 if (negate && fail_pos != nptr)
180 val = -val;
181
Martin v. Löwis737ea822004-06-08 18:52:54 +0000182 if (endptr)
183 *endptr = fail_pos;
184
185 return val;
186}
187
188
189/**
190 * PyOS_ascii_formatd:
191 * @buffer: A buffer to place the resulting string in
192 * @buf_len: The length of the buffer.
193 * @format: The printf()-style format to use for the
194 * code to use for converting.
195 * @d: The #gdouble to convert
196 *
197 * Converts a #gdouble to a string, using the '.' as
198 * decimal point. To format the number you pass in
199 * a printf()-style format string. Allowed conversion
200 * specifiers are 'e', 'E', 'f', 'F', 'g' and 'G'.
201 *
202 * Return value: The pointer to the buffer with the converted string.
203 **/
204char *
205PyOS_ascii_formatd(char *buffer,
Martin v. Löwis18e16552006-02-15 17:27:45 +0000206 size_t buf_len,
Martin v. Löwis737ea822004-06-08 18:52:54 +0000207 const char *format,
208 double d)
209{
210 struct lconv *locale_data;
211 const char *decimal_point;
Neal Norwitzd39d8612006-01-08 01:03:36 +0000212 size_t decimal_point_len, rest_len;
Martin v. Löwis737ea822004-06-08 18:52:54 +0000213 char *p;
Martin v. Löwis737ea822004-06-08 18:52:54 +0000214 char format_char;
215
216/* g_return_val_if_fail (buffer != NULL, NULL); */
217/* g_return_val_if_fail (format[0] == '%', NULL); */
218/* g_return_val_if_fail (strpbrk (format + 1, "'l%") == NULL, NULL); */
219
220 format_char = format[strlen(format) - 1];
221
222/* g_return_val_if_fail (format_char == 'e' || format_char == 'E' || */
223/* format_char == 'f' || format_char == 'F' || */
224/* format_char == 'g' || format_char == 'G', */
225/* NULL); */
226
227 if (format[0] != '%')
228 return NULL;
229
230 if (strpbrk(format + 1, "'l%"))
231 return NULL;
232
233 if (!(format_char == 'e' || format_char == 'E' ||
234 format_char == 'f' || format_char == 'F' ||
235 format_char == 'g' || format_char == 'G'))
236 return NULL;
237
238
239 PyOS_snprintf(buffer, buf_len, format, d);
240
241 locale_data = localeconv();
242 decimal_point = locale_data->decimal_point;
243 decimal_point_len = strlen(decimal_point);
244
245 assert(decimal_point_len != 0);
246
247 if (decimal_point[0] != '.' ||
248 decimal_point[1] != 0)
249 {
250 p = buffer;
251
252 if (*p == '+' || *p == '-')
253 p++;
254
255 while (isdigit((unsigned char)*p))
256 p++;
257
258 if (strncmp(p, decimal_point, decimal_point_len) == 0)
259 {
260 *p = '.';
261 p++;
262 if (decimal_point_len > 1) {
263 rest_len = strlen(p + (decimal_point_len - 1));
264 memmove(p, p + (decimal_point_len - 1),
265 rest_len);
266 p[rest_len] = 0;
267 }
268 }
269 }
270
271 return buffer;
272}
273
274double
275PyOS_ascii_atof(const char *nptr)
276{
277 return PyOS_ascii_strtod(nptr, NULL);
278}