Blame intl/l10nflist.c

Packit bbfece
/* Copyright (C) 1995-1999, 2000, 2001, 2002 Free Software Foundation, Inc.
Packit bbfece
   Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
Packit bbfece
Packit bbfece
   This program is free software; you can redistribute it and/or modify it
Packit bbfece
   under the terms of the GNU Library General Public License as published
Packit bbfece
   by the Free Software Foundation; either version 2, or (at your option)
Packit bbfece
   any later version.
Packit bbfece
Packit bbfece
   This program is distributed in the hope that it will be useful,
Packit bbfece
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit bbfece
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit bbfece
   Library General Public License for more details.
Packit bbfece
Packit bbfece
   You should have received a copy of the GNU Library General Public
Packit bbfece
   License along with this program; if not, write to the Free Software
Packit bbfece
   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301,
Packit bbfece
   USA.  */
Packit bbfece
Packit bbfece
/* Tell glibc's <string.h> to provide a prototype for stpcpy().
Packit bbfece
   This must come before <config.h> because <config.h> may include
Packit bbfece
   <features.h>, and once <features.h> has been included, it's too late.  */
Packit bbfece
#ifndef _GNU_SOURCE
Packit bbfece
# define _GNU_SOURCE	1
Packit bbfece
#endif
Packit bbfece
Packit bbfece
#ifdef HAVE_CONFIG_H
Packit bbfece
# include <config.h>
Packit bbfece
#endif
Packit bbfece
Packit bbfece
#include <string.h>
Packit bbfece
Packit bbfece
#if defined _LIBC || defined HAVE_ARGZ_H
Packit bbfece
# include <argz.h>
Packit bbfece
#endif
Packit bbfece
#include <ctype.h>
Packit bbfece
#include <sys/types.h>
Packit bbfece
#include <stdlib.h>
Packit bbfece
Packit bbfece
#include "loadinfo.h"
Packit bbfece
Packit bbfece
/* On some strange systems still no definition of NULL is found.  Sigh!  */
Packit bbfece
#ifndef NULL
Packit bbfece
# if defined __STDC__ && __STDC__
Packit bbfece
#  define NULL ((void *) 0)
Packit bbfece
# else
Packit bbfece
#  define NULL 0
Packit bbfece
# endif
Packit bbfece
#endif
Packit bbfece
Packit bbfece
/* @@ end of prolog @@ */
Packit bbfece
Packit bbfece
#ifdef _LIBC
Packit bbfece
/* Rename the non ANSI C functions.  This is required by the standard
Packit bbfece
   because some ANSI C functions will require linking with this object
Packit bbfece
   file and the name space must not be polluted.  */
Packit bbfece
# ifndef stpcpy
Packit bbfece
#  define stpcpy(dest, src) __stpcpy(dest, src)
Packit bbfece
# endif
Packit bbfece
#else
Packit bbfece
# ifndef HAVE_STPCPY
Packit bbfece
static char *stpcpy PARAMS ((char *dest, const char *src));
Packit bbfece
# endif
Packit bbfece
#endif
Packit bbfece
Packit bbfece
/* Pathname support.
Packit bbfece
   ISSLASH(C)           tests whether C is a directory separator character.
Packit bbfece
   IS_ABSOLUTE_PATH(P)  tests whether P is an absolute path.  If it is not,
Packit bbfece
                        it may be concatenated to a directory pathname.
Packit bbfece
 */
Packit bbfece
#if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__
Packit bbfece
  /* Win32, OS/2, DOS */
Packit bbfece
# define ISSLASH(C) ((C) == '/' || (C) == '\\')
Packit bbfece
# define HAS_DEVICE(P) \
Packit bbfece
    ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
Packit bbfece
     && (P)[1] == ':')
Packit bbfece
# define IS_ABSOLUTE_PATH(P) (ISSLASH ((P)[0]) || HAS_DEVICE (P))
Packit bbfece
#else
Packit bbfece
  /* Unix */
