string: locale-independent float handling
authorMarko Kreen <[email protected]>
Wed, 25 Jun 2014 22:59:12 +0000 (01:59 +0300)
committerMarko Kreen <[email protected]>
Fri, 27 Jun 2014 13:56:05 +0000 (16:56 +0300)
m4/usual.m4
test/compile.c
test/test_common.c
test/test_fnmatch.c
test/test_string.c
usual/cfparser.c
usual/string.c
usual/string.h

index cf7f46d9465554d8b425c57874909d6ecba6da70..dd73f8a71c15785700cae0e0854c55c43b1c9403 100644 (file)
@@ -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
index 4e6b24ef759028838c42793c066a6e28f7b9cb10..032b3223d160c25cdb3fe25c12b987dbd00222ba 100644 (file)
@@ -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);
index dca15200678f76d48fd09f81a56618bbbdf2deb9..2cf8697693cc9cb5f65cf064f5db6ebcb9bda7de 100644 (file)
@@ -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);
 }
 
index e41677bbf55fbc0cd02d3a81386979f8c89da35c..45f11f8df9b60c3cb6637bb31203be99dae42f2b 100644 (file)
@@ -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:;
 }
 
index cf4e3c0ca30f00d24e11123cc2c3c36b63d600dd..f6b4c05034cc6240f9e84deee5aed358b7896d8e 100644 (file)
@@ -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
 };
 
index 7e5e4c7c3447b5bbda5690ff45f7a3d29f415b18..f98b23b0312a4d54a2781ab3c9cae9d652c93cdb 100644 (file)
@@ -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) {
index e4f7f34547dbbe8649172242dc8f703a2a3fc363..0e663d16c2319fe56760a4f7dbdde0c17be1403f 100644 (file)
 #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;
+}
+
index 8fce6831e9f6c0ba38728bdfb305f322273956d6..21a738b92498f52ac50eef9e8d2dd7d0262da6ad 100644 (file)
@@ -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