Blame intl/l10nflist.c

Packit 6c4009
/* Copyright (C) 1995-2018 Free Software Foundation, Inc.
Packit 6c4009
   Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
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 stpcpy().
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 <string.h>
Packit 6c4009
Packit 6c4009
#if defined _LIBC || defined HAVE_ARGZ_H
Packit 6c4009
# include <argz.h>
Packit 6c4009
#endif
Packit 6c4009
#include <ctype.h>
Packit 6c4009
#include <sys/types.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
Packit 6c4009
#include "loadinfo.h"
Packit 6c4009
Packit 6c4009
/* On some strange systems still no definition of NULL is found.  Sigh!  */
Packit 6c4009
#ifndef NULL
Packit 6c4009
# if defined __STDC__ && __STDC__
Packit 6c4009
#  define NULL ((void *) 0)
Packit 6c4009
# else
Packit 6c4009
#  define NULL 0
Packit 6c4009
# endif
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
# ifndef stpcpy
Packit 6c4009
#  define stpcpy(dest, src) __stpcpy(dest, src)
Packit 6c4009
# endif
Packit 6c4009
#else
Packit 6c4009
# ifndef HAVE_STPCPY
Packit 6c4009
static char *stpcpy (char *dest, const char *src);
Packit 6c4009
# endif
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* Define function which are usually not available.  */
Packit 6c4009
Packit 6c4009
#if defined HAVE_ARGZ_COUNT
Packit 6c4009
# undef __argz_count
Packit 6c4009
# define __argz_count argz_count
Packit 6c4009
#else
Packit 6c4009
/* Returns the number of strings in ARGZ.  */
Packit 6c4009
static size_t
Packit 6c4009
argz_count__ (const char *argz, size_t len)
Packit 6c4009
{
Packit 6c4009
  size_t count = 0;
Packit 6c4009
  while (len > 0)
Packit 6c4009
    {
Packit 6c4009
      size_t part_len = strlen (argz);
Packit 6c4009
      argz += part_len + 1;
Packit 6c4009
      len -= part_len + 1;
Packit 6c4009
      count++;
Packit 6c4009
    }
Packit 6c4009
  return count;
Packit 6c4009
}
Packit 6c4009
# undef __argz_count
Packit 6c4009
# define __argz_count(argz, len) argz_count__ (argz, len)
Packit 6c4009
#endif	/* !_LIBC && !HAVE_ARGZ_COUNT */
Packit 6c4009
Packit 6c4009
#if defined HAVE_ARGZ_STRINGIFY
Packit 6c4009
# undef __argz_stringify
Packit 6c4009
# define __argz_stringify argz_stringify
Packit 6c4009
#else
Packit 6c4009
/* Make '\0' separated arg vector ARGZ printable by converting all the '\0's
Packit 6c4009
   except the last into the character SEP.  */