Packit bbfece
# define ISSLASH(C) ((C) == '/')
Packit bbfece
# define IS_ABSOLUTE_PATH(P) ISSLASH ((P)[0])
Packit bbfece
#endif
Packit bbfece
Packit bbfece
/* Define function which are usually not available.  */
Packit bbfece
Packit bbfece
#if !defined _LIBC && !defined HAVE___ARGZ_COUNT
Packit bbfece
/* Returns the number of strings in ARGZ.  */
Packit bbfece
static size_t argz_count__ PARAMS ((const char *argz, size_t len));
Packit bbfece
Packit bbfece
static size_t
Packit bbfece
argz_count__ (argz, len)
Packit bbfece
     const char *argz;
Packit bbfece
     size_t len;
Packit bbfece
{
Packit bbfece
  size_t count = 0;
Packit bbfece
  while (len > 0)
Packit bbfece
    {
Packit bbfece
      size_t part_len = strlen (argz);
Packit bbfece
      argz += part_len + 1;
Packit bbfece
      len -= part_len + 1;
Packit bbfece
      count++;
Packit bbfece
    }
Packit bbfece
  return count;
Packit bbfece
}
Packit bbfece
# undef __argz_count
Packit bbfece
# define __argz_count(argz, len) argz_count__ (argz, len)
Packit bbfece
#else
Packit bbfece
# ifdef _LIBC
Packit bbfece
#  define __argz_count(argz, len) INTUSE(__argz_count) (argz, len)
Packit bbfece
# endif
Packit bbfece
#endif	/* !_LIBC && !HAVE___ARGZ_COUNT */
Packit bbfece
Packit bbfece
#if !defined _LIBC && !defined HAVE___ARGZ_STRINGIFY
Packit bbfece
/* Make '\0' separated arg vector ARGZ printable by converting all the '\0's
Packit bbfece
   except the last into the character SEP.  */
Packit bbfece
static void argz_stringify__ PARAMS ((char *argz, size_t len, int sep));
Packit bbfece
Packit bbfece
static void
Packit bbfece
argz_stringify__ (argz, len, sep)
Packit bbfece
     char *argz;
Packit bbfece
     size_t len;
Packit bbfece
     int sep;
Packit bbfece
{
Packit bbfece
  while (len > 0)
Packit bbfece
    {
Packit bbfece
      size_t part_len = strlen (argz);
Packit bbfece
      argz += part_len;
Packit bbfece
      len -= part_len + 1;
Packit bbfece
      if (len > 0)
Packit bbfece
	*argz++ = sep;
Packit bbfece
    }
Packit bbfece
}
Packit bbfece
# undef __argz_stringify
Packit bbfece
# define __argz_stringify(argz, len, sep) argz_stringify__ (argz, len, sep)
Packit bbfece
#else
Packit bbfece
# ifdef _LIBC
Packit bbfece
#  define __argz_stringify(argz, len, sep) \
Packit bbfece
  INTUSE(__argz_stringify) (argz, len, sep)
Packit bbfece
# endif
Packit bbfece
#endif	/* !_LIBC && !HAVE___ARGZ_STRINGIFY */
Packit bbfece
Packit bbfece
#if !defined _LIBC && !defined HAVE___ARGZ_NEXT
Packit bbfece
static char *argz_next__ PARAMS ((char *argz, size_t argz_len,
Packit bbfece
				  const char *entry));
Packit bbfece
Packit bbfece
static char *
Packit bbfece
argz_next__ (argz, argz_len, entry)
Packit bbfece
     char *argz;
Packit bbfece
     size_t argz_len;
Packit bbfece
     const char *entry;
