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