2005-03-22 Roland McGrath <roland@redhat.com>
[kopensolaris-gnu/glibc.git] / intl / dcigettext.c
index e431e42..75762ea 100644 (file)
@@ -1,20 +1,21 @@
 /* Implementation of the internal dcigettext function.
 /* Implementation of the internal dcigettext function.
-   Copyright (C) 1995-1999, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1995-2002,2003,2004,2005 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
 
    The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Library General Public License as
-   published by the Free Software Foundation; either version 2 of the
-   License, or (at your option) any later version.
+   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
 
    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
-   Library General Public License for more details.
+   Lesser General Public License for more details.
 
 
-   You should have received a copy of the GNU Library General Public
-   License along with the GNU C Library; see the file COPYING.LIB.  If not,
-   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-   Boston, MA 02111-1307, USA.  */
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
 
 /* Tell glibc's <string.h> to provide a prototype for mempcpy().
    This must come before <config.h> because <config.h> may include
 
 /* Tell glibc's <string.h> to provide a prototype for mempcpy().
    This must come before <config.h> because <config.h> may include
 
 #include <sys/types.h>
 
 
 #include <sys/types.h>
 
-#if defined __GNUC__ && !defined C_ALLOCA
+#ifdef __GNUC__
 # define alloca __builtin_alloca
 # define HAVE_ALLOCA 1
 #else
 # define alloca __builtin_alloca
 # define HAVE_ALLOCA 1
 #else
-# if (defined HAVE_ALLOCA_H || defined _LIBC) && !defined C_ALLOCA
+# if defined HAVE_ALLOCA_H || defined _LIBC
 #  include <alloca.h>
 # else
 #  ifdef _AIX
 #  include <alloca.h>
 # else
 #  ifdef _AIX
@@ -54,47 +55,26 @@ extern int errno;
 # define __set_errno(val) errno = (val)
 #endif
 
 # define __set_errno(val) errno = (val)
 #endif
 
-#if defined STDC_HEADERS || defined _LIBC
-# include <stddef.h>
-# include <stdlib.h>
-#else
-char *getenv ();
-# ifdef HAVE_MALLOC_H
-#  include <malloc.h>
-# else
-void free ();
-# endif
-#endif
-
-#if defined HAVE_STRING_H || defined _LIBC
-# include <string.h>
-#else
-# include <strings.h>
-#endif
-#if !HAVE_STRCHR && !defined _LIBC
-# ifndef strchr
-#  define strchr index
-# endif
-#endif
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
 
 #if defined HAVE_UNISTD_H || defined _LIBC
 # include <unistd.h>
 #endif
 
 
 #if defined HAVE_UNISTD_H || defined _LIBC
 # include <unistd.h>
 #endif
 
-#if defined HAVE_LOCALE_H || defined _LIBC
-# include <locale.h>
-#endif
+#include <locale.h>
 
 #if defined HAVE_SYS_PARAM_H || defined _LIBC
 # include <sys/param.h>
 #endif
 
 
 #if defined HAVE_SYS_PARAM_H || defined _LIBC
 # include <sys/param.h>
 #endif
 
-#include "gettext.h"
 #include "gettextP.h"
 #include "gettextP.h"
+#include "plural-exp.h"
 #ifdef _LIBC
 # include <libintl.h>
 #else
 #ifdef _LIBC
 # include <libintl.h>
 #else
-# include "libgettext.h"
+# include "libgnuintl.h"
 #endif
 #include "hash-string.h"
 
 #endif
 #include "hash-string.h"
 
@@ -119,6 +99,21 @@ void free ();
     ((int) &((struct { char dummy1; TYPE dummy2; } *) 0)->dummy2)
 #endif
 
     ((int) &((struct { char dummy1; TYPE dummy2; } *) 0)->dummy2)
 #endif
 
+/* The internal variables in the standalone libintl.a must have different
+   names than the internal variables in GNU libc, otherwise programs
+   using libintl.a cannot be linked statically.  */
+#if !defined _LIBC
+# define _nl_default_default_domain libintl_nl_default_default_domain
+# define _nl_current_default_domain libintl_nl_current_default_domain
+# define _nl_default_dirname libintl_nl_default_dirname
+# define _nl_domain_bindings libintl_nl_domain_bindings
+#endif
+
+/* Some compilers, like SunOS4 cc, don't have offsetof in <stddef.h>.  */
+#ifndef offsetof
+# define offsetof(type,ident) ((size_t)&(((type*)0)->ident))
+#endif
+
 /* @@ end of prolog @@ */
 
 #ifdef _LIBC
 /* @@ end of prolog @@ */
 
 #ifdef _LIBC
@@ -177,33 +172,12 @@ static void *mempcpy PARAMS ((void *dest, const void *src, size_t n));
 # define PATH_MAX _POSIX_PATH_MAX
 #endif
 
 # define PATH_MAX _POSIX_PATH_MAX
 #endif
 