Packit bbfece
{
Packit bbfece
  if (entry)
Packit bbfece
    {
Packit bbfece
      if (entry < argz + argz_len)
Packit bbfece
        entry = strchr (entry, '\0') + 1;
Packit bbfece
Packit bbfece
      return entry >= argz + argz_len ? NULL : (char *) entry;
Packit bbfece
    }
Packit bbfece
  else
Packit bbfece
    if (argz_len > 0)
Packit bbfece
      return argz;
Packit bbfece
    else
Packit bbfece
      return 0;
Packit bbfece
}
Packit bbfece
# undef __argz_next
Packit bbfece
# define __argz_next(argz, len, entry) argz_next__ (argz, len, entry)
Packit bbfece
#endif	/* !_LIBC && !HAVE___ARGZ_NEXT */
Packit bbfece
Packit bbfece
Packit bbfece
/* Return number of bits set in X.  */
Packit bbfece
static int pop PARAMS ((int x));
Packit bbfece
Packit bbfece
static inline int
Packit bbfece
pop (x)
Packit bbfece
     int x;
Packit bbfece
{
Packit bbfece
  /* We assume that no more than 16 bits are used.  */
Packit bbfece
  x = ((x & ~0x5555) >> 1) + (x & 0x5555);
Packit bbfece
  x = ((x & ~0x3333) >> 2) + (x & 0x3333);
Packit bbfece
  x = ((x >> 4) + x) & 0x0f0f;
Packit bbfece
  x = ((x >> 8) + x) & 0xff;
Packit bbfece
Packit bbfece
  return x;
Packit bbfece
}
Packit bbfece
Packit bbfece

Packit bbfece
struct loaded_l10nfile *
Packit bbfece
_nl_make_l10nflist (l10nfile_list, dirlist, dirlist_len, mask, language,
Packit bbfece
		    territory, codeset, normalized_codeset, modifier, special,
Packit bbfece
		    sponsor, revision, filename, do_allocate)
Packit bbfece
     struct loaded_l10nfile **l10nfile_list;
Packit bbfece
     const char *dirlist;
Packit bbfece
     size_t dirlist_len;
Packit bbfece
     int mask;
Packit bbfece
     const char *language;
Packit bbfece
     const char *territory;
Packit bbfece
     const char *codeset;
Packit bbfece
     const char *normalized_codeset;
Packit bbfece
     const char *modifier;
Packit bbfece
     const char *special;
Packit bbfece
     const char *sponsor;
Packit bbfece
     const char *revision;
Packit bbfece
     const char *filename;
Packit bbfece
     int do_allocate;
