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