-/* XPG3 defines the result of `setlocale (category, NULL)' as:
-   ``Directs `setlocale()' to query `category' and return the current
-     setting of `local'.''
-   However it does not specify the exact format.  And even worse: POSIX
-   defines this not at all.  So we can use this feature only on selected
-   system (e.g. those using GNU C Library).  */
-#ifdef _LIBC
-# define HAVE_LOCALE_NULL
-#endif
-
-/* We want to allocate a string at the end of the struct.  gcc makes
-   this easy.  */
-#ifdef __GNUC__
-# define ZERO 0
-#else
-# define ZERO 1
-#endif
-
 /* This is the type used for the search tree where known translations
    are stored.  */
 struct known_translation_t
 {
   /* Domain in which to search.  */
 /* This is the type used for the search tree where known translations
    are stored.  */
 struct known_translation_t
 {
   /* Domain in which to search.  */
-  char *domain;
-
-  /* Plural index.  */
-  unsigned long int plindex;
+  char *domainname;
 
   /* The category.  */
   int category;
 
   /* The category.  */
   int category;
@@ -211,8 +185,12 @@ struct known_translation_t
   /* State of the catalog counter at the point the string was found.  */
   int counter;
 
   /* State of the catalog counter at the point the string was found.  */
   int counter;
 
+  /* Catalog where the string was found.  */
+  struct loaded_l10nfile *domain;
+
   /* And finally the translation.  */
   const char *translation;
   /* And finally the translation.  */
   const char *translation;
+  size_t translation_length;
 
   /* Pointer to the string in question.  */
   char msgid[ZERO];
 
   /* Pointer to the string in question.  */
   char msgid[ZERO];
@@ -230,26 +208,28 @@ static void *root;
 # endif
 
 /* Function to compare two entries in the table of known translations.  */
 # endif
 
 /* Function to compare two entries in the table of known translations.  */
+static int transcmp PARAMS ((const void *p1, const void *p2));
 static int
 static int
-transcmp (const void *p1, const void *p2)
+transcmp (p1, p2)
+     const void *p1;
+     const void *p2;
 {
 {
-  struct known_translation_t *s1 = (struct known_translation_t *) p1;
-  struct known_translation_t *s2 = (struct known_translation_t *) p2;
+  const struct known_translation_t *s1;
+  const struct known_translation_t *s2;
   int result;
 
   int result;
 
+  s1 = (const struct known_translation_t *) p1;
+  s2 = (const struct known_translation_t *) p2;
+
   result = strcmp (s1->msgid, s2->msgid);
   if (result == 0)
     {
   result = strcmp (s1->msgid, s2->msgid);
   if (result == 0)
     {
-      result = strcmp (s1->domain, s2->domain);
+      result = strcmp (s1->domainname, s2->domainname);
       if (result == 0)
       if (result == 0)
-       {
-         result = s1->plindex - s2->plindex;
-         if (result == 0)
-           /* We compare the category last (though this is the cheapest
-              operation) since it is hopefully always the same (namely
-              LC_MESSAGES).  */
-           result = s1->category - s2->category;
-       }
+       /* We compare the category last (though this is the cheapest
+          operation) since it is hopefully always the same (namely
+          LC_MESSAGES).  */
+       result = s1->category - s2->category;
     }
 
   return result;
     }
 
   return result;
@@ -258,31 +238,49 @@ transcmp (const void *p1, const void *p2)
 
 /* Name of the default domain used for gettext(3) prior any call to
    textdomain(3).  The default value for this is "messages".  */
 
 /* Name of the default domain used for gettext(3) prior any call to
    textdomain(3).  The default value for this is "messages".  */
-const char _nl_default_default_domain[] = "messages";
+const char _nl_default_default_domain[] attribute_hidden = "messages";
 
 /* Value used as the default domain for gettext(3).  */
 
 /* Value used as the default domain for gettext(3).  */
-const char *_nl_current_default_domain = _nl_default_default_domain;
+const char *_nl_current_default_domain attribute_hidden
+     = _nl_default_default_domain;
 
 /* Contains the default location of the message catalogs.  */
 
 /* Contains the default location of the message catalogs.  */
-const char _nl_default_dirname[] = GNULOCALEDIR;
+
+#ifdef _LIBC
+extern const char _nl_default_dirname[];
+libc_hidden_proto (_nl_default_dirname)
+#endif
+const char _nl_default_dirname[] = LOCALEDIR;
+#ifdef _LIBC
+libc_hidden_data_def (_nl_default_dirname)
+#endif
 
 /* List with bindings of specific domains created by bindtextdomain()
    calls.  */
 struct binding *_nl_domain_bindings;
 
 /* Prototypes for local functions.  */
 
 /* List with bindings of specific domains created by bindtextdomain()
    calls.  */
 struct binding *_nl_domain_bindings;
 
 /* Prototypes for local functions.  */
-static unsigned long int plural_eval (struct expression *pexp,
-                                     unsigned long int n) internal_function;
-static const char *category_to_name PARAMS ((int category)) internal_function;
+static char *plural_lookup PARAMS ((struct loaded_l10nfile *domain,
+                                   unsigned long int n,
+                                   const char *translation,
+                                   size_t translation_len))
+     internal_function;
 static const char *guess_category_value PARAMS ((int category,
                                                 const char *categoryname))
      internal_function;
 static const char *guess_category_value PARAMS ((int category,
                                                 const char *categoryname))
      internal_function;
+#ifdef _LIBC
+# include "../locale/localeinfo.h"
+# define category_to_name(category)    _nl_category_names[category]
+#else
+static const char *category_to_name PARAMS ((int category)) internal_function;
+#endif
 
 
 /* For those loosing systems which don't have `alloca' we have to add
    some additional code emulating it.  */
 #ifdef HAVE_ALLOCA
 /* Nothing has to be done.  */
 
 
 /* For those loosing systems which don't have `alloca' we have to add
    some additional code emulating it.  */
 #ifdef HAVE_ALLOCA
 /* Nothing has to be done.  */
+# define freea(p) /* nothing */
 # define ADD_BLOCK(list, address) /* nothing */
 # define FREE_BLOCKS(list) /* nothing */
 #else
 # define ADD_BLOCK(list, address) /* nothing */
 # define FREE_BLOCKS(list) /* nothing */
 #else
@@ -307,20 +305,27 @@ struct block_list
     while (list != NULL) {                                                   \
       struct block_list *old = list;                                         \
       list = list->next;                                                     \
     while (list != NULL) {                                                   \
       struct block_list *old = list;                                         \
       list = list->next;                                                     \
+      free (old->address);                                                   \
       free (old);                                                            \
     }                                                                        \
   } while (0)
 # undef alloca
 # define alloca(size) (malloc (size))
       free (old);                                                            \
     }                                                                        \
   } while (0)
 # undef alloca
 # define alloca(size) (malloc (size))
+# define freea(p) free (p)
 #endif /* have alloca */
 
 
 #endif /* have alloca */
 
 
+#ifdef _LIBC
 /* List of blocks allocated for translations.  */
 /* List of blocks allocated for translations.  */
-static struct transmem_list
+typedef struct transmem_list
 {
   struct transmem_list *next;
 {
   struct transmem_list *next;
-  char data[0];
-} *transmem_list;
+  char data[ZERO];
+} transmem_block_t;
+static struct transmem_list *transmem_list;
+#else
+typedef unsigned char transmem_block_t;
+#endif
 
 
 /* Names for the libintl functions are a problem.  They must not clash
 
 
 /* Names for the libintl functions are a problem.  They must not clash
@@ -330,11 +335,13 @@ static struct transmem_list
 #ifdef _LIBC
 # define DCIGETTEXT __dcigettext
 #else
 #ifdef _LIBC
 # define DCIGETTEXT __dcigettext
 #else
-# define DCIGETTEXT dcigettext__
+# define DCIGETTEXT libintl_dcigettext
 #endif
 
 /* Lock variable to protect the global data in the gettext implementation.  */
 #endif
 
 /* Lock variable to protect the global data in the gettext implementation.  */
-__libc_rwlock_define_initialized (, _nl_state_lock)
+#ifdef _LIBC
+__libc_rwlock_define_initialized (, _nl_state_lock attribute_hidden)
+#endif
 
 /* Checking whether the binaries runs SUID must be done and glibc provides
    easier methods therefore we make a difference here.  */
 
 /* Checking whether the binaries runs SUID must be done and glibc provides
    easier methods therefore we make a difference here.  */
@@ -342,6 +349,18 @@ __libc_rwlock_define_initialized (, _nl_state_lock)
 # define ENABLE_SECURE __libc_enable_secure
 # define DETERMINE_SECURE
 #else
 # define ENABLE_SECURE __libc_enable_secure
 # define DETERMINE_SECURE
 #else
+# ifndef HAVE_GETUID
+#  define getuid() 0
+# endif
+# ifndef HAVE_GETGID
+#  define getgid() 0
+# endif
+# ifndef HAVE_GETEUID
+#  define geteuid() getuid()
+# endif
+# ifndef HAVE_GETEGID
+#  define getegid() getgid()
+# endif
 static int enable_secure;
 # define ENABLE_SECURE (enable_secure == 1)
 # define DETERMINE_SECURE \
 static int enable_secure;
 # define ENABLE_SECURE (enable_secure == 1)
 # define DETERMINE_SECURE \
@@ -354,6 +373,9 @@ static int enable_secure;
     }
 #endif
 
     }
 #endif
 
