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