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