Packit 6c4009
static void
Packit 6c4009
argz_stringify__ (char *argz, size_t len, int sep)
Packit 6c4009
{
Packit 6c4009
  while (len > 0)
Packit 6c4009
    {
Packit 6c4009
      size_t part_len = strlen (argz);
Packit 6c4009
      argz += part_len;
Packit 6c4009
      len -= part_len + 1;
Packit 6c4009
      if (len > 0)
Packit 6c4009
	*argz++ = sep;
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
# undef __argz_stringify
Packit 6c4009
# define __argz_stringify(argz, len, sep) argz_stringify__ (argz, len, sep)
Packit 6c4009
#endif	/* !_LIBC && !HAVE_ARGZ_STRINGIFY */
Packit 6c4009
Packit 6c4009
#ifdef _LIBC
Packit 6c4009
#elif defined HAVE_ARGZ_NEXT
Packit 6c4009
# undef __argz_next
Packit 6c4009
# define __argz_next argz_next
Packit 6c4009
#else
Packit 6c4009
static char *
Packit 6c4009
argz_next__ (char *argz, size_t argz_len, const char *entry)
Packit 6c4009
{
Packit 6c4009
  if (entry)
Packit 6c4009
    {
Packit 6c4009
      if (entry < argz + argz_len)
Packit 6c4009
        entry = strchr (entry, '\0') + 1;
Packit 6c4009
Packit 6c4009
      return entry >= argz + argz_len ? NULL : (char *) entry;
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    if (argz_len > 0)
Packit 6c4009
      return argz;
Packit 6c4009
    else
Packit 6c4009
      return 0;
Packit 6c4009
}
Packit 6c4009
# undef __argz_next
Packit 6c4009
# define __argz_next(argz, len, entry) argz_next__ (argz, len, entry)
Packit 6c4009
#endif	/* !_LIBC && !HAVE_ARGZ_NEXT */
Packit 6c4009
Packit 6c4009
/* Return number of bits set in X.  */
Packit 6c4009
#ifndef ARCH_POP
Packit 6c4009
static inline int
Packit 6c4009
pop (int x)
Packit 6c4009
{
Packit 6c4009
  /* We assume that no more than 16 bits are used.  */
Packit 6c4009
  x = ((x & ~0x5555) >> 1) + (x & 0x5555);
Packit 6c4009
  x = ((x & ~0x3333) >> 2) + (x & 0x3333);
Packit 6c4009
  x = ((x >> 4) + x) & 0x0f0f;
Packit 6c4009
  x = ((x >> 8) + x) & 0xff;
Packit 6c4009
Packit 6c4009
  return x;
Packit 6c4009
}
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009

Packit 6c4009
struct loaded_l10nfile *
Packit 6c4009
_nl_make_l10nflist (struct loaded_l10nfile **l10nfile_list,
Packit 6c4009
		    const char *dirlist, size_t dirlist_len,
Packit 6c4009
		    int mask, const char *language, const char *territory,
Packit 6c4009
		    const char *codeset, const char *normalized_codeset,
Packit 6c4009
		    const char *modifier,
Packit 6c4009
		    const char *filename, int do_allocate)
Packit 6c4009
{
Packit 6c4009
  char *abs_filename;
Packit 6c4009
  struct loaded_l10nfile *last = NULL;
Packit 6c4009
  struct loaded_l10nfile *retval;
Packit 6c4009
  char *cp;
Packit 6c4009
  size_t entries;
Packit 6c4009
  int cnt;
Packit 6c4009
Packit 6c4009
  /* Allocate room for the full file name.  */
Packit 6c4009
  abs_filename = (char *) malloc (dirlist_len
Packit 6c4009
				  + strlen (language)
Packit 6c4009
				  + ((mask & XPG_TERRITORY) != 0
Packit 6c4009
				     ? strlen (territory) + 1 : 0)
Packit 6c4009
				  + ((mask & XPG_CODESET) != 0
Packit 6c4009
				     ? strlen (codeset) + 1 : 0)
Packit 6c4009
				  + ((mask & XPG_NORM_CODESET) != 0
Packit 6c4009
				     ? strlen (normalized_codeset) + 1 : 0)
Packit 6c4009
				  + ((mask & XPG_MODIFIER) != 0
Packit 6c4009
				     ? strlen (modifier) + 1 : 0)
Packit 6c4009
				  + 1 + strlen (filename) + 1);
Packit 6c4009
Packit 6c4009
  if (abs_filename == NULL)
Packit 6c4009
    return NULL;
Packit 6c4009
Packit 6c4009
  retval = NULL;
Packit 6c4009
  last = NULL;
Packit 6c4009
Packit 6c4009
  /* Construct file name.  */
Packit 6c4009
  memcpy (abs_filename, dirlist, dirlist_len);
Packit 6c4009
  __argz_stringify (abs_filename, dirlist_len, ':');
Packit 6c4009
  cp = abs_filename + (dirlist_len - 1);
Packit 6c4009
  *cp++ = '/';
Packit 6c4009
  cp = stpcpy (cp, language);
Packit 6c4009
Packit 6c4009
  if ((mask & XPG_TERRITORY) != 0)
Packit 6c4009
    {
Packit 6c4009
      *cp++ = '_';
Packit 6c4009
      cp = stpcpy (cp, territory);
Packit 6c4009
    }
Packit 6c4009
  if ((mask & XPG_CODESET) != 0)
Packit 6c4009
    {
Packit 6c4009
      *cp++ = '.';
Packit 6c4009
      cp = stpcpy (cp, codeset);
Packit 6c4009
    }
Packit 6c4009
  if ((mask & XPG_NORM_CODESET) != 0)
Packit 6c4009
    {
Packit 6c4009
      *cp++ = '.';
Packit 6c4009
      cp = stpcpy (cp, normalized_codeset);
Packit 6c4009
    }
Packit 6c4009
  if ((mask & XPG_MODIFIER) != 0)
Packit 6c4009
    {
Packit 6c4009
      *cp++ = '@';
Packit 6c4009
      cp = stpcpy (cp, modifier);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  *cp++ = '/';
Packit 6c4009
  stpcpy (cp, filename);
Packit 6c4009
Packit 6c4009
  /* Look in list of already loaded domains whether it is already
Packit 6c4009
     available.  */
Packit 6c4009
  last = NULL;
Packit 6c4009
  for (retval = *l10nfile_list; retval != NULL; retval = retval->next)
Packit 6c4009
    if (retval->filename != NULL)
Packit 6c4009
      {
Packit 6c4009
	int compare = strcmp (retval->filename, abs_filename);
Packit 6c4009
	if (compare == 0)
Packit 6c4009
	  /* We found it!  */
Packit 6c4009
	  break;
Packit 6c4009
	if (compare < 0)
Packit 6c4009
	  {
Packit 6c4009
	    /* It's not in the list.  */
Packit 6c4009
	    retval = NULL;
Packit 6c4009
	    break;
Packit 6c4009
	  }
Packit 6c4009
Packit 6c4009
	last = retval;
Packit 6c4009
      }
Packit 6c4009
Packit 6c4009
  if (retval != NULL || do_allocate == 0)
Packit 6c4009
    {
Packit 6c4009
      free (abs_filename);
Packit 6c4009
      return retval;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  retval = (struct loaded_l10nfile *)
Packit 6c4009
    malloc (sizeof (*retval) + (__argz_count (dirlist, dirlist_len)
Packit 6c4009
				* (1 << pop (mask))
Packit 6c4009
				* sizeof (struct loaded_l10nfile *)));
Packit 6c4009
  if (retval == NULL)
Packit 6c4009
    {
Packit 6c4009
      free (abs_filename);
Packit 6c4009
      return NULL;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  retval->filename = abs_filename;
Packit 6c4009
  /* If more than one directory is in the list this is a pseudo-entry
Packit 6c4009
     which just references others.  We do not try to load data for it,
Packit 6c4009
     ever.  */
Packit 6c4009
  retval->decided = (__argz_count (dirlist, dirlist_len) != 1
Packit 6c4009
		     || ((mask & XPG_CODESET) != 0
Packit 6c4009
			 && (mask & XPG_NORM_CODESET) != 0));
Packit 6c4009
  retval->data = NULL;
Packit 6c4009
Packit 6c4009
  if (last == NULL)
Packit 6c4009
    {
Packit 6c4009
      retval->next = *l10nfile_list;
Packit 6c4009
      *l10nfile_list = retval;
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      retval->next = last->next;
Packit 6c4009
      last->next = retval;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  entries = 0;
Packit 6c4009
  /* If the DIRLIST is a real list the RETVAL entry corresponds not to
Packit 6c4009
     a real file.  So we have to use the DIRLIST separation mechanism
Packit 6c4009
     of the inner loop.  */
Packit 6c4009
  cnt = __argz_count (dirlist, dirlist_len) == 1 ? mask - 1 : mask;
Packit 6c4009
  for (; cnt >= 0; --cnt)
Packit 6c4009
    if ((cnt & ~mask) == 0)
Packit 6c4009
      {
Packit 6c4009
	/* Iterate over all elements of the DIRLIST.  */
Packit 6c4009
	char *dir = NULL;
Packit 6c4009
Packit 6c4009
	while ((dir = __argz_next ((char *) dirlist, dirlist_len, dir))
Packit 6c4009
	       != NULL)
Packit 6c4009
	  retval->successor[entries++]
Packit 6c4009
	    = _nl_make_l10nflist (l10nfile_list, dir, strlen (dir) + 1, cnt,
Packit 6c4009
				  language, territory, codeset,
Packit 6c4009
				  normalized_codeset, modifier, filename, 1);
Packit 6c4009
      }
Packit 6c4009
  retval->successor[entries] = NULL;
Packit 6c4009
Packit 6c4009
  return retval;
Packit 6c4009
}
Packit 6c4009

Packit 6c4009
/* Normalize codeset name.  There is no standard for the codeset
Packit 6c4009
   names.  Normalization allows the user to use any of the common
Packit 6c4009
   names.  The return value is dynamically allocated and has to be
Packit 6c4009
   freed by the caller.  */
Packit 6c4009
const char *
Packit 6c4009
_nl_normalize_codeset (const char *codeset, size_t name_len)
Packit 6c4009
{
Packit 6c4009
  size_t len = 0;
Packit 6c4009
  int only_digit = 1;
Packit 6c4009
  char *retval;
Packit 6c4009
  char *wp;
Packit 6c4009
  size_t cnt;
Packit 6c4009
#if !IS_IN (libc)
Packit 6c4009
  locale_t locale = newlocale (0, "C", NULL);
Packit 6c4009
#else
Packit 6c4009
# define locale _nl_C_locobj_ptr
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  for (cnt = 0; cnt < name_len; ++cnt)
Packit 6c4009
    if (__isalnum_l ((unsigned char) codeset[cnt], locale))
Packit 6c4009
      {
Packit 6c4009
	++len;
Packit 6c4009
Packit 6c4009
	if (! __isdigit_l ((unsigned char) codeset[cnt], locale))
Packit 6c4009
	  only_digit = 0;
Packit 6c4009
      }
Packit 6c4009
Packit 6c4009
  retval = (char *) malloc ((only_digit ? 3 : 0) + len + 1);
Packit 6c4009
Packit 6c4009
  if (retval != NULL)
Packit 6c4009
    {
Packit 6c4009
      if (only_digit)
Packit 6c4009
	wp = stpcpy (retval, "iso");
Packit 6c4009
      else
Packit 6c4009
	wp = retval;
Packit 6c4009
Packit 6c4009
      for (cnt = 0; cnt < name_len; ++cnt)
Packit 6c4009
	if (__isalpha_l ((unsigned char) codeset[cnt], locale))
Packit 6c4009
	  *wp++ = __tolower_l ((unsigned char) codeset[cnt], locale);
Packit 6c4009
	else if (__isdigit_l ((unsigned char) codeset[cnt], locale))
Packit 6c4009
	  *wp++ = codeset[cnt];
Packit 6c4009
Packit 6c4009
      *wp = '\0';
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return (const char *) retval;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* @@ begin of epilog @@ */
Packit 6c4009
Packit 6c4009
/* We don't want libintl.a to depend on any other library.  So we
Packit 6c4009
   avoid the non-standard function stpcpy.  In GNU C Library this
Packit 6c4009
   function is available, though.  Also allow the symbol HAVE_STPCPY
Packit 6c4009
   to be defined.  */
Packit 6c4009
#if !_LIBC && !HAVE_STPCPY
Packit 6c4009
static char *
Packit 6c4009
stpcpy (char *dest, const char *src)
Packit 6c4009
{
Packit 6c4009
  while ((*dest++ = *src++) != '\0')
Packit 6c4009
    /* Do nothing. */ ;
Packit 6c4009
  return dest - 1;
Packit 6c4009
}
Packit 6c4009
#endif