bug-gettext
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [bug-gettext] [PATCH] intl: Add API to get/set language precedence l


From: Daiki Ueno
Subject: Re: [bug-gettext] [PATCH] intl: Add API to get/set language precedence list
Date: Thu, 19 May 2016 15:49:26 +0900
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/25.1.50 (gnu/linux)

Daiki Ueno <address@hidden> writes:

> To do that, I think we need a good name of the public header.

After some considerations, I came up with <langconf.h>, because those
functions are about user configuration of languages.

I also do not really like the name {set,get}_i18n_language(), because:

- "i18n" followed by "language" sounds redundant

- "language" is a bit misleading, because the document says it processes
  language precedence list

So, I would propose:

  /* Sets the language precedence list for the program to LANGUAGES.

     LANGUAGES may be a colon-separated list of language codes.
     If LANGUAGES is NULL, use the one inferred from the environment
     variable 'LANGUAGE'.

     The effect of the language precedence list is application
     dependent.  However, supporting applications should give priority
     to the language precedence list over the locale settings.

     This returns an opaque string that corresponds to the current
     language precedence list.  */
  const char *
  set_preferred_languages (const char *languages);

  /* Returns the language precedence list for the program.

     This returns NULL if the list is not set yet, or an opaque string
     that corresponds to the current language precedence list.  */
  const char *
  get_preferred_languages (void);

I also considered the following:

- {set,get}_gettext_language{,s}(): This is intuitive as long as the
  language precedence list is only used by gettext().  Otherwise it
  doesn't make sense, and it might make the border between the layer (B)
  and (C) vague.

- PREFIX_{set,get}_language{,s}(): Where PREFIX is "i18n", "nl", or
  something.

and here are some open questions:

- If this API is open to public, then how should the lock variable be
  exposed?

- Would it make any sense if those functions take a category, like
  LC_MESSAGES, to limit the effect?

Anyway, the above is still just a proposal, and better ideas are
welcome.

For the implementation, I'm attaching a couple of patches: one is
against gnulib master and the other one is against gettext master.

To test:

  $ cd .../gnulib
  $ patch -p1 < ...

  $ cd .../gettext
  $ patch -p1 < ...
  $ GNULIB_SRCDIR=`pwd`/../gnulib ./autogen.sh --no-git
  $ ./configure && make && make check

Regards,
-- 
Daiki Ueno
>From b5497a8b602a3b652d9fdbb7aa8bcc05fb158de4 Mon Sep 17 00:00:00 2001
From: Daiki Ueno <address@hidden>
Date: Wed, 18 May 2016 16:36:36 +0900
Subject: [PATCH v2] langconf, preferred-languages: new module

This adds functions to manipulate language precedence list, which
supersedes LANGUAGE envvar, used by gettext() or applications.
Suggested by Bruno Haible in:
https://lists.gnu.org/archive/html/bug-gettext/2016-05/msg00009.html
* modules/langconf: New file.
* modules/preferred-languages: New file.
* modules/preferred-languages-tests: New file.
* m4/langconf_h.m4: New file.
* lib/preferred-languages.c: New file.
* lib/langconf.in.h: New file.
* tests/test-preferred-languages.c: New file.
---
 ChangeLog                         |  13 ++++
 lib/langconf.in.h                 |  69 +++++++++++++++++++
 lib/preferred-languages.c         | 135 ++++++++++++++++++++++++++++++++++++++
 m4/langconf_h.m4                  |  58 ++++++++++++++++
 modules/langconf                  |  46 +++++++++++++
 modules/preferred-languages       |  27 ++++++++
 modules/preferred-languages-tests |  14 ++++
 tests/test-preferred-languages.c  |  60 +++++++++++++++++
 8 files changed, 422 insertions(+)
 create mode 100644 lib/langconf.in.h
 create mode 100644 lib/preferred-languages.c
 create mode 100644 m4/langconf_h.m4
 create mode 100644 modules/langconf
 create mode 100644 modules/preferred-languages
 create mode 100644 modules/preferred-languages-tests
 create mode 100644 tests/test-preferred-languages.c

diff --git a/ChangeLog b/ChangeLog
index 02a0f6f..2fec295 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2016-05-18  Daiki Ueno  <address@hidden>
+
+       langconf, preferred-languages: new module
+       This adds functions to manipulate language precedence list, which
+       supersedes LANGUAGE envvar, used by gettext() or applications.
+       * modules/langconf: New file.
+       * modules/preferred-languages: New file.
+       * modules/preferred-languages-tests: New file.
+       * m4/langconf_h.m4: New file.
+       * lib/preferred-languages.c: New file.
+       * lib/langconf.in.h: New file.
+       * tests/test-preferred-languages.c: New file.
+
 2016-05-17  Paul Eggert  <address@hidden>
 
        manywarnings: update for GCC 6.1
