blob: 6c19b45fd260070591307da151e8ab0639b7b8f8 [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 */
51
Martin v. Löwis737ea822004-06-08 18:52:54 +000052 assert(nptr != NULL);
53
54 fail_pos = NULL;
55
56 locale_data = localeconv();
57 decimal_point = locale_data->decimal_point;
58 decimal_point_len = strlen(decimal_point);
59
60 assert(decimal_point_len != 0);
61
62 decimal_point_pos = NULL;
63 if (decimal_point[0] != '.' ||
64 decimal_point[1] != 0)
65 {
66 p = nptr;
67 /* Skip leading space */
68 while (ISSPACE(*p))
69 p++;
70
71 /* Skip leading optional sign */
72 if (*p == '+' || *p == '-')
73 p++;
74
Neal Norwitze7214a12005-12-18 05:03:17 +000075 while (ISDIGIT(*p))
76 p++;
77
78 if (*p == '.')
Martin v. Löwis737ea822004-06-08 18:52:54 +000079 {
Neal Norwitze7214a12005-12-18 05:03:17 +000080 decimal_point_pos = p++;
Martin v. Löwis737ea822004-06-08 18:52:54 +000081
Martin v. Löwis737ea822004-06-08 18:52:54 +000082 while (ISDIGIT(*p))
83 p++;
84
Neal Norwitze7214a12005-12-18 05:03:17 +000085 if (*p == 'e' || *p == 'E')
86 p++;
87 if (*p == '+' || *p == '-')
88 p++;
89 while (ISDIGIT(*p))
90 p++;
91 end = p;
Martin v. Löwis737ea822004-06-08 18:52:54 +000092 }
Thomas Wouters0e3f5912006-08-11 14:57:12 +000093 else if (strncmp(p, decimal_point, decimal_point_len) == 0)
94 {
95 /* Python bug #1417699 */
96 *endptr = (char*)nptr;
97 errno = EINVAL;
98 return val;
99 }
Neal Norwitze7214a12005-12-18 05:03:17 +0000100 /* For the other cases, we need not convert the decimal point */
Martin v. Löwis737ea822004-06-08 18:52:54 +0000101 }
102
Neal Norwitze7214a12005-12-18 05:03:17 +0000103 /* Set errno to zero, so that we can distinguish zero results
104 and underflows */
Martin v. Löwis737ea822004-06-08 18:52:54 +0000105 errno = 0;
106
107 if (decimal_point_pos)
108 {
109 char *copy, *c;
110
Neal Norwitze7214a12005-12-18 05:03:17 +0000111 /* We need to convert the '.' to the locale specific decimal point */
Thomas Wouters477c8d52006-05-27 19:21:47 +0000112 copy = (char *)PyMem_MALLOC(end - nptr + 1 + decimal_point_len);
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000113 if (copy == NULL) {
114 if (endptr)
115 *endptr = (char *)nptr;
116 errno = ENOMEM;
117 return val;
118 }
Martin v. Löwis737ea822004-06-08 18:52:54 +0000119
120 c = copy;
121 memcpy(c, nptr, decimal_point_pos - nptr);
122 c += decimal_point_pos - nptr;
123 memcpy(c, decimal_point, decimal_point_len);
124 c += decimal_point_len;
125 memcpy(c, decimal_point_pos + 1, end - (decimal_point_pos + 1));
126 c += end - (decimal_point_pos + 1);
127 *c = 0;
128
129 val = strtod(copy, &fail_pos);
130
131 if (fail_pos)
132 {
133 if (fail_pos > decimal_point_pos)
134 fail_pos = (char *)nptr + (fail_pos - copy) - (decimal_point_len - 1);
135 else
136 fail_pos = (char *)nptr + (fail_pos - copy);
137 }
138
Thomas Wouters477c8d52006-05-27 19:21:47 +0000139 PyMem_FREE(copy);
Martin v. Löwis737ea822004-06-08 18:52:54 +0000140
141 }
Neal Norwitze7214a12005-12-18 05:03:17 +0000142 else {
143 unsigned i = 0;
144 if (nptr[i] == '-')
145 i++;
146 if (nptr[i] == '0' && (nptr[i+1] == 'x' || nptr[i+1] == 'X'))
Neal Norwitz0e7a0ed2005-12-18 05:37:36 +0000147 fail_pos = (char*)nptr;
Neal Norwitze7214a12005-12-18 05:03:17 +0000148 else
149 val = strtod(nptr, &fail_pos);
150 }
Martin v. Löwis737ea822004-06-08 18:52:54 +0000151
152 if (endptr)
153 *endptr = fail_pos;
154
155 return val;
156}
157
158
159/**
160 * PyOS_ascii_formatd:
161 * @buffer: A buffer to place the resulting string in
162 * @buf_len: The length of the buffer.
163 * @format: The printf()-style format to use for the
164 * code to use for converting.
165 * @d: The #gdouble to convert
166 *
167 * Converts a #gdouble to a string, using the '.' as
168 * decimal point. To format the number you pass in
169 * a printf()-style format string. Allowed conversion
170 * specifiers are 'e', 'E', 'f', 'F', 'g' and 'G'.
171 *
172 * Return value: The pointer to the buffer with the converted string.
173 **/
174char *
175PyOS_ascii_formatd(char *buffer,
Martin v. Löwis18e16552006-02-15 17:27:45 +0000176 size_t buf_len,
Martin v. Löwis737ea822004-06-08 18:52:54 +0000177 const char *format,
178 double d)
179{
180 struct lconv *locale_data;
181 const char *decimal_point;
Neal Norwitzd39d8612006-01-08 01:03:36 +0000182 size_t decimal_point_len, rest_len;
Martin v. Löwis737ea822004-06-08 18:52:54 +0000183 char *p;
Martin v. Löwis737ea822004-06-08 18:52:54 +0000184 char format_char;
185
186/* g_return_val_if_fail (buffer != NULL, NULL); */
187/* g_return_val_if_fail (format[0] == '%', NULL); */
188/* g_return_val_if_fail (strpbrk (format + 1, "'l%") == NULL, NULL); */
189
190 format_char = format[strlen(format) - 1];
191
192/* g_return_val_if_fail (format_char == 'e' || format_char == 'E' || */
193/* format_char == 'f' || format_char == 'F' || */
194/* format_char == 'g' || format_char == 'G', */
195/* NULL); */
196
197 if (format[0] != '%')
198 return NULL;
199
200 if (strpbrk(format + 1, "'l%"))
201 return NULL;
202
203 if (!(format_char == 'e' || format_char == 'E' ||
204 format_char == 'f' || format_char == 'F' ||
205 format_char == 'g' || format_char == 'G'))
206 return NULL;
207
208
209 PyOS_snprintf(buffer, buf_len, format, d);
210
211 locale_data = localeconv();
212 decimal_point = locale_data->decimal_point;
213 decimal_point_len = strlen(decimal_point);
214
215 assert(decimal_point_len != 0);
216
217 if (decimal_point[0] != '.' ||
218 decimal_point[1] != 0)
219 {
220 p = buffer;
221
222 if (*p == '+' || *p == '-')
223 p++;
224
225 while (isdigit((unsigned char)*p))
226 p++;
227
228 if (strncmp(p, decimal_point, decimal_point_len) == 0)
229 {
230 *p = '.';
231 p++;
232 if (decimal_point_len > 1) {
233 rest_len = strlen(p + (decimal_point_len - 1));
234 memmove(p, p + (decimal_point_len - 1),
235 rest_len);
236 p[rest_len] = 0;
237 }
238 }
239 }
240
241 return buffer;
242}
243
244double
245PyOS_ascii_atof(const char *nptr)
246{
247 return PyOS_ascii_strtod(nptr, NULL);
248}