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