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