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