+/* Get the function to evaluate the plural expression.  */
+#include "plural-eval.c"
+
 /* Look up MSGID in the DOMAINNAME message catalog for the current
    CATEGORY locale and, if PLURAL is nonzero, search over string
    depending on the plural form determined by N.  */
 /* Look up MSGID in the DOMAINNAME message catalog for the current
    CATEGORY locale and, if PLURAL is nonzero, search over string
    depending on the plural form determined by N.  */
@@ -376,6 +398,7 @@ DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category)
   char *dirname, *xdomainname;
   char *single_locale;
   char *retval;
   char *dirname, *xdomainname;
   char *single_locale;
   char *retval;
+  size_t retlen;
   int saved_errno;
 #if defined HAVE_TSEARCH || defined _LIBC
   struct known_translation_t *search;
   int saved_errno;
 #if defined HAVE_TSEARCH || defined _LIBC
   struct known_translation_t *search;
@@ -388,6 +411,15 @@ DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category)
   if (msgid1 == NULL)
     return NULL;
 
   if (msgid1 == NULL)
     return NULL;
 
+#ifdef _LIBC
+  if (category < 0 || category >= __LC_LAST || category == LC_ALL)
+    /* Bogus.  */
+    return (plural == 0
+           ? (char *) msgid1
+           /* Use the Germanic plural rule.  */
+           : n == 1 ? (char *) msgid1 : (char *) msgid2);
+#endif
+
   __libc_rwlock_rdlock (_nl_state_lock);
 
   /* If DOMAINNAME is NULL, we are interested in the default domain.  If
   __libc_rwlock_rdlock (_nl_state_lock);
 
   /* If DOMAINNAME is NULL, we are interested in the default domain.  If
@@ -399,23 +431,35 @@ DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category)
 #if defined HAVE_TSEARCH || defined _LIBC
   msgid_len = strlen (msgid1) + 1;
 
 #if defined HAVE_TSEARCH || defined _LIBC
   msgid_len = strlen (msgid1) + 1;
 
-  if (plural == 0)
+  /* Try to find the translation among those which we found at
+     some time.  */
+  search = (struct known_translation_t *)
+          alloca (offsetof (struct known_translation_t, msgid) + msgid_len);
+  memcpy (search->msgid, msgid1, msgid_len);
+  search->domainname = (char *) domainname;
+  search->category = category;
+
+  /* Since tfind/tsearch manage a balanced tree, concurrent tfind and
+     tsearch calls can be fatal.  */
+  __libc_rwlock_define_initialized (static, tree_lock);
+  __libc_rwlock_rdlock (tree_lock);
+
+  foundp = (struct known_translation_t **) tfind (search, &root, transcmp);
+
+  __libc_rwlock_unlock (tree_lock);
+
+  freea (search);
+  if (foundp != NULL && (*foundp)->counter == _nl_msg_cat_cntr)
     {
     {
-      /* Try to find the translation among those which we found at
-        some time.  */
-      search = (struct known_translation_t *) alloca (sizeof (*search)
-                                                     + msgid_len);
-      memcpy (search->msgid, msgid1, msgid_len);
-      search->domain = (char *) domainname;
-      search->plindex = 0;
-      search->category = category;
-
-      foundp = (struct known_translation_t **) tfind (search, &root, transcmp);
-      if (foundp != NULL && (*foundp)->counter == _nl_msg_cat_cntr)
-       {
-         __libc_rwlock_unlock (_nl_state_lock);
-         return (char *) (*foundp)->translation;
-       }
+      /* Now deal with plural.  */
+      if (plural)
+       retval = plural_lookup ((*foundp)->domain, n, (*foundp)->translation,
+                               (*foundp)->translation_length);
+      else
+       retval = (char *) (*foundp)->translation;
+
+      __libc_rwlock_unlock (_nl_state_lock);
+      return retval;
     }
 #endif
 
     }
 #endif
 
