Fix unintentional locale dependency (#3358)
Avoid unintentional locale dependency
hb_variation_to_string uses sprintf with %g, which will produce
a locale-dependent decimal point, which is not desired here.
The output is supposed to be compatible with CSS syntax, and
that always uses '.' for the decimal point.
Fix this by changing the per-thread locale to "C" around sprintf call.
Fixes https://github.com/harfbuzz/harfbuzz/issues/3355
Fixes https://github.com/harfbuzz/harfbuzz/pull/3357
Fixes https://github.com/harfbuzz/harfbuzz/pull/3358
Co-authored-by: Matthias Clasen <mclasen@redhat.com>
diff --git a/src/hb-common.cc b/src/hb-common.cc
index 26c8ad0..ea8ca2a 100644
--- a/src/hb-common.cc
+++ b/src/hb-common.cc
@@ -31,6 +31,10 @@
#include <locale.h>
+#ifdef HAVE_XLOCALE_H
+#include <xlocale.h> // Needed on BSD/OS X for uselocale
+#endif
+
#ifdef HB_NO_SETLOCALE
#define setlocale(Category, Locale) "C"
#endif
@@ -122,7 +126,7 @@
* @tag: #hb_tag_t to convert
* @buf: (out caller-allocates) (array fixed-size=4) (element-type uint8_t): Converted string
*
- * Converts an #hb_tag_t to a string and returns it in @buf.
+ * Converts an #hb_tag_t to a string and returns it in @buf.
* Strings will be four characters long.
*
* Since: 0.9.5
@@ -151,13 +155,13 @@
* @str: (array length=len) (element-type uint8_t): String to convert
* @len: Length of @str, or -1 if it is %NULL-terminated
*
- * Converts a string to an #hb_direction_t.
+ * Converts a string to an #hb_direction_t.
*
* Matching is loose and applies only to the first letter. For
* examples, "LTR" and "left-to-right" will both return #HB_DIRECTION_LTR.
*
* Unmatched strings will return #HB_DIRECTION_INVALID.
- *
+ *
* Return value: The #hb_direction_t matching @str
*
* Since: 0.9.2
@@ -1039,6 +1043,56 @@
return false;
}
+#if !defined(HARFBUZZ_NO_SETLOCALE) && defined(HAVE_NEWLOCALE) && defined(HAVE_USELOCALE)
+
+#ifdef WIN32
+using locale_t = _locale_t;
+#endif
+
+static inline void free_static_C_locale ();
+
+static struct hb_C_locale_lazy_loader_t : hb_lazy_loader_t<hb_remove_pointer<locale_t>,
+ hb_C_locale_lazy_loader_t>
+{
+ static locale_t create ()
+ {
+ locale_t l = newlocale (LC_ALL_MASK, "C", NULL);
+ if (!l)
+ return l;
+
+ hb_atexit (free_static_C_locale);
+
+ return l;
+ }
+ static void destroy (locale_t l)
+ {
+ freelocale (l);
+ }
+ static locale_t get_null ()
+ {
+ return (locale_t) 0;
+ }
+} static_C_locale;
+
+static inline
+void free_static_C_locale ()
+{
+ static_C_locale.free_instance ();
+}
+
+static locale_t
+get_C_locale ()
+{
+ return static_C_locale.get_unconst ();
+}
+
+#else
+#ifdef WIN32
+#define locale_t void *
+#endif
+#define uselocale(Locale) ((locale_t) 0)
+#endif
+
/**
* hb_variation_to_string:
* @variation: an #hb_variation_t to convert
@@ -1064,7 +1118,11 @@
while (len && s[len - 1] == ' ')
len--;
s[len++] = '=';
+
+ locale_t oldlocale HB_UNUSED;
+ oldlocale = uselocale (get_C_locale ());
len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value));
+ (void) uselocale (oldlocale);
assert (len < ARRAY_LENGTH (s));
len = hb_min (len, size - 1);