? Include/pystrtod.h ? Python/pystrtod.c Index: Makefile.pre.in =================================================================== RCS file: /cvsroot/python/python/dist/src/Makefile.pre.in,v retrieving revision 1.144 diff -u -p -r1.144 Makefile.pre.in --- Makefile.pre.in 3 Jun 2004 12:41:45 -0000 1.144 +++ Makefile.pre.in 6 Jun 2004 11:51:57 -0000 @@ -247,6 +247,7 @@ PYTHON_OBJS= \ Python/sysmodule.o \ Python/traceback.o \ Python/getopt.o \ + Python/pystrtod.o \ Python/$(DYNLOADFILE) \ $(MACHDEP_OBJS) \ $(THREADOBJ) Index: Include/Python.h =================================================================== RCS file: /cvsroot/python/python/dist/src/Include/Python.h,v retrieving revision 2.62 diff -u -p -r2.62 Python.h --- Include/Python.h 13 Mar 2004 23:11:44 -0000 2.62 +++ Include/Python.h 6 Jun 2004 11:51:57 -0000 @@ -119,6 +119,8 @@ #include "compile.h" #include "eval.h" +#include "pystrtod.h" + /* _Py_Mangle is defined in compile.c */ PyAPI_FUNC(int) _Py_Mangle(char *p, char *name, \ char *buffer, size_t maxlen); Index: Modules/_localemodule.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Modules/_localemodule.c,v retrieving revision 2.45 diff -u -p -r2.45 _localemodule.c --- Modules/_localemodule.c 21 Mar 2004 19:34:30 -0000 2.45 +++ Modules/_localemodule.c 6 Jun 2004 11:51:59 -0000 @@ -51,13 +51,6 @@ static PyObject *Error; PyDoc_STRVAR(setlocale__doc__, "(integer,string=None) -> string. Activates/queries locale processing."); -/* to record the LC_NUMERIC settings */ -static PyObject* grouping = NULL; -static PyObject* thousands_sep = NULL; -static PyObject* decimal_point = NULL; -/* if non-null, indicates that LC_NUMERIC is different from "C" */ -static char* saved_numeric = NULL; - /* the grouping is terminated by either 0 or CHAR_MAX */ static PyObject* copy_grouping(char* s) @@ -167,7 +160,6 @@ PyLocale_setlocale(PyObject* self, PyObj int category; char *locale = NULL, *result; PyObject *result_object; - struct lconv *lc; if (!PyArg_ParseTuple(args, "i|z:setlocale", &category, &locale)) return NULL; @@ -183,29 +175,6 @@ PyLocale_setlocale(PyObject* self, PyObj result_object = PyString_FromString(result); if (!result_object) return NULL; - /* record changes to LC_NUMERIC */ - if (category == LC_NUMERIC || category == LC_ALL) { - if (strcmp(locale, "C") == 0 || strcmp(locale, "POSIX") == 0) { - /* user just asked for default numeric locale */ - if (saved_numeric) - free(saved_numeric); - saved_numeric = NULL; - } else { - /* remember values */ - lc = localeconv(); - Py_XDECREF(grouping); - grouping = copy_grouping(lc->grouping); - Py_XDECREF(thousands_sep); - thousands_sep = PyString_FromString(lc->thousands_sep); - Py_XDECREF(decimal_point); - decimal_point = PyString_FromString(lc->decimal_point); - if (saved_numeric) - free(saved_numeric); - saved_numeric = strdup(locale); - /* restore to "C" */ - setlocale(LC_NUMERIC, "C"); - } - } /* record changes to LC_CTYPE */ if (category == LC_CTYPE || category == LC_ALL) fixup_ulcase(); @@ -213,18 +182,12 @@ PyLocale_setlocale(PyObject* self, PyObj PyErr_Clear(); } else { /* get locale */ - /* restore LC_NUMERIC first, if appropriate */ - if (saved_numeric) - setlocale(LC_NUMERIC, saved_numeric); result = setlocale(category, NULL); if (!result) { PyErr_SetString(Error, "locale query failed"); return NULL; } result_object = PyString_FromString(result); - /* restore back to "C" */ - if (saved_numeric) - setlocale(LC_NUMERIC, "C"); } return result_object; } @@ -262,20 +225,13 @@ PyLocale_localeconv(PyObject* self) Py_XDECREF(x) /* Numeric information */ - if (saved_numeric){ - /* cannot use localeconv results */ - PyDict_SetItemString(result, "decimal_point", decimal_point); - PyDict_SetItemString(result, "grouping", grouping); - PyDict_SetItemString(result, "thousands_sep", thousands_sep); - } else { - RESULT_STRING(decimal_point); - RESULT_STRING(thousands_sep); - x = copy_grouping(l->grouping); - if (!x) - goto failed; - PyDict_SetItemString(result, "grouping", x); - Py_XDECREF(x); - } + RESULT_STRING(decimal_point); + RESULT_STRING(thousands_sep); + x = copy_grouping(l->grouping); + if (!x) + goto failed; + PyDict_SetItemString(result, "grouping", x); + Py_XDECREF(x); /* Monetary information */ RESULT_STRING(int_curr_symbol); @@ -579,18 +535,6 @@ PyLocale_nl_langinfo(PyObject* self, PyO /* Check whether this is a supported constant. GNU libc sometimes returns numeric values in the char* return value, which would crash PyString_FromString. */ -#ifdef RADIXCHAR - if (saved_numeric) { - if(item == RADIXCHAR) { - Py_INCREF(decimal_point); - return decimal_point; - } - if(item == THOUSEP) { - Py_INCREF(thousands_sep); - return thousands_sep; - } - } -#endif for (i = 0; langinfo_constants[i].name; i++) if (langinfo_constants[i].value == item) { /* Check NULL as a workaround for GNU libc's returning NULL Index: Modules/cPickle.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Modules/cPickle.c,v retrieving revision 2.149 diff -u -p -r2.149 cPickle.c --- Modules/cPickle.c 26 Feb 2004 16:21:45 -0000 2.149 +++ Modules/cPickle.c 6 Jun 2004 11:52:01 -0000 @@ -3319,7 +3319,7 @@ load_float(Unpicklerobject *self) if (!( s=pystrndup(s,len))) return -1; errno = 0; - d = strtod(s, &endptr); + d = PyOS_ascii_strtod(s, &endptr); if (errno || (endptr[0] != '\n') || (endptr[1] != '\0')) { PyErr_SetString(PyExc_ValueError, Index: Modules/stropmodule.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Modules/stropmodule.c,v retrieving revision 2.90 diff -u -p -r2.90 stropmodule.c --- Modules/stropmodule.c 2 Aug 2002 02:27:13 -0000 2.90 +++ Modules/stropmodule.c 6 Jun 2004 11:52:02 -0000 @@ -838,7 +838,6 @@ PyDoc_STRVAR(atof__doc__, static PyObject * strop_atof(PyObject *self, PyObject *args) { - extern double strtod(const char *, char **); char *s, *end; double x; char buffer[256]; /* For errors */ @@ -854,7 +853,7 @@ strop_atof(PyObject *self, PyObject *arg } errno = 0; PyFPE_START_PROTECT("strop_atof", return 0) - x = strtod(s, &end); + x = PyOS_ascii_strtod(s, &end); PyFPE_END_PROTECT(x) while (*end && isspace(Py_CHARMASK(*end))) end++; Index: Objects/complexobject.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Objects/complexobject.c,v retrieving revision 2.70 diff -u -p -r2.70 complexobject.c --- Objects/complexobject.c 12 Oct 2003 19:09:37 -0000 2.70 +++ Objects/complexobject.c 6 Jun 2004 11:52:02 -0000 @@ -272,13 +272,19 @@ complex_dealloc(PyObject *op) static void complex_to_buf(char *buf, int bufsz, PyComplexObject *v, int precision) { - if (v->cval.real == 0.) - PyOS_snprintf(buf, bufsz, "%.*gj", - precision, v->cval.imag); - else - PyOS_snprintf(buf, bufsz, "(%.*g%+.*gj)", - precision, v->cval.real, - precision, v->cval.imag); + char format[32]; + if (v->cval.real == 0.) { + PyOS_snprintf(format, 32, "%%.%ig", precision); + PyOS_ascii_formatd(buf, bufsz, format, v->cval.imag); + strncat(buf, "j", bufsz); + } else { + char re[64], im[64]; + + PyOS_snprintf(format, 32, "%%.%ig", precision); + PyOS_ascii_formatd(re, 64, format, v->cval.real); + PyOS_ascii_formatd(im, 64, format, v->cval.imag); + PyOS_snprintf(buf, bufsz, "(%s+%sj)", re, im); + } } static int @@ -662,7 +668,6 @@ static PyMemberDef complex_members[] = { static PyObject * complex_subtype_from_string(PyTypeObject *type, PyObject *v) { - extern double strtod(const char *, char **); const char *s, *start; char *end; double x=0.0, y=0.0, z; @@ -774,7 +779,7 @@ complex_subtype_from_string(PyTypeObject } errno = 0; PyFPE_START_PROTECT("strtod", return 0) - z = strtod(s, &end) ; + z = PyOS_ascii_strtod(s, &end) ; PyFPE_END_PROTECT(z) if (errno != 0) { PyOS_snprintf(buffer, sizeof(buffer), Index: Objects/floatobject.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Objects/floatobject.c,v retrieving revision 2.130 diff -u -p -r2.130 floatobject.c --- Objects/floatobject.c 26 May 2004 17:36:12 -0000 2.130 +++ Objects/floatobject.c 6 Jun 2004 11:52:03 -0000 @@ -132,7 +132,7 @@ PyFloat_FromString(PyObject *v, char **p * key off errno. */ PyFPE_START_PROTECT("strtod", return NULL) - x = strtod(s, (char **)&end); + x = PyOS_ascii_strtod(s, (char **)&end); PyFPE_END_PROTECT(x) errno = 0; /* Believe it or not, Solaris 2.6 can move end *beyond* the null @@ -164,7 +164,7 @@ PyFloat_FromString(PyObject *v, char **p /* See above -- may have been strtod being anal about denorms. */ PyFPE_START_PROTECT("atof", return NULL) - x = atof(s); + x = PyOS_ascii_atof(s); PyFPE_END_PROTECT(x) errno = 0; /* whether atof ever set errno is undefined */ } @@ -223,6 +223,7 @@ static void format_float(char *buf, size_t buflen, PyFloatObject *v, int precision) { register char *cp; + char format[32]; /* Subroutine for float_repr and float_print. We want float numbers to be recognizable as such, i.e., they should contain a decimal point or an exponent. @@ -230,7 +231,8 @@ format_float(char *buf, size_t buflen, P in such cases, we append ".0" to the string. */ assert(PyFloat_Check(v)); - PyOS_snprintf(buf, buflen, "%.*g", precision, v->ob_fval); + PyOS_snprintf(format, 32, "%%.%ig", precision); + PyOS_ascii_formatd(buf, buflen, format, v->ob_fval); cp = buf; if (*cp == '-') cp++; Index: Objects/stringobject.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Objects/stringobject.c,v retrieving revision 2.217 diff -u -p -r2.217 stringobject.c --- Objects/stringobject.c 5 Jan 2004 00:29:51 -0000 2.217 +++ Objects/stringobject.c 6 Jun 2004 11:52:05 -0000 @@ -3582,7 +3582,7 @@ formatfloat(char *buf, size_t buflen, in PyOS_snprintf(fmt, sizeof(fmt), "%%%s.%d%c", (flags&F_ALT) ? "#" : "", prec, type); - PyOS_snprintf(buf, buflen, fmt, x); + PyOS_ascii_formatd(buf, buflen, fmt, x); return strlen(buf); } Index: Python/compile.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Python/compile.c,v retrieving revision 2.302 diff -u -p -r2.302 compile.c --- Python/compile.c 19 May 2004 08:20:15 -0000 2.302 +++ Python/compile.c 6 Jun 2004 11:52:07 -0000 @@ -1379,7 +1379,7 @@ parsenumber(struct compiling *c, char *s Py_complex z; z.real = 0.; PyFPE_START_PROTECT("atof", return 0) - z.imag = atof(s); + z.imag = PyOS_ascii_atof(s); PyFPE_END_PROTECT(z) return PyComplex_FromCComplex(z); } @@ -1387,7 +1387,7 @@ parsenumber(struct compiling *c, char *s #endif { PyFPE_START_PROTECT("atof", return 0) - dx = atof(s); + dx = PyOS_ascii_atof(s); PyFPE_END_PROTECT(dx) return PyFloat_FromDouble(dx); } Index: Python/marshal.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Python/marshal.c,v retrieving revision 1.76 diff -u -p -r1.76 marshal.c --- Python/marshal.c 26 Mar 2004 15:09:27 -0000 1.76 +++ Python/marshal.c 6 Jun 2004 11:52:08 -0000 @@ -457,7 +457,7 @@ r_object(RFILE *p) } buf[n] = '\0'; PyFPE_START_PROTECT("atof", return 0) - dx = atof(buf); + dx = PyOS_ascii_atof(buf); PyFPE_END_PROTECT(dx) return PyFloat_FromDouble(dx); } @@ -475,7 +475,7 @@ r_object(RFILE *p) } buf[n] = '\0'; PyFPE_START_PROTECT("atof", return 0) - c.real = atof(buf); + c.real = PyOS_ascii_atof(buf); PyFPE_END_PROTECT(c) n = r_byte(p); if (n == EOF || r_string(buf, (int)n, p) != n) { @@ -485,7 +485,7 @@ r_object(RFILE *p) } buf[n] = '\0'; PyFPE_START_PROTECT("atof", return 0) - c.imag = atof(buf); + c.imag = PyOS_ascii_atof(buf); PyFPE_END_PROTECT(c) return PyComplex_FromCComplex(c); } --- /dev/null 1994-07-18 01:46:18.000000000 +0200 +++ Include/pystrtod.h 2004-06-06 13:05:40.000000000 +0100 @@ -0,0 +1,18 @@ +#ifndef Py_STRTOD_H +#define Py_STRTOD_H + +#ifdef __cplusplus +extern "C" { +#endif + + +double PyOS_ascii_strtod(const char *str, char **ptr); +double PyOS_ascii_atof(const char *str); +char * PyOS_ascii_formatd(char *buffer, int buf_len, const char *format, double d); + + +#ifdef __cplusplus +} +#endif + +#endif /* !Py_STRTOD_H */ --- /dev/null 1994-07-18 01:46:18.000000000 +0200 +++ Python/pystrtod.c 2004-06-06 13:05:40.000000000 +0100 @@ -0,0 +1,258 @@ +/* -*- Mode: C; c-file-style: "python" -*- */ + +#include +#include + +/* ascii character tests (as opposed to locale tests) */ +#define ISSPACE(c) ((c) == ' ' || (c) == '\f' || (c) == '\n' || \ + (c) == '\r' || (c) == '\t' || (c) == '\v') +#define ISDIGIT(c) ((c) >= '0' && (c) <= '9') +#define ISXDIGIT(c) (ISDIGIT(c) || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F')) + + +/** + * PyOS_ascii_strtod: + * @nptr: the string to convert to a numeric value. + * @endptr: if non-%NULL, it returns the character after + * the last character used in the conversion. + * + * Converts a string to a #gdouble value. + * This function behaves like the standard strtod() function + * does in the C locale. It does this without actually + * changing the current locale, since that would not be + * thread-safe. + * + * This function is typically used when reading configuration + * files or other non-user input that should be locale independent. + * To handle input from the user you should normally use the + * locale-sensitive system strtod() function. + * + * If the correct value would cause overflow, plus or minus %HUGE_VAL + * is returned (according to the sign of the value), and %ERANGE is + * stored in %errno. If the correct value would cause underflow, + * zero is returned and %ERANGE is stored in %errno. + * + * This function resets %errno before calling strtod() so that + * you can reliably detect overflow and underflow. + * + * Return value: the #gdouble value. + **/ +double +PyOS_ascii_strtod(const char *nptr, + char **endptr) +{ + char *fail_pos; + double val; + struct lconv *locale_data; + const char *decimal_point; + int decimal_point_len; + const char *p, *decimal_point_pos; + const char *end = NULL; /* Silence gcc */ + +/* g_return_val_if_fail (nptr != NULL, 0); */ + assert(nptr != NULL); + + fail_pos = NULL; + + locale_data = localeconv(); + decimal_point = locale_data->decimal_point; + decimal_point_len = strlen(decimal_point); + + assert(decimal_point_len != 0); + + decimal_point_pos = NULL; + if (decimal_point[0] != '.' || + decimal_point[1] != 0) + { + p = nptr; + /* Skip leading space */ + while (ISSPACE(*p)) + p++; + + /* Skip leading optional sign */ + if (*p == '+' || *p == '-') + p++; + + if (p[0] == '0' && + (p[1] == 'x' || p[1] == 'X')) + { + p += 2; + /* HEX - find the (optional) decimal point */ + + while (ISXDIGIT(*p)) + p++; + + if (*p == '.') + { + decimal_point_pos = p++; + + while (ISXDIGIT(*p)) + p++; + + if (*p == 'p' || *p == 'P') + p++; + if (*p == '+' || *p == '-') + p++; + while (ISDIGIT(*p)) + p++; + end = p; + } + } + else + { + while (ISDIGIT(*p)) + p++; + + if (*p == '.') + { + decimal_point_pos = p++; + + while (ISDIGIT(*p)) + p++; + + if (*p == 'e' || *p == 'E') + p++; + if (*p == '+' || *p == '-') + p++; + while (ISDIGIT(*p)) + p++; + end = p; + } + } + /* For the other cases, we need not convert the decimal point */ + } + + /* Set errno to zero, so that we can distinguish zero results + and underflows */ + errno = 0; + + if (decimal_point_pos) + { + char *copy, *c; + + /* We need to convert the '.' to the locale specific decimal point */ + copy = malloc(end - nptr + 1 + decimal_point_len); + + c = copy; + memcpy(c, nptr, decimal_point_pos - nptr); + c += decimal_point_pos - nptr; + memcpy(c, decimal_point, decimal_point_len); + c += decimal_point_len; + memcpy(c, decimal_point_pos + 1, end - (decimal_point_pos + 1)); + c += end - (decimal_point_pos + 1); + *c = 0; + + val = strtod(copy, &fail_pos); + + if (fail_pos) + { + if (fail_pos > decimal_point_pos) + fail_pos = (char *)nptr + (fail_pos - copy) - (decimal_point_len - 1); + else + fail_pos = (char *)nptr + (fail_pos - copy); + } + + free(copy); + + } + else + val = strtod(nptr, &fail_pos); + + if (endptr) + *endptr = fail_pos; + + return val; +} + + +/** + * PyOS_ascii_formatd: + * @buffer: A buffer to place the resulting string in + * @buf_len: The length of the buffer. + * @format: The printf()-style format to use for the + * code to use for converting. + * @d: The #gdouble to convert + * + * Converts a #gdouble to a string, using the '.' as + * decimal point. To format the number you pass in + * a printf()-style format string. Allowed conversion + * specifiers are 'e', 'E', 'f', 'F', 'g' and 'G'. + * + * Return value: The pointer to the buffer with the converted string. + **/ +char * +PyOS_ascii_formatd(char *buffer, + int buf_len, + const char *format, + double d) +{ + struct lconv *locale_data; + const char *decimal_point; + int decimal_point_len; + char *p; + int rest_len; + char format_char; + +/* g_return_val_if_fail (buffer != NULL, NULL); */ +/* g_return_val_if_fail (format[0] == '%', NULL); */ +/* g_return_val_if_fail (strpbrk (format + 1, "'l%") == NULL, NULL); */ + + format_char = format[strlen(format) - 1]; + +/* g_return_val_if_fail (format_char == 'e' || format_char == 'E' || */ +/* format_char == 'f' || format_char == 'F' || */ +/* format_char == 'g' || format_char == 'G', */ +/* NULL); */ + + if (format[0] != '%') + return NULL; + + if (strpbrk(format + 1, "'l%")) + return NULL; + + if (!(format_char == 'e' || format_char == 'E' || + format_char == 'f' || format_char == 'F' || + format_char == 'g' || format_char == 'G')) + return NULL; + + + PyOS_snprintf(buffer, buf_len, format, d); + + locale_data = localeconv(); + decimal_point = locale_data->decimal_point; + decimal_point_len = strlen(decimal_point); + + assert(decimal_point_len != 0); + + if (decimal_point[0] != '.' || + decimal_point[1] != 0) + { + p = buffer; + + if (*p == '+' || *p == '-') + p++; + + while (isdigit((unsigned char)*p)) + p++; + + if (strncmp(p, decimal_point, decimal_point_len) == 0) + { + *p = '.'; + p++; + if (decimal_point_len > 1) { + rest_len = strlen(p + (decimal_point_len - 1)); + memmove(p, p + (decimal_point_len - 1), + rest_len); + p[rest_len] = 0; + } + } + } + + return buffer; +} + +double +PyOS_ascii_atof(const char *nptr) +{ + return PyOS_ascii_strtod(nptr, NULL); +}