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