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