diff options
Diffstat (limited to 'Python/pystrtod.c')
-rw-r--r-- | Python/pystrtod.c | 98 |
1 files changed, 73 insertions, 25 deletions
diff --git a/Python/pystrtod.c b/Python/pystrtod.c index 16efa9d..2ca8402 100644 --- a/Python/pystrtod.c +++ b/Python/pystrtod.c @@ -7,7 +7,6 @@ #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')) /** @@ -123,7 +122,8 @@ PyOS_ascii_strtod(const char *nptr, char **endptr) errno = EINVAL; return val; } - /* For the other cases, we need not convert the decimal point */ + /* For the other cases, we need not convert the decimal + point */ } /* Set errno to zero, so that we can distinguish zero results @@ -134,7 +134,8 @@ PyOS_ascii_strtod(const char *nptr, char **endptr) { char *copy, *c; - /* We need to convert the '.' to the locale specific decimal point */ + /* We need to convert the '.' to the locale specific decimal + point */ copy = (char *)PyMem_MALLOC(end - digits_pos + 1 + decimal_point_len); if (copy == NULL) { @@ -149,7 +150,8 @@ PyOS_ascii_strtod(const char *nptr, char **endptr) c += decimal_point_pos - digits_pos; memcpy(c, decimal_point, decimal_point_len); c += decimal_point_len; - memcpy(c, decimal_point_pos + 1, end - (decimal_point_pos + 1)); + memcpy(c, decimal_point_pos + 1, + end - (decimal_point_pos + 1)); c += end - (decimal_point_pos + 1); *c = 0; @@ -198,7 +200,7 @@ as necessary to represent the exponent. /** * PyOS_ascii_formatd: * @buffer: A buffer to place the resulting string in - * @buf_len: The length of the buffer. + * @buf_size: The length of the buffer. * @format: The printf()-style format to use for the * code to use for converting. * @d: The #gdouble to convert @@ -209,12 +211,14 @@ as necessary to represent the exponent. * specifiers are 'e', 'E', 'f', 'F', 'g', 'G', and 'n'. * * 'n' is the same as 'g', except it uses the current locale. + * 'Z' is the same as 'g', except it always has a decimal and + * at least one digit after the decimal. * * Return value: The pointer to the buffer with the converted string. **/ char * PyOS_ascii_formatd(char *buffer, - size_t buf_len, + size_t buf_size, const char *format, double d) { @@ -227,20 +231,13 @@ PyOS_ascii_formatd(char *buffer, can't modify it directly. FLOAT_FORMATBUFLEN should be longer than we ever need this to be. There's an upcoming check to ensure it's big enough. */ + /* Issue 2264: code 'Z' requires copying the format. 'Z' is 'g', but + also with at least one character past the decimal. */ char tmp_format[FLOAT_FORMATBUFLEN]; -/* 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); */ - /* The last character in the format string must be the format char */ format_char = format[format_len - 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; @@ -251,19 +248,24 @@ PyOS_ascii_formatd(char *buffer, if (strpbrk(format + 1, "'l%")) return NULL; + /* Also curious about this function is that it accepts format strings + like "%xg", which are invalid for floats. In general, the + interface to this function is not very good, but changing it is + difficult because it's a public API. */ + if (!(format_char == 'e' || format_char == 'E' || format_char == 'f' || format_char == 'F' || format_char == 'g' || format_char == 'G' || - format_char == 'n')) + format_char == 'n' || format_char == 'Z')) return NULL; - /* Map 'n' format_char to 'g', by copying the format string and - replacing the final 'n' with a 'g' */ - if (format_char == 'n') { + /* Map 'n' or 'Z' format_char to 'g', by copying the format string and + replacing the final char with a 'g' */ + if (format_char == 'n' || format_char == 'Z') { if (format_len + 1 >= sizeof(tmp_format)) { /* The format won't fit in our copy. Error out. In - practice, this will never happen and will be detected - by returning NULL */ + practice, this will never happen and will be + detected by returning NULL */ return NULL; } strcpy(tmp_format, format); @@ -271,8 +273,9 @@ PyOS_ascii_formatd(char *buffer, format = tmp_format; } + /* Have PyOS_snprintf do the hard work */ - PyOS_snprintf(buffer, buf_len, format, d); + PyOS_snprintf(buffer, buf_size, format, d); /* Get the current local, and find the decimal point character (or string?). Convert that string back to a dot. Do not do this if @@ -294,7 +297,8 @@ PyOS_ascii_formatd(char *buffer, while (isdigit(Py_CHARMASK(*p))) p++; - if (strncmp(p, decimal_point, decimal_point_len) == 0) { + if (strncmp(p, decimal_point, + decimal_point_len) == 0) { *p = '.'; p++; if (decimal_point_len > 1) { @@ -343,7 +347,8 @@ PyOS_ascii_formatd(char *buffer, if we can delete some of the leading zeros */ if (significant_digit_cnt < MIN_EXPONENT_DIGITS) significant_digit_cnt = MIN_EXPONENT_DIGITS; - extra_zeros_cnt = exponent_digit_cnt - significant_digit_cnt; + extra_zeros_cnt = exponent_digit_cnt - + significant_digit_cnt; /* Delete extra_zeros_cnt worth of characters from the front of the exponent */ @@ -360,7 +365,7 @@ PyOS_ascii_formatd(char *buffer, until there are 2, if there's enough room */ int zeros = MIN_EXPONENT_DIGITS - exponent_digit_cnt; if (start + zeros + exponent_digit_cnt + 1 - < buffer + buf_len) { + < buffer + buf_size) { memmove(start + zeros, start, exponent_digit_cnt + 1); memset(start, '0', zeros); @@ -368,6 +373,49 @@ PyOS_ascii_formatd(char *buffer, } } + /* If format_char is 'Z', make sure we have at least one character + after the decimal point (and make sure we have a decimal point). */ + if (format_char == 'Z') { + int insert_count = 0; + char* chars_to_insert; + + /* search for the first non-digit character */ + p = buffer; + while (*p && isdigit(Py_CHARMASK(*p))) + ++p; + + if (*p == '.') { + if (isdigit(Py_CHARMASK(*(p+1)))) { + /* Nothing to do, we already have a decimal + point and a digit after it */ + } + else { + /* We have a decimal point, but no following + digit. Insert a zero after the decimal. */ + ++p; + chars_to_insert = "0"; + insert_count = 1; + } + } + else { + chars_to_insert = ".0"; + insert_count = 2; + } + if (insert_count) { + size_t buf_len = strlen(buffer); + if (buf_len + insert_count + 1 >= buf_size) { + /* If there is not enough room in the buffer + for the additional text, just skip it. It's + not worth generating an error over. */ + } + else { + memmove(p + insert_count, p, + buffer + strlen(buffer) - p + 1); + memcpy(p, chars_to_insert, insert_count); + } + } + } + return buffer; } |