diff --git a/lib/langconf.in.h b/lib/langconf.in.h
new file mode 100644
index 0000000..7ab2b51
--- /dev/null
+++ b/lib/langconf.in.h
@@ -0,0 +1,69 @@
+/* Functions to manipulate language precedence list.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+
+   This program 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.
+
+   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 Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef address@hidden@_LANGCONF_H
+
+#if __GNUC__ >= 3
address@hidden@
+#endif
address@hidden@
+
+/* The include_next requires a split double-inclusion guard.  */
+#if @HAVE_LANGCONF_H@
+# @INCLUDE_NEXT@ @NEXT_LANGCONF_H@
+#endif
+
+#ifndef address@hidden@_LANGCONF_H
+#define address@hidden@_LANGCONF_H
+
+/* The definitions of _GL_FUNCDECL_RPL etc. are copied here.  */
+
+/* The definition of _GL_WARN_ON_USE is copied here.  */
+
+
+/* Declarations of functions.  */
+
+#if @GNULIB_PREFERRED_LANGUAGES@
+# if address@hidden@
+_GL_FUNCDECL_SYS (set_preferred_languages, const char *, (const char 
*languages));
+# endif
+_GL_CXXALIAS_SYS (set_preferred_languages, const char *, (const char 
*languages));
+_GL_CXXALIASWARN (set_preferred_languages);
+#elif defined GNULIB_POSIXCHECK
+# undef set_preferred_languages
+# if HAVE_RAW_DECL_SET_PREFERRED_LANGUAGES
+_GL_WARN_ON_USE (set_preferred_languages, "set_preferred_languages is 
unportable - "
+                 "use gnulib module langconf for portability");
+# endif
+#endif
+
+#if @GNULIB_PREFERRED_LANGUAGES@
+# if address@hidden@
+_GL_FUNCDECL_SYS (get_preferred_languages, const char *, (void));
+# endif
+_GL_CXXALIAS_SYS (get_preferred_languages, const char *, (void));
+_GL_CXXALIASWARN (get_preferred_languages);
+#elif defined GNULIB_POSIXCHECK
+# undef get_preferred_languages
+# if HAVE_RAW_DECL_GET_PREFERRED_LANGUAGES
+_GL_WARN_ON_USE (get_preferred_languages, "get_preferred_languages is 
unportable - "
+                 "use gnulib module langconf for portability");
+# endif
+#endif
+
+
+#endif  /* address@hidden@_LANGCONF_H */
+#endif  /* address@hidden@_LANGCONF_H */
diff --git a/lib/preferred-languages.c b/lib/preferred-languages.c
new file mode 100644
index 0000000..4fd302a
--- /dev/null
+++ b/lib/preferred-languages.c
@@ -0,0 +1,135 @@
+/* Functions to manipulate language precedence list.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+
+   This program 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.
+
+   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 Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef IN_LIBINTL
+# include "gettextP.h"
+#else
+# include <langconf.h>
+#endif
+
+#include <string.h>
+
+#ifndef attribute_hidden
+# define attribute_hidden
+#endif
+
+/* Handle multi-threaded applications.  */
+#ifdef _LIBC
+# include <bits/libc-lock.h>
+# define gl_rwlock_define_initialized __libc_rwlock_define_initialized
+# define gl_rwlock_rdlock __libc_rwlock_rdlock
+# define gl_rwlock_wrlock __libc_rwlock_wrlock
+# define gl_rwlock_unlock __libc_rwlock_unlock
+#else
+# ifdef IN_LIBINTL
+#  include "lock.h"
+# else
+#  include "glthread/lock.h"
+# endif
+#endif
+
+/* @@ end of prolog @@ */
+
+/* Names for the libintl functions are a problem.  They must not clash
+   with existing names and they should follow ANSI C.  But this source
+   code is also used in GNU C Library where the names have a __
+   prefix.  So we have to make a difference here.  */
+#ifdef _LIBC
+# define SET_PREFERRED_LANGUAGES __set_preferred_languages
+# define GET_PREFERRED_LANGUAGES __get_preferred_languages
+#elif IN_LIBINTL
+# define SET_PREFERRED_LANGUAGES libintl_set_preferred_languages
+# define GET_PREFERRED_LANGUAGES libintl_get_preferred_languages
+#else
+# define SET_PREFERRED_LANGUAGES set_preferred_languages
+# define GET_PREFERRED_LANGUAGES get_preferred_languages
+#endif
+
+#ifdef LIBC
+# define _nl_preferred_languages_lock __libc_preferred_languages_lock
+#elif IN_LIBINTL
+# define _nl_preferred_languages_lock libintl_preferred_languages_lock
+#endif
+
+/* Default language precedence list used for gettext(3) prior any call to
+   set_preferred_languages(3).  The default value for this is "".  */
+static const char _nl_default_preferred_languages[] attribute_hidden = "";
+
+/* Value used as the language precedence list for gettext(3).  */
+static const char *_nl_current_preferred_languages attribute_hidden;
+
+/* Lock variable to protect the global language precedence list in the
+   gettext implementation.  */
+gl_rwlock_define_initialized (, _nl_preferred_languages_lock attribute_hidden)
+
+
+/* Sets the language precedence list for the program to LANGUAGES.
+
+   LANGUAGES may be a colon-separated list of language codes.
+   If LANGUAGES is NULL, use the one inferred from the environment
+   variable 'LANGUAGE'.
+
+   The effect of the language precedence list is application
+   dependent.  However, supporting applications should give priority
+   to the language precedence list over the locale settings.
+
+   This returns an opaque string that corresponds to the current
+   language precedence list.  */
+const char *
+SET_PREFERRED_LANGUAGES (const char *languages)
+{
+  const char *new_languages;
+
+  gl_rwlock_wrlock (_nl_preferred_languages_lock);
+
+  if (languages == NULL)
+    languages = getenv ("LANGUAGE");
+
+  if (languages == NULL)
+    new_languages = _nl_default_preferred_languages;
+  else
+    {
+      new_languages = strdup (languages);
+      if (__builtin_expect (new_languages == NULL, 0))
+        new_languages = _nl_default_preferred_languages;
+    }
+
+  if (_nl_current_preferred_languages != _nl_default_preferred_languages)
+    free ((char *) _nl_current_preferred_languages);
+  _nl_current_preferred_languages = new_languages;
+
+  gl_rwlock_unlock (_nl_preferred_languages_lock);
+
+  return _nl_current_preferred_languages;
+}
+
+
+/* Returns the language precedence list for the program.
+
+   This returns NULL if the list is not set yet, or an opaque string
+   that corresponds to the current language precedence list.  */
+const char *
+GET_PREFERRED_LANGUAGES (void)
+{
+  /* No need to protect this access by
+     gl_rwlock_rdlock (_nl_preferred_languages_lock);
+     because reading a single aligned memory word is atomic.  */
+  return _nl_current_preferred_languages;
+}
diff --git a/m4/langconf_h.m4 b/m4/langconf_h.m4
new file mode 100644
index 0000000..3e50c41
--- /dev/null
+++ b/m4/langconf_h.m4
@@ -0,0 +1,58 @@
+# langconf_h.m4 serial 1
+dnl Copyright (C) 2016 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_LANGCONF_H],
+[
+  dnl Use AC_REQUIRE here, so that the default behavior below is expanded
+  dnl once only, before all statements that occur in other macros.
+  AC_REQUIRE([gl_LANGCONF_H_DEFAULTS])
+
+  dnl <langconf.h> is always overridden, because of GNULIB_POSIXCHECK.
+  gl_CHECK_NEXT_HEADERS([langconf.h])
+
+  dnl Determine whether <langconf.h> exists.
+  AC_CHECK_HEADERS_ONCE([langconf.h])
+  if test $ac_cv_header_langconf_h = yes; then
+    HAVE_LANGCONF_H=1
+  else
+    HAVE_LANGCONF_H=0
+  fi
+  AC_SUBST([HAVE_LANGCONF_H])
+
+  dnl Check for declarations of anything we want to poison if the
+  dnl corresponding gnulib module is not in use.
+  gl_WARN_ON_USE_PREPARE([[#include <langconf.h>]],
+    [set_preferred_languages get_preferred_languages])
+])
+
+AC_DEFUN([gl_LANGCONF_MODULE_INDICATOR],
+[
+  dnl Use AC_REQUIRE here, so that the default settings are expanded once only.
+  AC_REQUIRE([gl_LANGCONF_H_DEFAULTS])
+  gl_MODULE_INDICATOR_SET_VARIABLE([$1])
+  dnl Define it also as a C macro, for the benefit of the unit tests.
+  gl_MODULE_INDICATOR_FOR_TESTS([$1])
+])
+
+dnl Checks whether the system has the functions {set,get}_preferred_languages.
+dnl Set ac_cv_func_{set,get}_preferred_languages and
+dnl HAVE_{SET,GET}_PREFERRED_LANGUAGES.
+AC_DEFUN([gl_PREFERRED_LANGUAGES],
+[
+  AC_REQUIRE([gl_LANGCONF_H_DEFAULTS])
+  AC_CHECK_FUNCS_ONCE([set_preferred_languages get_preferred_languages])
+  if test $ac_cv_func_set_preferred_languages != yes || \
+     test $ac_cv_func_get_preferred_languages != yes; then
+    HAVE_PREFERRED_LANGUAGES=0
+  fi
+])
+
+AC_DEFUN([gl_LANGCONF_H_DEFAULTS],
+[
+  GNULIB_PREFERRED_LANGUAGES=0; AC_SUBST([GNULIB_PREFERRED_LANGUAGES])
+  dnl Assume proper GNU behavior unless another module says otherwise.
+  HAVE_PREFERRED_LANGUAGES=1;   AC_SUBST([HAVE_PREFERRED_LANGUAGES])
+])
diff --git a/modules/langconf b/modules/langconf
new file mode 100644
index 0000000..f0b93ad
--- /dev/null
+++ b/modules/langconf
@@ -0,0 +1,46 @@
+Description:
+A <langconf.h> for systems with missing declarations.
+
+Files:
+lib/langconf.in.h
+m4/langconf_h.m4
+
+Depends-on:
+include_next
+snippet/c++defs
+snippet/warn-on-use
+
+configure.ac:
+gl_LANGCONF_H
+
+Makefile.am:
+BUILT_SOURCES += langconf.h
+
+# We need the following in order to create <langconf.h> when the system
+# doesn't have one that works with the given compiler.
+langconf.h: langconf.in.h $(top_builddir)/config.status $(CXXDEFS_H) 
$(WARN_ON_USE_H)
+       $(AM_V_GEN)rm -f address@hidden $@ && \
+       { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */' && \
+         sed -e 's|@''GUARD_PREFIX''@|${gl_include_guard_prefix}|g' \
+             -e 's|@''HAVE_LANGCONF_H''@|$(HAVE_LANGCONF_H)|g' \
+             -e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \
+             -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \
+             -e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \
+             -e 's|@''NEXT_LANGCONF_H''@|$(NEXT_LANGCONF_H)|g' \
+             -e 
's/@''GNULIB_PREFERRED_LANGUAGES''@/$(GNULIB_PREFERRED_LANGUAGES)/g' \
+             -e 
's|@''HAVE_PREFERRED_LANGUAGES''@|$(HAVE_PREFERRED_LANGUAGES)|g' \
+             -e '/definitions of _GL_FUNCDECL_RPL/r $(CXXDEFS_H)' \
+             -e '/definition of _GL_ARG_NONNULL/r $(ARG_NONNULL_H)' \
+             -e '/definition of _GL_WARN_ON_USE/r $(WARN_ON_USE_H)' \
+             < $(srcdir)/langconf.in.h; \
+       } > address@hidden && \
+       mv address@hidden $@
+
+Include:
+<langconf.h>
+
+License:
+LGPLv2+
+
+Maintainer:
+all
diff --git a/modules/preferred-languages b/modules/preferred-languages
new file mode 100644
index 0000000..43e0d56
--- /dev/null
+++ b/modules/preferred-languages
@@ -0,0 +1,27 @@
+Description:
+Language precedence list.
+
+Files:
+lib/preferred-languages.c
+
+Depends-on:
+langconf
+lock
+
+configure.ac:
+gl_PREFERRED_LANGUAGES
+if test $HAVE_PREFERRED_LANGUAGES = 0; then
+  AC_LIBOBJ([preferred-languages])
+fi
+gl_LANGCONF_MODULE_INDICATOR([preferred-languages])
+
+Makefile.am:
+
+Include:
+<langconf.h>
+
+License:
+LGPLv2+
+
+Maintainer:
+all
diff --git a/modules/preferred-languages-tests 
b/modules/preferred-languages-tests
new file mode 100644
index 0000000..fb9dc9e
--- /dev/null
+++ b/modules/preferred-languages-tests
@@ -0,0 +1,14 @@
+Files:
+tests/test-preferred-languages.c
+tests/signature.h
+tests/macros.h
+
+Depends-on:
+setenv
+unsetenv
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-preferred-languages
+check_PROGRAMS += test-preferred-languages
diff --git a/tests/test-preferred-languages.c b/tests/test-preferred-languages.c
new file mode 100644
index 0000000..9675cd8
--- /dev/null
+++ b/tests/test-preferred-languages.c
@@ -0,0 +1,60 @@
+/* Test of {set,get}_preferred_languages.
+   Copyright (C) 2016 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 <http://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+#include <langconf.h>
+
+#include "signature.h"
+SIGNATURE_CHECK (set_preferred_languages, const char *, (const char *));
+SIGNATURE_CHECK (get_preferred_languages, const char *, (void));
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "macros.h"
+
+int
+main (void)
+{
+  const char *languages;
+
+  unsetenv ("LANGUAGE");
+
+  languages = get_preferred_languages ();
+  ASSERT (languages == NULL);
+
+  languages = set_preferred_languages (NULL);
+  ASSERT (languages != NULL && *languages == '\0');
+
+  languages = get_preferred_languages ();
+  ASSERT (languages != NULL && *languages == '\0');
+
+  setenv ("LANGUAGE", "a:b:c", 1);
+  languages = set_preferred_languages (NULL);
+  ASSERT (strcmp (languages, "a:b:c") == 0);
+
+  languages = get_preferred_languages ();
+  ASSERT (strcmp (languages, "a:b:c") == 0);
+
+  languages = set_preferred_languages ("d:e:f");
+  ASSERT (strcmp (languages, "d:e:f") == 0);
+
+  languages = get_preferred_languages ();
+  ASSERT (strcmp (languages, "d:e:f") == 0);
+
+  return 0;
+}
-- 
2.5.5

>From 83ea6f82bb34f691288396a743fe95904d0bdb9f Mon Sep 17 00:00:00 2001
From: Daiki Ueno <address@hidden>
Date: Thu, 12 May 2016 18:18:55 +0900
Subject: [PATCH v2] intl: Add API to get/set language precedence list

Due to a potential getenv() call for LANGUAGE envvar, multi-threaded
programs may crash in gettext(), when there is another thread calling
setenv().  To mitigate this, add a new API that allows setting language
precedence list programatically.  Suggested by Bruno Haible in:
https://lists.gnu.org/archive/html/bug-gettext/2016-05/msg00009.html
* autogen.sh (GNULIB_MODULES_RUNTIME_FOR_SRC)
(GNULIB_MODULES_TOOLS_FOR_SRC): Add 'preferred-languages'.
* gettext-runtime/intl/Makefile.in(SOURCES): Add preferred-languages.c.
(OBJECTS): Add preferred-languages.$lo.
(preferred-languages.$lo): New rule.
* gettext-runtime/intl/preferred-languages.c: New file.
* gettext-runtime/intl/dcigettext.c (guess_category_value): Check
LANGUAGE envvar only when the language precedence list is not set yet.
* gettext-runtime/intl/libgnuintl.in.h (libintl_set_preferred_languages)
(libintl_get_preferred_languages): New function declarations.
(set_preferred_languages, get_preferred_languages): New macro.
* gettext-tools/tests/Makefile.am (TESTS): Add test for
set_preferred_language().
(check_PROGRAMS): Add gettext-9-prg.
* gettext-tools/tests/gettext-9: New test.
* gettext-tools/tests/gettext-9-prg.c: New test program.
* gettext-tools/tests/.gitignore: Ignore gettext-9-prg.
---
 autogen.sh                                 |   2 +
 gettext-runtime/intl/Makefile.in           |   4 +
 gettext-runtime/intl/dcigettext.c          |  31 ++++++-
 gettext-runtime/intl/libgnuintl.in.h       |  10 +++
 gettext-runtime/intl/preferred-languages.c | 135 +++++++++++++++++++++++++++++
 gettext-runtime/m4/intl.m4                 |   1 +
 gettext-tools/gnulib-lib/.gitignore        |   3 +
 gettext-tools/gnulib-tests/.gitignore      |   1 +
 gettext-tools/tests/.gitignore             |   1 +
 gettext-tools/tests/Makefile.am            |   4 +-
 gettext-tools/tests/gettext-9              |  56 ++++++++++++
 gettext-tools/tests/gettext-9-prg.c        |  82 ++++++++++++++++++
 12 files changed, 325 insertions(+), 5 deletions(-)
 create mode 100644 gettext-runtime/intl/preferred-languages.c
 create mode 100755 gettext-tools/tests/gettext-9
 create mode 100644 gettext-tools/tests/gettext-9-prg.c

diff --git a/autogen.sh b/autogen.sh
index 51bdf05..aa707f2 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -140,6 +140,7 @@ if ! $skip_gnulib; then
       getopt-gnu
       gettext-h
       havelib
+      preferred-languages
       memmove
       progname
       propername
@@ -216,6 +217,7 @@ if ! $skip_gnulib; then
       javacomp
       javaexec
       libunistring-optional
+      preferred-languages
       localcharset
       locale
       localename
diff --git a/gettext-runtime/intl/Makefile.in b/gettext-runtime/intl/Makefile.in
index 4cc8b60..5a36d4a 100644
--- a/gettext-runtime/intl/Makefile.in
+++ b/gettext-runtime/intl/Makefile.in
@@ -141,6 +141,7 @@ SOURCES = \
   gettext.c \
   finddomain.c \
   hash-string.c \
+  preferred-languages.c \
   loadmsgcat.c \
   localealias.c \
   textdomain.c \
@@ -174,6 +175,7 @@ OBJECTS = \
   gettext.$lo \
   finddomain.$lo \
   hash-string.$lo \
+  preferred-languages.$lo \
   loadmsgcat.$lo \
   localealias.$lo \
   textdomain.$lo \
@@ -263,6 +265,8 @@ finddomain.lo: $(srcdir)/finddomain.c
        $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC --mode=compile $(COMPILE) 
$(srcdir)/finddomain.c
 hash-string.lo: $(srcdir)/hash-string.c
        $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC --mode=compile $(COMPILE) 
$(srcdir)/hash-string.c
+preferred-languages.lo: $(srcdir)/preferred-languages.c
+       $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC --mode=compile $(COMPILE) 
$(srcdir)/preferred-languages.c
 loadmsgcat.lo: $(srcdir)/loadmsgcat.c
        $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC --mode=compile $(COMPILE) 
$(srcdir)/loadmsgcat.c
 localealias.lo: $(srcdir)/localealias.c
diff --git a/gettext-runtime/intl/dcigettext.c 
b/gettext-runtime/intl/dcigettext.c
index 83bd775..f2041c4 100644
--- a/gettext-runtime/intl/dcigettext.c
+++ b/gettext-runtime/intl/dcigettext.c
@@ -434,9 +434,25 @@ typedef unsigned char transmem_block_t;
 # define DCIGETTEXT libintl_dcigettext
 #endif
 
+#ifdef LIBC
+# define _nl_preferred_languages_lock __libc_preferred_languages_lock
+#else
+# define _nl_preferred_languages_lock libintl_preferred_languages_lock
+#endif
+
+#ifdef LIBC
+# define get_preferred_languages __get_preferred_languages
+#else
+# define get_preferred_languages libintl_get_preferred_languages
+#endif
+
 /* Lock variable to protect the global data in the gettext implementation.  */
 gl_rwlock_define_initialized (, _nl_state_lock attribute_hidden)
 
+/* Lock variable to protect the global language precedence list in the
+   gettext implementation.  */
+gl_rwlock_define (extern, _nl_preferred_languages_lock attribute_hidden)
+
 /* Checking whether the binaries runs SUID must be done and glibc provides
    easier methods therefore we make a difference here.  */
 #ifdef _LIBC
@@ -527,6 +543,9 @@ DCIGETTEXT (const char *domainname, const char *msgid1, 
const char *msgid2,
   __libc_rwlock_rdlock (__libc_setlocale_lock);
 #endif
 
+  gl_rwlock_define (extern, _nl_preferred_languages_lock attribute_hidden);
+  gl_rwlock_rdlock (_nl_preferred_languages_lock);
+
   gl_rwlock_rdlock (_nl_state_lock);
 
   /* If DOMAINNAME is NULL, we are interested in the default domain.  If
@@ -582,6 +601,7 @@ DCIGETTEXT (const char *domainname, const char *msgid1, 
const char *msgid2,
        retval = (char *) (*foundp)->translation;
 
       gl_rwlock_unlock (_nl_state_lock);
+      gl_rwlock_unlock (_nl_preferred_languages_lock);
 # ifdef _LIBC
       __libc_rwlock_unlock (__libc_setlocale_lock);
 # endif
@@ -839,6 +859,7 @@ DCIGETTEXT (const char *domainname, const char *msgid1, 
const char *msgid2,
                retval = plural_lookup (domain, n, retval, retlen);
 
              gl_rwlock_unlock (_nl_state_lock);
+             gl_rwlock_unlock (_nl_preferred_languages_lock);
 #ifdef _LIBC
              __libc_rwlock_unlock (__libc_setlocale_lock);
 #endif
@@ -851,6 +872,7 @@ DCIGETTEXT (const char *domainname, const char *msgid1, 
const char *msgid2,
   /* Return the untranslated MSGID.  */
   FREE_BLOCKS (block_list);
   gl_rwlock_unlock (_nl_state_lock);
+  gl_rwlock_unlock (_nl_preferred_languages_lock);
 #ifdef _LIBC
   __libc_rwlock_unlock (__libc_setlocale_lock);
 #endif
@@ -1582,9 +1604,12 @@ guess_category_value (int category, const char 
*categoryname)
   if (strcmp (locale, "C") == 0)
     return locale;
 
-  /* The highest priority value is the value of the 'LANGUAGE' environment
-     variable.  */
-  language = getenv ("LANGUAGE");
+  /* The highest priority value is the language precedence list
+     supplied either by calling get_preferred_languages() or the value
+     of the 'LANGUAGE' environment variable.  */
+  language = get_preferred_languages ();
+  if (language == NULL)
+    language = getenv ("LANGUAGE");
   if (language != NULL && language[0] != '\0')
     return language;
 #if !defined IN_LIBGLOCALE && !defined _LIBC
diff --git a/gettext-runtime/intl/libgnuintl.in.h 
b/gettext-runtime/intl/libgnuintl.in.h
index 4fb1514..c2618d3 100644
--- a/gettext-runtime/intl/libgnuintl.in.h
+++ b/gettext-runtime/intl/libgnuintl.in.h
@@ -446,6 +446,16 @@ extern locale_t newlocale (int, const char *, locale_t);
 #endif
 
 
+/* Support for the language precedence list chosen by the user.  */
+#undef set_preferred_languages
+#define set_preferred_languages libintl_set_preferred_languages
+extern const char *libintl_set_preferred_languages (const char *);
+
+#undef get_preferred_languages
+#define get_preferred_languages libintl_get_preferred_languages
+extern const char *libintl_get_preferred_languages (void);
+
+
 /* Support for relocatable packages.  */
 
 /* Sets the original and the current installation prefix of the package.
diff --git a/gettext-runtime/intl/preferred-languages.c 
b/gettext-runtime/intl/preferred-languages.c
new file mode 100644
index 0000000..4fd302a
--- /dev/null
+++ b/gettext-runtime/intl/preferred-languages.c
@@ -0,0 +1,135 @@
+/* Functions to manipulate language precedence list.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+
+   This program 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.
+
+   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 Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef IN_LIBINTL
+# include "gettextP.h"
+#else
+# include <langconf.h>
+#endif
+
+#include <string.h>
+
+#ifndef attribute_hidden
+# define attribute_hidden
+#endif
+
+/* Handle multi-threaded applications.  */
+#ifdef _LIBC
+# include <bits/libc-lock.h>
+# define gl_rwlock_define_initialized __libc_rwlock_define_initialized
+# define gl_rwlock_rdlock __libc_rwlock_rdlock
+# define gl_rwlock_wrlock __libc_rwlock_wrlock
+# define gl_rwlock_unlock __libc_rwlock_unlock
+#else
+# ifdef IN_LIBINTL
+#  include "lock.h"
+# else
+#  include "glthread/lock.h"
+# endif
+#endif
+
+/* @@ end of prolog @@ */
+
+/* Names for the libintl functions are a problem.  They must not clash
+   with existing names and they should follow ANSI C.  But this source
+   code is also used in GNU C Library where the names have a __
+   prefix.  So we have to make a difference here.  */
+#ifdef _LIBC
+# define SET_PREFERRED_LANGUAGES __set_preferred_languages
+# define GET_PREFERRED_LANGUAGES __get_preferred_languages
+#elif IN_LIBINTL
+# define SET_PREFERRED_LANGUAGES libintl_set_preferred_languages
+# define GET_PREFERRED_LANGUAGES libintl_get_preferred_languages
+#else
+# define SET_PREFERRED_LANGUAGES set_preferred_languages
+# define GET_PREFERRED_LANGUAGES get_preferred_languages
+#endif
+
+#ifdef LIBC
+# define _nl_preferred_languages_lock __libc_preferred_languages_lock
+#elif IN_LIBINTL
+# define _nl_preferred_languages_lock libintl_preferred_languages_lock
+#endif
+
+/* Default language precedence list used for gettext(3) prior any call to
+   set_preferred_languages(3).  The default value for this is "".  */
+static const char _nl_default_preferred_languages[] attribute_hidden = "";
+
+/* Value used as the language precedence list for gettext(3).  */
+static const char *_nl_current_preferred_languages attribute_hidden;
+
+/* Lock variable to protect the global language precedence list in the
+   gettext implementation.  */
+gl_rwlock_define_initialized (, _nl_preferred_languages_lock attribute_hidden)
+
+
+/* Sets the language precedence list for the program to LANGUAGES.
+
+   LANGUAGES may be a colon-separated list of language codes.
+   If LANGUAGES is NULL, use the one inferred from the environment
+   variable 'LANGUAGE'.
+
+   The effect of the language precedence list is application
+   dependent.  However, supporting applications should give priority
+   to the language precedence list over the locale settings.
+
+   This returns an opaque string that corresponds to the current
+   language precedence list.  */
+const char *
+SET_PREFERRED_LANGUAGES (const char *languages)
+{
+  const char *new_languages;
+
+  gl_rwlock_wrlock (_nl_preferred_languages_lock);
+
+  if (languages == NULL)
+    languages = getenv ("LANGUAGE");
+
+  if (languages == NULL)
+    new_languages = _nl_default_preferred_languages;
+  else
+    {
+      new_languages = strdup (languages);
+      if (__builtin_expect (new_languages == NULL, 0))
+        new_languages = _nl_default_preferred_languages;
+    }
+
+  if (_nl_current_preferred_languages != _nl_default_preferred_languages)
+    free ((char *) _nl_current_preferred_languages);
+  _nl_current_preferred_languages = new_languages;
+
+  gl_rwlock_unlock (_nl_preferred_languages_lock);
+
+  return _nl_current_preferred_languages;
+}
+
+
+/* Returns the language precedence list for the program.
+
+   This returns NULL if the list is not set yet, or an opaque string
+   that corresponds to the current language precedence list.  */
+const char *
+GET_PREFERRED_LANGUAGES (void)
+{
+  /* No need to protect this access by
+     gl_rwlock_rdlock (_nl_preferred_languages_lock);
+     because reading a single aligned memory word is atomic.  */
+  return _nl_current_preferred_languages;
+}
diff --git a/gettext-runtime/m4/intl.m4 b/gettext-runtime/m4/intl.m4
index 42fac95..11e4325 100644
--- a/gettext-runtime/m4/intl.m4
+++ b/gettext-runtime/m4/intl.m4
@@ -44,6 +44,7 @@ AC_DEFUN([AM_INTL_SUBDIR],
   AC_REQUIRE([gt_INTL_MACOSX])dnl
   AC_REQUIRE([gl_EXTERN_INLINE])dnl
   AC_REQUIRE([gt_GL_ATTRIBUTE])dnl
+  AC_REQUIRE([gl_PREFERRED_LANGUAGES])dnl
 
   dnl Support for automake's --enable-silent-rules.
   case "$enable_silent_rules" in
diff --git a/gettext-tools/gnulib-lib/.gitignore 
b/gettext-tools/gnulib-lib/.gitignore
index d6ae633..0614c87 100644
--- a/gettext-tools/gnulib-lib/.gitignore
+++ b/gettext-tools/gnulib-lib/.gitignore
@@ -189,6 +189,8 @@
 /javaversion.class
 /javaversion.h
 /javaversion.java
+/langconf.c
+/langconf.in.h
 /langinfo.in.h
 /libunistring.valgrind
 /localcharset.c
@@ -250,6 +252,7 @@
 /pipe.h
 /pipe2-safer.c
 /pipe2.c
+/preferred-languages.c
 /printf-args.c
 /printf-args.h
 /printf-parse.c
diff --git a/gettext-tools/gnulib-tests/.gitignore 
b/gettext-tools/gnulib-tests/.gitignore
index 69eddcd..978bbb6 100644
--- a/gettext-tools/gnulib-tests/.gitignore
+++ b/gettext-tools/gnulib-tests/.gitignore
@@ -201,6 +201,7 @@
 /test-posix_spawn_file_actions_adddup2.c
 /test-posix_spawn_file_actions_addopen.c
 /test-pow.c
+/test-preferred-languages.c
 /test-quotearg-simple.c
 /test-quotearg.h
 /test-raise.c
diff --git a/gettext-tools/tests/.gitignore b/gettext-tools/tests/.gitignore
index 9e9b3b6..8513d2e 100644
--- a/gettext-tools/tests/.gitignore
+++ b/gettext-tools/tests/.gitignore
@@ -16,6 +16,7 @@
 /gettext-6-prg
 /gettext-7-prg
 /gettext-8-prg
+/gettext-9-prg
 /gettextpo-1-prg
 /sentence
 /testlocale
diff --git a/gettext-tools/tests/Makefile.am b/gettext-tools/tests/Makefile.am
index 20ad11c..260483e 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 gettext-3 gettext-4 gettext-5 gettext-6 gettext-7 \
-       gettext-8 \
+       gettext-8 gettext-9 \
        msgattrib-1 msgattrib-2 msgattrib-3 msgattrib-4 msgattrib-5 \
        msgattrib-6 msgattrib-7 msgattrib-8 msgattrib-9 msgattrib-10 \
        msgattrib-11 msgattrib-12 msgattrib-13 msgattrib-14 msgattrib-15 \
@@ -216,7 +216,7 @@ DEFS = -DLOCALEDIR=\"$(localedir)\" @DEFS@
 LDADD = $(address@hidden@) @INTL_MACOSX_LIBS@
 LDADD_yes = ../intl/libintl.la @LTLIBTHREAD@
 LDADD_no = ../intl/libgnuintl.la @LTLIBTHREAD@ @LTLIBINTL@
-check_PROGRAMS = tstgettext tstngettext testlocale gettext-3-prg gettext-4-prg 
gettext-5-prg gettext-6-prg gettext-7-prg gettext-8-prg cake fc3 fc4 fc5 
gettextpo-1-prg sentence
+check_PROGRAMS = tstgettext tstngettext testlocale gettext-3-prg gettext-4-prg 
gettext-5-prg gettext-6-prg gettext-7-prg gettext-8-prg gettext-9-prg cake fc3 
fc4 fc5 gettextpo-1-prg sentence
 tstgettext_SOURCES = tstgettext.c setlocale.c
 tstgettext_CFLAGS = -DINSTALLDIR=\".\"
 tstgettext_LDADD = ../gnulib-lib/libgettextlib.la $(LDADD)
diff --git a/gettext-tools/tests/gettext-9 b/gettext-tools/tests/gettext-9
new file mode 100755
index 0000000..754383a
--- /dev/null
+++ b/gettext-tools/tests/gettext-9
@@ -0,0 +1,56 @@
+#! /bin/sh
+. "${srcdir=.}/init.sh"; path_prepend_ . ../src
+
+# Test that on glibc systems, gettext() works right even with intermediate
+# setlocale() calls.
+
+# This test works only on glibc systems.
+: ${GLIBC2=no}
+test "$GLIBC2" = yes || {
+  echo "Skipping test: not a glibc system"
+  exit 77
+}
+
+# This test works only on systems that have a de_DE and fr_FR locale installed.
+LC_ALL=de_DE ../testlocale || {
+  if test -f /usr/bin/localedef; then
+    echo "Skipping test: locale de_DE not installed"
+  else
+    echo "Skipping test: locale de_DE not supported"
+  fi
+  exit 77
+}
+LC_ALL=fr_FR ../testlocale || {
+  if test -f /usr/bin/localedef; then
+    echo "Skipping test: locale fr_FR not installed"
+  else
+    echo "Skipping test: locale fr_FR not supported"
+  fi
+  exit 77
+}
+
+test -d gt-9 || mkdir gt-9
+test -d gt-9/de_DE || mkdir gt-9/de_DE
+test -d gt-9/de_DE/LC_MESSAGES || mkdir gt-9/de_DE/LC_MESSAGES
+test -d gt-9/fr_FR || mkdir gt-9/fr_FR
+test -d gt-9/fr_FR/LC_MESSAGES || mkdir gt-9/fr_FR/LC_MESSAGES
+
+: ${MSGFMT=msgfmt}
+${MSGFMT} -o gt-9/de_DE/LC_MESSAGES/tstlang.mo "$abs_srcdir"/gettext-3-1.po
+${MSGFMT} -o gt-9/fr_FR/LC_MESSAGES/tstlang.mo "$abs_srcdir"/gettext-3-2.po
+
+cat <<EOF > gt-9.ok
+String1 - Lang2: 1st string
+String2 - Lang2: 2nd string
+String1 - Lang2: 1st string
+String2 - Lang2: 2nd string
+String1 - First string for testing.
+String2 - Another string for testing.
+EOF
+
+../gettext-9-prg > gt-9.out || exit 1
+
+: ${DIFF=diff}
+${DIFF} gt-9.ok gt-9.out || exit 1
+
+exit 0
diff --git a/gettext-tools/tests/gettext-9-prg.c 
b/gettext-tools/tests/gettext-9-prg.c
new file mode 100644
index 0000000..90c46dc
--- /dev/null
+++ b/gettext-tools/tests/gettext-9-prg.c
@@ -0,0 +1,82 @@
+/* Test that gettext() honors language precedence list.
+   Copyright (C) 2007, 2015-2016 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 <http://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <address@hidden>, 2007.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+/* Make sure we use the included libintl, not the system's one. */
+#undef _LIBINTL_H
+#include "libgnuintl.h"
+
+#define N_(string) string
+
+struct data_t
+{
+  const char *selection;
+  const char *description;
+};
+
+struct data_t strings[] =
+{
+  { "String1", N_("First string for testing.") },
+  { "String2", N_("Another string for testing.") }
+};
+const int data_cnt = sizeof (strings) / sizeof (strings[0]);
+
+const char *lang[] = { "de_DE", "fr_FR", "ll_CC" };
+const int lang_cnt = sizeof (lang) / sizeof (lang[0]);
+
+int
+main ()
+{
+  int i;
+
+  /* Clean up environment.  */
+  unsetenv ("LANGUAGE");
+  unsetenv ("LC_ALL");
+  unsetenv ("LC_MESSAGES");
+  unsetenv ("LC_CTYPE");
+  unsetenv ("LANG");
+  unsetenv ("OUTPUT_CHARSET");
+
+  textdomain ("tstlang");
+
+  set_preferred_languages ("fr:fr_FR:de:de_DE");
+
+  for (i = 0; i < lang_cnt; ++i)
+    {
+      int j;
+
+      if (setlocale (LC_ALL, lang[i]) == NULL)
+        setlocale (LC_ALL, "C");
+
+      bindtextdomain ("tstlang", "gt-9");
+
+      for (j = 0; j < data_cnt; ++j)
+        printf ("%s - %s\n", strings[j].selection,
+                gettext (strings[j].description));
+    }
+
+  return 0;
+}
-- 
2.5.5


reply via email to

[Prev in Thread] Current Thread [Next in Thread]