source: trunk/third/librep/intl/dcgettext.c @ 15283

Revision 15283, 16.1 KB checked in by ghudson, 24 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r15282, 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#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.