This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
PATCH: GH #17081: Workaround glibc bug with LC_MESSAGES
authorKarl Williamson <khw@cpan.org>
Thu, 28 Nov 2019 02:15:11 +0000 (19:15 -0700)
committerKarl Williamson <khw@cpan.org>
Sat, 30 Nov 2019 19:08:18 +0000 (12:08 -0700)
Please see the ticket for a full explanation.  This bug has been
submitted to glibc, without any real action forthcoming so far.

This invalidates the message cache each time the locale of LC_MESSAGES
is changed, as glibc should be doing this when uselocale changes that,
but glibc fails to do so.

This patch is an extension to the one submitted by Niko Tyni++.

I don't know how to test it, since a test would rely on several
different locales in different languages being available, and that
depends on what's installed on the platform.  I suppose that one could
go through the available locales, and try to find three with different
wording for the same message.  Doing so however would trigger the bug,
and at the end, if we didn't get three that differed, we wouldn't know
we wouldn't know if it is because of the bug, or that they just didn't
exist on the system.

However, below is a perl program that demonstrated the patch worked.
You could adjust it to the available locales.  The buggy code shows the
same text for all locales.  The fixed shows three different languages.

use strict;

use Locale::gettext;
use POSIX;

$ENV{LANG} = 'C.UTF-8';

for my $lang (qw(fi_FI fr_FR en_US)) {
    $ENV{LANGUAGE} = $lang;
    setlocale(LC_MESSAGES, '');
    my $d = Locale::gettext->domain("bash");
    print $d->get('syntax error'), "\n";
}

locale.c

index cdf125c..7ce7b3e 100644 (file)
--- a/locale.c
+++ b/locale.c
@@ -402,6 +402,7 @@ S_category_name(const int category)
  * known at compile time; "do_setlocale_r", not known until run time  */
 #  define do_setlocale_c(cat, locale) my_setlocale(cat, locale)
 #  define do_setlocale_r(cat, locale) my_setlocale(cat, locale)
+#  define FIX_GLIBC_LC_MESSAGES_BUG(i)
 
 #else   /* Below uses POSIX 2008 */
 
@@ -415,6 +416,22 @@ S_category_name(const int category)
                         emulate_setlocale(cat, locale, cat ## _INDEX, TRUE)
 #  define do_setlocale_r(cat, locale) emulate_setlocale(cat, locale, 0, FALSE)
 
+#  if ! defined(__GLIBC__) || ! defined(USE_LOCALE_MESSAGES)
+
+#    define FIX_GLIBC_LC_MESSAGES_BUG(i)
+
+#  else /* Invalidate glibc cache of loaded translations, see [perl #134264] */
+
+#    include <libintl.h>
+#    define FIX_GLIBC_LC_MESSAGES_BUG(i)                                        \
+        STMT_START {                                                        \
+            if ((i) == LC_MESSAGES_INDEX) {                                 \
+                textdomain(textdomain(NULL));                               \
+            }                                                               \
+        } STMT_END
+
+#  endif
+
 /* A third array, parallel to the ones above to map from category to its
  * equivalent mask */
 const int category_masks[] = {
@@ -1158,6 +1175,8 @@ S_emulate_setlocale(const int category,
             Safefree(PL_curlocales[i]);
             PL_curlocales[i] = savepv(locale);
         }
+
+        FIX_GLIBC_LC_MESSAGES_BUG(LC_MESSAGES_INDEX);
     }
     else {
 
@@ -1172,6 +1191,8 @@ S_emulate_setlocale(const int category,
         /* Then update the category's record */
         Safefree(PL_curlocales[index]);
         PL_curlocales[index] = savepv(locale);
+
+        FIX_GLIBC_LC_MESSAGES_BUG(index);
     }
 
 #  endif