Back to Evolution Project page
00001 /* Handle aliases for locale names. 00002 Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc. 00003 Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995. 00004 00005 This program is free software; you can redistribute it and/or modify 00006 it under the terms of the GNU General Public License as published by 00007 the Free Software Foundation; either version 2, or (at your option) 00008 any later version. 00009 00010 This program is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 GNU General Public License for more details. 00014 00015 You should have received a copy of the GNU General Public License 00016 along with this program; if not, write to the Free Software Foundation, 00017 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 00018 00019 #ifdef HAVE_CONFIG_H 00020 # include <config.h> 00021 #endif 00022 00023 #include <ctype.h> 00024 #include <stdio.h> 00025 #include <sys/types.h> 00026 00027 #ifdef __GNUC__ 00028 # define alloca __builtin_alloca 00029 # define HAVE_ALLOCA 1 00030 #else 00031 # if defined HAVE_ALLOCA_H || defined _LIBC 00032 # include <alloca.h> 00033 # else 00034 # ifdef _AIX 00035 #pragma alloca 00036 # else 00037 # ifndef alloca 00038 char *alloca (); 00039 # endif 00040 # endif 00041 # endif 00042 #endif 00043 00044 #if defined STDC_HEADERS || defined _LIBC 00045 # include <stdlib.h> 00046 #else 00047 char *getenv (); 00048 # ifdef HAVE_MALLOC_H 00049 # include <malloc.h> 00050 # else 00051 void free (); 00052 # endif 00053 #endif 00054 00055 #if defined HAVE_STRING_H || defined _LIBC 00056 # ifndef _GNU_SOURCE 00057 # define _GNU_SOURCE 1 00058 # endif 00059 # include <string.h> 00060 #else 00061 # include <strings.h> 00062 # ifndef memcpy 00063 # define memcpy(Dst, Src, Num) bcopy (Src, Dst, Num) 00064 # endif 00065 #endif 00066 #if !HAVE_STRCHR && !defined _LIBC 00067 # ifndef strchr 00068 # define strchr index 00069 # endif 00070 #endif 00071 00072 #include "gettext.h" 00073 #include "gettextP.h" 00074 00075 /* @@ end of prolog @@ */ 00076 00077 #ifdef _LIBC 00078 /* Rename the non ANSI C functions. This is required by the standard 00079 because some ANSI C functions will require linking with this object 00080 file and the name space must not be polluted. */ 00081 # define strcasecmp __strcasecmp 00082 00083 # ifndef mempcpy 00084 # define mempcpy __mempcpy 00085 # endif 00086 # define HAVE_MEMPCPY 1 00087 00088 /* We need locking here since we can be called from different places. */ 00089 # include <bits/libc-lock.h> 00090 00091 __libc_lock_define_initialized (static, lock); 00092 #endif 00093 00094 #ifndef internal_function 00095 # define internal_function 00096 #endif 00097 00098 /* For those loosing systems which don't have `alloca' we have to add 00099 some additional code emulating it. */ 00100 #ifdef HAVE_ALLOCA 00101 /* Nothing has to be done. */ 00102 # define ADD_BLOCK(list, address) /* nothing */ 00103 # define FREE_BLOCKS(list) /* nothing */ 00104 #else 00105 struct block_list 00106 { 00107 void *address; 00108 struct block_list *next; 00109 }; 00110 # define ADD_BLOCK(list, addr) \ 00111 do { \ 00112 struct block_list *newp = (struct block_list *) malloc (sizeof (*newp)); \ 00113 /* If we cannot get a free block we cannot add the new element to \ 00114 the list. */ \ 00115 if (newp != NULL) { \ 00116 newp->address = (addr); \ 00117 newp->next = (list); \ 00118 (list) = newp; \ 00119 } \ 00120 } while (0) 00121 # define FREE_BLOCKS(list) \ 00122 do { \ 00123 while (list != NULL) { \ 00124 struct block_list *old = list; \ 00125 list = list->next; \ 00126 free (old); \ 00127 } \ 00128 } while (0) 00129 # undef alloca 00130 # define alloca(size) (malloc (size)) 00131 #endif /* have alloca */ 00132 00133 #if defined _LIBC_REENTRANT || defined HAVE_FGETS_UNLOCKED 00134 # undef fgets 00135 # define fgets(buf, len, s) fgets_unlocked (buf, len, s) 00136 #endif 00137 #if defined _LIBC_REENTRANT || defined HAVE_FEOF_UNLOCKED 00138 # undef feof 00139 # define feof(s) feof_unlocked (s) 00140 #endif 00141 00142 00143 struct alias_map 00144 { 00145 const char *alias; 00146 const char *value; 00147 }; 00148 00149 00150 static char *string_space = NULL; 00151 static size_t string_space_act = 0; 00152 static size_t string_space_max = 0; 00153 static struct alias_map *map; 00154 static size_t nmap = 0; 00155 static size_t maxmap = 0; 00156 00157 00158 /* Prototypes for local functions. */ 00159 static size_t read_alias_file PARAMS ((const char *fname, int fname_len)) 00160 internal_function; 00161 static void extend_alias_table PARAMS ((void)); 00162 static int alias_compare PARAMS ((const struct alias_map *map1, 00163 const struct alias_map *map2)); 00164 00165 00166 const char * 00167 _nl_expand_alias (name) 00168 const char *name; 00169 { 00170 static const char *locale_alias_path = LOCALE_ALIAS_PATH; 00171 struct alias_map *retval; 00172 const char *result = NULL; 00173 size_t added; 00174 00175 #ifdef _LIBC 00176 __libc_lock_lock (lock); 00177 #endif 00178 00179 do 00180 { 00181 struct alias_map item; 00182 00183 item.alias = name; 00184 00185 if (nmap > 0) 00186 retval = (struct alias_map *) bsearch (&item, map, nmap, 00187 sizeof (struct alias_map), 00188 (int (*) PARAMS ((const void *, 00189 const void *)) 00190 ) alias_compare); 00191 else 00192 retval = NULL; 00193 00194 /* We really found an alias. Return the value. */ 00195 if (retval != NULL) 00196 { 00197 result = retval->value; 00198 break; 00199 } 00200 00201 /* Perhaps we can find another alias file. */ 00202 added = 0; 00203 while (added == 0 && locale_alias_path[0] != '\0') 00204 { 00205 const char *start; 00206 00207 while (locale_alias_path[0] == ':') 00208 ++locale_alias_path; 00209 start = locale_alias_path; 00210 00211 while (locale_alias_path[0] != '\0' && locale_alias_path[0] != ':') 00212 ++locale_alias_path; 00213 00214 if (start < locale_alias_path) 00215 added = read_alias_file (start, locale_alias_path - start); 00216 } 00217 } 00218 while (added != 0); 00219 00220 #ifdef _LIBC 00221 __libc_lock_unlock (lock); 00222 #endif 00223 00224 return result; 00225 } 00226 00227 00228 static size_t 00229 internal_function 00230 read_alias_file (fname, fname_len) 00231 const char *fname; 00232 int fname_len; 00233 { 00234 #ifndef HAVE_ALLOCA 00235 struct block_list *block_list = NULL; 00236 #endif 00237 FILE *fp; 00238 char *full_fname; 00239 size_t added; 00240 static const char aliasfile[] = "/locale.alias"; 00241 00242 full_fname = (char *) alloca (fname_len + sizeof aliasfile); 00243 ADD_BLOCK (block_list, full_fname); 00244 #ifdef HAVE_MEMPCPY 00245 mempcpy (mempcpy (full_fname, fname, fname_len), 00246 aliasfile, sizeof aliasfile); 00247 #else 00248 memcpy (full_fname, fname, fname_len); 00249 memcpy (&full_fname[fname_len], aliasfile, sizeof aliasfile); 00250 #endif 00251 00252 fp = fopen (full_fname, "r"); 00253 if (fp == NULL) 00254 { 00255 FREE_BLOCKS (block_list); 00256 return 0; 00257 } 00258 00259 added = 0; 00260 while (!feof (fp)) 00261 { 00262 /* It is a reasonable approach to use a fix buffer here because 00263 a) we are only interested in the first two fields 00264 b) these fields must be usable as file names and so must not 00265 be that long 00266 */ 00267 char buf[BUFSIZ]; 00268 char *alias; 00269 char *value; 00270 char *cp; 00271 00272 if (fgets (buf, sizeof buf, fp) == NULL) 00273 /* EOF reached. */ 00274 break; 00275 00276 /* Possibly not the whole line fits into the buffer. Ignore 00277 the rest of the line. */ 00278 if (strchr (buf, '\n') == NULL) 00279 { 00280 char altbuf[BUFSIZ]; 00281 do 00282 if (fgets (altbuf, sizeof altbuf, fp) == NULL) 00283 /* Make sure the inner loop will be left. The outer loop 00284 will exit at the `feof' test. */ 00285 break; 00286 while (strchr (altbuf, '\n') == NULL); 00287 } 00288 00289 cp = buf; 00290 /* Ignore leading white space. */ 00291 while (isspace (cp[0])) 00292 ++cp; 00293 00294 /* A leading '#' signals a comment line. */ 00295 if (cp[0] != '\0' && cp[0] != '#') 00296 { 00297 alias = cp++; 00298 while (cp[0] != '\0' && !isspace (cp[0])) 00299 ++cp; 00300 /* Terminate alias name. */ 00301 if (cp[0] != '\0') 00302 *cp++ = '\0'; 00303 00304 /* Now look for the beginning of the value. */ 00305 while (isspace (cp[0])) 00306 ++cp; 00307 00308 if (cp[0] != '\0') 00309 { 00310 size_t alias_len; 00311 size_t value_len; 00312 00313 value = cp++; 00314 while (cp[0] != '\0' && !isspace (cp[0])) 00315 ++cp; 00316 /* Terminate value. */ 00317 if (cp[0] == '\n') 00318 { 00319 /* This has to be done to make the following test 00320 for the end of line possible. We are looking for 00321 the terminating '\n' which do not overwrite here. */ 00322 *cp++ = '\0'; 00323 *cp = '\n'; 00324 } 00325 else if (cp[0] != '\0') 00326 *cp++ = '\0'; 00327 00328 if (nmap >= maxmap) 00329 extend_alias_table (); 00330 00331 alias_len = strlen (alias) + 1; 00332 value_len = strlen (value) + 1; 00333 00334 if (string_space_act + alias_len + value_len > string_space_max) 00335 { 00336 /* Increase size of memory pool. */ 00337 size_t new_size = (string_space_max 00338 + (alias_len + value_len > 1024 00339 ? alias_len + value_len : 1024)); 00340 char *new_pool = (char *) realloc (string_space, new_size); 00341 if (new_pool == NULL) 00342 { 00343 FREE_BLOCKS (block_list); 00344 return added; 00345 } 00346 string_space = new_pool; 00347 string_space_max = new_size; 00348 } 00349 00350 map[nmap].alias = memcpy (&string_space[string_space_act], 00351 alias, alias_len); 00352 string_space_act += alias_len; 00353 00354 map[nmap].value = memcpy (&string_space[string_space_act], 00355 value, value_len); 00356 string_space_act += value_len; 00357 00358 ++nmap; 00359 ++added; 00360 } 00361 } 00362 } 00363 00364 /* Should we test for ferror()? I think we have to silently ignore 00365 errors. --drepper */ 00366 fclose (fp); 00367 00368 if (added > 0) 00369 qsort (map, nmap, sizeof (struct alias_map), 00370 (int (*) PARAMS ((const void *, const void *))) alias_compare); 00371 00372 FREE_BLOCKS (block_list); 00373 return added; 00374 } 00375 00376 00377 static void 00378 extend_alias_table () 00379 { 00380 size_t new_size; 00381 struct alias_map *new_map; 00382 00383 new_size = maxmap == 0 ? 100 : 2 * maxmap; 00384 new_map = (struct alias_map *) realloc (map, (new_size 00385 * sizeof (struct alias_map))); 00386 if (new_map == NULL) 00387 /* Simply don't extend: we don't have any more core. */ 00388 return; 00389 00390 map = new_map; 00391 maxmap = new_size; 00392 } 00393 00394 00395 #ifdef _LIBC 00396 static void __attribute__ ((unused)) 00397 free_mem (void) 00398 { 00399 if (string_space != NULL) 00400 free (string_space); 00401 if (map != NULL) 00402 free (map); 00403 } 00404 text_set_element (__libc_subfreeres, free_mem); 00405 #endif 00406 00407 00408 static int 00409 alias_compare (map1, map2) 00410 const struct alias_map *map1; 00411 const struct alias_map *map2; 00412 { 00413 #if defined _LIBC || defined HAVE_STRCASECMP 00414 return strcasecmp (map1->alias, map2->alias); 00415 #else 00416 const unsigned char *p1 = (const unsigned char *) map1->alias; 00417 const unsigned char *p2 = (const unsigned char *) map2->alias; 00418 unsigned char c1, c2; 00419 00420 if (p1 == p2) 00421 return 0; 00422 00423 do 00424 { 00425 /* I know this seems to be odd but the tolower() function in 00426 some systems libc cannot handle nonalpha characters. */ 00427 c1 = isupper (*p1) ? tolower (*p1) : *p1; 00428 c2 = isupper (*p2) ? tolower (*p2) : *p2; 00429 if (c1 == '\0') 00430 break; 00431 ++p1; 00432 ++p2; 00433 } 00434 while (c1 == c2); 00435 00436 return c1 - c2; 00437 #endif 00438 }