blob: ab2579982edc1ee159774fa1d05eefb2ee547deb [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
41PyOS_ascii_strtod(const char *nptr,
42 char **endptr)
43{
44 char *fail_pos;
45 double val;
46 struct lconv *locale_data;
47 const char *decimal_point;
48 int decimal_point_len;
49 const char *p, *decimal_point_pos;
50 const char *end = NULL; /* Silence gcc */
51
52/* g_return_val_if_fail (nptr != NULL, 0); */
53 assert(nptr != NULL);
54
55 fail_pos = NULL;
56
57 locale_data = localeconv();
58 decimal_point = locale_data->decimal_point;
59 decimal_point_len = strlen(decimal_point);
60
61 assert(decimal_point_len != 0);
62
63 decimal_point_pos = NULL;
64 if (decimal_point[0] != '.' ||
65 decimal_point[1] != 0)
66 {
67 p = nptr;
68 /* Skip leading space */
69 while (ISSPACE(*p))
70 p++;
71
72 /* Skip leading optional sign */
73 if (*p == '+' || *p == '-')
74 p++;
75
76 if (p[0] == '0' &&
77 (p[1] == 'x' || p[1] == 'X'))
78 {
79 p += 2;
80 /* HEX - find the (optional) decimal point */
81
82 while (ISXDIGIT(*p))
83 p++;
84
85 if (*p == '.')
86 {
87 decimal_point_pos = p++;
88
89 while (ISXDIGIT(*p))
90 p++;
91
92 if (*p == 'p' || *p == 'P')
93 p++;
94 if (*p == '+' || *p == '-')
95 p++;
96 while (ISDIGIT(*p))
97 p++;
98 end = p;
99 }
100 }
101 else
102 {
103 while (ISDIGIT(*p))
104 p++;
105
106 if (*p == '.')
107 {
108 decimal_point_pos = p++;
109
110 while (ISDIGIT(*p))
111 p++;
112
113 if (*p == 'e' || *p == 'E')
114 p++;
115 if (*p == '+' || *p == '-')
116 p++;
117 while (ISDIGIT(*p))
118 p++;
119 end = p;
120 }
121 }
122 /* For the other cases, we need not convert the decimal point */
123 }
124
125 /* Set errno to zero, so that we can distinguish zero results
126 and underflows */
127 errno = 0;
128
129 if (decimal_point_pos)
130 {
131 char *copy, *c;
132
133 /* We need to convert the '.' to the locale specific decimal point */
134 copy = malloc(end - nptr + 1 + decimal_point_len);
135
136 c = copy;
137 memcpy(c, nptr, decimal_point_pos - nptr);
138 c += decimal_point_pos - nptr;
139 memcpy(c, decimal_point, decimal_point_len);
140 c += decimal_point_len;
141 memcpy(c, decimal_point_pos + 1, end - (decimal_point_pos + 1));
142 c += end - (decimal_point_pos + 1);
143 *c = 0;
144
145 val = strtod(copy, &fail_pos);
146
147 if (fail_pos)
148 {
149 if (fail_pos > decimal_point_pos)
150 fail_pos = (char *)nptr + (fail_pos - copy) - (decimal_point_len - 1);
151 else
152 fail_pos = (char *)nptr + (fail_pos - copy);
153 }
154
155 free(copy);
156
157 }
158 else
159 val = strtod(nptr, &fail_pos);
160
161 if (endptr)
162 *endptr = fail_pos;
163
164 return val;
165}
166
167
168/**
169 * PyOS_ascii_formatd:
170 * @buffer: A buffer to place the resulting string in
171 * @buf_len: The length of the buffer.
172 * @format: The printf()-style format to use for the
173 * code to use for converting.
174 * @d: The #gdouble to convert
175 *
176 * Converts a #gdouble to a string, using the '.' as
177 * decimal point. To format the number you pass in
178 * a printf()-style format string. Allowed conversion
179 * specifiers are 'e', 'E', 'f', 'F', 'g' and 'G'.
180 *
181 * Return value: The pointer to the buffer with the converted string.
182 **/
183char *
184PyOS_ascii_formatd(char *buffer,
185 int buf_len,
186 const char *format,
187 double d)
188{
189 struct lconv *locale_data;
190 const char *decimal_point;
191 int decimal_point_len;
192 char *p;
193 int rest_len;
194 char format_char;
195
196/* g_return_val_if_fail (buffer != NULL, NULL); */
197/* g_return_val_if_fail (format[0] == '%', NULL); */
198/* g_return_val_if_fail (strpbrk (format + 1, "'l%") == NULL, NULL); */
199
200 format_char = format[strlen(format) - 1];
201
202/* g_return_val_if_fail (format_char == 'e' || format_char == 'E' || */
203/* format_char == 'f' || format_char == 'F' || */
204/* format_char == 'g' || format_char == 'G', */
205/* NULL); */
206
207 if (format[0] != '%')
208 return NULL;
209
210 if (strpbrk(format + 1, "'l%"))
211 return NULL;
212
213 if (!(format_char == 'e' || format_char == 'E' ||
214 format_char == 'f' || format_char == 'F' ||
215 format_char == 'g' || format_char == 'G'))
216 return NULL;
217
218
219 PyOS_snprintf(buffer, buf_len, format, d);
220
221 locale_data = localeconv();
222 decimal_point = locale_data->decimal_point;
223 decimal_point_len = strlen(decimal_point);
224
225 assert(decimal_point_len != 0);
226
227 if (decimal_point[0] != '.' ||
228 decimal_point[1] != 0)
229 {
230 p = buffer;
231
232 if (*p == '+' || *p == '-')
233 p++;
234
235 while (isdigit((unsigned char)*p))
236 p++;
237
238 if (strncmp(p, decimal_point, decimal_point_len) == 0)
239 {
240 *p = '.';
241 p++;
242 if (decimal_point_len > 1) {
243 rest_len = strlen(p + (decimal_point_len - 1));
244 memmove(p, p + (decimal_point_len - 1),
245 rest_len);
246 p[rest_len] = 0;
247 }
248 }
249 }
250
251 return buffer;
252}
253
254double
255PyOS_ascii_atof(const char *nptr)
256{
257 return PyOS_ascii_strtod(nptr, NULL);
258}