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