Home | History | Annotate | Line # | Download | only in intl
dcigettext.c revision 1.1
      1  1.1  christos /*	$NetBSD: dcigettext.c,v 1.1 2016/01/14 00:11:28 christos Exp $	*/
      2  1.1  christos 
      3  1.1  christos /* Implementation of the internal dcigettext function.
      4  1.1  christos    Copyright (C) 1995-1999, 2000-2003 Free Software Foundation, Inc.
      5  1.1  christos 
      6  1.1  christos    This program is free software; you can redistribute it and/or modify it
      7  1.1  christos    under the terms of the GNU Library General Public License as published
      8  1.1  christos    by the Free Software Foundation; either version 2, or (at your option)
      9  1.1  christos    any later version.
     10  1.1  christos 
     11  1.1  christos    This program is distributed in the hope that it will be useful,
     12  1.1  christos    but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  1.1  christos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14  1.1  christos    Library General Public License for more details.
     15  1.1  christos 
     16  1.1  christos    You should have received a copy of the GNU Library General Public
     17  1.1  christos    License along with this program; if not, write to the Free Software
     18  1.1  christos    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
     19  1.1  christos    USA.  */
     20  1.1  christos 
     21  1.1  christos /* Tell glibc's <string.h> to provide a prototype for mempcpy().
     22  1.1  christos    This must come before <config.h> because <config.h> may include
     23  1.1  christos    <features.h>, and once <features.h> has been included, it's too late.  */
     24  1.1  christos #ifndef _GNU_SOURCE
     25  1.1  christos # define _GNU_SOURCE	1
     26  1.1  christos #endif
     27  1.1  christos 
     28  1.1  christos #ifdef HAVE_CONFIG_H
     29  1.1  christos # include <config.h>
     30  1.1  christos #endif
     31  1.1  christos 
     32  1.1  christos #include <sys/types.h>
     33  1.1  christos 
     34  1.1  christos #ifdef __GNUC__
     35  1.1  christos # define alloca __builtin_alloca
     36  1.1  christos # define HAVE_ALLOCA 1
     37  1.1  christos #else
     38  1.1  christos # ifdef _MSC_VER
     39  1.1  christos #  include <malloc.h>
     40  1.1  christos #  define alloca _alloca
     41  1.1  christos # else
     42  1.1  christos #  if defined HAVE_ALLOCA_H || defined _LIBC
     43  1.1  christos #   include <alloca.h>
     44  1.1  christos #  else
     45  1.1  christos #   ifdef _AIX
     46  1.1  christos  #pragma alloca
     47  1.1  christos #   else
     48  1.1  christos #    ifndef alloca
     49  1.1  christos char *alloca ();
     50  1.1  christos #    endif
     51  1.1  christos #   endif
     52  1.1  christos #  endif
     53  1.1  christos # endif
     54  1.1  christos #endif
     55  1.1  christos 
     56  1.1  christos #include <errno.h>
     57  1.1  christos #ifndef errno
     58  1.1  christos extern int errno;
     59  1.1  christos #endif
     60  1.1  christos #ifndef __set_errno
     61  1.1  christos # define __set_errno(val) errno = (val)
     62  1.1  christos #endif
     63  1.1  christos 
     64  1.1  christos #include <stddef.h>
     65  1.1  christos #include <stdlib.h>
     66  1.1  christos #include <string.h>
     67  1.1  christos 
     68  1.1  christos #if defined HAVE_UNISTD_H || defined _LIBC
     69  1.1  christos # include <unistd.h>
     70  1.1  christos #endif
     71  1.1  christos 
     72  1.1  christos #include <locale.h>
     73  1.1  christos 
     74  1.1  christos #ifdef _LIBC
     75  1.1  christos   /* Guess whether integer division by zero raises signal SIGFPE.
     76  1.1  christos      Set to 1 only if you know for sure.  In case of doubt, set to 0.  */
     77  1.1  christos # if defined __alpha__ || defined __arm__ || defined __i386__ \
     78  1.1  christos      || defined __m68k__ || defined __s390__
     79  1.1  christos #  define INTDIV0_RAISES_SIGFPE 1
     80  1.1  christos # else
     81  1.1  christos #  define INTDIV0_RAISES_SIGFPE 0
     82  1.1  christos # endif
     83  1.1  christos #endif
     84  1.1  christos #if !INTDIV0_RAISES_SIGFPE
     85  1.1  christos # include <signal.h>
     86  1.1  christos #endif
     87  1.1  christos 
     88  1.1  christos #if defined HAVE_SYS_PARAM_H || defined _LIBC
     89  1.1  christos # include <sys/param.h>
     90  1.1  christos #endif
     91  1.1  christos 
     92  1.1  christos #include "gettextP.h"
     93  1.1  christos #include "plural-exp.h"
     94  1.1  christos #ifdef _LIBC
     95  1.1  christos # include <libintl.h>
     96  1.1  christos #else
     97  1.1  christos # include "libgnuintl.h"
     98  1.1  christos #endif
     99  1.1  christos #include "hash-string.h"
    100  1.1  christos 
    101  1.1  christos /* Thread safetyness.  */
    102  1.1  christos #ifdef _LIBC
    103  1.1  christos # include <bits/libc-lock.h>
    104  1.1  christos #else
    105  1.1  christos /* Provide dummy implementation if this is outside glibc.  */
    106  1.1  christos # define __libc_lock_define_initialized(CLASS, NAME)
    107  1.1  christos # define __libc_lock_lock(NAME)
    108  1.1  christos # define __libc_lock_unlock(NAME)
    109  1.1  christos # define __libc_rwlock_define_initialized(CLASS, NAME)
    110  1.1  christos # define __libc_rwlock_rdlock(NAME)
    111  1.1  christos # define __libc_rwlock_unlock(NAME)
    112  1.1  christos #endif
    113  1.1  christos 
    114  1.1  christos /* Alignment of types.  */
    115  1.1  christos #if defined __GNUC__ && __GNUC__ >= 2
    116  1.1  christos # define alignof(TYPE) __alignof__ (TYPE)
    117  1.1  christos #else
    118  1.1  christos # define alignof(TYPE) \
    119  1.1  christos     ((int) &((struct { char dummy1; TYPE dummy2; } *) 0)->dummy2)
    120  1.1  christos #endif
    121  1.1  christos 
    122  1.1  christos /* The internal variables in the standalone libintl.a must have different
    123  1.1  christos    names than the internal variables in GNU libc, otherwise programs
    124  1.1  christos    using libintl.a cannot be linked statically.  */
    125  1.1  christos #if !defined _LIBC
    126  1.1  christos # define _nl_default_default_domain libintl_nl_default_default_domain
    127  1.1  christos # define _nl_current_default_domain libintl_nl_current_default_domain
    128  1.1  christos # define _nl_default_dirname libintl_nl_default_dirname
    129  1.1  christos # define _nl_domain_bindings libintl_nl_domain_bindings
    130  1.1  christos #endif
    131  1.1  christos 
    132  1.1  christos /* Some compilers, like SunOS4 cc, don't have offsetof in <stddef.h>.  */
    133  1.1  christos #ifndef offsetof
    134  1.1  christos # define offsetof(type,ident) ((size_t)&(((type*)0)->ident))
    135  1.1  christos #endif
    136  1.1  christos 
    137  1.1  christos /* @@ end of prolog @@ */
    138  1.1  christos 
    139  1.1  christos #ifdef _LIBC
    140  1.1  christos /* Rename the non ANSI C functions.  This is required by the standard
    141  1.1  christos    because some ANSI C functions will require linking with this object
    142  1.1  christos    file and the name space must not be polluted.  */
    143  1.1  christos # define getcwd __getcwd
    144  1.1  christos # ifndef stpcpy
    145  1.1  christos #  define stpcpy __stpcpy
    146  1.1  christos # endif
    147  1.1  christos # define tfind __tfind
    148  1.1  christos #else
    149  1.1  christos # if !defined HAVE_GETCWD
    150  1.1  christos char *getwd ();
    151  1.1  christos #  define getcwd(buf, max) getwd (buf)
    152  1.1  christos # else
    153  1.1  christos #  if VMS
    154  1.1  christos #   define getcwd(buf, max) (getcwd) (buf, max, 0)
    155  1.1  christos #  else
    156  1.1  christos char *getcwd ();
    157  1.1  christos #  endif
    158  1.1  christos # endif
    159  1.1  christos # ifndef HAVE_STPCPY
    160  1.1  christos static char *stpcpy (char *dest, const char *src);
    161  1.1  christos # endif
    162  1.1  christos # ifndef HAVE_MEMPCPY
    163  1.1  christos static void *mempcpy (void *dest, const void *src, size_t n);
    164  1.1  christos # endif
    165  1.1  christos #endif
    166  1.1  christos 
    167  1.1  christos /* Amount to increase buffer size by in each try.  */
    168  1.1  christos #define PATH_INCR 32
    169  1.1  christos 
    170  1.1  christos /* The following is from pathmax.h.  */
    171  1.1  christos /* Non-POSIX BSD systems might have gcc's limits.h, which doesn't define
    172  1.1  christos    PATH_MAX but might cause redefinition warnings when sys/param.h is
    173  1.1  christos    later included (as on MORE/BSD 4.3).  */
    174  1.1  christos #if defined _POSIX_VERSION || (defined HAVE_LIMITS_H && !defined __GNUC__)
    175  1.1  christos # include <limits.h>
    176  1.1  christos #endif
    177  1.1  christos 
    178  1.1  christos #ifndef _POSIX_PATH_MAX
    179  1.1  christos # define _POSIX_PATH_MAX 255
    180  1.1  christos #endif
    181  1.1  christos 
    182  1.1  christos #if !defined PATH_MAX && defined _PC_PATH_MAX
    183  1.1  christos # define PATH_MAX (pathconf ("/", _PC_PATH_MAX) < 1 ? 1024 : pathconf ("/", _PC_PATH_MAX))
    184  1.1  christos #endif
    185  1.1  christos 
    186  1.1  christos /* Don't include sys/param.h if it already has been.  */
    187  1.1  christos #if defined HAVE_SYS_PARAM_H && !defined PATH_MAX && !defined MAXPATHLEN
    188  1.1  christos # include <sys/param.h>
    189  1.1  christos #endif
    190  1.1  christos 
    191  1.1  christos #if !defined PATH_MAX && defined MAXPATHLEN
    192  1.1  christos # define PATH_MAX MAXPATHLEN
    193  1.1  christos #endif
    194  1.1  christos 
    195  1.1  christos #ifndef PATH_MAX
    196  1.1  christos # define PATH_MAX _POSIX_PATH_MAX
    197  1.1  christos #endif
    198  1.1  christos 
    199  1.1  christos /* Pathname support.
    200  1.1  christos    ISSLASH(C)           tests whether C is a directory separator character.
    201  1.1  christos    IS_ABSOLUTE_PATH(P)  tests whether P is an absolute path.  If it is not,
    202  1.1  christos                         it may be concatenated to a directory pathname.
    203  1.1  christos    IS_PATH_WITH_DIR(P)  tests whether P contains a directory specification.
    204  1.1  christos  */
    205  1.1  christos #if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__
    206  1.1  christos   /* Win32, OS/2, DOS */
    207  1.1  christos # define ISSLASH(C) ((C) == '/' || (C) == '\\')
    208  1.1  christos # define HAS_DEVICE(P) \
    209  1.1  christos     ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
    210  1.1  christos      && (P)[1] == ':')
    211  1.1  christos # define IS_ABSOLUTE_PATH(P) (ISSLASH ((P)[0]) || HAS_DEVICE (P))
    212  1.1  christos # define IS_PATH_WITH_DIR(P) \
    213  1.1  christos     (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P))
    214  1.1  christos #else
    215  1.1  christos   /* Unix */
    216  1.1  christos # define ISSLASH(C) ((C) == '/')
    217  1.1  christos # define IS_ABSOLUTE_PATH(P) ISSLASH ((P)[0])
    218  1.1  christos # define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL)
    219  1.1  christos #endif
    220  1.1  christos 
    221  1.1  christos /* This is the type used for the search tree where known translations
    222  1.1  christos    are stored.  */
    223  1.1  christos struct known_translation_t
    224  1.1  christos {
    225  1.1  christos   /* Domain in which to search.  */
    226  1.1  christos   char *domainname;
    227  1.1  christos 
    228  1.1  christos   /* The category.  */
    229  1.1  christos   int category;
    230  1.1  christos 
    231  1.1  christos   /* State of the catalog counter at the point the string was found.  */
    232  1.1  christos   int counter;
    233  1.1  christos 
    234  1.1  christos   /* Catalog where the string was found.  */
    235  1.1  christos   struct loaded_l10nfile *domain;
    236  1.1  christos 
    237  1.1  christos   /* And finally the translation.  */
    238  1.1  christos   const char *translation;
    239  1.1  christos   size_t translation_length;
    240  1.1  christos 
    241  1.1  christos   /* Pointer to the string in question.  */
    242  1.1  christos   char msgid[ZERO];
    243  1.1  christos };
    244  1.1  christos 
    245  1.1  christos /* Root of the search tree with known translations.  We can use this
    246  1.1  christos    only if the system provides the `tsearch' function family.  */
    247  1.1  christos #if defined HAVE_TSEARCH || defined _LIBC
    248  1.1  christos # include <search.h>
    249  1.1  christos 
    250  1.1  christos static void *root;
    251  1.1  christos 
    252  1.1  christos # ifdef _LIBC
    253  1.1  christos #  define tsearch __tsearch
    254  1.1  christos # endif
    255  1.1  christos 
    256  1.1  christos /* Function to compare two entries in the table of known translations.  */
    257  1.1  christos static int
    258  1.1  christos transcmp (const void *p1, const void *p2)
    259  1.1  christos {
    260  1.1  christos   const struct known_translation_t *s1;
    261  1.1  christos   const struct known_translation_t *s2;
    262  1.1  christos   int result;
    263  1.1  christos 
    264  1.1  christos   s1 = (const struct known_translation_t *) p1;
    265  1.1  christos   s2 = (const struct known_translation_t *) p2;
    266  1.1  christos 
    267  1.1  christos   result = strcmp (s1->msgid, s2->msgid);
    268  1.1  christos   if (result == 0)
    269  1.1  christos     {
    270  1.1  christos       result = strcmp (s1->domainname, s2->domainname);
    271  1.1  christos       if (result == 0)
    272  1.1  christos 	/* We compare the category last (though this is the cheapest
    273  1.1  christos 	   operation) since it is hopefully always the same (namely
    274  1.1  christos 	   LC_MESSAGES).  */
    275  1.1  christos 	result = s1->category - s2->category;
    276  1.1  christos     }
    277  1.1  christos 
    278  1.1  christos   return result;
    279  1.1  christos }
    280  1.1  christos #endif
    281  1.1  christos 
    282  1.1  christos #ifndef INTVARDEF
    283  1.1  christos # define INTVARDEF(name)
    284  1.1  christos #endif
    285  1.1  christos #ifndef INTUSE
    286  1.1  christos # define INTUSE(name) name
    287  1.1  christos #endif
    288  1.1  christos 
    289  1.1  christos /* Name of the default domain used for gettext(3) prior any call to
    290  1.1  christos    textdomain(3).  The default value for this is "messages".  */
    291  1.1  christos const char _nl_default_default_domain[] attribute_hidden = "messages";
    292  1.1  christos 
    293  1.1  christos /* Value used as the default domain for gettext(3).  */
    294  1.1  christos const char *_nl_current_default_domain attribute_hidden
    295  1.1  christos      = _nl_default_default_domain;
    296  1.1  christos 
    297  1.1  christos /* Contains the default location of the message catalogs.  */
    298  1.1  christos #if defined __EMX__
    299  1.1  christos extern const char _nl_default_dirname[];
    300  1.1  christos #else
    301  1.1  christos const char _nl_default_dirname[] = LOCALEDIR;
    302  1.1  christos INTVARDEF (_nl_default_dirname)
    303  1.1  christos #endif
    304  1.1  christos 
    305  1.1  christos /* List with bindings of specific domains created by bindtextdomain()
    306  1.1  christos    calls.  */
    307  1.1  christos struct binding *_nl_domain_bindings;
    308  1.1  christos 
    309  1.1  christos /* Prototypes for local functions.  */
    310  1.1  christos static char *plural_lookup (struct loaded_l10nfile *domain,
    311  1.1  christos 			    unsigned long int n,
    312  1.1  christos 			    const char *translation, size_t translation_len)
    313  1.1  christos      internal_function;
    314  1.1  christos static const char *guess_category_value (int category,
    315  1.1  christos 					 const char *categoryname)
    316  1.1  christos      internal_function;
    317  1.1  christos #ifdef _LIBC
    318  1.1  christos # include "../locale/localeinfo.h"
    319  1.1  christos # define category_to_name(category)	_nl_category_names[category]
    320  1.1  christos #else
    321  1.1  christos static const char *category_to_name (int category) internal_function;
    322  1.1  christos #endif
    323  1.1  christos 
    324  1.1  christos 
    325  1.1  christos /* For those loosing systems which don't have `alloca' we have to add
    326  1.1  christos    some additional code emulating it.  */
    327  1.1  christos #ifdef HAVE_ALLOCA
    328  1.1  christos /* Nothing has to be done.  */
    329  1.1  christos # define freea(p) /* nothing */
    330  1.1  christos # define ADD_BLOCK(list, address) /* nothing */
    331  1.1  christos # define FREE_BLOCKS(list) /* nothing */
    332  1.1  christos #else
    333  1.1  christos struct block_list
    334  1.1  christos {
    335  1.1  christos   void *address;
    336  1.1  christos   struct block_list *next;
    337  1.1  christos };
    338  1.1  christos # define ADD_BLOCK(list, addr)						      \
    339  1.1  christos   do {									      \
    340  1.1  christos     struct block_list *newp = (struct block_list *) malloc (sizeof (*newp));  \
    341  1.1  christos     /* If we cannot get a free block we cannot add the new element to	      \
    342  1.1  christos        the list.  */							      \
    343  1.1  christos     if (newp != NULL) {							      \
    344  1.1  christos       newp->address = (addr);						      \
    345  1.1  christos       newp->next = (list);						      \
    346  1.1  christos       (list) = newp;							      \
    347  1.1  christos     }									      \
    348  1.1  christos   } while (0)
    349  1.1  christos # define FREE_BLOCKS(list)						      \
    350  1.1  christos   do {									      \
    351  1.1  christos     while (list != NULL) {						      \
    352  1.1  christos       struct block_list *old = list;					      \
    353  1.1  christos       list = list->next;						      \
    354  1.1  christos       free (old->address);						      \
    355  1.1  christos       free (old);							      \
    356  1.1  christos     }									      \
    357  1.1  christos   } while (0)
    358  1.1  christos # undef alloca
    359  1.1  christos # define alloca(size) (malloc (size))
    360  1.1  christos # define freea(p) free (p)
    361  1.1  christos #endif	/* have alloca */
    362  1.1  christos 
    363  1.1  christos 
    364  1.1  christos #ifdef _LIBC
    365  1.1  christos /* List of blocks allocated for translations.  */
    366  1.1  christos typedef struct transmem_list
    367  1.1  christos {
    368  1.1  christos   struct transmem_list *next;
    369  1.1  christos   char data[ZERO];
    370  1.1  christos } transmem_block_t;
    371  1.1  christos static struct transmem_list *transmem_list;
    372  1.1  christos #else
    373  1.1  christos typedef unsigned char transmem_block_t;
    374  1.1  christos #endif
    375  1.1  christos 
    376  1.1  christos 
    377  1.1  christos /* Names for the libintl functions are a problem.  They must not clash
    378  1.1  christos    with existing names and they should follow ANSI C.  But this source
    379  1.1  christos    code is also used in GNU C Library where the names have a __
    380  1.1  christos    prefix.  So we have to make a difference here.  */
    381  1.1  christos #ifdef _LIBC
    382  1.1  christos # define DCIGETTEXT __dcigettext
    383  1.1  christos #else
    384  1.1  christos # define DCIGETTEXT libintl_dcigettext
    385  1.1  christos #endif
    386  1.1  christos 
    387  1.1  christos /* Lock variable to protect the global data in the gettext implementation.  */
    388  1.1  christos #ifdef _LIBC
    389  1.1  christos __libc_rwlock_define_initialized (, _nl_state_lock attribute_hidden)
    390  1.1  christos #endif
    391  1.1  christos 
    392  1.1  christos /* Checking whether the binaries runs SUID must be done and glibc provides
    393  1.1  christos    easier methods therefore we make a difference here.  */
    394  1.1  christos #ifdef _LIBC
    395  1.1  christos # define ENABLE_SECURE __libc_enable_secure
    396  1.1  christos # define DETERMINE_SECURE
    397  1.1  christos #else
    398  1.1  christos # ifndef HAVE_GETUID
    399  1.1  christos #  define getuid() 0
    400  1.1  christos # endif
    401  1.1  christos # ifndef HAVE_GETGID
    402  1.1  christos #  define getgid() 0
    403  1.1  christos # endif
    404  1.1  christos # ifndef HAVE_GETEUID
    405  1.1  christos #  define geteuid() getuid()
    406  1.1  christos # endif
    407  1.1  christos # ifndef HAVE_GETEGID
    408  1.1  christos #  define getegid() getgid()
    409  1.1  christos # endif
    410  1.1  christos static int enable_secure;
    411  1.1  christos # define ENABLE_SECURE (enable_secure == 1)
    412  1.1  christos # define DETERMINE_SECURE \
    413  1.1  christos   if (enable_secure == 0)						      \
    414  1.1  christos     {									      \
    415  1.1  christos       if (getuid () != geteuid () || getgid () != getegid ())		      \
    416  1.1  christos 	enable_secure = 1;						      \
    417  1.1  christos       else								      \
    418  1.1  christos 	enable_secure = -1;						      \
    419  1.1  christos     }
    420  1.1  christos #endif
    421  1.1  christos 
    422  1.1  christos /* Get the function to evaluate the plural expression.  */
    423  1.1  christos #include "eval-plural.h"
    424  1.1  christos 
    425  1.1  christos /* Look up MSGID in the DOMAINNAME message catalog for the current
    426  1.1  christos    CATEGORY locale and, if PLURAL is nonzero, search over string
    427  1.1  christos    depending on the plural form determined by N.  */
    428  1.1  christos char *
    429  1.1  christos DCIGETTEXT (const char *domainname, const char *msgid1, const char *msgid2,
    430  1.1  christos 	    int plural, unsigned long int n, int category)
    431  1.1  christos {
    432  1.1  christos #ifndef HAVE_ALLOCA
    433  1.1  christos   struct block_list *block_list = NULL;
    434  1.1  christos #endif
    435  1.1  christos   struct loaded_l10nfile *domain;
    436  1.1  christos   struct binding *binding;
    437  1.1  christos   const char *categoryname;
    438  1.1  christos   const char *categoryvalue;
    439  1.1  christos   char *dirname, *xdomainname;
    440  1.1  christos   char *single_locale;
    441  1.1  christos   char *retval;
    442  1.1  christos   size_t retlen;
    443  1.1  christos   int saved_errno;
    444  1.1  christos #if defined HAVE_TSEARCH || defined _LIBC
    445  1.1  christos   struct known_translation_t *search;
    446  1.1  christos   struct known_translation_t **foundp = NULL;
    447  1.1  christos   size_t msgid_len;
    448  1.1  christos #endif
    449  1.1  christos   size_t domainname_len;
    450  1.1  christos 
    451  1.1  christos   /* If no real MSGID is given return NULL.  */
    452  1.1  christos   if (msgid1 == NULL)
    453  1.1  christos     return NULL;
    454  1.1  christos 
    455  1.1  christos #ifdef _LIBC
    456  1.1  christos   if (category < 0 || category >= __LC_LAST || category == LC_ALL)
    457  1.1  christos     /* Bogus.  */
    458  1.1  christos     return (plural == 0
    459  1.1  christos 	    ? (char *) msgid1
    460  1.1  christos 	    /* Use the Germanic plural rule.  */
    461  1.1  christos 	    : n == 1 ? (char *) msgid1 : (char *) msgid2);
    462  1.1  christos #endif
    463  1.1  christos 
    464  1.1  christos   __libc_rwlock_rdlock (_nl_state_lock);
    465  1.1  christos 
    466  1.1  christos   /* If DOMAINNAME is NULL, we are interested in the default domain.  If
    467  1.1  christos      CATEGORY is not LC_MESSAGES this might not make much sense but the
    468  1.1  christos      definition left this undefined.  */
    469  1.1  christos   if (domainname == NULL)
    470  1.1  christos     domainname = _nl_current_default_domain;
    471  1.1  christos 
    472  1.1  christos   /* OS/2 specific: backward compatibility with older libintl versions  */
    473  1.1  christos #ifdef LC_MESSAGES_COMPAT
    474  1.1  christos   if (category == LC_MESSAGES_COMPAT)
    475  1.1  christos     category = LC_MESSAGES;
    476  1.1  christos #endif
    477  1.1  christos 
    478  1.1  christos #if defined HAVE_TSEARCH || defined _LIBC
    479  1.1  christos   msgid_len = strlen (msgid1) + 1;
    480  1.1  christos 
    481  1.1  christos   /* Try to find the translation among those which we found at
    482  1.1  christos      some time.  */
    483  1.1  christos   search = (struct known_translation_t *)
    484  1.1  christos 	   alloca (offsetof (struct known_translation_t, msgid) + msgid_len);
    485  1.1  christos   memcpy (search->msgid, msgid1, msgid_len);
    486  1.1  christos   search->domainname = (char *) domainname;
    487  1.1  christos   search->category = category;
    488  1.1  christos 
    489  1.1  christos   foundp = (struct known_translation_t **) tfind (search, &root, transcmp);
    490  1.1  christos   freea (search);
    491  1.1  christos   if (foundp != NULL && (*foundp)->counter == _nl_msg_cat_cntr)
    492  1.1  christos     {
    493  1.1  christos       /* Now deal with plural.  */
    494  1.1  christos       if (plural)
    495  1.1  christos 	retval = plural_lookup ((*foundp)->domain, n, (*foundp)->translation,
    496  1.1  christos 				(*foundp)->translation_length);
    497  1.1  christos       else
    498  1.1  christos 	retval = (char *) (*foundp)->translation;
    499  1.1  christos 
    500  1.1  christos       __libc_rwlock_unlock (_nl_state_lock);
    501  1.1  christos       return retval;
    502  1.1  christos     }
    503  1.1  christos #endif
    504  1.1  christos 
    505  1.1  christos   /* Preserve the `errno' value.  */
    506  1.1  christos   saved_errno = errno;
    507  1.1  christos 
    508  1.1  christos   /* See whether this is a SUID binary or not.  */
    509  1.1  christos   DETERMINE_SECURE;
    510  1.1  christos 
    511  1.1  christos   /* First find matching binding.  */
    512  1.1  christos   for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next)
    513  1.1  christos     {
    514  1.1  christos       int compare = strcmp (domainname, binding->domainname);
    515  1.1  christos       if (compare == 0)
    516  1.1  christos 	/* We found it!  */
    517  1.1  christos 	break;
    518  1.1  christos       if (compare < 0)
    519  1.1  christos 	{
    520  1.1  christos 	  /* It is not in the list.  */
    521  1.1  christos 	  binding = NULL;
    522  1.1  christos 	  break;
    523  1.1  christos 	}
    524  1.1  christos     }
    525  1.1  christos 
    526  1.1  christos   if (binding == NULL)
    527  1.1  christos     dirname = (char *) INTUSE(_nl_default_dirname);
    528  1.1  christos   else if (IS_ABSOLUTE_PATH (binding->dirname))
    529  1.1  christos     dirname = binding->dirname;
    530  1.1  christos   else
    531  1.1  christos     {
    532  1.1  christos       /* We have a relative path.  Make it absolute now.  */
    533  1.1  christos       size_t dirname_len = strlen (binding->dirname) + 1;
    534  1.1  christos       size_t path_max;
    535  1.1  christos       char *ret;
    536  1.1  christos 
    537  1.1  christos       path_max = (unsigned int) PATH_MAX;
    538  1.1  christos       path_max += 2;		/* The getcwd docs say to do this.  */
    539  1.1  christos 
    540  1.1  christos       for (;;)
    541  1.1  christos 	{
    542  1.1  christos 	  dirname = (char *) alloca (path_max + dirname_len);
    543  1.1  christos 	  ADD_BLOCK (block_list, dirname);
    544  1.1  christos 
    545  1.1  christos 	  __set_errno (0);
    546  1.1  christos 	  ret = getcwd (dirname, path_max);
    547  1.1  christos 	  if (ret != NULL || errno != ERANGE)
    548  1.1  christos 	    break;
    549  1.1  christos 
    550  1.1  christos 	  path_max += path_max / 2;
    551  1.1  christos 	  path_max += PATH_INCR;
    552  1.1  christos 	}
    553  1.1  christos 
    554  1.1  christos       if (ret == NULL)
    555  1.1  christos 	/* We cannot get the current working directory.  Don't signal an
    556  1.1  christos 	   error but simply return the default string.  */
    557  1.1  christos 	goto return_untranslated;
    558  1.1  christos 
    559  1.1  christos       stpcpy (stpcpy (strchr (dirname, '\0'), "/"), binding->dirname);
    560  1.1  christos     }
    561  1.1  christos 
    562  1.1  christos   /* Now determine the symbolic name of CATEGORY and its value.  */
    563  1.1  christos   categoryname = category_to_name (category);
    564  1.1  christos   categoryvalue = guess_category_value (category, categoryname);
    565  1.1  christos 
    566  1.1  christos   domainname_len = strlen (domainname);
    567  1.1  christos   xdomainname = (char *) alloca (strlen (categoryname)
    568  1.1  christos 				 + domainname_len + 5);
    569  1.1  christos   ADD_BLOCK (block_list, xdomainname);
    570  1.1  christos 
    571  1.1  christos   stpcpy (mempcpy (stpcpy (stpcpy (xdomainname, categoryname), "/"),
    572  1.1  christos 		  domainname, domainname_len),
    573  1.1  christos 	  ".mo");
    574  1.1  christos 
    575  1.1  christos   /* Creating working area.  */
    576  1.1  christos   single_locale = (char *) alloca (strlen (categoryvalue) + 1);
    577  1.1  christos   ADD_BLOCK (block_list, single_locale);
    578  1.1  christos 
    579  1.1  christos 
    580  1.1  christos   /* Search for the given string.  This is a loop because we perhaps
    581  1.1  christos      got an ordered list of languages to consider for the translation.  */
    582  1.1  christos   while (1)
    583  1.1  christos     {
    584  1.1  christos       /* Make CATEGORYVALUE point to the next element of the list.  */
    585  1.1  christos       while (categoryvalue[0] != '\0' && categoryvalue[0] == ':')
    586  1.1  christos 	++categoryvalue;
    587  1.1  christos       if (categoryvalue[0] == '\0')
    588  1.1  christos 	{
    589  1.1  christos 	  /* The whole contents of CATEGORYVALUE has been searched but
    590  1.1  christos 	     no valid entry has been found.  We solve this situation
    591  1.1  christos 	     by implicitly appending a "C" entry, i.e. no translation
    592  1.1  christos 	     will take place.  */
    593  1.1  christos 	  single_locale[0] = 'C';
    594  1.1  christos 	  single_locale[1] = '\0';
    595  1.1  christos 	}
    596  1.1  christos       else
    597  1.1  christos 	{
    598  1.1  christos 	  char *cp = single_locale;
    599  1.1  christos 	  while (categoryvalue[0] != '\0' && categoryvalue[0] != ':')
    600  1.1  christos 	    *cp++ = *categoryvalue++;
    601  1.1  christos 	  *cp = '\0';
    602  1.1  christos 
    603  1.1  christos 	  /* When this is a SUID binary we must not allow accessing files
    604  1.1  christos 	     outside the dedicated directories.  */
    605  1.1  christos 	  if (ENABLE_SECURE && IS_PATH_WITH_DIR (single_locale))
    606  1.1  christos 	    /* Ingore this entry.  */
    607  1.1  christos 	    continue;
    608  1.1  christos 	}
    609  1.1  christos 
    610  1.1  christos       /* If the current locale value is C (or POSIX) we don't load a
    611  1.1  christos 	 domain.  Return the MSGID.  */
    612  1.1  christos       if (strcmp (single_locale, "C") == 0
    613  1.1  christos 	  || strcmp (single_locale, "POSIX") == 0)
    614  1.1  christos 	break;
    615  1.1  christos 
    616  1.1  christos       /* Find structure describing the message catalog matching the
    617  1.1  christos 	 DOMAINNAME and CATEGORY.  */
    618  1.1  christos       domain = _nl_find_domain (dirname, single_locale, xdomainname, binding);
    619  1.1  christos 
    620  1.1  christos       if (domain != NULL)
    621  1.1  christos 	{
    622  1.1  christos 	  retval = _nl_find_msg (domain, binding, msgid1, &retlen);
    623  1.1  christos 
    624  1.1  christos 	  if (retval == NULL)
    625  1.1  christos 	    {
    626  1.1  christos 	      int cnt;
    627  1.1  christos 
    628  1.1  christos 	      for (cnt = 0; domain->successor[cnt] != NULL; ++cnt)
    629  1.1  christos 		{
    630  1.1  christos 		  retval = _nl_find_msg (domain->successor[cnt], binding,
    631  1.1  christos 					 msgid1, &retlen);
    632  1.1  christos 
    633  1.1  christos 		  if (retval != NULL)
    634  1.1  christos 		    {
    635  1.1  christos 		      domain = domain->successor[cnt];
    636  1.1  christos 		      break;
    637  1.1  christos 		    }
    638  1.1  christos 		}
    639  1.1  christos 	    }
    640  1.1  christos 
    641  1.1  christos 	  if (retval != NULL)
    642  1.1  christos 	    {
    643  1.1  christos 	      /* Found the translation of MSGID1 in domain DOMAIN:
    644  1.1  christos 		 starting at RETVAL, RETLEN bytes.  */
    645  1.1  christos 	      FREE_BLOCKS (block_list);
    646  1.1  christos #if defined HAVE_TSEARCH || defined _LIBC
    647  1.1  christos 	      if (foundp == NULL)
    648  1.1  christos 		{
    649  1.1  christos 		  /* Create a new entry and add it to the search tree.  */
    650  1.1  christos 		  struct known_translation_t *newp;
    651  1.1  christos 
    652  1.1  christos 		  newp = (struct known_translation_t *)
    653  1.1  christos 		    malloc (offsetof (struct known_translation_t, msgid)
    654  1.1  christos 			    + msgid_len + domainname_len + 1);
    655  1.1  christos 		  if (newp != NULL)
    656  1.1  christos 		    {
    657  1.1  christos 		      newp->domainname =
    658  1.1  christos 			mempcpy (newp->msgid, msgid1, msgid_len);
    659  1.1  christos 		      memcpy (newp->domainname, domainname, domainname_len + 1);
    660  1.1  christos 		      newp->category = category;
    661  1.1  christos 		      newp->counter = _nl_msg_cat_cntr;
    662  1.1  christos 		      newp->domain = domain;
    663  1.1  christos 		      newp->translation = retval;
    664  1.1  christos 		      newp->translation_length = retlen;
    665  1.1  christos 
    666  1.1  christos 		      /* Insert the entry in the search tree.  */
    667  1.1  christos 		      foundp = (struct known_translation_t **)
    668  1.1  christos 			tsearch (newp, &root, transcmp);
    669  1.1  christos 		      if (foundp == NULL
    670  1.1  christos 			  || __builtin_expect (*foundp != newp, 0))
    671  1.1  christos 			/* The insert failed.  */
    672  1.1  christos 			free (newp);
    673  1.1  christos 		    }
    674  1.1  christos 		}
    675  1.1  christos 	      else
    676  1.1  christos 		{
    677  1.1  christos 		  /* We can update the existing entry.  */
    678  1.1  christos 		  (*foundp)->counter = _nl_msg_cat_cntr;
    679  1.1  christos 		  (*foundp)->domain = domain;
    680  1.1  christos 		  (*foundp)->translation = retval;
    681  1.1  christos 		  (*foundp)->translation_length = retlen;
    682  1.1  christos 		}
    683  1.1  christos #endif
    684  1.1  christos 	      __set_errno (saved_errno);
    685  1.1  christos 
    686  1.1  christos 	      /* Now deal with plural.  */
    687  1.1  christos 	      if (plural)
    688  1.1  christos 		retval = plural_lookup (domain, n, retval, retlen);
    689  1.1  christos 
    690  1.1  christos 	      __libc_rwlock_unlock (_nl_state_lock);
    691  1.1  christos 	      return retval;
    692  1.1  christos 	    }
    693  1.1  christos 	}
    694  1.1  christos     }
    695  1.1  christos 
    696  1.1  christos  return_untranslated:
    697  1.1  christos   /* Return the untranslated MSGID.  */
    698  1.1  christos   FREE_BLOCKS (block_list);
    699  1.1  christos   __libc_rwlock_unlock (_nl_state_lock);
    700  1.1  christos #ifndef _LIBC
    701  1.1  christos   if (!ENABLE_SECURE)
    702  1.1  christos     {
    703  1.1  christos       extern void _nl_log_untranslated (const char *logfilename,
    704  1.1  christos 					const char *domainname,
    705  1.1  christos 					const char *msgid1, const char *msgid2,
    706  1.1  christos 					int plural);
    707  1.1  christos       const char *logfilename = getenv ("GETTEXT_LOG_UNTRANSLATED");
    708  1.1  christos 
    709  1.1  christos       if (logfilename != NULL && logfilename[0] != '\0')
    710  1.1  christos 	_nl_log_untranslated (logfilename, domainname, msgid1, msgid2, plural);
    711  1.1  christos     }
    712  1.1  christos #endif
    713  1.1  christos   __set_errno (saved_errno);
    714  1.1  christos   return (plural == 0
    715  1.1  christos 	  ? (char *) msgid1
    716  1.1  christos 	  /* Use the Germanic plural rule.  */
    717  1.1  christos 	  : n == 1 ? (char *) msgid1 : (char *) msgid2);
    718  1.1  christos }
    719  1.1  christos 
    720  1.1  christos 
    721  1.1  christos char *
    722  1.1  christos internal_function
    723  1.1  christos _nl_find_msg (struct loaded_l10nfile *domain_file,
    724  1.1  christos 	      struct binding *domainbinding, const char *msgid,
    725  1.1  christos 	      size_t *lengthp)
    726  1.1  christos {
    727  1.1  christos   struct loaded_domain *domain;
    728  1.1  christos   nls_uint32 nstrings;
    729  1.1  christos   size_t act;
    730  1.1  christos   char *result;
    731  1.1  christos   size_t resultlen;
    732  1.1  christos 
    733  1.1  christos   if (domain_file->decided == 0)
    734  1.1  christos     _nl_load_domain (domain_file, domainbinding);
    735  1.1  christos 
    736  1.1  christos   if (domain_file->data == NULL)
    737  1.1  christos     return NULL;
    738  1.1  christos 
    739  1.1  christos   domain = (struct loaded_domain *) domain_file->data;
    740  1.1  christos 
    741  1.1  christos   nstrings = domain->nstrings;
    742  1.1  christos 
    743  1.1  christos   /* Locate the MSGID and its translation.  */
    744  1.1  christos   if (domain->hash_tab != NULL)
    745  1.1  christos     {
    746  1.1  christos       /* Use the hashing table.  */
    747  1.1  christos       nls_uint32 len = strlen (msgid);
    748  1.1  christos       nls_uint32 hash_val = hash_string (msgid);
    749  1.1  christos       nls_uint32 idx = hash_val % domain->hash_size;
    750  1.1  christos       nls_uint32 incr = 1 + (hash_val % (domain->hash_size - 2));
    751  1.1  christos 
    752  1.1  christos       while (1)
    753  1.1  christos 	{
    754  1.1  christos 	  nls_uint32 nstr =
    755  1.1  christos 	    W (domain->must_swap_hash_tab, domain->hash_tab[idx]);
    756  1.1  christos 
    757  1.1  christos 	  if (nstr == 0)
    758  1.1  christos 	    /* Hash table entry is empty.  */
    759  1.1  christos 	    return NULL;
    760  1.1  christos 
    761  1.1  christos 	  nstr--;
    762  1.1  christos 
    763  1.1  christos 	  /* Compare msgid with the original string at index nstr.
    764  1.1  christos 	     We compare the lengths with >=, not ==, because plural entries
    765  1.1  christos 	     are represented by strings with an embedded NUL.  */
    766  1.1  christos 	  if (nstr < nstrings
    767  1.1  christos 	      ? W (domain->must_swap, domain->orig_tab[nstr].length) >= len
    768  1.1  christos 		&& (strcmp (msgid,
    769  1.1  christos 			    domain->data + W (domain->must_swap,
    770  1.1  christos 					      domain->orig_tab[nstr].offset))
    771  1.1  christos 		    == 0)
    772  1.1  christos 	      : domain->orig_sysdep_tab[nstr - nstrings].length > len
    773  1.1  christos 		&& (strcmp (msgid,
    774  1.1  christos 			    domain->orig_sysdep_tab[nstr - nstrings].pointer)
    775  1.1  christos 		    == 0))
    776  1.1  christos 	    {
    777  1.1  christos 	      act = nstr;
    778  1.1  christos 	      goto found;
    779  1.1  christos 	    }
    780  1.1  christos 
    781  1.1  christos 	  if (idx >= domain->hash_size - incr)
    782  1.1  christos 	    idx -= domain->hash_size - incr;
    783  1.1  christos 	  else
    784  1.1  christos 	    idx += incr;
    785  1.1  christos 	}
    786  1.1  christos       /* NOTREACHED */
    787  1.1  christos     }
    788  1.1  christos   else
    789  1.1  christos     {
    790  1.1  christos       /* Try the default method:  binary search in the sorted array of
    791  1.1  christos 	 messages.  */
    792  1.1  christos       size_t top, bottom;
    793  1.1  christos 
    794  1.1  christos       bottom = 0;
    795  1.1  christos       top = nstrings;
    796  1.1  christos       while (bottom < top)
    797  1.1  christos 	{
    798  1.1  christos 	  int cmp_val;
    799  1.1  christos 
    800  1.1  christos 	  act = (bottom + top) / 2;
    801  1.1  christos 	  cmp_val = strcmp (msgid, (domain->data
    802  1.1  christos 				    + W (domain->must_swap,
    803  1.1  christos 					 domain->orig_tab[act].offset)));
    804  1.1  christos 	  if (cmp_val < 0)
    805  1.1  christos 	    top = act;
    806  1.1  christos 	  else if (cmp_val > 0)
    807  1.1  christos 	    bottom = act + 1;
    808  1.1  christos 	  else
    809  1.1  christos 	    goto found;
    810  1.1  christos 	}
    811  1.1  christos       /* No translation was found.  */
    812  1.1  christos       return NULL;
    813  1.1  christos     }
    814  1.1  christos 
    815  1.1  christos  found:
    816  1.1  christos   /* The translation was found at index ACT.  If we have to convert the
    817  1.1  christos      string to use a different character set, this is the time.  */
    818  1.1  christos   if (act < nstrings)
    819  1.1  christos     {
    820  1.1  christos       result = (char *)
    821  1.1  christos 	(domain->data + W (domain->must_swap, domain->trans_tab[act].offset));
    822  1.1  christos       resultlen = W (domain->must_swap, domain->trans_tab[act].length) + 1;
    823  1.1  christos     }
    824  1.1  christos   else
    825  1.1  christos     {
    826  1.1  christos       result = (char *) domain->trans_sysdep_tab[act - nstrings].pointer;
    827  1.1  christos       resultlen = domain->trans_sysdep_tab[act - nstrings].length;
    828  1.1  christos     }
    829  1.1  christos 
    830  1.1  christos #if defined _LIBC || HAVE_ICONV
    831  1.1  christos   if (domain->codeset_cntr
    832  1.1  christos       != (domainbinding != NULL ? domainbinding->codeset_cntr : 0))
    833  1.1  christos     {
    834  1.1  christos       /* The domain's codeset has changed through bind_textdomain_codeset()
    835  1.1  christos 	 since the message catalog was initialized or last accessed.  We
    836  1.1  christos 	 have to reinitialize the converter.  */
    837  1.1  christos       _nl_free_domain_conv (domain);
    838  1.1  christos       _nl_init_domain_conv (domain_file, domain, domainbinding);
    839  1.1  christos     }
    840  1.1  christos 
    841  1.1  christos   if (
    842  1.1  christos # ifdef _LIBC
    843  1.1  christos       domain->conv != (__gconv_t) -1
    844  1.1  christos # else
    845  1.1  christos #  if HAVE_ICONV
    846  1.1  christos       domain->conv != (iconv_t) -1
    847  1.1  christos #  endif
    848  1.1  christos # endif
    849  1.1  christos       )
    850  1.1  christos     {
    851  1.1  christos       /* We are supposed to do a conversion.  First allocate an
    852  1.1  christos 	 appropriate table with the same structure as the table
    853  1.1  christos 	 of translations in the file, where we can put the pointers
    854  1.1  christos 	 to the converted strings in.
    855  1.1  christos 	 There is a slight complication with plural entries.  They
    856  1.1  christos 	 are represented by consecutive NUL terminated strings.  We
    857  1.1  christos 	 handle this case by converting RESULTLEN bytes, including
    858  1.1  christos 	 NULs.  */
    859  1.1  christos 
    860  1.1  christos       if (domain->conv_tab == NULL
    861  1.1  christos 	  && ((domain->conv_tab =
    862  1.1  christos 		 (char **) calloc (nstrings + domain->n_sysdep_strings,
    863  1.1  christos 				   sizeof (char *)))
    864  1.1  christos 	      == NULL))
    865  1.1  christos 	/* Mark that we didn't succeed allocating a table.  */
    866  1.1  christos 	domain->conv_tab = (char **) -1;
    867  1.1  christos 
    868  1.1  christos       if (__builtin_expect (domain->conv_tab == (char **) -1, 0))
    869  1.1  christos 	/* Nothing we can do, no more memory.  */
    870  1.1  christos 	goto converted;
    871  1.1  christos 
    872  1.1  christos       if (domain->conv_tab[act] == NULL)
    873  1.1  christos 	{
    874  1.1  christos 	  /* We haven't used this string so far, so it is not
    875  1.1  christos 	     translated yet.  Do this now.  */
    876  1.1  christos 	  /* We use a bit more efficient memory handling.
    877  1.1  christos 	     We allocate always larger blocks which get used over
    878  1.1  christos 	     time.  This is faster than many small allocations.   */
    879  1.1  christos 	  __libc_lock_define_initialized (static, lock)
    880  1.1  christos # define INITIAL_BLOCK_SIZE	4080
    881  1.1  christos 	  static unsigned char *freemem;
    882  1.1  christos 	  static size_t freemem_size;
    883  1.1  christos 
    884  1.1  christos 	  const unsigned char *inbuf;
    885  1.1  christos 	  unsigned char *outbuf;
    886  1.1  christos 	  int malloc_count;
    887  1.1  christos # ifndef _LIBC
    888  1.1  christos 	  transmem_block_t *transmem_list = NULL;
    889  1.1  christos # endif
    890  1.1  christos 
    891  1.1  christos 	  __libc_lock_lock (lock);
    892  1.1  christos 
    893  1.1  christos 	  inbuf = (const unsigned char *) result;
    894  1.1  christos 	  outbuf = freemem + sizeof (size_t);
    895  1.1  christos 
    896  1.1  christos 	  malloc_count = 0;
    897  1.1  christos 	  while (1)
    898  1.1  christos 	    {
    899  1.1  christos 	      transmem_block_t *newmem;
    900  1.1  christos # ifdef _LIBC
    901  1.1  christos 	      size_t non_reversible;
    902  1.1  christos 	      int res;
    903  1.1  christos 
    904  1.1  christos 	      if (freemem_size < sizeof (size_t))
    905  1.1  christos 		goto resize_freemem;
    906  1.1  christos 
    907  1.1  christos 	      res = __gconv (domain->conv,
    908  1.1  christos 			     &inbuf, inbuf + resultlen,
    909  1.1  christos 			     &outbuf,
    910  1.1  christos 			     outbuf + freemem_size - sizeof (size_t),
    911  1.1  christos 			     &non_reversible);
    912  1.1  christos 
    913  1.1  christos 	      if (res == __GCONV_OK || res == __GCONV_EMPTY_INPUT)
    914  1.1  christos 		break;
    915  1.1  christos 
    916  1.1  christos 	      if (res != __GCONV_FULL_OUTPUT)
    917  1.1  christos 		{
    918  1.1  christos 		  __libc_lock_unlock (lock);
    919  1.1  christos 		  goto converted;
    920  1.1  christos 		}
    921  1.1  christos 
    922  1.1  christos 	      inbuf = result;
    923  1.1  christos # else
    924  1.1  christos #  if HAVE_ICONV
    925  1.1  christos 	      const char *inptr = (const char *) inbuf;
    926  1.1  christos 	      size_t inleft = resultlen;
    927  1.1  christos 	      char *outptr = (char *) outbuf;
    928  1.1  christos 	      size_t outleft;
    929  1.1  christos 
    930  1.1  christos 	      if (freemem_size < sizeof (size_t))
    931  1.1  christos 		goto resize_freemem;
    932  1.1  christos 
    933  1.1  christos 	      outleft = freemem_size - sizeof (size_t);
    934  1.1  christos 	      if (iconv (domain->conv,
    935  1.1  christos 			 (ICONV_CONST char **) &inptr, &inleft,
    936  1.1  christos 			 &outptr, &outleft)
    937  1.1  christos 		  != (size_t) (-1))
    938  1.1  christos 		{
    939  1.1  christos 		  outbuf = (unsigned char *) outptr;
    940  1.1  christos 		  break;
    941  1.1  christos 		}
    942  1.1  christos 	      if (errno != E2BIG)
    943  1.1  christos 		{
    944  1.1  christos 		  __libc_lock_unlock (lock);
    945  1.1  christos 		  goto converted;
    946  1.1  christos 		}
    947  1.1  christos #  endif
    948  1.1  christos # endif
    949  1.1  christos 
    950  1.1  christos 	    resize_freemem:
    951  1.1  christos 	      /* We must allocate a new buffer or resize the old one.  */
    952  1.1  christos 	      if (malloc_count > 0)
    953  1.1  christos 		{
    954  1.1  christos 		  ++malloc_count;
    955  1.1  christos 		  freemem_size = malloc_count * INITIAL_BLOCK_SIZE;
    956  1.1  christos 		  newmem = (transmem_block_t *) realloc (transmem_list,
    957  1.1  christos 							 freemem_size);
    958  1.1  christos # ifdef _LIBC
    959  1.1  christos 		  if (newmem != NULL)
    960  1.1  christos 		    transmem_list = transmem_list->next;
    961  1.1  christos 		  else
    962  1.1  christos 		    {
    963  1.1  christos 		      struct transmem_list *old = transmem_list;
    964  1.1  christos 
    965  1.1  christos 		      transmem_list = transmem_list->next;
    966  1.1  christos 		      free (old);
    967  1.1  christos 		    }
    968  1.1  christos # endif
    969  1.1  christos 		}
    970  1.1  christos 	      else
    971  1.1  christos 		{
    972  1.1  christos 		  malloc_count = 1;
    973  1.1  christos 		  freemem_size = INITIAL_BLOCK_SIZE;
    974  1.1  christos 		  newmem = (transmem_block_t *) malloc (freemem_size);
    975  1.1  christos 		}
    976  1.1  christos 	      if (__builtin_expect (newmem == NULL, 0))
    977  1.1  christos 		{
    978  1.1  christos 		  freemem = NULL;
    979  1.1  christos 		  freemem_size = 0;
    980  1.1  christos 		  __libc_lock_unlock (lock);
    981  1.1  christos 		  goto converted;
    982  1.1  christos 		}
    983  1.1  christos 
    984  1.1  christos # ifdef _LIBC
    985  1.1  christos 	      /* Add the block to the list of blocks we have to free
    986  1.1  christos                  at some point.  */
    987  1.1  christos 	      newmem->next = transmem_list;
    988  1.1  christos 	      transmem_list = newmem;
    989  1.1  christos 
    990  1.1  christos 	      freemem = newmem->data;
    991  1.1  christos 	      freemem_size -= offsetof (struct transmem_list, data);
    992  1.1  christos # else
    993  1.1  christos 	      transmem_list = newmem;
    994  1.1  christos 	      freemem = newmem;
    995  1.1  christos # endif
    996  1.1  christos 
    997  1.1  christos 	      outbuf = freemem + sizeof (size_t);
    998  1.1  christos 	    }
    999  1.1  christos 
   1000  1.1  christos 	  /* We have now in our buffer a converted string.  Put this
   1001  1.1  christos 	     into the table of conversions.  */
   1002  1.1  christos 	  *(size_t *) freemem = outbuf - freemem - sizeof (size_t);
   1003  1.1  christos 	  domain->conv_tab[act] = (char *) freemem;
   1004  1.1  christos 	  /* Shrink freemem, but keep it aligned.  */
   1005  1.1  christos 	  freemem_size -= outbuf - freemem;
   1006  1.1  christos 	  freemem = outbuf;
   1007  1.1  christos 	  freemem += freemem_size & (alignof (size_t) - 1);
   1008  1.1  christos 	  freemem_size = freemem_size & ~ (alignof (size_t) - 1);
   1009  1.1  christos 
   1010  1.1  christos 	  __libc_lock_unlock (lock);
   1011  1.1  christos 	}
   1012  1.1  christos 
   1013  1.1  christos       /* Now domain->conv_tab[act] contains the translation of all
   1014  1.1  christos 	 the plural variants.  */
   1015  1.1  christos       result = domain->conv_tab[act] + sizeof (size_t);
   1016  1.1  christos       resultlen = *(size_t *) domain->conv_tab[act];
   1017  1.1  christos     }
   1018  1.1  christos 
   1019  1.1  christos  converted:
   1020  1.1  christos   /* The result string is converted.  */
   1021  1.1  christos 
   1022  1.1  christos #endif /* _LIBC || HAVE_ICONV */
   1023  1.1  christos 
   1024  1.1  christos   *lengthp = resultlen;
   1025  1.1  christos   return result;
   1026  1.1  christos }
   1027  1.1  christos 
   1028  1.1  christos 
   1029  1.1  christos /* Look up a plural variant.  */
   1030  1.1  christos static char *
   1031  1.1  christos internal_function
   1032  1.1  christos plural_lookup (struct loaded_l10nfile *domain, unsigned long int n,
   1033  1.1  christos 	       const char *translation, size_t translation_len)
   1034  1.1  christos {
   1035  1.1  christos   struct loaded_domain *domaindata = (struct loaded_domain *) domain->data;
   1036  1.1  christos   unsigned long int index;
   1037  1.1  christos   const char *p;
   1038  1.1  christos 
   1039  1.1  christos   index = plural_eval (domaindata->plural, n);
   1040  1.1  christos   if (index >= domaindata->nplurals)
   1041  1.1  christos     /* This should never happen.  It means the plural expression and the
   1042  1.1  christos        given maximum value do not match.  */
   1043  1.1  christos     index = 0;
   1044  1.1  christos 
   1045  1.1  christos   /* Skip INDEX strings at TRANSLATION.  */
   1046  1.1  christos   p = translation;
   1047  1.1  christos   while (index-- > 0)
   1048  1.1  christos     {
   1049  1.1  christos #ifdef _LIBC
   1050  1.1  christos       p = __rawmemchr (p, '\0');
   1051  1.1  christos #else
   1052  1.1  christos       p = strchr (p, '\0');
   1053  1.1  christos #endif
   1054  1.1  christos       /* And skip over the NUL byte.  */
   1055  1.1  christos       p++;
   1056  1.1  christos 
   1057  1.1  christos       if (p >= translation + translation_len)
   1058  1.1  christos 	/* This should never happen.  It means the plural expression
   1059  1.1  christos 	   evaluated to a value larger than the number of variants
   1060  1.1  christos 	   available for MSGID1.  */
   1061  1.1  christos 	return (char *) translation;
   1062  1.1  christos     }
   1063  1.1  christos   return (char *) p;
   1064  1.1  christos }
   1065  1.1  christos 
   1066  1.1  christos #ifndef _LIBC
   1067  1.1  christos /* Return string representation of locale CATEGORY.  */
   1068  1.1  christos static const char *
   1069  1.1  christos internal_function
   1070  1.1  christos category_to_name (int category)
   1071  1.1  christos {
   1072  1.1  christos   const char *retval;
   1073  1.1  christos 
   1074  1.1  christos   switch (category)
   1075  1.1  christos   {
   1076  1.1  christos #ifdef LC_COLLATE
   1077  1.1  christos   case LC_COLLATE:
   1078  1.1  christos     retval = "LC_COLLATE";
   1079  1.1  christos     break;
   1080  1.1  christos #endif
   1081  1.1  christos #ifdef LC_CTYPE
   1082  1.1  christos   case LC_CTYPE:
   1083  1.1  christos     retval = "LC_CTYPE";
   1084  1.1  christos     break;
   1085  1.1  christos #endif
   1086  1.1  christos #ifdef LC_MONETARY
   1087  1.1  christos   case LC_MONETARY:
   1088  1.1  christos     retval = "LC_MONETARY";
   1089  1.1  christos     break;
   1090  1.1  christos #endif
   1091  1.1  christos #ifdef LC_NUMERIC
   1092  1.1  christos   case LC_NUMERIC:
   1093  1.1  christos     retval = "LC_NUMERIC";
   1094  1.1  christos     break;
   1095  1.1  christos #endif
   1096  1.1  christos #ifdef LC_TIME
   1097  1.1  christos   case LC_TIME:
   1098  1.1  christos     retval = "LC_TIME";
   1099  1.1  christos     break;
   1100  1.1  christos #endif
   1101  1.1  christos #ifdef LC_MESSAGES
   1102  1.1  christos   case LC_MESSAGES:
   1103  1.1  christos     retval = "LC_MESSAGES";
   1104  1.1  christos     break;
   1105  1.1  christos #endif
   1106  1.1  christos #ifdef LC_RESPONSE
   1107  1.1  christos   case LC_RESPONSE:
   1108  1.1  christos     retval = "LC_RESPONSE";
   1109  1.1  christos     break;
   1110  1.1  christos #endif
   1111  1.1  christos #ifdef LC_ALL
   1112  1.1  christos   case LC_ALL:
   1113  1.1  christos     /* This might not make sense but is perhaps better than any other
   1114  1.1  christos        value.  */
   1115  1.1  christos     retval = "LC_ALL";
   1116  1.1  christos     break;
   1117  1.1  christos #endif
   1118  1.1  christos   default:
   1119  1.1  christos     /* If you have a better idea for a default value let me know.  */
   1120  1.1  christos     retval = "LC_XXX";
   1121  1.1  christos   }
   1122  1.1  christos 
   1123  1.1  christos   return retval;
   1124  1.1  christos }
   1125  1.1  christos #endif
   1126  1.1  christos 
   1127  1.1  christos /* Guess value of current locale from value of the environment variables.  */
   1128  1.1  christos static const char *
   1129  1.1  christos internal_function
   1130  1.1  christos guess_category_value (int category, const char *categoryname)
   1131  1.1  christos {
   1132  1.1  christos   const char *language;
   1133  1.1  christos   const char *retval;
   1134  1.1  christos 
   1135  1.1  christos   /* The highest priority value is the `LANGUAGE' environment
   1136  1.1  christos      variable.  But we don't use the value if the currently selected
   1137  1.1  christos      locale is the C locale.  This is a GNU extension.  */
   1138  1.1  christos   language = getenv ("LANGUAGE");
   1139  1.1  christos   if (language != NULL && language[0] == '\0')
   1140  1.1  christos     language = NULL;
   1141  1.1  christos 
   1142  1.1  christos   /* We have to proceed with the POSIX methods of looking to `LC_ALL',
   1143  1.1  christos      `LC_xxx', and `LANG'.  On some systems this can be done by the
   1144  1.1  christos      `setlocale' function itself.  */
   1145  1.1  christos #ifdef _LIBC
   1146  1.1  christos   retval = __current_locale_name (category);
   1147  1.1  christos #else
   1148  1.1  christos   retval = _nl_locale_name (category, categoryname);
   1149  1.1  christos #endif
   1150  1.1  christos 
   1151  1.1  christos   /* Ignore LANGUAGE if the locale is set to "C" because
   1152  1.1  christos      1. "C" locale usually uses the ASCII encoding, and most international
   1153  1.1  christos 	messages use non-ASCII characters. These characters get displayed
   1154  1.1  christos 	as question marks (if using glibc's iconv()) or as invalid 8-bit
   1155  1.1  christos 	characters (because other iconv()s refuse to convert most non-ASCII
   1156  1.1  christos 	characters to ASCII). In any case, the output is ugly.
   1157  1.1  christos      2. The precise output of some programs in the "C" locale is specified
   1158  1.1  christos 	by POSIX and should not depend on environment variables like
   1159  1.1  christos 	"LANGUAGE".  We allow such programs to use gettext().  */
   1160  1.1  christos   return language != NULL && strcmp (retval, "C") != 0 ? language : retval;
   1161  1.1  christos }
   1162  1.1  christos 
   1163  1.1  christos /* @@ begin of epilog @@ */
   1164  1.1  christos 
   1165  1.1  christos /* We don't want libintl.a to depend on any other library.  So we
   1166  1.1  christos    avoid the non-standard function stpcpy.  In GNU C Library this
   1167  1.1  christos    function is available, though.  Also allow the symbol HAVE_STPCPY
   1168  1.1  christos    to be defined.  */
   1169  1.1  christos #if !_LIBC && !HAVE_STPCPY
   1170  1.1  christos static char *
   1171  1.1  christos stpcpy (char *dest, const char *src)
   1172  1.1  christos {
   1173  1.1  christos   while ((*dest++ = *src++) != '\0')
   1174  1.1  christos     /* Do nothing. */ ;
   1175  1.1  christos   return dest - 1;
   1176  1.1  christos }
   1177  1.1  christos #endif
   1178  1.1  christos 
   1179  1.1  christos #if !_LIBC && !HAVE_MEMPCPY
   1180  1.1  christos static void *
   1181  1.1  christos mempcpy (void *dest, const void *src, size_t n)
   1182  1.1  christos {
   1183  1.1  christos   return (void *) ((char *) memcpy (dest, src, n) + n);
   1184  1.1  christos }
   1185  1.1  christos #endif
   1186  1.1  christos 
   1187  1.1  christos 
   1188  1.1  christos #ifdef _LIBC
   1189  1.1  christos /* If we want to free all resources we have to do some work at
   1190  1.1  christos    program's end.  */
   1191  1.1  christos libc_freeres_fn (free_mem)
   1192  1.1  christos {
   1193  1.1  christos   void *old;
   1194  1.1  christos 
   1195  1.1  christos   while (_nl_domain_bindings != NULL)
   1196  1.1  christos     {
   1197  1.1  christos       struct binding *oldp = _nl_domain_bindings;
   1198  1.1  christos       _nl_domain_bindings = _nl_domain_bindings->next;
   1199  1.1  christos       if (oldp->dirname != INTUSE(_nl_default_dirname))
   1200  1.1  christos 	/* Yes, this is a pointer comparison.  */
   1201  1.1  christos 	free (oldp->dirname);
   1202  1.1  christos       free (oldp->codeset);
   1203  1.1  christos       free (oldp);
   1204  1.1  christos     }
   1205  1.1  christos 
   1206  1.1  christos   if (_nl_current_default_domain != _nl_default_default_domain)
   1207  1.1  christos     /* Yes, again a pointer comparison.  */
   1208  1.1  christos     free ((char *) _nl_current_default_domain);
   1209  1.1  christos 
   1210  1.1  christos   /* Remove the search tree with the known translations.  */
   1211  1.1  christos   __tdestroy (root, free);
   1212  1.1  christos   root = NULL;
   1213  1.1  christos 
   1214  1.1  christos   while (transmem_list != NULL)
   1215  1.1  christos     {
   1216  1.1  christos       old = transmem_list;
   1217  1.1  christos       transmem_list = transmem_list->next;
   1218  1.1  christos       free (old);
   1219  1.1  christos     }
   1220  1.1  christos }
   1221  1.1  christos #endif
   1222