Blame intl/localealias.c

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