@@ -454,16 +498,18 @@ DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category)
       path_max = (unsigned int) PATH_MAX;
       path_max += 2;           /* The getcwd docs say to do this.  */
 
       path_max = (unsigned int) PATH_MAX;
       path_max += 2;           /* The getcwd docs say to do this.  */
 
-      dirname = (char *) alloca (path_max + dirname_len);
-      ADD_BLOCK (block_list, dirname);
-
-      __set_errno (0);
-      while ((ret = getcwd (dirname, path_max)) == NULL && errno == ERANGE)
+      for (;;)
        {
        {
-         path_max += PATH_INCR;
          dirname = (char *) alloca (path_max + dirname_len);
          ADD_BLOCK (block_list, dirname);
          dirname = (char *) alloca (path_max + dirname_len);
          ADD_BLOCK (block_list, dirname);
+
          __set_errno (0);
          __set_errno (0);
+         ret = getcwd (dirname, path_max);
+         if (ret != NULL || errno != ERANGE)
+           break;
+
+         path_max += path_max / 2;
+         path_max += PATH_INCR;
        }
 
       if (ret == NULL)
        }
 
       if (ret == NULL)
@@ -471,6 +517,7 @@ DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category)
          /* We cannot get the current working directory.  Don't signal an
             error but simply return the default string.  */
          FREE_BLOCKS (block_list);
          /* We cannot get the current working directory.  Don't signal an
             error but simply return the default string.  */
          FREE_BLOCKS (block_list);
