ref: b5a6dc7849cbd9f1fd23183ba46f0d5deb24e81d
dir: /sys/src/cmd/python/Python/pystrtod.c/
/* -*- Mode: C; c-file-style: "python" -*- */ #include <Python.h> #include <locale.h> /* 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. * If memory allocation fails, %ENOMEM 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 = -1.0; struct lconv *locale_data; const char *decimal_point; size_t decimal_point_len; const char *p, *decimal_point_pos; const char *end = NULL; /* Silence gcc */ 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++; 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; } else if (strncmp(p, decimal_point, decimal_point_len) == 0) { /* Python bug #1417699 */ *endptr = (char*)nptr; errno = EINVAL; return val; } /* 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 = (char *)PyMem_MALLOC(end - nptr + 1 + decimal_point_len); if (copy == NULL) { if (endptr) *endptr = (char *)nptr; errno = ENOMEM; return val; } 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); } PyMem_FREE(copy); } else { unsigned i = 0; if (nptr[i] == '-') i++; if (nptr[i] == '0' && (nptr[i+1] == 'x' || nptr[i+1] == 'X')) fail_pos = (char*)nptr; 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, size_t buf_len, const char *format, double d) { struct lconv *locale_data; const char *decimal_point; size_t decimal_point_len, rest_len; char *p; 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); }