1 /* Handle aliases for locale names. 2 Copyright (C) 1995-1999, 2000-2001, 2003, 2005 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 53 char *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 128 struct 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 139 libc_freeres_ptr (static char *string_space); 140 static size_t string_space_act; 141 static size_t string_space_max; 142 libc_freeres_ptr (static struct alias_map *map); 143 static size_t nmap; 144 static size_t maxmap; 145 146 147 /* Prototypes for local functions. */ 148 static size_t read_alias_file (const char *fname, int fname_len) 149 internal_function; 150 static int extend_alias_table (void); 151 static int alias_compare (const struct alias_map *map1, 152 const struct alias_map *map2); 153 154 155 const 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 216 static size_t 217 internal_function 218 read_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 = memcpy (&string_space[string_space_act], 353 alias, alias_len); 354 string_space_act += alias_len; 355 356 map[nmap].value = memcpy (&string_space[string_space_act], 357 value, value_len); 358 string_space_act += value_len; 359 360 ++nmap; 361 ++added; 362 } 363 } 364 } 365 366 /* Possibly not the whole line fits into the buffer. Ignore 367 the rest of the line. */ 368 if (! complete_line) 369 do 370 if (FGETS (buf, sizeof buf, fp) == NULL) 371 /* Make sure the inner loop will be left. The outer loop 372 will exit at the `feof' test. */ 373 break; 374 while (strchr (buf, '\n') == NULL); 375 } 376 377 out: 378 /* Should we test for ferror()? I think we have to silently ignore 379 errors. --drepper */ 380 fclose (fp); 381 382 if (added > 0) 383 qsort (map, nmap, sizeof (struct alias_map), 384 (int (*) (const void *, const void *)) alias_compare); 385 386 return added; 387 } 388 389 390 static int 391 extend_alias_table () 392 { 393 size_t new_size; 394 struct alias_map *new_map; 395 396 new_size = maxmap == 0 ? 100 : 2 * maxmap; 397 new_map = (struct alias_map *) realloc (map, (new_size 398 * sizeof (struct alias_map))); 399 if (new_map == NULL) 400 /* Simply don't extend: we don't have any more core. */ 401 return -1; 402 403 map = new_map; 404 maxmap = new_size; 405 return 0; 406 } 407 408 409 static int 410 alias_compare (const struct alias_map *map1, const struct alias_map *map2) 411 { 412 #if defined _LIBC || defined HAVE_STRCASECMP 413 return strcasecmp (map1->alias, map2->alias); 414 #else 415 const unsigned char *p1 = (const unsigned char *) map1->alias; 416 const unsigned char *p2 = (const unsigned char *) map2->alias; 417 unsigned char c1, c2; 418 419 if (p1 == p2) 420 return 0; 421 422 do 423 { 424 /* I know this seems to be odd but the tolower() function in 425 some systems libc cannot handle nonalpha characters. */ 426 c1 = isupper (*p1) ? tolower (*p1) : *p1; 427 c2 = isupper (*p2) ? tolower (*p2) : *p2; 428 if (c1 == '\0') 429 break; 430 ++p1; 431 ++p2; 432 } 433 while (c1 == c2); 434 435 return c1 - c2; 436 #endif 437 } 438