+         __libc_rwlock_unlock (_nl_state_lock);
          __set_errno (saved_errno);
          return (plural == 0
                  ? (char *) msgid1
          __set_errno (saved_errno);
          return (plural == 0
                  ? (char *) msgid1
@@ -550,39 +597,7 @@ DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category)
 
       if (domain != NULL)
        {
 
       if (domain != NULL)
        {
-         unsigned long int index = 0;
-
-         if (plural != 0)
-           {
-             struct loaded_domain *domaindata =
-               (struct loaded_domain *) domain->data;
-             index = plural_eval (domaindata->plural, n);
-             if (index >= domaindata->nplurals)
-               /* This should never happen.  It means the plural expression
-                  and the given maximum value do not match.  */
-               index = 0;
-
-#if defined HAVE_TSEARCH || defined _LIBC
-             /* Try to find the translation among those which we
-                found at some time.  */
-             search = (struct known_translation_t *) alloca (sizeof (*search)
-                                                             + msgid_len);
-             memcpy (search->msgid, msgid1, msgid_len);
-             search->domain = (char *) domainname;
-             search->plindex = index;
-             search->category = category;
-
-             foundp = (struct known_translation_t **) tfind (search, &root,
-                                                             transcmp);
-             if (foundp != NULL && (*foundp)->counter == _nl_msg_cat_cntr)
-               {
-                 __libc_rwlock_unlock (_nl_state_lock);
-                 return (char *) (*foundp)->translation;
-               }
-#endif
-           }
-
-         retval = _nl_find_msg (domain, msgid1, index);
+         retval = _nl_find_msg (domain, binding, msgid1, &retlen);
 
          if (retval == NULL)
            {
 
          if (retval == NULL)
            {
@@ -590,18 +605,22 @@ DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category)
 
              for (cnt = 0; domain->successor[cnt] != NULL; ++cnt)
                {
 
              for (cnt = 0; domain->successor[cnt] != NULL; ++cnt)
                {
-                 retval = _nl_find_msg (domain->successor[cnt], msgid1,
-                                        index);
+                 retval = _nl_find_msg (domain->successor[cnt], binding,
+                                        msgid1, &retlen);
 
                  if (retval != NULL)
 
                  if (retval != NULL)
-                   break;
+                   {
+                     domain = domain->successor[cnt];
+                     break;
+                   }
                }
            }
 
          if (retval != NULL)
            {
                }
            }
 
          if (retval != NULL)
            {
+             /* Found the translation of MSGID1 in domain DOMAIN:
+                starting at RETVAL, RETLEN bytes.  */
              FREE_BLOCKS (block_list);
              FREE_BLOCKS (block_list);
-             __set_errno (saved_errno);
 #if defined HAVE_TSEARCH || defined _LIBC
              if (foundp == NULL)
                {
 #if defined HAVE_TSEARCH || defined _LIBC
              if (foundp == NULL)
                {
@@ -609,20 +628,27 @@ DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category)
                  struct known_translation_t *newp;
 
                  newp = (struct known_translation_t *)
                  struct known_translation_t *newp;
 
                  newp = (struct known_translation_t *)
-                   malloc (sizeof (*newp) + msgid_len
-                           + domainname_len + 1 - ZERO);
+                   malloc (offsetof (struct known_translation_t, msgid)
+                           + msgid_len + domainname_len + 1);
                  if (newp != NULL)
                    {
                  if (newp != NULL)
                    {
-                     newp->domain = mempcpy (newp->msgid, msgid1, msgid_len);
-                     memcpy (newp->domain, domainname, domainname_len + 1);
-                     newp->plindex = index;
+                     newp->domainname =
+                       mempcpy (newp->msgid, msgid1, msgid_len);
+                     memcpy (newp->domainname, domainname, domainname_len + 1);
                      newp->category = category;
                      newp->counter = _nl_msg_cat_cntr;
                      newp->category = category;
                      newp->counter = _nl_msg_cat_cntr;
+                     newp->domain = domain;
                      newp->translation = retval;
                      newp->translation = retval;
+                     newp->translation_length = retlen;
+
+                     __libc_rwlock_wrlock (tree_lock);
 
                      /* Insert the entry in the search tree.  */
                      foundp = (struct known_translation_t **)
                        tsearch (newp, &root, transcmp);
 
                      /* Insert the entry in the search tree.  */
                      foundp = (struct known_translation_t **)
                        tsearch (newp, &root, transcmp);
+
+                     __libc_rwlock_unlock (tree_lock);
+
                      if (foundp == NULL
                          || __builtin_expect (*foundp != newp, 0))
                        /* The insert failed.  */
                      if (foundp == NULL
                          || __builtin_expect (*foundp != newp, 0))
                        /* The insert failed.  */
@@ -633,9 +659,17 @@ DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category)
                {
                  /* We can update the existing entry.  */
                  (*foundp)->counter = _nl_msg_cat_cntr;
                {
                  /* We can update the existing entry.  */
                  (*foundp)->counter = _nl_msg_cat_cntr;
+                 (*foundp)->domain = domain;
                  (*foundp)->translation = retval;
                  (*foundp)->translation = retval;
+                 (*foundp)->translation_length = retlen;
                }
 #endif
                }
 #endif
+             __set_errno (saved_errno);
+
+             /* Now deal with plural.  */
+             if (plural)
+               retval = plural_lookup (domain, n, retval, retlen);
+
              __libc_rwlock_unlock (_nl_state_lock);
              return retval;
            }
              __libc_rwlock_unlock (_nl_state_lock);
              return retval;
            }
@@ -647,67 +681,70 @@ DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category)
 
 char *
 internal_function
 
 char *
 internal_function
-_nl_find_msg (domain_file, msgid, index)
+_nl_find_msg (domain_file, domainbinding, msgid, lengthp)
      struct loaded_l10nfile *domain_file;
      struct loaded_l10nfile *domain_file;
+     struct binding *domainbinding;
      const char *msgid;
      const char *msgid;
-     unsigned long int index;
+     size_t *lengthp;
 {
   struct loaded_domain *domain;
 {
   struct loaded_domain *domain;
+  nls_uint32 nstrings;
   size_t act;
   char *result;
   size_t act;
   char *result;
+  size_t resultlen;
 
 
-  if (domain_file->decided == 0)
-    _nl_load_domain (domain_file);
+  if (domain_file->decided <= 0)
+    _nl_load_domain (domain_file, domainbinding);
 
   if (domain_file->data == NULL)
     return NULL;
 
   domain = (struct loaded_domain *) domain_file->data;
 
 
   if (domain_file->data == NULL)
     return NULL;
 
   domain = (struct loaded_domain *) domain_file->data;
 
+  nstrings = domain->nstrings;
+
   /* Locate the MSGID and its translation.  */
   /* Locate the MSGID and its translation.  */
-  if (domain->hash_size > 2 && domain->hash_tab != NULL)
+  if (domain->hash_tab != NULL)
     {
       /* Use the hashing table.  */
       nls_uint32 len = strlen (msgid);
     {
       /* Use the hashing table.  */
       nls_uint32 len = strlen (msgid);
-      nls_uint32 hash_val = hash_string (msgid);
+      nls_uint32 hash_val = __hash_string (msgid);
       nls_uint32 idx = hash_val % domain->hash_size;
       nls_uint32 incr = 1 + (hash_val % (domain->hash_size - 2));
       nls_uint32 idx = hash_val % domain->hash_size;
       nls_uint32 incr = 1 + (hash_val % (domain->hash_size - 2));
-      nls_uint32 nstr = W (domain->must_swap, domain->hash_tab[idx]);
-
-      if (nstr == 0)
-       /* Hash table entry is empty.  */
-       return NULL;
-
-      if (W (domain->must_swap, domain->orig_tab[nstr - 1].length) == len
-         && strcmp (msgid,
-                    domain->data + W (domain->must_swap,
-                                      domain->orig_tab[nstr - 1].offset)) == 0)
-       {
-         act = nstr - 1;
-         goto found;
-       }
 
       while (1)
        {
 
       while (1)
        {
-         if (idx >= domain->hash_size - incr)
-           idx -= domain->hash_size - incr;
-         else
-           idx += incr;
+         nls_uint32 nstr =
+           W (domain->must_swap_hash_tab, domain->hash_tab[idx]);
 
 
-         nstr = W (domain->must_swap, domain->hash_tab[idx]);
          if (nstr == 0)
            /* Hash table entry is empty.  */
            return NULL;
 
          if (nstr == 0)
            /* Hash table entry is empty.  */
            return NULL;
 
-         if (W (domain->must_swap, domain->orig_tab[nstr - 1].length) == len
-             && (strcmp (msgid,
-                         domain->data + W (domain->must_swap,
-                                           domain->orig_tab[nstr - 1].offset))
-                 == 0))
+         nstr--;
+
+         /* Compare msgid with the original string at index nstr.
+            We compare the lengths with >=, not ==, because plural entries
+            are represented by strings with an embedded NUL.  */
+         if (nstr < nstrings
+             ? W (domain->must_swap, domain->orig_tab[nstr].length) >= len
+               && (strcmp (msgid,
+                           domain->data + W (domain->must_swap,
+                                             domain->orig_tab[nstr].offset))
+                   == 0)
+             : domain->orig_sysdep_tab[nstr - nstrings].length > len
+               && (strcmp (msgid,
+                           domain->orig_sysdep_tab[nstr - nstrings].pointer)
+                   == 0))
            {
            {
-             act = nstr - 1;
+             act = nstr;
              goto found;
            }
              goto found;
            }
+
+         if (idx >= domain->hash_size - incr)
+           idx -= domain->hash_size - incr;
+         else
+           idx += incr;
        }
       /* NOTREACHED */
     }
        }
       /* NOTREACHED */
     }
@@ -718,7 +755,7 @@ _nl_find_msg (domain_file, msgid, index)
       size_t top, bottom;
 
       bottom = 0;
       size_t top, bottom;
 
       bottom = 0;
-      top = domain->nstrings;
+      top = nstrings;
       while (bottom < top)
        {
          int cmp_val;
       while (bottom < top)
        {
          int cmp_val;
@@ -741,10 +778,29 @@ _nl_find_msg (domain_file, msgid, index)
  found:
   /* The translation was found at index ACT.  If we have to convert the
      string to use a different character set, this is the time.  */
  found:
   /* The translation was found at index ACT.  If we have to convert the
      string to use a different character set, this is the time.  */
-  result = ((char *) domain->data
-           + W (domain->must_swap, domain->trans_tab[act].offset));
+  if (act < nstrings)
+    {
+      result = (char *)
+       (domain->data + W (domain->must_swap, domain->trans_tab[act].offset));
+      resultlen = W (domain->must_swap, domain->trans_tab[act].length) + 1;
+    }
+  else
+    {
+      result = (char *) domain->trans_sysdep_tab[act - nstrings].pointer;
+      resultlen = domain->trans_sysdep_tab[act - nstrings].length;
+    }
 
 #if defined _LIBC || HAVE_ICONV
 
 #if defined _LIBC || HAVE_ICONV
+  if (domain->codeset_cntr
+      != (domainbinding != NULL ? domainbinding->codeset_cntr : 0))
+    {
+      /* The domain's codeset has changed through bind_textdomain_codeset()
+        since the message catalog was initialized or last accessed.  We
+        have to reinitialize the converter.  */
+      _nl_free_domain_conv (domain);
+      _nl_init_domain_conv (domain_file, domain, domainbinding);
+    }
+
   if (
 # ifdef _LIBC
       domain->conv != (__gconv_t) -1
   if (
 # ifdef _LIBC
       domain->conv != (__gconv_t) -1
@@ -759,13 +815,15 @@ _nl_find_msg (domain_file, msgid, index)
         appropriate table with the same structure as the table
         of translations in the file, where we can put the pointers
         to the converted strings in.
         appropriate table with the same structure as the table
         of translations in the file, where we can put the pointers
         to the converted strings in.
-        There is a slight complication with the INDEX: We don't know
-        a priori which entries are plural entries. Therefore at any
-        moment we can only translate the variants 0 .. INDEX.  */
+        There is a slight complication with plural entries.  They
+        are represented by consecutive NUL terminated strings.  We
+        handle this case by converting RESULTLEN bytes, including
+        NULs.  */
 
       if (domain->conv_tab == NULL
 
       if (domain->conv_tab == NULL
-         && ((domain->conv_tab = (char **) calloc (domain->nstrings,
-                                                   sizeof (char *)))
+         && ((domain->conv_tab =
+                (char **) calloc (nstrings + domain->n_sysdep_strings,
+                                  sizeof (char *)))
              == NULL))
        /* Mark that we didn't succeed allocating a table.  */
        domain->conv_tab = (char **) -1;
              == NULL))
        /* Mark that we didn't succeed allocating a table.  */
        domain->conv_tab = (char **) -1;
@@ -774,8 +832,7 @@ _nl_find_msg (domain_file, msgid, index)
        /* Nothing we can do, no more memory.  */
        goto converted;
 
        /* Nothing we can do, no more memory.  */
        goto converted;
 
-      if (domain->conv_tab[act] == NULL
-         || *(nls_uint32 *) domain->conv_tab[act] < index)
+      if (domain->conv_tab[act] == NULL)
        {
          /* We haven't used this string so far, so it is not
             translated yet.  Do this now.  */
        {
          /* We haven't used this string so far, so it is not
             translated yet.  Do this now.  */
@@ -783,46 +840,37 @@ _nl_find_msg (domain_file, msgid, index)
             We allocate always larger blocks which get used over
             time.  This is faster than many small allocations.   */
          __libc_lock_define_initialized (static, lock)
             We allocate always larger blocks which get used over
             time.  This is faster than many small allocations.   */
          __libc_lock_define_initialized (static, lock)
-#define INITIAL_BLOCK_SIZE     4080
+# define INITIAL_BLOCK_SIZE    4080
          static unsigned char *freemem;
          static size_t freemem_size;
 
          static unsigned char *freemem;
          static size_t freemem_size;
 
-         size_t resultlen;
          const unsigned char *inbuf;
          unsigned char *outbuf;
          int malloc_count;
          const unsigned char *inbuf;
          unsigned char *outbuf;
          int malloc_count;
-
-         /* Note that we translate (index + 1) consecutive strings at
-            once, including the final NUL byte.  */
-         {
-           unsigned long int i = index;
-           char *p = result;
-           do
-             p += strlen (p) + 1;
-           while (i-- > 0);
-           resultlen = p - result;
-         }
+# ifndef _LIBC
+         transmem_block_t *transmem_list = NULL;
+# endif
 
          __libc_lock_lock (lock);
 
 
          __libc_lock_lock (lock);
 
-         inbuf = result;
-         outbuf = freemem + sizeof (nls_uint32);
+         inbuf = (const unsigned char *) result;
+         outbuf = freemem + sizeof (size_t);
 
          malloc_count = 0;
          while (1)
            {
 
          malloc_count = 0;
          while (1)
            {
+             transmem_block_t *newmem;
 # ifdef _LIBC
 # ifdef _LIBC
-             struct transmem_list *newmem;
              size_t non_reversible;
              int res;
 
              size_t non_reversible;
              int res;
 
-             if (freemem_size < 4)
+             if (freemem_size < sizeof (size_t))
                goto resize_freemem;
 
              res = __gconv (domain->conv,
                             &inbuf, inbuf + resultlen,
                             &outbuf,
                goto resize_freemem;
 
              res = __gconv (domain->conv,
                             &inbuf, inbuf + resultlen,
                             &outbuf,
-                            outbuf + freemem_size - sizeof (nls_uint32),
+                            outbuf + freemem_size - sizeof (size_t),
                             &non_reversible);
 
              if (res == __GCONV_OK || res == __GCONV_EMPTY_INPUT)
                             &non_reversible);
 
              if (res == __GCONV_OK || res == __GCONV_EMPTY_INPUT)
@@ -834,20 +882,21 @@ _nl_find_msg (domain_file, msgid, index)
                  goto converted;
                }
 
                  goto converted;
                }
 
-             inbuf = result;
+             inbuf = (const unsigned char *) result;
 # else
 #  if HAVE_ICONV
 # else
 #  if HAVE_ICONV
-#   define transmem freemem
              const char *inptr = (const char *) inbuf;
              size_t inleft = resultlen;
              char *outptr = (char *) outbuf;
              size_t outleft;
 
              const char *inptr = (const char *) inbuf;
              size_t inleft = resultlen;
              char *outptr = (char *) outbuf;
              size_t outleft;
 
-             if (freemem_size < 4)
+             if (freemem_size < sizeof (size_t))
                goto resize_freemem;
 
                goto resize_freemem;
 
-             outleft = freemem_size - 4;
-             if (iconv (domain->conv, &inptr, &inleft, &outptr, &outleft)
+             outleft = freemem_size - sizeof (size_t);
+             if (iconv (domain->conv,
+                        (ICONV_CONST char **) &inptr, &inleft,
+                        &outptr, &outleft)
                  != (size_t) (-1))
                {
                  outbuf = (unsigned char *) outptr;
                  != (size_t) (-1))
                {
                  outbuf = (unsigned char *) outptr;
@@ -858,30 +907,34 @@ _nl_find_msg (domain_file, msgid, index)
                  __libc_lock_unlock (lock);
                  goto converted;
                }
                  __libc_lock_unlock (lock);
                  goto converted;
                }
-#  else
-#   define transmem freemem
 #  endif
 # endif
 
            resize_freemem:
 #  endif
 # endif
 
            resize_freemem:
-             /* We must allocate a new buffer of resize the old one.  */
+             /* We must allocate a new buffer or resize the old one.  */
              if (malloc_count > 0)
                {
              if (malloc_count > 0)
                {
-                 struct transmem_list *next = transmem_list->next;
-
                  ++malloc_count;
                  freemem_size = malloc_count * INITIAL_BLOCK_SIZE;
                  ++malloc_count;
                  freemem_size = malloc_count * INITIAL_BLOCK_SIZE;
-                 newmem = (struct transmem_list *) realloc (transmem_list,
-                                                            freemem_size);
-
+                 newmem = (transmem_block_t *) realloc (transmem_list,
+                                                        freemem_size);
+# ifdef _LIBC
                  if (newmem != NULL)
                  if (newmem != NULL)
-                   transmem_list = next;
+                   transmem_list = transmem_list->next;
+                 else
+                   {
+                     struct transmem_list *old = transmem_list;
+
+                     transmem_list = transmem_list->next;
+                     free (old);
+                   }
+# endif
                }
              else
                {
                  malloc_count = 1;
                  freemem_size = INITIAL_BLOCK_SIZE;
                }
              else
                {
                  malloc_count = 1;
                  freemem_size = INITIAL_BLOCK_SIZE;
-                 newmem = (struct transmem_list *) malloc (freemem_size);
+                 newmem = (transmem_block_t *) malloc (freemem_size);
                }
              if (__builtin_expect (newmem == NULL, 0))
                {
                }
              if (__builtin_expect (newmem == NULL, 0))
                {
@@ -897,29 +950,33 @@ _nl_find_msg (domain_file, msgid, index)
              newmem->next = transmem_list;
              transmem_list = newmem;
 
              newmem->next = transmem_list;
              transmem_list = newmem;
 
-             freemem = newmem->data;
+             freemem = (unsigned char *) newmem->data;
              freemem_size -= offsetof (struct transmem_list, data);
              freemem_size -= offsetof (struct transmem_list, data);
+# else
+             transmem_list = newmem;
+             freemem = newmem;
 # endif
 
 # endif
 
-             outbuf = freemem + sizeof (nls_uint32);
+             outbuf = freemem + sizeof (size_t);
            }
 
          /* We have now in our buffer a converted string.  Put this
             into the table of conversions.  */
            }
 
          /* We have now in our buffer a converted string.  Put this
             into the table of conversions.  */
-         *(nls_uint32 *) freemem = index;
-         domain->conv_tab[act] = freemem;
+         *(size_t *) freemem = outbuf - freemem - sizeof (size_t);
+         domain->conv_tab[act] = (char *) freemem;
          /* Shrink freemem, but keep it aligned.  */
          freemem_size -= outbuf - freemem;
          freemem = outbuf;
          /* Shrink freemem, but keep it aligned.  */
          freemem_size -= outbuf - freemem;
          freemem = outbuf;
-         freemem += freemem_size & (alignof (nls_uint32) - 1);
-         freemem_size = freemem_size & ~ (alignof (nls_uint32) - 1);
+         freemem += freemem_size & (alignof (size_t) - 1);
+         freemem_size = freemem_size & ~ (alignof (size_t) - 1);
 
          __libc_lock_unlock (lock);
        }
 
 
          __libc_lock_unlock (lock);
        }
 
-      /* Now domain->conv_tab[act] contains the translation of at least
-        the variants 0 .. INDEX.  */
-      result = domain->conv_tab[act] + sizeof (nls_uint32);
+      /* Now domain->conv_tab[act] contains the translation of all
+        the plural variants.  */
+      result = domain->conv_tab[act] + sizeof (size_t);
+      resultlen = *(size_t *) domain->conv_tab[act];
     }
 
  converted:
     }
 
  converted:
@@ -927,70 +984,52 @@ _nl_find_msg (domain_file, msgid, index)
 
 #endif /* _LIBC || HAVE_ICONV */
 
 
 #endif /* _LIBC || HAVE_ICONV */
 
-  /* Now skip some strings.  How much depends on the index passed in.  */
-  while (index-- > 0)
-    {
-#ifdef _LIBC
-      result = __rawmemchr (result, '\0');
-#else
-      result = strchr (result, '\0');
-#endif
-      /* And skip over the NUL byte.  */
-      ++result;
-    }
-
+  *lengthp = resultlen;
   return result;
 }
 
 
   return result;
 }
 
 
-/* Function to evaluate the plural expression and return an index value.  */
-static unsigned long int
+/* Look up a plural variant.  */
+static char *
 internal_function
 internal_function
-plural_eval (struct expression *pexp, unsigned long int n)
+plural_lookup (domain, n, translation, translation_len)
+     struct loaded_l10nfile *domain;
+     unsigned long int n;
+     const char *translation;
+     size_t translation_len;
 {
 {
-  switch (pexp->operation)
+  struct loaded_domain *domaindata = (struct loaded_domain *) domain->data;
+  unsigned long int index;
+  const char *p;
+
+  index = plural_eval (domaindata->plural, n);
+  if (index >= domaindata->nplurals)
+    /* This should never happen.  It means the plural expression and the
+       given maximum value do not match.  */
+    index = 0;
+
+  /* Skip INDEX strings at TRANSLATION.  */
+  p = translation;
+  while (index-- > 0)
     {
     {
-    case var:
-      return n;
-    case num:
-      return pexp->val.num;
-    case mult:
-      return (plural_eval (pexp->val.args2.left, n)
-             * plural_eval (pexp->val.args2.right, n));
-    case divide:
-      return (plural_eval (pexp->val.args2.left, n)
-             / plural_eval (pexp->val.args2.right, n));
-    case module:
-      return (plural_eval (pexp->val.args2.left, n)
-             % plural_eval (pexp->val.args2.right, n));
-    case plus:
-      return (plural_eval (pexp->val.args2.left, n)
-             + plural_eval (pexp->val.args2.right, n));
-    case minus:
-      return (plural_eval (pexp->val.args2.left, n)
-             - plural_eval (pexp->val.args2.right, n));
-    case equal:
-      return (plural_eval (pexp->val.args2.left, n)
-             == plural_eval (pexp->val.args2.right, n));
-    case not_equal:
-      return (plural_eval (pexp->val.args2.left, n)
-             != plural_eval (pexp->val.args2.right, n));
-    case land:
-      return (plural_eval (pexp->val.args2.left, n)
-             && plural_eval (pexp->val.args2.right, n));
-    case lor:
-      return (plural_eval (pexp->val.args2.left, n)
-             || plural_eval (pexp->val.args2.right, n));
-    case qmop:
-      return (plural_eval (pexp->val.args3.bexp, n)
-             ? plural_eval (pexp->val.args3.tbranch, n)
-             : plural_eval (pexp->val.args3.fbranch, n));
+#ifdef _LIBC
+      p = __rawmemchr (p, '\0');
+#else
+      p = strchr (p, '\0');
+#endif
+      /* And skip over the NUL byte.  */
+      p++;
+
+      if (p >= translation + translation_len)
+       /* This should never happen.  It means the plural expression
+          evaluated to a value larger than the number of variants
+          available for MSGID1.  */
+       return (char *) translation;
     }
     }
-  /* NOTREACHED */
-  return 0;
+  return (char *) p;
 }
 
 }
 
-
+#ifndef _LIBC
 /* Return string representation of locale CATEGORY.  */
 static const char *
 internal_function
 /* Return string representation of locale CATEGORY.  */
 static const char *
 internal_function
@@ -1050,6 +1089,7 @@ category_to_name (category)
 
   return retval;
 }
 
   return retval;
 }
+#endif
 
 /* Guess value of current locale from value of the environment variables.  */
 static const char *
 
 /* Guess value of current locale from value of the environment variables.  */
 static const char *
@@ -1058,39 +1098,26 @@ guess_category_value (category, categoryname)
      int category;
      const char *categoryname;
 {
      int category;
      const char *categoryname;
 {
+  const char *language;
   const char *retval;
 
   /* The highest priority value is the `LANGUAGE' environment
   const char *retval;
 
   /* The highest priority value is the `LANGUAGE' environment
-     variable.  This is a GNU extension.  */
-  retval = getenv ("LANGUAGE");
-  if (retval != NULL && retval[0] != '\0')
-    return retval;
-
-  /* `LANGUAGE' is not set.  So we have to proceed with the POSIX
-     methods of looking to `LC_ALL', `LC_xxx', and `LANG'.  On some
-     systems this can be done by the `setlocale' function itself.  */
-#if defined HAVE_SETLOCALE && defined HAVE_LC_MESSAGES && defined HAVE_LOCALE_NULL
-  return setlocale (category, NULL);
+     variable.  But we don't use the value if the currently selected
+     locale is the C locale.  This is a GNU extension.  */
+  language = getenv ("LANGUAGE");
+  if (language != NULL && language[0] == '\0')
+    language = NULL;
+
+  /* We have to proceed with the POSIX methods of looking to `LC_ALL',
+     `LC_xxx', and `LANG'.  On some systems this can be done by the
+     `setlocale' function itself.  */
+#ifdef _LIBC
+  retval = __current_locale_name (category);
 #else
 #else
-  /* Setting of LC_ALL overwrites all other.  */
-  retval = getenv ("LC_ALL");
-  if (retval != NULL && retval[0] != '\0')
-    return retval;
-
-  /* Next comes the name of the desired category.  */
-  retval = getenv (categoryname);
-  if (retval != NULL && retval[0] != '\0')
-    return retval;
-
-  /* Last possibility is the LANG environment variable.  */
-  retval = getenv ("LANG");
-  if (retval != NULL && retval[0] != '\0')
-    return retval;
-
-  /* We use C as the default domain.  POSIX says this is implementation
-     defined.  */
-  return "C";
+  retval = _nl_locale_name (category, categoryname);
 #endif
 #endif
+
+  return language != NULL && strcmp (retval, "C") != 0 ? language : retval;
 }
 
 /* @@ begin of epilog @@ */
 }
 
 /* @@ begin of epilog @@ */
@@ -1126,8 +1153,7 @@ mempcpy (dest, src, n)
 #ifdef _LIBC
 /* If we want to free all resources we have to do some work at
    program's end.  */
 #ifdef _LIBC
 /* If we want to free all resources we have to do some work at
    program's end.  */
-static void __attribute__ ((unused))
-free_mem (void)
+libc_freeres_fn (free_mem)
 {
   void *old;
 
 {
   void *old;
 
@@ -1157,6 +1183,4 @@ free_mem (void)
       free (old);
     }
 }
       free (old);
     }
 }
-
-text_set_element (__libc_subfreeres, free_mem);
 #endif
 #endif