libvirt creates invalid commands if wrong locale is selected. For
example with locale that uses comma as a decimal point, JSON commands
created with decimal numbers are invalid because comma separates the
entries in JSON. Fortunately even when decimal point is affected,
thousands grouping is not, because for grouping to be enabled with
*printf, there has to be an apostrophe flag specified (and supported).
This patch adds specific internal function for printing with C locale
and a fallback for the particular case with double formatting in case
these functions are not supported.
---
v2:
- added support for xlocale
- fallback option kept, but main function moved to util.c
src/libvirt_private.syms | 1 +
src/util/json.c | 23 ++++++++++++++++++++++-
src/util/util.c | 42 ++++++++++++++++++++++++++++++++++++++++++
src/util/util.h | 2 ++
4 files changed, 67 insertions(+), 1 deletions(-)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 79b4a18..6ce87bf 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1138,6 +1138,7 @@ safezero;
virArgvToString;
virAsprintf;
virBuildPathInternal;
+virCasprintf;
virDirCreate;
virEnumFromString;
virEnumToString;
diff --git a/src/util/json.c b/src/util/json.c
index 5132989..3dfa039 100644
--- a/src/util/json.c
+++ b/src/util/json.c
@@ -22,6 +22,7 @@
#include <config.h>
+#include <locale.h>
#include "json.h"
#include "memory.h"
@@ -201,8 +202,28 @@ virJSONValuePtr virJSONValueNewNumberDouble(double data)
{
virJSONValuePtr val = NULL;
char *str;
- if (virAsprintf(&str, "%lf", data) < 0)
+
+ int ret = virCasprintf(&str, "%lf", data)
+
+ if (ret == -1) {
return NULL;
+ } else if (ret == -2) {
+ char *radix, *tmp;
+ struct lconv *lc;
+
+ if ((ret = virAsprintf(&str, "%lf", data)) < 0)
+ return NULL;
+
+ lc = localeconv();
+ radix = lc->decimal_point;
+ tmp = strstr(str, radix);
+ if (tmp) {
+ *tmp = '.';
+ if (strlen(radix) > 1)
+ memmove(tmp + 1, tmp + strlen(radix), strlen(str) - (tmp - str));
+ }
+ }
+
val = virJSONValueNewNumber(str);
VIR_FREE(str);
return val;
diff --git a/src/util/util.c b/src/util/util.c
index e5bb27b..de090a6 100644
--- a/src/util/util.c
+++ b/src/util/util.c
@@ -44,6 +44,7 @@
#include <signal.h>
#include <termios.h>
#include <pty.h>
+#include <locale.h>
#if HAVE_LIBDEVMAPPER_H
# include <libdevmapper.h>
@@ -2003,6 +2004,47 @@ virAsprintf(char **strp, const char *fmt, ...)
}
/**
+ * virCasprintf
+ *
+ * the same as virAsprintf, but with C locale (thread-safe)
+ *
+ * If thread-safe locales aren't supported, then the return value is -2,
+ * no memory (or other vasnprintf errors) results in -1.
+ * When successful, the value returned by virAsprintf is passed.
+ */
+#if HAVE_XLOCALE_H
+int
+virCasprintf(char **strp, const char *fmt, ...)
+{
+ va_list ap;
+ int ret = -1;
+ locale_t c_loc, old_loc;
+ c_loc = newlocale(LC_ALL, "C", NULL);
+ if (!c_loc)
+ goto no_memory;
+
+ old_loc = uselocale(c_loc);
+
+ va_start(ap, fmt);
+ ret = virVasprintf(strp, fmt, ap);
+ va_end(ap);
+
+ uselocale(old_loc);
+
+ no_memory:
+ freelocale(c_loc);
+ return ret;
+}
+#else
+int
+virCasprintf(char **strp ATTRIBUTE_UNUSED,
+ const char *fmt ATTRIBUTE_UNUSED, ...)
+{
+ return -2;
+}
+#endif
+
+/**
* virStrncpy
*
* A safe version of strncpy. The last parameter is the number of bytes
diff --git a/src/util/util.h b/src/util/util.h
index d151c27..4b7f286 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -201,6 +201,8 @@ int virParseVersionString(const char *str, unsigned long *version,
bool allowMissing);
int virAsprintf(char **strp, const char *fmt, ...)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_FMT_PRINTF(2, 3);
+int virCasprintf(char **strp, const char *fmt, ...)
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_FMT_PRINTF(2, 3);
int virVasprintf(char **strp, const char *fmt, va_list list)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_FMT_PRINTF(2, 0);
char *virStrncpy(char *dest, const char *src, size_t n, size_t destbytes)
--
1.7.8.6