Main Page   Alphabetical List   Data Structures   File List   Data Fields   Globals  

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

Generated on Sun Feb 16 23:39:49 2003 for FreeLCD by doxygen1.2.18