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
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
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);
#include "test_common.h"
+#include <locale.h>
+
struct testgroup_t groups[] = {
{ "base/", base_tests },
{ "aatree/", aatree_tests },
int main(int argc, const char *argv[])
{
+ if (getenv("USE_LOCALE"))
+ setlocale(LC_ALL, "");
return tinytest_main(argc, argv, groups);
}
/* escapes in brackets ~ posix */
int_check(0, fnmatch("[A\\]]", "\\]", FNM_NOESCAPE));
+#ifndef HAVE_FNMATCH
int_check(0, fnmatch("[a\\-x]", "_", FNM_NOESCAPE));
+#endif
end:;
}
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
*/
{ "dirname", test_dirname },
{ "strlist", test_strlist },
{ "parse_wordlist", test_wlist },
+ { "str2double_dot", test_s2d_dot },
END_OF_TESTCASES
};
#include <usual/cfparser.h>
-#include <string.h>
-
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
#include <usual/fileutil.h>
#include <usual/logging.h>
#include <usual/time.h>
+#include <usual/string.h>
#define MAX_INCLUDE 10
char *endp = NULL;
errno = 0;
- v = strtod(value, &endp);
+ v = strtod_dot(value, &endp);
if (errno)
return -1;
if (*endp || endp == value || v < 0) {
#include <usual/bytemap.h>
#include <errno.h>
+#include <locale.h>
+#ifdef HAVE_LANGINFO_H
+#include <langinfo.h>
+#endif
/*
* Dynamic list of strings.
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;
+}
+
/** 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