xref: /netbsd-src/external/gpl3/gcc.old/dist/intl/localealias.c (revision 36ac495d2b3ea2b9d96377b2143ebfedac224b92)
1*36ac495dSmrg /* Handle aliases for locale names.
2*36ac495dSmrg    Copyright (C) 1995-1999, 2000-2001, 2003 Free Software Foundation, Inc.
3*36ac495dSmrg 
4*36ac495dSmrg    This program is free software; you can redistribute it and/or modify it
5*36ac495dSmrg    under the terms of the GNU Library General Public License as published
6*36ac495dSmrg    by the Free Software Foundation; either version 2, or (at your option)
7*36ac495dSmrg    any later version.
8*36ac495dSmrg 
9*36ac495dSmrg    This program is distributed in the hope that it will be useful,
10*36ac495dSmrg    but WITHOUT ANY WARRANTY; without even the implied warranty of
11*36ac495dSmrg    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12*36ac495dSmrg    Library General Public License for more details.
13*36ac495dSmrg 
14*36ac495dSmrg    You should have received a copy of the GNU Library General Public
15*36ac495dSmrg    License along with this program; if not, write to the Free Software
16*36ac495dSmrg    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301,
17*36ac495dSmrg    USA.  */
18*36ac495dSmrg 
19*36ac495dSmrg /* Tell glibc's <string.h> to provide a prototype for mempcpy().
20*36ac495dSmrg    This must come before <config.h> because <config.h> may include
21*36ac495dSmrg    <features.h>, and once <features.h> has been included, it's too late.  */
22*36ac495dSmrg #ifndef _GNU_SOURCE
23*36ac495dSmrg # define _GNU_SOURCE    1
24*36ac495dSmrg #endif
25*36ac495dSmrg 
26*36ac495dSmrg #ifdef HAVE_CONFIG_H
27*36ac495dSmrg # include <config.h>
28*36ac495dSmrg #endif
29*36ac495dSmrg 
30*36ac495dSmrg #include <ctype.h>
31*36ac495dSmrg #include <stdio.h>
32*36ac495dSmrg #if defined _LIBC || defined HAVE___FSETLOCKING
33*36ac495dSmrg # include <stdio_ext.h>
34*36ac495dSmrg #endif
35*36ac495dSmrg #include <sys/types.h>
36*36ac495dSmrg 
37*36ac495dSmrg #ifdef __GNUC__
38*36ac495dSmrg # undef alloca
39*36ac495dSmrg # define alloca __builtin_alloca
40*36ac495dSmrg # define HAVE_ALLOCA 1
41*36ac495dSmrg #else
42*36ac495dSmrg # ifdef _MSC_VER
43*36ac495dSmrg #  include <malloc.h>
44*36ac495dSmrg #  define alloca _alloca
45*36ac495dSmrg # else
46*36ac495dSmrg #  if defined HAVE_ALLOCA_H || defined _LIBC
47*36ac495dSmrg #   include <alloca.h>
48*36ac495dSmrg #  else
49*36ac495dSmrg #   ifdef _AIX
50*36ac495dSmrg  #pragma alloca
51*36ac495dSmrg #   else
52*36ac495dSmrg #    ifndef alloca
53*36ac495dSmrg char *alloca ();
54*36ac495dSmrg #    endif
55*36ac495dSmrg #   endif
56*36ac495dSmrg #  endif
57*36ac495dSmrg # endif
58*36ac495dSmrg #endif
59*36ac495dSmrg 
60*36ac495dSmrg #include <stdlib.h>
61*36ac495dSmrg #include <string.h>
62*36ac495dSmrg 
63*36ac495dSmrg #include "gettextP.h"
64*36ac495dSmrg 
65*36ac495dSmrg #if ENABLE_RELOCATABLE
66*36ac495dSmrg # include "relocatable.h"
67*36ac495dSmrg #else
68*36ac495dSmrg # define relocate(pathname) (pathname)
69*36ac495dSmrg #endif
70*36ac495dSmrg 
71*36ac495dSmrg /* @@ end of prolog @@ */
72*36ac495dSmrg 
73*36ac495dSmrg #ifdef _LIBC
74*36ac495dSmrg /* Rename the non ANSI C functions.  This is required by the standard
75*36ac495dSmrg    because some ANSI C functions will require linking with this object
76*36ac495dSmrg    file and the name space must not be polluted.  */
77*36ac495dSmrg # define strcasecmp __strcasecmp
78*36ac495dSmrg 
79*36ac495dSmrg # ifndef mempcpy
80*36ac495dSmrg #  define mempcpy __mempcpy
81*36ac495dSmrg # endif
82*36ac495dSmrg # define HAVE_MEMPCPY	1
83*36ac495dSmrg # define HAVE___FSETLOCKING	1
84*36ac495dSmrg 
85*36ac495dSmrg /* We need locking here since we can be called from different places.  */
86*36ac495dSmrg # include <bits/libc-lock.h>
87*36ac495dSmrg 
88*36ac495dSmrg __libc_lock_define_initialized (static, lock);
89*36ac495dSmrg #endif
90*36ac495dSmrg 
91*36ac495dSmrg #ifndef internal_function
92*36ac495dSmrg # define internal_function
93*36ac495dSmrg #endif
94*36ac495dSmrg 
95*36ac495dSmrg /* Some optimizations for glibc.  */
96*36ac495dSmrg #ifdef _LIBC
97*36ac495dSmrg # define FEOF(fp)		feof_unlocked (fp)
98*36ac495dSmrg # define FGETS(buf, n, fp)	fgets_unlocked (buf, n, fp)
99*36ac495dSmrg #else
100*36ac495dSmrg # define FEOF(fp)		feof (fp)
101*36ac495dSmrg # define FGETS(buf, n, fp)	fgets (buf, n, fp)
102*36ac495dSmrg #endif
103*36ac495dSmrg 
104*36ac495dSmrg /* For those losing systems which don't have `alloca' we have to add
105*36ac495dSmrg    some additional code emulating it.  */
106*36ac495dSmrg #ifdef HAVE_ALLOCA
107*36ac495dSmrg # define freea(p) /* nothing */
108*36ac495dSmrg #else
109*36ac495dSmrg # define alloca(n) malloc (n)
110*36ac495dSmrg # define freea(p) free (p)
111*36ac495dSmrg #endif
112*36ac495dSmrg 
113*36ac495dSmrg #if defined _LIBC_REENTRANT || defined HAVE_FGETS_UNLOCKED
114*36ac495dSmrg # undef fgets
115*36ac495dSmrg # define fgets(buf, len, s) fgets_unlocked (buf, len, s)
116*36ac495dSmrg #endif
117*36ac495dSmrg #if defined _LIBC_REENTRANT || defined HAVE_FEOF_UNLOCKED
118*36ac495dSmrg # undef feof
119*36ac495dSmrg # define feof(s) feof_unlocked (s)
120*36ac495dSmrg #endif
121*36ac495dSmrg 
122*36ac495dSmrg 
123*36ac495dSmrg struct alias_map
124*36ac495dSmrg {
125*36ac495dSmrg   const char *alias;
126*36ac495dSmrg   const char *value;
127*36ac495dSmrg };
128*36ac495dSmrg 
129*36ac495dSmrg 
130*36ac495dSmrg #ifndef _LIBC
131*36ac495dSmrg # define libc_freeres_ptr(decl) decl
132*36ac495dSmrg #endif
133*36ac495dSmrg 
134*36ac495dSmrg libc_freeres_ptr (static char *string_space);
135*36ac495dSmrg static size_t string_space_act;
136*36ac495dSmrg static size_t string_space_max;
137*36ac495dSmrg libc_freeres_ptr (static struct alias_map *map);
138*36ac495dSmrg static size_t nmap;
139*36ac495dSmrg static size_t maxmap;
140*36ac495dSmrg 
141*36ac495dSmrg 
142*36ac495dSmrg /* Prototypes for local functions.  */
143*36ac495dSmrg static size_t read_alias_file PARAMS ((const char *fname, int fname_len))
144*36ac495dSmrg      internal_function;
145*36ac495dSmrg static int extend_alias_table PARAMS ((void));
146*36ac495dSmrg static int alias_compare PARAMS ((const struct alias_map *map1,
147*36ac495dSmrg 				  const struct alias_map *map2));
148*36ac495dSmrg 
149*36ac495dSmrg 
150*36ac495dSmrg const char *
_nl_expand_alias(name)151*36ac495dSmrg _nl_expand_alias (name)
152*36ac495dSmrg     const char *name;
153*36ac495dSmrg {
154*36ac495dSmrg   static const char *locale_alias_path;
155*36ac495dSmrg   struct alias_map *retval;
156*36ac495dSmrg   const char *result = NULL;
157*36ac495dSmrg   size_t added;
158*36ac495dSmrg 
159*36ac495dSmrg #ifdef _LIBC
160*36ac495dSmrg   __libc_lock_lock (lock);
161*36ac495dSmrg #endif
162*36ac495dSmrg 
163*36ac495dSmrg   if (locale_alias_path == NULL)
164*36ac495dSmrg     locale_alias_path = LOCALE_ALIAS_PATH;
165*36ac495dSmrg 
166*36ac495dSmrg   do
167*36ac495dSmrg     {
168*36ac495dSmrg       struct alias_map item;
169*36ac495dSmrg 
170*36ac495dSmrg       item.alias = name;
171*36ac495dSmrg 
172*36ac495dSmrg       if (nmap > 0)
173*36ac495dSmrg 	retval = (struct alias_map *) bsearch (&item, map, nmap,
174*36ac495dSmrg 					       sizeof (struct alias_map),
175*36ac495dSmrg 					       (int (*) PARAMS ((const void *,
176*36ac495dSmrg 								 const void *))
177*36ac495dSmrg 						) alias_compare);
178*36ac495dSmrg       else
179*36ac495dSmrg 	retval = NULL;
180*36ac495dSmrg 
181*36ac495dSmrg       /* We really found an alias.  Return the value.  */
182*36ac495dSmrg       if (retval != NULL)
183*36ac495dSmrg 	{
184*36ac495dSmrg 	  result = retval->value;
185*36ac495dSmrg 	  break;
186*36ac495dSmrg 	}
187*36ac495dSmrg 
188*36ac495dSmrg       /* Perhaps we can find another alias file.  */
189*36ac495dSmrg       added = 0;
190*36ac495dSmrg       while (added == 0 && locale_alias_path[0] != '\0')
191*36ac495dSmrg 	{
192*36ac495dSmrg 	  const char *start;
193*36ac495dSmrg 
194*36ac495dSmrg 	  while (locale_alias_path[0] == PATH_SEPARATOR)
195*36ac495dSmrg 	    ++locale_alias_path;
196*36ac495dSmrg 	  start = locale_alias_path;
197*36ac495dSmrg 
198*36ac495dSmrg 	  while (locale_alias_path[0] != '\0'
199*36ac495dSmrg 		 && locale_alias_path[0] != PATH_SEPARATOR)
200*36ac495dSmrg 	    ++locale_alias_path;
201*36ac495dSmrg 
202*36ac495dSmrg 	  if (start < locale_alias_path)
203*36ac495dSmrg 	    added = read_alias_file (start, locale_alias_path - start);
204*36ac495dSmrg 	}
205*36ac495dSmrg     }
206*36ac495dSmrg   while (added != 0);
207*36ac495dSmrg 
208*36ac495dSmrg #ifdef _LIBC
209*36ac495dSmrg   __libc_lock_unlock (lock);
210*36ac495dSmrg #endif
211*36ac495dSmrg 
212*36ac495dSmrg   return result;
213*36ac495dSmrg }
214*36ac495dSmrg 
215*36ac495dSmrg 
216*36ac495dSmrg static size_t
217*36ac495dSmrg internal_function
read_alias_file(fname,fname_len)218*36ac495dSmrg read_alias_file (fname, fname_len)
219*36ac495dSmrg      const char *fname;
220*36ac495dSmrg      int fname_len;
221*36ac495dSmrg {
222*36ac495dSmrg   FILE *fp;
223*36ac495dSmrg   char *full_fname;
224*36ac495dSmrg   size_t added;
225*36ac495dSmrg   static const char aliasfile[] = "/locale.alias";
226*36ac495dSmrg 
227*36ac495dSmrg   full_fname = (char *) alloca (fname_len + sizeof aliasfile);
228*36ac495dSmrg #ifdef HAVE_MEMPCPY
229*36ac495dSmrg   mempcpy (mempcpy (full_fname, fname, fname_len),
230*36ac495dSmrg 	   aliasfile, sizeof aliasfile);
231*36ac495dSmrg #else
232*36ac495dSmrg   memcpy (full_fname, fname, fname_len);
233*36ac495dSmrg   memcpy (&full_fname[fname_len], aliasfile, sizeof aliasfile);
234*36ac495dSmrg #endif
235*36ac495dSmrg 
236*36ac495dSmrg   fp = fopen (relocate (full_fname), "r");
237*36ac495dSmrg   freea (full_fname);
238*36ac495dSmrg   if (fp == NULL)
239*36ac495dSmrg     return 0;
240*36ac495dSmrg 
241*36ac495dSmrg #ifdef HAVE___FSETLOCKING
242*36ac495dSmrg   /* No threads present.  */
243*36ac495dSmrg   __fsetlocking (fp, FSETLOCKING_BYCALLER);
244*36ac495dSmrg #endif
245*36ac495dSmrg 
246*36ac495dSmrg   added = 0;
247*36ac495dSmrg   while (!FEOF (fp))
248*36ac495dSmrg     {
249*36ac495dSmrg       /* It is a reasonable approach to use a fix buffer here because
250*36ac495dSmrg 	 a) we are only interested in the first two fields
251*36ac495dSmrg 	 b) these fields must be usable as file names and so must not
252*36ac495dSmrg 	    be that long
253*36ac495dSmrg 	 We avoid a multi-kilobyte buffer here since this would use up
254*36ac495dSmrg 	 stack space which we might not have if the program ran out of
255*36ac495dSmrg 	 memory.  */
256*36ac495dSmrg       char buf[400];
257*36ac495dSmrg       char *alias;
258*36ac495dSmrg       char *value;
259*36ac495dSmrg       char *cp;
260*36ac495dSmrg 
261*36ac495dSmrg       if (FGETS (buf, sizeof buf, fp) == NULL)
262*36ac495dSmrg 	/* EOF reached.  */
263*36ac495dSmrg 	break;
264*36ac495dSmrg 
265*36ac495dSmrg       cp = buf;
266*36ac495dSmrg       /* Ignore leading white space.  */
267*36ac495dSmrg       while (isspace ((unsigned char) cp[0]))
268*36ac495dSmrg 	++cp;
269*36ac495dSmrg 
270*36ac495dSmrg       /* A leading '#' signals a comment line.  */
271*36ac495dSmrg       if (cp[0] != '\0' && cp[0] != '#')
272*36ac495dSmrg 	{
273*36ac495dSmrg 	  alias = cp++;
274*36ac495dSmrg 	  while (cp[0] != '\0' && !isspace ((unsigned char) cp[0]))
275*36ac495dSmrg 	    ++cp;
276*36ac495dSmrg 	  /* Terminate alias name.  */
277*36ac495dSmrg 	  if (cp[0] != '\0')
278*36ac495dSmrg 	    *cp++ = '\0';
279*36ac495dSmrg 
280*36ac495dSmrg 	  /* Now look for the beginning of the value.  */
281*36ac495dSmrg 	  while (isspace ((unsigned char) cp[0]))
282*36ac495dSmrg 	    ++cp;
283*36ac495dSmrg 
284*36ac495dSmrg 	  if (cp[0] != '\0')
285*36ac495dSmrg 	    {
286*36ac495dSmrg 	      size_t alias_len;
287*36ac495dSmrg 	      size_t value_len;
288*36ac495dSmrg 
289*36ac495dSmrg 	      value = cp++;
290*36ac495dSmrg 	      while (cp[0] != '\0' && !isspace ((unsigned char) cp[0]))
291*36ac495dSmrg 		++cp;
292*36ac495dSmrg 	      /* Terminate value.  */
293*36ac495dSmrg 	      if (cp[0] == '\n')
294*36ac495dSmrg 		{
295*36ac495dSmrg 		  /* This has to be done to make the following test
296*36ac495dSmrg 		     for the end of line possible.  We are looking for
297*36ac495dSmrg 		     the terminating '\n' which do not overwrite here.  */
298*36ac495dSmrg 		  *cp++ = '\0';
299*36ac495dSmrg 		  *cp = '\n';
300*36ac495dSmrg 		}
301*36ac495dSmrg 	      else if (cp[0] != '\0')
302*36ac495dSmrg 		*cp++ = '\0';
303*36ac495dSmrg 
304*36ac495dSmrg 	      if (nmap >= maxmap)
305*36ac495dSmrg 		if (__builtin_expect (extend_alias_table (), 0))
306*36ac495dSmrg 		  return added;
307*36ac495dSmrg 
308*36ac495dSmrg 	      alias_len = strlen (alias) + 1;
309*36ac495dSmrg 	      value_len = strlen (value) + 1;
310*36ac495dSmrg 
311*36ac495dSmrg 	      if (string_space_act + alias_len + value_len > string_space_max)
312*36ac495dSmrg 		{
313*36ac495dSmrg 		  /* Increase size of memory pool.  */
314*36ac495dSmrg 		  size_t new_size = (string_space_max
315*36ac495dSmrg 				     + (alias_len + value_len > 1024
316*36ac495dSmrg 					? alias_len + value_len : 1024));
317*36ac495dSmrg 		  char *new_pool = (char *) realloc (string_space, new_size);
318*36ac495dSmrg 		  if (new_pool == NULL)
319*36ac495dSmrg 		    return added;
320*36ac495dSmrg 
321*36ac495dSmrg 		  if (__builtin_expect (string_space != new_pool, 0))
322*36ac495dSmrg 		    {
323*36ac495dSmrg 		      size_t i;
324*36ac495dSmrg 
325*36ac495dSmrg 		      for (i = 0; i < nmap; i++)
326*36ac495dSmrg 			{
327*36ac495dSmrg 			  map[i].alias += new_pool - string_space;
328*36ac495dSmrg 			  map[i].value += new_pool - string_space;
329*36ac495dSmrg 			}
330*36ac495dSmrg 		    }
331*36ac495dSmrg 
332*36ac495dSmrg 		  string_space = new_pool;
333*36ac495dSmrg 		  string_space_max = new_size;
334*36ac495dSmrg 		}
335*36ac495dSmrg 
336*36ac495dSmrg 	      map[nmap].alias = memcpy (&string_space[string_space_act],
337*36ac495dSmrg 					alias, alias_len);
338*36ac495dSmrg 	      string_space_act += alias_len;
339*36ac495dSmrg 
340*36ac495dSmrg 	      map[nmap].value = memcpy (&string_space[string_space_act],
341*36ac495dSmrg 					value, value_len);
342*36ac495dSmrg 	      string_space_act += value_len;
343*36ac495dSmrg 
344*36ac495dSmrg 	      ++nmap;
345*36ac495dSmrg 	      ++added;
346*36ac495dSmrg 	    }
347*36ac495dSmrg 	}
348*36ac495dSmrg 
349*36ac495dSmrg       /* Possibly not the whole line fits into the buffer.  Ignore
350*36ac495dSmrg 	 the rest of the line.  */
351*36ac495dSmrg       while (strchr (buf, '\n') == NULL)
352*36ac495dSmrg 	if (FGETS (buf, sizeof buf, fp) == NULL)
353*36ac495dSmrg 	  /* Make sure the inner loop will be left.  The outer loop
354*36ac495dSmrg 	     will exit at the `feof' test.  */
355*36ac495dSmrg 	  break;
356*36ac495dSmrg     }
357*36ac495dSmrg 
358*36ac495dSmrg   /* Should we test for ferror()?  I think we have to silently ignore
359*36ac495dSmrg      errors.  --drepper  */
360*36ac495dSmrg   fclose (fp);
361*36ac495dSmrg 
362*36ac495dSmrg   if (added > 0)
363*36ac495dSmrg     qsort (map, nmap, sizeof (struct alias_map),
364*36ac495dSmrg 	   (int (*) PARAMS ((const void *, const void *))) alias_compare);
365*36ac495dSmrg 
366*36ac495dSmrg   return added;
367*36ac495dSmrg }
368*36ac495dSmrg 
369*36ac495dSmrg 
370*36ac495dSmrg static int
extend_alias_table()371*36ac495dSmrg extend_alias_table ()
372*36ac495dSmrg {
373*36ac495dSmrg   size_t new_size;
374*36ac495dSmrg   struct alias_map *new_map;
375*36ac495dSmrg 
376*36ac495dSmrg   new_size = maxmap == 0 ? 100 : 2 * maxmap;
377*36ac495dSmrg   new_map = (struct alias_map *) realloc (map, (new_size
378*36ac495dSmrg 						* sizeof (struct alias_map)));
379*36ac495dSmrg   if (new_map == NULL)
380*36ac495dSmrg     /* Simply don't extend: we don't have any more core.  */
381*36ac495dSmrg     return -1;
382*36ac495dSmrg 
383*36ac495dSmrg   map = new_map;
384*36ac495dSmrg   maxmap = new_size;
385*36ac495dSmrg   return 0;
386*36ac495dSmrg }
387*36ac495dSmrg 
388*36ac495dSmrg 
389*36ac495dSmrg static int
alias_compare(map1,map2)390*36ac495dSmrg alias_compare (map1, map2)
391*36ac495dSmrg      const struct alias_map *map1;
392*36ac495dSmrg      const struct alias_map *map2;
393*36ac495dSmrg {
394*36ac495dSmrg #if defined _LIBC || defined HAVE_STRCASECMP
395*36ac495dSmrg   return strcasecmp (map1->alias, map2->alias);
396*36ac495dSmrg #else
397*36ac495dSmrg   const unsigned char *p1 = (const unsigned char *) map1->alias;
398*36ac495dSmrg   const unsigned char *p2 = (const unsigned char *) map2->alias;
399*36ac495dSmrg   unsigned char c1, c2;
400*36ac495dSmrg 
401*36ac495dSmrg   if (p1 == p2)
402*36ac495dSmrg     return 0;
403*36ac495dSmrg 
404*36ac495dSmrg   do
405*36ac495dSmrg     {
406*36ac495dSmrg       /* I know this seems to be odd but the tolower() function in
407*36ac495dSmrg 	 some systems libc cannot handle nonalpha characters.  */
408*36ac495dSmrg       c1 = isupper (*p1) ? tolower (*p1) : *p1;
409*36ac495dSmrg       c2 = isupper (*p2) ? tolower (*p2) : *p2;
410*36ac495dSmrg       if (c1 == '\0')
411*36ac495dSmrg 	break;
412*36ac495dSmrg       ++p1;
413*36ac495dSmrg       ++p2;
414*36ac495dSmrg     }
415*36ac495dSmrg   while (c1 == c2);
416*36ac495dSmrg 
417*36ac495dSmrg   return c1 - c2;
418*36ac495dSmrg #endif
419*36ac495dSmrg }
420