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