summaryrefslogtreecommitdiffstats
path: root/Python/pystrtod.c
diff options
context:
space:
mode:
Diffstat (limited to 'Python/pystrtod.c')
-rw-r--r--Python/pystrtod.c98
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;
}