Home | History | Annotate | Download | only in intl
      1 /* Implementation of the internal dcigettext function.
      2    Copyright (C) 1995-1999, 2000-2003 Free Software Foundation, Inc.
      3 
      4    This program is free software; you can redistribute it and/or modify it
      5    under the terms of the GNU Library General Public License as published
      6    by the Free Software Foundation; either version 2, or (at your option)
      7    any later version.
      8 
      9    This program is distributed in the hope that it will be useful,
     10    but WITHOUT ANY WARRANTY; without even the implied warranty of
     11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12    Library General Public License for more details.
     13 
     14    You should have received a copy of the GNU Library General Public
     15    License along with this program; if not, write to the Free Software
     16    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
     17    USA.  */
     18 
     19 /* Tell glibc's <string.h> to provide a prototype for mempcpy().
     20    This must come before <config.h> because <config.h> may include
     21    <features.h>, and once <features.h> has been included, it's too late.  */
     22 #ifndef _GNU_SOURCE
     23 # define _GNU_SOURCE	1
     24 #endif
     25 
     26 #ifdef HAVE_CONFIG_H
     27 # include <config.h>
     28 #endif
     29 
     30 #include <sys/types.h>
     31 
     32 #ifdef __GNUC__
     33 # define alloca __builtin_alloca
     34 # define HAVE_ALLOCA 1
     35 #else
     36 # ifdef _MSC_VER
     37 #  include <malloc.h>
     38 #  define alloca _alloca
     39 # else
     40 #  if defined HAVE_ALLOCA_H || defined _LIBC
     41 #   include <alloca.h>
     42 #  else
     43 #   ifdef _AIX
     44  #pragma alloca
     45 #   else
     46 #    ifndef alloca
     47 char *alloca ();
     48 #    endif
     49 #   endif
     50 #  endif
     51 # endif
     52 #endif
     53 
     54 #include <errno.h>
     55 #ifndef errno
     56 extern int errno;
     57 #endif
     58 #ifndef __set_errno
     59 # define __set_errno(val) errno = (val)
     60 #endif
     61 
     62 #include <stddef.h>
     63 #include <stdlib.h>
     64 #include <string.h>
     65 
     66 #if defined HAVE_UNISTD_H || defined _LIBC
     67 # include <unistd.h>
     68 #endif
     69 
     70 #include <locale.h>
     71 
     72 #ifdef _LIBC
     73   /* Guess whether integer division by zero raises signal SIGFPE.
     74      Set to 1 only if you know for sure.  In case of doubt, set to 0.  */
     75 # if defined __alpha__ || defined __arm__ || defined __i386__ \
     76      || defined __m68k__ || defined __s390__
     77 #  define INTDIV0_RAISES_SIGFPE 1
     78 # else
     79 #  define INTDIV0_RAISES_SIGFPE 0
     80 # endif
     81 #endif
     82 #if !INTDIV0_RAISES_SIGFPE
     83 # include <signal.h>
     84 #endif
     85 
     86 #if defined HAVE_SYS_PARAM_H || defined _LIBC
     87 # include <sys/param.h>
     88 #endif
     89 
     90 #include "gettextP.h"
     91 #include "plural-exp.h"
     92 #ifdef _LIBC
     93 # include <libintl.h>
     94 #else
     95 # include "libgnuintl.h"
     96 #endif
     97 #include "hash-string.h"
     98 
     99 /* Thread safetyness.  */
    100 #ifdef _LIBC
    101 # include <bits/libc-lock.h>
    102 #else
    103 /* Provide dummy implementation if this is outside glibc.  */
    104 # define __libc_lock_define_initialized(CLASS, NAME)
    105 # define __libc_lock_lock(NAME)
    106 # define __libc_lock_unlock(NAME)
    107 # define __libc_rwlock_define_initialized(CLASS, NAME)
    108 # define __libc_rwlock_rdlock(NAME)
    109 # define __libc_rwlock_unlock(NAME)
    110 #endif
    111 
    112 /* Alignment of types.  */
    113 #if defined __GNUC__ && __GNUC__ >= 2
    114 # define alignof(TYPE) __alignof__ (TYPE)
    115 #else
    116 # define alignof(TYPE) \
    117     ((int) &((struct { char dummy1; TYPE dummy2; } *) 0)->dummy2)
    118 #endif
    119 
    120 /* The internal variables in the standalone libintl.a must have different
    121    names than the internal variables in GNU libc, otherwise programs
    122    using libintl.a cannot be linked statically.  */
    123 #if !defined _LIBC
    124 # define _nl_default_default_domain libintl_nl_default_default_domain
    125 # define _nl_current_default_domain libintl_nl_current_default_domain
    126 # define _nl_default_dirname libintl_nl_default_dirname
    127 # define _nl_domain_bindings libintl_nl_domain_bindings
    128 #endif
    129 
    130 /* Some compilers, like SunOS4 cc, don't have offsetof in <stddef.h>.  */
    131 #ifndef offsetof
    132 # define offsetof(type,ident) ((size_t)&(((type*)0)->ident))
    133 #endif
    134 
    135 /* @@ end of prolog @@ */
    136 
    137 #ifdef _LIBC
    138 /* Rename the non ANSI C functions.  This is required by the standard
    139    because some ANSI C functions will require linking with this object
    140    file and the name space must not be polluted.  */
    141 # define getcwd __getcwd
    142 # ifndef stpcpy
    143 #  define stpcpy __stpcpy
    144 # endif
    145 # define tfind __tfind
    146 #else
    147 # if !defined HAVE_GETCWD
    148 char *getwd ();
    149 #  define getcwd(buf, max) getwd (buf)
    150 # else
    151 #  if VMS
    152 #   define getcwd(buf, max) (getcwd) (buf, max, 0)
    153 #  else
    154 char *getcwd ();
    155 #  endif
    156 # endif
    157 # ifndef HAVE_STPCPY
    158 #define stpcpy(dest, src) my_stpcpy(dest, src)
    159 static char *stpcpy (char *dest, const char *src);
    160 # endif
    161 # ifndef HAVE_MEMPCPY
    162 static void *mempcpy (void *dest, const void *src, size_t n);
    163 # endif
    164 #endif
    165 
    166 /* Amount to increase buffer size by in each try.  */
    167 #define PATH_INCR 32
    168 
    169 /* The following is from pathmax.h.  */
    170 /* Non-POSIX BSD systems might have gcc's limits.h, which doesn't define
    171    PATH_MAX but might cause redefinition warnings when sys/param.h is
    172    later included (as on MORE/BSD 4.3).  */
    173 #if defined _POSIX_VERSION || (defined HAVE_LIMITS_H && !defined __GNUC__)
    174 # include <limits.h>
    175 #endif
    176 
    177 #ifndef _POSIX_PATH_MAX
    178 # define _POSIX_PATH_MAX 255
    179 #endif
    180 
    181 #if !defined PATH_MAX && defined _PC_PATH_MAX
    182 # define PATH_MAX (pathconf ("/", _PC_PATH_MAX) < 1 ? 1024 : pathconf ("/", _PC_PATH_MAX))
    183 #endif
    184 
    185 /* Don't include sys/param.h if it already has been.  */
    186 #if defined HAVE_SYS_PARAM_H && !defined PATH_MAX && !defined MAXPATHLEN
    187 # include <sys/param.h>
    188 #endif
    189 
    190 #if !defined PATH_MAX && defined MAXPATHLEN
    191 # define PATH_MAX MAXPATHLEN
    192 #endif
    193 
    194 #ifndef PATH_MAX
    195 # define PATH_MAX _POSIX_PATH_MAX
    196 #endif
    197 
    198 /* Pathname support.
    199    ISSLASH(C)           tests whether C is a directory separator character.
    200    IS_ABSOLUTE_PATH(P)  tests whether P is an absolute path.  If it is not,
    201                         it may be concatenated to a directory pathname.
    202    IS_PATH_WITH_DIR(P)  tests whether P contains a directory specification.
    203  */
    204 #if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__
    205   /* Win32, OS/2, DOS */
    206 # define ISSLASH(C) ((C) == '/' || (C) == '\\')
    207 # define HAS_DEVICE(P) \
    208     ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
    209      && (P)[1] == ':')
    210 # define IS_ABSOLUTE_PATH(P) (ISSLASH ((P)[0]) || HAS_DEVICE (P))
    211 # define IS_PATH_WITH_DIR(P) \
    212     (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P))
    213 #else
    214   /* Unix */
    215 # define ISSLASH(C) ((C) == '/')
    216 # define IS_ABSOLUTE_PATH(P) ISSLASH ((P)[0])
    217 # define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL)
    218 #endif
    219 
    220 /* This is the type used for the search tree where known translations
    221    are stored.  */
    222 struct known_translation_t
    223 {
    224   /* Domain in which to search.  */
    225   char *domainname;
    226 
    227   /* The category.  */
    228   int category;
    229 
    230   /* State of the catalog counter at the point the string was found.  */
    231   int counter;
    232 
    233   /* Catalog where the string was found.  */
    234   struct loaded_l10nfile *domain;
    235 
    236   /* And finally the translation.  */
    237   const char *translation;
    238   size_t translation_length;
    239 
    240   /* Pointer to the string in question.  */
    241   char msgid[ZERO];
    242 };
    243 
    244 /* Root of the search tree with known translations.  We can use this
    245    only if the system provides the `tsearch' function family.  */
    246 #if defined HAVE_TSEARCH || defined _LIBC
    247 # include <search.h>
    248 
    249 static void *root;
    250 
    251 # ifdef _LIBC
    252 #  define tsearch __tsearch
    253 # endif
    254 
    255 /* Function to compare two entries in the table of known translations.  */
    256 static int
    257 transcmp (const void *p1, const void *p2)
    258 {
    259   const struct known_translation_t *s1;
    260   const struct known_translation_t *s2;
    261   int result;
    262 
    263   s1 = (const struct known_translation_t *) p1;
    264   s2 = (const struct known_translation_t *) p2;
    265 
    266   result = strcmp (s1->msgid, s2->msgid);
    267   if (result == 0)
    268     {
    269       result = strcmp (s1->domainname, s2->domainname);
    270       if (result == 0)
    271 	/* We compare the category last (though this is the cheapest
    272 	   operation) since it is hopefully always the same (namely
    273 	   LC_MESSAGES).  */
    274 	result = s1->category - s2->category;
    275     }
    276 
    277   return result;
    278 }
    279 #endif
    280 
    281 #ifndef INTVARDEF
    282 # define INTVARDEF(name)
    283 #endif
    284 #ifndef INTUSE
    285 # define INTUSE(name) name
    286 #endif
    287 
    288 /* Name of the default domain used for gettext(3) prior any call to
    289    textdomain(3).  The default value for this is "messages".  */
    290 const char _nl_default_default_domain[] attribute_hidden = "messages";
    291 
    292 /* Value used as the default domain for gettext(3).  */
    293 const char *_nl_current_default_domain attribute_hidden
    294      = _nl_default_default_domain;
    295 
    296 /* Contains the default location of the message catalogs.  */
    297 #if defined __EMX__
    298 extern const char _nl_default_dirname[];
    299 #else
    300 const char _nl_default_dirname[] = LOCALEDIR;
    301 INTVARDEF (_nl_default_dirname)
    302 #endif
    303 
    304 /* List with bindings of specific domains created by bindtextdomain()
    305    calls.  */
    306 struct binding *_nl_domain_bindings;
    307 
    308 /* Prototypes for local functions.  */
    309 static char *plural_lookup (struct loaded_l10nfile *domain,
    310 			    unsigned long int n,
    311 			    const char *translation, size_t translation_len)
    312      internal_function;
    313 static const char *guess_category_value (int category,
    314 					 const char *categoryname)
    315      internal_function;
    316 #ifdef _LIBC
    317 # include "../locale/localeinfo.h"
    318 # define category_to_name(category)	_nl_category_names[category]
    319 #else
    320 static const char *category_to_name (int category) internal_function;
    321 #endif
    322 
    323 
    324 /* For those loosing systems which don't have `alloca' we have to add
    325    some additional code emulating it.  */
    326 #ifdef HAVE_ALLOCA
    327 /* Nothing has to be done.  */
    328 # define freea(p) /* nothing */
    329 # define ADD_BLOCK(list, address) /* nothing */
    330 # define FREE_BLOCKS(list) /* nothing */
    331 #else
    332 struct block_list
    333 {
    334   void *address;
    335   struct block_list *next;
    336 };
    337 # define ADD_BLOCK(list, addr)						      \
    338   do {									      \
    339     struct block_list *newp = (struct block_list *) malloc (sizeof (*newp));  \
    340     /* If we cannot get a free block we cannot add the new element to	      \
    341        the list.  */							      \
    342     if (newp != NULL) {							      \
    343       newp->address = (addr);						      \
    344       newp->next = (list);						      \
    345       (list) = newp;							      \
    346     }									      \
    347   } while (0)
    348 # define FREE_BLOCKS(list)						      \
    349   do {									      \
    350     while (list != NULL) {						      \
    351       struct block_list *old = list;					      \
    352       list = list->next;						      \
    353       free (old->address);						      \
    354       free (old);							      \
    355     }									      \
    356   } while (0)
    357 # undef alloca
    358 # define alloca(size) (malloc (size))
    359 # define freea(p) free (p)
    360 #endif	/* have alloca */
    361 
    362 
    363 #ifdef _LIBC
    364 /* List of blocks allocated for translations.  */
    365 typedef struct transmem_list
    366 {
    367   struct transmem_list *next;
    368   char data[ZERO];
    369 } transmem_block_t;
    370 static struct transmem_list *transmem_list;
    371 #else
    372 typedef unsigned char transmem_block_t;
    373 #endif
    374 
    375 
    376 /* Names for the libintl functions are a problem.  They must not clash
    377    with existing names and they should follow ANSI C.  But this source
    378    code is also used in GNU C Library where the names have a __
    379    prefix.  So we have to make a difference here.  */
    380 #ifdef _LIBC
    381 # define DCIGETTEXT __dcigettext
    382 #else
    383 # define DCIGETTEXT libintl_dcigettext
    384 #endif
    385 
    386 /* Lock variable to protect the global data in the gettext implementation.  */
    387 #ifdef _LIBC
    388 __libc_rwlock_define_initialized (, _nl_state_lock attribute_hidden)
    389 #endif
    390 
    391 /* Checking whether the binaries runs SUID must be done and glibc provides
    392    easier methods therefore we make a difference here.  */
    393 #ifdef _LIBC
    394 # define ENABLE_SECURE __libc_enable_secure
    395 # define DETERMINE_SECURE
    396 #else
    397 # ifndef HAVE_GETUID
    398 #  define getuid() 0
    399 # endif
    400 # ifndef HAVE_GETGID
    401 #  define getgid() 0
    402 # endif
    403 # ifndef HAVE_GETEUID
    404 #  define geteuid() getuid()
    405 # endif
    406 # ifndef HAVE_GETEGID
    407 #  define getegid() getgid()
    408 # endif
    409 static int enable_secure;
    410 # define ENABLE_SECURE (enable_secure == 1)
    411 # define DETERMINE_SECURE \
    412   if (enable_secure == 0)						      \
    413     {									      \
    414       if (getuid () != geteuid () || getgid () != getegid ())		      \
    415 	enable_secure = 1;						      \
    416       else								      \
    417 	enable_secure = -1;						      \
    418     }
    419 #endif
    420 
    421 /* Get the function to evaluate the plural expression.  */
    422 #include "eval-plural.h"
    423 
    424 /* Look up MSGID in the DOMAINNAME message catalog for the current
    425    CATEGORY locale and, if PLURAL is nonzero, search over string
    426    depending on the plural form determined by N.  */
    427 char *
    428 DCIGETTEXT (const char *domainname, const char *msgid1, const char *msgid2,
    429 	    int plural, unsigned long int n, int category)
    430 {
    431 #ifndef HAVE_ALLOCA
    432   struct block_list *block_list = NULL;
    433 #endif
    434   struct loaded_l10nfile *domain;
    435   struct binding *binding;
    436   const char *categoryname;
    437   const char *categoryvalue;
    438   char *dirname, *xdomainname;
    439   char *single_locale;
    440   char *retval;
    441   size_t retlen;
    442   int saved_errno;
    443 #if defined HAVE_TSEARCH || defined _LIBC
    444   struct known_translation_t *search;
    445   struct known_translation_t **foundp = NULL;
    446   size_t msgid_len;
    447 #endif
    448   size_t domainname_len;
    449 
    450   /* If no real MSGID is given return NULL.  */
    451   if (msgid1 == NULL)
    452     return NULL;
    453 
    454 #ifdef _LIBC
    455   if (category < 0 || category >= __LC_LAST || category == LC_ALL)
    456     /* Bogus.  */
    457     return (plural == 0
    458 	    ? (char *) msgid1
    459 	    /* Use the Germanic plural rule.  */
    460 	    : n == 1 ? (char *) msgid1 : (char *) msgid2);
    461 #endif
    462 
    463   __libc_rwlock_rdlock (_nl_state_lock);
    464 
    465   /* If DOMAINNAME is NULL, we are interested in the default domain.  If
    466      CATEGORY is not LC_MESSAGES this might not make much sense but the
    467      definition left this undefined.  */
    468   if (domainname == NULL)
    469     domainname = _nl_current_default_domain;
    470 
    471   /* OS/2 specific: backward compatibility with older libintl versions  */
    472 #ifdef LC_MESSAGES_COMPAT
    473   if (category == LC_MESSAGES_COMPAT)
    474     category = LC_MESSAGES;
    475 #endif
    476 
    477 #if defined HAVE_TSEARCH || defined _LIBC
    478   msgid_len = strlen (msgid1) + 1;
    479 
    480   /* Try to find the translation among those which we found at
    481      some time.  */
    482   search = (struct known_translation_t *)
    483 	   alloca (offsetof (struct known_translation_t, msgid) + msgid_len);
    484   memcpy (search->msgid, msgid1, msgid_len);
    485   search->domainname = (char *) domainname;
    486   search->category = category;
    487 
    488   foundp = (struct known_translation_t **) tfind (search, &root, transcmp);
    489   freea (search);
    490   if (foundp != NULL && (*foundp)->counter == _nl_msg_cat_cntr)
    491     {
    492       /* Now deal with plural.  */
    493       if (plural)
    494 	retval = plural_lookup ((*foundp)->domain, n, (*foundp)->translation,
    495 				(*foundp)->translation_length);
    496       else
    497 	retval = (char *) (*foundp)->translation;
    498 
    499       __libc_rwlock_unlock (_nl_state_lock);
    500       return retval;
    501     }
    502 #endif
    503 
    504   /* Preserve the `errno' value.  */
    505   saved_errno = errno;
    506 
    507   /* See whether this is a SUID binary or not.  */
    508   DETERMINE_SECURE;
    509 
    510   /* First find matching binding.  */
    511   for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next)
    512     {
    513       int compare = strcmp (domainname, binding->domainname);
    514       if (compare == 0)
    515 	/* We found it!  */
    516 	break;
    517       if (compare < 0)
    518 	{
    519 	  /* It is not in the list.  */
    520 	  binding = NULL;
    521 	  break;
    522 	}
    523     }
    524 
    525   if (binding == NULL)
    526     dirname = (char *) INTUSE(_nl_default_dirname);
    527   else if (IS_ABSOLUTE_PATH (binding->dirname))
    528     dirname = binding->dirname;
    529   else
    530     {
    531       /* We have a relative path.  Make it absolute now.  */
    532       size_t dirname_len = strlen (binding->dirname) + 1;
    533       size_t path_max;
    534       char *ret;
    535 
    536       path_max = (unsigned int) PATH_MAX;
    537       path_max += 2;		/* The getcwd docs say to do this.  */
    538 
    539       for (;;)
    540 	{
    541 	  dirname = (char *) alloca (path_max + dirname_len);
    542 	  ADD_BLOCK (block_list, dirname);
    543 
    544 	  __set_errno (0);
    545 	  ret = getcwd (dirname, path_max);
    546 	  if (ret != NULL || errno != ERANGE)
    547 	    break;
    548 
    549 	  path_max += path_max / 2;
    550 	  path_max += PATH_INCR;
    551 	}
    552 
    553       if (ret == NULL)
    554 	/* We cannot get the current working directory.  Don't signal an
    555 	   error but simply return the default string.  */
    556 	goto return_untranslated;
    557 
    558       stpcpy (stpcpy (strchr (dirname, '\0'), "/"), binding->dirname);
    559     }
    560 
    561   /* Now determine the symbolic name of CATEGORY and its value.  */
    562   categoryname = category_to_name (category);
    563   categoryvalue = guess_category_value (category, categoryname);
    564 
    565   domainname_len = strlen (domainname);
    566   xdomainname = (char *) alloca (strlen (categoryname)
    567 				 + domainname_len + 5);
    568   ADD_BLOCK (block_list, xdomainname);
    569 
    570   stpcpy (mempcpy (stpcpy (stpcpy (xdomainname, categoryname), "/"),
    571 		  domainname, domainname_len),
    572 	  ".mo");
    573 
    574   /* Creating working area.  */
    575   single_locale = (char *) alloca (strlen (categoryvalue) + 1);
    576   ADD_BLOCK (block_list, single_locale);
    577 
    578 
    579   /* Search for the given string.  This is a loop because we perhaps
    580      got an ordered list of languages to consider for the translation.  */
    581   while (1)
    582     {
    583       /* Make CATEGORYVALUE point to the next element of the list.  */
    584       while (categoryvalue[0] != '\0' && categoryvalue[0] == ':')
    585 	++categoryvalue;
    586       if (categoryvalue[0] == '\0')
    587 	{
    588 	  /* The whole contents of CATEGORYVALUE has been searched but
    589 	     no valid entry has been found.  We solve this situation
    590 	     by implicitly appending a "C" entry, i.e. no translation
    591 	     will take place.  */
    592 	  single_locale[0] = 'C';
    593 	  single_locale[1] = '\0';
    594 	}
    595       else
    596 	{
    597 	  char *cp = single_locale;
    598 	  while (categoryvalue[0] != '\0' && categoryvalue[0] != ':')
    599 	    *cp++ = *categoryvalue++;
    600 	  *cp = '\0';
    601 
    602 	  /* When this is a SUID binary we must not allow accessing files
    603 	     outside the dedicated directories.  */
    604 	  if (ENABLE_SECURE && IS_PATH_WITH_DIR (single_locale))
    605 	    /* Ingore this entry.  */
    606 	    continue;
    607 	}
    608 
    609       /* If the current locale value is C (or POSIX) we don't load a
    610 	 domain.  Return the MSGID.  */
    611       if (strcmp (single_locale, "C") == 0
    612 	  || strcmp (single_locale, "POSIX") == 0)
    613 	break;
    614 
    615       /* Find structure describing the message catalog matching the
    616 	 DOMAINNAME and CATEGORY.  */
    617       domain = _nl_find_domain (dirname, single_locale, xdomainname, binding);
    618 
    619       if (domain != NULL)
    620 	{
    621 	  retval = _nl_find_msg (domain, binding, msgid1, &retlen);
    622 
    623 	  if (retval == NULL)
    624 	    {
    625 	      int cnt;
    626 
    627 	      for (cnt = 0; domain->successor[cnt] != NULL; ++cnt)
    628 		{
    629 		  retval = _nl_find_msg (domain->successor[cnt], binding,
    630 					 msgid1, &retlen);
    631 
    632 		  if (retval != NULL)
    633 		    {
    634 		      domain = domain->successor[cnt];
    635 		      break;
    636 		    }
    637 		}
    638 	    }
    639 
    640 	  if (retval != NULL)
    641 	    {
    642 	      /* Found the translation of MSGID1 in domain DOMAIN:
    643 		 starting at RETVAL, RETLEN bytes.  */
    644 	      FREE_BLOCKS (block_list);
    645 #if defined HAVE_TSEARCH || defined _LIBC
    646 	      if (foundp == NULL)
    647 		{
    648 		  /* Create a new entry and add it to the search tree.  */
    649 		  struct known_translation_t *newp;
    650 
    651 		  newp = (struct known_translation_t *)
    652 		    malloc (offsetof (struct known_translation_t, msgid)
    653 			    + msgid_len + domainname_len + 1);
    654 		  if (newp != NULL)
    655 		    {
    656 		      newp->domainname =
    657 			mempcpy (newp->msgid, msgid1, msgid_len);
    658 		      memcpy (newp->domainname, domainname, domainname_len + 1);
    659 		      newp->category = category;
    660 		      newp->counter = _nl_msg_cat_cntr;
    661 		      newp->domain = domain;
    662 		      newp->translation = retval;
    663 		      newp->translation_length = retlen;
    664 
    665 		      /* Insert the entry in the search tree.  */
    666 		      foundp = (struct known_translation_t **)
    667 			tsearch (newp, &root, transcmp);
    668 		      if (foundp == NULL
    669 			  || __builtin_expect (*foundp != newp, 0))
    670 			/* The insert failed.  */
    671 			free (newp);
    672 		    }
    673 		}
    674 	      else
    675 		{
    676 		  /* We can update the existing entry.  */
    677 		  (*foundp)->counter = _nl_msg_cat_cntr;
    678 		  (*foundp)->domain = domain;
    679 		  (*foundp)->translation = retval;
    680 		  (*foundp)->translation_length = retlen;
    681 		}
    682 #endif
    683 	      __set_errno (saved_errno);
    684 
    685 	      /* Now deal with plural.  */
    686 	      if (plural)
    687 		retval = plural_lookup (domain, n, retval, retlen);
    688 
    689 	      __libc_rwlock_unlock (_nl_state_lock);
    690 	      return retval;
    691 	    }
    692 	}
    693     }
    694 
    695  return_untranslated:
    696   /* Return the untranslated MSGID.  */
    697   FREE_BLOCKS (block_list);
    698   __libc_rwlock_unlock (_nl_state_lock);
    699 #if 0				/* Doesn't work with diet libc -- TYT */
    700 #ifndef _LIBC
    701   if (!ENABLE_SECURE)
    702     {
    703       extern void _nl_log_untranslated (const char *logfilename,
    704 					const char *domainname,
    705 					const char *msgid1, const char *msgid2,
    706 					int plural);
    707       const char *logfilename = getenv ("GETTEXT_LOG_UNTRANSLATED");
    708 
    709       if (logfilename != NULL && logfilename[0] != '\0')
    710 	_nl_log_untranslated (logfilename, domainname, msgid1, msgid2, plural);
    711     }
    712 #endif
    713 #endif
    714   __set_errno (saved_errno);
    715   return (plural == 0
    716 	  ? (char *) msgid1
    717 	  /* Use the Germanic plural rule.  */
    718 	  : n == 1 ? (char *) msgid1 : (char *) msgid2);
    719 }
    720 
    721 
    722 char *
    723 internal_function
    724 _nl_find_msg (struct loaded_l10nfile *domain_file,
    725 	      struct binding *domainbinding, const char *msgid,
    726 	      size_t *lengthp)
    727 {
    728   struct loaded_domain *domain;
    729   nls_uint32 nstrings;
    730   size_t act;
    731   char *result;
    732   size_t resultlen;
    733 
    734   if (domain_file->decided == 0)
    735     _nl_load_domain (domain_file, domainbinding);
    736 
    737   if (domain_file->data == NULL)
    738     return NULL;
    739 
    740   domain = (struct loaded_domain *) domain_file->data;
    741 
    742   nstrings = domain->nstrings;
    743 
    744   /* Locate the MSGID and its translation.  */
    745   if (domain->hash_tab != NULL)
    746     {
    747       /* Use the hashing table.  */
    748       nls_uint32 len = strlen (msgid);
    749       nls_uint32 hash_val = hash_string (msgid);
    750       nls_uint32 idx = hash_val % domain->hash_size;
    751       nls_uint32 incr = 1 + (hash_val % (domain->hash_size - 2));
    752 
    753       while (1)
    754 	{
    755 	  nls_uint32 nstr =
    756 	    W (domain->must_swap_hash_tab, domain->hash_tab[idx]);
    757 
    758 	  if (nstr == 0)
    759 	    /* Hash table entry is empty.  */
    760 	    return NULL;
    761 
    762 	  nstr--;
    763 
    764 	  /* Compare msgid with the original string at index nstr.
    765 	     We compare the lengths with >=, not ==, because plural entries
    766 	     are represented by strings with an embedded NUL.  */
    767 	  if (nstr < nstrings
    768 	      ? W (domain->must_swap, domain->orig_tab[nstr].length) >= len
    769 		&& (strcmp (msgid,
    770 			    domain->data + W (domain->must_swap,
    771 					      domain->orig_tab[nstr].offset))
    772 		    == 0)
    773 	      : domain->orig_sysdep_tab[nstr - nstrings].length > len
    774 		&& (strcmp (msgid,
    775 			    domain->orig_sysdep_tab[nstr - nstrings].pointer)
    776 		    == 0))
    777 	    {
    778 	      act = nstr;
    779 	      goto found;
    780 	    }
    781 
    782 	  if (idx >= domain->hash_size - incr)
    783 	    idx -= domain->hash_size - incr;
    784 	  else
    785 	    idx += incr;
    786 	}
    787       /* NOTREACHED */
    788     }
    789   else
    790     {
    791       /* Try the default method:  binary search in the sorted array of
    792 	 messages.  */
    793       size_t top, bottom;
    794 
    795       bottom = 0;
    796       top = nstrings;
    797       while (bottom < top)
    798 	{
    799 	  int cmp_val;
    800 
    801 	  act = (bottom + top) / 2;
    802 	  cmp_val = strcmp (msgid, (domain->data
    803 				    + W (domain->must_swap,
    804 					 domain->orig_tab[act].offset)));
    805 	  if (cmp_val < 0)
    806 	    top = act;
    807 	  else if (cmp_val > 0)
    808 	    bottom = act + 1;
    809 	  else
    810 	    goto found;
    811 	}
    812       /* No translation was found.  */
    813       return NULL;
    814     }
    815 
    816  found:
    817   /* The translation was found at index ACT.  If we have to convert the
    818      string to use a different character set, this is the time.  */
    819   if (act < nstrings)
    820     {
    821       result = (char *)
    822 	(domain->data + W (domain->must_swap, domain->trans_tab[act].offset));
    823       resultlen = W (domain->must_swap, domain->trans_tab[act].length) + 1;
    824     }
    825   else
    826     {
    827       result = (char *) domain->trans_sysdep_tab[act - nstrings].pointer;
    828       resultlen = domain->trans_sysdep_tab[act - nstrings].length;
    829     }
    830 
    831 #if defined _LIBC || HAVE_ICONV
    832   if (domain->codeset_cntr
    833       != (domainbinding != NULL ? domainbinding->codeset_cntr : 0))
    834     {
    835       /* The domain's codeset has changed through bind_textdomain_codeset()
    836 	 since the message catalog was initialized or last accessed.  We
    837 	 have to reinitialize the converter.  */
    838       _nl_free_domain_conv (domain);
    839       _nl_init_domain_conv (domain_file, domain, domainbinding);
    840     }
    841 
    842   if (
    843 # ifdef _LIBC
    844       domain->conv != (__gconv_t) -1
    845 # else
    846 #  if HAVE_ICONV
    847       domain->conv != (iconv_t) -1
    848 #  endif
    849 # endif
    850       )
    851     {
    852       /* We are supposed to do a conversion.  First allocate an
    853 	 appropriate table with the same structure as the table
    854 	 of translations in the file, where we can put the pointers
    855 	 to the converted strings in.
    856 	 There is a slight complication with plural entries.  They
    857 	 are represented by consecutive NUL terminated strings.  We
    858 	 handle this case by converting RESULTLEN bytes, including
    859 	 NULs.  */
    860 
    861       if (domain->conv_tab == NULL
    862 	  && ((domain->conv_tab =
    863 		 (char **) calloc (nstrings + domain->n_sysdep_strings,
    864 				   sizeof (char *)))
    865 	      == NULL))
    866 	/* Mark that we didn't succeed allocating a table.  */
    867 	domain->conv_tab = (char **) -1;
    868 
    869       if (__builtin_expect (domain->conv_tab == (char **) -1, 0))
    870 	/* Nothing we can do, no more memory.  */
    871 	goto converted;
    872 
    873       if (domain->conv_tab[act] == NULL)
    874 	{
    875 	  /* We haven't used this string so far, so it is not
    876 	     translated yet.  Do this now.  */
    877 	  /* We use a bit more efficient memory handling.
    878 	     We allocate always larger blocks which get used over
    879 	     time.  This is faster than many small allocations.   */
    880 	  __libc_lock_define_initialized (static, lock)
    881 # define INITIAL_BLOCK_SIZE	4080
    882 	  static unsigned char *freemem;
    883 	  static size_t freemem_size;
    884 
    885 	  const unsigned char *inbuf;
    886 	  unsigned char *outbuf;
    887 	  int malloc_count;
    888 # ifndef _LIBC
    889 	  transmem_block_t *transmem_list = NULL;
    890 # endif
    891 
    892 	  __libc_lock_lock (lock);
    893 
    894 	  inbuf = (const unsigned char *) result;
    895 	  outbuf = freemem + sizeof (size_t);
    896 
    897 	  malloc_count = 0;
    898 	  while (1)
    899 	    {
    900 	      transmem_block_t *newmem;
    901 # ifdef _LIBC
    902 	      size_t non_reversible;
    903 	      int res;
    904 
    905 	      if (freemem_size < sizeof (size_t))
    906 		goto resize_freemem;
    907 
    908 	      res = __gconv (domain->conv,
    909 			     &inbuf, inbuf + resultlen,
    910 			     &outbuf,
    911 			     outbuf + freemem_size - sizeof (size_t),
    912 			     &non_reversible);
    913 
    914 	      if (res == __GCONV_OK || res == __GCONV_EMPTY_INPUT)
    915 		break;
    916 
    917 	      if (res != __GCONV_FULL_OUTPUT)
    918 		{
    919 		  __libc_lock_unlock (lock);
    920 		  goto converted;
    921 		}
    922 
    923 	      inbuf = result;
    924 # else
    925 #  if HAVE_ICONV
    926 	      const char *inptr = (const char *) inbuf;
    927 	      size_t inleft = resultlen;
    928 	      char *outptr = (char *) outbuf;
    929 	      size_t outleft;
    930 
    931 	      if (freemem_size < sizeof (size_t))
    932 		goto resize_freemem;
    933 
    934 	      outleft = freemem_size - sizeof (size_t);
    935 	      if (iconv (domain->conv,
    936 			 (ICONV_CONST char **) &inptr, &inleft,
    937 			 &outptr, &outleft)
    938 		  != (size_t) (-1))
    939 		{
    940 		  outbuf = (unsigned char *) outptr;
    941 		  break;
    942 		}
    943 	      if (errno != E2BIG)
    944 		{
    945 		  __libc_lock_unlock (lock);
    946 		  goto converted;
    947 		}
    948 #  endif
    949 # endif
    950 
    951 	    resize_freemem:
    952 	      /* We must allocate a new buffer or resize the old one.  */
    953 	      if (malloc_count > 0)
    954 		{
    955 		  ++malloc_count;
    956 		  freemem_size = malloc_count * INITIAL_BLOCK_SIZE;
    957 		  newmem = (transmem_block_t *) realloc (transmem_list,
    958 							 freemem_size);
    959 # ifdef _LIBC
    960 		  if (newmem != NULL)
    961 		    transmem_list = transmem_list->next;
    962 		  else
    963 		    {
    964 		      struct transmem_list *old = transmem_list;
    965 
    966 		      transmem_list = transmem_list->next;
    967 		      free (old);
    968 		    }
    969 # endif
    970 		}
    971 	      else
    972 		{
    973 		  malloc_count = 1;
    974 		  freemem_size = INITIAL_BLOCK_SIZE;
    975 		  newmem = (transmem_block_t *) malloc (freemem_size);
    976 		}
    977 	      if (__builtin_expect (newmem == NULL, 0))
    978 		{
    979 		  freemem = NULL;
    980 		  freemem_size = 0;
    981 		  __libc_lock_unlock (lock);
    982 		  goto converted;
    983 		}
    984 
    985 # ifdef _LIBC
    986 	      /* Add the block to the list of blocks we have to free
    987                  at some point.  */
    988 	      newmem->next = transmem_list;
    989 	      transmem_list = newmem;
    990 
    991 	      freemem = newmem->data;
    992 	      freemem_size -= offsetof (struct transmem_list, data);
    993 # else
    994 	      transmem_list = newmem;
    995 	      freemem = newmem;
    996 # endif
    997 
    998 	      outbuf = freemem + sizeof (size_t);
    999 	    }
   1000 
   1001 	  /* We have now in our buffer a converted string.  Put this
   1002 	     into the table of conversions.  */
   1003 	  *(size_t *) freemem = outbuf - freemem - sizeof (size_t);
   1004 	  domain->conv_tab[act] = (char *) freemem;
   1005 	  /* Shrink freemem, but keep it aligned.  */
   1006 	  freemem_size -= outbuf - freemem;
   1007 	  freemem = outbuf;
   1008 	  freemem += freemem_size & (alignof (size_t) - 1);
   1009 	  freemem_size = freemem_size & ~ (alignof (size_t) - 1);
   1010 
   1011 	  __libc_lock_unlock (lock);
   1012 	}
   1013 
   1014       /* Now domain->conv_tab[act] contains the translation of all
   1015 	 the plural variants.  */
   1016       result = domain->conv_tab[act] + sizeof (size_t);
   1017       resultlen = *(size_t *) domain->conv_tab[act];
   1018     }
   1019 
   1020  converted:
   1021   /* The result string is converted.  */
   1022 
   1023 #endif /* _LIBC || HAVE_ICONV */
   1024 
   1025   *lengthp = resultlen;
   1026   return result;
   1027 }
   1028 
   1029 
   1030 /* Look up a plural variant.  */
   1031 static char *
   1032 internal_function
   1033 plural_lookup (struct loaded_l10nfile *domain, unsigned long int n,
   1034 	       const char *translation, size_t translation_len)
   1035 {
   1036   struct loaded_domain *domaindata = (struct loaded_domain *) domain->data;
   1037   unsigned long int index;
   1038   const char *p;
   1039 
   1040   index = plural_eval (domaindata->plural, n);
   1041   if (index >= domaindata->nplurals)
   1042     /* This should never happen.  It means the plural expression and the
   1043        given maximum value do not match.  */
   1044     index = 0;
   1045 
   1046   /* Skip INDEX strings at TRANSLATION.  */
   1047   p = translation;
   1048   while (index-- > 0)
   1049     {
   1050 #ifdef _LIBC
   1051       p = __rawmemchr (p, '\0');
   1052 #else
   1053       p = strchr (p, '\0');
   1054 #endif
   1055       /* And skip over the NUL byte.  */
   1056       p++;
   1057 
   1058       if (p >= translation + translation_len)
   1059 	/* This should never happen.  It means the plural expression
   1060 	   evaluated to a value larger than the number of variants
   1061 	   available for MSGID1.  */
   1062 	return (char *) translation;
   1063     }
   1064   return (char *) p;
   1065 }
   1066 
   1067 #ifndef _LIBC
   1068 /* Return string representation of locale CATEGORY.  */
   1069 static const char *
   1070 internal_function
   1071 category_to_name (int category)
   1072 {
   1073   const char *retval;
   1074 
   1075   switch (category)
   1076   {
   1077 #ifdef LC_COLLATE
   1078   case LC_COLLATE:
   1079     retval = "LC_COLLATE";
   1080     break;
   1081 #endif
   1082 #ifdef LC_CTYPE
   1083   case LC_CTYPE:
   1084     retval = "LC_CTYPE";
   1085     break;
   1086 #endif
   1087 #ifdef LC_MONETARY
   1088   case LC_MONETARY:
   1089     retval = "LC_MONETARY";
   1090     break;
   1091 #endif
   1092 #ifdef LC_NUMERIC
   1093   case LC_NUMERIC:
   1094     retval = "LC_NUMERIC";
   1095     break;
   1096 #endif
   1097 #ifdef LC_TIME
   1098   case LC_TIME:
   1099     retval = "LC_TIME";
   1100     break;
   1101 #endif
   1102 #ifdef LC_MESSAGES
   1103   case LC_MESSAGES:
   1104     retval = "LC_MESSAGES";
   1105     break;
   1106 #endif
   1107 #ifdef LC_RESPONSE
   1108   case LC_RESPONSE:
   1109     retval = "LC_RESPONSE";
   1110     break;
   1111 #endif
   1112 #ifdef LC_ALL
   1113   case LC_ALL:
   1114     /* This might not make sense but is perhaps better than any other
   1115        value.  */
   1116     retval = "LC_ALL";
   1117     break;
   1118 #endif
   1119   default:
   1120     /* If you have a better idea for a default value let me know.  */
   1121     retval = "LC_XXX";
   1122   }
   1123 
   1124   return retval;
   1125 }
   1126 #endif
   1127 
   1128 /* Guess value of current locale from value of the environment variables.  */
   1129 static const char *
   1130 internal_function
   1131 guess_category_value (int category, const char *categoryname)
   1132 {
   1133   const char *language;
   1134   const char *retval;
   1135 
   1136   /* The highest priority value is the `LANGUAGE' environment
   1137      variable.  But we don't use the value if the currently selected
   1138      locale is the C locale.  This is a GNU extension.  */
   1139   language = getenv ("LANGUAGE");
   1140   if (language != NULL && language[0] == '\0')
   1141     language = NULL;
   1142 
   1143   /* We have to proceed with the POSIX methods of looking to `LC_ALL',
   1144      `LC_xxx', and `LANG'.  On some systems this can be done by the
   1145      `setlocale' function itself.  */
   1146 #ifdef _LIBC
   1147   retval = __current_locale_name (category);
   1148 #else
   1149   retval = _nl_locale_name (category, categoryname);
   1150 #endif
   1151 
   1152   /* Ignore LANGUAGE if the locale is set to "C" because
   1153      1. "C" locale usually uses the ASCII encoding, and most international
   1154 	messages use non-ASCII characters. These characters get displayed
   1155 	as question marks (if using glibc's iconv()) or as invalid 8-bit
   1156 	characters (because other iconv()s refuse to convert most non-ASCII
   1157 	characters to ASCII). In any case, the output is ugly.
   1158      2. The precise output of some programs in the "C" locale is specified
   1159 	by POSIX and should not depend on environment variables like
   1160 	"LANGUAGE".  We allow such programs to use gettext().  */
   1161   return language != NULL && strcmp (retval, "C") != 0 ? language : retval;
   1162 }
   1163 
   1164 /* @@ begin of epilog @@ */
   1165 
   1166 /* We don't want libintl.a to depend on any other library.  So we
   1167    avoid the non-standard function stpcpy.  In GNU C Library this
   1168    function is available, though.  Also allow the symbol HAVE_STPCPY
   1169    to be defined.  */
   1170 #if !_LIBC && !HAVE_STPCPY
   1171 static char *
   1172 stpcpy (char *dest, const char *src)
   1173 {
   1174   while ((*dest++ = *src++) != '\0')
   1175     /* Do nothing. */ ;
   1176   return dest - 1;
   1177 }
   1178 #endif
   1179 
   1180 #if !_LIBC && !HAVE_MEMPCPY
   1181 static void *
   1182 mempcpy (void *dest, const void *src, size_t n)
   1183 {
   1184   return (void *) ((char *) memcpy (dest, src, n) + n);
   1185 }
   1186 #endif
   1187 
   1188 
   1189 #ifdef _LIBC
   1190 /* If we want to free all resources we have to do some work at
   1191    program's end.  */
   1192 libc_freeres_fn (free_mem)
   1193 {
   1194   void *old;
   1195 
   1196   while (_nl_domain_bindings != NULL)
   1197     {
   1198       struct binding *oldp = _nl_domain_bindings;
   1199       _nl_domain_bindings = _nl_domain_bindings->next;
   1200       if (oldp->dirname != INTUSE(_nl_default_dirname))
   1201 	/* Yes, this is a pointer comparison.  */
   1202 	free (oldp->dirname);
   1203       free (oldp->codeset);
   1204       free (oldp);
   1205     }
   1206 
   1207   if (_nl_current_default_domain != _nl_default_default_domain)
   1208     /* Yes, again a pointer comparison.  */
   1209     free ((char *) _nl_current_default_domain);
   1210 
   1211   /* Remove the search tree with the known translations.  */
   1212   __tdestroy (root, free);
   1213   root = NULL;
   1214 
   1215   while (transmem_list != NULL)
   1216     {
   1217       old = transmem_list;
   1218       transmem_list = transmem_list->next;
   1219       free (old);
   1220     }
   1221 }
   1222 #endif
   1223