Main Page | Modules | Class Hierarchy | Class List | Directories | File List | Class Members | File Members | Related Pages

dcigettext.c

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

Generated on Tue Dec 20 10:14:20 2005 for vlc-0.8.4a by  doxygen 1.4.2