Blame intl/localealias.c

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