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 <hanneswinkler2000@web.de>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
This commit is contained in:
Kieran Bingham 2022-12-21 11:15:55 +00:00
parent 0081e4e6b2
commit e8ae254970

View file

@ -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<locale_t>(0));
if (locale_ == static_cast<locale_t>(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<double> 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;