source: trunk/third/gnome-core/intl/dcgettext.c @ 15328

Revision 15328, 17.1 KB checked in by ghudson, 24 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r15327, which included commits to RCS files with non-trunk default branches.
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#if defined __GNUC__ && !defined C_ALLOCA
25# define alloca __builtin_alloca
26# define HAVE_ALLOCA 1
27#else
28# if (defined HAVE_ALLOCA_H || defined _LIBC) && !defined C_ALLOCA
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/* Checking whether the binaries runs SUID must be done and glibc provides
221   easier methods therefore we make a difference here.  */
222#ifdef _LIBC
223# define ENABLE_SECURE __libc_enable_secure
224# define DETERMINE_SECURE
225#else
226static int enable_secure;
227# define ENABLE_SECURE (enable_secure == 1)
228# define DETERMINE_SECURE \
229  if (enable_secure == 0)                                                     \
230    {                                                                         \
231      if (getuid () != geteuid () || getgid () != getegid ())                 \
232        enable_secure = 1;                                                    \
233      else                                                                    \
234        enable_secure = -1;                                                   \
235    }
236#endif
237
238/* Look up MSGID in the DOMAINNAME message catalog for the current CATEGORY
239   locale.  */
240char *
241DCGETTEXT (domainname, msgid, category)
242     const char *domainname;
243     const char *msgid;
244     int category;
245{
246#ifndef HAVE_ALLOCA
247  struct block_list *block_list = NULL;
248#endif
249  struct loaded_l10nfile *domain;
250  struct binding *binding;
251  const char *categoryname;
252  const char *categoryvalue;
253  char *dirname, *xdomainname;
254  char *single_locale;
255  char *retval;
256  int saved_errno = errno;
257
258  /* If no real MSGID is given return NULL.  */
259  if (msgid == NULL)
260    return NULL;
261
262  /* See whether this is a SUID binary or not.  */
263  DETERMINE_SECURE;
264
265  /* If DOMAINNAME is NULL, we are interested in the default domain.  If
266     CATEGORY is not LC_MESSAGES this might not make much sense but the
267     definition left this undefined.  */
268  if (domainname == NULL)
269    domainname = _nl_current_default_domain;
270
271  /* First find matching binding.  */
272  for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next)
273    {
274      int compare = strcmp (domainname, binding->domainname);
275      if (compare == 0)
276        /* We found it!  */
277        break;
278      if (compare < 0)
279        {
280          /* It is not in the list.  */
281          binding = NULL;
282          break;
283        }
284    }
285
286  if (binding == NULL)
287    dirname = (char *) _nl_default_dirname;
288  else if (binding->dirname[0] == '/')
289    dirname = binding->dirname;
290  else
291    {
292      /* We have a relative path.  Make it absolute now.  */
293      size_t dirname_len = strlen (binding->dirname) + 1;
294      size_t path_max;
295      char *ret;
296
297      path_max = (unsigned int) PATH_MAX;
298      path_max += 2;            /* The getcwd docs say to do this.  */
299
300      dirname = (char *) alloca (path_max + dirname_len);
301      ADD_BLOCK (block_list, dirname);
302
303      __set_errno (0);
304      while ((ret = getcwd (dirname, path_max)) == NULL && errno == ERANGE)
305        {
306          path_max += PATH_INCR;
307          dirname = (char *) alloca (path_max + dirname_len);
308          ADD_BLOCK (block_list, dirname);
309          __set_errno (0);
310        }
311
312      if (ret == NULL)
313        {
314          /* We cannot get the current working directory.  Don't signal an
315             error but simply return the default string.  */
316          FREE_BLOCKS (block_list);
317          __set_errno (saved_errno);
318          return (char *) msgid;
319        }
320
321      stpcpy (stpcpy (strchr (dirname, '\0'), "/"), binding->dirname);
322    }
323
324  /* Now determine the symbolic name of CATEGORY and its value.  */
325  categoryname = category_to_name (category);
326  categoryvalue = guess_category_value (category, categoryname);
327
328  xdomainname = (char *) alloca (strlen (categoryname)
329                                 + strlen (domainname) + 5);
330  ADD_BLOCK (block_list, xdomainname);
331
332  stpcpy (stpcpy (stpcpy (stpcpy (xdomainname, categoryname), "/"),
333                  domainname),
334          ".mo");
335
336  /* Creating working area.  */
337  single_locale = (char *) alloca (strlen (categoryvalue) + 1);
338  ADD_BLOCK (block_list, single_locale);
339
340
341  /* Search for the given string.  This is a loop because we perhaps
342     got an ordered list of languages to consider for the translation.  */
343  while (1)
344    {
345      /* Make CATEGORYVALUE point to the next element of the list.  */
346      while (categoryvalue[0] != '\0' && categoryvalue[0] == ':')
347        ++categoryvalue;
348      if (categoryvalue[0] == '\0')
349        {
350          /* The whole contents of CATEGORYVALUE has been searched but
351             no valid entry has been found.  We solve this situation
352             by implicitly appending a "C" entry, i.e. no translation
353             will take place.  */
354          single_locale[0] = 'C';
355          single_locale[1] = '\0';
356        }
357      else
358        {
359          char *cp = single_locale;
360          while (categoryvalue[0] != '\0' && categoryvalue[0] != ':')
361            *cp++ = *categoryvalue++;
362          *cp = '\0';
363
364          /* When this is a SUID binary we must not allow accessing files
365             outside the dedicated directories.  */
366          if (ENABLE_SECURE
367              && (memchr (single_locale, '/',
368                          _nl_find_language (single_locale) - single_locale)
369                  != NULL))
370            /* Ingore this entry.  */
371            continue;
372        }
373
374      /* If the current locale value is C (or POSIX) we don't load a
375         domain.  Return the MSGID.  */
376      if (strcmp (single_locale, "C") == 0
377          || strcmp (single_locale, "POSIX") == 0)
378        {
379          FREE_BLOCKS (block_list);
380          __set_errno (saved_errno);
381          return (char *) msgid;
382        }
383
384
385      /* Find structure describing the message catalog matching the
386         DOMAINNAME and CATEGORY.  */
387      domain = _nl_find_domain (dirname, single_locale, xdomainname);
388
389      if (domain != NULL)
390        {
391          retval = find_msg (domain, msgid);
392
393          if (retval == NULL)
394            {
395              int cnt;
396
397              for (cnt = 0; domain->successor[cnt] != NULL; ++cnt)
398                {
399                  retval = find_msg (domain->successor[cnt], msgid);
400
401                  if (retval != NULL)
402                    break;
403                }
404            }
405
406          if (retval != NULL)
407            {
408              FREE_BLOCKS (block_list);
409              __set_errno (saved_errno);
410              return retval;
411            }
412        }
413    }
414  /* NOTREACHED */
415}
416
417#ifdef _LIBC
418/* Alias for function name in GNU C Library.  */
419weak_alias (__dcgettext, dcgettext);
420#endif
421
422
423static char *
424internal_function
425find_msg (domain_file, msgid)
426     struct loaded_l10nfile *domain_file;
427     const char *msgid;
428{
429  size_t act = 0;
430  size_t top, bottom;
431  struct loaded_domain *domain;
432
433  if (domain_file->decided == 0)
434    _nl_load_domain (domain_file);
435
436  if (domain_file->data == NULL)
437    return NULL;
438
439  domain = (struct loaded_domain *) domain_file->data;
440
441  /* Locate the MSGID and its translation.  */
442  if (domain->hash_size > 2 && domain->hash_tab != NULL)
443    {
444      /* Use the hashing table.  */
445      nls_uint32 len = strlen (msgid);
446      nls_uint32 hash_val = hash_string (msgid);
447      nls_uint32 idx = hash_val % domain->hash_size;
448      nls_uint32 incr = 1 + (hash_val % (domain->hash_size - 2));
449      nls_uint32 nstr = W (domain->must_swap, domain->hash_tab[idx]);
450
451      if (nstr == 0)
452        /* Hash table entry is empty.  */
453        return NULL;
454
455      if (W (domain->must_swap, domain->orig_tab[nstr - 1].length) == len
456          && strcmp (msgid,
457                     domain->data + W (domain->must_swap,
458                                       domain->orig_tab[nstr - 1].offset)) == 0)
459        return (char *) domain->data + W (domain->must_swap,
460                                          domain->trans_tab[nstr - 1].offset);
461
462      while (1)
463        {
464          if (idx >= domain->hash_size - incr)
465            idx -= domain->hash_size - incr;
466          else
467            idx += incr;
468
469          nstr = W (domain->must_swap, domain->hash_tab[idx]);
470          if (nstr == 0)
471            /* Hash table entry is empty.  */
472            return NULL;
473
474          if (W (domain->must_swap, domain->orig_tab[nstr - 1].length) == len
475              && strcmp (msgid,
476                         domain->data + W (domain->must_swap,
477                                           domain->orig_tab[nstr - 1].offset))
478                 == 0)
479            return (char *) domain->data
480              + W (domain->must_swap, domain->trans_tab[nstr - 1].offset);
481        }
482      /* NOTREACHED */
483    }
484
485  /* Now we try the default method:  binary search in the sorted
486     array of messages.  */
487  bottom = 0;
488  top = domain->nstrings;
489  while (bottom < top)
490    {
491      int cmp_val;
492
493      act = (bottom + top) / 2;
494      cmp_val = strcmp (msgid, domain->data
495                               + W (domain->must_swap,
496                                    domain->orig_tab[act].offset));
497      if (cmp_val < 0)
498        top = act;
499      else if (cmp_val > 0)
500        bottom = act + 1;
501      else
502        break;
503    }
504
505  /* If an translation is found return this.  */
506  return bottom >= top ? NULL : (char *) domain->data
507                                + W (domain->must_swap,
508                                     domain->trans_tab[act].offset);
509}
510
511
512/* Return string representation of locale CATEGORY.  */
513static const char *
514internal_function
515category_to_name (category)
516     int category;
517{
518  const char *retval;
519
520  switch (category)
521  {
522#ifdef LC_COLLATE
523  case LC_COLLATE:
524    retval = "LC_COLLATE";
525    break;
526#endif
527#ifdef LC_CTYPE
528  case LC_CTYPE:
529    retval = "LC_CTYPE";
530    break;
531#endif
532#ifdef LC_MONETARY
533  case LC_MONETARY:
534    retval = "LC_MONETARY";
535    break;
536#endif
537#ifdef LC_NUMERIC
538  case LC_NUMERIC:
539    retval = "LC_NUMERIC";
540    break;
541#endif
542#ifdef LC_TIME
543  case LC_TIME:
544    retval = "LC_TIME";
545    break;
546#endif
547#ifdef LC_MESSAGES
548  case LC_MESSAGES:
549    retval = "LC_MESSAGES";
550    break;
551#endif
552#ifdef LC_RESPONSE
553  case LC_RESPONSE:
554    retval = "LC_RESPONSE";
555    break;
556#endif
557#ifdef LC_ALL
558  case LC_ALL:
559    /* This might not make sense but is perhaps better than any other
560       value.  */
561    retval = "LC_ALL";
562    break;
563#endif
564  default:
565    /* If you have a better idea for a default value let me know.  */
566    retval = "LC_XXX";
567  }
568
569  return retval;
570}
571
572/* Guess value of current locale from value of the environment variables.  */
573static const char *
574internal_function
575guess_category_value (category, categoryname)
576     int category;
577     const char *categoryname;
578{
579  const char *retval;
580
581  /* The highest priority value is the `LANGUAGE' environment
582     variable.  This is a GNU extension.  */
583  retval = getenv ("LANGUAGE");
584  if (retval != NULL && retval[0] != '\0')
585    return retval;
586
587  /* `LANGUAGE' is not set.  So we have to proceed with the POSIX
588     methods of looking to `LC_ALL', `LC_xxx', and `LANG'.  On some
589     systems this can be done by the `setlocale' function itself.  */
590#if defined HAVE_SETLOCALE && defined HAVE_LC_MESSAGES && defined HAVE_LOCALE_NULL
591  return setlocale (category, NULL);
592#else
593  /* Setting of LC_ALL overwrites all other.  */
594  retval = getenv ("LC_ALL");
595  if (retval != NULL && retval[0] != '\0')
596    return retval;
597
598  /* Next comes the name of the desired category.  */
599  retval = getenv (categoryname);
600  if (retval != NULL && retval[0] != '\0')
601    return retval;
602
603  /* Last possibility is the LANG environment variable.  */
604  retval = getenv ("LANG");
605  if (retval != NULL && retval[0] != '\0')
606    return retval;
607
608  /* We use C as the default domain.  POSIX says this is implementation
609     defined.  */
610  return "C";
611#endif
612}
613
614/* @@ begin of epilog @@ */
615
616/* We don't want libintl.a to depend on any other library.  So we
617   avoid the non-standard function stpcpy.  In GNU C Library this
618   function is available, though.  Also allow the symbol HAVE_STPCPY
619   to be defined.  */
620#if !_LIBC && !HAVE_STPCPY
621static char *
622stpcpy (dest, src)
623     char *dest;
624     const char *src;
625{
626  while ((*dest++ = *src++) != '\0')
627    /* Do nothing. */ ;
628  return dest - 1;
629}
630#endif
631
632
633#ifdef _LIBC
634/* If we want to free all resources we have to do some work at
635   program's end.  */
636static void __attribute__ ((unused))
637free_mem (void)
638{
639  struct binding *runp;
640
641  for (runp = _nl_domain_bindings; runp != NULL; runp = runp->next)
642    {
643      free (runp->domainname);
644      if (runp->dirname != _nl_default_dirname)
645        /* Yes, this is a pointer comparison.  */
646        free (runp->dirname);
647    }
648
649  if (_nl_current_default_domain != _nl_default_default_domain)
650    /* Yes, again a pointer comparison.  */
651    free ((char *) _nl_current_default_domain);
652}
653
654text_set_element (__libc_subfreeres, free_mem);
655#endif
Note: See TracBrowser for help on using the repository browser.