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