diff options
Diffstat (limited to 'intl/dcigettext.c')
-rw-r--r-- | intl/dcigettext.c | 851 |
1 files changed, 540 insertions, 311 deletions
diff --git a/intl/dcigettext.c b/intl/dcigettext.c index 723dd7ef46..7af4a76410 100644 --- a/intl/dcigettext.c +++ b/intl/dcigettext.c @@ -1,20 +1,18 @@ /* Implementation of the internal dcigettext function. Copyright (C) 1995-2014 Free Software Foundation, Inc. - This file is part of the GNU C Library. - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. - The GNU C Library is distributed in the hope that it will be useful, + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, see - <http://www.gnu.org/licenses/>. */ + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* Tell glibc's <string.h> to provide a prototype for mempcpy(). This must come before <config.h> because <config.h> may include @@ -33,14 +31,19 @@ # define alloca __builtin_alloca # define HAVE_ALLOCA 1 #else -# if defined HAVE_ALLOCA_H || defined _LIBC -# include <alloca.h> +# ifdef _MSC_VER +# include <malloc.h> +# define alloca _alloca # else -# ifdef _AIX - #pragma alloca +# if defined HAVE_ALLOCA_H || defined _LIBC +# include <alloca.h> # else -# ifndef alloca +# ifdef _AIX + #pragma alloca +# else +# ifndef alloca char *alloca (); +# endif # endif # endif # endif @@ -64,30 +67,49 @@ extern int errno; #include <locale.h> +#ifdef _LIBC + /* Guess whether integer division by zero raises signal SIGFPE. + Set to 1 only if you know for sure. In case of doubt, set to 0. */ +# if defined __alpha__ || defined __arm__ || defined __i386__ \ + || defined __m68k__ || defined __s390__ +# define INTDIV0_RAISES_SIGFPE 1 +# else +# define INTDIV0_RAISES_SIGFPE 0 +# endif +#endif +#if !INTDIV0_RAISES_SIGFPE +# include <signal.h> +#endif + #if defined HAVE_SYS_PARAM_H || defined _LIBC # include <sys/param.h> #endif +#if !defined _LIBC +# include "localcharset.h" +#endif + #include "gettextP.h" #include "plural-exp.h" #ifdef _LIBC # include <libintl.h> #else +# ifdef IN_LIBGLOCALE +# include <libintl.h> +# endif # include "libgnuintl.h" #endif #include "hash-string.h" -/* Thread safetyness. */ +/* Handle multi-threaded applications. */ #ifdef _LIBC # include <bits/libc-lock.h> +# define gl_rwlock_define_initialized __libc_rwlock_define_initialized +# define gl_rwlock_rdlock __libc_rwlock_rdlock +# define gl_rwlock_wrlock __libc_rwlock_wrlock +# define gl_rwlock_unlock __libc_rwlock_unlock #else -/* Provide dummy implementation if this is outside glibc. */ -# define __libc_lock_define_initialized(CLASS, NAME) -# define __libc_lock_lock(NAME) -# define __libc_lock_unlock(NAME) -# define __libc_rwlock_define_initialized(CLASS, NAME) -# define __libc_rwlock_rdlock(NAME) -# define __libc_rwlock_unlock(NAME) +# include "lock.h" #endif /* Alignment of types. */ @@ -98,16 +120,6 @@ extern int errno; ((int) &((struct { char dummy1; TYPE dummy2; } *) 0)->dummy2) #endif -/* The internal variables in the standalone libintl.a must have different - names than the internal variables in GNU libc, otherwise programs - using libintl.a cannot be linked statically. */ -#if !defined _LIBC -# define _nl_default_default_domain libintl_nl_default_default_domain -# define _nl_current_default_domain libintl_nl_current_default_domain -# define _nl_default_dirname libintl_nl_default_dirname -# define _nl_domain_bindings libintl_nl_domain_bindings -#endif - /* Some compilers, like SunOS4 cc, don't have offsetof in <stddef.h>. */ #ifndef offsetof # define offsetof(type,ident) ((size_t)&(((type*)0)->ident)) @@ -129,16 +141,36 @@ extern int errno; char *getwd (); # define getcwd(buf, max) getwd (buf) # else +# if VMS +# define getcwd(buf, max) (getcwd) (buf, max, 0) +# else char *getcwd (); +# endif # endif # ifndef HAVE_STPCPY -static char *stpcpy PARAMS ((char *dest, const char *src)); +static char *stpcpy (char *dest, const char *src); # endif # ifndef HAVE_MEMPCPY -static void *mempcpy PARAMS ((void *dest, const void *src, size_t n)); +static void *mempcpy (void *dest, const void *src, size_t n); # endif #endif +/* Use a replacement if the system does not provide the `tsearch' function + family. */ +#if defined HAVE_TSEARCH || defined _LIBC +# include <search.h> +#else +# define tsearch libintl_tsearch +# define tfind libintl_tfind +# define tdelete libintl_tdelete +# define twalk libintl_twalk +# include "tsearch.h" +#endif + +#ifdef _LIBC +# define tsearch __tsearch +#endif + /* Amount to increase buffer size by in each try. */ #define PATH_INCR 32 @@ -171,8 +203,30 @@ static void *mempcpy PARAMS ((void *dest, const void *src, size_t n)); # define PATH_MAX _POSIX_PATH_MAX #endif +/* Pathname support. + ISSLASH(C) tests whether C is a directory separator character. + IS_ABSOLUTE_PATH(P) tests whether P is an absolute path. If it is not, + it may be concatenated to a directory pathname. + IS_PATH_WITH_DIR(P) tests whether P contains a directory specification. + */ +#if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__ + /* Win32, Cygwin, OS/2, DOS */ +# define ISSLASH(C) ((C) == '/' || (C) == '\\') +# define HAS_DEVICE(P) \ + ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \ + && (P)[1] == ':') +# define IS_ABSOLUTE_PATH(P) (ISSLASH ((P)[0]) || HAS_DEVICE (P)) +# define IS_PATH_WITH_DIR(P) \ + (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P)) +#else + /* Unix */ +# define ISSLASH(C) ((C) == '/') +# define IS_ABSOLUTE_PATH(P) ISSLASH ((P)[0]) +# define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL) +#endif + /* Whether to support different locales in different threads. */ -#if defined _LIBC || HAVE_NL_LOCALE_NAME +#if defined _LIBC || HAVE_USELOCALE || defined IN_LIBGLOCALE # define HAVE_PER_THREAD_LOCALE #endif @@ -191,6 +245,11 @@ struct known_translation_t const char *localename; #endif +#ifdef IN_LIBGLOCALE + /* The character encoding. */ + const char *encoding; +#endif + /* State of the catalog counter at the point the string was found. */ int counter; @@ -210,23 +269,14 @@ struct known_translation_t msgid; }; -/* Root of the search tree with known translations. We can use this - only if the system provides the `tsearch' function family. */ -#if defined HAVE_TSEARCH || defined _LIBC -# include <search.h> +gl_rwlock_define_initialized (static, tree_lock) +/* Root of the search tree with known translations. */ static void *root; -# ifdef _LIBC -# define tsearch __tsearch -# endif - /* Function to compare two entries in the table of known translations. */ -static int transcmp PARAMS ((const void *p1, const void *p2)); static int -transcmp (p1, p2) - const void *p1; - const void *p2; +transcmp (const void *p1, const void *p2) { const struct known_translation_t *s1; const struct known_translation_t *s2; @@ -246,59 +296,83 @@ transcmp (p1, p2) result = strcmp (s1->localename, s2->localename); if (result == 0) #endif - /* We compare the category last (though this is the cheapest - operation) since it is hopefully always the same (namely - LC_MESSAGES). */ - result = s1->category - s2->category; + { +#ifdef IN_LIBGLOCALE + result = strcmp (s1->encoding, s2->encoding); + if (result == 0) +#endif + /* We compare the category last (though this is the cheapest + operation) since it is hopefully always the same (namely + LC_MESSAGES). */ + result = s1->category - s2->category; + } } } return result; } -#endif /* Name of the default domain used for gettext(3) prior any call to textdomain(3). The default value for this is "messages". */ const char _nl_default_default_domain[] attribute_hidden = "messages"; +#ifndef IN_LIBGLOCALE /* Value used as the default domain for gettext(3). */ const char *_nl_current_default_domain attribute_hidden = _nl_default_default_domain; +#endif /* Contains the default location of the message catalogs. */ - -#ifdef _LIBC +#if defined __EMX__ +extern const char _nl_default_dirname[]; +#else +# ifdef _LIBC extern const char _nl_default_dirname[]; libc_hidden_proto (_nl_default_dirname) -#endif +# endif const char _nl_default_dirname[] = LOCALEDIR; -#ifdef _LIBC +# ifdef _LIBC libc_hidden_data_def (_nl_default_dirname) +# endif #endif +#ifndef IN_LIBGLOCALE /* List with bindings of specific domains created by bindtextdomain() calls. */ struct binding *_nl_domain_bindings; +#endif /* Prototypes for local functions. */ -static char *plural_lookup PARAMS ((struct loaded_l10nfile *domain, - unsigned long int n, - const char *translation, - size_t translation_len)) +static char *plural_lookup (struct loaded_l10nfile *domain, + unsigned long int n, + const char *translation, size_t translation_len) + internal_function; + +#ifdef IN_LIBGLOCALE +static const char *guess_category_value (int category, + const char *categoryname, + const char *localename) internal_function; -static const char *guess_category_value PARAMS ((int category, - const char *categoryname)) +#else +static const char *guess_category_value (int category, + const char *categoryname) internal_function; +#endif + #ifdef _LIBC # include "../locale/localeinfo.h" # define category_to_name(category) \ _nl_category_names.str + _nl_category_name_idxs[category] #else -static const char *category_to_name PARAMS ((int category)) internal_function; +static const char *category_to_name (int category) internal_function; +#endif +#if (defined _LIBC || HAVE_ICONV) && !defined IN_LIBGLOCALE +static const char *get_output_charset (struct binding *domainbinding) + internal_function; #endif -/* For those loosing systems which don't have `alloca' we have to add +/* For those losing systems which don't have `alloca' we have to add some additional code emulating it. */ #ifdef HAVE_ALLOCA /* Nothing has to be done. */ @@ -348,10 +422,6 @@ static struct transmem_list *transmem_list; #else typedef unsigned char transmem_block_t; #endif -#if defined _LIBC || HAVE_ICONV -static const char *get_output_charset PARAMS ((struct binding *domainbinding)) - internal_function; -#endif /* Names for the libintl functions are a problem. They must not clash @@ -365,9 +435,7 @@ static const char *get_output_charset PARAMS ((struct binding *domainbinding)) #endif /* Lock variable to protect the global data in the gettext implementation. */ -#ifdef _LIBC -__libc_rwlock_define_initialized (, _nl_state_lock attribute_hidden) -#endif +gl_rwlock_define_initialized (, _nl_state_lock attribute_hidden) /* Checking whether the binaries runs SUID must be done and glibc provides easier methods therefore we make a difference here. */ @@ -400,19 +468,23 @@ static int enable_secure; #endif /* Get the function to evaluate the plural expression. */ -#include "plural-eval.c" +#include "eval-plural.h" /* Look up MSGID in the DOMAINNAME message catalog for the current CATEGORY locale and, if PLURAL is nonzero, search over string depending on the plural form determined by N. */ +#ifdef IN_LIBGLOCALE char * -DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category) - const char *domainname; - const char *msgid1; - const char *msgid2; - int plural; - unsigned long int n; - int category; +gl_dcigettext (const char *domainname, + const char *msgid1, const char *msgid2, + int plural, unsigned long int n, + int category, + const char *localename, const char *encoding) +#else +char * +DCIGETTEXT (const char *domainname, const char *msgid1, const char *msgid2, + int plural, unsigned long int n, int category) +#endif { #ifndef HAVE_ALLOCA struct block_list *block_list = NULL; @@ -421,17 +493,16 @@ DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category) struct binding *binding; const char *categoryname; const char *categoryvalue; - char *dirname, *xdomainname; + const char *dirname; + char *xdomainname; char *single_locale; char *retval; size_t retlen; int saved_errno; -#if defined HAVE_TSEARCH || defined _LIBC struct known_translation_t search; struct known_translation_t **foundp = NULL; -# ifdef HAVE_PER_THREAD_LOCALE +#if defined HAVE_PER_THREAD_LOCALE && !defined IN_LIBGLOCALE const char *localename; -# endif #endif size_t domainname_len; @@ -448,12 +519,15 @@ DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category) : n == 1 ? (char *) msgid1 : (char *) msgid2); #endif + /* Preserve the `errno' value. */ + saved_errno = errno; + #ifdef _LIBC __libc_rwlock_define (extern, __libc_setlocale_lock attribute_hidden) __libc_rwlock_rdlock (__libc_setlocale_lock); #endif - __libc_rwlock_rdlock (_nl_state_lock); + gl_rwlock_rdlock (_nl_state_lock); /* If DOMAINNAME is NULL, we are interested in the default domain. If CATEGORY is not LC_MESSAGES this might not make much sense but the @@ -461,28 +535,42 @@ DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category) if (domainname == NULL) domainname = _nl_current_default_domain; -#if defined HAVE_TSEARCH || defined _LIBC + /* OS/2 specific: backward compatibility with older libintl versions */ +#ifdef LC_MESSAGES_COMPAT + if (category == LC_MESSAGES_COMPAT) + category = LC_MESSAGES; +#endif + /* Try to find the translation among those which we found at some time. */ search.domain = NULL; search.msgid.ptr = msgid1; search.domainname = domainname; search.category = category; -# ifdef HAVE_PER_THREAD_LOCALE +#ifdef HAVE_PER_THREAD_LOCALE +# ifndef IN_LIBGLOCALE # ifdef _LIBC localename = strdupa (__current_locale_name (category)); +# else + categoryname = category_to_name (category); +# define CATEGORYNAME_INITIALIZED + localename = _nl_locale_name_thread_unsafe (category, categoryname); + if (localename == NULL) + localename = ""; # endif +# endif search.localename = localename; +# ifdef IN_LIBGLOCALE + search.encoding = encoding; # endif /* Since tfind/tsearch manage a balanced tree, concurrent tfind and tsearch calls can be fatal. */ - __libc_rwlock_define_initialized (static, tree_lock); - __libc_rwlock_rdlock (tree_lock); + gl_rwlock_rdlock (tree_lock); foundp = (struct known_translation_t **) tfind (&search, &root, transcmp); - __libc_rwlock_unlock (tree_lock); + gl_rwlock_unlock (tree_lock); if (foundp != NULL && (*foundp)->counter == _nl_msg_cat_cntr) { @@ -493,21 +581,25 @@ DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category) else retval = (char *) (*foundp)->translation; + gl_rwlock_unlock (_nl_state_lock); # ifdef _LIBC __libc_rwlock_unlock (__libc_setlocale_lock); # endif - __libc_rwlock_unlock (_nl_state_lock); + __set_errno (saved_errno); return retval; } #endif - /* Preserve the `errno' value. */ - saved_errno = errno; - /* See whether this is a SUID binary or not. */ DETERMINE_SECURE; /* First find matching binding. */ +#ifdef IN_LIBGLOCALE + /* We can use a trivial binding, since _nl_find_msg will ignore it anyway, + and _nl_load_domain and _nl_find_domain just pass it through. */ + binding = NULL; + dirname = bindtextdomain (domainname, NULL); +#else for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next) { int compare = strcmp (domainname, binding->domainname); @@ -523,50 +615,65 @@ DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category) } if (binding == NULL) - dirname = (char *) _nl_default_dirname; - else if (binding->dirname[0] == '/') - dirname = binding->dirname; + dirname = _nl_default_dirname; else { - /* We have a relative path. Make it absolute now. */ - size_t dirname_len = strlen (binding->dirname) + 1; - size_t path_max; - char *ret; + dirname = binding->dirname; +#endif + if (!IS_ABSOLUTE_PATH (dirname)) + { + /* We have a relative path. Make it absolute now. */ + size_t dirname_len = strlen (dirname) + 1; + size_t path_max; + char *resolved_dirname; + char *ret; - path_max = (unsigned int) PATH_MAX; - path_max += 2; /* The getcwd docs say to do this. */ + path_max = (unsigned int) PATH_MAX; + path_max += 2; /* The getcwd docs say to do this. */ - for (;;) - { - dirname = (char *) alloca (path_max + dirname_len); - ADD_BLOCK (block_list, dirname); + for (;;) + { + resolved_dirname = (char *) alloca (path_max + dirname_len); + ADD_BLOCK (block_list, tmp_dirname); - __set_errno (0); - ret = getcwd (dirname, path_max); - if (ret != NULL || errno != ERANGE) - break; + __set_errno (0); + ret = getcwd (resolved_dirname, path_max); + if (ret != NULL || errno != ERANGE) + break; - path_max += path_max / 2; - path_max += PATH_INCR; - } + path_max += path_max / 2; + path_max += PATH_INCR; + } - if (ret == NULL) - goto no_translation; + if (ret == NULL) + /* We cannot get the current working directory. Don't signal an + error but simply return the default string. */ + goto return_untranslated; - stpcpy (stpcpy (strchr (dirname, '\0'), "/"), binding->dirname); + stpcpy (stpcpy (strchr (resolved_dirname, '\0'), "/"), dirname); + dirname = resolved_dirname; + } +#ifndef IN_LIBGLOCALE } +#endif /* Now determine the symbolic name of CATEGORY and its value. */ +#ifndef CATEGORYNAME_INITIALIZED categoryname = category_to_name (category); +#endif +#ifdef IN_LIBGLOCALE + categoryvalue = guess_category_value (category, categoryname, localename); +#else categoryvalue = guess_category_value (category, categoryname); +#endif domainname_len = strlen (domainname); xdomainname = (char *) alloca (strlen (categoryname) + domainname_len + 5); ADD_BLOCK (block_list, xdomainname); - stpcpy (mempcpy (stpcpy (stpcpy (xdomainname, categoryname), "/"), - domainname, domainname_len), + stpcpy ((char *) mempcpy (stpcpy (stpcpy (xdomainname, categoryname), "/"), + domainname, domainname_len), ".mo"); /* Creating working area. */ @@ -599,7 +706,7 @@ DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category) /* When this is a SUID binary we must not allow accessing files outside the dedicated directories. */ - if (ENABLE_SECURE && strchr (single_locale, '/') != NULL) + if (ENABLE_SECURE && IS_PATH_WITH_DIR (single_locale)) /* Ingore this entry. */ continue; } @@ -608,18 +715,7 @@ DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category) domain. Return the MSGID. */ if (strcmp (single_locale, "C") == 0 || strcmp (single_locale, "POSIX") == 0) - { - no_translation: - FREE_BLOCKS (block_list); - __libc_rwlock_unlock (__libc_setlocale_lock); - __libc_rwlock_unlock (_nl_state_lock); - __set_errno (saved_errno); - return (plural == 0 - ? (char *) msgid1 - /* Use the Germanic plural rule. */ - : n == 1 ? (char *) msgid1 : (char *) msgid2); - } - + break; /* Find structure describing the message catalog matching the DOMAINNAME and CATEGORY. */ @@ -627,7 +723,11 @@ DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category) if (domain != NULL) { +#if defined IN_LIBGLOCALE + retval = _nl_find_msg (domain, binding, encoding, msgid1, &retlen); +#else retval = _nl_find_msg (domain, binding, msgid1, 1, &retlen); +#endif if (retval == NULL) { @@ -635,13 +735,18 @@ DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category) for (cnt = 0; domain->successor[cnt] != NULL; ++cnt) { +#if defined IN_LIBGLOCALE + retval = _nl_find_msg (domain->successor[cnt], binding, + encoding, msgid1, &retlen); +#else retval = _nl_find_msg (domain->successor[cnt], binding, msgid1, 1, &retlen); +#endif /* Resource problems are not fatal, instead we return no translation. */ - if (__glibc_unlikely (retval == (char *) -1)) - goto no_translation; + if (__builtin_expect (retval == (char *) -1, 0)) + goto return_untranslated; if (retval != NULL) { @@ -654,15 +759,14 @@ DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category) /* Returning -1 means that some resource problem exists (likely memory) and that the strings could not be converted. Return the original strings. */ - if (__glibc_unlikely (retval == (char *) -1)) - goto no_translation; + if (__builtin_expect (retval == (char *) -1, 0)) + break; if (retval != NULL) { /* Found the translation of MSGID1 in domain DOMAIN: starting at RETVAL, RETLEN bytes. */ FREE_BLOCKS (block_list); -#if defined HAVE_TSEARCH || defined _LIBC if (foundp == NULL) { /* Create a new entry and add it to the search tree. */ @@ -673,41 +777,45 @@ DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category) msgid_len = strlen (msgid1) + 1; size = offsetof (struct known_translation_t, msgid) + msgid_len + domainname_len + 1; -# ifdef HAVE_PER_THREAD_LOCALE +#ifdef HAVE_PER_THREAD_LOCALE size += strlen (localename) + 1; -# endif +#endif newp = (struct known_translation_t *) malloc (size); if (newp != NULL) { char *new_domainname; -# ifdef HAVE_PER_THREAD_LOCALE +#ifdef HAVE_PER_THREAD_LOCALE char *new_localename; -# endif +#endif new_domainname = - mempcpy (newp->msgid.appended, msgid1, msgid_len); + (char *) mempcpy (newp->msgid.appended, msgid1, + msgid_len); memcpy (new_domainname, domainname, domainname_len + 1); -# ifdef HAVE_PER_THREAD_LOCALE +#ifdef HAVE_PER_THREAD_LOCALE new_localename = new_domainname + domainname_len + 1; strcpy (new_localename, localename); -# endif +#endif newp->domainname = new_domainname; newp->category = category; -# ifdef HAVE_PER_THREAD_LOCALE +#ifdef HAVE_PER_THREAD_LOCALE newp->localename = new_localename; -# endif +#endif +#ifdef IN_LIBGLOCALE + newp->encoding = encoding; +#endif newp->counter = _nl_msg_cat_cntr; newp->domain = domain; newp->translation = retval; newp->translation_length = retlen; - __libc_rwlock_wrlock (tree_lock); + gl_rwlock_wrlock (tree_lock); /* Insert the entry in the search tree. */ foundp = (struct known_translation_t **) tsearch (newp, &root, transcmp); - __libc_rwlock_unlock (tree_lock); + gl_rwlock_unlock (tree_lock); if (foundp == NULL || __builtin_expect (*foundp != newp, 0)) @@ -723,31 +831,68 @@ DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category) (*foundp)->translation = retval; (*foundp)->translation_length = retlen; } -#endif + __set_errno (saved_errno); /* Now deal with plural. */ if (plural) retval = plural_lookup (domain, n, retval, retlen); + gl_rwlock_unlock (_nl_state_lock); +#ifdef _LIBC __libc_rwlock_unlock (__libc_setlocale_lock); - __libc_rwlock_unlock (_nl_state_lock); +#endif return retval; } } } - /* NOTREACHED */ + + return_untranslated: + /* Return the untranslated MSGID. */ + FREE_BLOCKS (block_list); + gl_rwlock_unlock (_nl_state_lock); +#ifdef _LIBC + __libc_rwlock_unlock (__libc_setlocale_lock); +#endif +#ifndef _LIBC + if (!ENABLE_SECURE) + { + extern void _nl_log_untranslated (const char *logfilename, + const char *domainname, + const char *msgid1, const char *msgid2, + int plural); + const char *logfilename = getenv ("GETTEXT_LOG_UNTRANSLATED"); + + if (logfilename != NULL && logfilename[0] != '\0') + _nl_log_untranslated (logfilename, domainname, msgid1, msgid2, plural); + } +#endif + __set_errno (saved_errno); + return (plural == 0 + ? (char *) msgid1 + /* Use the Germanic plural rule. */ + : n == 1 ? (char *) msgid1 : (char *) msgid2); } +/* Look up the translation of msgid within DOMAIN_FILE and DOMAINBINDING. + Return it if found. Return NULL if not found or in case of a conversion + failure (problem in the particular message catalog). Return (char *) -1 + in case of a memory allocation failure during conversion (only if + ENCODING != NULL resp. CONVERT == true). */ char * internal_function -_nl_find_msg (domain_file, domainbinding, msgid, convert, lengthp) - struct loaded_l10nfile *domain_file; - struct binding *domainbinding; - const char *msgid; - int convert; - size_t *lengthp; +#ifdef IN_LIBGLOCALE +_nl_find_msg (struct loaded_l10nfile *domain_file, + struct binding *domainbinding, const char *encoding, + const char *msgid, + size_t *lengthp) +#else +_nl_find_msg (struct loaded_l10nfile *domain_file, + struct binding *domainbinding, + const char *msgid, int convert, + size_t *lengthp) +#endif { struct loaded_domain *domain; nls_uint32 nstrings; @@ -853,19 +998,27 @@ _nl_find_msg (domain_file, domainbinding, msgid, convert, lengthp) } #if defined _LIBC || HAVE_ICONV +# ifdef IN_LIBGLOCALE + if (encoding != NULL) +# else if (convert) +# endif { /* We are supposed to do a conversion. */ +# ifndef IN_LIBGLOCALE const char *encoding = get_output_charset (domainbinding); +# endif + size_t nconversions; + struct converted_domain *convd; + size_t i; /* Protect against reallocation of the table. */ - __libc_rwlock_rdlock (domain->conversions_lock); + gl_rwlock_rdlock (domain->conversions_lock); /* Search whether a table with converted translations for this encoding has already been allocated. */ - size_t nconversions = domain->nconversions; - struct converted_domain *convd = NULL; - size_t i; + nconversions = domain->nconversions; + convd = NULL; for (i = nconversions; i > 0; ) { @@ -877,12 +1030,12 @@ _nl_find_msg (domain_file, domainbinding, msgid, convert, lengthp) } } - __libc_rwlock_unlock (domain->conversions_lock); + gl_rwlock_unlock (domain->conversions_lock); if (convd == NULL) { /* We have to allocate a new conversions table. */ - __libc_rwlock_wrlock (domain->conversions_lock); + gl_rwlock_wrlock (domain->conversions_lock); nconversions = domain->nconversions; /* Maybe in the meantime somebody added the translation. @@ -897,140 +1050,151 @@ _nl_find_msg (domain_file, domainbinding, msgid, convert, lengthp) } } - /* Allocate a table for the converted translations for this - encoding. */ - struct converted_domain *new_conversions = - (struct converted_domain *) - realloc (domain->conversions, - (nconversions + 1) * sizeof (struct converted_domain)); + { + /* Allocate a table for the converted translations for this + encoding. */ + struct converted_domain *new_conversions = + (struct converted_domain *) + (domain->conversions != NULL + ? realloc (domain->conversions, + (nconversions + 1) * sizeof (struct converted_domain)) + : malloc ((nconversions + 1) * sizeof (struct converted_domain))); + + if (__builtin_expect (new_conversions == NULL, 0)) + { + /* Nothing we can do, no more memory. We cannot use the + translation because it might be encoded incorrectly. */ + unlock_fail: + gl_rwlock_unlock (domain->conversions_lock); + return (char *) -1; + } - if (__glibc_unlikely (new_conversions == NULL)) - { + domain->conversions = new_conversions; + + /* Copy the 'encoding' string to permanent storage. */ + encoding = strdup (encoding); + if (__builtin_expect (encoding == NULL, 0)) /* Nothing we can do, no more memory. We cannot use the translation because it might be encoded incorrectly. */ - unlock_fail: - __libc_rwlock_unlock (domain->conversions_lock); - return (char *) -1; - } - - domain->conversions = new_conversions; - - /* Copy the 'encoding' string to permanent storage. */ - encoding = strdup (encoding); - if (__glibc_unlikely (encoding == NULL)) - /* Nothing we can do, no more memory. We cannot use the - translation because it might be encoded incorrectly. */ - goto unlock_fail; + goto unlock_fail; - convd = &new_conversions[nconversions]; - convd->encoding = encoding; + convd = &new_conversions[nconversions]; + convd->encoding = encoding; - /* Find out about the character set the file is encoded with. - This can be found (in textual form) in the entry "". If this - entry does not exist or if this does not contain the 'charset=' - information, we will assume the charset matches the one the - current locale and we don't have to perform any conversion. */ + /* Find out about the character set the file is encoded with. + This can be found (in textual form) in the entry "". If this + entry does not exist or if this does not contain the 'charset=' + information, we will assume the charset matches the one the + current locale and we don't have to perform any conversion. */ # ifdef _LIBC - convd->conv = (__gconv_t) -1; + convd->conv = (__gconv_t) -1; # else # if HAVE_ICONV - convd->conv = (iconv_t) -1; + convd->conv = (iconv_t) -1; # endif # endif - { - char *nullentry; - size_t nullentrylen; - - /* Get the header entry. This is a recursion, but it doesn't - reallocate domain->conversions because we pass convert = 0. */ - nullentry = - _nl_find_msg (domain_file, domainbinding, "", 0, &nullentrylen); + { + char *nullentry; + size_t nullentrylen; + + /* Get the header entry. This is a recursion, but it doesn't + reallocate domain->conversions because we pass + encoding = NULL or convert = 0, respectively. */ + nullentry = +# ifdef IN_LIBGLOCALE + _nl_find_msg (domain_file, domainbinding, NULL, "", + &nullentrylen); +# else + _nl_find_msg (domain_file, domainbinding, "", 0, &nullentrylen); +# endif - /* Resource problems are fatal. If we continue onwards we will - only attempt to calloc a new conv_tab and fail later. */ - if (__glibc_unlikely (nullentry == (char *) -1)) - return (char *) -1; + /* Resource problems are fatal. If we continue onwards we will + only attempt to calloc a new conv_tab and fail later. */ + if (__builtin_expect (nullentry == (char *) -1, 0)) + return (char *) -1; - if (nullentry != NULL) - { - const char *charsetstr; + if (nullentry != NULL) + { + const char *charsetstr; - charsetstr = strstr (nullentry, "charset="); - if (charsetstr != NULL) - { - size_t len; - char *charset; - const char *outcharset; + charsetstr = strstr (nullentry, "charset="); + if (charsetstr != NULL) + { + size_t len; + char *charset; + const char *outcharset; - charsetstr += strlen ("charset="); - len = strcspn (charsetstr, " \t\n"); + charsetstr += strlen ("charset="); + len = strcspn (charsetstr, " \t\n"); - charset = (char *) alloca (len + 1); + charset = (char *) alloca (len + 1); # if defined _LIBC || HAVE_MEMPCPY - *((char *) mempcpy (charset, charsetstr, len)) = '\0'; + *((char *) mempcpy (charset, charsetstr, len)) = '\0'; # else - memcpy (charset, charsetstr, len); - charset[len] = '\0'; + memcpy (charset, charsetstr, len); + charset[len] = '\0'; # endif - outcharset = encoding; + outcharset = encoding; # ifdef _LIBC - /* We always want to use transliteration. */ - outcharset = norm_add_slashes (outcharset, "TRANSLIT"); - charset = norm_add_slashes (charset, ""); - int r = __gconv_open (outcharset, charset, &convd->conv, - GCONV_AVOID_NOCONV); - if (__glibc_unlikely (r != __GCONV_OK)) - { - /* If the output encoding is the same there is - nothing to do. Otherwise do not use the - translation at all. */ - if (__glibc_likely (r != __GCONV_NULCONV)) - { - __libc_rwlock_unlock (domain->conversions_lock); - free ((char *) encoding); - return NULL; - } - - convd->conv = (__gconv_t) -1; - } + /* We always want to use transliteration. */ + outcharset = norm_add_slashes (outcharset, "TRANSLIT"); + charset = norm_add_slashes (charset, ""); + int r = __gconv_open (outcharset, charset, &convd->conv, + GCONV_AVOID_NOCONV); + if (__builtin_expect (r != __GCONV_OK, 0)) + { + /* If the output encoding is the same there is + nothing to do. Otherwise do not use the + translation at all. */ + if (__builtin_expect (r != __GCONV_NULCONV, 1)) + { + gl_rwlock_unlock (domain->conversions_lock); + free ((char *) encoding); + return NULL; + } + + convd->conv = (__gconv_t) -1; + } # else # if HAVE_ICONV - /* When using GNU libc >= 2.2 or GNU libiconv >= 1.5, - we want to use transliteration. */ -# if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2) || __GLIBC__ > 2 \ + /* When using GNU libc >= 2.2 or GNU libiconv >= 1.5, + we want to use transliteration. */ +# if (((__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2) || __GLIBC__ > 2) \ + && !defined __UCLIBC__) \ || _LIBICONV_VERSION >= 0x0105 - if (strchr (outcharset, '/') == NULL) - { - char *tmp; + if (strchr (outcharset, '/') == NULL) + { + char *tmp; - len = strlen (outcharset); - tmp = (char *) alloca (len + 10 + 1); - memcpy (tmp, outcharset, len); - memcpy (tmp + len, "//TRANSLIT", 10 + 1); - outcharset = tmp; + len = strlen (outcharset); + tmp = (char *) alloca (len + 10 + 1); + memcpy (tmp, outcharset, len); + memcpy (tmp + len, "//TRANSLIT", 10 + 1); + outcharset = tmp; - convd->conv = iconv_open (outcharset, charset); + convd->conv = iconv_open (outcharset, charset); - freea (outcharset); - } - else + freea (outcharset); + } + else # endif - convd->conv = iconv_open (outcharset, charset); + convd->conv = iconv_open (outcharset, charset); # endif # endif - freea (charset); - } - } + freea (charset); + } + } + } + convd->conv_tab = NULL; + /* Here domain->conversions is still == new_conversions. */ + domain->nconversions++; } - convd->conv_tab = NULL; - /* Here domain->conversions is still == new_conversions. */ - domain->nconversions++; found_convd: - __libc_rwlock_unlock (domain->conversions_lock); + gl_rwlock_unlock (domain->conversions_lock); } if ( @@ -1043,7 +1207,6 @@ _nl_find_msg (domain_file, domainbinding, msgid, convert, lengthp) # endif ) { - __libc_lock_define_initialized (static, lock) /* We are supposed to do a conversion. First allocate an appropriate table with the same structure as the table of translations in the file, where we can put the pointers @@ -1053,14 +1216,22 @@ _nl_find_msg (domain_file, domainbinding, msgid, convert, lengthp) handle this case by converting RESULTLEN bytes, including NULs. */ - if (__glibc_unlikely (convd->conv_tab == NULL)) + /* This lock primarily protects the memory management variables + freemem, freemem_size. It also protects write accesses to + convd->conv_tab. It's not worth using a separate lock (such + as domain->conversions_lock) for this purpose, because when + modifying convd->conv_tab, we also need to lock freemem, + freemem_size for most of the time. */ + __libc_lock_define_initialized (static, lock) + + if (__builtin_expect (convd->conv_tab == NULL, 0)) { __libc_lock_lock (lock); if (convd->conv_tab == NULL) { - convd->conv_tab - = calloc (nstrings + domain->n_sysdep_strings, - sizeof (char *)); + convd->conv_tab = + (char **) calloc (nstrings + domain->n_sysdep_strings, + sizeof (char *)); if (convd->conv_tab != NULL) goto not_translated_yet; /* Mark that we didn't succeed allocating a table. */ @@ -1069,16 +1240,13 @@ _nl_find_msg (domain_file, domainbinding, msgid, convert, lengthp) __libc_lock_unlock (lock); } - if (__glibc_unlikely (convd->conv_tab == (char **) -1)) + if (__builtin_expect (convd->conv_tab == (char **) -1, 0)) /* Nothing we can do, no more memory. We cannot use the translation because it might be encoded incorrectly. */ return (char *) -1; if (convd->conv_tab[act] == NULL) { - __libc_lock_lock (lock); - not_translated_yet:; - /* We haven't used this string so far, so it is not translated yet. Do this now. */ /* We use a bit more efficient memory handling. @@ -1092,11 +1260,17 @@ _nl_find_msg (domain_file, domainbinding, msgid, convert, lengthp) unsigned char *outbuf; int malloc_count; # ifndef _LIBC - transmem_block_t *transmem_list = NULL; + transmem_block_t *transmem_list; # endif + __libc_lock_lock (lock); + not_translated_yet: + inbuf = (const unsigned char *) result; outbuf = freemem + sizeof (size_t); +# ifndef _LIBC + transmem_list = NULL; +# endif malloc_count = 0; while (1) @@ -1190,7 +1364,7 @@ _nl_find_msg (domain_file, domainbinding, msgid, convert, lengthp) /* Fall through and return -1. */ # endif } - if (__glibc_unlikely (newmem == NULL)) + if (__builtin_expect (newmem == NULL, 0)) { freemem = NULL; freemem_size = 0; @@ -1241,11 +1415,8 @@ _nl_find_msg (domain_file, domainbinding, msgid, convert, lengthp) /* Look up a plural variant. */ static char * internal_function -plural_lookup (domain, n, translation, translation_len) - struct loaded_l10nfile *domain; - unsigned long int n; - const char *translation; - size_t translation_len; +plural_lookup (struct loaded_l10nfile *domain, unsigned long int n, + const char *translation, size_t translation_len) { struct loaded_domain *domaindata = (struct loaded_domain *) domain->data; unsigned long int index; @@ -1282,8 +1453,7 @@ plural_lookup (domain, n, translation, translation_len) /* Return string representation of locale CATEGORY. */ static const char * internal_function -category_to_name (category) - int category; +category_to_name (int category) { const char *retval; @@ -1340,41 +1510,102 @@ category_to_name (category) } #endif -/* Guess value of current locale from value of the environment variables. */ +/* Guess value of current locale from value of the environment variables + or system-dependent defaults. */ static const char * internal_function -guess_category_value (category, categoryname) - int category; - const char *categoryname; +#ifdef IN_LIBGLOCALE +guess_category_value (int category, const char *categoryname, + const char *locale) + +#else +guess_category_value (int category, const char *categoryname) +#endif { const char *language; - const char *retval; - - /* The highest priority value is the `LANGUAGE' environment - variable. But we don't use the value if the currently selected - locale is the C locale. This is a GNU extension. */ - language = getenv ("LANGUAGE"); - if (language != NULL && language[0] == '\0') - language = NULL; +#ifndef IN_LIBGLOCALE + const char *locale; +# ifndef _LIBC + const char *language_default; + int locale_defaulted; +# endif +#endif - /* We have to proceed with the POSIX methods of looking to `LC_ALL', + /* We use the settings in the following order: + 1. The value of the environment variable 'LANGUAGE'. This is a GNU + extension. Its value can be a colon-separated list of locale names. + 2. The value of the environment variable 'LC_ALL', 'LC_xxx', or 'LANG'. + More precisely, the first among these that is set to a non-empty value. + This is how POSIX specifies it. The value is a single locale name. + 3. A system-dependent preference list of languages. Its value can be a + colon-separated list of locale names. + 4. A system-dependent default locale name. + This way: + - System-dependent settings can be overridden by environment variables. + - If the system provides both a list of languages and a default locale, + the former is used. */ + +#ifndef IN_LIBGLOCALE + /* Fetch the locale name, through the POSIX method of looking to `LC_ALL', `LC_xxx', and `LANG'. On some systems this can be done by the `setlocale' function itself. */ -#ifdef _LIBC - retval = __current_locale_name (category); -#else - retval = _nl_locale_name (category, categoryname); +# ifdef _LIBC + locale = __current_locale_name (category); +# else + locale_defaulted = 0; +# if HAVE_USELOCALE + locale = _nl_locale_name_thread_unsafe (category, categoryname); + if (locale == NULL) +# endif + { + locale = _nl_locale_name_posix (category, categoryname); + if (locale == NULL) + { + locale = _nl_locale_name_default (); + locale_defaulted = 1; + } + } +# endif #endif - return language != NULL && strcmp (retval, "C") != 0 ? language : retval; + /* Ignore LANGUAGE and its system-dependent analogon if the locale is set + to "C" because + 1. "C" locale usually uses the ASCII encoding, and most international + messages use non-ASCII characters. These characters get displayed + as question marks (if using glibc's iconv()) or as invalid 8-bit + characters (because other iconv()s refuse to convert most non-ASCII + characters to ASCII). In any case, the output is ugly. + 2. The precise output of some programs in the "C" locale is specified + by POSIX and should not depend on environment variables like + "LANGUAGE" or system-dependent information. We allow such programs + to use gettext(). */ + if (strcmp (locale, "C") == 0) + return locale; + + /* The highest priority value is the value of the 'LANGUAGE' environment + variable. */ + language = getenv ("LANGUAGE"); + if (language != NULL && language[0] != '\0') + return language; +#if !defined IN_LIBGLOCALE && !defined _LIBC + /* The next priority value is the locale name, if not defaulted. */ + if (locale_defaulted) + { + /* The next priority value is the default language preferences list. */ + language_default = _nl_language_preferences_default (); + if (language_default != NULL) + return language_default; + } + /* The least priority value is the locale name, if defaulted. */ +#endif + return locale; } -#if defined _LIBC || HAVE_ICONV +#if (defined _LIBC || HAVE_ICONV) && !defined IN_LIBGLOCALE /* Returns the output charset. */ static const char * internal_function -get_output_charset (domainbinding) - struct binding *domainbinding; +get_output_charset (struct binding *domainbinding) { /* The output charset should normally be determined by the locale. But sometimes the locale is not used or not correctly set up, so we provide @@ -1415,7 +1646,6 @@ get_output_charset (domainbinding) return _NL_CURRENT (LC_CTYPE, CODESET); # else # if HAVE_ICONV - extern const char *locale_charset PARAMS ((void)); return locale_charset (); # endif # endif @@ -1432,9 +1662,7 @@ get_output_charset (domainbinding) to be defined. */ #if !_LIBC && !HAVE_STPCPY static char * -stpcpy (dest, src) - char *dest; - const char *src; +stpcpy (char *dest, const char *src) { while ((*dest++ = *src++) != '\0') /* Do nothing. */ ; @@ -1444,15 +1672,16 @@ stpcpy (dest, src) #if !_LIBC && !HAVE_MEMPCPY static void * -mempcpy (dest, src, n) - void *dest; - const void *src; - size_t n; +mempcpy (void *dest, const void *src, size_t n) { return (void *) ((char *) memcpy (dest, src, n) + n); } #endif +#if !_LIBC && !HAVE_TSEARCH +# include "tsearch.c" +#endif + #ifdef _LIBC /* If we want to free all resources we have to do some work at |