[libvirt] [PATCH] json: fix interface locale dependency

Daniel P. Berrange berrange at redhat.com
Mon Aug 6 12:24:21 UTC 2012


On Mon, Aug 06, 2012 at 10:12:17AM +0200, Martin Kletzander wrote:
> 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.
> 
> But even when decimal point is affected, grouping is not, because for
> grouping to be enabled with *printf, there has to be a apostrophe flag
> specified (and supported).
> ---
> Fortunately, there should be no other place where output-formatting is
> affected by this problem.
> 
> I tried to change this in various ways with this posted one being the
> cleanest from my point of view, because:
>  - setting locale is per-proccess, not per-thread (not thread-safe)

Actually in glibc there is a per-thread locale:

  /* Switch the current thread's locale to DATASET.
     If DATASET is null, instead just return the current setting.
     The special value LC_GLOBAL_LOCALE is the initial setting
     for all threads and can also be installed any time, meaning
     the thread uses the global settings controlled by `setlocale'.  */
  extern __locale_t uselocale (__locale_t __dataset) __THROW;


>  typedef struct _virJSONParserState virJSONParserState;
>  typedef virJSONParserState *virJSONParserStatePtr;
>  struct _virJSONParserState {
> @@ -200,9 +201,27 @@ virJSONValuePtr virJSONValueNewNumberUlong(unsigned long long data)
>  virJSONValuePtr virJSONValueNewNumberDouble(double data)
>  {
>      virJSONValuePtr val = NULL;
> -    char *str;
> +    char *str, *radix, *tmp;
> +
>      if (virAsprintf(&str, "%lf", data) < 0)
>          return NULL;
> +
> +    /* because printing double is locale-dependent, we could end up
> +     * with invalid JSON code, so we have to do something like this */
> +    radix = localeconv()->decimal_point;
> +    tmp = strstr(str, radix);
> +    if (tmp) {
> +        *tmp = '.';
> +        if (strlen(radix) > 1) {
> +            /* if the current locale specifies more characters as
> +             * decimal point then cover the others with decimal
> +             * numbers */
> +            memcpy(tmp + 1,
> +                   tmp + strlen(radix),
> +                   strlen(str) - (tmp - str));
> +        }
> +    }
> +
>      val = virJSONValueNewNumber(str);
>      VIR_FREE(str);
>      return val;

GLib has a g_ascii_dtostr() which forces uses of '.' as separator. Since
GLib is LGPLv2+ licensed, we can just copy their impl, which actually
uses GLibc's uselocale() if possible, otherwise has a fallback impl.

Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|




More information about the libvir-list mailing list