glibc/glibc-rh1307234-1.patch
Florian Weimer 0ad6e19e8d Resolves: #1307234
Fix strfmon_l to use the locale object for grouping
2016-05-07 16:48:42 +02:00

454 lines
13 KiB
Diff
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

commit 985fc132f23dbb83de76c5af9e783ef1b5900148
Author: Florian Weimer <fweimer@redhat.com>
Date: Mon Apr 4 15:18:13 2016 +0200
strfmon_l: Use specified locale for number formatting [BZ #19633]
Index: b/include/printf.h
===================================================================
--- a/include/printf.h
+++ b/include/printf.h
@@ -1,6 +1,7 @@
#ifndef _PRINTF_H
#include <stdio-common/printf.h>
+#include <xlocale.h>
/* Now define the internal interfaces. */
extern int __printf_fphex (FILE *, const struct printf_info *,
@@ -8,5 +9,8 @@ extern int __printf_fphex (FILE *, const
extern int __printf_fp (FILE *, const struct printf_info *,
const void *const *);
libc_hidden_proto (__printf_fp)
+extern int __printf_fp_l (FILE *, locale_t, const struct printf_info *,
+ const void *const *);
+libc_hidden_proto (__printf_fp_l)
#endif
Index: b/locale/localeinfo.h
===================================================================
--- a/locale/localeinfo.h
+++ b/locale/localeinfo.h
@@ -299,6 +299,27 @@ extern __thread struct __locale_data *co
#endif
+/* Extract CATEGORY locale's string for ITEM. */
+static inline const char *
+_nl_lookup (locale_t l, int category, int item)
+{
+ return l->__locales[category]->values[_NL_ITEM_INDEX (item)].string;
+}
+
+/* Extract CATEGORY locale's wide string for ITEM. */
+static inline const wchar_t *
+_nl_lookup_wstr (locale_t l, int category, int item)
+{
+ return (wchar_t *) l->__locales[category]
+ ->values[_NL_ITEM_INDEX (item)].wstr;
+}
+
+/* Extract the CATEGORY locale's word for ITEM. */
+static inline uint32_t
+_nl_lookup_word (locale_t l, int category, int item)
+{
+ return l->__locales[category]->values[_NL_ITEM_INDEX (item)].word;
+}
/* Default search path if no LOCPATH environment variable. */
extern const char _nl_default_locale_path[] attribute_hidden;
Index: b/stdio-common/printf_fp.c
===================================================================
--- a/stdio-common/printf_fp.c
+++ b/stdio-common/printf_fp.c
@@ -209,9 +209,9 @@ hack_digit (struct hack_digit_param *p)
}
int
-___printf_fp (FILE *fp,
- const struct printf_info *info,
- const void *const *args)
+___printf_fp_l (FILE *fp, locale_t loc,
+ const struct printf_info *info,
+ const void *const *args)
{
/* The floating-point value to output. */
union
@@ -263,18 +263,19 @@ ___printf_fp (FILE *fp,
/* Figure out the decimal point character. */
if (info->extra == 0)
{
- decimal = _NL_CURRENT (LC_NUMERIC, DECIMAL_POINT);
- decimalwc = _NL_CURRENT_WORD (LC_NUMERIC, _NL_NUMERIC_DECIMAL_POINT_WC);
+ decimal = _nl_lookup (loc, LC_NUMERIC, DECIMAL_POINT);
+ decimalwc = _nl_lookup_word
+ (loc, LC_NUMERIC, _NL_NUMERIC_DECIMAL_POINT_WC);
}
else
{
- decimal = _NL_CURRENT (LC_MONETARY, MON_DECIMAL_POINT);
+ decimal = _nl_lookup (loc, LC_MONETARY, MON_DECIMAL_POINT);
if (*decimal == '\0')
- decimal = _NL_CURRENT (LC_NUMERIC, DECIMAL_POINT);
- decimalwc = _NL_CURRENT_WORD (LC_MONETARY,
+ decimal = _nl_lookup (loc, LC_NUMERIC, DECIMAL_POINT);
+ decimalwc = _nl_lookup_word (loc, LC_MONETARY,
_NL_MONETARY_DECIMAL_POINT_WC);
if (decimalwc == L'\0')
- decimalwc = _NL_CURRENT_WORD (LC_NUMERIC,
+ decimalwc = _nl_lookup_word (loc, LC_NUMERIC,
_NL_NUMERIC_DECIMAL_POINT_WC);
}
/* The decimal point character must not be zero. */
@@ -284,9 +285,9 @@ ___printf_fp (FILE *fp,
if (info->group)
{
if (info->extra == 0)
- grouping = _NL_CURRENT (LC_NUMERIC, GROUPING);
+ grouping = _nl_lookup (loc, LC_NUMERIC, GROUPING);
else
- grouping = _NL_CURRENT (LC_MONETARY, MON_GROUPING);
+ grouping = _nl_lookup (loc, LC_MONETARY, MON_GROUPING);
if (*grouping <= 0 || *grouping == CHAR_MAX)
grouping = NULL;
@@ -296,19 +297,20 @@ ___printf_fp (FILE *fp,
if (wide)
{
if (info->extra == 0)
- thousands_sepwc =
- _NL_CURRENT_WORD (LC_NUMERIC, _NL_NUMERIC_THOUSANDS_SEP_WC);
+ thousands_sepwc = _nl_lookup_word
+ (loc, LC_NUMERIC, _NL_NUMERIC_THOUSANDS_SEP_WC);
else
thousands_sepwc =
- _NL_CURRENT_WORD (LC_MONETARY,
+ _nl_lookup_word (loc, LC_MONETARY,
_NL_MONETARY_THOUSANDS_SEP_WC);
}
else
{
if (info->extra == 0)
- thousands_sep = _NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP);
+ thousands_sep = _nl_lookup (loc, LC_NUMERIC, THOUSANDS_SEP);
else
- thousands_sep = _NL_CURRENT (LC_MONETARY, MON_THOUSANDS_SEP);
+ thousands_sep = _nl_lookup
+ (loc, LC_MONETARY, MON_THOUSANDS_SEP);
}
if ((wide && thousands_sepwc == L'\0')
@@ -1174,9 +1176,11 @@ ___printf_fp (FILE *fp,
size_t decimal_len;
size_t thousands_sep_len;
wchar_t *copywc;
- size_t factor = (info->i18n
- ? _NL_CURRENT_WORD (LC_CTYPE, _NL_CTYPE_MB_CUR_MAX)
- : 1);
+ size_t factor;
+ if (info->i18n)
+ factor = _nl_lookup_word (loc, LC_CTYPE, _NL_CTYPE_MB_CUR_MAX);
+ else
+ factor = 1;
decimal_len = strlen (decimal);
@@ -1247,8 +1251,18 @@ ___printf_fp (FILE *fp,
}
return done;
}
+ldbl_hidden_def (___printf_fp_l, __printf_fp_l)
+ldbl_strong_alias (___printf_fp_l, __printf_fp_l)
+
+int
+___printf_fp (FILE *fp, const struct printf_info *info,
+ const void *const *args)
+{
+ return ___printf_fp_l (fp, _NL_CURRENT_LOCALE, info, args);
+}
ldbl_hidden_def (___printf_fp, __printf_fp)
ldbl_strong_alias (___printf_fp, __printf_fp)
+
/* Return the number of extra grouping characters that will be inserted
into a number with INTDIG_MAX integer digits. */
Index: b/stdlib/Makefile
===================================================================
--- a/stdlib/Makefile
+++ b/stdlib/Makefile
@@ -76,7 +76,7 @@ tests := tst-strtol tst-strtod testmb t
tst-secure-getenv tst-strtod-overflow tst-strtod-round \
tst-tininess tst-strtod-underflow tst-tls-atexit \
tst-setcontext3 tst-tls-atexit-nodelete \
- tst-strtol-locale tst-strtod-nan-locale
+ tst-strtol-locale tst-strtod-nan-locale tst-strfmon_l
tests-static := tst-secure-getenv
modules-names = tst-tls-atexit-lib
@@ -125,7 +125,8 @@ include ../Rules
ifeq ($(run-built-tests),yes)
LOCALES := cs_CZ.UTF-8 de_DE.UTF-8 en_US.ISO-8859-1 tr_TR.UTF-8 \
- tr_TR.ISO-8859-9
+ tr_TR.ISO-8859-9 tg_TJ.UTF-8 te_IN.UTF-8 bn_IN.UTF-8 \
+ el_GR.UTF-8
include ../gen-locales.mk
$(objpfx)bug-strtod2.out: $(gen-locales)
@@ -136,6 +137,7 @@ $(objpfx)tst-strtod4.out: $(gen-locales)
$(objpfx)tst-strtod5.out: $(gen-locales)
$(objpfx)tst-strtol-locale.out: $(gen-locales)
$(objpfx)tst-strtod-nan-locale.out: $(gen-locales)
+$(objpfx)tst-strfmon_l.out: $(gen-locales)
endif
# Testdir has to be named stdlib and needs to be writable
Index: b/stdlib/strfmon_l.c
===================================================================
--- a/stdlib/strfmon_l.c
+++ b/stdlib/strfmon_l.c
@@ -68,9 +68,6 @@
#define _NL_CURRENT(category, item) \
(current->values[_NL_ITEM_INDEX (item)].string)
-extern int __printf_fp (FILE *, const struct printf_info *,
- const void *const *);
-libc_hidden_proto (__printf_fp)
/* This function determines the number of digit groups in the output.
The definition is in printf_fp.c. */
extern unsigned int __guess_grouping (unsigned int intdig_max,
@@ -532,7 +529,7 @@ __vstrfmon_l (char *s, size_t maxsize, _
info.extra = 1; /* This means use values from LC_MONETARY. */
ptr = &fpnum;
- done = __printf_fp (&f._sbf._f, &info, &ptr);
+ done = __printf_fp_l (&f._sbf._f, loc, &info, &ptr);
if (done < 0)
return -1;
Index: b/stdlib/tst-strfmon_l.c
===================================================================
--- /dev/null
+++ b/stdlib/tst-strfmon_l.c
@@ -0,0 +1,220 @@
+/* Test locale dependence of strfmon_l.
+ Copyright (C) 2016 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <monetary.h>
+#include <string.h>
+#include <stdlib.h>
+#include <locale.h>
+
+static const char *const en_us_name = "en_US.ISO-8859-1";
+
+/* Locale value to be used by tests. */
+static locale_t loc;
+static const char *loc_name;
+
+/* Set the global locale to GLOBAL_NAME, and the locale referenced by
+ the loc variable above to LOCAL_NAME. */
+static void
+init_loc (const char *global_name, const char *local_name)
+{
+ loc = newlocale (LC_ALL_MASK, local_name, 0);
+ if (loc == 0)
+ {
+ printf ("error: newlocale (%s): %m\n", local_name);
+ abort ();
+ }
+ loc_name = local_name;
+
+ if (setlocale (LC_ALL, global_name) == NULL)
+ {
+ printf ("error: setlocale (%s): %m\n", global_name);
+ abort ();
+ }
+}
+
+/* Expected strings for a positive or negative value. */
+struct testcase
+{
+ const char *i; /* %i */
+ const char *n; /* %n */
+ const char *i_ungrouped; /* %^i */
+ const char *n_ungrouped; /* %^n */
+};
+
+/* Collected expected strings for both positive and negative
+ values. */
+struct testcase_pair
+{
+ struct testcase positive; /* 1234567.89 */
+ struct testcase negative; /* -1234567.89 */
+};
+
+static bool errors;
+
+/* Test one value using the locale loc. */
+static void
+test_one (const char *format, double value, const char *expected)
+{
+ static char actual[64];
+ int result = strfmon_l (actual, sizeof (actual), loc, format, value);
+ if (result < 0)
+ {
+ printf ("error: locale %s, format \"%s\", value %g: strfmon_l: %m\n",
+ loc_name, format, value);
+ errors = true;
+ }
+ else if (strcmp (actual, expected) != 0)
+ {
+ printf ("error: locale %s, format \"%s\", value %g: mismatch\n",
+ loc_name, format, value);
+ printf ("error: expected: \"%s\"\n", expected);
+ printf ("error: actual: \"%s\"\n", actual);
+ errors = true;
+ }
+}
+
+static void
+test_pair (const struct testcase_pair *pair)
+{
+ double positive = 1234567.89;
+ test_one ("%i", positive, pair->positive.i);
+ test_one ("%n", positive, pair->positive.n);
+ test_one ("%^i", positive, pair->positive.i_ungrouped);
+ test_one ("%^n", positive, pair->positive.n_ungrouped);
+ double negative = -1234567.89;
+ test_one ("%i", negative, pair->negative.i);
+ test_one ("%n", negative, pair->negative.n);
+ test_one ("%^i", negative, pair->negative.i_ungrouped);
+ test_one ("%^n", negative, pair->negative.n_ungrouped);
+}
+
+static const struct testcase_pair en_us =
+ {
+ {
+ "USD 1,234,567.89", "$1,234,567.89",
+ "USD 1234567.89", "$1234567.89"
+ },
+ {
+ "-USD 1,234,567.89", "-$1,234,567.89",
+ "-USD 1234567.89", "-$1234567.89"
+ }
+ };
+
+static void
+test_en_us (const char *other_name)
+{
+ init_loc (other_name, en_us_name);
+ test_pair (&en_us);
+ freelocale (loc);
+}
+
+struct locale_pair
+{
+ const char *locale_name;
+ struct testcase_pair pair;
+};
+
+static const struct locale_pair tests[] =
+ {
+ {
+ "de_DE.UTF-8",
+ {
+ {
+ "1.234.567,89 EUR", "1.234.567,89 \u20ac",
+ "1234567,89 EUR", "1234567,89 \u20ac"
+ },
+ {
+ "-1.234.567,89 EUR", "-1.234.567,89 \u20ac",
+ "-1234567,89 EUR", "-1234567,89 \u20ac"
+ }
+ },
+ },
+ {
+ "tg_TJ.UTF-8",
+ {
+ {
+ "1 234 567.89 TJS", "1 234 567.89 \u0440\u0443\u0431",
+ "1234567.89 TJS", "1234567.89 \u0440\u0443\u0431"
+ },
+ {
+ "-1 234 567.89 TJS", "-1 234 567.89 \u0440\u0443\u0431",
+ "-1234567.89 TJS", "-1234567.89 \u0440\u0443\u0431"
+ }
+ }
+ },
+ {
+ "te_IN.UTF-8",
+ {
+ {
+ "INR12,34,567.89", "\u20b912,34,567.89",
+ "INR1234567.89", "\u20b91234567.89"
+ },
+ {
+ "-INR12,34,567.89", "-\u20b912,34,567.89",
+ "-INR1234567.89", "-\u20b91234567.89"
+ }
+ }
+ },
+ {
+ "bn_IN.UTF-8",
+ {
+ {
+ "INR 12,345,67.89", "\u20b9 12,345,67.89",
+ "INR 1234567.89", "\u20b9 1234567.89"
+ },
+ {
+ "-INR 12,345,67.89", "-\u20b9 12,345,67.89",
+ "-INR 1234567.89", "-\u20b9 1234567.89"
+ }
+ }
+ },
+ {
+ "el_GR.UTF-8",
+ {
+ {
+ "1.234.567,89EUR", "1.234.567,89\u20ac",
+ "1234567,89EUR", "1234567,89\u20ac"
+ },
+ {
+ "-EUR1.234.567,89", "-\u20ac1.234.567,89",
+ "-EUR1234567,89", "-\u20ac1234567,89",
+ }
+ }
+ },
+ {}
+ };
+
+static int
+do_test (void)
+{
+ for (const struct locale_pair *test = tests;
+ test->locale_name != NULL; ++test)
+ {
+ init_loc (en_us_name, test->locale_name);
+ test_pair (&test->pair);
+ freelocale (loc);
+ test_en_us (test->locale_name);
+ }
+
+ return errors;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"