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