diff options
author | Lennart Poettering <lennart@poettering.net> | 2013-04-25 00:04:02 -0300 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2013-04-25 00:05:14 -0300 |
commit | d6dd604b551987b411ec8930c23bd5c9c93ef864 (patch) | |
tree | 6599838f561b0621fda1bfb660bef74981607367 /src | |
parent | db5c0122853a9ecf1cc92e6593461932df2fa866 (diff) |
util: rework safe_atod() to be locale-independent
This adds some syntactic sugar with a macro RUN_WITH_LOCALE() that reset
the thread-specific locale temporarily.
Diffstat (limited to 'src')
-rw-r--r-- | src/shared/util.c | 6 | ||||
-rw-r--r-- | src/shared/util.h | 35 | ||||
-rw-r--r-- | src/test/test-util.c | 38 |
3 files changed, 72 insertions, 7 deletions
diff --git a/src/shared/util.c b/src/shared/util.c index a6ec79a292..38ee493a81 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -372,8 +372,10 @@ int safe_atod(const char *s, double *ret_d) { assert(s); assert(ret_d); - errno = 0; - d = strtod(s, &x); + RUN_WITH_LOCALE(LC_NUMERIC_MASK, "C") { + errno = 0; + d = strtod(s, &x); + } if (!x || x == s || *x || errno) return errno ? -errno : -EINVAL; diff --git a/src/shared/util.h b/src/shared/util.h index 6575f56811..d76f41e777 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -38,6 +38,7 @@ #include <sys/resource.h> #include <stddef.h> #include <unistd.h> +#include <locale.h> #include "macro.h" #include "time-util.h" @@ -643,19 +644,19 @@ static inline void _reset_errno_(int *saved_errno) { errno = *saved_errno; } -#define PROTECT_ERRNO __attribute__((cleanup(_reset_errno_))) int _saved_errno_ = errno +#define PROTECT_ERRNO _cleanup_(_reset_errno_) int _saved_errno_ = errno -struct umask_struct { +struct _umask_struct_ { mode_t mask; bool quit; }; -static inline void _reset_umask_(struct umask_struct *s) { +static inline void _reset_umask_(struct _umask_struct_ *s) { umask(s->mask); }; #define RUN_WITH_UMASK(mask) \ - for (__attribute__((cleanup(_reset_umask_))) struct umask_struct _saved_umask_ = { umask(mask), false }; \ + for (_cleanup_(_reset_umask_) struct _umask_struct_ _saved_umask_ = { umask(mask), false }; \ !_saved_umask_.quit ; \ _saved_umask_.quit = true) @@ -706,3 +707,29 @@ int unlink_noerrno(const char *path); sprintf(_r_, "/proc/%lu/" field, (unsigned long) _pid_); \ _r_; \ }) + +struct _locale_struct_ { + locale_t saved_locale; + locale_t new_locale; + bool quit; +}; + +static inline void _reset_locale_(struct _locale_struct_ *s) { + PROTECT_ERRNO; + if (s->saved_locale != (locale_t) 0) + uselocale(s->saved_locale); + if (s->new_locale != (locale_t) 0) + freelocale(s->new_locale); +} + +#define RUN_WITH_LOCALE(mask, loc) \ + for (_cleanup_(_reset_locale_) struct _locale_struct_ _saved_locale_ = { (locale_t) 0, (locale_t) 0, false }; \ + ({ \ + if (!_saved_locale_.quit) { \ + PROTECT_ERRNO; \ + _saved_locale_.new_locale = newlocale((mask), (loc), (locale_t) 0); \ + if (_saved_locale_.new_locale != (locale_t) 0) \ + _saved_locale_.saved_locale = uselocale(_saved_locale_.new_locale); \ + } \ + !_saved_locale_.quit; }) ; \ + _saved_locale_.quit = true) diff --git a/src/test/test-util.c b/src/test/test-util.c index 83959c0950..66a10ead46 100644 --- a/src/test/test-util.c +++ b/src/test/test-util.c @@ -23,6 +23,7 @@ #include <string.h> #include <unistd.h> #include <fcntl.h> +#include <locale.h> #include "util.h" @@ -145,13 +146,48 @@ static void test_safe_atolli(void) { static void test_safe_atod(void) { int r; double d; + char *e; + + r = safe_atod("junk", &d); + assert_se(r == -EINVAL); r = safe_atod("0.2244", &d); assert_se(r == 0); assert_se(abs(d - 0.2244) < 0.000001); - r = safe_atod("junk", &d); + r = safe_atod("0,5", &d); assert_se(r == -EINVAL); + + errno = 0; + strtod("0,5", &e); + assert_se(*e == ','); + + /* Check if this really is locale independent */ + setlocale(LC_NUMERIC, "de_DE.utf8"); + + r = safe_atod("0.2244", &d); + assert_se(r == 0); + assert_se(abs(d - 0.2244) < 0.000001); + + r = safe_atod("0,5", &d); + assert_se(r == -EINVAL); + + errno = 0; + assert_se(abs(strtod("0,5", &e) - 0.5) < 0.00001); + + /* And check again, reset */ + setlocale(LC_NUMERIC, "C"); + + r = safe_atod("0.2244", &d); + assert_se(r == 0); + assert_se(abs(d - 0.2244) < 0.000001); + + r = safe_atod("0,5", &d); + assert_se(r == -EINVAL); + + errno = 0; + strtod("0,5", &e); + assert_se(*e == ','); } static void test_strappend(void) { |