source: branches/libc-0.6/src/binutils/intl/dcgettext.c

Last change on this file was 610, checked in by bird, 22 years ago

This commit was generated by cvs2svn to compensate for changes in r609,
which included commits to RCS files with non-trunk default branches.

  • Property cvs2svn:cvs-rev set to 1.1.1.2
  • Property svn:eol-style set to native
  • Property svn:executable set to *
File size: 16.1 KB
Line 
1/* Implementation of the dcgettext(3) function.
2 Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software Foundation,
16 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
17
18#ifdef HAVE_CONFIG_H
19# include <config.h>
20#endif
21
22#include <sys/types.h>
23
24#ifdef __GNUC__
25# define alloca __builtin_alloca
26# define HAVE_ALLOCA 1
27#else
28# if defined HAVE_ALLOCA_H || defined _LIBC
29# include <alloca.h>
30# else
31# ifdef _AIX
32 #pragma alloca
33# else
34# ifndef alloca
35char *alloca ();
36# endif
37# endif
38# endif
39#endif
40
41#include <errno.h>
42#ifndef errno
43extern int errno;
44#endif
45#ifndef __set_errno
46# define __set_errno(val) errno = (val)
47#endif
48
49#if defined STDC_HEADERS || defined _LIBC
50# include <stdlib.h>
51#else
52char *getenv ();
53# ifdef HAVE_MALLOC_H
54# include <malloc.h>
55# else
56void free ();
57# endif
58#endif
59
60#if defined HAVE_STRING_H || defined _LIBC
61# ifndef _GNU_SOURCE
62# define _GNU_SOURCE 1
63# endif
64# include <string.h>
65#else
66# include <strings.h>
67#endif
68#if !HAVE_STRCHR && !defined _LIBC
69# ifndef strchr
70# define strchr index
71# endif
72#endif
73
74#if defined HAVE_UNISTD_H || defined _LIBC
75# include <unistd.h>
76#endif
77
78#include "gettext.h"
79#include "gettextP.h"
80#ifdef _LIBC
81# include <libintl.h>
82#else
83# include "libgettext.h"
84#endif
85#include "hash-string.h"
86
87/* @@ end of prolog @@ */
88
89#ifdef _LIBC
90/* Rename the non ANSI C functions. This is required by the standard
91 because some ANSI C functions will require linking with this object
92 file and the name space must not be polluted. */
93# define getcwd __getcwd
94# ifndef stpcpy
95# define stpcpy __stpcpy
96# endif
97#else
98# if !defined HAVE_GETCWD
99char *getwd ();
100# define getcwd(buf, max) getwd (buf)
101# else
102char *getcwd ();
103# endif
104# ifndef HAVE_STPCPY
105static char *stpcpy PARAMS ((char *dest, const char *src));
106# endif
107#endif
108
109/* Amount to increase buffer size by in each try. */
110#define PATH_INCR 32
111
112/* The following is from pathmax.h. */
113/* Non-POSIX BSD systems might have gcc's limits.h, which doesn't define
114 PATH_MAX but might cause redefinition warnings when sys/param.h is
115 later included (as on MORE/BSD 4.3). */
116#if defined(_POSIX_VERSION) || (defined(HAVE_LIMITS_H) && !defined(__GNUC__))
117# include <limits.h>
118#endif
119
120#ifndef _POSIX_PATH_MAX
121# define _POSIX_PATH_MAX 255
122#endif
123
124#if !defined(PATH_MAX) && defined(_PC_PATH_MAX)
125# define PATH_MAX (pathconf ("/", _PC_PATH_MAX) < 1 ? 1024 : pathconf ("/", _PC_PATH_MAX))
126#endif
127
128/* Don't include sys/param.h if it already has been. */
129#if defined(HAVE_SYS_PARAM_H) && !defined(PATH_MAX) && !defined(MAXPATHLEN)
130# include <sys/param.h>
131#endif
132
133#if !defined(PATH_MAX) && defined(MAXPATHLEN)
134# define PATH_MAX MAXPATHLEN
135#endif
136
137#ifndef PATH_MAX
138# define PATH_MAX _POSIX_PATH_MAX
139#endif
140
141/* XPG3 defines the result of `setlocale (category, NULL)' as:
142 ``Directs `setlocale()' to query `category' and return the current
143 setting of `local'.''
144 However it does not specify the exact format. And even worse: POSIX
145 defines this not at all. So we can use this feature only on selected
146 system (e.g. those using GNU C Library). */
147#ifdef _LIBC
148# define HAVE_LOCALE_NULL
149#endif
150
151/* Name of the default domain used for gettext(3) prior any call to
152 textdomain(3). The default value for this is "messages". */
153const char _nl_default_default_domain[] = "messages";
154
155/* Value used as the default domain for gettext(3). */
156const char *_nl_current_default_domain = _nl_default_default_domain;
157
158/* Contains the default location of the message catalogs. */
159const char _nl_default_dirname[] = GNULOCALEDIR;
160
161/* List with bindings of specific domains created by bindtextdomain()
162 calls. */
163struct binding *_nl_domain_bindings;
164
165/* Prototypes for local functions. */
166static char *find_msg PARAMS ((struct loaded_l10nfile *domain_file,
167 const char *msgid)) internal_function;
168static const char *category_to_name PARAMS ((int category)) internal_function;
169static const char *guess_category_value PARAMS ((int category,
170 const char *categoryname))
171 internal_function;
172
173
174/* For those loosing systems which don't have `alloca' we have to add
175 some additional code emulating it. */
176#ifdef HAVE_ALLOCA
177/* Nothing has to be done. */
178# define ADD_BLOCK(list, address) /* nothing */
179# define FREE_BLOCKS(list) /* nothing */
180#else
181struct block_list
182{
183 void *address;
184 struct block_list *next;
185};
186# define ADD_BLOCK(list, addr) \
187 do { \
188 struct block_list *newp = (struct block_list *) malloc (sizeof (*newp)); \
189 /* If we cannot get a free block we cannot add the new element to \
190 the list. */ \
191 if (newp != NULL) { \
192 newp->address = (addr); \
193 newp->next = (list); \
194 (list) = newp; \
195 } \
196 } while (0)
197# define FREE_BLOCKS(list) \
198 do { \
199 while (list != NULL) { \
200 struct block_list *old = list; \
201 list = list->next; \
202 free (old); \
203 } \
204 } while (0)
205# undef alloca
206# define alloca(size) (malloc (size))
207#endif /* have alloca */
208
209
210/* Names for the libintl functions are a problem. They must not clash
211 with existing names and they should follow ANSI C. But this source
212 code is also used in GNU C Library where the names have a __
213 prefix. So we have to make a difference here. */
214#ifdef _LIBC
215# define DCGETTEXT __dcgettext
216#else
217# define DCGETTEXT dcgettext__
218#endif
219
220/* Look up MSGID in the DOMAINNAME message catalog for the current CATEGORY
221 locale. */
222char *
223DCGETTEXT (domainname, msgid, category)
224 const char *domainname;
225 const char *msgid;
226 int category;
227{
228#ifndef HAVE_ALLOCA
229 struct block_list *block_list = NULL;
230#endif
231 struct loaded_l10nfile *domain;
232 struct binding *binding;
233 const char *categoryname;
234 const char *categoryvalue;
235 char *dirname, *xdomainname;
236 char *single_locale;
237 char *retval;
238 int saved_errno = errno;
239
240 /* If no real MSGID is given return NULL. */
241 if (msgid == NULL)
242 return NULL;
243
244 /* If DOMAINNAME is NULL, we are interested in the default domain. If
245 CATEGORY is not LC_MESSAGES this might not make much sense but the
246 defintion left this undefined. */
247 if (domainname == NULL)
248 domainname = _nl_current_default_domain;
249
250 /* First find matching binding. */
251 for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next)
252 {
253 int compare = strcmp (domainname, binding->domainname);
254 if (compare == 0)
255 /* We found it! */
256 break;
257 if (compare < 0)
258 {
259 /* It is not in the list. */
260 binding = NULL;
261 break;
262 }
263 }
264
265 if (binding == NULL)
266 dirname = (char *) _nl_default_dirname;
267 else if (binding->dirname[0] == '/')
268 dirname = binding->dirname;
269 else
270 {
271 /* We have a relative path. Make it absolute now. */
272 size_t dirname_len = strlen (binding->dirname) + 1;
273 size_t path_max;
274 char *ret;
275
276 path_max = (unsigned) PATH_MAX;
277 path_max += 2; /* The getcwd docs say to do this. */
278
279 dirname = (char *) alloca (path_max + dirname_len);
280 ADD_BLOCK (block_list, dirname);
281
282 __set_errno (0);
283 while ((ret = getcwd (dirname, path_max)) == NULL && errno == ERANGE)
284 {
285 path_max += PATH_INCR;
286 dirname = (char *) alloca (path_max + dirname_len);
287 ADD_BLOCK (block_list, dirname);
288 __set_errno (0);
289 }
290
291 if (ret == NULL)
292 {
293 /* We cannot get the current working directory. Don't signal an
294 error but simply return the default string. */
295 FREE_BLOCKS (block_list);
296 __set_errno (saved_errno);
297 return (char *) msgid;
298 }
299
300 stpcpy (stpcpy (strchr (dirname, '\0'), "/"), binding->dirname);
301 }
302
303 /* Now determine the symbolic name of CATEGORY and its value. */
304 categoryname = category_to_name (category);
305 categoryvalue = guess_category_value (category, categoryname);
306
307 xdomainname = (char *) alloca (strlen (categoryname)
308 + strlen (domainname) + 5);
309 ADD_BLOCK (block_list, xdomainname);
310
311 stpcpy (stpcpy (stpcpy (stpcpy (xdomainname, categoryname), "/"),
312 domainname),
313 ".mo");
314
315 /* Creating working area. */
316 single_locale = (char *) alloca (strlen (categoryvalue) + 1);
317 ADD_BLOCK (block_list, single_locale);
318
319
320 /* Search for the given string. This is a loop because we perhaps
321 got an ordered list of languages to consider for th translation. */
322 while (1)
323 {
324 /* Make CATEGORYVALUE point to the next element of the list. */
325 while (categoryvalue[0] != '\0' && categoryvalue[0] == ':')
326 ++categoryvalue;
327 if (categoryvalue[0] == '\0')
328 {
329 /* The whole contents of CATEGORYVALUE has been searched but
330 no valid entry has been found. We solve this situation
331 by implicitly appending a "C" entry, i.e. no translation
332 will take place. */
333 single_locale[0] = 'C';
334 single_locale[1] = '\0';
335 }
336 else
337 {
338 char *cp = single_locale;
339 while (categoryvalue[0] != '\0' && categoryvalue[0] != ':')
340 *cp++ = *categoryvalue++;
341 *cp = '\0';
342 }
343
344 /* If the current locale value is C (or POSIX) we don't load a
345 domain. Return the MSGID. */
346 if (strcmp (single_locale, "C") == 0
347 || strcmp (single_locale, "POSIX") == 0)
348 {
349 FREE_BLOCKS (block_list);
350 __set_errno (saved_errno);
351 return (char *) msgid;
352 }
353
354
355 /* Find structure describing the message catalog matching the
356 DOMAINNAME and CATEGORY. */
357 domain = _nl_find_domain (dirname, single_locale, xdomainname);
358
359 if (domain != NULL)
360 {
361 retval = find_msg (domain, msgid);
362
363 if (retval == NULL)
364 {
365 int cnt;
366
367 for (cnt = 0; domain->successor[cnt] != NULL; ++cnt)
368 {
369 retval = find_msg (domain->successor[cnt], msgid);
370
371 if (retval != NULL)
372 break;
373 }
374 }
375
376 if (retval != NULL)
377 {
378 FREE_BLOCKS (block_list);
379 __set_errno (saved_errno);
380 return retval;
381 }
382 }
383 }
384 /* NOTREACHED */
385}
386
387#ifdef _LIBC
388/* Alias for function name in GNU C Library. */
389weak_alias (__dcgettext, dcgettext);
390#endif
391
392
393static char *
394internal_function
395find_msg (domain_file, msgid)
396 struct loaded_l10nfile *domain_file;
397 const char *msgid;
398{
399 size_t top, act, bottom;
400 struct loaded_domain *domain;
401
402 if (domain_file->decided == 0)
403 _nl_load_domain (domain_file);
404
405 if (domain_file->data == NULL)
406 return NULL;
407
408 domain = (struct loaded_domain *) domain_file->data;
409
410 /* Locate the MSGID and its translation. */
411 if (domain->hash_size > 2 && domain->hash_tab != NULL)
412 {
413 /* Use the hashing table. */
414 nls_uint32 len = strlen (msgid);
415 nls_uint32 hash_val = hash_string (msgid);
416 nls_uint32 idx = hash_val % domain->hash_size;
417 nls_uint32 incr = 1 + (hash_val % (domain->hash_size - 2));
418 nls_uint32 nstr = W (domain->must_swap, domain->hash_tab[idx]);
419
420 if (nstr == 0)
421 /* Hash table entry is empty. */
422 return NULL;
423
424 if (W (domain->must_swap, domain->orig_tab[nstr - 1].length) == len
425 && strcmp (msgid,
426 domain->data + W (domain->must_swap,
427 domain->orig_tab[nstr - 1].offset)) == 0)
428 return (char *) domain->data + W (domain->must_swap,
429 domain->trans_tab[nstr - 1].offset);
430
431 while (1)
432 {
433 if (idx >= domain->hash_size - incr)
434 idx -= domain->hash_size - incr;
435 else
436 idx += incr;
437
438 nstr = W (domain->must_swap, domain->hash_tab[idx]);
439 if (nstr == 0)
440 /* Hash table entry is empty. */
441 return NULL;
442
443 if (W (domain->must_swap, domain->orig_tab[nstr - 1].length) == len
444 && strcmp (msgid,
445 domain->data + W (domain->must_swap,
446 domain->orig_tab[nstr - 1].offset))
447 == 0)
448 return (char *) domain->data
449 + W (domain->must_swap, domain->trans_tab[nstr - 1].offset);
450 }
451 /* NOTREACHED */
452 }
453
454 /* Now we try the default method: binary search in the sorted
455 array of messages. */
456 bottom = 0;
457 top = domain->nstrings;
458 while (bottom < top)
459 {
460 int cmp_val;
461
462 act = (bottom + top) / 2;
463 cmp_val = strcmp (msgid, domain->data
464 + W (domain->must_swap,
465 domain->orig_tab[act].offset));
466 if (cmp_val < 0)
467 top = act;
468 else if (cmp_val > 0)
469 bottom = act + 1;
470 else
471 break;
472 }
473
474 /* If an translation is found return this. */
475 return bottom >= top ? NULL : (char *) domain->data
476 + W (domain->must_swap,
477 domain->trans_tab[act].offset);
478}
479
480
481/* Return string representation of locale CATEGORY. */
482static const char *
483internal_function
484category_to_name (category)
485 int category;
486{
487 const char *retval;
488
489 switch (category)
490 {
491#ifdef LC_COLLATE
492 case LC_COLLATE:
493 retval = "LC_COLLATE";
494 break;
495#endif
496#ifdef LC_CTYPE
497 case LC_CTYPE:
498 retval = "LC_CTYPE";
499 break;
500#endif
501#ifdef LC_MONETARY
502 case LC_MONETARY:
503 retval = "LC_MONETARY";
504 break;
505#endif
506#ifdef LC_NUMERIC
507 case LC_NUMERIC:
508 retval = "LC_NUMERIC";
509 break;
510#endif
511#ifdef LC_TIME
512 case LC_TIME:
513 retval = "LC_TIME";
514 break;
515#endif
516#ifdef LC_MESSAGES
517 case LC_MESSAGES:
518 retval = "LC_MESSAGES";
519 break;
520#endif
521#ifdef LC_RESPONSE
522 case LC_RESPONSE:
523 retval = "LC_RESPONSE";
524 break;
525#endif
526#ifdef LC_ALL
527 case LC_ALL:
528 /* This might not make sense but is perhaps better than any other
529 value. */
530 retval = "LC_ALL";
531 break;
532#endif
533 default:
534 /* If you have a better idea for a default value let me know. */
535 retval = "LC_XXX";
536 }
537
538 return retval;
539}
540
541/* Guess value of current locale from value of the environment variables. */
542static const char *
543internal_function
544guess_category_value (category, categoryname)
545 int category;
546 const char *categoryname;
547{
548 const char *retval;
549
550 /* The highest priority value is the `LANGUAGE' environment
551 variable. This is a GNU extension. */
552 retval = getenv ("LANGUAGE");
553 if (retval != NULL && retval[0] != '\0')
554 return retval;
555
556 /* `LANGUAGE' is not set. So we have to proceed with the POSIX
557 methods of looking to `LC_ALL', `LC_xxx', and `LANG'. On some
558 systems this can be done by the `setlocale' function itself. */
559#if defined HAVE_SETLOCALE && defined HAVE_LC_MESSAGES && defined HAVE_LOCALE_NULL
560 return setlocale (category, NULL);
561#else
562 /* Setting of LC_ALL overwrites all other. */
563 retval = getenv ("LC_ALL");
564 if (retval != NULL && retval[0] != '\0')
565 return retval;
566
567 /* Next comes the name of the desired category. */
568 retval = getenv (categoryname);
569 if (retval != NULL && retval[0] != '\0')
570 return retval;
571
572 /* Last possibility is the LANG environment variable. */
573 retval = getenv ("LANG");
574 if (retval != NULL && retval[0] != '\0')
575 return retval;
576
577 /* We use C as the default domain. POSIX says this is implementation
578 defined. */
579 return "C";
580#endif
581}
582
583/* @@ begin of epilog @@ */
584
585/* We don't want libintl.a to depend on any other library. So we
586 avoid the non-standard function stpcpy. In GNU C Library this
587 function is available, though. Also allow the symbol HAVE_STPCPY
588 to be defined. */
589#if !_LIBC && !HAVE_STPCPY
590static char *
591stpcpy (dest, src)
592 char *dest;
593 const char *src;
594{
595 while ((*dest++ = *src++) != '\0')
596 /* Do nothing. */ ;
597 return dest - 1;
598}
599#endif
600
601
602#ifdef _LIBC
603/* If we want to free all resources we have to do some work at
604 program's end. */
605static void __attribute__ ((unused))
606free_mem (void)
607{
608 struct binding *runp;
609
610 for (runp = _nl_domain_bindings; runp != NULL; runp = runp->next)
611 {
612 free (runp->domainname);
613 if (runp->dirname != _nl_default_dirname)
614 /* Yes, this is a pointer comparison. */
615 free (runp->dirname);
616 }
617
618 if (_nl_current_default_domain != _nl_default_default_domain)
619 /* Yes, again a pointer comparison. */
620 free ((char *) _nl_current_default_domain);
621}
622
623text_set_element (__libc_subfreeres, free_mem);
624#endif
Note: See TracBrowser for help on using the repository browser.