/* Implementation of the internal dcigettext function.
Copyright (C) 1995-1999, 2000-2006 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published
by the Free Software Foundation; either version 2, or (at your option)
any later version.
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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
USA. */
/* Tell glibc's <string.h> to provide a prototype for mempcpy().
This must come before <config.h> because <config.h> may include
<features.h>, and once <features.h> has been included, it's too late. */
#ifndef _GNU_SOURCE
# define _GNU_SOURCE 1
#endif
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
/* NL_LOCALE_NAME does not work in glibc-2.4. Ignore it. */
#undef HAVE_NL_LOCALE_NAME
#if defined HAVE_UNISTD_H || defined _LIBC
# include <unistd.h>
#endif
#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 && HAVE_NL_LOCALE_NAME
# include <langinfo.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"
/* 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))
#endif
/* @@ end of prolog @@ */
#ifdef _LIBC
/* Rename the non ANSI C functions. This is required by the standard
because some ANSI C functions will require linking with this object
file and the name space must not be polluted. */
# define getcwd __getcwd
# ifndef stpcpy
# define stpcpy __stpcpy
# endif
# define tfind __tfind
#else
# if !defined HAVE_GETCWD
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 (char *dest, const char *src);
# endif
# ifndef HAVE_MEMPCPY
static void *mempcpy (void *dest, const void *src, size_t n);
# endif
#endif
/* Amount to increase buffer size by in each try. */
#define PATH_INCR 32
/* The following is from pathmax.h. */
/* Non-POSIX BSD systems might have gcc's limits.h, which doesn't define
PATH_MAX but might cause redefinition warnings when sys/param.h is
later included (as on MORE/BSD 4.3). */
#if defined _POSIX_VERSION || (defined HAVE_LIMITS_H && !defined __GNUC__)
# include <limits.h>
#endif
/* Don't include sys/param.h if it already has been. */
#if defined HAVE_SYS_PARAM_H && !defined PATH_MAX && !defined MAXPATHLEN
# include <sys/param.h>
#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 || (HAVE_STRUCT___LOCALE_STRUCT___NAMES && defined USE_IN_GETTEXT_TESTS) || defined IN_LIBGLOCALE
# define HAVE_PER_THREAD_LOCALE
#endif
/* This is the type used for the search tree where known translations
are stored. */
struct known_translation_t
{
/* Domain in which to search. */
const char *domainname;
/* The category. */
int category;
#ifdef HAVE_PER_THREAD_LOCALE
/* Name of the relevant locale category, or "" for the global locale. */
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;
/* Catalog where the string was found. */
struct loaded_l10nfile *domain;
/* And finally the translation. */
const char *translation;
size_t translation_length;
/* Pointer to the string in question. */
char msgid[ZERO];
};
/* 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)
static void *root;
# ifdef _LIBC
# define tsearch __tsearch
# endif
/* Function to compare two entries in the table of known translations. */
static int
transcmp (const void *p1, const void *p2)
{
const struct known_translation_t *s1;
const struct known_translation_t *s2;
int result;
result = strcmp (s1->msgid, s2->msgid);
if (result == 0)
{
result = strcmp (s1->domainname, s2->domainname);
if (result == 0)
{
#ifdef HAVE_PER_THREAD_LOCALE
result = strcmp (s1->localename, s2->localename);
if (result == 0)
#endif
{
#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
#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 (struct loaded_l10nfile *domain,
unsigned long int n,
const char *translation, size_t translation_len)
internal_function;
/* Names for the libintl functions are a problem. They must not clash
with existing names and they should follow ANSI C. But this source
code is also used in GNU C Library where the names have a __
prefix. So we have to make a difference here. */
#ifdef _LIBC
# define DCIGETTEXT __dcigettext
#else
# define DCIGETTEXT libintl_dcigettext
#endif
/* Lock variable to protect the global data in the gettext implementation. */
gl_rwlock_define_initialized (, _nl_state_lock attribute_hidden)
/* 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
definition left this undefined. */
if (domainname == NULL)
domainname = _nl_current_default_domain;
/* OS/2 specific: backward compatibility with older libintl versions */
#ifdef LC_MESSAGES_COMPAT
if (category == LC_MESSAGES_COMPAT)
category = LC_MESSAGES;
#endif
#if defined HAVE_TSEARCH || defined _LIBC
msgid_len = strlen (msgid1) + 1;
/* Try to find the translation among those which we found at
some time. */
search = (struct known_translation_t *)
alloca (offsetof (struct known_translation_t, msgid) + msgid_len);
memcpy (search->msgid, msgid1, msgid_len);
search->domainname = domainname;
search->category = category;
# ifdef HAVE_PER_THREAD_LOCALE
# ifndef IN_LIBGLOCALE
# ifdef _LIBC
localename = __current_locale_name (category);
# else
# if HAVE_NL_LOCALE_NAME
/* NL_LOCALE_NAME is public glibc API introduced in glibc-2.4. */
localename = nl_langinfo (NL_LOCALE_NAME (category));
# else
# if HAVE_STRUCT___LOCALE_STRUCT___NAMES && defined USE_IN_GETTEXT_TESTS
/* The __names field is not public glibc API and must therefore not be used
in code that is installed in public locations. */
{
locale_t thread_locale = uselocale (NULL);
if (thread_locale != LC_GLOBAL_LOCALE)
localename = thread_locale->__names[category];
else
localename = "";
}
# endif
# endif
# endif
# endif
search->localename = localename;
# ifdef IN_LIBGLOCALE
search->encoding = encoding;
# endif
# endif
/* Since tfind/tsearch manage a balanced tree, concurrent tfind and
tsearch calls can be fatal. */
gl_rwlock_rdlock (tree_lock);
/* 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);
if (compare == 0)
/* We found it! */
break;
if (compare < 0)
{
/* It is not in the list. */
binding = NULL;
break;
}
}
if (binding == NULL)
dirname = _nl_default_dirname;
else
{
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. */
if (ret == NULL)
/* We cannot get the current working directory. Don't signal an
error but simply return the default string. */
goto return_untranslated;
/* Search for the given string. This is a loop because we perhaps
got an ordered list of languages to consider for the translation. */
while (1)
{
/* Make CATEGORYVALUE point to the next element of the list. */
while (categoryvalue[0] != '\0' && categoryvalue[0] == ':')
++categoryvalue;
if (categoryvalue[0] == '\0')
{
/* The whole contents of CATEGORYVALUE has been searched but
no valid entry has been found. We solve this situation
by implicitly appending a "C" entry, i.e. no translation
will take place. */
single_locale[0] = 'C';
single_locale[1] = '\0';
}
else
{
char *cp = single_locale;
while (categoryvalue[0] != '\0' && categoryvalue[0] != ':')
*cp++ = *categoryvalue++;
*cp = '\0';
/* When this is a SUID binary we must not allow accessing files
outside the dedicated directories. */
if (ENABLE_SECURE && IS_PATH_WITH_DIR (single_locale))
/* Ingore this entry. */
continue;
}
/* If the current locale value is C (or POSIX) we don't load a
domain. Return the MSGID. */
if (strcmp (single_locale, "C") == 0
|| strcmp (single_locale, "POSIX") == 0)
break;
/* Find structure describing the message catalog matching the
DOMAINNAME and CATEGORY. */
domain = _nl_find_domain (dirname, single_locale, xdomainname, binding);
/* Returning -1 means that some resource problem exists
(likely memory) and that the strings could not be
converted. Return the original strings. */
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. */
size_t size;
struct known_translation_t *newp;
/* 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
#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;
size_t act;
char *result;
size_t resultlen;
if (domain_file->decided <= 0)
_nl_load_domain (domain_file, domainbinding);
/* Locate the MSGID and its translation. */
if (domain->hash_tab != NULL)
{
/* Use the hashing table. */
nls_uint32 len = strlen (msgid);
nls_uint32 hash_val = __hash_string (msgid);
nls_uint32 idx = hash_val % domain->hash_size;
nls_uint32 incr = 1 + (hash_val % (domain->hash_size - 2));
while (1)
{
nls_uint32 nstr =
W (domain->must_swap_hash_tab, domain->hash_tab[idx]);
if (nstr == 0)
/* Hash table entry is empty. */
return NULL;
nstr--;
/* Compare msgid with the original string at index nstr.
We compare the lengths with >=, not ==, because plural entries
are represented by strings with an embedded NUL. */
if (nstr < nstrings
? W (domain->must_swap, domain->orig_tab[nstr].length) >= len
&& (strcmp (msgid,
domain->data + W (domain->must_swap,
domain->orig_tab[nstr].offset))
== 0)
: domain->orig_sysdep_tab[nstr - nstrings].length > len
&& (strcmp (msgid,
domain->orig_sysdep_tab[nstr - nstrings].pointer)
== 0))
{
act = nstr;
goto found;
}
if (idx >= domain->hash_size - incr)
idx -= domain->hash_size - incr;
else
idx += incr;
}
/* NOTREACHED */
}
else
{
/* Try the default method: binary search in the sorted array of
messages. */
size_t top, bottom;
bottom = 0;
top = nstrings;
while (bottom < top)
{
int cmp_val;
act = (bottom + top) / 2;
cmp_val = strcmp (msgid, (domain->data
+ W (domain->must_swap,
domain->orig_tab[act].offset)));
if (cmp_val < 0)
top = act;
else if (cmp_val > 0)
bottom = act + 1;
else
goto found;
}
/* No translation was found. */
return NULL;
}
found:
/* The translation was found at index ACT. If we have to convert the
string to use a different character set, this is the time. */
if (act < nstrings)
{
result = (char *)
(domain->data + W (domain->must_swap, domain->trans_tab[act].offset));
resultlen = W (domain->must_swap, domain->trans_tab[act].length) + 1;
}
else
{
result = (char *) domain->trans_sysdep_tab[act - nstrings].pointer;
resultlen = domain->trans_sysdep_tab[act - nstrings].length;
}
#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
/* 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;
for (i = nconversions; i > 0; )
{
i--;
if (strcmp (domain->conversions[i].encoding, encoding) == 0)
{
convd = &domain->conversions[i];
break;
}
}
if (convd == NULL)
{
/* 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. */
return (char *) -1;
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. */
return (char *) -1;
/* 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;
# else
# if HAVE_ICONV
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
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
# 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 (__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_NOCONV, 1))
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 \
|| _LIBICONV_VERSION >= 0x0105
if (strchr (outcharset, '/') == NULL)
{
char *tmp;
freea (charset);
}
}
}
convd->conv_tab = NULL;
/* Here domain->conversions is still == new_conversions. */
domain->nconversions++;
}
if (
# ifdef _LIBC
convd->conv != (__gconv_t) -1
# else
# if HAVE_ICONV
convd->conv != (iconv_t) -1
# endif
# endif
)
{
/* 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
to the converted strings in.
There is a slight complication with plural entries. They
are represented by consecutive NUL terminated strings. We
handle this case by converting RESULTLEN bytes, including
NULs. */
if (convd->conv_tab == NULL
&& ((convd->conv_tab =
(char **) calloc (nstrings + domain->n_sysdep_strings,
sizeof (char *)))
== NULL))
/* Mark that we didn't succeed allocating a table. */
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)
{
/* 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.
We allocate always larger blocks which get used over
time. This is faster than many small allocations. */
__libc_lock_define_initialized (static, lock)
# define INITIAL_BLOCK_SIZE 4080
static unsigned char *freemem;
static size_t freemem_size;
/* We have now in our buffer a converted string. Put this
into the table of conversions. */
*(size_t *) freemem = outbuf - freemem - sizeof (size_t);
convd->conv_tab[act] = (char *) freemem;
/* Shrink freemem, but keep it aligned. */
freemem_size -= outbuf - freemem;
freemem = outbuf;
freemem += freemem_size & (alignof (size_t) - 1);
freemem_size = freemem_size & ~ (alignof (size_t) - 1);
__libc_lock_unlock (lock);
}
/* Now convd->conv_tab[act] contains the translation of all
the plural variants. */
result = convd->conv_tab[act] + sizeof (size_t);
resultlen = *(size_t *) convd->conv_tab[act];
}
}
/* The result string is converted. */
#endif /* _LIBC || HAVE_ICONV */
*lengthp = resultlen;
return result;
}
/* Look up a plural variant. */
static char *
internal_function
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;
const char *p;
index = plural_eval (domaindata->plural, n);
if (index >= domaindata->nplurals)
/* This should never happen. It means the plural expression and the
given maximum value do not match. */
index = 0;
/* Skip INDEX strings at TRANSLATION. */
p = translation;
while (index-- > 0)
{
#ifdef _LIBC
p = __rawmemchr (p, '\0');
#else
p = strchr (p, '\0');
#endif
/* And skip over the NUL byte. */
p++;
if (p >= translation + translation_len)
/* This should never happen. It means the plural expression
evaluated to a value larger than the number of variants
available for MSGID1. */
return (char *) translation;
}
return (char *) p;
}
switch (category)
{
#ifdef LC_COLLATE
case LC_COLLATE:
retval = "LC_COLLATE";
break;
#endif
#ifdef LC_CTYPE
case LC_CTYPE:
retval = "LC_CTYPE";
break;
#endif
#ifdef LC_MONETARY
case LC_MONETARY:
retval = "LC_MONETARY";
break;
#endif
#ifdef LC_NUMERIC
case LC_NUMERIC:
retval = "LC_NUMERIC";
break;
#endif
#ifdef LC_TIME
case LC_TIME:
retval = "LC_TIME";
break;
#endif
#ifdef LC_MESSAGES
case LC_MESSAGES:
retval = "LC_MESSAGES";
break;
#endif
#ifdef LC_RESPONSE
case LC_RESPONSE:
retval = "LC_RESPONSE";
break;
#endif
#ifdef LC_ALL
case LC_ALL:
/* This might not make sense but is perhaps better than any other
value. */
retval = "LC_ALL";
break;
#endif
default:
/* If you have a better idea for a default value let me know. */
retval = "LC_XXX";
}
return retval;
}
#endif
/* Guess value of current locale from value of the environment variables
or system-dependent defaults. */
static const char *
internal_function
#ifdef IN_LIBGLOCALE
guess_category_value (int category, const char *categoryname,
const char *locale)
/* 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
locale = __current_locale_name (category);
# else
# if HAVE_STRUCT___LOCALE_STRUCT___NAMES && defined USE_IN_GETTEXT_TESTS
/* The __names field is not public glibc API and must therefore not be used
in code that is installed in public locations. */
locale_t thread_locale = uselocale (NULL);
if (thread_locale != LC_GLOBAL_LOCALE)
{
locale = thread_locale->__names[category];
locale_defaulted = 0;
}
else
# endif
{
locale = _nl_locale_name_posix (category, categoryname);
locale_defaulted = 0;
if (locale == NULL)
{
locale = _nl_locale_name_default ();
locale_defaulted = 1;
}
}
# endif
#endif
/* 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) && !defined IN_LIBGLOCALE
/* Returns the output charset. */
static const char *
internal_function
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
a possibility for the user to override this: the OUTPUT_CHARSET
environment variable. Moreover, the value specified through
bind_textdomain_codeset overrides both. */
if (domainbinding != NULL && domainbinding->codeset != NULL)
return domainbinding->codeset;
else
{
/* For speed reasons, we look at the value of OUTPUT_CHARSET only
once. This is a user variable that is not supposed to change
during a program run. */
static char *output_charset_cache;
static int output_charset_cached;
if (!output_charset_cached)
{
const char *value = getenv ("OUTPUT_CHARSET");
/* We don't want libintl.a to depend on any other library. So we
avoid the non-standard function stpcpy. In GNU C Library this
function is available, though. Also allow the symbol HAVE_STPCPY
to be defined. */
#if !_LIBC && !HAVE_STPCPY
static char *
stpcpy (char *dest, const char *src)
{
while ((*dest++ = *src++) != '\0')
/* Do nothing. */ ;
return dest - 1;
}
#endif