Packit bbfece
{
Packit bbfece
  char *abs_filename;
Packit bbfece
  struct loaded_l10nfile **lastp;
Packit bbfece
  struct loaded_l10nfile *retval;
Packit bbfece
  char *cp;
Packit bbfece
  size_t dirlist_count;
Packit bbfece
  size_t entries;
Packit bbfece
  int cnt;
Packit bbfece
Packit bbfece
  /* If LANGUAGE contains an absolute directory specification, we ignore
Packit bbfece
     DIRLIST.  */
Packit bbfece
  if (IS_ABSOLUTE_PATH (language))
Packit bbfece
    dirlist_len = 0;
Packit bbfece
Packit bbfece
  /* Allocate room for the full file name.  */
Packit bbfece
  abs_filename = (char *) malloc (dirlist_len
Packit bbfece
				  + strlen (language)
Packit bbfece
				  + ((mask & TERRITORY) != 0
Packit bbfece
				     ? strlen (territory) + 1 : 0)
Packit bbfece
				  + ((mask & XPG_CODESET) != 0
Packit bbfece
				     ? strlen (codeset) + 1 : 0)
Packit bbfece
				  + ((mask & XPG_NORM_CODESET) != 0
Packit bbfece
				     ? strlen (normalized_codeset) + 1 : 0)
Packit bbfece
				  + (((mask & XPG_MODIFIER) != 0
Packit bbfece
				      || (mask & CEN_AUDIENCE) != 0)
Packit bbfece
				     ? strlen (modifier) + 1 : 0)
Packit bbfece
				  + ((mask & CEN_SPECIAL) != 0
Packit bbfece
				     ? strlen (special) + 1 : 0)
Packit bbfece
				  + (((mask & CEN_SPONSOR) != 0
Packit bbfece
				      || (mask & CEN_REVISION) != 0)
Packit bbfece
				     ? (1 + ((mask & CEN_SPONSOR) != 0
Packit bbfece
					     ? strlen (sponsor) : 0)
Packit bbfece
					+ ((mask & CEN_REVISION) != 0
Packit bbfece
					   ? strlen (revision) + 1 : 0)) : 0)
Packit bbfece
				  + 1 + strlen (filename) + 1);
Packit bbfece
Packit bbfece
  if (abs_filename == NULL)
Packit bbfece
    return NULL;
Packit bbfece
Packit bbfece
  /* Construct file name.  */
Packit bbfece
  cp = abs_filename;
Packit bbfece
  if (dirlist_len > 0)
Packit bbfece
    {
Packit bbfece
      memcpy (cp, dirlist, dirlist_len);
Packit bbfece
      __argz_stringify (cp, dirlist_len, PATH_SEPARATOR);
Packit bbfece
      cp += dirlist_len;
Packit bbfece
      cp[-1] = '/';
Packit bbfece
    }
Packit bbfece
Packit bbfece
  cp = stpcpy (cp, language);
Packit bbfece
Packit bbfece
  if ((mask & TERRITORY) != 0)
Packit bbfece
    {
Packit bbfece
      *cp++ = '_';
Packit bbfece
      cp = stpcpy (cp, territory);
Packit bbfece
    }
Packit bbfece
  if ((mask & XPG_CODESET) != 0)
Packit bbfece
    {
Packit bbfece
      *cp++ = '.';
Packit bbfece
      cp = stpcpy (cp, codeset);
Packit bbfece
    }
Packit bbfece
  if ((mask & XPG_NORM_CODESET) != 0)
Packit bbfece
    {
Packit bbfece
      *cp++ = '.';
Packit bbfece
      cp = stpcpy (cp, normalized_codeset);
Packit bbfece
    }
Packit bbfece
  if ((mask & (XPG_MODIFIER | CEN_AUDIENCE)) != 0)
Packit bbfece
    {
Packit bbfece
      /* This component can be part of both syntaces but has different
Packit bbfece
	 leading characters.  For CEN we use `+', else `@'.  */
Packit bbfece
      *cp++ = (mask & CEN_AUDIENCE) != 0 ? '+' : '@';
Packit bbfece
      cp = stpcpy (cp, modifier);
Packit bbfece
    }
Packit bbfece
  if ((mask & CEN_SPECIAL) != 0)
Packit bbfece
    {
Packit bbfece
      *cp++ = '+';
Packit bbfece
      cp = stpcpy (cp, special);
Packit bbfece
    }
Packit bbfece
  if ((mask & (CEN_SPONSOR | CEN_REVISION)) != 0)
Packit bbfece
    {
Packit bbfece
      *cp++ = ',';
Packit bbfece
      if ((mask & CEN_SPONSOR) != 0)
Packit bbfece
	cp = stpcpy (cp, sponsor);
Packit bbfece
      if ((mask & CEN_REVISION) != 0)
Packit bbfece
	{
Packit bbfece
	  *cp++ = '_';
Packit bbfece
	  cp = stpcpy (cp, revision);
Packit bbfece
	}
Packit bbfece
    }
Packit bbfece
Packit bbfece
  *cp++ = '/';
Packit bbfece
  stpcpy (cp, filename);
Packit bbfece
Packit bbfece
  /* Look in list of already loaded domains whether it is already
Packit bbfece
     available.  */
Packit bbfece
  lastp = l10nfile_list;
Packit bbfece
  for (retval = *l10nfile_list; retval != NULL; retval = retval->next)
Packit bbfece
    if (retval->filename != NULL)
Packit bbfece
      {
Packit bbfece
	int compare = strcmp (retval->filename, abs_filename);
Packit bbfece
	if (compare == 0)
Packit bbfece
	  /* We found it!  */
Packit bbfece
	  break;
Packit bbfece
	if (compare < 0)
Packit bbfece
	  {
Packit bbfece
	    /* It's not in the list.  */
Packit bbfece
	    retval = NULL;
Packit bbfece
	    break;
Packit bbfece
	  }
Packit bbfece
Packit bbfece
	lastp = &retval->next;
Packit bbfece
      }
Packit bbfece
Packit bbfece
  if (retval != NULL || do_allocate == 0)
Packit bbfece
    {
Packit bbfece
      free (abs_filename);
Packit bbfece
      return retval;
Packit bbfece
    }
Packit bbfece
Packit bbfece
  dirlist_count = (dirlist_len > 0 ? __argz_count (dirlist, dirlist_len) : 1);
Packit bbfece
Packit bbfece
  /* Allocate a new loaded_l10nfile.  */
Packit bbfece
  retval =
Packit bbfece
    (struct loaded_l10nfile *)
Packit bbfece
    malloc (sizeof (*retval)
Packit bbfece
	    + (((dirlist_count << pop (mask)) + (dirlist_count > 1 ? 1 : 0))
Packit bbfece
	       * sizeof (struct loaded_l10nfile *)));
Packit bbfece
  if (retval == NULL)
Packit bbfece
    return NULL;
Packit bbfece
Packit bbfece
  retval->filename = abs_filename;
Packit bbfece
Packit bbfece
  /* We set retval->data to NULL here; it is filled in later.
Packit bbfece
     Setting retval->decided to 1 here means that retval does not
Packit bbfece
     correspond to a real file (dirlist_count > 1) or is not worth
Packit bbfece
     looking up (if an unnormalized codeset was specified).  */
Packit bbfece
  retval->decided = (dirlist_count > 1
Packit bbfece
		     || ((mask & XPG_CODESET) != 0
Packit bbfece
			 && (mask & XPG_NORM_CODESET) != 0));
Packit bbfece
  retval->data = NULL;
Packit bbfece
Packit bbfece
  retval->next = *lastp;
Packit bbfece
  *lastp = retval;
Packit bbfece
Packit bbfece
  entries = 0;
Packit bbfece
  /* Recurse to fill the inheritance list of RETVAL.
Packit bbfece
     If the DIRLIST is a real list (i.e. DIRLIST_COUNT > 1), the RETVAL
Packit bbfece
     entry does not correspond to a real file; retval->filename contains
Packit bbfece
     colons.  In this case we loop across all elements of DIRLIST and
Packit bbfece
     across all bit patterns dominated by MASK.
Packit bbfece
     If the DIRLIST is a single directory or entirely redundant (i.e.
Packit bbfece
     DIRLIST_COUNT == 1), we loop across all bit patterns dominated by
Packit bbfece
     MASK, excluding MASK itself.
Packit bbfece
     In either case, we loop down from MASK to 0.  This has the effect
Packit bbfece
     that the extra bits in the locale name are dropped in this order:
Packit bbfece
     first the modifier, then the territory, then the codeset, then the
Packit bbfece
     normalized_codeset.  */
Packit bbfece
  for (cnt = dirlist_count > 1 ? mask : mask - 1; cnt >= 0; --cnt)
Packit bbfece
    if ((cnt & ~mask) == 0
Packit bbfece
	&& ((cnt & CEN_SPECIFIC) == 0 || (cnt & XPG_SPECIFIC) == 0)
Packit bbfece
	&& ((cnt & XPG_CODESET) == 0 || (cnt & XPG_NORM_CODESET) == 0))
Packit bbfece
      {
Packit bbfece
	if (dirlist_count > 1)
Packit bbfece
	  {
Packit bbfece
	    /* Iterate over all elements of the DIRLIST.  */
Packit bbfece
	    char *dir = NULL;
Packit bbfece
Packit bbfece
	    while ((dir = __argz_next ((char *) dirlist, dirlist_len, dir))
Packit bbfece
		   != NULL)
Packit bbfece
	      retval->successor[entries++]
Packit bbfece
		= _nl_make_l10nflist (l10nfile_list, dir, strlen (dir) + 1,
Packit bbfece
				      cnt, language, territory, codeset,
Packit bbfece
				      normalized_codeset, modifier, special,
Packit bbfece
				      sponsor, revision, filename, 1);
Packit bbfece
	  }
Packit bbfece
	else
Packit bbfece
	  retval->successor[entries++]
Packit bbfece
	    = _nl_make_l10nflist (l10nfile_list, dirlist, dirlist_len,
Packit bbfece
				  cnt, language, territory, codeset,
Packit bbfece
				  normalized_codeset, modifier, special,
Packit bbfece
				  sponsor, revision, filename, 1);
Packit bbfece
      }
Packit bbfece
  retval->successor[entries] = NULL;
Packit bbfece
Packit bbfece
  return retval;
Packit bbfece
}
Packit bbfece

