Back to index

cell-binutils  2.17cvs20070401
dcigettext.c
Go to the documentation of this file.
00001 /* Implementation of the internal dcigettext function.
00002    Copyright (C) 1995-1999, 2000-2003 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 #include "gettextP.h"
00091 #include "plural-exp.h"
00092 #ifdef _LIBC
00093 # include <libintl.h>
00094 #else
00095 # include "libgnuintl.h"
00096 #endif
00097 #include "hash-string.h"
00098 
00099 /* Thread safetyness.  */
00100 #ifdef _LIBC
00101 # include <bits/libc-lock.h>
00102 #else
00103 /* Provide dummy implementation if this is outside glibc.  */
00104 # define __libc_lock_define_initialized(CLASS, NAME)
00105 # define __libc_lock_lock(NAME)
00106 # define __libc_lock_unlock(NAME)
00107 # define __libc_rwlock_define_initialized(CLASS, NAME)
00108 # define __libc_rwlock_rdlock(NAME)
00109 # define __libc_rwlock_unlock(NAME)
00110 #endif
00111 
00112 /* Alignment of types.  */
00113 #if defined __GNUC__ && __GNUC__ >= 2
00114 # define alignof(TYPE) __alignof__ (TYPE)
00115 #else
00116 # define alignof(TYPE) \
00117     ((int) &((struct { char dummy1; TYPE dummy2; } *) 0)->dummy2)
00118 #endif
00119 
00120 /* The internal variables in the standalone libintl.a must have different
00121    names than the internal variables in GNU libc, otherwise programs
00122    using libintl.a cannot be linked statically.  */
00123 #if !defined _LIBC
00124 # define _nl_default_default_domain libintl_nl_default_default_domain
00125 # define _nl_current_default_domain libintl_nl_current_default_domain
00126 # define _nl_default_dirname libintl_nl_default_dirname
00127 # define _nl_domain_bindings libintl_nl_domain_bindings
00128 #endif
00129 
00130 /* Some compilers, like SunOS4 cc, don't have offsetof in <stddef.h>.  */
00131 #ifndef offsetof
00132 # define offsetof(type,ident) ((size_t)&(((type*)0)->ident))
00133 #endif
00134 
00135 /* @@ end of prolog @@ */
00136 
00137 #ifdef _LIBC
00138 /* Rename the non ANSI C functions.  This is required by the standard
00139    because some ANSI C functions will require linking with this object
00140    file and the name space must not be polluted.  */
00141 # define getcwd __getcwd
00142 # ifndef stpcpy
00143 #  define stpcpy __stpcpy
00144 # endif
00145 # define tfind __tfind
00146 #else
00147 # if !defined HAVE_GETCWD
00148 char *getwd ();
00149 #  define getcwd(buf, max) getwd (buf)
00150 # else
00151 char *getcwd ();
00152 # endif
00153 # ifndef HAVE_STPCPY
00154 static char *stpcpy PARAMS ((char *dest, const char *src));
00155 # endif
00156 # ifndef HAVE_MEMPCPY
00157 static void *mempcpy PARAMS ((void *dest, const void *src, size_t n));
00158 # endif
00159 #endif
00160 
00161 /* Amount to increase buffer size by in each try.  */
00162 #define PATH_INCR 32
00163 
00164 /* The following is from pathmax.h.  */
00165 /* Non-POSIX BSD systems might have gcc's limits.h, which doesn't define
00166    PATH_MAX but might cause redefinition warnings when sys/param.h is
00167    later included (as on MORE/BSD 4.3).  */
00168 #if defined _POSIX_VERSION || (defined HAVE_LIMITS_H && !defined __GNUC__)
00169 # include <limits.h>
00170 #endif
00171 
00172 #ifndef _POSIX_PATH_MAX
00173 # define _POSIX_PATH_MAX 255
00174 #endif
00175 
00176 #if !defined PATH_MAX && defined _PC_PATH_MAX
00177 # define PATH_MAX (pathconf ("/", _PC_PATH_MAX) < 1 ? 1024 : pathconf ("/", _PC_PATH_MAX))
00178 #endif
00179 
00180 /* Don't include sys/param.h if it already has been.  */
00181 #if defined HAVE_SYS_PARAM_H && !defined PATH_MAX && !defined MAXPATHLEN
00182 # include <sys/param.h>
00183 #endif
00184 
00185 #if !defined PATH_MAX && defined MAXPATHLEN
00186 # define PATH_MAX MAXPATHLEN
00187 #endif
00188 
00189 #ifndef PATH_MAX
00190 # define PATH_MAX _POSIX_PATH_MAX
00191 #endif
00192 
00193 /* Pathname support.
00194    ISSLASH(C)           tests whether C is a directory separator character.
00195    IS_ABSOLUTE_PATH(P)  tests whether P is an absolute path.  If it is not,
00196                         it may be concatenated to a directory pathname.
00197    IS_PATH_WITH_DIR(P)  tests whether P contains a directory specification.
00198  */
00199 #if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__
00200   /* Win32, OS/2, DOS */
00201 # define ISSLASH(C) ((C) == '/' || (C) == '\\')
00202 # define HAS_DEVICE(P) \
00203     ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
00204      && (P)[1] == ':')
00205 # define IS_ABSOLUTE_PATH(P) (ISSLASH ((P)[0]) || HAS_DEVICE (P))
00206 # define IS_PATH_WITH_DIR(P) \
00207     (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P))
00208 #else
00209   /* Unix */
00210 # define ISSLASH(C) ((C) == '/')
00211 # define IS_ABSOLUTE_PATH(P) ISSLASH ((P)[0])
00212 # define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL)
00213 #endif
00214 
00215 /* This is the type used for the search tree where known translations
00216    are stored.  */
00217 struct known_translation_t
00218 {
00219   /* Domain in which to search.  */
00220   char *domainname;
00221 
00222   /* The category.  */
00223   int category;
00224 
00225   /* State of the catalog counter at the point the string was found.  */
00226   int counter;
00227 
00228   /* Catalog where the string was found.  */
00229   struct loaded_l10nfile *domain;
00230 
00231   /* And finally the translation.  */
00232   const char *translation;
00233   size_t translation_length;
00234 
00235   /* Pointer to the string in question.  */
00236   char msgid[ZERO];
00237 };
00238 
00239 /* Root of the search tree with known translations.  We can use this
00240    only if the system provides the `tsearch' function family.  */
00241 #if defined HAVE_TSEARCH || defined _LIBC
00242 # include <search.h>
00243 
00244 static void *root;
00245 
00246 # ifdef _LIBC
00247 #  define tsearch __tsearch
00248 # endif
00249 
00250 /* Function to compare two entries in the table of known translations.  */
00251 static int transcmp PARAMS ((const void *p1, const void *p2));
00252 static int
00253 transcmp (p1, p2)
00254      const void *p1;
00255      const void *p2;
00256 {
00257   const struct known_translation_t *s1;
00258   const struct known_translation_t *s2;
00259   int result;
00260 
00261   s1 = (const struct known_translation_t *) p1;
00262   s2 = (const struct known_translation_t *) p2;
00263 
00264   result = strcmp (s1->msgid, s2->msgid);
00265   if (result == 0)
00266     {
00267       result = strcmp (s1->domainname, s2->domainname);
00268       if (result == 0)
00269        /* We compare the category last (though this is the cheapest
00270           operation) since it is hopefully always the same (namely
00271           LC_MESSAGES).  */
00272        result = s1->category - s2->category;
00273     }
00274 
00275   return result;
00276 }
00277 #endif
00278 
00279 #ifndef INTVARDEF
00280 # define INTVARDEF(name)
00281 #endif
00282 #ifndef INTUSE
00283 # define INTUSE(name) name
00284 #endif
00285 
00286 /* Name of the default domain used for gettext(3) prior any call to
00287    textdomain(3).  The default value for this is "messages".  */
00288 const char _nl_default_default_domain[] attribute_hidden = "messages";
00289 
00290 /* Value used as the default domain for gettext(3).  */
00291 const char *_nl_current_default_domain attribute_hidden
00292      = _nl_default_default_domain;
00293 
00294 /* Contains the default location of the message catalogs.  */
00295 #if defined __EMX__
00296 extern const char _nl_default_dirname[];
00297 #else
00298 const char _nl_default_dirname[] = LOCALEDIR;
00299 INTVARDEF (_nl_default_dirname)
00300 #endif
00301 
00302 /* List with bindings of specific domains created by bindtextdomain()
00303    calls.  */
00304 struct binding *_nl_domain_bindings;
00305 
00306 /* Prototypes for local functions.  */
00307 static char *plural_lookup PARAMS ((struct loaded_l10nfile *domain,
00308                                 unsigned long int n,
00309                                 const char *translation,
00310                                 size_t translation_len))
00311      internal_function;
00312 static const char *guess_category_value PARAMS ((int category,
00313                                            const char *categoryname))
00314      internal_function;
00315 #ifdef _LIBC
00316 # include "../locale/localeinfo.h"
00317 # define category_to_name(category)       _nl_category_names[category]
00318 #else
00319 static const char *category_to_name PARAMS ((int category)) internal_function;
00320 #endif
00321 
00322 
00323 /* For those loosing systems which don't have `alloca' we have to add
00324    some additional code emulating it.  */
00325 #ifdef HAVE_ALLOCA
00326 /* Nothing has to be done.  */
00327 # define freea(p) /* nothing */
00328 # define ADD_BLOCK(list, address) /* nothing */
00329 # define FREE_BLOCKS(list) /* nothing */
00330 #else
00331 struct block_list
00332 {
00333   void *address;
00334   struct block_list *next;
00335 };
00336 # define ADD_BLOCK(list, addr)                                              \
00337   do {                                                               \
00338     struct block_list *newp = (struct block_list *) malloc (sizeof (*newp));  \
00339     /* If we cannot get a free block we cannot add the new element to       \
00340        the list.  */                                                 \
00341     if (newp != NULL) {                                                     \
00342       newp->address = (addr);                                               \
00343       newp->next = (list);                                           \
00344       (list) = newp;                                                 \
00345     }                                                                \
00346   } while (0)
00347 # define FREE_BLOCKS(list)                                           \
00348   do {                                                               \
00349     while (list != NULL) {                                           \
00350       struct block_list *old = list;                                        \
00351       list = list->next;                                             \
00352       free (old->address);                                           \
00353       free (old);                                                    \
00354     }                                                                \
00355   } while (0)
00356 # undef alloca
00357 # define alloca(size) (malloc (size))
00358 # define freea(p) free (p)
00359 #endif /* have alloca */
00360 
00361 
00362 #ifdef _LIBC
00363 /* List of blocks allocated for translations.  */
00364 typedef struct transmem_list
00365 {
00366   struct transmem_list *next;
00367   char data[ZERO];
00368 } transmem_block_t;
00369 static struct transmem_list *transmem_list;
00370 #else
00371 typedef unsigned char transmem_block_t;
00372 #endif
00373 
00374 
00375 /* Names for the libintl functions are a problem.  They must not clash
00376    with existing names and they should follow ANSI C.  But this source
00377    code is also used in GNU C Library where the names have a __
00378    prefix.  So we have to make a difference here.  */
00379 #ifdef _LIBC
00380 # define DCIGETTEXT __dcigettext
00381 #else
00382 # define DCIGETTEXT libintl_dcigettext
00383 #endif
00384 
00385 /* Lock variable to protect the global data in the gettext implementation.  */
00386 #ifdef _LIBC
00387 __libc_rwlock_define_initialized (, _nl_state_lock attribute_hidden)
00388 #endif
00389 
00390 /* Checking whether the binaries runs SUID must be done and glibc provides
00391    easier methods therefore we make a difference here.  */
00392 #ifdef _LIBC
00393 # define ENABLE_SECURE __libc_enable_secure
00394 # define DETERMINE_SECURE
00395 #else
00396 # ifndef HAVE_GETUID
00397 #  define getuid() 0
00398 # endif
00399 # ifndef HAVE_GETGID
00400 #  define getgid() 0
00401 # endif
00402 # ifndef HAVE_GETEUID
00403 #  define geteuid() getuid()
00404 # endif
00405 # ifndef HAVE_GETEGID
00406 #  define getegid() getgid()
00407 # endif
00408 static int enable_secure;
00409 # define ENABLE_SECURE (enable_secure == 1)
00410 # define DETERMINE_SECURE \
00411   if (enable_secure == 0)                                            \
00412     {                                                                \
00413       if (getuid () != geteuid () || getgid () != getegid ())               \
00414        enable_secure = 1;                                            \
00415       else                                                           \
00416        enable_secure = -1;                                           \
00417     }
00418 #endif
00419 
00420 /* Get the function to evaluate the plural expression.  */
00421 #include "eval-plural.h"
00422 
00423 /* Look up MSGID in the DOMAINNAME message catalog for the current
00424    CATEGORY locale and, if PLURAL is nonzero, search over string
00425    depending on the plural form determined by N.  */
00426 char *
00427 DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category)
00428      const char *domainname;
00429      const char *msgid1;
00430      const char *msgid2;
00431      int plural;
00432      unsigned long int n;
00433      int category;
00434 {
00435 #ifndef HAVE_ALLOCA
00436   struct block_list *block_list = NULL;
00437 #endif
00438   struct loaded_l10nfile *domain;
00439   struct binding *binding;
00440   const char *categoryname;
00441   const char *categoryvalue;
00442   char *dirname, *xdomainname;
00443   char *single_locale;
00444   char *retval;
00445   size_t retlen;
00446   int saved_errno;
00447 #if defined HAVE_TSEARCH || defined _LIBC
00448   struct known_translation_t *search;
00449   struct known_translation_t **foundp = NULL;
00450   size_t msgid_len;
00451 #endif
00452   size_t domainname_len;
00453 
00454   /* If no real MSGID is given return NULL.  */
00455   if (msgid1 == NULL)
00456     return NULL;
00457 
00458 #ifdef _LIBC
00459   if (category < 0 || category >= __LC_LAST || category == LC_ALL)
00460     /* Bogus.  */
00461     return (plural == 0
00462            ? (char *) msgid1
00463            /* Use the Germanic plural rule.  */
00464            : n == 1 ? (char *) msgid1 : (char *) msgid2);
00465 #endif
00466 
00467   __libc_rwlock_rdlock (_nl_state_lock);
00468 
00469   /* If DOMAINNAME is NULL, we are interested in the default domain.  If
00470      CATEGORY is not LC_MESSAGES this might not make much sense but the
00471      definition left this undefined.  */
00472   if (domainname == NULL)
00473     domainname = _nl_current_default_domain;
00474 
00475   /* OS/2 specific: backward compatibility with older libintl versions  */
00476 #ifdef LC_MESSAGES_COMPAT
00477   if (category == LC_MESSAGES_COMPAT)
00478     category = LC_MESSAGES;
00479 #endif
00480 
00481 #if defined HAVE_TSEARCH || defined _LIBC
00482   msgid_len = strlen (msgid1) + 1;
00483 
00484   /* Try to find the translation among those which we found at
00485      some time.  */
00486   search = (struct known_translation_t *)
00487           alloca (offsetof (struct known_translation_t, msgid) + msgid_len);
00488   memcpy (search->msgid, msgid1, msgid_len);
00489   search->domainname = (char *) domainname;
00490   search->category = category;
00491 
00492   foundp = (struct known_translation_t **) tfind (search, &root, transcmp);
00493   freea (search);
00494   if (foundp != NULL && (*foundp)->counter == _nl_msg_cat_cntr)
00495     {
00496       /* Now deal with plural.  */
00497       if (plural)
00498        retval = plural_lookup ((*foundp)->domain, n, (*foundp)->translation,
00499                             (*foundp)->translation_length);
00500       else
00501        retval = (char *) (*foundp)->translation;
00502 
00503       __libc_rwlock_unlock (_nl_state_lock);
00504       return retval;
00505     }
00506 #endif
00507 
00508   /* Preserve the `errno' value.  */
00509   saved_errno = errno;
00510 
00511   /* See whether this is a SUID binary or not.  */
00512   DETERMINE_SECURE;
00513 
00514   /* First find matching binding.  */
00515   for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next)
00516     {
00517       int compare = strcmp (domainname, binding->domainname);
00518       if (compare == 0)
00519        /* We found it!  */
00520        break;
00521       if (compare < 0)
00522        {
00523          /* It is not in the list.  */
00524          binding = NULL;
00525          break;
00526        }
00527     }
00528 
00529   if (binding == NULL)
00530     dirname = (char *) INTUSE(_nl_default_dirname);
00531   else if (IS_ABSOLUTE_PATH (binding->dirname))
00532     dirname = binding->dirname;
00533   else
00534     {
00535       /* We have a relative path.  Make it absolute now.  */
00536       size_t dirname_len = strlen (binding->dirname) + 1;
00537       size_t path_max;
00538       char *ret;
00539 
00540       path_max = (unsigned int) PATH_MAX;
00541       path_max += 2;        /* The getcwd docs say to do this.  */
00542 
00543       for (;;)
00544        {
00545          dirname = (char *) alloca (path_max + dirname_len);
00546          ADD_BLOCK (block_list, dirname);
00547 
00548          __set_errno (0);
00549          ret = getcwd (dirname, path_max);
00550          if (ret != NULL || errno != ERANGE)
00551            break;
00552 
00553          path_max += path_max / 2;
00554          path_max += PATH_INCR;
00555        }
00556 
00557       if (ret == NULL)
00558        /* We cannot get the current working directory.  Don't signal an
00559           error but simply return the default string.  */
00560        goto return_untranslated;
00561 
00562       stpcpy (stpcpy (strchr (dirname, '\0'), "/"), binding->dirname);
00563     }
00564 
00565   /* Now determine the symbolic name of CATEGORY and its value.  */
00566   categoryname = category_to_name (category);
00567   categoryvalue = guess_category_value (category, categoryname);
00568 
00569   domainname_len = strlen (domainname);
00570   xdomainname = (char *) alloca (strlen (categoryname)
00571                              + domainname_len + 5);
00572   ADD_BLOCK (block_list, xdomainname);
00573 
00574   stpcpy (mempcpy (stpcpy (stpcpy (xdomainname, categoryname), "/"),
00575                 domainname, domainname_len),
00576          ".mo");
00577 
00578   /* Creating working area.  */
00579   single_locale = (char *) alloca (strlen (categoryvalue) + 1);
00580   ADD_BLOCK (block_list, single_locale);
00581 
00582 
00583   /* Search for the given string.  This is a loop because we perhaps
00584      got an ordered list of languages to consider for the translation.  */
00585   while (1)
00586     {
00587       /* Make CATEGORYVALUE point to the next element of the list.  */
00588       while (categoryvalue[0] != '\0' && categoryvalue[0] == ':')
00589        ++categoryvalue;
00590       if (categoryvalue[0] == '\0')
00591        {
00592          /* The whole contents of CATEGORYVALUE has been searched but
00593             no valid entry has been found.  We solve this situation
00594             by implicitly appending a "C" entry, i.e. no translation
00595             will take place.  */
00596          single_locale[0] = 'C';
00597          single_locale[1] = '\0';
00598        }
00599       else
00600        {
00601          char *cp = single_locale;
00602          while (categoryvalue[0] != '\0' && categoryvalue[0] != ':')
00603            *cp++ = *categoryvalue++;
00604          *cp = '\0';
00605 
00606          /* When this is a SUID binary we must not allow accessing files
00607             outside the dedicated directories.  */
00608          if (ENABLE_SECURE && IS_PATH_WITH_DIR (single_locale))
00609            /* Ingore this entry.  */
00610            continue;
00611        }
00612 
00613       /* If the current locale value is C (or POSIX) we don't load a
00614         domain.  Return the MSGID.  */
00615       if (strcmp (single_locale, "C") == 0
00616          || strcmp (single_locale, "POSIX") == 0)
00617        break;
00618 
00619       /* Find structure describing the message catalog matching the
00620         DOMAINNAME and CATEGORY.  */
00621       domain = _nl_find_domain (dirname, single_locale, xdomainname, binding);
00622 
00623       if (domain != NULL)
00624        {
00625          retval = _nl_find_msg (domain, binding, msgid1, &retlen);
00626 
00627          if (retval == NULL)
00628            {
00629              int cnt;
00630 
00631              for (cnt = 0; domain->successor[cnt] != NULL; ++cnt)
00632               {
00633                 retval = _nl_find_msg (domain->successor[cnt], binding,
00634                                     msgid1, &retlen);
00635 
00636                 if (retval != NULL)
00637                   {
00638                     domain = domain->successor[cnt];
00639                     break;
00640                   }
00641               }
00642            }
00643 
00644          if (retval != NULL)
00645            {
00646              /* Found the translation of MSGID1 in domain DOMAIN:
00647                starting at RETVAL, RETLEN bytes.  */
00648              FREE_BLOCKS (block_list);
00649 #if defined HAVE_TSEARCH || defined _LIBC
00650              if (foundp == NULL)
00651               {
00652                 /* Create a new entry and add it to the search tree.  */
00653                 struct known_translation_t *newp;
00654 
00655                 newp = (struct known_translation_t *)
00656                   malloc (offsetof (struct known_translation_t, msgid)
00657                          + msgid_len + domainname_len + 1);
00658                 if (newp != NULL)
00659                   {
00660                     newp->domainname =
00661                      mempcpy (newp->msgid, msgid1, msgid_len);
00662                     memcpy (newp->domainname, domainname, domainname_len + 1);
00663                     newp->category = category;
00664                     newp->counter = _nl_msg_cat_cntr;
00665                     newp->domain = domain;
00666                     newp->translation = retval;
00667                     newp->translation_length = retlen;
00668 
00669                     /* Insert the entry in the search tree.  */
00670                     foundp = (struct known_translation_t **)
00671                      tsearch (newp, &root, transcmp);
00672                     if (foundp == NULL
00673                        || __builtin_expect (*foundp != newp, 0))
00674                      /* The insert failed.  */
00675                      free (newp);
00676                   }
00677               }
00678              else
00679               {
00680                 /* We can update the existing entry.  */
00681                 (*foundp)->counter = _nl_msg_cat_cntr;
00682                 (*foundp)->domain = domain;
00683                 (*foundp)->translation = retval;
00684                 (*foundp)->translation_length = retlen;
00685               }
00686 #endif
00687              __set_errno (saved_errno);
00688 
00689              /* Now deal with plural.  */
00690              if (plural)
00691               retval = plural_lookup (domain, n, retval, retlen);
00692 
00693              __libc_rwlock_unlock (_nl_state_lock);
00694              return retval;
00695            }
00696        }
00697     }
00698 
00699  return_untranslated:
00700   /* Return the untranslated MSGID.  */
00701   FREE_BLOCKS (block_list);
00702   __libc_rwlock_unlock (_nl_state_lock);
00703 #ifndef _LIBC
00704   if (!ENABLE_SECURE)
00705     {
00706       extern void _nl_log_untranslated PARAMS ((const char *logfilename,
00707                                           const char *domainname,
00708                                           const char *msgid1,
00709                                           const char *msgid2,
00710                                           int plural));
00711       const char *logfilename = getenv ("GETTEXT_LOG_UNTRANSLATED");
00712 
00713       if (logfilename != NULL && logfilename[0] != '\0')
00714        _nl_log_untranslated (logfilename, domainname, msgid1, msgid2, plural);
00715     }
00716 #endif
00717   __set_errno (saved_errno);
00718   return (plural == 0
00719          ? (char *) msgid1
00720          /* Use the Germanic plural rule.  */
00721          : n == 1 ? (char *) msgid1 : (char *) msgid2);
00722 }
00723 
00724 
00725 char *
00726 internal_function
00727 _nl_find_msg (domain_file, domainbinding, msgid, lengthp)
00728      struct loaded_l10nfile *domain_file;
00729      struct binding *domainbinding;
00730      const char *msgid;
00731      size_t *lengthp;
00732 {
00733   struct loaded_domain *domain;
00734   nls_uint32 nstrings;
00735   size_t act;
00736   char *result;
00737   size_t resultlen;
00738 
00739   if (domain_file->decided == 0)
00740     _nl_load_domain (domain_file, domainbinding);
00741 
00742   if (domain_file->data == NULL)
00743     return NULL;
00744 
00745   domain = (struct loaded_domain *) domain_file->data;
00746 
00747   nstrings = domain->nstrings;
00748 
00749   /* Locate the MSGID and its translation.  */
00750   if (domain->hash_tab != NULL)
00751     {
00752       /* Use the hashing table.  */
00753       nls_uint32 len = strlen (msgid);
00754       nls_uint32 hash_val = hash_string (msgid);
00755       nls_uint32 idx = hash_val % domain->hash_size;
00756       nls_uint32 incr = 1 + (hash_val % (domain->hash_size - 2));
00757 
00758       while (1)
00759        {
00760          nls_uint32 nstr =
00761            W (domain->must_swap_hash_tab, domain->hash_tab[idx]);
00762 
00763          if (nstr == 0)
00764            /* Hash table entry is empty.  */
00765            return NULL;
00766 
00767          nstr--;
00768 
00769          /* Compare msgid with the original string at index nstr.
00770             We compare the lengths with >=, not ==, because plural entries
00771             are represented by strings with an embedded NUL.  */
00772          if (nstr < nstrings
00773              ? W (domain->must_swap, domain->orig_tab[nstr].length) >= len
00774               && (strcmp (msgid,
00775                          domain->data + W (domain->must_swap,
00776                                          domain->orig_tab[nstr].offset))
00777                   == 0)
00778              : domain->orig_sysdep_tab[nstr - nstrings].length > len
00779               && (strcmp (msgid,
00780                          domain->orig_sysdep_tab[nstr - nstrings].pointer)
00781                   == 0))
00782            {
00783              act = nstr;
00784              goto found;
00785            }
00786 
00787          if (idx >= domain->hash_size - incr)
00788            idx -= domain->hash_size - incr;
00789          else
00790            idx += incr;
00791        }
00792       /* NOTREACHED */
00793     }
00794   else
00795     {
00796       /* Try the default method:  binary search in the sorted array of
00797         messages.  */
00798       size_t top, bottom;
00799 
00800       bottom = 0;
00801       top = nstrings;
00802       while (bottom < top)
00803        {
00804          int cmp_val;
00805 
00806          act = (bottom + top) / 2;
00807          cmp_val = strcmp (msgid, (domain->data
00808                                 + W (domain->must_swap,
00809                                     domain->orig_tab[act].offset)));
00810          if (cmp_val < 0)
00811            top = act;
00812          else if (cmp_val > 0)
00813            bottom = act + 1;
00814          else
00815            goto found;
00816        }
00817       /* No translation was found.  */
00818       return NULL;
00819     }
00820 
00821  found:
00822   /* The translation was found at index ACT.  If we have to convert the
00823      string to use a different character set, this is the time.  */
00824   if (act < nstrings)
00825     {
00826       result = (char *)
00827        (domain->data + W (domain->must_swap, domain->trans_tab[act].offset));
00828       resultlen = W (domain->must_swap, domain->trans_tab[act].length) + 1;
00829     }
00830   else
00831     {
00832       result = (char *) domain->trans_sysdep_tab[act - nstrings].pointer;
00833       resultlen = domain->trans_sysdep_tab[act - nstrings].length;
00834     }
00835 
00836 #if defined _LIBC || HAVE_ICONV
00837   if (domain->codeset_cntr
00838       != (domainbinding != NULL ? domainbinding->codeset_cntr : 0))
00839     {
00840       /* The domain's codeset has changed through bind_textdomain_codeset()
00841         since the message catalog was initialized or last accessed.  We
00842         have to reinitialize the converter.  */
00843       _nl_free_domain_conv (domain);
00844       _nl_init_domain_conv (domain_file, domain, domainbinding);
00845     }
00846 
00847   if (
00848 # ifdef _LIBC
00849       domain->conv != (__gconv_t) -1
00850 # else
00851 #  if HAVE_ICONV
00852       domain->conv != (iconv_t) -1
00853 #  endif
00854 # endif
00855       )
00856     {
00857       /* We are supposed to do a conversion.  First allocate an
00858         appropriate table with the same structure as the table
00859         of translations in the file, where we can put the pointers
00860         to the converted strings in.
00861         There is a slight complication with plural entries.  They
00862         are represented by consecutive NUL terminated strings.  We
00863         handle this case by converting RESULTLEN bytes, including
00864         NULs.  */
00865 
00866       if (domain->conv_tab == NULL
00867          && ((domain->conv_tab =
00868                (char **) calloc (nstrings + domain->n_sysdep_strings,
00869                                sizeof (char *)))
00870              == NULL))
00871        /* Mark that we didn't succeed allocating a table.  */
00872        domain->conv_tab = (char **) -1;
00873 
00874       if (__builtin_expect (domain->conv_tab == (char **) -1, 0))
00875        /* Nothing we can do, no more memory.  */
00876        goto converted;
00877 
00878       if (domain->conv_tab[act] == NULL)
00879        {
00880          /* We haven't used this string so far, so it is not
00881             translated yet.  Do this now.  */
00882          /* We use a bit more efficient memory handling.
00883             We allocate always larger blocks which get used over
00884             time.  This is faster than many small allocations.   */
00885          __libc_lock_define_initialized (static, lock)
00886 # define INITIAL_BLOCK_SIZE 4080
00887          static unsigned char *freemem;
00888          static size_t freemem_size;
00889 
00890          const unsigned char *inbuf;
00891          unsigned char *outbuf;
00892          int malloc_count;
00893 # ifndef _LIBC
00894          transmem_block_t *transmem_list = NULL;
00895 # endif
00896 
00897          __libc_lock_lock (lock);
00898 
00899          inbuf = (const unsigned char *) result;
00900          outbuf = freemem + sizeof (size_t);
00901 
00902          malloc_count = 0;
00903          while (1)
00904            {
00905              transmem_block_t *newmem;
00906 # ifdef _LIBC
00907              size_t non_reversible;
00908              int res;
00909 
00910              if (freemem_size < sizeof (size_t))
00911               goto resize_freemem;
00912 
00913              res = __gconv (domain->conv,
00914                           &inbuf, inbuf + resultlen,
00915                           &outbuf,
00916                           outbuf + freemem_size - sizeof (size_t),
00917                           &non_reversible);
00918 
00919              if (res == __GCONV_OK || res == __GCONV_EMPTY_INPUT)
00920               break;
00921 
00922              if (res != __GCONV_FULL_OUTPUT)
00923               {
00924                 __libc_lock_unlock (lock);
00925                 goto converted;
00926               }
00927 
00928              inbuf = result;
00929 # else
00930 #  if HAVE_ICONV
00931              const char *inptr = (const char *) inbuf;
00932              size_t inleft = resultlen;
00933              char *outptr = (char *) outbuf;
00934              size_t outleft;
00935 
00936              if (freemem_size < sizeof (size_t))
00937               goto resize_freemem;
00938 
00939              outleft = freemem_size - sizeof (size_t);
00940              if (iconv (domain->conv,
00941                       (ICONV_CONST char **) &inptr, &inleft,
00942                       &outptr, &outleft)
00943                 != (size_t) (-1))
00944               {
00945                 outbuf = (unsigned char *) outptr;
00946                 break;
00947               }
00948              if (errno != E2BIG)
00949               {
00950                 __libc_lock_unlock (lock);
00951                 goto converted;
00952               }
00953 #  endif
00954 # endif
00955 
00956            resize_freemem:
00957              /* We must allocate a new buffer or resize the old one.  */
00958              if (malloc_count > 0)
00959               {
00960                 ++malloc_count;
00961                 freemem_size = malloc_count * INITIAL_BLOCK_SIZE;
00962                 newmem = (transmem_block_t *) realloc (transmem_list,
00963                                                   freemem_size);
00964 # ifdef _LIBC
00965                 if (newmem != NULL)
00966                   transmem_list = transmem_list->next;
00967                 else
00968                   {
00969                     struct transmem_list *old = transmem_list;
00970 
00971                     transmem_list = transmem_list->next;
00972                     free (old);
00973                   }
00974 # endif
00975               }
00976              else
00977               {
00978                 malloc_count = 1;
00979                 freemem_size = INITIAL_BLOCK_SIZE;
00980                 newmem = (transmem_block_t *) malloc (freemem_size);
00981               }
00982              if (__builtin_expect (newmem == NULL, 0))
00983               {
00984                 freemem = NULL;
00985                 freemem_size = 0;
00986                 __libc_lock_unlock (lock);
00987                 goto converted;
00988               }
00989 
00990 # ifdef _LIBC
00991              /* Add the block to the list of blocks we have to free
00992                  at some point.  */
00993              newmem->next = transmem_list;
00994              transmem_list = newmem;
00995 
00996              freemem = newmem->data;
00997              freemem_size -= offsetof (struct transmem_list, data);
00998 # else
00999              transmem_list = newmem;
01000              freemem = newmem;
01001 # endif
01002 
01003              outbuf = freemem + sizeof (size_t);
01004            }
01005 
01006          /* We have now in our buffer a converted string.  Put this
01007             into the table of conversions.  */
01008          *(size_t *) freemem = outbuf - freemem - sizeof (size_t);
01009          domain->conv_tab[act] = (char *) freemem;
01010          /* Shrink freemem, but keep it aligned.  */
01011          freemem_size -= outbuf - freemem;
01012          freemem = outbuf;
01013          freemem += freemem_size & (alignof (size_t) - 1);
01014          freemem_size = freemem_size & ~ (alignof (size_t) - 1);
01015 
01016          __libc_lock_unlock (lock);
01017        }
01018 
01019       /* Now domain->conv_tab[act] contains the translation of all
01020         the plural variants.  */
01021       result = domain->conv_tab[act] + sizeof (size_t);
01022       resultlen = *(size_t *) domain->conv_tab[act];
01023     }
01024 
01025  converted:
01026   /* The result string is converted.  */
01027 
01028 #endif /* _LIBC || HAVE_ICONV */
01029 
01030   *lengthp = resultlen;
01031   return result;
01032 }
01033 
01034 
01035 /* Look up a plural variant.  */
01036 static char *
01037 internal_function
01038 plural_lookup (domain, n, translation, translation_len)
01039      struct loaded_l10nfile *domain;
01040      unsigned long int n;
01041      const char *translation;
01042      size_t translation_len;
01043 {
01044   struct loaded_domain *domaindata = (struct loaded_domain *) domain->data;
01045   unsigned long int index;
01046   const char *p;
01047 
01048   index = plural_eval (domaindata->plural, n);
01049   if (index >= domaindata->nplurals)
01050     /* This should never happen.  It means the plural expression and the
01051        given maximum value do not match.  */
01052     index = 0;
01053 
01054   /* Skip INDEX strings at TRANSLATION.  */
01055   p = translation;
01056   while (index-- > 0)
01057     {
01058 #ifdef _LIBC
01059       p = __rawmemchr (p, '\0');
01060 #else
01061       p = strchr (p, '\0');
01062 #endif
01063       /* And skip over the NUL byte.  */
01064       p++;
01065 
01066       if (p >= translation + translation_len)
01067        /* This should never happen.  It means the plural expression
01068           evaluated to a value larger than the number of variants
01069           available for MSGID1.  */
01070        return (char *) translation;
01071     }
01072   return (char *) p;
01073 }
01074 
01075 #ifndef _LIBC
01076 /* Return string representation of locale CATEGORY.  */
01077 static const char *
01078 internal_function
01079 category_to_name (category)
01080      int category;
01081 {
01082   const char *retval;
01083 
01084   switch (category)
01085   {
01086 #ifdef LC_COLLATE
01087   case LC_COLLATE:
01088     retval = "LC_COLLATE";
01089     break;
01090 #endif
01091 #ifdef LC_CTYPE
01092   case LC_CTYPE:
01093     retval = "LC_CTYPE";
01094     break;
01095 #endif
01096 #ifdef LC_MONETARY
01097   case LC_MONETARY:
01098     retval = "LC_MONETARY";
01099     break;
01100 #endif
01101 #ifdef LC_NUMERIC
01102   case LC_NUMERIC:
01103     retval = "LC_NUMERIC";
01104     break;
01105 #endif
01106 #ifdef LC_TIME
01107   case LC_TIME:
01108     retval = "LC_TIME";
01109     break;
01110 #endif
01111 #ifdef LC_MESSAGES
01112   case LC_MESSAGES:
01113     retval = "LC_MESSAGES";
01114     break;
01115 #endif
01116 #ifdef LC_RESPONSE
01117   case LC_RESPONSE:
01118     retval = "LC_RESPONSE";
01119     break;
01120 #endif
01121 #ifdef LC_ALL
01122   case LC_ALL:
01123     /* This might not make sense but is perhaps better than any other
01124        value.  */
01125     retval = "LC_ALL";
01126     break;
01127 #endif
01128   default:
01129     /* If you have a better idea for a default value let me know.  */
01130     retval = "LC_XXX";
01131   }
01132 
01133   return retval;
01134 }
01135 #endif
01136 
01137 /* Guess value of current locale from value of the environment variables.  */
01138 static const char *
01139 internal_function
01140 guess_category_value (category, categoryname)
01141      int category;
01142      const char *categoryname;
01143 {
01144   const char *language;
01145   const char *retval;
01146 
01147   /* The highest priority value is the `LANGUAGE' environment
01148      variable.  But we don't use the value if the currently selected
01149      locale is the C locale.  This is a GNU extension.  */
01150   language = getenv ("LANGUAGE");
01151   if (language != NULL && language[0] == '\0')
01152     language = NULL;
01153 
01154   /* We have to proceed with the POSIX methods of looking to `LC_ALL',
01155      `LC_xxx', and `LANG'.  On some systems this can be done by the
01156      `setlocale' function itself.  */
01157 #ifdef _LIBC
01158   retval = __current_locale_name (category);
01159 #else
01160   retval = _nl_locale_name (category, categoryname);
01161 #endif
01162 
01163   /* Ignore LANGUAGE if the locale is set to "C" because
01164      1. "C" locale usually uses the ASCII encoding, and most international
01165        messages use non-ASCII characters. These characters get displayed
01166        as question marks (if using glibc's iconv()) or as invalid 8-bit
01167        characters (because other iconv()s refuse to convert most non-ASCII
01168        characters to ASCII). In any case, the output is ugly.
01169      2. The precise output of some programs in the "C" locale is specified
01170        by POSIX and should not depend on environment variables like
01171        "LANGUAGE".  We allow such programs to use gettext().  */
01172   return language != NULL && strcmp (retval, "C") != 0 ? language : retval;
01173 }
01174 
01175 /* @@ begin of epilog @@ */
01176 
01177 /* We don't want libintl.a to depend on any other library.  So we
01178    avoid the non-standard function stpcpy.  In GNU C Library this
01179    function is available, though.  Also allow the symbol HAVE_STPCPY
01180    to be defined.  */
01181 #if !_LIBC && !HAVE_STPCPY
01182 static char *
01183 stpcpy (dest, src)
01184      char *dest;
01185      const char *src;
01186 {
01187   while ((*dest++ = *src++) != '\0')
01188     /* Do nothing. */ ;
01189   return dest - 1;
01190 }
01191 #endif
01192 
01193 #if !_LIBC && !HAVE_MEMPCPY
01194 static void *
01195 mempcpy (dest, src, n)
01196      void *dest;
01197      const void *src;
01198      size_t n;
01199 {
01200   return (void *) ((char *) memcpy (dest, src, n) + n);
01201 }
01202 #endif
01203 
01204 
01205 #ifdef _LIBC
01206 /* If we want to free all resources we have to do some work at
01207    program's end.  */
01208 libc_freeres_fn (free_mem)
01209 {
01210   void *old;
01211 
01212   while (_nl_domain_bindings != NULL)
01213     {
01214       struct binding *oldp = _nl_domain_bindings;
01215       _nl_domain_bindings = _nl_domain_bindings->next;
01216       if (oldp->dirname != INTUSE(_nl_default_dirname))
01217        /* Yes, this is a pointer comparison.  */
01218        free (oldp->dirname);
01219       free (oldp->codeset);
01220       free (oldp);
01221     }
01222 
01223   if (_nl_current_default_domain != _nl_default_default_domain)
01224     /* Yes, again a pointer comparison.  */
01225     free ((char *) _nl_current_default_domain);
01226 
01227   /* Remove the search tree with the known translations.  */
01228   __tdestroy (root, free);
01229   root = NULL;
01230 
01231   while (transmem_list != NULL)
01232     {
01233       old = transmem_list;
01234       transmem_list = transmem_list->next;
01235       free (old);
01236     }
01237 }
01238 #endif