Rev 32881 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
/* Handle aliases for locale names.Copyright (C) 1995-1999, 2000-2001, 2003 Free Software Foundation, Inc.This program is free software; you can redistribute it and/or modify itunder the terms of the GNU Library General Public License as publishedby the Free Software Foundation; either version 2, or (at your option)any later version.This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNULibrary General Public License for more details.You should have received a copy of the GNU Library General PublicLicense along with this program; if not, write to the Free SoftwareFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,USA. *//* Tell glibc's <string.h> to provide a prototype for mempcpy().This must come before <config.h> because <config.h> may include<features.h>, and once <features.h> has been included, it's too late. */#ifndef _GNU_SOURCE# define _GNU_SOURCE 1#endif#ifdef HAVE_CONFIG_H# include <config.h>#endif#include <ctype.h>#include <stdio.h>#if defined _LIBC || defined HAVE___FSETLOCKING# include <stdio_ext.h>#endif#include <sys/types.h>#ifdef __GNUC__# undef alloca# define alloca __builtin_alloca# define HAVE_ALLOCA 1#else# ifdef _MSC_VER# include <malloc.h># define alloca _alloca# else# if defined HAVE_ALLOCA_H || defined _LIBC# include <alloca.h># else# ifdef _AIX#pragma alloca# else# ifndef allocachar *alloca ();# endif# endif# endif# endif#endif#include <stdlib.h>#include <string.h>#include "gettextP.h"#if ENABLE_RELOCATABLE# include "relocatable.h"#else# define relocate(pathname) (pathname)#endif/* @@ end of prolog @@ */#ifdef _LIBC/* Rename the non ANSI C functions. This is required by the standardbecause some ANSI C functions will require linking with this objectfile and the name space must not be polluted. */# define strcasecmp __strcasecmp# ifndef mempcpy# define mempcpy __mempcpy# endif# define HAVE_MEMPCPY 1# define HAVE___FSETLOCKING 1/* We need locking here since we can be called from different places. */# include <bits/libc-lock.h>__libc_lock_define_initialized (static, lock);#endif#ifndef internal_function# define internal_function#endif/* Some optimizations for glibc. */#ifdef _LIBC# define FEOF(fp) feof_unlocked (fp)# define FGETS(buf, n, fp) fgets_unlocked (buf, n, fp)#else# define FEOF(fp) feof (fp)# define FGETS(buf, n, fp) fgets (buf, n, fp)#endif/* For those losing systems which don't have `alloca' we have to addsome additional code emulating it. */#ifdef HAVE_ALLOCA# define freea(p) /* nothing */#else# define alloca(n) malloc (n)# define freea(p) free (p)#endif#if defined _LIBC_REENTRANT || HAVE_DECL_FGETS_UNLOCKED# undef fgets# define fgets(buf, len, s) fgets_unlocked (buf, len, s)#endif#if defined _LIBC_REENTRANT || HAVE_DECL_FEOF_UNLOCKED# undef feof# define feof(s) feof_unlocked (s)#endifstruct alias_map{const char *alias;const char *value;};#ifndef _LIBC# define libc_freeres_ptr(decl) decl#endiflibc_freeres_ptr (static char *string_space);static size_t string_space_act;static size_t string_space_max;libc_freeres_ptr (static struct alias_map *map);static size_t nmap;static size_t maxmap;/* Prototypes for local functions. */static size_t read_alias_file (const char *fname, int fname_len)internal_function;static int extend_alias_table (void);static int alias_compare (const struct alias_map *map1,const struct alias_map *map2);const char *_nl_expand_alias (const char *name){static const char *locale_alias_path;struct alias_map *retval;const char *result = NULL;size_t added;#ifdef _LIBC__libc_lock_lock (lock);#endifif (locale_alias_path == NULL)locale_alias_path = LOCALE_ALIAS_PATH;do{struct alias_map item;item.alias = name;if (nmap > 0)retval = (struct alias_map *) bsearch (&item, map, nmap,sizeof (struct alias_map),(int (*) (const void *,const void *)) alias_compare);elseretval = NULL;/* We really found an alias. Return the value. */if (retval != NULL){result = retval->value;break;}/* Perhaps we can find another alias file. */added = 0;while (added == 0 && locale_alias_path[0] != '\0'){const char *start;while (locale_alias_path[0] == PATH_SEPARATOR)++locale_alias_path;start = locale_alias_path;while (locale_alias_path[0] != '\0'&& locale_alias_path[0] != PATH_SEPARATOR)++locale_alias_path;if (start < locale_alias_path)added = read_alias_file (start, locale_alias_path - start);}}while (added != 0);#ifdef _LIBC__libc_lock_unlock (lock);#endifreturn result;}static size_tinternal_functionread_alias_file (const char *fname, int fname_len){FILE *fp;char *full_fname;size_t added;static const char aliasfile[] = "/locale.alias";full_fname = (char *) alloca (fname_len + sizeof aliasfile);#ifdef HAVE_MEMPCPYmempcpy (mempcpy (full_fname, fname, fname_len),aliasfile, sizeof aliasfile);#elsememcpy (full_fname, fname, fname_len);memcpy (&full_fname[fname_len], aliasfile, sizeof aliasfile);#endiffp = fopen (relocate (full_fname), "r");freea (full_fname);if (fp == NULL)return 0;#ifdef HAVE___FSETLOCKING/* No threads present. */__fsetlocking (fp, FSETLOCKING_BYCALLER);#endifadded = 0;while (!FEOF (fp)){/* It is a reasonable approach to use a fix buffer here becausea) we are only interested in the first two fieldsb) these fields must be usable as file names and so must notbe that longWe avoid a multi-kilobyte buffer here since this would use upstack space which we might not have if the program ran out ofmemory. */char buf[400];char *alias;char *value;char *cp;if (FGETS (buf, sizeof buf, fp) == NULL)/* EOF reached. */break;cp = buf;/* Ignore leading white space. */while (isspace ((unsigned char) cp[0]))++cp;/* A leading '#' signals a comment line. */if (cp[0] != '\0' && cp[0] != '#'){alias = cp++;while (cp[0] != '\0' && !isspace ((unsigned char) cp[0]))++cp;/* Terminate alias name. */if (cp[0] != '\0')*cp++ = '\0';/* Now look for the beginning of the value. */while (isspace ((unsigned char) cp[0]))++cp;if (cp[0] != '\0'){size_t alias_len;size_t value_len;value = cp++;while (cp[0] != '\0' && !isspace ((unsigned char) cp[0]))++cp;/* Terminate value. */if (cp[0] == '\n'){/* This has to be done to make the following testfor the end of line possible. We are looking forthe terminating '\n' which do not overwrite here. */*cp++ = '\0';*cp = '\n';}else if (cp[0] != '\0')*cp++ = '\0';if (nmap >= maxmap)if (__builtin_expect (extend_alias_table (), 0))return added;alias_len = strlen (alias) + 1;value_len = strlen (value) + 1;if (string_space_act + alias_len + value_len > string_space_max){/* Increase size of memory pool. */size_t new_size = (string_space_max+ (alias_len + value_len > 1024? alias_len + value_len : 1024));char *new_pool = (char *) realloc (string_space, new_size);if (new_pool == NULL)return added;if (__builtin_expect (string_space != new_pool, 0)){size_t i;for (i = 0; i < nmap; i++){map[i].alias += new_pool - string_space;map[i].value += new_pool - string_space;}}string_space = new_pool;string_space_max = new_size;}map[nmap].alias = memcpy (&string_space[string_space_act],alias, alias_len);string_space_act += alias_len;map[nmap].value = memcpy (&string_space[string_space_act],value, value_len);string_space_act += value_len;++nmap;++added;}}/* Possibly not the whole line fits into the buffer. Ignorethe rest of the line. */while (strchr (buf, '\n') == NULL)if (FGETS (buf, sizeof buf, fp) == NULL)/* Make sure the inner loop will be left. The outer loopwill exit at the `feof' test. */break;}/* Should we test for ferror()? I think we have to silently ignoreerrors. --drepper */fclose (fp);if (added > 0)qsort (map, nmap, sizeof (struct alias_map),(int (*) (const void *, const void *)) alias_compare);return added;}static intextend_alias_table (){size_t new_size;struct alias_map *new_map;new_size = maxmap == 0 ? 100 : 2 * maxmap;new_map = (struct alias_map *) realloc (map, (new_size* sizeof (struct alias_map)));if (new_map == NULL)/* Simply don't extend: we don't have any more core. */return -1;map = new_map;maxmap = new_size;return 0;}static intalias_compare (const struct alias_map *map1, const struct alias_map *map2){#if defined _LIBC || defined HAVE_STRCASECMPreturn strcasecmp (map1->alias, map2->alias);#elseconst unsigned char *p1 = (const unsigned char *) map1->alias;const unsigned char *p2 = (const unsigned char *) map2->alias;unsigned char c1, c2;if (p1 == p2)return 0;do{/* I know this seems to be odd but the tolower() function insome systems libc cannot handle nonalpha characters. */c1 = isupper (*p1) ? tolower (*p1) : *p1;c2 = isupper (*p2) ? tolower (*p2) : *p2;if (c1 == '\0')break;++p1;++p2;}while (c1 == c2);return c1 - c2;#endif}