|  | /* Implementation of the internal dcigettext function. | 
|  | Copyright (C) 1995-1999, 2000-2003 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 | 
|  |  | 
|  | #include <sys/types.h> | 
|  |  | 
|  | #ifdef __GNUC__ | 
|  | # define alloca __builtin_alloca | 
|  | # define HAVE_ALLOCA 1 | 
|  | #else | 
|  | # ifdef _MSC_VER | 
|  | #  include <malloc.h> | 
|  | #  define alloca _alloca | 
|  | # else | 
|  | #  if defined HAVE_ALLOCA_H || defined _LIBC | 
|  | #   include <alloca.h> | 
|  | #  else | 
|  | #   ifdef _AIX | 
|  | #pragma alloca | 
|  | #   else | 
|  | #    ifndef alloca | 
|  | char *alloca (); | 
|  | #    endif | 
|  | #   endif | 
|  | #  endif | 
|  | # endif | 
|  | #endif | 
|  |  | 
|  | #include <errno.h> | 
|  | #ifndef errno | 
|  | extern int errno; | 
|  | #endif | 
|  | #ifndef __set_errno | 
|  | # define __set_errno(val) errno = (val) | 
|  | #endif | 
|  |  | 
|  | #include <stddef.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  |  | 
|  | #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 | 
|  |  | 
|  | #include "gettextP.h" | 
|  | #include "plural-exp.h" | 
|  | #ifdef _LIBC | 
|  | # include <libintl.h> | 
|  | #else | 
|  | # include "libgnuintl.h" | 
|  | #endif | 
|  | #include "hash-string.h" | 
|  |  | 
|  | /* Thread safetyness.  */ | 
|  | #ifdef _LIBC | 
|  | # include <bits/libc-lock.h> | 
|  | #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) | 
|  | #endif | 
|  |  | 
|  | /* Alignment of types.  */ | 
|  | #if defined __GNUC__ && __GNUC__ >= 2 | 
|  | # define alignof(TYPE) __alignof__ (TYPE) | 
|  | #else | 
|  | # define alignof(TYPE) \ | 
|  | ((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)) | 
|  | #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 | 
|  | char *getcwd (); | 
|  | # endif | 
|  | # ifndef HAVE_STPCPY | 
|  | static char *stpcpy PARAMS ((char *dest, const char *src)); | 
|  | # endif | 
|  | # ifndef HAVE_MEMPCPY | 
|  | static void *mempcpy PARAMS ((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 | 
|  |  | 
|  | #ifndef _POSIX_PATH_MAX | 
|  | # define _POSIX_PATH_MAX 255 | 
|  | #endif | 
|  |  | 
|  | #if !defined PATH_MAX && defined _PC_PATH_MAX | 
|  | # define PATH_MAX (pathconf ("/", _PC_PATH_MAX) < 1 ? 1024 : pathconf ("/", _PC_PATH_MAX)) | 
|  | #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 | 
|  |  | 
|  | #if !defined PATH_MAX && defined MAXPATHLEN | 
|  | # define PATH_MAX MAXPATHLEN | 
|  | #endif | 
|  |  | 
|  | #ifndef PATH_MAX | 
|  | # 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 __EMX__ || defined __DJGPP__ | 
|  | /* Win32, 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 | 
|  |  | 
|  | /* This is the type used for the search tree where known translations | 
|  | are stored.  */ | 
|  | struct known_translation_t | 
|  | { | 
|  | /* Domain in which to search.  */ | 
|  | char *domainname; | 
|  |  | 
|  | /* The category.  */ | 
|  | int category; | 
|  |  | 
|  | /* 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> | 
|  |  | 
|  | 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; | 
|  | { | 
|  | const struct known_translation_t *s1; | 
|  | const struct known_translation_t *s2; | 
|  | int result; | 
|  |  | 
|  | s1 = (const struct known_translation_t *) p1; | 
|  | s2 = (const struct known_translation_t *) p2; | 
|  |  | 
|  | result = strcmp (s1->msgid, s2->msgid); | 
|  | if (result == 0) | 
|  | { | 
|  | result = strcmp (s1->domainname, s2->domainname); | 
|  | if (result == 0) | 
|  | /* 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 | 
|  |  | 
|  | #ifndef INTVARDEF | 
|  | # define INTVARDEF(name) | 
|  | #endif | 
|  | #ifndef INTUSE | 
|  | # define INTUSE(name) name | 
|  | #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"; | 
|  |  | 
|  | /* Value used as the default domain for gettext(3).  */ | 
|  | const char *_nl_current_default_domain attribute_hidden | 
|  | = _nl_default_default_domain; | 
|  |  | 
|  | /* Contains the default location of the message catalogs.  */ | 
|  | #if defined __EMX__ | 
|  | extern const char _nl_default_dirname[]; | 
|  | #else | 
|  | const char _nl_default_dirname[] = LOCALEDIR; | 
|  | INTVARDEF (_nl_default_dirname) | 
|  | #endif | 
|  |  | 
|  | /* List with bindings of specific domains created by bindtextdomain() | 
|  | calls.  */ | 
|  | struct binding *_nl_domain_bindings; | 
|  |  | 
|  | /* Prototypes for local functions.  */ | 
|  | static char *plural_lookup PARAMS ((struct loaded_l10nfile *domain, | 
|  | unsigned long int n, | 
|  | const char *translation, | 
|  | size_t translation_len)) | 
|  | internal_function; | 
|  | static const char *guess_category_value PARAMS ((int category, | 
|  | const char *categoryname)) | 
|  | internal_function; | 
|  | #ifdef _LIBC | 
|  | # include "../locale/localeinfo.h" | 
|  | # define category_to_name(category)	_nl_category_names[category] | 
|  | #else | 
|  | static const char *category_to_name PARAMS ((int category)) internal_function; | 
|  | #endif | 
|  |  | 
|  |  | 
|  | /* For those loosing systems which don't have `alloca' we have to add | 
|  | some additional code emulating it.  */ | 
|  | #ifdef HAVE_ALLOCA | 
|  | /* Nothing has to be done.  */ | 
|  | # define freea(p) /* nothing */ | 
|  | # define ADD_BLOCK(list, address) /* nothing */ | 
|  | # define FREE_BLOCKS(list) /* nothing */ | 
|  | #else | 
|  | struct block_list | 
|  | { | 
|  | void *address; | 
|  | struct block_list *next; | 
|  | }; | 
|  | # define ADD_BLOCK(list, addr)						      \ | 
|  | do {									      \ | 
|  | struct block_list *newp = (struct block_list *) malloc (sizeof (*newp));  \ | 
|  | /* If we cannot get a free block we cannot add the new element to	      \ | 
|  | the list.  */							      \ | 
|  | if (newp != NULL) {							      \ | 
|  | newp->address = (addr);						      \ | 
|  | newp->next = (list);						      \ | 
|  | (list) = newp;							      \ | 
|  | }									      \ | 
|  | } while (0) | 
|  | # define FREE_BLOCKS(list)						      \ | 
|  | do {									      \ | 
|  | while (list != NULL) {						      \ | 
|  | struct block_list *old = list;					      \ | 
|  | list = list->next;						      \ | 
|  | free (old->address);						      \ | 
|  | free (old);							      \ | 
|  | }									      \ | 
|  | } while (0) | 
|  | # undef alloca | 
|  | # define alloca(size) (malloc (size)) | 
|  | # define freea(p) free (p) | 
|  | #endif	/* have alloca */ | 
|  |  | 
|  |  | 
|  | #ifdef _LIBC | 
|  | /* List of blocks allocated for translations.  */ | 
|  | typedef struct transmem_list | 
|  | { | 
|  | struct transmem_list *next; | 
|  | char data[ZERO]; | 
|  | } transmem_block_t; | 
|  | static struct transmem_list *transmem_list; | 
|  | #else | 
|  | typedef unsigned char transmem_block_t; | 
|  | #endif | 
|  |  | 
|  |  | 
|  | /* 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.  */ | 
|  | #ifdef _LIBC | 
|  | __libc_rwlock_define_initialized (, _nl_state_lock attribute_hidden) | 
|  | #endif | 
|  |  | 
|  | /* Checking whether the binaries runs SUID must be done and glibc provides | 
|  | easier methods therefore we make a difference here.  */ | 
|  | #ifdef _LIBC | 
|  | # define ENABLE_SECURE __libc_enable_secure | 
|  | # define DETERMINE_SECURE | 
|  | #else | 
|  | # ifndef HAVE_GETUID | 
|  | #  define getuid() 0 | 
|  | # endif | 
|  | # ifndef HAVE_GETGID | 
|  | #  define getgid() 0 | 
|  | # endif | 
|  | # ifndef HAVE_GETEUID | 
|  | #  define geteuid() getuid() | 
|  | # endif | 
|  | # ifndef HAVE_GETEGID | 
|  | #  define getegid() getgid() | 
|  | # endif | 
|  | static int enable_secure; | 
|  | # define ENABLE_SECURE (enable_secure == 1) | 
|  | # define DETERMINE_SECURE \ | 
|  | if (enable_secure == 0)						      \ | 
|  | {									      \ | 
|  | if (getuid () != geteuid () || getgid () != getegid ())		      \ | 
|  | enable_secure = 1;						      \ | 
|  | else								      \ | 
|  | enable_secure = -1;						      \ | 
|  | } | 
|  | #endif | 
|  |  | 
|  | /* Get the function to evaluate the plural expression.  */ | 
|  | #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.  */ | 
|  | 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; | 
|  | { | 
|  | #ifndef HAVE_ALLOCA | 
|  | struct block_list *block_list = NULL; | 
|  | #endif | 
|  | struct loaded_l10nfile *domain; | 
|  | struct binding *binding; | 
|  | const char *categoryname; | 
|  | const char *categoryvalue; | 
|  | char *dirname, *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; | 
|  | size_t msgid_len; | 
|  | #endif | 
|  | size_t domainname_len; | 
|  |  | 
|  | /* If no real MSGID is given return NULL.  */ | 
|  | if (msgid1 == NULL) | 
|  | return NULL; | 
|  |  | 
|  | #ifdef _LIBC | 
|  | if (category < 0 || category >= __LC_LAST || category == LC_ALL) | 
|  | /* Bogus.  */ | 
|  | return (plural == 0 | 
|  | ? (char *) msgid1 | 
|  | /* Use the Germanic plural rule.  */ | 
|  | : n == 1 ? (char *) msgid1 : (char *) msgid2); | 
|  | #endif | 
|  |  | 
|  | __libc_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 | 
|  | 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 = (char *) domainname; | 
|  | search->category = category; | 
|  |  | 
|  | foundp = (struct known_translation_t **) tfind (search, &root, transcmp); | 
|  | freea (search); | 
|  | if (foundp != NULL && (*foundp)->counter == _nl_msg_cat_cntr) | 
|  | { | 
|  | /* Now deal with plural.  */ | 
|  | if (plural) | 
|  | retval = plural_lookup ((*foundp)->domain, n, (*foundp)->translation, | 
|  | (*foundp)->translation_length); | 
|  | else | 
|  | retval = (char *) (*foundp)->translation; | 
|  |  | 
|  | __libc_rwlock_unlock (_nl_state_lock); | 
|  | 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.  */ | 
|  | 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 = (char *) INTUSE(_nl_default_dirname); | 
|  | else if (IS_ABSOLUTE_PATH (binding->dirname)) | 
|  | dirname = binding->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; | 
|  |  | 
|  | 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); | 
|  |  | 
|  | __set_errno (0); | 
|  | ret = getcwd (dirname, path_max); | 
|  | if (ret != NULL || errno != ERANGE) | 
|  | break; | 
|  |  | 
|  | path_max += path_max / 2; | 
|  | path_max += PATH_INCR; | 
|  | } | 
|  |  | 
|  | 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); | 
|  | } | 
|  |  | 
|  | /* Now determine the symbolic name of CATEGORY and its value.  */ | 
|  | categoryname = category_to_name (category); | 
|  | categoryvalue = guess_category_value (category, categoryname); | 
|  |  | 
|  | 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), | 
|  | ".mo"); | 
|  |  | 
|  | /* Creating working area.  */ | 
|  | single_locale = (char *) alloca (strlen (categoryvalue) + 1); | 
|  | ADD_BLOCK (block_list, single_locale); | 
|  |  | 
|  |  | 
|  | /* 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); | 
|  |  | 
|  | if (domain != NULL) | 
|  | { | 
|  | retval = _nl_find_msg (domain, binding, msgid1, &retlen); | 
|  |  | 
|  | if (retval == NULL) | 
|  | { | 
|  | int cnt; | 
|  |  | 
|  | for (cnt = 0; domain->successor[cnt] != NULL; ++cnt) | 
|  | { | 
|  | retval = _nl_find_msg (domain->successor[cnt], binding, | 
|  | msgid1, &retlen); | 
|  |  | 
|  | if (retval != NULL) | 
|  | { | 
|  | domain = domain->successor[cnt]; | 
|  | 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.  */ | 
|  | struct known_translation_t *newp; | 
|  |  | 
|  | newp = (struct known_translation_t *) | 
|  | malloc (offsetof (struct known_translation_t, msgid) | 
|  | + msgid_len + domainname_len + 1); | 
|  | if (newp != NULL) | 
|  | { | 
|  | newp->domainname = | 
|  | mempcpy (newp->msgid, msgid1, msgid_len); | 
|  | memcpy (newp->domainname, domainname, domainname_len + 1); | 
|  | newp->category = category; | 
|  | newp->counter = _nl_msg_cat_cntr; | 
|  | newp->domain = domain; | 
|  | newp->translation = retval; | 
|  | newp->translation_length = retlen; | 
|  |  | 
|  | /* Insert the entry in the search tree.  */ | 
|  | foundp = (struct known_translation_t **) | 
|  | tsearch (newp, &root, transcmp); | 
|  | if (foundp == NULL | 
|  | || __builtin_expect (*foundp != newp, 0)) | 
|  | /* The insert failed.  */ | 
|  | free (newp); | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | /* We can update the existing entry.  */ | 
|  | (*foundp)->counter = _nl_msg_cat_cntr; | 
|  | (*foundp)->domain = domain; | 
|  | (*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); | 
|  |  | 
|  | __libc_rwlock_unlock (_nl_state_lock); | 
|  | return retval; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return_untranslated: | 
|  | /* Return the untranslated MSGID.  */ | 
|  | FREE_BLOCKS (block_list); | 
|  | __libc_rwlock_unlock (_nl_state_lock); | 
|  | #ifndef _LIBC | 
|  | if (!ENABLE_SECURE) | 
|  | { | 
|  | extern void _nl_log_untranslated PARAMS ((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); | 
|  | } | 
|  |  | 
|  |  | 
|  | char * | 
|  | internal_function | 
|  | _nl_find_msg (domain_file, domainbinding, msgid, lengthp) | 
|  | struct loaded_l10nfile *domain_file; | 
|  | struct binding *domainbinding; | 
|  | const char *msgid; | 
|  | size_t *lengthp; | 
|  | { | 
|  | 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); | 
|  |  | 
|  | if (domain_file->data == NULL) | 
|  | return NULL; | 
|  |  | 
|  | domain = (struct loaded_domain *) domain_file->data; | 
|  |  | 
|  | nstrings = domain->nstrings; | 
|  |  | 
|  | /* 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 | 
|  | if (domain->codeset_cntr | 
|  | != (domainbinding != NULL ? domainbinding->codeset_cntr : 0)) | 
|  | { | 
|  | /* The domain's codeset has changed through bind_textdomain_codeset() | 
|  | since the message catalog was initialized or last accessed.  We | 
|  | have to reinitialize the converter.  */ | 
|  | _nl_free_domain_conv (domain); | 
|  | _nl_init_domain_conv (domain_file, domain, domainbinding); | 
|  | } | 
|  |  | 
|  | if ( | 
|  | # ifdef _LIBC | 
|  | domain->conv != (__gconv_t) -1 | 
|  | # else | 
|  | #  if HAVE_ICONV | 
|  | domain->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 (domain->conv_tab == NULL | 
|  | && ((domain->conv_tab = | 
|  | (char **) calloc (nstrings + domain->n_sysdep_strings, | 
|  | sizeof (char *))) | 
|  | == NULL)) | 
|  | /* Mark that we didn't succeed allocating a table.  */ | 
|  | domain->conv_tab = (char **) -1; | 
|  |  | 
|  | if (__builtin_expect (domain->conv_tab == (char **) -1, 0)) | 
|  | /* Nothing we can do, no more memory.  */ | 
|  | goto converted; | 
|  |  | 
|  | if (domain->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; | 
|  |  | 
|  | const unsigned char *inbuf; | 
|  | unsigned char *outbuf; | 
|  | int malloc_count; | 
|  | # ifndef _LIBC | 
|  | transmem_block_t *transmem_list = NULL; | 
|  | # endif | 
|  |  | 
|  | __libc_lock_lock (lock); | 
|  |  | 
|  | inbuf = (const unsigned char *) result; | 
|  | outbuf = freemem + sizeof (size_t); | 
|  |  | 
|  | malloc_count = 0; | 
|  | while (1) | 
|  | { | 
|  | transmem_block_t *newmem; | 
|  | # ifdef _LIBC | 
|  | size_t non_reversible; | 
|  | int res; | 
|  |  | 
|  | if (freemem_size < sizeof (size_t)) | 
|  | goto resize_freemem; | 
|  |  | 
|  | res = __gconv (domain->conv, | 
|  | &inbuf, inbuf + resultlen, | 
|  | &outbuf, | 
|  | outbuf + freemem_size - sizeof (size_t), | 
|  | &non_reversible); | 
|  |  | 
|  | if (res == __GCONV_OK || res == __GCONV_EMPTY_INPUT) | 
|  | break; | 
|  |  | 
|  | if (res != __GCONV_FULL_OUTPUT) | 
|  | { | 
|  | __libc_lock_unlock (lock); | 
|  | goto converted; | 
|  | } | 
|  |  | 
|  | inbuf = result; | 
|  | # else | 
|  | #  if HAVE_ICONV | 
|  | const char *inptr = (const char *) inbuf; | 
|  | size_t inleft = resultlen; | 
|  | char *outptr = (char *) outbuf; | 
|  | size_t outleft; | 
|  |  | 
|  | if (freemem_size < sizeof (size_t)) | 
|  | goto resize_freemem; | 
|  |  | 
|  | outleft = freemem_size - sizeof (size_t); | 
|  | if (iconv (domain->conv, | 
|  | (ICONV_CONST char **) &inptr, &inleft, | 
|  | &outptr, &outleft) | 
|  | != (size_t) (-1)) | 
|  | { | 
|  | outbuf = (unsigned char *) outptr; | 
|  | break; | 
|  | } | 
|  | if (errno != E2BIG) | 
|  | { | 
|  | __libc_lock_unlock (lock); | 
|  | goto converted; | 
|  | } | 
|  | #  endif | 
|  | # endif | 
|  |  | 
|  | resize_freemem: | 
|  | /* We must allocate a new buffer or resize the old one.  */ | 
|  | if (malloc_count > 0) | 
|  | { | 
|  | ++malloc_count; | 
|  | freemem_size = malloc_count * INITIAL_BLOCK_SIZE; | 
|  | newmem = (transmem_block_t *) realloc (transmem_list, | 
|  | freemem_size); | 
|  | # ifdef _LIBC | 
|  | if (newmem != NULL) | 
|  | transmem_list = transmem_list->next; | 
|  | else | 
|  | { | 
|  | struct transmem_list *old = transmem_list; | 
|  |  | 
|  | transmem_list = transmem_list->next; | 
|  | free (old); | 
|  | } | 
|  | # endif | 
|  | } | 
|  | else | 
|  | { | 
|  | malloc_count = 1; | 
|  | freemem_size = INITIAL_BLOCK_SIZE; | 
|  | newmem = (transmem_block_t *) malloc (freemem_size); | 
|  | } | 
|  | if (__builtin_expect (newmem == NULL, 0)) | 
|  | { | 
|  | freemem = NULL; | 
|  | freemem_size = 0; | 
|  | __libc_lock_unlock (lock); | 
|  | goto converted; | 
|  | } | 
|  |  | 
|  | # ifdef _LIBC | 
|  | /* Add the block to the list of blocks we have to free | 
|  | at some point.  */ | 
|  | newmem->next = transmem_list; | 
|  | transmem_list = newmem; | 
|  |  | 
|  | freemem = newmem->data; | 
|  | freemem_size -= offsetof (struct transmem_list, data); | 
|  | # else | 
|  | transmem_list = newmem; | 
|  | freemem = newmem; | 
|  | # endif | 
|  |  | 
|  | outbuf = freemem + sizeof (size_t); | 
|  | } | 
|  |  | 
|  | /* We have now in our buffer a converted string.  Put this | 
|  | into the table of conversions.  */ | 
|  | *(size_t *) freemem = outbuf - freemem - sizeof (size_t); | 
|  | domain->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 domain->conv_tab[act] contains the translation of all | 
|  | the plural variants.  */ | 
|  | result = domain->conv_tab[act] + sizeof (size_t); | 
|  | resultlen = *(size_t *) domain->conv_tab[act]; | 
|  | } | 
|  |  | 
|  | converted: | 
|  | /* The result string is converted.  */ | 
|  |  | 
|  | #endif /* _LIBC || HAVE_ICONV */ | 
|  |  | 
|  | *lengthp = resultlen; | 
|  | return result; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* 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; | 
|  | { | 
|  | 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; | 
|  | } | 
|  |  | 
|  | #ifndef _LIBC | 
|  | /* Return string representation of locale CATEGORY.  */ | 
|  | static const char * | 
|  | internal_function | 
|  | category_to_name (category) | 
|  | int category; | 
|  | { | 
|  | const char *retval; | 
|  |  | 
|  | 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.  */ | 
|  | static const char * | 
|  | internal_function | 
|  | guess_category_value (category, categoryname) | 
|  | int category; | 
|  | const char *categoryname; | 
|  | { | 
|  | 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; | 
|  |  | 
|  | /* We have to proceed with the POSIX methods 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); | 
|  | #endif | 
|  |  | 
|  | /* Ignore LANGUAGE 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".  We allow such programs to use gettext().  */ | 
|  | return language != NULL && strcmp (retval, "C") != 0 ? language : retval; | 
|  | } | 
|  |  | 
|  | /* @@ begin of epilog @@ */ | 
|  |  | 
|  | /* 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 (dest, src) | 
|  | char *dest; | 
|  | const char *src; | 
|  | { | 
|  | while ((*dest++ = *src++) != '\0') | 
|  | /* Do nothing. */ ; | 
|  | return dest - 1; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | #if !_LIBC && !HAVE_MEMPCPY | 
|  | static void * | 
|  | mempcpy (dest, src, n) | 
|  | void *dest; | 
|  | const void *src; | 
|  | size_t n; | 
|  | { | 
|  | return (void *) ((char *) memcpy (dest, src, n) + n); | 
|  | } | 
|  | #endif | 
|  |  | 
|  |  | 
|  | #ifdef _LIBC | 
|  | /* If we want to free all resources we have to do some work at | 
|  | program's end.  */ | 
|  | libc_freeres_fn (free_mem) | 
|  | { | 
|  | void *old; | 
|  |  | 
|  | while (_nl_domain_bindings != NULL) | 
|  | { | 
|  | struct binding *oldp = _nl_domain_bindings; | 
|  | _nl_domain_bindings = _nl_domain_bindings->next; | 
|  | if (oldp->dirname != INTUSE(_nl_default_dirname)) | 
|  | /* Yes, this is a pointer comparison.  */ | 
|  | free (oldp->dirname); | 
|  | free (oldp->codeset); | 
|  | free (oldp); | 
|  | } | 
|  |  | 
|  | if (_nl_current_default_domain != _nl_default_default_domain) | 
|  | /* Yes, again a pointer comparison.  */ | 
|  | free ((char *) _nl_current_default_domain); | 
|  |  | 
|  | /* Remove the search tree with the known translations.  */ | 
|  | __tdestroy (root, free); | 
|  | root = NULL; | 
|  |  | 
|  | while (transmem_list != NULL) | 
|  | { | 
|  | old = transmem_list; | 
|  | transmem_list = transmem_list->next; | 
|  | free (old); | 
|  | } | 
|  | } | 
|  | #endif |