Back to index

courier  0.68.2
dcigettext.c
Go to the documentation of this file.
00001 /* Implementation of the internal dcigettext function.
00002    Copyright (C) 1995-1999, 2000-2009 Free Software Foundation, Inc.
00003 
00004    This program is free software; you can redistribute it and/or modify it
00005    under the terms of the GNU Library General Public License as published
00006    by the Free Software Foundation; either version 2, or (at your option)
00007    any later version.
00008 
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public
00015    License along with this program; if not, write to the Free Software
00016    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
00017    USA.  */
00018 
00019 /* Tell glibc's <string.h> to provide a prototype for mempcpy().
00020    This must come before <config.h> because <config.h> may include
00021    <features.h>, and once <features.h> has been included, it's too late.  */
00022 #ifndef _GNU_SOURCE
00023 # define _GNU_SOURCE 1
00024 #endif
00025 
00026 #ifdef HAVE_CONFIG_H
00027 # include <config.h>
00028 #endif
00029 
00030 #include <sys/types.h>
00031 
00032 #ifdef __GNUC__
00033 # define alloca __builtin_alloca
00034 # define HAVE_ALLOCA 1
00035 #else
00036 # ifdef _MSC_VER
00037 #  include <malloc.h>
00038 #  define alloca _alloca
00039 # else
00040 #  if defined HAVE_ALLOCA_H || defined _LIBC
00041 #   include <alloca.h>
00042 #  else
00043 #   ifdef _AIX
00044  #pragma alloca
00045 #   else
00046 #    ifndef alloca
00047 char *alloca ();
00048 #    endif
00049 #   endif
00050 #  endif
00051 # endif
00052 #endif
00053 
00054 #include <errno.h>
00055 #ifndef errno
00056 extern int errno;
00057 #endif
00058 #ifndef __set_errno
00059 # define __set_errno(val) errno = (val)
00060 #endif
00061 
00062 #include <stddef.h>
00063 #include <stdlib.h>
00064 #include <string.h>
00065 
00066 #if defined HAVE_UNISTD_H || defined _LIBC
00067 # include <unistd.h>
00068 #endif
00069 
00070 #include <locale.h>
00071 
00072 #ifdef _LIBC
00073   /* Guess whether integer division by zero raises signal SIGFPE.
00074      Set to 1 only if you know for sure.  In case of doubt, set to 0.  */
00075 # if defined __alpha__ || defined __arm__ || defined __i386__ \
00076      || defined __m68k__ || defined __s390__
00077 #  define INTDIV0_RAISES_SIGFPE 1
00078 # else
00079 #  define INTDIV0_RAISES_SIGFPE 0
00080 # endif
00081 #endif
00082 #if !INTDIV0_RAISES_SIGFPE
00083 # include <signal.h>
00084 #endif
00085 
00086 #if defined HAVE_SYS_PARAM_H || defined _LIBC
00087 # include <sys/param.h>
00088 #endif
00089 
00090 #if !defined _LIBC
00091 # include "localcharset.h"
00092 #endif
00093 
00094 #include "gettextP.h"
00095 #include "plural-exp.h"
00096 #ifdef _LIBC
00097 # include <libintl.h>
00098 #else
00099 # ifdef IN_LIBGLOCALE
00100 #  include <libintl.h>
00101 # endif
00102 # include "libgnuintl.h"
00103 #endif
00104 #include "hash-string.h"
00105 
00106 /* Handle multi-threaded applications.  */
00107 #ifdef _LIBC
00108 # include <bits/libc-lock.h>
00109 # define gl_rwlock_define_initialized __libc_rwlock_define_initialized
00110 # define gl_rwlock_rdlock __libc_rwlock_rdlock
00111 # define gl_rwlock_wrlock __libc_rwlock_wrlock
00112 # define gl_rwlock_unlock __libc_rwlock_unlock
00113 #else
00114 # include "lock.h"
00115 #endif
00116 
00117 /* Alignment of types.  */
00118 #if defined __GNUC__ && __GNUC__ >= 2
00119 # define alignof(TYPE) __alignof__ (TYPE)
00120 #else
00121 # define alignof(TYPE) \
00122     ((int) &((struct { char dummy1; TYPE dummy2; } *) 0)->dummy2)
00123 #endif
00124 
00125 /* Some compilers, like SunOS4 cc, don't have offsetof in <stddef.h>.  */
00126 #ifndef offsetof
00127 # define offsetof(type,ident) ((size_t)&(((type*)0)->ident))
00128 #endif
00129 
00130 /* @@ end of prolog @@ */
00131 
00132 #ifdef _LIBC
00133 /* Rename the non ANSI C functions.  This is required by the standard
00134    because some ANSI C functions will require linking with this object
00135    file and the name space must not be polluted.  */
00136 # define getcwd __getcwd
00137 # ifndef stpcpy
00138 #  define stpcpy __stpcpy
00139 # endif
00140 # define tfind __tfind
00141 #else
00142 # if !defined HAVE_GETCWD
00143 char *getwd ();
00144 #  define getcwd(buf, max) getwd (buf)
00145 # else
00146 #  if VMS
00147 #   define getcwd(buf, max) (getcwd) (buf, max, 0)
00148 #  else
00149 char *getcwd ();
00150 #  endif
00151 # endif
00152 # ifndef HAVE_STPCPY
00153 static char *stpcpy (char *dest, const char *src);
00154 # endif
00155 # ifndef HAVE_MEMPCPY
00156 static void *mempcpy (void *dest, const void *src, size_t n);
00157 # endif
00158 #endif
00159 
00160 /* Use a replacement if the system does not provide the `tsearch' function
00161    family.  */
00162 #if HAVE_TSEARCH || defined _LIBC
00163 # include <search.h>
00164 #else
00165 # define tsearch libintl_tsearch
00166 # define tfind libintl_tfind
00167 # define tdelete libintl_tdelete
00168 # define twalk libintl_twalk
00169 # include "tsearch.h"
00170 #endif
00171 
00172 #ifdef _LIBC
00173 # define tsearch __tsearch
00174 #endif
00175 
00176 /* Amount to increase buffer size by in each try.  */
00177 #define PATH_INCR 32
00178 
00179 /* The following is from pathmax.h.  */
00180 /* Non-POSIX BSD systems might have gcc's limits.h, which doesn't define
00181    PATH_MAX but might cause redefinition warnings when sys/param.h is
00182    later included (as on MORE/BSD 4.3).  */
00183 #if defined _POSIX_VERSION || (defined HAVE_LIMITS_H && !defined __GNUC__)
00184 # include <limits.h>
00185 #endif
00186 
00187 #ifndef _POSIX_PATH_MAX
00188 # define _POSIX_PATH_MAX 255
00189 #endif
00190 
00191 #if !defined PATH_MAX && defined _PC_PATH_MAX
00192 # define PATH_MAX (pathconf ("/", _PC_PATH_MAX) < 1 ? 1024 : pathconf ("/", _PC_PATH_MAX))
00193 #endif
00194 
00195 /* Don't include sys/param.h if it already has been.  */
00196 #if defined HAVE_SYS_PARAM_H && !defined PATH_MAX && !defined MAXPATHLEN
00197 # include <sys/param.h>
00198 #endif
00199 
00200 #if !defined PATH_MAX && defined MAXPATHLEN
00201 # define PATH_MAX MAXPATHLEN
00202 #endif
00203 
00204 #ifndef PATH_MAX
00205 # define PATH_MAX _POSIX_PATH_MAX
00206 #endif
00207 
00208 /* Pathname support.
00209    ISSLASH(C)           tests whether C is a directory separator character.
00210    IS_ABSOLUTE_PATH(P)  tests whether P is an absolute path.  If it is not,
00211                         it may be concatenated to a directory pathname.
00212    IS_PATH_WITH_DIR(P)  tests whether P contains a directory specification.
00213  */
00214 #if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__
00215   /* Win32, Cygwin, OS/2, DOS */
00216 # define ISSLASH(C) ((C) == '/' || (C) == '\\')
00217 # define HAS_DEVICE(P) \
00218     ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
00219      && (P)[1] == ':')
00220 # define IS_ABSOLUTE_PATH(P) (ISSLASH ((P)[0]) || HAS_DEVICE (P))
00221 # define IS_PATH_WITH_DIR(P) \
00222     (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P))
00223 #else
00224   /* Unix */
00225 # define ISSLASH(C) ((C) == '/')
00226 # define IS_ABSOLUTE_PATH(P) ISSLASH ((P)[0])
00227 # define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL)
00228 #endif
00229 
00230 /* Whether to support different locales in different threads.  */
00231 #if defined _LIBC || HAVE_USELOCALE || defined IN_LIBGLOCALE
00232 # define HAVE_PER_THREAD_LOCALE
00233 #endif
00234 
00235 /* This is the type used for the search tree where known translations
00236    are stored.  */
00237 struct known_translation_t
00238 {
00239   /* Domain in which to search.  */
00240   const char *domainname;
00241 
00242   /* The category.  */
00243   int category;
00244 
00245 #ifdef HAVE_PER_THREAD_LOCALE
00246   /* Name of the relevant locale category, or "" for the global locale.  */
00247   const char *localename;
00248 #endif
00249 
00250 #ifdef IN_LIBGLOCALE
00251   /* The character encoding.  */
00252   const char *encoding;
00253 #endif
00254 
00255   /* State of the catalog counter at the point the string was found.  */
00256   int counter;
00257 
00258   /* Catalog where the string was found.  */
00259   struct loaded_l10nfile *domain;
00260 
00261   /* And finally the translation.  */
00262   const char *translation;
00263   size_t translation_length;
00264 
00265   /* Pointer to the string in question.  */
00266   union
00267     {
00268       char appended[ZERO];  /* used if domain != NULL */
00269       const char *ptr;      /* used if domain == NULL */
00270     }
00271   msgid;
00272 };
00273 
00274 gl_rwlock_define_initialized (static, tree_lock)
00275 
00276 /* Root of the search tree with known translations.  */
00277 static void *root;
00278 
00279 /* Function to compare two entries in the table of known translations.  */
00280 static int
00281 transcmp (const void *p1, const void *p2)
00282 {
00283   const struct known_translation_t *s1;
00284   const struct known_translation_t *s2;
00285   int result;
00286 
00287   s1 = (const struct known_translation_t *) p1;
00288   s2 = (const struct known_translation_t *) p2;
00289 
00290   result = strcmp (s1->domain != NULL ? s1->msgid.appended : s1->msgid.ptr,
00291                  s2->domain != NULL ? s2->msgid.appended : s2->msgid.ptr);
00292   if (result == 0)
00293     {
00294       result = strcmp (s1->domainname, s2->domainname);
00295       if (result == 0)
00296        {
00297 #ifdef HAVE_PER_THREAD_LOCALE
00298          result = strcmp (s1->localename, s2->localename);
00299          if (result == 0)
00300 #endif
00301            {
00302 #ifdef IN_LIBGLOCALE
00303              result = strcmp (s1->encoding, s2->encoding);
00304              if (result == 0)
00305 #endif
00306               /* We compare the category last (though this is the cheapest
00307                  operation) since it is hopefully always the same (namely
00308                  LC_MESSAGES).  */
00309               result = s1->category - s2->category;
00310            }
00311        }
00312     }
00313 
00314   return result;
00315 }
00316 
00317 /* Name of the default domain used for gettext(3) prior any call to
00318    textdomain(3).  The default value for this is "messages".  */
00319 const char _nl_default_default_domain[] attribute_hidden = "messages";
00320 
00321 #ifndef IN_LIBGLOCALE
00322 /* Value used as the default domain for gettext(3).  */
00323 const char *_nl_current_default_domain attribute_hidden
00324      = _nl_default_default_domain;
00325 #endif
00326 
00327 /* Contains the default location of the message catalogs.  */
00328 #if defined __EMX__
00329 extern const char _nl_default_dirname[];
00330 #else
00331 # ifdef _LIBC
00332 extern const char _nl_default_dirname[];
00333 libc_hidden_proto (_nl_default_dirname)
00334 # endif
00335 const char _nl_default_dirname[] = LOCALEDIR;
00336 # ifdef _LIBC
00337 libc_hidden_data_def (_nl_default_dirname)
00338 # endif
00339 #endif
00340 
00341 #ifndef IN_LIBGLOCALE
00342 /* List with bindings of specific domains created by bindtextdomain()
00343    calls.  */
00344 struct binding *_nl_domain_bindings;
00345 #endif
00346 
00347 /* Prototypes for local functions.  */
00348 static char *plural_lookup (struct loaded_l10nfile *domain,
00349                          unsigned long int n,
00350                          const char *translation, size_t translation_len)
00351      internal_function;
00352 
00353 #ifdef IN_LIBGLOCALE
00354 static const char *guess_category_value (int category,
00355                                     const char *categoryname,
00356                                     const char *localename)
00357      internal_function;
00358 #else
00359 static const char *guess_category_value (int category,
00360                                     const char *categoryname)
00361      internal_function;
00362 #endif
00363 
00364 #ifdef _LIBC
00365 # include "../locale/localeinfo.h"
00366 # define category_to_name(category) \
00367   _nl_category_names.str + _nl_category_name_idxs[category]
00368 #else
00369 static const char *category_to_name (int category) internal_function;
00370 #endif
00371 #if (defined _LIBC || HAVE_ICONV) && !defined IN_LIBGLOCALE
00372 static const char *get_output_charset (struct binding *domainbinding)
00373      internal_function;
00374 #endif
00375 
00376 
00377 /* For those loosing systems which don't have `alloca' we have to add
00378    some additional code emulating it.  */
00379 #ifdef HAVE_ALLOCA
00380 /* Nothing has to be done.  */
00381 # define freea(p) /* nothing */
00382 # define ADD_BLOCK(list, address) /* nothing */
00383 # define FREE_BLOCKS(list) /* nothing */
00384 #else
00385 struct block_list
00386 {
00387   void *address;
00388   struct block_list *next;
00389 };
00390 # define ADD_BLOCK(list, addr)                                              \
00391   do {                                                               \
00392     struct block_list *newp = (struct block_list *) malloc (sizeof (*newp));  \
00393     /* If we cannot get a free block we cannot add the new element to       \
00394        the list.  */                                                 \
00395     if (newp != NULL) {                                                     \
00396       newp->address = (addr);                                               \
00397       newp->next = (list);                                           \
00398       (list) = newp;                                                 \
00399     }                                                                \
00400   } while (0)
00401 # define FREE_BLOCKS(list)                                           \
00402   do {                                                               \
00403     while (list != NULL) {                                           \
00404       struct block_list *old = list;                                        \
00405       list = list->next;                                             \
00406       free (old->address);                                           \
00407       free (old);                                                    \
00408     }                                                                \
00409   } while (0)
00410 # undef alloca
00411 # define alloca(size) (malloc (size))
00412 # define freea(p) free (p)
00413 #endif /* have alloca */
00414 
00415 
00416 #ifdef _LIBC
00417 /* List of blocks allocated for translations.  */
00418 typedef struct transmem_list
00419 {
00420   struct transmem_list *next;
00421   char data[ZERO];
00422 } transmem_block_t;
00423 static struct transmem_list *transmem_list;
00424 #else
00425 typedef unsigned char transmem_block_t;
00426 #endif
00427 
00428 
00429 /* Names for the libintl functions are a problem.  They must not clash
00430    with existing names and they should follow ANSI C.  But this source
00431    code is also used in GNU C Library where the names have a __
00432    prefix.  So we have to make a difference here.  */
00433 #ifdef _LIBC
00434 # define DCIGETTEXT __dcigettext
00435 #else
00436 # define DCIGETTEXT libintl_dcigettext
00437 #endif
00438 
00439 /* Lock variable to protect the global data in the gettext implementation.  */
00440 gl_rwlock_define_initialized (, _nl_state_lock attribute_hidden)
00441 
00442 /* Checking whether the binaries runs SUID must be done and glibc provides
00443    easier methods therefore we make a difference here.  */
00444 #ifdef _LIBC
00445 # define ENABLE_SECURE __libc_enable_secure
00446 # define DETERMINE_SECURE
00447 #else
00448 # ifndef HAVE_GETUID
00449 #  define getuid() 0
00450 # endif
00451 # ifndef HAVE_GETGID
00452 #  define getgid() 0
00453 # endif
00454 # ifndef HAVE_GETEUID
00455 #  define geteuid() getuid()
00456 # endif
00457 # ifndef HAVE_GETEGID
00458 #  define getegid() getgid()
00459 # endif
00460 static int enable_secure;
00461 # define ENABLE_SECURE (enable_secure == 1)
00462 # define DETERMINE_SECURE \
00463   if (enable_secure == 0)                                            \
00464     {                                                                \
00465       if (getuid () != geteuid () || getgid () != getegid ())               \
00466        enable_secure = 1;                                            \
00467       else                                                           \
00468        enable_secure = -1;                                           \
00469     }
00470 #endif
00471 
00472 /* Get the function to evaluate the plural expression.  */
00473 #include "eval-plural.h"
00474 
00475 /* Look up MSGID in the DOMAINNAME message catalog for the current
00476    CATEGORY locale and, if PLURAL is nonzero, search over string
00477    depending on the plural form determined by N.  */
00478 #ifdef IN_LIBGLOCALE
00479 char *
00480 gl_dcigettext (const char *domainname,
00481               const char *msgid1, const char *msgid2,
00482               int plural, unsigned long int n,
00483               int category,
00484               const char *localename, const char *encoding)
00485 #else
00486 char *
00487 DCIGETTEXT (const char *domainname, const char *msgid1, const char *msgid2,
00488            int plural, unsigned long int n, int category)
00489 #endif
00490 {
00491 #ifndef HAVE_ALLOCA
00492   struct block_list *block_list = NULL;
00493 #endif
00494   struct loaded_l10nfile *domain;
00495   struct binding *binding;
00496   const char *categoryname;
00497   const char *categoryvalue;
00498   const char *dirname;
00499   char *xdomainname;
00500   char *single_locale;
00501   char *retval;
00502   size_t retlen;
00503   int saved_errno;
00504   struct known_translation_t search;
00505   struct known_translation_t **foundp = NULL;
00506 #if defined HAVE_PER_THREAD_LOCALE && !defined IN_LIBGLOCALE
00507   const char *localename;
00508 #endif
00509   size_t domainname_len;
00510 
00511   /* If no real MSGID is given return NULL.  */
00512   if (msgid1 == NULL)
00513     return NULL;
00514 
00515 #ifdef _LIBC
00516   if (category < 0 || category >= __LC_LAST || category == LC_ALL)
00517     /* Bogus.  */
00518     return (plural == 0
00519            ? (char *) msgid1
00520            /* Use the Germanic plural rule.  */
00521            : n == 1 ? (char *) msgid1 : (char *) msgid2);
00522 #endif
00523 
00524   /* Preserve the `errno' value.  */
00525   saved_errno = errno;
00526 
00527 #ifdef _LIBC
00528   __libc_rwlock_define (extern, __libc_setlocale_lock attribute_hidden)
00529   __libc_rwlock_rdlock (__libc_setlocale_lock);
00530 #endif
00531 
00532   gl_rwlock_rdlock (_nl_state_lock);
00533 
00534   /* If DOMAINNAME is NULL, we are interested in the default domain.  If
00535      CATEGORY is not LC_MESSAGES this might not make much sense but the
00536      definition left this undefined.  */
00537   if (domainname == NULL)
00538     domainname = _nl_current_default_domain;
00539 
00540   /* OS/2 specific: backward compatibility with older libintl versions  */
00541 #ifdef LC_MESSAGES_COMPAT
00542   if (category == LC_MESSAGES_COMPAT)
00543     category = LC_MESSAGES;
00544 #endif
00545 
00546   /* Try to find the translation among those which we found at
00547      some time.  */
00548   search.domain = NULL;
00549   search.msgid.ptr = msgid1;
00550   search.domainname = domainname;
00551   search.category = category;
00552 #ifdef HAVE_PER_THREAD_LOCALE
00553 # ifndef IN_LIBGLOCALE
00554 #  ifdef _LIBC
00555   localename = _strdupa (_current_locale_name (category));
00556 #  else
00557   categoryname = category_to_name (category);
00558 #   define CATEGORYNAME_INITIALIZED
00559   localename = _nl_locale_name_thread_unsafe (category, categoryname);
00560   if (localename == NULL)
00561     localename = "";
00562 #  endif
00563 # endif
00564   search.localename = localename;
00565 # ifdef IN_LIBGLOCALE
00566   search.encoding = encoding;
00567 # endif
00568 
00569   /* Since tfind/tsearch manage a balanced tree, concurrent tfind and
00570      tsearch calls can be fatal.  */
00571   gl_rwlock_rdlock (tree_lock);
00572 
00573   foundp = (struct known_translation_t **) tfind (&search, &root, transcmp);
00574 
00575   gl_rwlock_unlock (tree_lock);
00576 
00577   if (foundp != NULL && (*foundp)->counter == _nl_msg_cat_cntr)
00578     {
00579       /* Now deal with plural.  */
00580       if (plural)
00581        retval = plural_lookup ((*foundp)->domain, n, (*foundp)->translation,
00582                             (*foundp)->translation_length);
00583       else
00584        retval = (char *) (*foundp)->translation;
00585 
00586       gl_rwlock_unlock (_nl_state_lock);
00587 # ifdef _LIBC
00588       __libc_rwlock_unlock (__libc_setlocale_lock);
00589 # endif
00590       __set_errno (saved_errno);
00591       return retval;
00592     }
00593 #endif
00594 
00595   /* See whether this is a SUID binary or not.  */
00596   DETERMINE_SECURE;
00597 
00598   /* First find matching binding.  */
00599 #ifdef IN_LIBGLOCALE
00600   /* We can use a trivial binding, since _nl_find_msg will ignore it anyway,
00601      and _nl_load_domain and _nl_find_domain just pass it through.  */
00602   binding = NULL;
00603   dirname = bindtextdomain (domainname, NULL);
00604 #else
00605   for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next)
00606     {
00607       int compare = strcmp (domainname, binding->domainname);
00608       if (compare == 0)
00609        /* We found it!  */
00610        break;
00611       if (compare < 0)
00612        {
00613          /* It is not in the list.  */
00614          binding = NULL;
00615          break;
00616        }
00617     }
00618 
00619   if (binding == NULL)
00620     dirname = _nl_default_dirname;
00621   else
00622     {
00623       dirname = binding->dirname;
00624 #endif
00625       if (!IS_ABSOLUTE_PATH (dirname))
00626        {
00627          /* We have a relative path.  Make it absolute now.  */
00628          size_t dirname_len = strlen (dirname) + 1;
00629          size_t path_max;
00630          char *resolved_dirname;
00631          char *ret;
00632 
00633          path_max = (unsigned int) PATH_MAX;
00634          path_max += 2;            /* The getcwd docs say to do this.  */
00635 
00636          for (;;)
00637            {
00638              resolved_dirname = (char *) alloca (path_max + dirname_len);
00639              ADD_BLOCK (block_list, tmp_dirname);
00640 
00641              __set_errno (0);
00642              ret = getcwd (resolved_dirname, path_max);
00643              if (ret != NULL || errno != ERANGE)
00644               break;
00645 
00646              path_max += path_max / 2;
00647              path_max += PATH_INCR;
00648            }
00649 
00650          if (ret == NULL)
00651            /* We cannot get the current working directory.  Don't signal an
00652               error but simply return the default string.  */
00653            goto return_untranslated;
00654 
00655          stpcpy (stpcpy (strchr (resolved_dirname, '\0'), "/"), dirname);
00656          dirname = resolved_dirname;
00657        }
00658 #ifndef IN_LIBGLOCALE
00659     }
00660 #endif
00661 
00662   /* Now determine the symbolic name of CATEGORY and its value.  */
00663 #ifndef CATEGORYNAME_INITIALIZED
00664   categoryname = category_to_name (category);
00665 #endif
00666 #ifdef IN_LIBGLOCALE
00667   categoryvalue = guess_category_value (category, categoryname, localename);
00668 #else
00669   categoryvalue = guess_category_value (category, categoryname);
00670 #endif
00671 
00672   domainname_len = strlen (domainname);
00673   xdomainname = (char *) alloca (strlen (categoryname)
00674                              + domainname_len + 5);
00675   ADD_BLOCK (block_list, xdomainname);
00676 
00677   stpcpy ((char *) mempcpy (stpcpy (stpcpy (xdomainname, categoryname), "/"),
00678                          domainname, domainname_len),
00679          ".mo");
00680 
00681   /* Creating working area.  */
00682   single_locale = (char *) alloca (strlen (categoryvalue) + 1);
00683   ADD_BLOCK (block_list, single_locale);
00684 
00685 
00686   /* Search for the given string.  This is a loop because we perhaps
00687      got an ordered list of languages to consider for the translation.  */
00688   while (1)
00689     {
00690       /* Make CATEGORYVALUE point to the next element of the list.  */
00691       while (categoryvalue[0] != '\0' && categoryvalue[0] == ':')
00692        ++categoryvalue;
00693       if (categoryvalue[0] == '\0')
00694        {
00695          /* The whole contents of CATEGORYVALUE has been searched but
00696             no valid entry has been found.  We solve this situation
00697             by implicitly appending a "C" entry, i.e. no translation
00698             will take place.  */
00699          single_locale[0] = 'C';
00700          single_locale[1] = '\0';
00701        }
00702       else
00703        {
00704          char *cp = single_locale;
00705          while (categoryvalue[0] != '\0' && categoryvalue[0] != ':')
00706            *cp++ = *categoryvalue++;
00707          *cp = '\0';
00708 
00709          /* When this is a SUID binary we must not allow accessing files
00710             outside the dedicated directories.  */
00711          if (ENABLE_SECURE && IS_PATH_WITH_DIR (single_locale))
00712            /* Ingore this entry.  */
00713            continue;
00714        }
00715 
00716       /* If the current locale value is C (or POSIX) we don't load a
00717         domain.  Return the MSGID.  */
00718       if (strcmp (single_locale, "C") == 0
00719          || strcmp (single_locale, "POSIX") == 0)
00720        break;
00721 
00722       /* Find structure describing the message catalog matching the
00723         DOMAINNAME and CATEGORY.  */
00724       domain = _nl_find_domain (dirname, single_locale, xdomainname, binding);
00725 
00726       if (domain != NULL)
00727        {
00728 #if defined IN_LIBGLOCALE
00729          retval = _nl_find_msg (domain, binding, encoding, msgid1, &retlen);
00730 #else
00731          retval = _nl_find_msg (domain, binding, msgid1, 1, &retlen);
00732 #endif
00733 
00734          if (retval == NULL)
00735            {
00736              int cnt;
00737 
00738              for (cnt = 0; domain->successor[cnt] != NULL; ++cnt)
00739               {
00740 #if defined IN_LIBGLOCALE
00741                 retval = _nl_find_msg (domain->successor[cnt], binding,
00742                                     encoding, msgid1, &retlen);
00743 #else
00744                 retval = _nl_find_msg (domain->successor[cnt], binding,
00745                                     msgid1, 1, &retlen);
00746 #endif
00747 
00748                 if (retval != NULL)
00749                   {
00750                     domain = domain->successor[cnt];
00751                     break;
00752                   }
00753               }
00754            }
00755 
00756          /* Returning -1 means that some resource problem exists
00757             (likely memory) and that the strings could not be
00758             converted.  Return the original strings.  */
00759          if (__builtin_expect (retval == (char *) -1, 0))
00760            break;
00761 
00762          if (retval != NULL)
00763            {
00764              /* Found the translation of MSGID1 in domain DOMAIN:
00765                starting at RETVAL, RETLEN bytes.  */
00766              FREE_BLOCKS (block_list);
00767              if (foundp == NULL)
00768               {
00769                 /* Create a new entry and add it to the search tree.  */
00770                 size_t msgid_len;
00771                 size_t size;
00772                 struct known_translation_t *newp;
00773 
00774                 msgid_len = strlen (msgid1) + 1;
00775                 size = offsetof (struct known_translation_t, msgid)
00776                       + msgid_len + domainname_len + 1;
00777 #ifdef HAVE_PER_THREAD_LOCALE
00778                 size += strlen (localename) + 1;
00779 #endif
00780                 newp = (struct known_translation_t *) malloc (size);
00781                 if (newp != NULL)
00782                   {
00783                     char *new_domainname;
00784 #ifdef HAVE_PER_THREAD_LOCALE
00785                     char *new_localename;
00786 #endif
00787 
00788                     new_domainname =
00789                      (char *) mempcpy (newp->msgid.appended, msgid1,
00790                                      msgid_len);
00791                     memcpy (new_domainname, domainname, domainname_len + 1);
00792 #ifdef HAVE_PER_THREAD_LOCALE
00793                     new_localename = new_domainname + domainname_len + 1;
00794                     strcpy (new_localename, localename);
00795 #endif
00796                     newp->domainname = new_domainname;
00797                     newp->category = category;
00798 #ifdef HAVE_PER_THREAD_LOCALE
00799                     newp->localename = new_localename;
00800 #endif
00801 #ifdef IN_LIBGLOCALE
00802                     newp->encoding = encoding;
00803 #endif
00804                     newp->counter = _nl_msg_cat_cntr;
00805                     newp->domain = domain;
00806                     newp->translation = retval;
00807                     newp->translation_length = retlen;
00808 
00809                     gl_rwlock_wrlock (tree_lock);
00810 
00811                     /* Insert the entry in the search tree.  */
00812                     foundp = (struct known_translation_t **)
00813                      tsearch (newp, &root, transcmp);
00814 
00815                     gl_rwlock_unlock (tree_lock);
00816 
00817                     if (foundp == NULL
00818                        || __builtin_expect (*foundp != newp, 0))
00819                      /* The insert failed.  */
00820                      free (newp);
00821                   }
00822               }
00823              else
00824               {
00825                 /* We can update the existing entry.  */
00826                 (*foundp)->counter = _nl_msg_cat_cntr;
00827                 (*foundp)->domain = domain;
00828                 (*foundp)->translation = retval;
00829                 (*foundp)->translation_length = retlen;
00830               }
00831 
00832              __set_errno (saved_errno);
00833 
00834              /* Now deal with plural.  */
00835              if (plural)
00836               retval = plural_lookup (domain, n, retval, retlen);
00837 
00838              gl_rwlock_unlock (_nl_state_lock);
00839 #ifdef _LIBC
00840              __libc_rwlock_unlock (__libc_setlocale_lock);
00841 #endif
00842              return retval;
00843            }
00844        }
00845     }
00846 
00847  return_untranslated:
00848   /* Return the untranslated MSGID.  */
00849   FREE_BLOCKS (block_list);
00850   gl_rwlock_unlock (_nl_state_lock);
00851 #ifdef _LIBC
00852   __libc_rwlock_unlock (__libc_setlocale_lock);
00853 #endif
00854 #ifndef _LIBC
00855   if (!ENABLE_SECURE)
00856     {
00857       extern void _nl_log_untranslated (const char *logfilename,
00858                                    const char *domainname,
00859                                    const char *msgid1, const char *msgid2,
00860                                    int plural);
00861       const char *logfilename = getenv ("GETTEXT_LOG_UNTRANSLATED");
00862 
00863       if (logfilename != NULL && logfilename[0] != '\0')
00864        _nl_log_untranslated (logfilename, domainname, msgid1, msgid2, plural);
00865     }
00866 #endif
00867   __set_errno (saved_errno);
00868   return (plural == 0
00869          ? (char *) msgid1
00870          /* Use the Germanic plural rule.  */
00871          : n == 1 ? (char *) msgid1 : (char *) msgid2);
00872 }
00873 
00874 
00875 /* Look up the translation of msgid within DOMAIN_FILE and DOMAINBINDING.
00876    Return it if found.  Return NULL if not found or in case of a conversion
00877    failure (problem in the particular message catalog).  Return (char *) -1
00878    in case of a memory allocation failure during conversion (only if
00879    ENCODING != NULL resp. CONVERT == true).  */
00880 char *
00881 internal_function
00882 #ifdef IN_LIBGLOCALE
00883 _nl_find_msg (struct loaded_l10nfile *domain_file,
00884              struct binding *domainbinding, const char *encoding,
00885              const char *msgid,
00886              size_t *lengthp)
00887 #else
00888 _nl_find_msg (struct loaded_l10nfile *domain_file,
00889              struct binding *domainbinding,
00890              const char *msgid, int convert,
00891              size_t *lengthp)
00892 #endif
00893 {
00894   struct loaded_domain *domain;
00895   nls_uint32 nstrings;
00896   size_t act;
00897   char *result;
00898   size_t resultlen;
00899 
00900   if (domain_file->decided <= 0)
00901     _nl_load_domain (domain_file, domainbinding);
00902 
00903   if (domain_file->data == NULL)
00904     return NULL;
00905 
00906   domain = (struct loaded_domain *) domain_file->data;
00907 
00908   nstrings = domain->nstrings;
00909 
00910   /* Locate the MSGID and its translation.  */
00911   if (domain->hash_tab != NULL)
00912     {
00913       /* Use the hashing table.  */
00914       nls_uint32 len = strlen (msgid);
00915       nls_uint32 hash_val = __hash_string (msgid);
00916       nls_uint32 idx = hash_val % domain->hash_size;
00917       nls_uint32 incr = 1 + (hash_val % (domain->hash_size - 2));
00918 
00919       while (1)
00920        {
00921          nls_uint32 nstr =
00922            W (domain->must_swap_hash_tab, domain->hash_tab[idx]);
00923 
00924          if (nstr == 0)
00925            /* Hash table entry is empty.  */
00926            return NULL;
00927 
00928          nstr--;
00929 
00930          /* Compare msgid with the original string at index nstr.
00931             We compare the lengths with >=, not ==, because plural entries
00932             are represented by strings with an embedded NUL.  */
00933          if (nstr < nstrings
00934              ? W (domain->must_swap, domain->orig_tab[nstr].length) >= len
00935               && (strcmp (msgid,
00936                          domain->data + W (domain->must_swap,
00937                                          domain->orig_tab[nstr].offset))
00938                   == 0)
00939              : domain->orig_sysdep_tab[nstr - nstrings].length > len
00940               && (strcmp (msgid,
00941                          domain->orig_sysdep_tab[nstr - nstrings].pointer)
00942                   == 0))
00943            {
00944              act = nstr;
00945              goto found;
00946            }
00947 
00948          if (idx >= domain->hash_size - incr)
00949            idx -= domain->hash_size - incr;
00950          else
00951            idx += incr;
00952        }
00953       /* NOTREACHED */
00954     }
00955   else
00956     {
00957       /* Try the default method:  binary search in the sorted array of
00958         messages.  */
00959       size_t top, bottom;
00960 
00961       bottom = 0;
00962       top = nstrings;
00963       while (bottom < top)
00964        {
00965          int cmp_val;
00966 
00967          act = (bottom + top) / 2;
00968          cmp_val = strcmp (msgid, (domain->data
00969                                 + W (domain->must_swap,
00970                                     domain->orig_tab[act].offset)));
00971          if (cmp_val < 0)
00972            top = act;
00973          else if (cmp_val > 0)
00974            bottom = act + 1;
00975          else
00976            goto found;
00977        }
00978       /* No translation was found.  */
00979       return NULL;
00980     }
00981 
00982  found:
00983   /* The translation was found at index ACT.  If we have to convert the
00984      string to use a different character set, this is the time.  */
00985   if (act < nstrings)
00986     {
00987       result = (char *)
00988        (domain->data + W (domain->must_swap, domain->trans_tab[act].offset));
00989       resultlen = W (domain->must_swap, domain->trans_tab[act].length) + 1;
00990     }
00991   else
00992     {
00993       result = (char *) domain->trans_sysdep_tab[act - nstrings].pointer;
00994       resultlen = domain->trans_sysdep_tab[act - nstrings].length;
00995     }
00996 
00997 #if defined _LIBC || HAVE_ICONV
00998 # ifdef IN_LIBGLOCALE
00999   if (encoding != NULL)
01000 # else
01001   if (convert)
01002 # endif
01003     {
01004       /* We are supposed to do a conversion.  */
01005 # ifndef IN_LIBGLOCALE
01006       const char *encoding = get_output_charset (domainbinding);
01007 # endif
01008       size_t nconversions;
01009       struct converted_domain *convd;
01010       size_t i;
01011 
01012       /* Protect against reallocation of the table.  */
01013       gl_rwlock_rdlock (domain->conversions_lock);
01014 
01015       /* Search whether a table with converted translations for this
01016         encoding has already been allocated.  */
01017       nconversions = domain->nconversions;
01018       convd = NULL;
01019 
01020       for (i = nconversions; i > 0; )
01021        {
01022          i--;
01023          if (strcmp (domain->conversions[i].encoding, encoding) == 0)
01024            {
01025              convd = &domain->conversions[i];
01026              break;
01027            }
01028        }
01029 
01030       gl_rwlock_unlock (domain->conversions_lock);
01031 
01032       if (convd == NULL)
01033        {
01034          /* We have to allocate a new conversions table.  */
01035          gl_rwlock_wrlock (domain->conversions_lock);
01036          nconversions = domain->nconversions;
01037 
01038          /* Maybe in the meantime somebody added the translation.
01039             Recheck.  */
01040          for (i = nconversions; i > 0; )
01041            {
01042              i--;
01043              if (strcmp (domain->conversions[i].encoding, encoding) == 0)
01044               {
01045                 convd = &domain->conversions[i];
01046                 goto found_convd;
01047               }
01048            }
01049 
01050          {
01051            /* Allocate a table for the converted translations for this
01052               encoding.  */
01053            struct converted_domain *new_conversions =
01054              (struct converted_domain *)
01055              (domain->conversions != NULL
01056               ? realloc (domain->conversions,
01057                        (nconversions + 1) * sizeof (struct converted_domain))
01058               : malloc ((nconversions + 1) * sizeof (struct converted_domain)));
01059 
01060            if (__builtin_expect (new_conversions == NULL, 0))
01061              {
01062               /* Nothing we can do, no more memory.  We cannot use the
01063                  translation because it might be encoded incorrectly.  */
01064              unlock_fail:
01065               gl_rwlock_unlock (domain->conversions_lock);
01066               return (char *) -1;
01067              }
01068 
01069            domain->conversions = new_conversions;
01070 
01071            /* Copy the 'encoding' string to permanent storage.  */
01072            encoding = strdup (encoding);
01073            if (__builtin_expect (encoding == NULL, 0))
01074              /* Nothing we can do, no more memory.  We cannot use the
01075                translation because it might be encoded incorrectly.  */
01076              goto unlock_fail;
01077 
01078            convd = &new_conversions[nconversions];
01079            convd->encoding = encoding;
01080 
01081            /* Find out about the character set the file is encoded with.
01082               This can be found (in textual form) in the entry "".  If this
01083               entry does not exist or if this does not contain the 'charset='
01084               information, we will assume the charset matches the one the
01085               current locale and we don't have to perform any conversion.  */
01086 # ifdef _LIBC
01087            convd->conv = (__gconv_t) -1;
01088 # else
01089 #  if HAVE_ICONV
01090            convd->conv = (iconv_t) -1;
01091 #  endif
01092 # endif
01093            {
01094              char *nullentry;
01095              size_t nullentrylen;
01096 
01097              /* Get the header entry.  This is a recursion, but it doesn't
01098                reallocate domain->conversions because we pass
01099                encoding = NULL or convert = 0, respectively.  */
01100              nullentry =
01101 # ifdef IN_LIBGLOCALE
01102               _nl_find_msg (domain_file, domainbinding, NULL, "",
01103                            &nullentrylen);
01104 # else
01105               _nl_find_msg (domain_file, domainbinding, "", 0, &nullentrylen);
01106 # endif
01107 
01108              if (nullentry != NULL)
01109               {
01110                 const char *charsetstr;
01111 
01112                 charsetstr = strstr (nullentry, "charset=");
01113                 if (charsetstr != NULL)
01114                   {
01115                     size_t len;
01116                     char *charset;
01117                     const char *outcharset;
01118 
01119                     charsetstr += strlen ("charset=");
01120                     len = strcspn (charsetstr, " \t\n");
01121 
01122                     charset = (char *) alloca (len + 1);
01123 # if defined _LIBC || HAVE_MEMPCPY
01124                     *((char *) mempcpy (charset, charsetstr, len)) = '\0';
01125 # else
01126                     memcpy (charset, charsetstr, len);
01127                     charset[len] = '\0';
01128 # endif
01129 
01130                     outcharset = encoding;
01131 
01132 # ifdef _LIBC
01133                     /* We always want to use transliteration.  */
01134                     outcharset = norm_add_slashes (outcharset, "TRANSLIT");
01135                     charset = norm_add_slashes (charset, "");
01136                     int r = __gconv_open (outcharset, charset, &convd->conv,
01137                                        GCONV_AVOID_NOCONV);
01138                     if (__builtin_expect (r != __GCONV_OK, 0))
01139                      {
01140                        /* If the output encoding is the same there is
01141                           nothing to do.  Otherwise do not use the
01142                           translation at all.  */
01143                        if (__builtin_expect (r != __GCONV_NULCONV, 1))
01144                          {
01145                            gl_rwlock_unlock (domain->conversions_lock);
01146                            free ((char *) encoding);
01147                            return NULL;
01148                          }
01149 
01150                        convd->conv = (__gconv_t) -1;
01151                      }
01152 # else
01153 #  if HAVE_ICONV
01154                     /* When using GNU libc >= 2.2 or GNU libiconv >= 1.5,
01155                       we want to use transliteration.  */
01156 #   if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2) || __GLIBC__ > 2 \
01157        || _LIBICONV_VERSION >= 0x0105
01158                     if (strchr (outcharset, '/') == NULL)
01159                      {
01160                        char *tmp;
01161 
01162                        len = strlen (outcharset);
01163                        tmp = (char *) alloca (len + 10 + 1);
01164                        memcpy (tmp, outcharset, len);
01165                        memcpy (tmp + len, "//TRANSLIT", 10 + 1);
01166                        outcharset = tmp;
01167 
01168                        convd->conv = iconv_open (outcharset, charset);
01169 
01170                        freea (outcharset);
01171                      }
01172                     else
01173 #   endif
01174                      convd->conv = iconv_open (outcharset, charset);
01175 #  endif
01176 # endif
01177 
01178                     freea (charset);
01179                   }
01180               }
01181            }
01182            convd->conv_tab = NULL;
01183            /* Here domain->conversions is still == new_conversions.  */
01184            domain->nconversions++;
01185          }
01186 
01187        found_convd:
01188          gl_rwlock_unlock (domain->conversions_lock);
01189        }
01190 
01191       if (
01192 # ifdef _LIBC
01193          convd->conv != (__gconv_t) -1
01194 # else
01195 #  if HAVE_ICONV
01196          convd->conv != (iconv_t) -1
01197 #  endif
01198 # endif
01199          )
01200        {
01201          /* We are supposed to do a conversion.  First allocate an
01202             appropriate table with the same structure as the table
01203             of translations in the file, where we can put the pointers
01204             to the converted strings in.
01205             There is a slight complication with plural entries.  They
01206             are represented by consecutive NUL terminated strings.  We
01207             handle this case by converting RESULTLEN bytes, including
01208             NULs.  */
01209 
01210          /* This lock primarily protects the memory management variables
01211             freemem, freemem_size.  It also protects write accesses to
01212             convd->conv_tab.  It's not worth using a separate lock (such
01213             as domain->conversions_lock) for this purpose, because when
01214             modifying convd->conv_tab, we also need to lock freemem,
01215             freemem_size for most of the time.  */
01216          __libc_lock_define_initialized (static, lock)
01217 
01218          if (__builtin_expect (convd->conv_tab == NULL, 0))
01219            {
01220              __libc_lock_lock (lock);
01221              if (convd->conv_tab == NULL)
01222               {
01223                 convd->conv_tab =
01224                   (char **) calloc (nstrings + domain->n_sysdep_strings,
01225                                   sizeof (char *));
01226                 if (convd->conv_tab != NULL)
01227                   goto not_translated_yet;
01228                 /* Mark that we didn't succeed allocating a table.  */
01229                 convd->conv_tab = (char **) -1;
01230               }
01231              __libc_lock_unlock (lock);
01232            }
01233 
01234          if (__builtin_expect (convd->conv_tab == (char **) -1, 0))
01235            /* Nothing we can do, no more memory.  We cannot use the
01236               translation because it might be encoded incorrectly.  */
01237            return (char *) -1;
01238 
01239          if (convd->conv_tab[act] == NULL)
01240            {
01241              /* We haven't used this string so far, so it is not
01242                translated yet.  Do this now.  */
01243              /* We use a bit more efficient memory handling.
01244                We allocate always larger blocks which get used over
01245                time.  This is faster than many small allocations.   */
01246 # define INITIAL_BLOCK_SIZE 4080
01247              static unsigned char *freemem;
01248              static size_t freemem_size;
01249 
01250              const unsigned char *inbuf;
01251              unsigned char *outbuf;
01252              int malloc_count;
01253 # ifndef _LIBC
01254              transmem_block_t *transmem_list;
01255 # endif
01256 
01257              __libc_lock_lock (lock);
01258            not_translated_yet:
01259 
01260              inbuf = (const unsigned char *) result;
01261              outbuf = freemem + sizeof (size_t);
01262 # ifndef _LIBC
01263              transmem_list = NULL;
01264 # endif
01265 
01266              malloc_count = 0;
01267              while (1)
01268               {
01269                 transmem_block_t *newmem;
01270 # ifdef _LIBC
01271                 size_t non_reversible;
01272                 int res;
01273 
01274                 if (freemem_size < sizeof (size_t))
01275                   goto resize_freemem;
01276 
01277                 res = __gconv (convd->conv,
01278                              &inbuf, inbuf + resultlen,
01279                              &outbuf,
01280                              outbuf + freemem_size - sizeof (size_t),
01281                              &non_reversible);
01282 
01283                 if (res == __GCONV_OK || res == __GCONV_EMPTY_INPUT)
01284                   break;
01285 
01286                 if (res != __GCONV_FULL_OUTPUT)
01287                   {
01288                     /* We should not use the translation at all, it
01289                       is incorrectly encoded.  */
01290                     __libc_lock_unlock (lock);
01291                     return NULL;
01292                   }
01293 
01294                 inbuf = (const unsigned char *) result;
01295 # else
01296 #  if HAVE_ICONV
01297                 const char *inptr = (const char *) inbuf;
01298                 size_t inleft = resultlen;
01299                 char *outptr = (char *) outbuf;
01300                 size_t outleft;
01301 
01302                 if (freemem_size < sizeof (size_t))
01303                   goto resize_freemem;
01304 
01305                 outleft = freemem_size - sizeof (size_t);
01306                 if (iconv (convd->conv,
01307                           (ICONV_CONST char **) &inptr, &inleft,
01308                           &outptr, &outleft)
01309                     != (size_t) (-1))
01310                   {
01311                     outbuf = (unsigned char *) outptr;
01312                     break;
01313                   }
01314                 if (errno != E2BIG)
01315                   {
01316                     __libc_lock_unlock (lock);
01317                     return NULL;
01318                   }
01319 #  endif
01320 # endif
01321 
01322               resize_freemem:
01323                 /* We must allocate a new buffer or resize the old one.  */
01324                 if (malloc_count > 0)
01325                   {
01326                     ++malloc_count;
01327                     freemem_size = malloc_count * INITIAL_BLOCK_SIZE;
01328                     newmem = (transmem_block_t *) realloc (transmem_list,
01329                                                       freemem_size);
01330 # ifdef _LIBC
01331                     if (newmem != NULL)
01332                      transmem_list = transmem_list->next;
01333                     else
01334                      {
01335                        struct transmem_list *old = transmem_list;
01336 
01337                        transmem_list = transmem_list->next;
01338                        free (old);
01339                      }
01340 # endif
01341                   }
01342                 else
01343                   {
01344                     malloc_count = 1;
01345                     freemem_size = INITIAL_BLOCK_SIZE;
01346                     newmem = (transmem_block_t *) malloc (freemem_size);
01347                   }
01348                 if (__builtin_expect (newmem == NULL, 0))
01349                   {
01350                     freemem = NULL;
01351                     freemem_size = 0;
01352                     __libc_lock_unlock (lock);
01353                     return (char *) -1;
01354                   }
01355 
01356 # ifdef _LIBC
01357                 /* Add the block to the list of blocks we have to free
01358                    at some point.  */
01359                 newmem->next = transmem_list;
01360                 transmem_list = newmem;
01361 
01362                 freemem = (unsigned char *) newmem->data;
01363                 freemem_size -= offsetof (struct transmem_list, data);
01364 # else
01365                 transmem_list = newmem;
01366                 freemem = newmem;
01367 # endif
01368 
01369                 outbuf = freemem + sizeof (size_t);
01370               }
01371 
01372              /* We have now in our buffer a converted string.  Put this
01373                into the table of conversions.  */
01374              *(size_t *) freemem = outbuf - freemem - sizeof (size_t);
01375              convd->conv_tab[act] = (char *) freemem;
01376              /* Shrink freemem, but keep it aligned.  */
01377              freemem_size -= outbuf - freemem;
01378              freemem = outbuf;
01379              freemem += freemem_size & (alignof (size_t) - 1);
01380              freemem_size = freemem_size & ~ (alignof (size_t) - 1);
01381 
01382              __libc_lock_unlock (lock);
01383            }
01384 
01385          /* Now convd->conv_tab[act] contains the translation of all
01386             the plural variants.  */
01387          result = convd->conv_tab[act] + sizeof (size_t);
01388          resultlen = *(size_t *) convd->conv_tab[act];
01389        }
01390     }
01391 
01392   /* The result string is converted.  */
01393 
01394 #endif /* _LIBC || HAVE_ICONV */
01395 
01396   *lengthp = resultlen;
01397   return result;
01398 }
01399 
01400 
01401 /* Look up a plural variant.  */
01402 static char *
01403 internal_function
01404 plural_lookup (struct loaded_l10nfile *domain, unsigned long int n,
01405               const char *translation, size_t translation_len)
01406 {
01407   struct loaded_domain *domaindata = (struct loaded_domain *) domain->data;
01408   unsigned long int index;
01409   const char *p;
01410 
01411   index = plural_eval (domaindata->plural, n);
01412   if (index >= domaindata->nplurals)
01413     /* This should never happen.  It means the plural expression and the
01414        given maximum value do not match.  */
01415     index = 0;
01416 
01417   /* Skip INDEX strings at TRANSLATION.  */
01418   p = translation;
01419   while (index-- > 0)
01420     {
01421 #ifdef _LIBC
01422       p = __rawmemchr (p, '\0');
01423 #else
01424       p = strchr (p, '\0');
01425 #endif
01426       /* And skip over the NUL byte.  */
01427       p++;
01428 
01429       if (p >= translation + translation_len)
01430        /* This should never happen.  It means the plural expression
01431           evaluated to a value larger than the number of variants
01432           available for MSGID1.  */
01433        return (char *) translation;
01434     }
01435   return (char *) p;
01436 }
01437 
01438 #ifndef _LIBC
01439 /* Return string representation of locale CATEGORY.  */
01440 static const char *
01441 internal_function
01442 category_to_name (int category)
01443 {
01444   const char *retval;
01445 
01446   switch (category)
01447   {
01448 #ifdef LC_COLLATE
01449   case LC_COLLATE:
01450     retval = "LC_COLLATE";
01451     break;
01452 #endif
01453 #ifdef LC_CTYPE
01454   case LC_CTYPE:
01455     retval = "LC_CTYPE";
01456     break;
01457 #endif
01458 #ifdef LC_MONETARY
01459   case LC_MONETARY:
01460     retval = "LC_MONETARY";
01461     break;
01462 #endif
01463 #ifdef LC_NUMERIC
01464   case LC_NUMERIC:
01465     retval = "LC_NUMERIC";
01466     break;
01467 #endif
01468 #ifdef LC_TIME
01469   case LC_TIME:
01470     retval = "LC_TIME";
01471     break;
01472 #endif
01473 #ifdef LC_MESSAGES
01474   case LC_MESSAGES:
01475     retval = "LC_MESSAGES";
01476     break;
01477 #endif
01478 #ifdef LC_RESPONSE
01479   case LC_RESPONSE:
01480     retval = "LC_RESPONSE";
01481     break;
01482 #endif
01483 #ifdef LC_ALL
01484   case LC_ALL:
01485     /* This might not make sense but is perhaps better than any other
01486        value.  */
01487     retval = "LC_ALL";
01488     break;
01489 #endif
01490   default:
01491     /* If you have a better idea for a default value let me know.  */
01492     retval = "LC_XXX";
01493   }
01494 
01495   return retval;
01496 }
01497 #endif
01498 
01499 /* Guess value of current locale from value of the environment variables
01500    or system-dependent defaults.  */
01501 static const char *
01502 internal_function
01503 #ifdef IN_LIBGLOCALE
01504 guess_category_value (int category, const char *categoryname,
01505                     const char *locale)
01506 
01507 #else
01508 guess_category_value (int category, const char *categoryname)
01509 #endif
01510 {
01511   const char *language;
01512 #ifndef IN_LIBGLOCALE
01513   const char *locale;
01514 # ifndef _LIBC
01515   const char *language_default;
01516   int locale_defaulted;
01517 # endif
01518 #endif
01519 
01520   /* We use the settings in the following order:
01521      1. The value of the environment variable 'LANGUAGE'.  This is a GNU
01522         extension.  Its value can be a colon-separated list of locale names.
01523      2. The value of the environment variable 'LC_ALL', 'LC_xxx', or 'LANG'.
01524         More precisely, the first among these that is set to a non-empty value.
01525         This is how POSIX specifies it.  The value is a single locale name.
01526      3. A system-dependent preference list of languages.  Its value can be a
01527         colon-separated list of locale names.
01528      4. A system-dependent default locale name.
01529      This way:
01530        - System-dependent settings can be overridden by environment variables.
01531        - If the system provides both a list of languages and a default locale,
01532          the former is used.  */
01533 
01534 #ifndef IN_LIBGLOCALE
01535   /* Fetch the locale name, through the POSIX method of looking to `LC_ALL',
01536      `LC_xxx', and `LANG'.  On some systems this can be done by the
01537      `setlocale' function itself.  */
01538 # ifdef _LIBC
01539   locale = __current_locale_name (category);
01540 # else
01541   locale_defaulted = 0;
01542 #  if HAVE_USELOCALE
01543   locale = _nl_locale_name_thread_unsafe (category, categoryname);
01544   if (locale == NULL)
01545 #  endif
01546     {
01547       locale = _nl_locale_name_posix (category, categoryname);
01548       if (locale == NULL)
01549        {
01550          locale = _nl_locale_name_default ();
01551          locale_defaulted = 1;
01552        }
01553     }
01554 # endif
01555 #endif
01556 
01557   /* Ignore LANGUAGE and its system-dependent analogon if the locale is set
01558      to "C" because
01559      1. "C" locale usually uses the ASCII encoding, and most international
01560        messages use non-ASCII characters. These characters get displayed
01561        as question marks (if using glibc's iconv()) or as invalid 8-bit
01562        characters (because other iconv()s refuse to convert most non-ASCII
01563        characters to ASCII). In any case, the output is ugly.
01564      2. The precise output of some programs in the "C" locale is specified
01565        by POSIX and should not depend on environment variables like
01566        "LANGUAGE" or system-dependent information.  We allow such programs
01567         to use gettext().  */
01568   if (strcmp (locale, "C") == 0)
01569     return locale;
01570 
01571   /* The highest priority value is the value of the 'LANGUAGE' environment
01572      variable.  */
01573   language = getenv ("LANGUAGE");
01574   if (language != NULL && language[0] != '\0')
01575     return language;
01576 #if !defined IN_LIBGLOCALE && !defined _LIBC
01577   /* The next priority value is the locale name, if not defaulted.  */
01578   if (locale_defaulted)
01579     {
01580       /* The next priority value is the default language preferences list. */
01581       language_default = _nl_language_preferences_default ();
01582       if (language_default != NULL)
01583         return language_default;
01584     }
01585   /* The least priority value is the locale name, if defaulted.  */
01586 #endif
01587   return locale;
01588 }
01589 
01590 #if (defined _LIBC || HAVE_ICONV) && !defined IN_LIBGLOCALE
01591 /* Returns the output charset.  */
01592 static const char *
01593 internal_function
01594 get_output_charset (struct binding *domainbinding)
01595 {
01596   /* The output charset should normally be determined by the locale.  But
01597      sometimes the locale is not used or not correctly set up, so we provide
01598      a possibility for the user to override this: the OUTPUT_CHARSET
01599      environment variable.  Moreover, the value specified through
01600      bind_textdomain_codeset overrides both.  */
01601   if (domainbinding != NULL && domainbinding->codeset != NULL)
01602     return domainbinding->codeset;
01603   else
01604     {
01605       /* For speed reasons, we look at the value of OUTPUT_CHARSET only
01606         once.  This is a user variable that is not supposed to change
01607         during a program run.  */
01608       static char *output_charset_cache;
01609       static int output_charset_cached;
01610 
01611       if (!output_charset_cached)
01612        {
01613          const char *value = getenv ("OUTPUT_CHARSET");
01614 
01615          if (value != NULL && value[0] != '\0')
01616            {
01617              size_t len = strlen (value) + 1;
01618              char *value_copy = (char *) malloc (len);
01619 
01620              if (value_copy != NULL)
01621               memcpy (value_copy, value, len);
01622              output_charset_cache = value_copy;
01623            }
01624          output_charset_cached = 1;
01625        }
01626 
01627       if (output_charset_cache != NULL)
01628        return output_charset_cache;
01629       else
01630        {
01631 # ifdef _LIBC
01632          return _NL_CURRENT (LC_CTYPE, CODESET);
01633 # else
01634 #  if HAVE_ICONV
01635          return locale_charset ();
01636 #  endif
01637 # endif
01638        }
01639     }
01640 }
01641 #endif
01642 
01643 /* @@ begin of epilog @@ */
01644 
01645 /* We don't want libintl.a to depend on any other library.  So we
01646    avoid the non-standard function stpcpy.  In GNU C Library this
01647    function is available, though.  Also allow the symbol HAVE_STPCPY
01648    to be defined.  */
01649 #if !_LIBC && !HAVE_STPCPY
01650 static char *
01651 stpcpy (char *dest, const char *src)
01652 {
01653   while ((*dest++ = *src++) != '\0')
01654     /* Do nothing. */ ;
01655   return dest - 1;
01656 }
01657 #endif
01658 
01659 #if !_LIBC && !HAVE_MEMPCPY
01660 static void *
01661 mempcpy (void *dest, const void *src, size_t n)
01662 {
01663   return (void *) ((char *) memcpy (dest, src, n) + n);
01664 }
01665 #endif
01666 
01667 #if !_LIBC && !HAVE_TSEARCH
01668 # include "tsearch.c"
01669 #endif
01670 
01671 
01672 #ifdef _LIBC
01673 /* If we want to free all resources we have to do some work at
01674    program's end.  */
01675 libc_freeres_fn (free_mem)
01676 {
01677   void *old;
01678 
01679   while (_nl_domain_bindings != NULL)
01680     {
01681       struct binding *oldp = _nl_domain_bindings;
01682       _nl_domain_bindings = _nl_domain_bindings->next;
01683       if (oldp->dirname != _nl_default_dirname)
01684        /* Yes, this is a pointer comparison.  */
01685        free (oldp->dirname);
01686       free (oldp->codeset);
01687       free (oldp);
01688     }
01689 
01690   if (_nl_current_default_domain != _nl_default_default_domain)
01691     /* Yes, again a pointer comparison.  */
01692     free ((char *) _nl_current_default_domain);
01693 
01694   /* Remove the search tree with the known translations.  */
01695   __tdestroy (root, free);
01696   root = NULL;
01697 
01698   while (transmem_list != NULL)
01699     {
01700       old = transmem_list;
01701       transmem_list = transmem_list->next;
01702       free (old);
01703     }
01704 }
01705 #endif