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