Back to index

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