1/* localealias.c - Handle aliases for locale names. */ 2 3/* Copyright (C) 1995-1999, 2000-2001, 2003, 2005-2009 Free Software Foundation, Inc. 4 5 This file is part of GNU Bash. 6 7 Bash is free software: you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation, either version 3 of the License, or 10 (at your option) any later version. 11 12 Bash is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with Bash. If not, see <http://www.gnu.org/licenses/>. 19*/ 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 55char *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 || defined HAVE_FGETS_UNLOCKED 116# undef fgets 117# define fgets(buf, len, s) fgets_unlocked (buf, len, s) 118#endif 119#if defined _LIBC_REENTRANT || defined HAVE_FEOF_UNLOCKED 120# undef feof 121# define feof(s) feof_unlocked (s) 122#endif 123 124 125struct 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 136libc_freeres_ptr (static char *string_space); 137static size_t string_space_act; 138static size_t string_space_max; 139libc_freeres_ptr (static struct alias_map *map); 140static size_t nmap; 141static size_t maxmap; 142 143 144/* Prototypes for local functions. */ 145static size_t read_alias_file PARAMS ((const char *fname, int fname_len)) 146 internal_function; 147static int extend_alias_table PARAMS ((void)); 148static int alias_compare PARAMS ((const struct alias_map *map1, 149 const struct alias_map *map2)); 150 151 152const char * 153_nl_expand_alias (name) 154 const char *name; 155{ 156 static const char *locale_alias_path; 157 struct alias_map *retval; 158 const char *result = NULL; 159 size_t added; 160 161#ifdef _LIBC 162 __libc_lock_lock (lock); 163#endif 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 (*) PARAMS ((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#ifdef _LIBC 211 __libc_lock_unlock (lock); 212#endif 213 214 return result; 215} 216 217 218static size_t 219internal_function 220read_alias_file (fname, fname_len) 221 const char *fname; 222 int fname_len; 223{ 224 FILE *fp; 225 char *full_fname; 226 size_t added; 227 static const char aliasfile[] = "/locale.alias"; 228 229 full_fname = (char *) alloca (fname_len + sizeof aliasfile); 230#ifdef HAVE_MEMPCPY 231 mempcpy (mempcpy (full_fname, fname, fname_len), 232 aliasfile, sizeof aliasfile); 233#else 234 memcpy (full_fname, fname, fname_len); 235 memcpy (&full_fname[fname_len], aliasfile, sizeof aliasfile); 236#endif 237 238 fp = fopen (relocate (full_fname), "r"); 239 freea (full_fname); 240 if (fp == NULL) 241 return 0; 242 243#ifdef HAVE___FSETLOCKING 244 /* No threads present. */ 245 __fsetlocking (fp, FSETLOCKING_BYCALLER); 246#endif 247 248 added = 0; 249 while (!FEOF (fp)) 250 { 251 /* It is a reasonable approach to use a fix buffer here because 252 a) we are only interested in the first two fields 253 b) these fields must be usable as file names and so must not 254 be that long 255 We avoid a multi-kilobyte buffer here since this would use up 256 stack space which we might not have if the program ran out of 257 memory. */ 258 char buf[400]; 259 char *alias; 260 char *value; 261 char *cp; 262 263 if (FGETS (buf, sizeof buf, fp) == NULL) 264 /* EOF reached. */ 265 break; 266 267 cp = buf; 268 /* Ignore leading white space. */ 269 while (isspace ((unsigned char) cp[0])) 270 ++cp; 271 272 /* A leading '#' signals a comment line. */ 273 if (cp[0] != '\0' && cp[0] != '#') 274 { 275 alias = cp++; 276 while (cp[0] != '\0' && !isspace ((unsigned char) cp[0])) 277 ++cp; 278 /* Terminate alias name. */ 279 if (cp[0] != '\0') 280 *cp++ = '\0'; 281 282 /* Now look for the beginning of the value. */ 283 while (isspace ((unsigned char) cp[0])) 284 ++cp; 285 286 if (cp[0] != '\0') 287 { 288 size_t alias_len; 289 size_t value_len; 290 291 value = cp++; 292 while (cp[0] != '\0' && !isspace ((unsigned char) cp[0])) 293 ++cp; 294 /* Terminate value. */ 295 if (cp[0] == '\n') 296 { 297 /* This has to be done to make the following test 298 for the end of line possible. We are looking for 299 the terminating '\n' which do not overwrite here. */ 300 *cp++ = '\0'; 301 *cp = '\n'; 302 } 303 else if (cp[0] != '\0') 304 *cp++ = '\0'; 305 306 if (nmap >= maxmap) 307 if (__builtin_expect (extend_alias_table (), 0)) 308 return added; 309 310 alias_len = strlen (alias) + 1; 311 value_len = strlen (value) + 1; 312 313 if (string_space_act + alias_len + value_len > string_space_max) 314 { 315 /* Increase size of memory pool. */ 316 size_t new_size = (string_space_max 317 + (alias_len + value_len > 1024 318 ? alias_len + value_len : 1024)); 319 char *new_pool = (char *) realloc (string_space, new_size); 320 if (new_pool == NULL) 321 return added; 322 323 if (__builtin_expect (string_space != new_pool, 0)) 324 { 325 size_t i; 326 327 for (i = 0; i < nmap; i++) 328 { 329 map[i].alias += new_pool - string_space; 330 map[i].value += new_pool - string_space; 331 } 332 } 333 334 string_space = new_pool; 335 string_space_max = new_size; 336 } 337 338 map[nmap].alias = memcpy (&string_space[string_space_act], 339 alias, alias_len); 340 string_space_act += alias_len; 341 342 map[nmap].value = memcpy (&string_space[string_space_act], 343 value, value_len); 344 string_space_act += value_len; 345 346 ++nmap; 347 ++added; 348 } 349 } 350 351 /* Possibly not the whole line fits into the buffer. Ignore 352 the rest of the line. */ 353 while (strchr (buf, '\n') == NULL) 354 if (FGETS (buf, sizeof buf, fp) == NULL) 355 /* Make sure the inner loop will be left. The outer loop 356 will exit at the `feof' test. */ 357 break; 358 } 359 360 /* Should we test for ferror()? I think we have to silently ignore 361 errors. --drepper */ 362 fclose (fp); 363 364 if (added > 0) 365 qsort (map, nmap, sizeof (struct alias_map), 366 (int (*) PARAMS ((const void *, const void *))) alias_compare); 367 368 return added; 369} 370 371 372static int 373extend_alias_table () 374{ 375 size_t new_size; 376 struct alias_map *new_map; 377 378 new_size = maxmap == 0 ? 100 : 2 * maxmap; 379 new_map = (struct alias_map *) realloc (map, (new_size 380 * sizeof (struct alias_map))); 381 if (new_map == NULL) 382 /* Simply don't extend: we don't have any more core. */ 383 return -1; 384 385 map = new_map; 386 maxmap = new_size; 387 return 0; 388} 389 390 391static int 392alias_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