xref: /openbsd-src/gnu/usr.bin/binutils/intl/dcgettext.c (revision d2201f2f89f0be1a0be6f7568000ed297414a06d)
1*d2201f2fSdrahn /* Implementation of the dcgettext(3) function.
2*d2201f2fSdrahn    Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
3f7cc78ecSespie 
4f7cc78ecSespie    This program is free software; you can redistribute it and/or modify
5f7cc78ecSespie    it under the terms of the GNU General Public License as published by
6f7cc78ecSespie    the Free Software Foundation; either version 2, or (at your option)
7f7cc78ecSespie    any later version.
8f7cc78ecSespie 
9f7cc78ecSespie    This program is distributed in the hope that it will be useful,
10f7cc78ecSespie    but WITHOUT ANY WARRANTY; without even the implied warranty of
11f7cc78ecSespie    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12f7cc78ecSespie    GNU General Public License for more details.
13f7cc78ecSespie 
14f7cc78ecSespie    You should have received a copy of the GNU General Public License
15f7cc78ecSespie    along with this program; if not, write to the Free Software Foundation,
16f7cc78ecSespie    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
17f7cc78ecSespie 
18f7cc78ecSespie #ifdef HAVE_CONFIG_H
19f7cc78ecSespie # include <config.h>
20f7cc78ecSespie #endif
21f7cc78ecSespie 
22f7cc78ecSespie #include <sys/types.h>
23f7cc78ecSespie 
24f7cc78ecSespie #ifdef __GNUC__
25f7cc78ecSespie # define alloca __builtin_alloca
26f7cc78ecSespie # define HAVE_ALLOCA 1
27f7cc78ecSespie #else
28f7cc78ecSespie # if defined HAVE_ALLOCA_H || defined _LIBC
29f7cc78ecSespie #  include <alloca.h>
30f7cc78ecSespie # else
31f7cc78ecSespie #  ifdef _AIX
32f7cc78ecSespie  #pragma alloca
33f7cc78ecSespie #  else
34f7cc78ecSespie #   ifndef alloca
35f7cc78ecSespie char *alloca ();
36f7cc78ecSespie #   endif
37f7cc78ecSespie #  endif
38f7cc78ecSespie # endif
39f7cc78ecSespie #endif
40f7cc78ecSespie 
41f7cc78ecSespie #include <errno.h>
42f7cc78ecSespie #ifndef errno
43f7cc78ecSespie extern int errno;
44f7cc78ecSespie #endif
45f7cc78ecSespie #ifndef __set_errno
46f7cc78ecSespie # define __set_errno(val) errno = (val)
47f7cc78ecSespie #endif
48f7cc78ecSespie 
49f7cc78ecSespie #if defined STDC_HEADERS || defined _LIBC
50f7cc78ecSespie # include <stdlib.h>
51f7cc78ecSespie #else
52f7cc78ecSespie char *getenv ();
53f7cc78ecSespie # ifdef HAVE_MALLOC_H
54f7cc78ecSespie #  include <malloc.h>
55f7cc78ecSespie # else
56f7cc78ecSespie void free ();
57f7cc78ecSespie # endif
58f7cc78ecSespie #endif
59f7cc78ecSespie 
60f7cc78ecSespie #if defined HAVE_STRING_H || defined _LIBC
61f7cc78ecSespie # ifndef _GNU_SOURCE
62f7cc78ecSespie #  define _GNU_SOURCE	1
63f7cc78ecSespie # endif
64f7cc78ecSespie # include <string.h>
65f7cc78ecSespie #else
66f7cc78ecSespie # include <strings.h>
67f7cc78ecSespie #endif
68f7cc78ecSespie #if !HAVE_STRCHR && !defined _LIBC
69f7cc78ecSespie # ifndef strchr
70f7cc78ecSespie #  define strchr index
71f7cc78ecSespie # endif
72f7cc78ecSespie #endif
73f7cc78ecSespie 
74f7cc78ecSespie #if defined HAVE_UNISTD_H || defined _LIBC
75f7cc78ecSespie # include <unistd.h>
76f7cc78ecSespie #endif
77f7cc78ecSespie 
78f7cc78ecSespie #include "gettext.h"
79f7cc78ecSespie #include "gettextP.h"
80f7cc78ecSespie #ifdef _LIBC
81f7cc78ecSespie # include <libintl.h>
82f7cc78ecSespie #else
83f7cc78ecSespie # include "libgettext.h"
84f7cc78ecSespie #endif
85f7cc78ecSespie #include "hash-string.h"
86f7cc78ecSespie 
87f7cc78ecSespie /* @@ end of prolog @@ */
88f7cc78ecSespie 
89f7cc78ecSespie #ifdef _LIBC
90f7cc78ecSespie /* Rename the non ANSI C functions.  This is required by the standard
91f7cc78ecSespie    because some ANSI C functions will require linking with this object
92f7cc78ecSespie    file and the name space must not be polluted.  */
93f7cc78ecSespie # define getcwd __getcwd
94*d2201f2fSdrahn # ifndef stpcpy
95f7cc78ecSespie #  define stpcpy __stpcpy
96*d2201f2fSdrahn # endif
97f7cc78ecSespie #else
98f7cc78ecSespie # if !defined HAVE_GETCWD
99f7cc78ecSespie char *getwd ();
100f7cc78ecSespie #  define getcwd(buf, max) getwd (buf)
101f7cc78ecSespie # else
102f7cc78ecSespie char *getcwd ();
103f7cc78ecSespie # endif
104f7cc78ecSespie # ifndef HAVE_STPCPY
105f7cc78ecSespie static char *stpcpy PARAMS ((char *dest, const char *src));
106f7cc78ecSespie # endif
107f7cc78ecSespie #endif
108f7cc78ecSespie 
109f7cc78ecSespie /* Amount to increase buffer size by in each try.  */
110f7cc78ecSespie #define PATH_INCR 32
111f7cc78ecSespie 
112f7cc78ecSespie /* The following is from pathmax.h.  */
113f7cc78ecSespie /* Non-POSIX BSD systems might have gcc's limits.h, which doesn't define
114f7cc78ecSespie    PATH_MAX but might cause redefinition warnings when sys/param.h is
115f7cc78ecSespie    later included (as on MORE/BSD 4.3).  */
116f7cc78ecSespie #if defined(_POSIX_VERSION) || (defined(HAVE_LIMITS_H) && !defined(__GNUC__))
117f7cc78ecSespie # include <limits.h>
118f7cc78ecSespie #endif
119f7cc78ecSespie 
120f7cc78ecSespie #ifndef _POSIX_PATH_MAX
121f7cc78ecSespie # define _POSIX_PATH_MAX 255
122f7cc78ecSespie #endif
123f7cc78ecSespie 
124f7cc78ecSespie #if !defined(PATH_MAX) && defined(_PC_PATH_MAX)
125f7cc78ecSespie # define PATH_MAX (pathconf ("/", _PC_PATH_MAX) < 1 ? 1024 : pathconf ("/", _PC_PATH_MAX))
126f7cc78ecSespie #endif
127f7cc78ecSespie 
128f7cc78ecSespie /* Don't include sys/param.h if it already has been.  */
129f7cc78ecSespie #if defined(HAVE_SYS_PARAM_H) && !defined(PATH_MAX) && !defined(MAXPATHLEN)
130f7cc78ecSespie # include <sys/param.h>
131f7cc78ecSespie #endif
132f7cc78ecSespie 
133f7cc78ecSespie #if !defined(PATH_MAX) && defined(MAXPATHLEN)
134f7cc78ecSespie # define PATH_MAX MAXPATHLEN
135f7cc78ecSespie #endif
136f7cc78ecSespie 
137f7cc78ecSespie #ifndef PATH_MAX
138f7cc78ecSespie # define PATH_MAX _POSIX_PATH_MAX
139f7cc78ecSespie #endif
140f7cc78ecSespie 
141f7cc78ecSespie /* XPG3 defines the result of `setlocale (category, NULL)' as:
142f7cc78ecSespie    ``Directs `setlocale()' to query `category' and return the current
143f7cc78ecSespie      setting of `local'.''
144f7cc78ecSespie    However it does not specify the exact format.  And even worse: POSIX
145f7cc78ecSespie    defines this not at all.  So we can use this feature only on selected
146f7cc78ecSespie    system (e.g. those using GNU C Library).  */
147f7cc78ecSespie #ifdef _LIBC
148f7cc78ecSespie # define HAVE_LOCALE_NULL
149f7cc78ecSespie #endif
150f7cc78ecSespie 
151f7cc78ecSespie /* Name of the default domain used for gettext(3) prior any call to
152f7cc78ecSespie    textdomain(3).  The default value for this is "messages".  */
153f7cc78ecSespie const char _nl_default_default_domain[] = "messages";
154f7cc78ecSespie 
155f7cc78ecSespie /* Value used as the default domain for gettext(3).  */
156f7cc78ecSespie const char *_nl_current_default_domain = _nl_default_default_domain;
157f7cc78ecSespie 
158f7cc78ecSespie /* Contains the default location of the message catalogs.  */
159f7cc78ecSespie const char _nl_default_dirname[] = GNULOCALEDIR;
160f7cc78ecSespie 
161f7cc78ecSespie /* List with bindings of specific domains created by bindtextdomain()
162f7cc78ecSespie    calls.  */
163f7cc78ecSespie struct binding *_nl_domain_bindings;
164f7cc78ecSespie 
165f7cc78ecSespie /* Prototypes for local functions.  */
166f7cc78ecSespie static char *find_msg PARAMS ((struct loaded_l10nfile *domain_file,
167*d2201f2fSdrahn 			       const char *msgid)) internal_function;
168*d2201f2fSdrahn static const char *category_to_name PARAMS ((int category)) internal_function;
169f7cc78ecSespie static const char *guess_category_value PARAMS ((int category,
170*d2201f2fSdrahn 						 const char *categoryname))
171*d2201f2fSdrahn      internal_function;
172f7cc78ecSespie 
173f7cc78ecSespie 
174f7cc78ecSespie /* For those loosing systems which don't have `alloca' we have to add
175f7cc78ecSespie    some additional code emulating it.  */
176f7cc78ecSespie #ifdef HAVE_ALLOCA
177f7cc78ecSespie /* Nothing has to be done.  */
178f7cc78ecSespie # define ADD_BLOCK(list, address) /* nothing */
179f7cc78ecSespie # define FREE_BLOCKS(list) /* nothing */
180f7cc78ecSespie #else
181f7cc78ecSespie struct block_list
182f7cc78ecSespie {
183f7cc78ecSespie   void *address;
184f7cc78ecSespie   struct block_list *next;
185f7cc78ecSespie };
186f7cc78ecSespie # define ADD_BLOCK(list, addr)						      \
187f7cc78ecSespie   do {									      \
188f7cc78ecSespie     struct block_list *newp = (struct block_list *) malloc (sizeof (*newp));  \
189f7cc78ecSespie     /* If we cannot get a free block we cannot add the new element to	      \
190f7cc78ecSespie        the list.  */							      \
191f7cc78ecSespie     if (newp != NULL) {							      \
192f7cc78ecSespie       newp->address = (addr);						      \
193f7cc78ecSespie       newp->next = (list);						      \
194f7cc78ecSespie       (list) = newp;							      \
195f7cc78ecSespie     }									      \
196f7cc78ecSespie   } while (0)
197f7cc78ecSespie # define FREE_BLOCKS(list)						      \
198f7cc78ecSespie   do {									      \
199f7cc78ecSespie     while (list != NULL) {						      \
200f7cc78ecSespie       struct block_list *old = list;					      \
201f7cc78ecSespie       list = list->next;						      \
202f7cc78ecSespie       free (old);							      \
203f7cc78ecSespie     }									      \
204f7cc78ecSespie   } while (0)
205f7cc78ecSespie # undef alloca
206f7cc78ecSespie # define alloca(size) (malloc (size))
207f7cc78ecSespie #endif	/* have alloca */
208f7cc78ecSespie 
209f7cc78ecSespie 
210f7cc78ecSespie /* Names for the libintl functions are a problem.  They must not clash
211f7cc78ecSespie    with existing names and they should follow ANSI C.  But this source
212f7cc78ecSespie    code is also used in GNU C Library where the names have a __
213f7cc78ecSespie    prefix.  So we have to make a difference here.  */
214f7cc78ecSespie #ifdef _LIBC
215f7cc78ecSespie # define DCGETTEXT __dcgettext
216f7cc78ecSespie #else
217f7cc78ecSespie # define DCGETTEXT dcgettext__
218f7cc78ecSespie #endif
219f7cc78ecSespie 
220f7cc78ecSespie /* Look up MSGID in the DOMAINNAME message catalog for the current CATEGORY
221f7cc78ecSespie    locale.  */
222f7cc78ecSespie char *
DCGETTEXT(domainname,msgid,category)223f7cc78ecSespie DCGETTEXT (domainname, msgid, category)
224f7cc78ecSespie      const char *domainname;
225f7cc78ecSespie      const char *msgid;
226f7cc78ecSespie      int category;
227f7cc78ecSespie {
228f7cc78ecSespie #ifndef HAVE_ALLOCA
229f7cc78ecSespie   struct block_list *block_list = NULL;
230f7cc78ecSespie #endif
231f7cc78ecSespie   struct loaded_l10nfile *domain;
232f7cc78ecSespie   struct binding *binding;
233f7cc78ecSespie   const char *categoryname;
234f7cc78ecSespie   const char *categoryvalue;
235f7cc78ecSespie   char *dirname, *xdomainname;
236f7cc78ecSespie   char *single_locale;
237f7cc78ecSespie   char *retval;
238f7cc78ecSespie   int saved_errno = errno;
239f7cc78ecSespie 
240f7cc78ecSespie   /* If no real MSGID is given return NULL.  */
241f7cc78ecSespie   if (msgid == NULL)
242f7cc78ecSespie     return NULL;
243f7cc78ecSespie 
244f7cc78ecSespie   /* If DOMAINNAME is NULL, we are interested in the default domain.  If
245f7cc78ecSespie      CATEGORY is not LC_MESSAGES this might not make much sense but the
246f7cc78ecSespie      defintion left this undefined.  */
247f7cc78ecSespie   if (domainname == NULL)
248f7cc78ecSespie     domainname = _nl_current_default_domain;
249f7cc78ecSespie 
250f7cc78ecSespie   /* First find matching binding.  */
251f7cc78ecSespie   for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next)
252f7cc78ecSespie     {
253f7cc78ecSespie       int compare = strcmp (domainname, binding->domainname);
254f7cc78ecSespie       if (compare == 0)
255f7cc78ecSespie 	/* We found it!  */
256f7cc78ecSespie 	break;
257f7cc78ecSespie       if (compare < 0)
258f7cc78ecSespie 	{
259f7cc78ecSespie 	  /* It is not in the list.  */
260f7cc78ecSespie 	  binding = NULL;
261f7cc78ecSespie 	  break;
262f7cc78ecSespie 	}
263f7cc78ecSespie     }
264f7cc78ecSespie 
265f7cc78ecSespie   if (binding == NULL)
266f7cc78ecSespie     dirname = (char *) _nl_default_dirname;
267f7cc78ecSespie   else if (binding->dirname[0] == '/')
268f7cc78ecSespie     dirname = binding->dirname;
269f7cc78ecSespie   else
270f7cc78ecSespie     {
271f7cc78ecSespie       /* We have a relative path.  Make it absolute now.  */
272f7cc78ecSespie       size_t dirname_len = strlen (binding->dirname) + 1;
273f7cc78ecSespie       size_t path_max;
274f7cc78ecSespie       char *ret;
275f7cc78ecSespie 
276f7cc78ecSespie       path_max = (unsigned) PATH_MAX;
277f7cc78ecSespie       path_max += 2;		/* The getcwd docs say to do this.  */
278f7cc78ecSespie 
279f7cc78ecSespie       dirname = (char *) alloca (path_max + dirname_len);
280f7cc78ecSespie       ADD_BLOCK (block_list, dirname);
281f7cc78ecSespie 
282f7cc78ecSespie       __set_errno (0);
283f7cc78ecSespie       while ((ret = getcwd (dirname, path_max)) == NULL && errno == ERANGE)
284f7cc78ecSespie 	{
285f7cc78ecSespie 	  path_max += PATH_INCR;
286f7cc78ecSespie 	  dirname = (char *) alloca (path_max + dirname_len);
287f7cc78ecSespie 	  ADD_BLOCK (block_list, dirname);
288f7cc78ecSespie 	  __set_errno (0);
289f7cc78ecSespie 	}
290f7cc78ecSespie 
291f7cc78ecSespie       if (ret == NULL)
292f7cc78ecSespie 	{
293f7cc78ecSespie 	  /* We cannot get the current working directory.  Don't signal an
294f7cc78ecSespie 	     error but simply return the default string.  */
295f7cc78ecSespie 	  FREE_BLOCKS (block_list);
296f7cc78ecSespie 	  __set_errno (saved_errno);
297f7cc78ecSespie 	  return (char *) msgid;
298f7cc78ecSespie 	}
299f7cc78ecSespie 
300f7cc78ecSespie       stpcpy (stpcpy (strchr (dirname, '\0'), "/"), binding->dirname);
301f7cc78ecSespie     }
302f7cc78ecSespie 
303f7cc78ecSespie   /* Now determine the symbolic name of CATEGORY and its value.  */
304f7cc78ecSespie   categoryname = category_to_name (category);
305f7cc78ecSespie   categoryvalue = guess_category_value (category, categoryname);
306f7cc78ecSespie 
307f7cc78ecSespie   xdomainname = (char *) alloca (strlen (categoryname)
308f7cc78ecSespie 				 + strlen (domainname) + 5);
309f7cc78ecSespie   ADD_BLOCK (block_list, xdomainname);
310f7cc78ecSespie 
311f7cc78ecSespie   stpcpy (stpcpy (stpcpy (stpcpy (xdomainname, categoryname), "/"),
312f7cc78ecSespie 		  domainname),
313f7cc78ecSespie 	  ".mo");
314f7cc78ecSespie 
315f7cc78ecSespie   /* Creating working area.  */
316f7cc78ecSespie   single_locale = (char *) alloca (strlen (categoryvalue) + 1);
317f7cc78ecSespie   ADD_BLOCK (block_list, single_locale);
318f7cc78ecSespie 
319f7cc78ecSespie 
320f7cc78ecSespie   /* Search for the given string.  This is a loop because we perhaps
321f7cc78ecSespie      got an ordered list of languages to consider for th translation.  */
322f7cc78ecSespie   while (1)
323f7cc78ecSespie     {
324f7cc78ecSespie       /* Make CATEGORYVALUE point to the next element of the list.  */
325f7cc78ecSespie       while (categoryvalue[0] != '\0' && categoryvalue[0] == ':')
326f7cc78ecSespie 	++categoryvalue;
327f7cc78ecSespie       if (categoryvalue[0] == '\0')
328f7cc78ecSespie 	{
329f7cc78ecSespie 	  /* The whole contents of CATEGORYVALUE has been searched but
330f7cc78ecSespie 	     no valid entry has been found.  We solve this situation
331f7cc78ecSespie 	     by implicitly appending a "C" entry, i.e. no translation
332f7cc78ecSespie 	     will take place.  */
333f7cc78ecSespie 	  single_locale[0] = 'C';
334f7cc78ecSespie 	  single_locale[1] = '\0';
335f7cc78ecSespie 	}
336f7cc78ecSespie       else
337f7cc78ecSespie 	{
338f7cc78ecSespie 	  char *cp = single_locale;
339f7cc78ecSespie 	  while (categoryvalue[0] != '\0' && categoryvalue[0] != ':')
340f7cc78ecSespie 	    *cp++ = *categoryvalue++;
341f7cc78ecSespie 	  *cp = '\0';
342f7cc78ecSespie 	}
343f7cc78ecSespie 
344f7cc78ecSespie       /* If the current locale value is C (or POSIX) we don't load a
345f7cc78ecSespie 	 domain.  Return the MSGID.  */
346f7cc78ecSespie       if (strcmp (single_locale, "C") == 0
347f7cc78ecSespie 	  || strcmp (single_locale, "POSIX") == 0)
348f7cc78ecSespie 	{
349f7cc78ecSespie 	  FREE_BLOCKS (block_list);
350f7cc78ecSespie 	  __set_errno (saved_errno);
351f7cc78ecSespie 	  return (char *) msgid;
352f7cc78ecSespie 	}
353f7cc78ecSespie 
354f7cc78ecSespie 
355f7cc78ecSespie       /* Find structure describing the message catalog matching the
356f7cc78ecSespie 	 DOMAINNAME and CATEGORY.  */
357f7cc78ecSespie       domain = _nl_find_domain (dirname, single_locale, xdomainname);
358f7cc78ecSespie 
359f7cc78ecSespie       if (domain != NULL)
360f7cc78ecSespie 	{
361f7cc78ecSespie 	  retval = find_msg (domain, msgid);
362f7cc78ecSespie 
363f7cc78ecSespie 	  if (retval == NULL)
364f7cc78ecSespie 	    {
365f7cc78ecSespie 	      int cnt;
366f7cc78ecSespie 
367f7cc78ecSespie 	      for (cnt = 0; domain->successor[cnt] != NULL; ++cnt)
368f7cc78ecSespie 		{
369f7cc78ecSespie 		  retval = find_msg (domain->successor[cnt], msgid);
370f7cc78ecSespie 
371f7cc78ecSespie 		  if (retval != NULL)
372f7cc78ecSespie 		    break;
373f7cc78ecSespie 		}
374f7cc78ecSespie 	    }
375f7cc78ecSespie 
376f7cc78ecSespie 	  if (retval != NULL)
377f7cc78ecSespie 	    {
378f7cc78ecSespie 	      FREE_BLOCKS (block_list);
379f7cc78ecSespie 	      __set_errno (saved_errno);
380f7cc78ecSespie 	      return retval;
381f7cc78ecSespie 	    }
382f7cc78ecSespie 	}
383f7cc78ecSespie     }
384f7cc78ecSespie   /* NOTREACHED */
385f7cc78ecSespie }
386f7cc78ecSespie 
387f7cc78ecSespie #ifdef _LIBC
388f7cc78ecSespie /* Alias for function name in GNU C Library.  */
389f7cc78ecSespie weak_alias (__dcgettext, dcgettext);
390f7cc78ecSespie #endif
391f7cc78ecSespie 
392f7cc78ecSespie 
393f7cc78ecSespie static char *
394*d2201f2fSdrahn internal_function
find_msg(domain_file,msgid)395f7cc78ecSespie find_msg (domain_file, msgid)
396f7cc78ecSespie      struct loaded_l10nfile *domain_file;
397f7cc78ecSespie      const char *msgid;
398f7cc78ecSespie {
399f7cc78ecSespie   size_t top, act, bottom;
400f7cc78ecSespie   struct loaded_domain *domain;
401f7cc78ecSespie 
402f7cc78ecSespie   if (domain_file->decided == 0)
403f7cc78ecSespie     _nl_load_domain (domain_file);
404f7cc78ecSespie 
405f7cc78ecSespie   if (domain_file->data == NULL)
406f7cc78ecSespie     return NULL;
407f7cc78ecSespie 
408f7cc78ecSespie   domain = (struct loaded_domain *) domain_file->data;
409f7cc78ecSespie 
410f7cc78ecSespie   /* Locate the MSGID and its translation.  */
411f7cc78ecSespie   if (domain->hash_size > 2 && domain->hash_tab != NULL)
412f7cc78ecSespie     {
413f7cc78ecSespie       /* Use the hashing table.  */
414f7cc78ecSespie       nls_uint32 len = strlen (msgid);
415f7cc78ecSespie       nls_uint32 hash_val = hash_string (msgid);
416f7cc78ecSespie       nls_uint32 idx = hash_val % domain->hash_size;
417f7cc78ecSespie       nls_uint32 incr = 1 + (hash_val % (domain->hash_size - 2));
418f7cc78ecSespie       nls_uint32 nstr = W (domain->must_swap, domain->hash_tab[idx]);
419f7cc78ecSespie 
420f7cc78ecSespie       if (nstr == 0)
421f7cc78ecSespie 	/* Hash table entry is empty.  */
422f7cc78ecSespie 	return NULL;
423f7cc78ecSespie 
424f7cc78ecSespie       if (W (domain->must_swap, domain->orig_tab[nstr - 1].length) == len
425f7cc78ecSespie 	  && strcmp (msgid,
426f7cc78ecSespie 		     domain->data + W (domain->must_swap,
427f7cc78ecSespie 				       domain->orig_tab[nstr - 1].offset)) == 0)
428f7cc78ecSespie 	return (char *) domain->data + W (domain->must_swap,
429f7cc78ecSespie 					  domain->trans_tab[nstr - 1].offset);
430f7cc78ecSespie 
431f7cc78ecSespie       while (1)
432f7cc78ecSespie 	{
433f7cc78ecSespie 	  if (idx >= domain->hash_size - incr)
434f7cc78ecSespie 	    idx -= domain->hash_size - incr;
435f7cc78ecSespie 	  else
436f7cc78ecSespie 	    idx += incr;
437f7cc78ecSespie 
438f7cc78ecSespie 	  nstr = W (domain->must_swap, domain->hash_tab[idx]);
439f7cc78ecSespie 	  if (nstr == 0)
440f7cc78ecSespie 	    /* Hash table entry is empty.  */
441f7cc78ecSespie 	    return NULL;
442f7cc78ecSespie 
443f7cc78ecSespie 	  if (W (domain->must_swap, domain->orig_tab[nstr - 1].length) == len
444f7cc78ecSespie 	      && strcmp (msgid,
445f7cc78ecSespie 			 domain->data + W (domain->must_swap,
446f7cc78ecSespie 					   domain->orig_tab[nstr - 1].offset))
447f7cc78ecSespie 	         == 0)
448f7cc78ecSespie 	    return (char *) domain->data
449f7cc78ecSespie 	      + W (domain->must_swap, domain->trans_tab[nstr - 1].offset);
450f7cc78ecSespie 	}
451f7cc78ecSespie       /* NOTREACHED */
452f7cc78ecSespie     }
453f7cc78ecSespie 
454f7cc78ecSespie   /* Now we try the default method:  binary search in the sorted
455f7cc78ecSespie      array of messages.  */
456f7cc78ecSespie   bottom = 0;
457f7cc78ecSespie   top = domain->nstrings;
458f7cc78ecSespie   while (bottom < top)
459f7cc78ecSespie     {
460f7cc78ecSespie       int cmp_val;
461f7cc78ecSespie 
462f7cc78ecSespie       act = (bottom + top) / 2;
463f7cc78ecSespie       cmp_val = strcmp (msgid, domain->data
464f7cc78ecSespie 			       + W (domain->must_swap,
465f7cc78ecSespie 				    domain->orig_tab[act].offset));
466f7cc78ecSespie       if (cmp_val < 0)
467f7cc78ecSespie 	top = act;
468f7cc78ecSespie       else if (cmp_val > 0)
469f7cc78ecSespie 	bottom = act + 1;
470f7cc78ecSespie       else
471f7cc78ecSespie 	break;
472f7cc78ecSespie     }
473f7cc78ecSespie 
474f7cc78ecSespie   /* If an translation is found return this.  */
475f7cc78ecSespie   return bottom >= top ? NULL : (char *) domain->data
476f7cc78ecSespie                                 + W (domain->must_swap,
477f7cc78ecSespie 				     domain->trans_tab[act].offset);
478f7cc78ecSespie }
479f7cc78ecSespie 
480f7cc78ecSespie 
481f7cc78ecSespie /* Return string representation of locale CATEGORY.  */
482f7cc78ecSespie static const char *
483*d2201f2fSdrahn internal_function
category_to_name(category)484f7cc78ecSespie category_to_name (category)
485f7cc78ecSespie      int category;
486f7cc78ecSespie {
487f7cc78ecSespie   const char *retval;
488f7cc78ecSespie 
489f7cc78ecSespie   switch (category)
490f7cc78ecSespie   {
491f7cc78ecSespie #ifdef LC_COLLATE
492f7cc78ecSespie   case LC_COLLATE:
493f7cc78ecSespie     retval = "LC_COLLATE";
494f7cc78ecSespie     break;
495f7cc78ecSespie #endif
496f7cc78ecSespie #ifdef LC_CTYPE
497f7cc78ecSespie   case LC_CTYPE:
498f7cc78ecSespie     retval = "LC_CTYPE";
499f7cc78ecSespie     break;
500f7cc78ecSespie #endif
501f7cc78ecSespie #ifdef LC_MONETARY
502f7cc78ecSespie   case LC_MONETARY:
503f7cc78ecSespie     retval = "LC_MONETARY";
504f7cc78ecSespie     break;
505f7cc78ecSespie #endif
506f7cc78ecSespie #ifdef LC_NUMERIC
507f7cc78ecSespie   case LC_NUMERIC:
508f7cc78ecSespie     retval = "LC_NUMERIC";
509f7cc78ecSespie     break;
510f7cc78ecSespie #endif
511f7cc78ecSespie #ifdef LC_TIME
512f7cc78ecSespie   case LC_TIME:
513f7cc78ecSespie     retval = "LC_TIME";
514f7cc78ecSespie     break;
515f7cc78ecSespie #endif
516f7cc78ecSespie #ifdef LC_MESSAGES
517f7cc78ecSespie   case LC_MESSAGES:
518f7cc78ecSespie     retval = "LC_MESSAGES";
519f7cc78ecSespie     break;
520f7cc78ecSespie #endif
521f7cc78ecSespie #ifdef LC_RESPONSE
522f7cc78ecSespie   case LC_RESPONSE:
523f7cc78ecSespie     retval = "LC_RESPONSE";
524f7cc78ecSespie     break;
525f7cc78ecSespie #endif
526f7cc78ecSespie #ifdef LC_ALL
527f7cc78ecSespie   case LC_ALL:
528f7cc78ecSespie     /* This might not make sense but is perhaps better than any other
529f7cc78ecSespie        value.  */
530f7cc78ecSespie     retval = "LC_ALL";
531f7cc78ecSespie     break;
532f7cc78ecSespie #endif
533f7cc78ecSespie   default:
534f7cc78ecSespie     /* If you have a better idea for a default value let me know.  */
535f7cc78ecSespie     retval = "LC_XXX";
536f7cc78ecSespie   }
537f7cc78ecSespie 
538f7cc78ecSespie   return retval;
539f7cc78ecSespie }
540f7cc78ecSespie 
541f7cc78ecSespie /* Guess value of current locale from value of the environment variables.  */
542f7cc78ecSespie static const char *
543*d2201f2fSdrahn internal_function
guess_category_value(category,categoryname)544f7cc78ecSespie guess_category_value (category, categoryname)
545f7cc78ecSespie      int category;
546f7cc78ecSespie      const char *categoryname;
547f7cc78ecSespie {
548f7cc78ecSespie   const char *retval;
549f7cc78ecSespie 
550f7cc78ecSespie   /* The highest priority value is the `LANGUAGE' environment
551f7cc78ecSespie      variable.  This is a GNU extension.  */
552f7cc78ecSespie   retval = getenv ("LANGUAGE");
553f7cc78ecSespie   if (retval != NULL && retval[0] != '\0')
554f7cc78ecSespie     return retval;
555f7cc78ecSespie 
556f7cc78ecSespie   /* `LANGUAGE' is not set.  So we have to proceed with the POSIX
557f7cc78ecSespie      methods of looking to `LC_ALL', `LC_xxx', and `LANG'.  On some
558f7cc78ecSespie      systems this can be done by the `setlocale' function itself.  */
559f7cc78ecSespie #if defined HAVE_SETLOCALE && defined HAVE_LC_MESSAGES && defined HAVE_LOCALE_NULL
560f7cc78ecSespie   return setlocale (category, NULL);
561f7cc78ecSespie #else
562f7cc78ecSespie   /* Setting of LC_ALL overwrites all other.  */
563f7cc78ecSespie   retval = getenv ("LC_ALL");
564f7cc78ecSespie   if (retval != NULL && retval[0] != '\0')
565f7cc78ecSespie     return retval;
566f7cc78ecSespie 
567f7cc78ecSespie   /* Next comes the name of the desired category.  */
568f7cc78ecSespie   retval = getenv (categoryname);
569f7cc78ecSespie   if (retval != NULL && retval[0] != '\0')
570f7cc78ecSespie     return retval;
571f7cc78ecSespie 
572f7cc78ecSespie   /* Last possibility is the LANG environment variable.  */
573f7cc78ecSespie   retval = getenv ("LANG");
574f7cc78ecSespie   if (retval != NULL && retval[0] != '\0')
575f7cc78ecSespie     return retval;
576f7cc78ecSespie 
577f7cc78ecSespie   /* We use C as the default domain.  POSIX says this is implementation
578f7cc78ecSespie      defined.  */
579f7cc78ecSespie   return "C";
580f7cc78ecSespie #endif
581f7cc78ecSespie }
582f7cc78ecSespie 
583f7cc78ecSespie /* @@ begin of epilog @@ */
584f7cc78ecSespie 
585f7cc78ecSespie /* We don't want libintl.a to depend on any other library.  So we
586f7cc78ecSespie    avoid the non-standard function stpcpy.  In GNU C Library this
587f7cc78ecSespie    function is available, though.  Also allow the symbol HAVE_STPCPY
588f7cc78ecSespie    to be defined.  */
589f7cc78ecSespie #if !_LIBC && !HAVE_STPCPY
590f7cc78ecSespie static char *
stpcpy(dest,src)591f7cc78ecSespie stpcpy (dest, src)
592f7cc78ecSespie      char *dest;
593f7cc78ecSespie      const char *src;
594f7cc78ecSespie {
595f7cc78ecSespie   while ((*dest++ = *src++) != '\0')
596f7cc78ecSespie     /* Do nothing. */ ;
597f7cc78ecSespie   return dest - 1;
598f7cc78ecSespie }
599f7cc78ecSespie #endif
600*d2201f2fSdrahn 
601*d2201f2fSdrahn 
602*d2201f2fSdrahn #ifdef _LIBC
603*d2201f2fSdrahn /* If we want to free all resources we have to do some work at
604*d2201f2fSdrahn    program's end.  */
605*d2201f2fSdrahn static void __attribute__ ((unused))
free_mem(void)606*d2201f2fSdrahn free_mem (void)
607*d2201f2fSdrahn {
608*d2201f2fSdrahn   struct binding *runp;
609*d2201f2fSdrahn 
610*d2201f2fSdrahn   for (runp = _nl_domain_bindings; runp != NULL; runp = runp->next)
611*d2201f2fSdrahn     {
612*d2201f2fSdrahn       free (runp->domainname);
613*d2201f2fSdrahn       if (runp->dirname != _nl_default_dirname)
614*d2201f2fSdrahn 	/* Yes, this is a pointer comparison.  */
615*d2201f2fSdrahn 	free (runp->dirname);
616*d2201f2fSdrahn     }
617*d2201f2fSdrahn 
618*d2201f2fSdrahn   if (_nl_current_default_domain != _nl_default_default_domain)
619*d2201f2fSdrahn     /* Yes, again a pointer comparison.  */
620*d2201f2fSdrahn     free ((char *) _nl_current_default_domain);
621*d2201f2fSdrahn }
622*d2201f2fSdrahn 
623*d2201f2fSdrahn text_set_element (__libc_subfreeres, free_mem);
624*d2201f2fSdrahn #endif
625