diff options
author | Carlos Garcia Campos <carlosgc@gnome.org> | 2008-11-25 15:28:59 +0100 |
---|---|---|
committer | Carlos Garcia Campos <carlosgc@gnome.org> | 2008-11-25 15:28:59 +0100 |
commit | deded1a671fd8d76ed9920efa9747912bdcd01df (patch) | |
tree | be41dfa6e1bf312dc3a5c469bd422d8207a93150 | |
parent | 1e1c9ae337489aef9b8d2db77c615ad18e29bec1 (diff) |
Add _spectre_strtod to spectre utils
Copied from glib g_strtod(), a thread-safe locale-independent version
of the standard strtod().
-rw-r--r-- | libspectre/spectre-utils.c | 124 | ||||
-rw-r--r-- | libspectre/spectre-utils.h | 18 |
2 files changed, 134 insertions, 8 deletions
diff --git a/libspectre/spectre-utils.c b/libspectre/spectre-utils.c index 67750a8..0d8cf02 100644 --- a/libspectre/spectre-utils.c +++ b/libspectre/spectre-utils.c @@ -22,6 +22,8 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <errno.h> +#include <locale.h> #include "spectre-utils.h" @@ -250,3 +252,125 @@ _spectre_strcasecmp (const char *s1, return (((int)(unsigned char) *s1) - ((int)(unsigned char) *s2)); } + +#define ascii_isspace(c) \ + (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v') +#define ascii_isdigit(c) \ + (c >= '0' && c <= '9') + +/* 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. + * A limitation of the implementation is that this function + * will still accept localized versions of infinities and NANs. + */ +double +_spectre_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 */ + int strtod_errno; + + fail_pos = NULL; + + locale_data = localeconv (); + decimal_point = locale_data->decimal_point; + decimal_point_len = strlen (decimal_point); + + decimal_point_pos = NULL; + end = NULL; + + if (decimal_point[0] != '.' || decimal_point[1] != 0) { + p = nptr; + /* Skip leading space */ + while (ascii_isspace (*p)) + p++; + + /* Skip leading optional sign */ + if (*p == '+' || *p == '-') + p++; + + if (ascii_isdigit (*p) || *p == '.') { + while (ascii_isdigit (*p)) + p++; + + if (*p == '.') + decimal_point_pos = p++; + + while (ascii_isdigit (*p)) + p++; + + if (*p == 'e' || *p == 'E') + p++; + if (*p == '+' || *p == '-') + p++; + while (ascii_isdigit (*p)) + p++; + + end = p; + } + /* For the other cases, we need not convert the decimal point */ + } + + if (decimal_point_pos) { + char *copy, *c; + + /* We need to convert the '.' to the locale specific decimal point */ + copy = (char *) 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; + + errno = 0; + val = strtod (copy, &fail_pos); + strtod_errno = errno; + + if (fail_pos) { + if (fail_pos - copy > decimal_point_pos - nptr) + fail_pos = (char *)nptr + (fail_pos - copy) - (decimal_point_len - 1); + else + fail_pos = (char *)nptr + (fail_pos - copy); + } + + free (copy); + } else if (end) { + char *copy; + + copy = (char *) malloc (end - (char *)nptr + 1); + memcpy (copy, nptr, end - nptr); + *(copy + (end - (char *)nptr)) = 0; + + errno = 0; + val = strtod (copy, &fail_pos); + strtod_errno = errno; + + if (fail_pos) { + fail_pos = (char *)nptr + (fail_pos - copy); + } + + free (copy); + } else { + errno = 0; + val = strtod (nptr, &fail_pos); + strtod_errno = errno; + } + + if (endptr) + *endptr = fail_pos; + + errno = strtod_errno; + + return val; +} diff --git a/libspectre/spectre-utils.h b/libspectre/spectre-utils.h index bfaf9a4..3ed2da8 100644 --- a/libspectre/spectre-utils.h +++ b/libspectre/spectre-utils.h @@ -121,14 +121,16 @@ void _spectre_real_assert (int condition, #endif /* SPECTRE_DISABLE_CHECKS */ /* String handling helpers */ -char *_spectre_strdup_printf (const char *format, - ...); -char *_spectre_strdup (const char *str); -int _spectre_strncasecmp (const char *s1, - const char *s2, - size_t n); -int _spectre_strcasecmp (const char *s1, - const char *s2); +char *_spectre_strdup_printf (const char *format, + ...); +char *_spectre_strdup (const char *str); +int _spectre_strncasecmp (const char *s1, + const char *s2, + size_t n); +int _spectre_strcasecmp (const char *s1, + const char *s2); +double _spectre_strtod (const char *nptr, + char **endptr); SPECTRE_END_DECLS |