summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Garcia Campos <carlosgc@gnome.org>2008-11-25 15:28:59 +0100
committerCarlos Garcia Campos <carlosgc@gnome.org>2008-11-25 15:28:59 +0100
commitdeded1a671fd8d76ed9920efa9747912bdcd01df (patch)
treebe41dfa6e1bf312dc3a5c469bd422d8207a93150
parent1e1c9ae337489aef9b8d2db77c615ad18e29bec1 (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.c124
-rw-r--r--libspectre/spectre-utils.h18
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