summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarko Kreen2014-06-25 22:59:12 +0000
committerMarko Kreen2014-06-27 13:56:05 +0000
commit8d066ade11302c9f25f29f35b57ce6f9ad9d4bd4 (patch)
tree1d4e456d38b9fdacd7fb808e6996c02f33f47d91
parent98655be803733da090074cf4d8f7ae355990f142 (diff)
string: locale-independent float handling
-rw-r--r--m4/usual.m43
-rw-r--r--test/compile.c1
-rw-r--r--test/test_common.c4
-rw-r--r--test/test_fnmatch.c2
-rw-r--r--test/test_string.c16
-rw-r--r--usual/cfparser.c5
-rw-r--r--usual/string.c105
-rw-r--r--usual/string.h7
8 files changed, 139 insertions, 4 deletions
diff --git a/m4/usual.m4 b/m4/usual.m4
index cf7f46d..dd73f8a 100644
--- a/m4/usual.m4
+++ b/m4/usual.m4
@@ -183,6 +183,7 @@ AC_CHECK_HEADERS([sys/param.h sys/uio.h pwd.h grp.h])
AC_CHECK_HEADERS([sys/wait.h sys/mman.h syslog.h netdb.h dlfcn.h])
AC_CHECK_HEADERS([err.h pthread.h endian.h sys/endian.h byteswap.h])
AC_CHECK_HEADERS([malloc.h regex.h getopt.h fnmatch.h])
+AC_CHECK_HEADERS([langinfo.h])
dnl ucred.h may have prereqs
AC_CHECK_HEADERS([ucred.h sys/ucred.h], [], [], [
#ifdef HAVE_SYS_TYPES_H
@@ -207,7 +208,7 @@ AC_CHECK_FUNCS(err errx warn warnx getprogname setprogname)
AC_CHECK_FUNCS(posix_memalign memalign valloc)
AC_CHECK_FUNCS(getopt getopt_long getopt_long_only)
AC_CHECK_FUNCS(fls flsl flsll ffs ffsl ffsll)
-AC_CHECK_FUNCS(fnmatch mbsnrtowcs)
+AC_CHECK_FUNCS(fnmatch mbsnrtowcs nl_langinfo strtod_l)
### Functions provided only on win32
AC_CHECK_FUNCS(localtime_r gettimeofday recvmsg sendmsg usleep getrusage)
### Functions used by libusual itself
diff --git a/test/compile.c b/test/compile.c
index 4e6b24e..032b322 100644
--- a/test/compile.c
+++ b/test/compile.c
@@ -42,6 +42,7 @@ int main(void)
static_assert(sizeof(int) >= 4, "unsupported int size");
heap = heap_create(heap_is_better, NULL, NULL);
+ heap_top(heap);
aatree_init(&aatree, NULL, NULL);
cbtree = cbtree_create(NULL, NULL, NULL, NULL);
cbtree_destroy(cbtree);
diff --git a/test/test_common.c b/test/test_common.c
index dca1520..2cf8697 100644
--- a/test/test_common.c
+++ b/test/test_common.c
@@ -2,6 +2,8 @@
#include "test_common.h"
+#include <locale.h>
+
struct testgroup_t groups[] = {
{ "base/", base_tests },
{ "aatree/", aatree_tests },
@@ -35,6 +37,8 @@ struct testgroup_t groups[] = {
int main(int argc, const char *argv[])
{
+ if (getenv("USE_LOCALE"))
+ setlocale(LC_ALL, "");
return tinytest_main(argc, argv, groups);
}
diff --git a/test/test_fnmatch.c b/test/test_fnmatch.c
index e41677b..45f11f8 100644
--- a/test/test_fnmatch.c
+++ b/test/test_fnmatch.c
@@ -115,7 +115,9 @@ static void test_fnmatch_posix(void *p)
/* escapes in brackets ~ posix */
int_check(0, fnmatch("[A\\]]", "\\]", FNM_NOESCAPE));
+#ifndef HAVE_FNMATCH
int_check(0, fnmatch("[a\\-x]", "_", FNM_NOESCAPE));
+#endif
end:;
}
diff --git a/test/test_string.c b/test/test_string.c
index cf4e3c0..f6b4c05 100644
--- a/test/test_string.c
+++ b/test/test_string.c
@@ -337,6 +337,21 @@ static void test_memspn(void *z)
end:;
}
+static void test_s2d_dot(void *p)
+{
+ char buf[128];
+ double val;
+ char *end;
+
+ memset(buf, 0, sizeof(buf));
+ dtostr_dot(buf, sizeof(buf), 1.5);
+ str_check(buf, "1.5");
+ val = strtod_dot(buf, &end);
+ tt_assert(val == 1.5);
+ tt_assert(*end == 0);
+end:;
+}
+
/*
* Describe
*/
@@ -354,6 +369,7 @@ struct testcase_t string_tests[] = {
{ "dirname", test_dirname },
{ "strlist", test_strlist },
{ "parse_wordlist", test_wlist },
+ { "str2double_dot", test_s2d_dot },
END_OF_TESTCASES
};
diff --git a/usual/cfparser.c b/usual/cfparser.c
index 7e5e4c7..f98b23b 100644
--- a/usual/cfparser.c
+++ b/usual/cfparser.c
@@ -18,8 +18,6 @@
#include <usual/cfparser.h>
-#include <string.h>
-
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
@@ -28,6 +26,7 @@
#include <usual/fileutil.h>
#include <usual/logging.h>
#include <usual/time.h>
+#include <usual/string.h>
#define MAX_INCLUDE 10
@@ -512,7 +511,7 @@ static double parse_time(const char *value)
char *endp = NULL;
errno = 0;
- v = strtod(value, &endp);
+ v = strtod_dot(value, &endp);
if (errno)
return -1;
if (*endp || endp == value || v < 0) {
diff --git a/usual/string.c b/usual/string.c
index e4f7f34..0e663d1 100644
--- a/usual/string.c
+++ b/usual/string.c
@@ -24,6 +24,10 @@
#include <usual/bytemap.h>
#include <errno.h>
+#include <locale.h>
+#ifdef HAVE_LANGINFO_H
+#include <langinfo.h>
+#endif
/*
* Dynamic list of strings.
@@ -403,3 +407,104 @@ size_t memcspn(const void *data, size_t dlen, const void *reject, size_t rlen)
return dlen;
}
+double strtod_dot(const char *s, char **tokend)
+{
+ const char *dp;
+ char buf[128];
+ char *dst, *tmp, *end, *dot = NULL;
+ unsigned int i, dplen;
+ double res;
+
+ /* check if locale is sane */
+#ifdef HAVE_NL_LANGINFO
+ dp = nl_langinfo(RADIXCHAR);
+#else
+ dp = localeconv()->decimal_point;
+#endif
+ if (memcmp(dp, ".", 2) == 0)
+ return strtod(s, tokend);
+
+ /* try to use proper api */
+#ifdef HAVE_STRTOD_L
+ {
+ static locale_t c_locale = NULL;
+ if (!c_locale)
+ c_locale = newlocale(LC_ALL_MASK, "C", NULL);
+ if (c_locale)
+ return strtod_l(s, tokend, c_locale);
+ }
+#endif
+
+ while (*s && isspace(*s))
+ s++;
+
+ dot = NULL;
+ dst = buf;
+ end = buf + sizeof(buf) - 5;
+ dplen = dp[1] ? strlen(dp) : 1;
+ for (i = 0; s[i]; i++) {
+ if (s[i] >= '0' && s[i] <= '9') {
+ *dst++ = s[i];
+ } else if (s[i] == '.') {
+ dot = dst;
+ memcpy(dst, dp, dplen);
+ dst += dplen;
+ } else if (s[i] == '-' || s[i] == '+' || s[i] == 'e' || s[i] == 'E') {
+ *dst++ = s[i];
+ } else {
+ break;
+ }
+
+ if (dst >= end) {
+ errno = ERANGE;
+ return 0;
+ }
+ }
+ *dst = '\0';
+
+ if (!dot)
+ return strtod(s, tokend);
+
+ tmp = NULL;
+ res = strtod(buf, &tmp);
+ if (tmp && tokend) {
+ *tokend = (char *)s + (tmp - buf);
+ if (dot && tmp > dot && dplen > 1)
+ *tokend -= (dplen - 1);
+ }
+ return res;
+}
+
+
+ssize_t dtostr_dot(char *buf, size_t buflen, double val)
+{
+ const char *dp;
+ ssize_t len, dplen;
+ char *p;
+
+ /* render with max precision */
+ len = snprintf(buf, buflen, "%.17g", val);
+ if (len >= (ssize_t)buflen || len < 0)
+ return len;
+
+ /* check if locale is sane */
+#ifdef HAVE_NL_LANGINFO
+ dp = nl_langinfo(RADIXCHAR);
+#else
+ dp = localeconv()->decimal_point;
+#endif
+ if (memcmp(dp, ".", 2) == 0)
+ return len;
+
+ dplen = dp[1] ? strlen(dp) : 1;
+ p = memchr(buf, dp[0], len);
+ if (p) {
+ *p = '.';
+ if (dp[1]) {
+ memmove(p + 1, p + dplen, strlen(p + dplen) + 1);
+ len -= dplen - 1;
+ }
+ }
+ return len;
+}
+
diff --git a/usual/string.h b/usual/string.h
index 8fce683..21a738b 100644
--- a/usual/string.h
+++ b/usual/string.h
@@ -117,5 +117,12 @@ const char *usual_strerror_r(int e, char *dst, size_t dstlen);
/** Compat: Provide GNU-style API: const char *strerror_r(int e, char *dst, size_t dstlen) */
#define strerror_r(a,b,c) usual_strerror_r(a,b,c)
+/** strtod() that uses '.' as decimal separator */
+double strtod_dot(const char *s, char **tokend);
+
+/** Convert double to string with '.' as decimal separator */
+ssize_t dtostr_dot(char *buf, size_t buflen, double val);
+
+
#endif