The upstream patch is backported by excluding a dependency in stdlib/Makefile whose task is to ensure that locales are generated before the relevant tests are run. The dependency was added in order to fix SWBZ#18969 which is triggered when running `make check' for a specific subdirectory (stdlib here). Instead of this, the locales necessary for the test are generated via an addition in localedata/Makefile. commit 985fc132f23dbb83de76c5af9e783ef1b5900148 Author: Florian Weimer 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 +#include /* 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 @@ -290,6 +290,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 @@ -153,9 +153,9 @@ static wchar_t *group_number (wchar_t *b 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') @@ -1170,9 +1172,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); @@ -1243,8 +1247,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 @@ -71,7 +71,7 @@ tests := tst-strtol tst-strtod testmb t tst-qsort2 tst-makecontext2 tst-strtod6 tst-unsetenv1 \ tst-makecontext3 bug-getcontext bug-fmtmsg1 \ tst-secure-getenv tst-strtod-overflow tst-strtod-round \ - tst-tininess tst-strtod-underflow + tst-tininess tst-strtod-underflow tst-strfmon_l tests-static := tst-secure-getenv include ../Makeconfig 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 + . */ + +#include +#include +#include +#include +#include +#include + +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" Index: b/localedata/Makefile =================================================================== --- a/localedata/Makefile +++ b/localedata/Makefile @@ -120,7 +120,7 @@ LOCALES := de_DE.ISO-8859-1 de_DE.UTF-8 hr_HR.ISO-8859-2 sv_SE.ISO-8859-1 ja_JP.SJIS fr_FR.ISO-8859-1 \ nb_NO.ISO-8859-1 nn_NO.ISO-8859-1 tr_TR.UTF-8 cs_CZ.UTF-8 \ zh_TW.EUC-TW fa_IR.UTF-8 fr_FR.UTF-8 ja_JP.UTF-8 si_LK.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 LOCALE_SRCS := $(shell echo "$(LOCALES)"|sed 's/\([^ .]*\)[^ ]*/\1/g') CHARMAPS := $(shell echo "$(LOCALES)" | \ sed -e 's/[^ .]*[.]\([^ ]*\)/\1/g' -e s/SJIS/SHIFT_JIS/g)