Packit bbfece
/* Normalize codeset name.  There is no standard for the codeset
Packit bbfece
   names.  Normalization allows the user to use any of the common
Packit bbfece
   names.  The return value is dynamically allocated and has to be
Packit bbfece
   freed by the caller.  */
Packit bbfece
const char *
Packit bbfece
_nl_normalize_codeset (codeset, name_len)
Packit bbfece
     const char *codeset;
Packit bbfece
     size_t name_len;
Packit bbfece
{
Packit bbfece
  int len = 0;
Packit bbfece
  int only_digit = 1;
Packit bbfece
  char *retval;
Packit bbfece
  char *wp;
Packit bbfece
  size_t cnt;
Packit bbfece
Packit bbfece
  for (cnt = 0; cnt < name_len; ++cnt)
Packit bbfece
    if (isalnum ((unsigned char) codeset[cnt]))
Packit bbfece
      {
Packit bbfece
	++len;
Packit bbfece
Packit bbfece
	if (isalpha ((unsigned char) codeset[cnt]))
Packit bbfece
	  only_digit = 0;
Packit bbfece
      }
Packit bbfece
Packit bbfece
  retval = (char *) malloc ((only_digit ? 3 : 0) + len + 1);
Packit bbfece
Packit bbfece
  if (retval != NULL)
Packit bbfece
    {
Packit bbfece
      if (only_digit)
Packit bbfece
	wp = stpcpy (retval, "iso");
Packit bbfece
      else
Packit bbfece
	wp = retval;
Packit bbfece
Packit bbfece
      for (cnt = 0; cnt < name_len; ++cnt)
Packit bbfece
	if (isalpha ((unsigned char) codeset[cnt]))
Packit bbfece
	  *wp++ = tolower ((unsigned char) codeset[cnt]);
Packit bbfece
	else if (isdigit ((unsigned char) codeset[cnt]))
Packit bbfece
	  *wp++ = codeset[cnt];
Packit bbfece
Packit bbfece
      *wp = '\0';
Packit bbfece
    }
Packit bbfece
Packit bbfece
  return (const char *) retval;
Packit bbfece
}
Packit bbfece
Packit bbfece
Packit bbfece
/* @@ begin of epilog @@ */
Packit bbfece
Packit bbfece
/* We don't want libintl.a to depend on any other library.  So we
Packit bbfece
   avoid the non-standard function stpcpy.  In GNU C Library this
Packit bbfece
   function is available, though.  Also allow the symbol HAVE_STPCPY
Packit bbfece
   to be defined.  */
Packit bbfece
#if !_LIBC && !HAVE_STPCPY
Packit bbfece
static char *
Packit bbfece
stpcpy (dest, src)
Packit bbfece
     char *dest;
Packit bbfece
     const char *src;
Packit bbfece
{
Packit bbfece
  while ((*dest++ = *src++) != '\0')
Packit bbfece
    /* Do nothing. */ ;
Packit bbfece
  return dest - 1;
Packit bbfece
}
Packit bbfece
#endif