From: Bruno Haible Date: Sat, 17 Jun 2023 08:49:04 +0000 (+0200) Subject: intl: Fix translation lookup failure when wbindtextdomain is used. X-Git-Tag: v0.22~7 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=40df836f7474dec73a1cd89e80977692d7f42dcb;p=thirdparty%2Fgettext.git intl: Fix translation lookup failure when wbindtextdomain is used. Reported by Luca Bacci at . The bug showed up when a call to bindtextdomain() with an absolute directory and a call to wbindtextdomain() for a different domain was in effect. Translations in the first referenced domain worked, translations in the second referenced domain did not. * gettext-runtime/intl/loadinfo.h (struct loaded_l10nfile, _nl_make_l10nflist): Improve comments. * gettext-runtime/intl/finddomain.c (_nl_loaded_domains): Likewise. * gettext-runtime/intl/l10nflist.c (_nl_make_l10nflist): Fix code that looks up the loaded_l10nfile in the given list, and make it more maintainable. * gettext-tools/tests/intl-6: Improve comments. * gettext-tools/tests/intl-6-prg.c (main): Restructure. * gettext-tools/tests/intl-7: New file, based on gettext-tools/tests/intl-6. * gettext-tools/tests/intl-6-prg.c: New file, based on gettext-tools/tests/intl-6-prg.c. * gettext-tools/tests/Makefile.am (TESTS): Add intl-7. (check_PROGRAMS): Add intl-7-prg. (intl_7_prg_SOURCES, intl_7_prg_LDADD): New variables. --- diff --git a/.gitignore b/.gitignore index 2983e47b1..3a15ea8a2 100644 --- a/.gitignore +++ b/.gitignore @@ -773,6 +773,8 @@ autom4te.cache/ /gettext-tools/tests/intl-5-prg.exe /gettext-tools/tests/intl-6-prg /gettext-tools/tests/intl-6-prg.exe +/gettext-tools/tests/intl-7-prg +/gettext-tools/tests/intl-7-prg.exe /gettext-tools/tests/intl-setlocale-1-prg /gettext-tools/tests/intl-setlocale-1-prg.exe /gettext-tools/tests/intl-setlocale-2-prg diff --git a/gettext-runtime/intl/finddomain.c b/gettext-runtime/intl/finddomain.c index e54b7346f..59352b5d2 100644 --- a/gettext-runtime/intl/finddomain.c +++ b/gettext-runtime/intl/finddomain.c @@ -1,5 +1,5 @@ /* Handle list of needed message catalogs - Copyright (C) 1995-2021 Free Software Foundation, Inc. + Copyright (C) 1995-2023 Free Software Foundation, Inc. Written by Ulrich Drepper , 1995. This program is free software: you can redistribute it and/or modify @@ -47,7 +47,11 @@ #endif /* @@ end of prolog @@ */ -/* List of already loaded domains. */ +/* List of already loaded domains. + On most platforms, it is sorted in decreasing order of ->filename. + On native Windows platforms, the elements with ->filename != NULL + are sorted in decreasing order of ->filename, and the elements with + ->wfilename != NULL are sorted in decreasing order of ->wfilename. */ static struct loaded_l10nfile *_nl_loaded_domains; /* Lock that protects the access to _NL_LOADED_DOMAINS. */ diff --git a/gettext-runtime/intl/l10nflist.c b/gettext-runtime/intl/l10nflist.c index d97060628..8d9eae811 100644 --- a/gettext-runtime/intl/l10nflist.c +++ b/gettext-runtime/intl/l10nflist.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1995-2020 Free Software Foundation, Inc. +/* Copyright (C) 1995-2023 Free Software Foundation, Inc. Contributed by Ulrich Drepper , 1995. This program is free software: you can redistribute it and/or modify @@ -218,31 +218,48 @@ _nl_make_l10nflist (struct loaded_l10nfile **l10nfile_list, /* Look in list of already loaded domains whether it is already available. */ lastp = l10nfile_list; - for (retval = *l10nfile_list; retval != NULL; retval = retval->next) - if (retval->filename != NULL #if defined _WIN32 && !defined __CYGWIN__ - || retval->wfilename != NULL + if (abs_wfilename != NULL) + { + for (retval = *l10nfile_list; retval != NULL; retval = retval->next) + { + if (retval->wfilename != NULL) + { + int compare = wcscmp (retval->wfilename, abs_wfilename); + if (compare == 0) + /* We found it! */ + break; + if (compare < 0) + { + /* It's not in the list, and we have found the place where it + needs to be inserted: at *LASTP. */ + retval = NULL; + break; + } + } + lastp = &retval->next; + } + } + else #endif - ) + for (retval = *l10nfile_list; retval != NULL; retval = retval->next) { - int compare = #if defined _WIN32 && !defined __CYGWIN__ - abs_wfilename != NULL - ? retval->wfilename != NULL && wcscmp (retval->wfilename, abs_wfilename) - : retval->filename != NULL && strcmp (retval->filename, abs_filename); -#else - strcmp (retval->filename, abs_filename); + if (retval->filename != NULL) #endif - if (compare == 0) - /* We found it! */ - break; - if (compare < 0) { - /* It's not in the list. */ - retval = NULL; - break; + int compare = strcmp (retval->filename, abs_filename); + if (compare == 0) + /* We found it! */ + break; + if (compare < 0) + { + /* It's not in the list, and we have found the place where it + needs to be inserted: at *LASTP. */ + retval = NULL; + break; + } } - lastp = &retval->next; } diff --git a/gettext-runtime/intl/loadinfo.h b/gettext-runtime/intl/loadinfo.h index c0a31d23b..97d8dd4a1 100644 --- a/gettext-runtime/intl/loadinfo.h +++ b/gettext-runtime/intl/loadinfo.h @@ -1,4 +1,4 @@ -/* Copyright (C) 1996-2020 Free Software Foundation, Inc. +/* Copyright (C) 1996-2023 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper , 1996. @@ -62,6 +62,9 @@ struct loaded_l10nfile { + /* The file name of the localization file. + It is set at construction time and then never changed. + Exactly one of filename, wfilename is non-NULL. */ const char *filename; #if defined _WIN32 && !defined __CYGWIN__ const wchar_t *wfilename; @@ -84,7 +87,11 @@ extern const char *_nl_normalize_codeset (const char *codeset, /* Lookup a locale dependent file. *L10NFILE_LIST denotes a pool of lookup results of locale dependent - files of the same kind, sorted in decreasing order of ->filename. + files of the same kind. + On most platforms, it is sorted in decreasing order of ->filename. + On native Windows platforms, the elements with ->filename != NULL + are sorted in decreasing order of ->filename, and the elements with + ->wfilename != NULL are sorted in decreasing order of ->wfilename. DIRLIST and DIRLIST_LEN are an argz list of directories in which to look. diff --git a/gettext-tools/tests/Makefile.am b/gettext-tools/tests/Makefile.am index f39459e79..f4e3d227d 100644 --- a/gettext-tools/tests/Makefile.am +++ b/gettext-tools/tests/Makefile.am @@ -21,7 +21,7 @@ EXTRA_DIST = MOSTLYCLEANFILES = core *.stackdump TESTS = gettext-1 gettext-2 \ - intl-1 intl-2 intl-3 intl-4 intl-5 intl-6 \ + intl-1 intl-2 intl-3 intl-4 intl-5 intl-6 intl-7 \ intl-setlocale-1 intl-setlocale-2 \ intl-thread-1 intl-thread-2 intl-thread-3 \ intl-version \ @@ -294,7 +294,7 @@ DEFS = -DLOCALEDIR=$(localedir_c_make) @DEFS@ LDADD = $(LDADD_@USE_INCLUDED_LIBINTL@) @INTL_MACOSX_LIBS@ LDADD_yes = ../../gettext-runtime/intl/libintl.la @LTLIBTHREAD@ LDADD_no = ../../gettext-runtime/intl/libgnuintl.la @LTLIBTHREAD@ @LTLIBINTL@ -check_PROGRAMS = tstgettext tstngettext testlocale intl-1-prg intl-3-prg intl-4-prg intl-5-prg intl-6-prg intl-setlocale-1-prg intl-setlocale-2-prg intl-thread-1-prg intl-thread-2-prg intl-thread-3-prg intl-version-prg cake fc3 fc4 fc5 gettextpo-1-prg sentence-1-prg +check_PROGRAMS = tstgettext tstngettext testlocale intl-1-prg intl-3-prg intl-4-prg intl-5-prg intl-6-prg intl-7-prg intl-setlocale-1-prg intl-setlocale-2-prg intl-thread-1-prg intl-thread-2-prg intl-thread-3-prg intl-version-prg cake fc3 fc4 fc5 gettextpo-1-prg sentence-1-prg tstgettext_SOURCES = \ tstgettext.c ../../gettext-runtime/src/escapes.h \ setlocale.c @@ -314,6 +314,8 @@ intl_5_prg_SOURCES = intl-5-prg.c intl_5_prg_LDADD = ../gnulib-lib/libgettextlib.la $(LDADD) intl_6_prg_SOURCES = intl-6-prg.c intl_6_prg_LDADD = ../gnulib-lib/libgettextlib.la $(LDADD) +intl_7_prg_SOURCES = intl-7-prg.c +intl_7_prg_LDADD = ../gnulib-lib/libgettextlib.la $(LDADD) intl_setlocale_1_prg_SOURCES = intl-setlocale-1-prg.c intl_setlocale_1_prg_LDADD = ../gnulib-lib/libgettextlib.la $(LDADD) intl_setlocale_2_prg_SOURCES = intl-setlocale-2-prg.c diff --git a/gettext-tools/tests/intl-6 b/gettext-tools/tests/intl-6 index 970c58caf..c1b3637ed 100755 --- a/gettext-tools/tests/intl-6 +++ b/gettext-tools/tests/intl-6 @@ -3,6 +3,7 @@ # Test that gettext() does basic translation lookup, even when the directory # with the message catalogs contains arbitrary Unicode characters. +# On native Windows, it uses wbindtextdomain(). test -d in-6 || mkdir in-6 test -d in-6/fr || mkdir in-6/fr diff --git a/gettext-tools/tests/intl-6-prg.c b/gettext-tools/tests/intl-6-prg.c index f2f9a65dc..0cf4bf843 100644 --- a/gettext-tools/tests/intl-6-prg.c +++ b/gettext-tools/tests/intl-6-prg.c @@ -1,5 +1,5 @@ /* Test program, used by the intl-6 test. - Copyright (C) 2000, 2005, 2007, 2013, 2018, 2020 Free Software Foundation, Inc. + Copyright (C) 2000, 2005, 2007, 2013, 2018, 2020, 2023 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -49,6 +49,16 @@ main (int argc, char *argv[]) wchar_t *wdir; int ret; + /* Clean up environment. */ + unsetenv ("LANGUAGE"); + unsetenv ("OUTPUT_CHARSET"); + + xsetenv ("LC_ALL", locale, 1); + if (setlocale (LC_ALL, "") == NULL) + setlocale (LC_ALL, "C"); + + /* Set up translation domain. */ + wdir = (wchar_t *) malloc ((strlen (dir) + 1) * sizeof (wchar_t)); mbstowcs (wdir, dir, strlen (dir) + 1); @@ -64,22 +74,15 @@ main (int argc, char *argv[]) exit (1); } - /* Clean up environment. */ - unsetenv ("LANGUAGE"); - unsetenv ("OUTPUT_CHARSET"); - textdomain ("tstprog"); - xsetenv ("LC_ALL", locale, 1); - if (setlocale (LC_ALL, "") == NULL) - setlocale (LC_ALL, "C"); - #if defined _WIN32 && !defined __CYGWIN__ wbindtextdomain ("tstprog", wunicodedir); #else bindtextdomain ("tstprog", unicodedir); #endif + /* Look up the translation. */ printf ("%s\n", gettext ("cheese")); /* Rename the directory back. */ diff --git a/gettext-tools/tests/intl-7 b/gettext-tools/tests/intl-7 new file mode 100755 index 000000000..59a2fd8ea --- /dev/null +++ b/gettext-tools/tests/intl-7 @@ -0,0 +1,137 @@ +#! /bin/sh +. "${srcdir=.}/init.sh"; path_prepend_ . ../src + +# Test that bindtextdomain() and wbindtextdomain(), in the same program, +# but applied to several different domains, work fine. + +# Try to find a writable absolute directory name. +case "$host_os" in + mingw*) + # On native Windows, it is important that ${ldir} is an absolute directory, + # so that the test exercises a mix between directories specified as 'char *' + # and directories specified as 'wchar_t *'. (If we used a relative + # directory, the code in dcigettext.c would transform it to a 'wchar_t *'.) + if (cygpath --version) >/dev/null 2>/dev/null; then + writable=`cygpath -w "$HOME"` + else + if test -n "${HOMEDRIVE}${HOMEPATH}" && test -w "${HOMEDRIVE}${HOMEPATH}"; then + writable="${HOMEDRIVE}${HOMEPATH}" + else + writable="C:" + fi + fi + ldir="${writable}"'\gettext-intl-7-'$$ + trap 'rm -fr "${ldir}"' HUP INT QUIT TERM + ;; + *) + ldir="`pwd`/in-7l" + ;; +esac + +test -d "${ldir}" || mkdir "${ldir}" +test -d "${ldir}"/fr || mkdir "${ldir}"/fr +test -d "${ldir}"/fr/LC_MESSAGES || mkdir "${ldir}"/fr/LC_MESSAGES + +test -d in-7 || mkdir in-7 +test -d in-7/fr || mkdir in-7/fr +test -d in-7/fr/LC_MESSAGES || mkdir in-7/fr/LC_MESSAGES +test -d in-7/fr_FR || mkdir in-7/fr_FR +test -d in-7/fr_FR/LC_MESSAGES || mkdir in-7/fr_FR/LC_MESSAGES + +cat < in-7l.po +msgid "" +msgstr "" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=US-ASCII\n" +"Content-Transfer-Encoding: 7-bit\n" + +msgid "dog" +msgstr "chien" +EOF + +cat < in-7-1.po +msgid "" +msgstr "" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=US-ASCII\n" +"Content-Transfer-Encoding: 7-bit\n" + +msgid "cheese" +msgstr "fromage" +EOF + +cat < in-7-2.po +msgid "" +msgstr "" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=US-ASCII\n" +"Content-Transfer-Encoding: 7-bit\n" + +msgid "cheese" +msgstr "camembert" +EOF + +cat < in-7-3.po +msgid "" +msgstr "" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=US-ASCII\n" +"Content-Transfer-Encoding: 7-bit\n" + +msgid "jam" +msgstr "confiture" +EOF + +: ${MSGFMT=msgfmt} +${MSGFMT} -o "${ldir}"/fr/LC_MESSAGES/tstlib.mo in-7l.po +${MSGFMT} -o in-7/fr/LC_MESSAGES/tstfoo.mo in-7-1.po +${MSGFMT} -o in-7/fr_FR/LC_MESSAGES/tstfoo.mo in-7-2.po +${MSGFMT} -o in-7/fr/LC_MESSAGES/tstbar.mo in-7-3.po + +: ${DIFF=diff} +cat < in-7.ok +chien +confiture +camembert +EOF + +: ${LOCALE_FR=fr_FR} +: ${LOCALE_FR_UTF8=fr_FR.UTF-8} +if test $LOCALE_FR != none; then + case "$host_os" in + cygwin*) + # On Cygwin, file names are interpreted according to the character + # encoding of the current locale, see + # . + # Therefore arbitrary Unicode characters are only supported in UTF-8 + # locales (including "C.UTF-8") and in the "C" locale. In particular, + # they are not supported in the fr_FR.ISO-8859-1 locale. + ;; + *) + prepare_locale_ in-7/fr in-7/$LOCALE_FR + prepare_locale_ in-7/fr_FR in-7/$LOCALE_FR + prepare_locale_ "${ldir}"/fr "${ldir}"/$LOCALE_FR + ../intl-7-prg "${ldir}" in-7 $LOCALE_FR > in-7.tmp || Exit 1 + LC_ALL=C tr -d '\r' < in-7.tmp > in-7.out || Exit 1 + ${DIFF} in-7.ok in-7.out || Exit 1 + ;; + esac +fi +if test $LOCALE_FR_UTF8 != none; then + prepare_locale_ in-7/fr in-7/$LOCALE_FR_UTF8 + prepare_locale_ in-7/fr_FR in-7/$LOCALE_FR_UTF8 + prepare_locale_ "${ldir}"/fr "${ldir}"/$LOCALE_FR_UTF8 + ../intl-7-prg "${ldir}" in-7 $LOCALE_FR_UTF8 > in-7.tmp || Exit 1 + LC_ALL=C tr -d '\r' < in-7.tmp > in-7.out || Exit 1 + ${DIFF} in-7.ok in-7.out || Exit 1 +fi +if test $LOCALE_FR = none && test $LOCALE_FR_UTF8 = none; then + if test -f /usr/bin/localedef; then + echo "Skipping test: no french locale is installed" + else + echo "Skipping test: no french locale is supported" + fi + Exit 77 +fi + +Exit 0 diff --git a/gettext-tools/tests/intl-7-prg.c b/gettext-tools/tests/intl-7-prg.c new file mode 100644 index 000000000..6c62c7fca --- /dev/null +++ b/gettext-tools/tests/intl-7-prg.c @@ -0,0 +1,106 @@ +/* Test program, used by the intl-7 test. + Copyright (C) 2000, 2005, 2007, 2013, 2018, 2020, 2023 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#if defined _WIN32 && !defined __CYGWIN__ +# include +#endif + +#include "xsetenv.h" +/* Make sure we use the included libintl, not the system's one. */ +#undef _LIBINTL_H +#include "libgnuintl.h" + +const char unicodedir[] = "русский…日本語…हिंदी…😷"; +#if defined _WIN32 && !defined __CYGWIN__ +const wchar_t wunicodedir[] = /* the same string in UTF-16 encoding */ + { 0x0440, 0x0443, 0x0441, 0x0441, 0x043A, 0x0438, 0x0439, 0x2026, + 0x65E5, 0x672C, 0x8A9E, 0x2026, + 0x0939, 0x093F, 0x0902, 0x0926, 0x0940, 0x2026, + 0xD83D, 0xDE37, 0 + }; +#endif + +int +main (int argc, char *argv[]) +{ + const char *lib_dir = argv[1]; + const char *dir = argv[2]; + const char *locale = argv[3]; + wchar_t *wdir; + int ret; + + /* Clean up environment. */ + unsetenv ("LANGUAGE"); + unsetenv ("OUTPUT_CHARSET"); + + xsetenv ("LC_ALL", locale, 1); + if (setlocale (LC_ALL, "") == NULL) + setlocale (LC_ALL, "C"); + + /* Set up translation domains. */ + + bindtextdomain ("tstlib", lib_dir); + + wdir = (wchar_t *) malloc ((strlen (dir) + 1) * sizeof (wchar_t)); + mbstowcs (wdir, dir, strlen (dir) + 1); + + /* Rename the directory. */ +#if defined _WIN32 && !defined __CYGWIN__ + ret = _wrename (wdir, wunicodedir); +#else + ret = rename (dir, unicodedir); +#endif + if (ret != 0) + { + fprintf (stderr, "Initial rename failed.\n"); + exit (1); + } + +#if defined _WIN32 && !defined __CYGWIN__ + wbindtextdomain ("tstbar", wunicodedir); + wbindtextdomain ("tstfoo", wunicodedir); +#else + bindtextdomain ("tstbar", unicodedir); + bindtextdomain ("tstfoo", unicodedir); +#endif + + /* Look up the translations. */ + printf ("%s\n", dgettext ("tstlib", "dog")); + printf ("%s\n", dgettext ("tstbar", "jam")); + printf ("%s\n", dgettext ("tstfoo", "cheese")); + + /* Rename the directory back. */ +#if defined _WIN32 && !defined __CYGWIN__ + ret = _wrename (wunicodedir, wdir); +#else + ret = rename (unicodedir, dir); +#endif + if (ret != 0) + { + fprintf (stderr, "Final rename failed.\n"); + exit (1); + } + + return 0; +}