source: trunk/third/gaim-encryption/intl/dcigettext.c @ 22512

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