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