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