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