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