(distribute): Add plural-eval.c.
[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 _nl_default_default_domain__
107 # define _nl_current_default_domain _nl_current_default_domain__
108 # define _nl_default_dirname _nl_default_dirname__
109 # define _nl_domain_bindings _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 *category_to_name PARAMS ((int category)) internal_function;
269 static const char *guess_category_value PARAMS ((int category,
270                                                  const char *categoryname))
271      internal_function;
272
273
274 /* For those loosing systems which don't have `alloca' we have to add
275    some additional code emulating it.  */
276 #ifdef HAVE_ALLOCA
277 /* Nothing has to be done.  */
278 # define ADD_BLOCK(list, address) /* nothing */
279 # define FREE_BLOCKS(list) /* nothing */
280 #else
281 struct block_list
282 {
283   void *address;
284   struct block_list *next;
285 };
286 # define ADD_BLOCK(list, addr)                                                \
287   do {                                                                        \
288     struct block_list *newp = (struct block_list *) malloc (sizeof (*newp));  \
289     /* If we cannot get a free block we cannot add the new element to         \
290        the list.  */                                                          \
291     if (newp != NULL) {                                                       \
292       newp->address = (addr);                                                 \
293       newp->next = (list);                                                    \
294       (list) = newp;                                                          \
295     }                                                                         \
296   } while (0)
297 # define FREE_BLOCKS(list)                                                    \
298   do {                                                                        \
299     while (list != NULL) {                                                    \
300       struct block_list *old = list;                                          \
301       list = list->next;                                                      \
302       free (old);                                                             \
303     }                                                                         \
304   } while (0)
305 # undef alloca
306 # define alloca(size) (malloc (size))
307 #endif  /* have alloca */
308
309
310 #ifdef _LIBC
311 /* List of blocks allocated for translations.  */
312 typedef struct transmem_list
313 {
314   struct transmem_list *next;
315   char data[ZERO];
316 } transmem_block_t;
317 static struct transmem_list *transmem_list;
318 #else
319 typedef unsigned char transmem_block_t;
320 #endif
321
322
323 /* Names for the libintl functions are a problem.  They must not clash
324    with existing names and they should follow ANSI C.  But this source
325    code is also used in GNU C Library where the names have a __
326    prefix.  So we have to make a difference here.  */
327 #ifdef _LIBC
328 # define DCIGETTEXT __dcigettext
329 #else
330 # define DCIGETTEXT dcigettext__
331 #endif
332
333 /* Lock variable to protect the global data in the gettext implementation.  */
334 #ifdef _LIBC
335 __libc_rwlock_define_initialized (, _nl_state_lock attribute_hidden)
336 #endif
337
338 /* Checking whether the binaries runs SUID must be done and glibc provides
339    easier methods therefore we make a difference here.  */
340 #ifdef _LIBC
341 # define ENABLE_SECURE __libc_enable_secure
342 # define DETERMINE_SECURE
343 #else
344 static int enable_secure;
345 # define ENABLE_SECURE (enable_secure == 1)
346 # define DETERMINE_SECURE \
347   if (enable_secure == 0)                                                     \
348     {                                                                         \
349       if (getuid () != geteuid () || getgid () != getegid ())                 \
350         enable_secure = 1;                                                    \
351       else                                                                    \
352         enable_secure = -1;                                                   \
353     }
354 #endif
355
356 /* Get the function to evaluate the plural expression.  */
357 #include "plural-eval.c"
358
359 /* Look up MSGID in the DOMAINNAME message catalog for the current
360    CATEGORY locale and, if PLURAL is nonzero, search over string
361    depending on the plural form determined by N.  */
362 char *
363 DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category)
364      const char *domainname;
365      const char *msgid1;
366      const char *msgid2;
367      int plural;
368      unsigned long int n;
369      int category;
370 {
371 #ifndef HAVE_ALLOCA
372   struct block_list *block_list = NULL;
373 #endif
374   struct loaded_l10nfile *domain;
375   struct binding *binding;
376   const char *categoryname;
377   const char *categoryvalue;
378   char *dirname, *xdomainname;
379   char *single_locale;
380   char *retval;
381   size_t retlen;
382   int saved_errno;
383 #if defined HAVE_TSEARCH || defined _LIBC
384   struct known_translation_t *search;
385   struct known_translation_t **foundp = NULL;
386   size_t msgid_len;
387 #endif
388   size_t domainname_len;
389
390   /* If no real MSGID is given return NULL.  */
391   if (msgid1 == NULL)
392     return NULL;
393
394   __libc_rwlock_rdlock (_nl_state_lock);
395
396   /* If DOMAINNAME is NULL, we are interested in the default domain.  If
397      CATEGORY is not LC_MESSAGES this might not make much sense but the
398      definition left this undefined.  */
399   if (domainname == NULL)
400     domainname = _nl_current_default_domain;
401
402 #if defined HAVE_TSEARCH || defined _LIBC
403   msgid_len = strlen (msgid1) + 1;
404
405   /* Try to find the translation among those which we found at
406      some time.  */
407   search = (struct known_translation_t *)
408            alloca (offsetof (struct known_translation_t, msgid) + msgid_len);
409   memcpy (search->msgid, msgid1, msgid_len);
410   search->domainname = (char *) domainname;
411   search->category = category;
412
413   foundp = (struct known_translation_t **) tfind (search, &root, transcmp);
414   if (foundp != NULL && (*foundp)->counter == _nl_msg_cat_cntr)
415     {
416       /* Now deal with plural.  */
417       if (plural)
418         retval = plural_lookup ((*foundp)->domain, n, (*foundp)->translation,
419                                 (*foundp)->translation_length);
420       else
421         retval = (char *) (*foundp)->translation;
422
423       __libc_rwlock_unlock (_nl_state_lock);
424       return retval;
425     }
426 #endif
427
428   /* Preserve the `errno' value.  */
429   saved_errno = errno;
430
431   /* See whether this is a SUID binary or not.  */
432   DETERMINE_SECURE;
433
434   /* First find matching binding.  */
435   for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next)
436     {
437       int compare = strcmp (domainname, binding->domainname);
438       if (compare == 0)
439         /* We found it!  */
440         break;
441       if (compare < 0)
442         {
443           /* It is not in the list.  */
444           binding = NULL;
445           break;
446         }
447     }
448
449   if (binding == NULL)
450     dirname = (char *) INTUSE(_nl_default_dirname);
451   else if (binding->dirname[0] == '/')
452     dirname = binding->dirname;
453   else
454     {
455       /* We have a relative path.  Make it absolute now.  */
456       size_t dirname_len = strlen (binding->dirname) + 1;
457       size_t path_max;
458       char *ret;
459
460       path_max = (unsigned int) PATH_MAX;
461       path_max += 2;            /* The getcwd docs say to do this.  */
462
463       for (;;)
464         {
465           dirname = (char *) alloca (path_max + dirname_len);
466           ADD_BLOCK (block_list, dirname);
467
468           __set_errno (0);
469           ret = getcwd (dirname, path_max);
470           if (ret != NULL || errno != ERANGE)
471             break;
472
473           path_max += path_max / 2;
474           path_max += PATH_INCR;
475         }
476
477       if (ret == NULL)
478         {
479           /* We cannot get the current working directory.  Don't signal an
480              error but simply return the default string.  */
481           FREE_BLOCKS (block_list);
482           __libc_rwlock_unlock (_nl_state_lock);
483           __set_errno (saved_errno);
484           return (plural == 0
485                   ? (char *) msgid1
486                   /* Use the Germanic plural rule.  */
487                   : n == 1 ? (char *) msgid1 : (char *) msgid2);
488         }
489
490       stpcpy (stpcpy (strchr (dirname, '\0'), "/"), binding->dirname);
491     }
492
493   /* Now determine the symbolic name of CATEGORY and its value.  */
494   categoryname = category_to_name (category);
495   categoryvalue = guess_category_value (category, categoryname);
496
497   domainname_len = strlen (domainname);
498   xdomainname = (char *) alloca (strlen (categoryname)
499                                  + domainname_len + 5);
500   ADD_BLOCK (block_list, xdomainname);
501
502   stpcpy (mempcpy (stpcpy (stpcpy (xdomainname, categoryname), "/"),
503                   domainname, domainname_len),
504           ".mo");
505
506   /* Creating working area.  */
507   single_locale = (char *) alloca (strlen (categoryvalue) + 1);
508   ADD_BLOCK (block_list, single_locale);
509
510
511   /* Search for the given string.  This is a loop because we perhaps
512      got an ordered list of languages to consider for the translation.  */
513   while (1)
514     {
515       /* Make CATEGORYVALUE point to the next element of the list.  */
516       while (categoryvalue[0] != '\0' && categoryvalue[0] == ':')
517         ++categoryvalue;
518       if (categoryvalue[0] == '\0')
519         {
520           /* The whole contents of CATEGORYVALUE has been searched but
521              no valid entry has been found.  We solve this situation
522              by implicitly appending a "C" entry, i.e. no translation
523              will take place.  */
524           single_locale[0] = 'C';
525           single_locale[1] = '\0';
526         }
527       else
528         {
529           char *cp = single_locale;
530           while (categoryvalue[0] != '\0' && categoryvalue[0] != ':')
531             *cp++ = *categoryvalue++;
532           *cp = '\0';
533
534           /* When this is a SUID binary we must not allow accessing files
535              outside the dedicated directories.  */
536           if (ENABLE_SECURE && strchr (single_locale, '/') != NULL)
537             /* Ingore this entry.  */
538             continue;
539         }
540
541       /* If the current locale value is C (or POSIX) we don't load a
542          domain.  Return the MSGID.  */
543       if (strcmp (single_locale, "C") == 0
544           || strcmp (single_locale, "POSIX") == 0)
545         {
546           FREE_BLOCKS (block_list);
547           __libc_rwlock_unlock (_nl_state_lock);
548           __set_errno (saved_errno);
549           return (plural == 0
550                   ? (char *) msgid1
551                   /* Use the Germanic plural rule.  */
552                   : n == 1 ? (char *) msgid1 : (char *) msgid2);
553         }
554
555
556       /* Find structure describing the message catalog matching the
557          DOMAINNAME and CATEGORY.  */
558       domain = _nl_find_domain (dirname, single_locale, xdomainname, binding);
559
560       if (domain != NULL)
561         {
562           retval = _nl_find_msg (domain, binding, msgid1, &retlen);
563
564           if (retval == NULL)
565             {
566               int cnt;
567
568               for (cnt = 0; domain->successor[cnt] != NULL; ++cnt)
569                 {
570                   retval = _nl_find_msg (domain->successor[cnt], binding,
571                                          msgid1, &retlen);
572
573                   if (retval != NULL)
574                     {
575                       domain = domain->successor[cnt];
576                       break;
577                     }
578                 }
579             }
580
581           if (retval != NULL)
582             {
583               /* Found the translation of MSGID1 in domain DOMAIN:
584                  starting at RETVAL, RETLEN bytes.  */
585               FREE_BLOCKS (block_list);
586               __set_errno (saved_errno);
587 #if defined HAVE_TSEARCH || defined _LIBC
588               if (foundp == NULL)
589                 {
590                   /* Create a new entry and add it to the search tree.  */
591                   struct known_translation_t *newp;
592
593                   newp = (struct known_translation_t *)
594                     malloc (offsetof (struct known_translation_t, msgid)
595                             + msgid_len + domainname_len + 1);
596                   if (newp != NULL)
597                     {
598                       newp->domainname =
599                         mempcpy (newp->msgid, msgid1, msgid_len);
600                       memcpy (newp->domainname, domainname, domainname_len + 1);
601                       newp->category = category;
602                       newp->counter = _nl_msg_cat_cntr;
603                       newp->domain = domain;
604                       newp->translation = retval;
605                       newp->translation_length = retlen;
606
607                       /* Insert the entry in the search tree.  */
608                       foundp = (struct known_translation_t **)
609                         tsearch (newp, &root, transcmp);
610                       if (foundp == NULL
611                           || __builtin_expect (*foundp != newp, 0))
612                         /* The insert failed.  */
613                         free (newp);
614                     }
615                 }
616               else
617                 {
618                   /* We can update the existing entry.  */
619                   (*foundp)->counter = _nl_msg_cat_cntr;
620                   (*foundp)->domain = domain;
621                   (*foundp)->translation = retval;
622                   (*foundp)->translation_length = retlen;
623                 }
624 #endif
625               /* Now deal with plural.  */
626               if (plural)
627                 retval = plural_lookup (domain, n, retval, retlen);
628
629               __libc_rwlock_unlock (_nl_state_lock);
630               return retval;
631             }
632         }
633     }
634   /* NOTREACHED */
635 }
636
637
638 char *
639 internal_function
640 _nl_find_msg (domain_file, domainbinding, msgid, lengthp)
641      struct loaded_l10nfile *domain_file;
642      struct binding *domainbinding;
643      const char *msgid;
644      size_t *lengthp;
645 {
646   struct loaded_domain *domain;
647   nls_uint32 nstrings;
648   size_t act;
649   char *result;
650   size_t resultlen;
651
652   if (domain_file->decided == 0)
653     _nl_load_domain (domain_file, domainbinding);
654
655   if (domain_file->data == NULL)
656     return NULL;
657
658   domain = (struct loaded_domain *) domain_file->data;
659
660   nstrings = domain->nstrings;
661
662   /* Locate the MSGID and its translation.  */
663   if (domain->hash_tab != NULL)
664     {
665       /* Use the hashing table.  */
666       nls_uint32 len = strlen (msgid);
667       nls_uint32 hash_val = hash_string (msgid);
668       nls_uint32 idx = hash_val % domain->hash_size;
669       nls_uint32 incr = 1 + (hash_val % (domain->hash_size - 2));
670
671       while (1)
672         {
673           nls_uint32 nstr =
674             W (domain->must_swap_hash_tab, domain->hash_tab[idx]);
675
676           if (nstr == 0)
677             /* Hash table entry is empty.  */
678             return NULL;
679
680           nstr--;
681
682           /* Compare msgid with the original string at index nstr.
683              We compare the lengths with >=, not ==, because plural entries
684              are represented by strings with an embedded NUL.  */
685           if (nstr < nstrings
686               ? W (domain->must_swap, domain->orig_tab[nstr].length) >= len
687                 && (strcmp (msgid,
688                             domain->data + W (domain->must_swap,
689                                               domain->orig_tab[nstr].offset))
690                     == 0)
691               : domain->orig_sysdep_tab[nstr - nstrings].length > len
692                 && (strcmp (msgid,
693                             domain->orig_sysdep_tab[nstr - nstrings].pointer)
694                     == 0))
695             {
696               act = nstr;
697               goto found;
698             }
699
700           if (idx >= domain->hash_size - incr)
701             idx -= domain->hash_size - incr;
702           else
703             idx += incr;
704         }
705       /* NOTREACHED */
706     }
707   else
708     {
709       /* Try the default method:  binary search in the sorted array of
710          messages.  */
711       size_t top, bottom;
712
713       bottom = 0;
714       top = nstrings;
715       while (bottom < top)
716         {
717           int cmp_val;
718
719           act = (bottom + top) / 2;
720           cmp_val = strcmp (msgid, (domain->data
721                                     + W (domain->must_swap,
722                                          domain->orig_tab[act].offset)));
723           if (cmp_val < 0)
724             top = act;
725           else if (cmp_val > 0)
726             bottom = act + 1;
727           else
728             goto found;
729         }
730       /* No translation was found.  */
731       return NULL;
732     }
733
734  found:
735   /* The translation was found at index ACT.  If we have to convert the
736      string to use a different character set, this is the time.  */
737   if (act < nstrings)
738     {
739       result = (char *)
740         (domain->data + W (domain->must_swap, domain->trans_tab[act].offset));
741       resultlen = W (domain->must_swap, domain->trans_tab[act].length) + 1;
742     }
743   else
744     {
745       result = (char *) domain->trans_sysdep_tab[act - nstrings].pointer;
746       resultlen = domain->trans_sysdep_tab[act - nstrings].length;
747     }
748
749 #if defined _LIBC || HAVE_ICONV
750   if (domain->codeset_cntr
751       != (domainbinding != NULL ? domainbinding->codeset_cntr : 0))
752     {
753       /* The domain's codeset has changed through bind_textdomain_codeset()
754          since the message catalog was initialized or last accessed.  We
755          have to reinitialize the converter.  */
756       _nl_free_domain_conv (domain);
757       _nl_init_domain_conv (domain_file, domain, domainbinding);
758     }
759
760   if (
761 # ifdef _LIBC
762       domain->conv != (__gconv_t) -1
763 # else
764 #  if HAVE_ICONV
765       domain->conv != (iconv_t) -1
766 #  endif
767 # endif
768       )
769     {
770       /* We are supposed to do a conversion.  First allocate an
771          appropriate table with the same structure as the table
772          of translations in the file, where we can put the pointers
773          to the converted strings in.
774          There is a slight complication with plural entries.  They
775          are represented by consecutive NUL terminated strings.  We
776          handle this case by converting RESULTLEN bytes, including
777          NULs.  */
778
779       if (domain->conv_tab == NULL
780           && ((domain->conv_tab =
781                  (char **) calloc (nstrings + domain->n_sysdep_strings,
782                                    sizeof (char *)))
783               == NULL))
784         /* Mark that we didn't succeed allocating a table.  */
785         domain->conv_tab = (char **) -1;
786
787       if (__builtin_expect (domain->conv_tab == (char **) -1, 0))
788         /* Nothing we can do, no more memory.  */
789         goto converted;
790
791       if (domain->conv_tab[act] == NULL)
792         {
793           /* We haven't used this string so far, so it is not
794              translated yet.  Do this now.  */
795           /* We use a bit more efficient memory handling.
796              We allocate always larger blocks which get used over
797              time.  This is faster than many small allocations.   */
798           __libc_lock_define_initialized (static, lock)
799 # define INITIAL_BLOCK_SIZE     4080
800           static unsigned char *freemem;
801           static size_t freemem_size;
802
803           const unsigned char *inbuf;
804           unsigned char *outbuf;
805           int malloc_count;
806 # ifndef _LIBC
807           transmem_block_t *transmem_list = NULL;
808 # endif
809
810           __libc_lock_lock (lock);
811
812           inbuf = (const unsigned char *) result;
813           outbuf = freemem + sizeof (size_t);
814
815           malloc_count = 0;
816           while (1)
817             {
818               transmem_block_t *newmem;
819 # ifdef _LIBC
820               size_t non_reversible;
821               int res;
822
823               if (freemem_size < sizeof (size_t))
824                 goto resize_freemem;
825
826               res = __gconv (domain->conv,
827                              &inbuf, inbuf + resultlen,
828                              &outbuf,
829                              outbuf + freemem_size - sizeof (size_t),
830                              &non_reversible);
831
832               if (res == __GCONV_OK || res == __GCONV_EMPTY_INPUT)
833                 break;
834
835               if (res != __GCONV_FULL_OUTPUT)
836                 {
837                   __libc_lock_unlock (lock);
838                   goto converted;
839                 }
840
841               inbuf = result;
842 # else
843 #  if HAVE_ICONV
844               const char *inptr = (const char *) inbuf;
845               size_t inleft = resultlen;
846               char *outptr = (char *) outbuf;
847               size_t outleft;
848
849               if (freemem_size < sizeof (size_t))
850                 goto resize_freemem;
851
852               outleft = freemem_size - sizeof (size_t);
853               if (iconv (domain->conv,
854                          (ICONV_CONST char **) &inptr, &inleft,
855                          &outptr, &outleft)
856                   != (size_t) (-1))
857                 {
858                   outbuf = (unsigned char *) outptr;
859                   break;
860                 }
861               if (errno != E2BIG)
862                 {
863                   __libc_lock_unlock (lock);
864                   goto converted;
865                 }
866 #  endif
867 # endif
868
869             resize_freemem:
870               /* We must allocate a new buffer or resize the old one.  */
871               if (malloc_count > 0)
872                 {
873                   ++malloc_count;
874                   freemem_size = malloc_count * INITIAL_BLOCK_SIZE;
875                   newmem = (transmem_block_t *) realloc (transmem_list,
876                                                          freemem_size);
877 # ifdef _LIBC
878                   if (newmem != NULL)
879                     transmem_list = transmem_list->next;
880                   else
881                     {
882                       struct transmem_list *old = transmem_list;
883
884                       transmem_list = transmem_list->next;
885                       free (old);
886                     }
887 # endif
888                 }
889               else
890                 {
891                   malloc_count = 1;
892                   freemem_size = INITIAL_BLOCK_SIZE;
893                   newmem = (transmem_block_t *) malloc (freemem_size);
894                 }
895               if (__builtin_expect (newmem == NULL, 0))
896                 {
897                   freemem = NULL;
898                   freemem_size = 0;
899                   __libc_lock_unlock (lock);
900                   goto converted;
901                 }
902
903 # ifdef _LIBC
904               /* Add the block to the list of blocks we have to free
905                  at some point.  */
906               newmem->next = transmem_list;
907               transmem_list = newmem;
908
909               freemem = newmem->data;
910               freemem_size -= offsetof (struct transmem_list, data);
911 # else
912               transmem_list = newmem;
913               freemem = newmem;
914 # endif
915
916               outbuf = freemem + sizeof (size_t);
917             }
918
919           /* We have now in our buffer a converted string.  Put this
920              into the table of conversions.  */
921           *(size_t *) freemem = outbuf - freemem - sizeof (size_t);
922           domain->conv_tab[act] = (char *) freemem;
923           /* Shrink freemem, but keep it aligned.  */
924           freemem_size -= outbuf - freemem;
925           freemem = outbuf;
926           freemem += freemem_size & (alignof (size_t) - 1);
927           freemem_size = freemem_size & ~ (alignof (size_t) - 1);
928
929           __libc_lock_unlock (lock);
930         }
931
932       /* Now domain->conv_tab[act] contains the translation of all
933          the plural variants.  */
934       result = domain->conv_tab[act] + sizeof (size_t);
935       resultlen = *(size_t *) domain->conv_tab[act];
936     }
937
938  converted:
939   /* The result string is converted.  */
940
941 #endif /* _LIBC || HAVE_ICONV */
942
943   *lengthp = resultlen;
944   return result;
945 }
946
947
948 /* Look up a plural variant.  */
949 static char *
950 internal_function
951 plural_lookup (domain, n, translation, translation_len)
952      struct loaded_l10nfile *domain;
953      unsigned long int n;
954      const char *translation;
955      size_t translation_len;
956 {
957   struct loaded_domain *domaindata = (struct loaded_domain *) domain->data;
958   unsigned long int index;
959   const char *p;
960
961   index = plural_eval (domaindata->plural, n);
962   if (index >= domaindata->nplurals)
963     /* This should never happen.  It means the plural expression and the
964        given maximum value do not match.  */
965     index = 0;
966
967   /* Skip INDEX strings at TRANSLATION.  */
968   p = translation;
969   while (index-- > 0)
970     {
971 #ifdef _LIBC
972       p = __rawmemchr (p, '\0');
973 #else
974       p = strchr (p, '\0');
975 #endif
976       /* And skip over the NUL byte.  */
977       p++;
978
979       if (p >= translation + translation_len)
980         /* This should never happen.  It means the plural expression
981            evaluated to a value larger than the number of variants
982            available for MSGID1.  */
983         return (char *) translation;
984     }
985   return (char *) p;
986 }
987
988
989 /* Return string representation of locale CATEGORY.  */
990 static const char *
991 internal_function
992 category_to_name (category)
993      int category;
994 {
995   const char *retval;
996
997   switch (category)
998   {
999 #ifdef LC_COLLATE
1000   case LC_COLLATE:
1001     retval = "LC_COLLATE";
1002     break;
1003 #endif
1004 #ifdef LC_CTYPE
1005   case LC_CTYPE:
1006     retval = "LC_CTYPE";
1007     break;
1008 #endif
1009 #ifdef LC_MONETARY
1010   case LC_MONETARY:
1011     retval = "LC_MONETARY";
1012     break;
1013 #endif
1014 #ifdef LC_NUMERIC
1015   case LC_NUMERIC:
1016     retval = "LC_NUMERIC";
1017     break;
1018 #endif
1019 #ifdef LC_TIME
1020   case LC_TIME:
1021     retval = "LC_TIME";
1022     break;
1023 #endif
1024 #ifdef LC_MESSAGES
1025   case LC_MESSAGES:
1026     retval = "LC_MESSAGES";
1027     break;
1028 #endif
1029 #ifdef LC_RESPONSE
1030   case LC_RESPONSE:
1031     retval = "LC_RESPONSE";
1032     break;
1033 #endif
1034 #ifdef LC_ALL
1035   case LC_ALL:
1036     /* This might not make sense but is perhaps better than any other
1037        value.  */
1038     retval = "LC_ALL";
1039     break;
1040 #endif
1041   default:
1042     /* If you have a better idea for a default value let me know.  */
1043     retval = "LC_XXX";
1044   }
1045
1046   return retval;
1047 }
1048
1049 /* Guess value of current locale from value of the environment variables.  */
1050 static const char *
1051 internal_function
1052 guess_category_value (category, categoryname)
1053      int category;
1054      const char *categoryname;
1055 {
1056   const char *language;
1057   const char *retval;
1058
1059   /* The highest priority value is the `LANGUAGE' environment
1060      variable.  But we don't use the value if the currently selected
1061      locale is the C locale.  This is a GNU extension.  */
1062   language = getenv ("LANGUAGE");
1063   if (language != NULL && language[0] == '\0')
1064     language = NULL;
1065
1066   /* We have to proceed with the POSIX methods of looking to `LC_ALL',
1067      `LC_xxx', and `LANG'.  On some systems this can be done by the
1068      `setlocale' function itself.  */
1069 #ifdef _LIBC
1070   retval = __current_locale_name (category);
1071 #else
1072   retval = _nl_locale_name (category, categoryname);
1073 #endif
1074
1075   return language != NULL && strcmp (retval, "C") != 0 ? language : retval;
1076 }
1077
1078 /* @@ begin of epilog @@ */
1079
1080 /* We don't want libintl.a to depend on any other library.  So we
1081    avoid the non-standard function stpcpy.  In GNU C Library this
1082    function is available, though.  Also allow the symbol HAVE_STPCPY
1083    to be defined.  */
1084 #if !_LIBC && !HAVE_STPCPY
1085 static char *
1086 stpcpy (dest, src)
1087      char *dest;
1088      const char *src;
1089 {
1090   while ((*dest++ = *src++) != '\0')
1091     /* Do nothing. */ ;
1092   return dest - 1;
1093 }
1094 #endif
1095
1096 #if !_LIBC && !HAVE_MEMPCPY
1097 static void *
1098 mempcpy (dest, src, n)
1099      void *dest;
1100      const void *src;
1101      size_t n;
1102 {
1103   return (void *) ((char *) memcpy (dest, src, n) + n);
1104 }
1105 #endif
1106
1107
1108 #ifdef _LIBC
1109 /* If we want to free all resources we have to do some work at
1110    program's end.  */
1111 static void __attribute__ ((unused))
1112 free_mem (void)
1113 {
1114   void *old;
1115
1116   while (_nl_domain_bindings != NULL)
1117     {
1118       struct binding *oldp = _nl_domain_bindings;
1119       _nl_domain_bindings = _nl_domain_bindings->next;
1120       if (oldp->dirname != INTUSE(_nl_default_dirname))
1121         /* Yes, this is a pointer comparison.  */
1122         free (oldp->dirname);
1123       free (oldp->codeset);
1124       free (oldp);
1125     }
1126
1127   if (_nl_current_default_domain != _nl_default_default_domain)
1128     /* Yes, again a pointer comparison.  */
1129     free ((char *) _nl_current_default_domain);
1130
1131   /* Remove the search tree with the known translations.  */
1132   __tdestroy (root, free);
1133   root = NULL;
1134
1135   while (transmem_list != NULL)
1136     {
1137       old = transmem_list;
1138       transmem_list = transmem_list->next;
1139       free (old);
1140     }
1141 }
1142
1143 text_set_element (__libc_subfreeres, free_mem);
1144 #endif