From e8ae254970cfdeb1b5aba307a95a3189b09c9784 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Wed, 21 Dec 2022 11:15:55 +0000 Subject: libcamera: yaml_parser: Use C locale When parsing configuration files on systems with differing locales, the use of strtod can produce different results, or in the worst case - fail to parse expected values. Fix this by using strtod_l() instead. To avoid constructing and destructing a locale_t instance for every use of strtod_l(), create an RAII class that wraps the locale_t and use it to provide a global "C" locale. Bug: https://bugs.libcamera.org/show_bug.cgi?id=174 Bug: https://github.com/raspberrypi/libcamera/issues/29 Reported-by: https://github.com/kralo Reported-by: Hannes Winkler Reviewed-by: Laurent Pinchart Signed-off-by: Kieran Bingham --- src/libcamera/yaml_parser.cpp | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/src/libcamera/yaml_parser.cpp b/src/libcamera/yaml_parser.cpp index d8a7c2f9..2806c591 100644 --- a/src/libcamera/yaml_parser.cpp +++ b/src/libcamera/yaml_parser.cpp @@ -31,6 +31,38 @@ namespace { /* Empty static YamlObject as a safe result for invalid operations */ static const YamlObject empty; +/* + * Construct a global RAII locale for use by all YAML parser instances to + * ensure consistency when parsing configuration files and types regardless of + * the system locale configuration. + * + * For more information see: + * - https://bugs.libcamera.org/show_bug.cgi?id=174 + */ +class Locale +{ +public: + Locale(const char *locale) + { + locale_ = newlocale(LC_ALL_MASK, locale, static_cast(0)); + if (locale_ == static_cast(0)) + LOG(YamlParser, Fatal) + << "Failed to construct a locale"; + } + + ~Locale() + { + freelocale(locale_); + } + + locale_t locale() { return locale_; } + +private: + locale_t locale_; +}; + +Locale yamlLocale("C"); + } /* namespace */ /** @@ -283,7 +315,7 @@ std::optional YamlObject::get() const char *end; errno = 0; - double value = std::strtod(value_.c_str(), &end); + double value = strtod_l(value_.c_str(), &end, yamlLocale.locale()); if ('\0' != *end || errno == ERANGE) return std::nullopt; -- cgit v1.2.1