Blame intl/dcigettext.c

Packit 6c4009
/* Implementation of the internal dcigettext function.
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 <sys/types.h>
Packit 6c4009
Packit 6c4009
#ifdef __GNUC__
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 <errno.h>
Packit 6c4009
#ifndef errno
Packit 6c4009
extern int errno;
Packit 6c4009
#endif
Packit 6c4009
#ifndef __set_errno
Packit 6c4009
# define __set_errno(val) errno = (val)
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#include <stddef.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <stdio.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
Packit 6c4009
#if defined HAVE_UNISTD_H || defined _LIBC
Packit 6c4009
# include <unistd.h>
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#include <locale.h>
Packit 6c4009
Packit 6c4009
#ifdef _LIBC
Packit 6c4009
  /* Guess whether integer division by zero raises signal SIGFPE.
Packit 6c4009
     Set to 1 only if you know for sure.  In case of doubt, set to 0.  */
Packit 6c4009
# if defined __alpha__ || defined __arm__ || defined __i386__ \
Packit 6c4009
     || defined __m68k__ || defined __s390__
Packit 6c4009
#  define INTDIV0_RAISES_SIGFPE 1
Packit 6c4009
# else
Packit 6c4009
#  define INTDIV0_RAISES_SIGFPE 0
Packit 6c4009
# endif
Packit 6c4009
#endif
Packit 6c4009
#if !INTDIV0_RAISES_SIGFPE
Packit 6c4009
# include <signal.h>
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#if defined HAVE_SYS_PARAM_H || defined _LIBC
Packit 6c4009
# include <sys/param.h>
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#if !defined _LIBC
Packit 6c4009
# include "localcharset.h"
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#include "gettextP.h"
Packit 6c4009
#include "plural-exp.h"
Packit 6c4009
#ifdef _LIBC
Packit 6c4009
# include <libintl.h>
Packit 6c4009
#else
Packit 6c4009
# ifdef IN_LIBGLOCALE
Packit 6c4009
#  include <libintl.h>
Packit 6c4009
# endif
Packit 6c4009
# include "libgnuintl.h"
Packit 6c4009
#endif
Packit 6c4009
#include "hash-string.h"
Packit 6c4009
Packit 6c4009
/* Handle multi-threaded applications.  */
Packit 6c4009
#ifdef _LIBC
Packit 6c4009
# include <libc-lock.h>
Packit 6c4009
# define gl_rwlock_define_initialized __libc_rwlock_define_initialized
Packit 6c4009
# define gl_rwlock_rdlock __libc_rwlock_rdlock
Packit 6c4009
# define gl_rwlock_wrlock __libc_rwlock_wrlock
Packit 6c4009
# define gl_rwlock_unlock __libc_rwlock_unlock
Packit 6c4009
#else
Packit 6c4009
# include "lock.h"
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* Alignment of types.  */
Packit 6c4009
#if defined __GNUC__ && __GNUC__ >= 2
Packit 6c4009
# define alignof(TYPE) __alignof__ (TYPE)
Packit 6c4009
#else
Packit 6c4009
# define alignof(TYPE) \
Packit 6c4009
    ((int) &((struct { char dummy1; TYPE dummy2; } *) 0)->dummy2)
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* Some compilers, like SunOS4 cc, don't have offsetof in <stddef.h>.  */
Packit 6c4009
#ifndef offsetof
Packit 6c4009
# define offsetof(type,ident) ((size_t)&(((type*)0)->ident))
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 strdup __strdup
Packit 6c4009
# define getcwd __getcwd
Packit 6c4009
# ifndef stpcpy
Packit 6c4009
#  define stpcpy __stpcpy
Packit 6c4009
# endif
Packit 6c4009
# define tfind __tfind
Packit 6c4009
#else
Packit 6c4009
# if !defined HAVE_GETCWD
Packit 6c4009
char *getwd ();
Packit 6c4009
#  define getcwd(buf, max) getwd (buf)
Packit 6c4009
# else
Packit 6c4009
#  if VMS
Packit 6c4009
#   define getcwd(buf, max) (getcwd) (buf, max, 0)
Packit 6c4009
#  else
Packit 6c4009
char *getcwd ();
Packit 6c4009
#  endif
Packit 6c4009
# endif
Packit 6c4009
# ifndef HAVE_STPCPY
Packit 6c4009
static char *stpcpy (char *dest, const char *src);
Packit 6c4009
# endif
Packit 6c4009
# ifndef HAVE_MEMPCPY
Packit 6c4009
static void *mempcpy (void *dest, const void *src, size_t n);
Packit 6c4009
# endif
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* Use a replacement if the system does not provide the `tsearch' function
Packit 6c4009
   family.  */
Packit 6c4009
#if defined HAVE_TSEARCH || defined _LIBC
Packit 6c4009
# include <search.h>
Packit 6c4009
#else
Packit 6c4009
# define tsearch libintl_tsearch
Packit 6c4009
# define tfind libintl_tfind
Packit 6c4009
# define tdelete libintl_tdelete
Packit 6c4009
# define twalk libintl_twalk
Packit 6c4009
# include "tsearch.h"
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#ifdef _LIBC
Packit 6c4009
# define tsearch __tsearch
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* Amount to increase buffer size by in each try.  */
Packit 6c4009
#define PATH_INCR 32
Packit 6c4009
Packit 6c4009
/* The following is from pathmax.h.  */
Packit 6c4009
/* Non-POSIX BSD systems might have gcc's limits.h, which doesn't define
Packit 6c4009
   PATH_MAX but might cause redefinition warnings when sys/param.h is
Packit 6c4009
   later included (as on MORE/BSD 4.3).  */
Packit 6c4009
#if defined _POSIX_VERSION || (defined HAVE_LIMITS_H && !defined __GNUC__)
Packit 6c4009
# include <limits.h>
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#ifndef _POSIX_PATH_MAX
Packit 6c4009
# define _POSIX_PATH_MAX 255
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#if !defined PATH_MAX && defined _PC_PATH_MAX
Packit 6c4009
# define PATH_MAX (__pathconf ("/", _PC_PATH_MAX) < 1 ? 1024 : __pathconf ("/", _PC_PATH_MAX))
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* Don't include sys/param.h if it already has been.  */
Packit 6c4009
#if defined HAVE_SYS_PARAM_H && !defined PATH_MAX && !defined MAXPATHLEN
Packit 6c4009
# include <sys/param.h>
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#if !defined PATH_MAX && defined MAXPATHLEN
Packit 6c4009
# define PATH_MAX MAXPATHLEN
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#ifndef PATH_MAX
Packit 6c4009
# define PATH_MAX _POSIX_PATH_MAX
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* Pathname support.
Packit 6c4009
   ISSLASH(C)           tests whether C is a directory separator character.
Packit 6c4009
   IS_ABSOLUTE_PATH(P)  tests whether P is an absolute path.  If it is not,
Packit 6c4009
                        it may be concatenated to a directory pathname.
Packit 6c4009
   IS_PATH_WITH_DIR(P)  tests whether P contains a directory specification.
Packit 6c4009
 */
Packit 6c4009
#if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__
Packit 6c4009
  /* Win32, Cygwin, OS/2, DOS */
Packit 6c4009
# define ISSLASH(C) ((C) == '/' || (C) == '\\')
Packit 6c4009
# define HAS_DEVICE(P) \
Packit 6c4009
    ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
Packit 6c4009
     && (P)[1] == ':')
Packit 6c4009
# define IS_ABSOLUTE_PATH(P) (ISSLASH ((P)[0]) || HAS_DEVICE (P))
Packit 6c4009
# define IS_PATH_WITH_DIR(P) \
Packit 6c4009
    (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P))
Packit 6c4009
#else
Packit 6c4009
  /* Unix */
Packit 6c4009
# define ISSLASH(C) ((C) == '/')
Packit 6c4009
# define IS_ABSOLUTE_PATH(P) ISSLASH ((P)[0])
Packit 6c4009
# define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL)
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* Whether to support different locales in different threads.  */
Packit 6c4009
#if defined _LIBC || HAVE_USELOCALE || defined IN_LIBGLOCALE
Packit 6c4009
# define HAVE_PER_THREAD_LOCALE
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* This is the type used for the search tree where known translations
Packit 6c4009
   are stored.  */
Packit 6c4009
struct known_translation_t
Packit 6c4009
{
Packit 6c4009
  /* Domain in which to search.  */
Packit 6c4009
  const char *domainname;
Packit 6c4009
Packit 6c4009
  /* The category.  */
Packit 6c4009
  int category;
Packit 6c4009
Packit 6c4009
#ifdef HAVE_PER_THREAD_LOCALE
Packit 6c4009
  /* Name of the relevant locale category, or "" for the global locale.  */
Packit 6c4009
  const char *localename;
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#ifdef IN_LIBGLOCALE
Packit 6c4009
  /* The character encoding.  */
Packit 6c4009
  const char *encoding;
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  /* State of the catalog counter at the point the string was found.  */
Packit 6c4009
  int counter;
Packit 6c4009
Packit 6c4009
  /* Catalog where the string was found.  */
Packit 6c4009
  struct loaded_l10nfile *domain;
Packit 6c4009
Packit 6c4009
  /* And finally the translation.  */
Packit 6c4009
  const char *translation;
Packit 6c4009
  size_t translation_length;
Packit 6c4009
Packit 6c4009
  /* Pointer to the string in question.  */
Packit 6c4009
  union
Packit 6c4009
    {
Packit 6c4009
      char appended[ZERO];  /* used if domain != NULL */
Packit 6c4009
      const char *ptr;      /* used if domain == NULL */
Packit 6c4009
    }
Packit 6c4009
  msgid;
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
gl_rwlock_define_initialized (static, tree_lock)
Packit 6c4009
Packit 6c4009
/* Root of the search tree with known translations.  */
Packit 6c4009
static void *root;
Packit 6c4009
Packit 6c4009
/* Function to compare two entries in the table of known translations.  */
Packit 6c4009
static int
Packit 6c4009
transcmp (const void *p1, const void *p2)
Packit 6c4009
{
Packit 6c4009
  const struct known_translation_t *s1;
Packit 6c4009
  const struct known_translation_t *s2;
Packit 6c4009
  int result;
Packit 6c4009
Packit 6c4009
  s1 = (const struct known_translation_t *) p1;
Packit 6c4009
  s2 = (const struct known_translation_t *) p2;
Packit 6c4009
Packit 6c4009
  result = strcmp (s1->domain != NULL ? s1->msgid.appended : s1->msgid.ptr,
Packit 6c4009
		   s2->domain != NULL ? s2->msgid.appended : s2->msgid.ptr);
Packit 6c4009
  if (result == 0)
Packit 6c4009
    {
Packit 6c4009
      result = strcmp (s1->domainname, s2->domainname);
Packit 6c4009
      if (result == 0)
Packit 6c4009
	{
Packit 6c4009
#ifdef HAVE_PER_THREAD_LOCALE
Packit 6c4009
	  result = strcmp (s1->localename, s2->localename);
Packit 6c4009
	  if (result == 0)
Packit 6c4009
#endif
Packit 6c4009
	    {
Packit 6c4009
#ifdef IN_LIBGLOCALE
Packit 6c4009
	      result = strcmp (s1->encoding, s2->encoding);
Packit 6c4009
	      if (result == 0)
Packit 6c4009
#endif
Packit 6c4009
		/* We compare the category last (though this is the cheapest
Packit 6c4009
		   operation) since it is hopefully always the same (namely
Packit 6c4009
		   LC_MESSAGES).  */
Packit 6c4009
		result = s1->category - s2->category;
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return result;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Name of the default domain used for gettext(3) prior any call to
Packit 6c4009
   textdomain(3).  The default value for this is "messages".  */
Packit 6c4009
const char _nl_default_default_domain[] attribute_hidden = "messages";
Packit 6c4009
Packit 6c4009
#ifndef IN_LIBGLOCALE
Packit 6c4009
/* Value used as the default domain for gettext(3).  */
Packit 6c4009
const char *_nl_current_default_domain attribute_hidden
Packit 6c4009
     = _nl_default_default_domain;
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* Contains the default location of the message catalogs.  */
Packit 6c4009
#if defined __EMX__
Packit 6c4009
extern const char _nl_default_dirname[];
Packit 6c4009
#else
Packit 6c4009
# ifdef _LIBC
Packit 6c4009
extern const char _nl_default_dirname[];
Packit 6c4009
libc_hidden_proto (_nl_default_dirname)
Packit 6c4009
# endif
Packit 6c4009
const char _nl_default_dirname[] = LOCALEDIR;
Packit 6c4009
# ifdef _LIBC
Packit 6c4009
libc_hidden_data_def (_nl_default_dirname)
Packit 6c4009
# endif
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#ifndef IN_LIBGLOCALE
Packit 6c4009
/* List with bindings of specific domains created by bindtextdomain()
Packit 6c4009
   calls.  */
Packit 6c4009
struct binding *_nl_domain_bindings;
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* Prototypes for local functions.  */
Packit 6c4009
static char *plural_lookup (struct loaded_l10nfile *domain,
Packit 6c4009
			    unsigned long int n,
Packit 6c4009
			    const char *translation, size_t translation_len);
Packit 6c4009
Packit 6c4009
#ifdef IN_LIBGLOCALE
Packit 6c4009
static const char *guess_category_value (int category,
Packit 6c4009
					 const char *categoryname,
Packit 6c4009
					 const char *localename);
Packit 6c4009
#else
Packit 6c4009
static const char *guess_category_value (int category,
Packit 6c4009
					 const char *categoryname);
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#ifdef _LIBC
Packit 6c4009
# include "../locale/localeinfo.h"
Packit 6c4009
# define category_to_name(category) \
Packit 6c4009
  _nl_category_names.str + _nl_category_name_idxs[category]
Packit 6c4009
#else
Packit 6c4009
static const char *category_to_name (int category);
Packit 6c4009
#endif
Packit 6c4009
#if (defined _LIBC || HAVE_ICONV) && !defined IN_LIBGLOCALE
Packit 6c4009
static const char *get_output_charset (struct binding *domainbinding);
Packit 6c4009
#endif
Packit 6c4009
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
/* Nothing has to be done.  */
Packit 6c4009
# define freea(p) /* nothing */
Packit 6c4009
# define ADD_BLOCK(list, address) /* nothing */
Packit 6c4009
# define FREE_BLOCKS(list) /* nothing */
Packit 6c4009
#else
Packit 6c4009
struct block_list
Packit 6c4009
{
Packit 6c4009
  void *address;
Packit 6c4009
  struct block_list *next;
Packit 6c4009
};
Packit 6c4009
# define ADD_BLOCK(list, addr)						      \
Packit 6c4009
  do {									      \
Packit 6c4009
    struct block_list *newp = (struct block_list *) malloc (sizeof (*newp));  \
Packit 6c4009
    /* If we cannot get a free block we cannot add the new element to	      \
Packit 6c4009
       the list.  */							      \
Packit 6c4009
    if (newp != NULL) {							      \
Packit 6c4009
      newp->address = (addr);						      \
Packit 6c4009
      newp->next = (list);						      \
Packit 6c4009
      (list) = newp;							      \
Packit 6c4009
    }									      \
Packit 6c4009
  } while (0)
Packit 6c4009
# define FREE_BLOCKS(list)						      \
Packit 6c4009
  do {									      \
Packit 6c4009
    while (list != NULL) {						      \
Packit 6c4009
      struct block_list *old = list;					      \
Packit 6c4009
      list = list->next;						      \
Packit 6c4009
      free (old->address);						      \
Packit 6c4009
      free (old);							      \
Packit 6c4009
    }									      \
Packit 6c4009
  } while (0)
Packit 6c4009
# undef alloca
Packit 6c4009
# define alloca(size) (malloc (size))
Packit 6c4009
# define freea(p) free (p)
Packit 6c4009
#endif	/* have alloca */
Packit 6c4009
Packit 6c4009
Packit 6c4009
#ifdef _LIBC
Packit 6c4009
/* List of blocks allocated for translations.  */
Packit 6c4009
typedef struct transmem_list
Packit 6c4009
{
Packit 6c4009
  struct transmem_list *next;
Packit 6c4009
  char data[ZERO];
Packit 6c4009
} transmem_block_t;
Packit 6c4009
static struct transmem_list *transmem_list;
Packit 6c4009
#else
Packit 6c4009
typedef unsigned char transmem_block_t;
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Names for the libintl functions are a problem.  They must not clash
Packit 6c4009
   with existing names and they should follow ANSI C.  But this source
Packit 6c4009
   code is also used in GNU C Library where the names have a __
Packit 6c4009
   prefix.  So we have to make a difference here.  */
Packit 6c4009
#ifdef _LIBC
Packit 6c4009
# define DCIGETTEXT __dcigettext
Packit 6c4009
#else
Packit 6c4009
# define DCIGETTEXT libintl_dcigettext
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* Lock variable to protect the global data in the gettext implementation.  */
Packit 6c4009
gl_rwlock_define_initialized (, _nl_state_lock attribute_hidden)
Packit 6c4009
Packit 6c4009
/* Checking whether the binaries runs SUID must be done and glibc provides
Packit 6c4009
   easier methods therefore we make a difference here.  */
Packit 6c4009
#ifdef _LIBC
Packit 6c4009
# define ENABLE_SECURE __libc_enable_secure
Packit 6c4009
# define DETERMINE_SECURE
Packit 6c4009
#else
Packit 6c4009
# ifndef HAVE_GETUID
Packit 6c4009
#  define getuid() 0
Packit 6c4009
# endif
Packit 6c4009
# ifndef HAVE_GETGID
Packit 6c4009
#  define getgid() 0
Packit 6c4009
# endif
Packit 6c4009
# ifndef HAVE_GETEUID
Packit 6c4009
#  define geteuid() getuid()
Packit 6c4009
# endif
Packit 6c4009
# ifndef HAVE_GETEGID
Packit 6c4009
#  define getegid() getgid()
Packit 6c4009
# endif
Packit 6c4009
static int enable_secure;
Packit 6c4009
# define ENABLE_SECURE (enable_secure == 1)
Packit 6c4009
# define DETERMINE_SECURE \
Packit 6c4009
  if (enable_secure == 0)						      \
Packit 6c4009
    {									      \
Packit 6c4009
      if (getuid () != geteuid () || getgid () != getegid ())		      \
Packit 6c4009
	enable_secure = 1;						      \
Packit 6c4009
      else								      \
Packit 6c4009
	enable_secure = -1;						      \
Packit 6c4009
    }
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* Get the function to evaluate the plural expression.  */
Packit 6c4009
#include "eval-plural.h"
Packit 6c4009
Packit 6c4009
/* Look up MSGID in the DOMAINNAME message catalog for the current
Packit 6c4009
   CATEGORY locale and, if PLURAL is nonzero, search over string
Packit 6c4009
   depending on the plural form determined by N.  */
Packit 6c4009
#ifdef IN_LIBGLOCALE
Packit 6c4009
char *
Packit 6c4009
gl_dcigettext (const char *domainname,
Packit 6c4009
	       const char *msgid1, const char *msgid2,
Packit 6c4009
	       int plural, unsigned long int n,
Packit 6c4009
	       int category,
Packit 6c4009
	       const char *localename, const char *encoding)
Packit 6c4009
#else
Packit 6c4009
char *
Packit 6c4009
DCIGETTEXT (const char *domainname, const char *msgid1, const char *msgid2,
Packit 6c4009
	    int plural, unsigned long int n, int category)
Packit 6c4009
#endif
Packit 6c4009
{
Packit 6c4009
#ifndef HAVE_ALLOCA
Packit 6c4009
  struct block_list *block_list = NULL;
Packit 6c4009
#endif
Packit 6c4009
  struct loaded_l10nfile *domain;
Packit 6c4009
  struct binding *binding;
Packit 6c4009
  const char *categoryname;
Packit 6c4009
  const char *categoryvalue;
Packit 6c4009
  const char *dirname;
Packit 6c4009
  char *xdirname = NULL;
Packit 6c4009
  char *xdomainname;
Packit 6c4009
  char *single_locale;
Packit 6c4009
  char *retval;
Packit 6c4009
  size_t retlen;
Packit 6c4009
  int saved_errno;
Packit 6c4009
  struct known_translation_t search;
Packit 6c4009
  struct known_translation_t **foundp = NULL;
Packit 6c4009
#if defined HAVE_PER_THREAD_LOCALE && !defined IN_LIBGLOCALE
Packit 6c4009
  const char *localename;
Packit 6c4009
#endif
Packit 6c4009
  size_t domainname_len;
Packit 6c4009
Packit 6c4009
  /* If no real MSGID is given return NULL.  */
Packit 6c4009
  if (msgid1 == NULL)
Packit 6c4009
    return NULL;
Packit 6c4009
Packit 6c4009
#ifdef _LIBC
Packit 6c4009
  if (category < 0 || category >= __LC_LAST || category == LC_ALL)
Packit 6c4009
    /* Bogus.  */
Packit 6c4009
    return (plural == 0
Packit 6c4009
	    ? (char *) msgid1
Packit 6c4009
	    /* Use the Germanic plural rule.  */
Packit 6c4009
	    : n == 1 ? (char *) msgid1 : (char *) msgid2);
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  /* Preserve the `errno' value.  */
Packit 6c4009
  saved_errno = errno;
Packit 6c4009
Packit 6c4009
#ifdef _LIBC
Packit 6c4009
  __libc_rwlock_define (extern, __libc_setlocale_lock attribute_hidden)
Packit 6c4009
  __libc_rwlock_rdlock (__libc_setlocale_lock);
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  gl_rwlock_rdlock (_nl_state_lock);
Packit 6c4009
Packit 6c4009
  /* If DOMAINNAME is NULL, we are interested in the default domain.  If
Packit 6c4009
     CATEGORY is not LC_MESSAGES this might not make much sense but the
Packit 6c4009
     definition left this undefined.  */
Packit 6c4009
  if (domainname == NULL)
Packit 6c4009
    domainname = _nl_current_default_domain;
Packit 6c4009
Packit 6c4009
  /* OS/2 specific: backward compatibility with older libintl versions  */
Packit 6c4009
#ifdef LC_MESSAGES_COMPAT
Packit 6c4009
  if (category == LC_MESSAGES_COMPAT)
Packit 6c4009
    category = LC_MESSAGES;
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  /* Try to find the translation among those which we found at
Packit 6c4009
     some time.  */
Packit 6c4009
  search.domain = NULL;
Packit 6c4009
  search.msgid.ptr = msgid1;
Packit 6c4009
  search.domainname = domainname;
Packit 6c4009
  search.category = category;
Packit 6c4009
#ifdef HAVE_PER_THREAD_LOCALE
Packit 6c4009
# ifndef IN_LIBGLOCALE
Packit 6c4009
#  ifdef _LIBC
Packit 6c4009
  localename = __current_locale_name (category);
Packit 6c4009
#  else
Packit 6c4009
  categoryname = category_to_name (category);
Packit 6c4009
#   define CATEGORYNAME_INITIALIZED
Packit 6c4009
  localename = _nl_locale_name_thread_unsafe (category, categoryname);
Packit 6c4009
  if (localename == NULL)
Packit 6c4009
    localename = "";
Packit 6c4009
#  endif
Packit 6c4009
# endif
Packit 6c4009
  search.localename = localename;
Packit 6c4009
# ifdef IN_LIBGLOCALE
Packit 6c4009
  search.encoding = encoding;
Packit 6c4009
# endif
Packit 6c4009
Packit 6c4009
  /* Since tfind/tsearch manage a balanced tree, concurrent tfind and
Packit 6c4009
     tsearch calls can be fatal.  */
Packit 6c4009
  gl_rwlock_rdlock (tree_lock);
Packit 6c4009
Packit 6c4009
  foundp = (struct known_translation_t **) tfind (&search, &root, transcmp);
Packit 6c4009
Packit 6c4009
  gl_rwlock_unlock (tree_lock);
Packit 6c4009
Packit 6c4009
  if (foundp != NULL && (*foundp)->counter == _nl_msg_cat_cntr)
Packit 6c4009
    {
Packit 6c4009
      /* Now deal with plural.  */
Packit 6c4009
      if (plural)
Packit 6c4009
	retval = plural_lookup ((*foundp)->domain, n, (*foundp)->translation,
Packit 6c4009
				(*foundp)->translation_length);
Packit 6c4009
      else
Packit 6c4009
	retval = (char *) (*foundp)->translation;
Packit 6c4009
Packit 6c4009
      gl_rwlock_unlock (_nl_state_lock);
Packit 6c4009
# ifdef _LIBC
Packit 6c4009
      __libc_rwlock_unlock (__libc_setlocale_lock);
Packit 6c4009
# endif
Packit 6c4009
      __set_errno (saved_errno);
Packit 6c4009
      return retval;
Packit 6c4009
    }
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  /* See whether this is a SUID binary or not.  */
Packit 6c4009
  DETERMINE_SECURE;
Packit 6c4009
Packit 6c4009
  /* First find matching binding.  */
Packit 6c4009
#ifdef IN_LIBGLOCALE
Packit 6c4009
  /* We can use a trivial binding, since _nl_find_msg will ignore it anyway,
Packit 6c4009
     and _nl_load_domain and _nl_find_domain just pass it through.  */
Packit 6c4009
  binding = NULL;
Packit 6c4009
  dirname = bindtextdomain (domainname, NULL);
Packit 6c4009
#else
Packit 6c4009
  for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next)
Packit 6c4009
    {
Packit 6c4009
      int compare = strcmp (domainname, binding->domainname);
Packit 6c4009
      if (compare == 0)
Packit 6c4009
	/* We found it!  */
Packit 6c4009
	break;
Packit 6c4009
      if (compare < 0)
Packit 6c4009
	{
Packit 6c4009
	  /* It is not in the list.  */
Packit 6c4009
	  binding = NULL;
Packit 6c4009
	  break;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (binding == NULL)
Packit 6c4009
    dirname = _nl_default_dirname;
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      dirname = binding->dirname;
Packit 6c4009
#endif
Packit 6c4009
      if (!IS_ABSOLUTE_PATH (dirname))
Packit 6c4009
	{
Packit 6c4009
	  /* We have a relative path.  Make it absolute now.  */
Packit 6c4009
	  char *cwd = getcwd (NULL, 0);
Packit 6c4009
	  if (cwd == NULL)
Packit 6c4009
	    /* We cannot get the current working directory.  Don't
Packit 6c4009
	       signal an error but simply return the default
Packit 6c4009
	       string.  */
Packit 6c4009
	    goto return_untranslated;
Packit 6c4009
	  int ret = __asprintf (&xdirname, "%s/%s", cwd, dirname);
Packit 6c4009
	  free (cwd);
Packit 6c4009
	  if (ret < 0)
Packit Service 51c50e
	    goto return_untranslated;
Packit 6c4009
	  dirname = xdirname;
Packit 6c4009
	}
Packit 6c4009
#ifndef IN_LIBGLOCALE
Packit 6c4009
    }
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  /* Now determine the symbolic name of CATEGORY and its value.  */
Packit 6c4009
#ifndef CATEGORYNAME_INITIALIZED
Packit 6c4009
  categoryname = category_to_name (category);
Packit 6c4009
#endif
Packit 6c4009
#ifdef IN_LIBGLOCALE
Packit 6c4009
  categoryvalue = guess_category_value (category, categoryname, localename);
Packit 6c4009
#else
Packit 6c4009
  categoryvalue = guess_category_value (category, categoryname);
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  domainname_len = strlen (domainname);
Packit 6c4009
  xdomainname = (char *) alloca (strlen (categoryname)
Packit 6c4009
				 + domainname_len + 5);
Packit 6c4009
  ADD_BLOCK (block_list, xdomainname);
Packit 6c4009
Packit 6c4009
  stpcpy ((char *) mempcpy (stpcpy (stpcpy (xdomainname, categoryname), "/"),
Packit 6c4009
			    domainname, domainname_len),
Packit 6c4009
	  ".mo");
Packit 6c4009
Packit 6c4009
  /* Creating working area.  */
Packit 6c4009
  single_locale = (char *) alloca (strlen (categoryvalue) + 1);
Packit 6c4009
  ADD_BLOCK (block_list, single_locale);
Packit 6c4009
Packit 6c4009
Packit 6c4009
  /* Search for the given string.  This is a loop because we perhaps
Packit 6c4009
     got an ordered list of languages to consider for the translation.  */
Packit 6c4009
  while (1)
Packit 6c4009
    {
Packit 6c4009
      /* Make CATEGORYVALUE point to the next element of the list.  */
Packit 6c4009
      while (categoryvalue[0] != '\0' && categoryvalue[0] == ':')
Packit 6c4009
	++categoryvalue;
Packit 6c4009
      if (categoryvalue[0] == '\0')
Packit 6c4009
	{
Packit 6c4009
	  /* The whole contents of CATEGORYVALUE has been searched but
Packit 6c4009
	     no valid entry has been found.  We solve this situation
Packit 6c4009
	     by implicitly appending a "C" entry, i.e. no translation
Packit 6c4009
	     will take place.  */
Packit 6c4009
	  single_locale[0] = 'C';
Packit 6c4009
	  single_locale[1] = '\0';
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
	{
Packit 6c4009
	  char *cp = single_locale;
Packit 6c4009
	  while (categoryvalue[0] != '\0' && categoryvalue[0] != ':')
Packit 6c4009
	    *cp++ = *categoryvalue++;
Packit 6c4009
	  *cp = '\0';
Packit 6c4009
Packit 6c4009
	  /* When this is a SUID binary we must not allow accessing files
Packit 6c4009
	     outside the dedicated directories.  */
Packit 6c4009
	  if (ENABLE_SECURE && IS_PATH_WITH_DIR (single_locale))
Packit 6c4009
	    /* Ingore this entry.  */
Packit 6c4009
	    continue;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      /* If the current locale value is C (or POSIX) we don't load a
Packit 6c4009
	 domain.  Return the MSGID.  */
Packit 6c4009
      if (strcmp (single_locale, "C") == 0
Packit 6c4009
	  || strcmp (single_locale, "POSIX") == 0)
Packit 6c4009
	break;
Packit 6c4009
Packit 6c4009
      /* Find structure describing the message catalog matching the
Packit 6c4009
	 DOMAINNAME and CATEGORY.  */
Packit 6c4009
      domain = _nl_find_domain (dirname, single_locale, xdomainname, binding);
Packit 6c4009
Packit 6c4009
      if (domain != NULL)
Packit 6c4009
	{
Packit 6c4009
#if defined IN_LIBGLOCALE
Packit 6c4009
	  retval = _nl_find_msg (domain, binding, encoding, msgid1, &retlen);
Packit 6c4009
#else
Packit 6c4009
	  retval = _nl_find_msg (domain, binding, msgid1, 1, &retlen);
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
	  if (retval == NULL)
Packit 6c4009
	    {
Packit 6c4009
	      int cnt;
Packit 6c4009
Packit 6c4009
	      for (cnt = 0; domain->successor[cnt] != NULL; ++cnt)
Packit 6c4009
		{
Packit 6c4009
#if defined IN_LIBGLOCALE
Packit 6c4009
		  retval = _nl_find_msg (domain->successor[cnt], binding,
Packit 6c4009
					 encoding, msgid1, &retlen);
Packit 6c4009
#else
Packit 6c4009
		  retval = _nl_find_msg (domain->successor[cnt], binding,
Packit 6c4009
					 msgid1, 1, &retlen);
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
		  /* Resource problems are not fatal, instead we return no
Packit 6c4009
		     translation.  */
Packit 6c4009
		  if (__builtin_expect (retval == (char *) -1, 0))
Packit 6c4009
		    goto return_untranslated;
Packit 6c4009
Packit 6c4009
		  if (retval != NULL)
Packit 6c4009
		    {
Packit 6c4009
		      domain = domain->successor[cnt];
Packit 6c4009
		      break;
Packit 6c4009
		    }
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  /* Returning -1 means that some resource problem exists
Packit 6c4009
	     (likely memory) and that the strings could not be
Packit 6c4009
	     converted.  Return the original strings.  */
Packit 6c4009
	  if (__builtin_expect (retval == (char *) -1, 0))
Packit 6c4009
	    break;
Packit 6c4009
Packit 6c4009
	  if (retval != NULL)
Packit 6c4009
	    {
Packit 6c4009
	      /* Found the translation of MSGID1 in domain DOMAIN:
Packit 6c4009
		 starting at RETVAL, RETLEN bytes.  */
Packit 6c4009
	      free (xdirname);
Packit 6c4009
	      FREE_BLOCKS (block_list);
Packit 6c4009
	      if (foundp == NULL)
Packit 6c4009
		{
Packit 6c4009
		  /* Create a new entry and add it to the search tree.  */
Packit 6c4009
		  size_t msgid_len;
Packit 6c4009
		  size_t size;
Packit 6c4009
		  struct known_translation_t *newp;
Packit 6c4009
Packit 6c4009
		  msgid_len = strlen (msgid1) + 1;
Packit 6c4009
		  size = offsetof (struct known_translation_t, msgid)
Packit 6c4009
			 + msgid_len + domainname_len + 1;
Packit 6c4009
#ifdef HAVE_PER_THREAD_LOCALE
Packit 6c4009
		  size += strlen (localename) + 1;
Packit 6c4009
#endif
Packit 6c4009
		  newp = (struct known_translation_t *) malloc (size);
Packit 6c4009
		  if (newp != NULL)
Packit 6c4009
		    {
Packit 6c4009
		      char *new_domainname;
Packit 6c4009
#ifdef HAVE_PER_THREAD_LOCALE
Packit 6c4009
		      char *new_localename;
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
		      new_domainname =
Packit 6c4009
			(char *) mempcpy (newp->msgid.appended, msgid1,
Packit 6c4009
					  msgid_len);
Packit 6c4009
		      memcpy (new_domainname, domainname, domainname_len + 1);
Packit 6c4009
#ifdef HAVE_PER_THREAD_LOCALE
Packit 6c4009
		      new_localename = new_domainname + domainname_len + 1;
Packit 6c4009
		      strcpy (new_localename, localename);
Packit 6c4009
#endif
Packit 6c4009
		      newp->domainname = new_domainname;
Packit 6c4009
		      newp->category = category;
Packit 6c4009
#ifdef HAVE_PER_THREAD_LOCALE
Packit 6c4009
		      newp->localename = new_localename;
Packit 6c4009
#endif
Packit 6c4009
#ifdef IN_LIBGLOCALE
Packit 6c4009
		      newp->encoding = encoding;
Packit 6c4009
#endif
Packit 6c4009
		      newp->counter = _nl_msg_cat_cntr;
Packit 6c4009
		      newp->domain = domain;
Packit 6c4009
		      newp->translation = retval;
Packit 6c4009
		      newp->translation_length = retlen;
Packit 6c4009
Packit 6c4009
		      gl_rwlock_wrlock (tree_lock);
Packit 6c4009
Packit 6c4009
		      /* Insert the entry in the search tree.  */
Packit 6c4009
		      foundp = (struct known_translation_t **)
Packit 6c4009
			tsearch (newp, &root, transcmp);
Packit 6c4009
Packit 6c4009
		      gl_rwlock_unlock (tree_lock);
Packit 6c4009
Packit 6c4009
		      if (foundp == NULL
Packit 6c4009
			  || __builtin_expect (*foundp != newp, 0))
Packit 6c4009
			/* The insert failed.  */
Packit 6c4009
			free (newp);
Packit 6c4009
		    }
Packit 6c4009
		}
Packit 6c4009
	      else
Packit 6c4009
		{
Packit 6c4009
		  /* We can update the existing entry.  */
Packit 6c4009
		  (*foundp)->counter = _nl_msg_cat_cntr;
Packit 6c4009
		  (*foundp)->domain = domain;
Packit 6c4009
		  (*foundp)->translation = retval;
Packit 6c4009
		  (*foundp)->translation_length = retlen;
Packit 6c4009
		}
Packit 6c4009
Packit 6c4009
	      __set_errno (saved_errno);
Packit 6c4009
Packit 6c4009
	      /* Now deal with plural.  */
Packit 6c4009
	      if (plural)
Packit 6c4009
		retval = plural_lookup (domain, n, retval, retlen);
Packit 6c4009
Packit 6c4009
	      gl_rwlock_unlock (_nl_state_lock);
Packit 6c4009
#ifdef _LIBC
Packit 6c4009
	      __libc_rwlock_unlock (__libc_setlocale_lock);
Packit 6c4009
#endif
Packit 6c4009
	      return retval;
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
 return_untranslated:
Packit 6c4009
  /* Return the untranslated MSGID.  */
Packit 6c4009
  free (xdirname);
Packit 6c4009
  FREE_BLOCKS (block_list);
Packit 6c4009
  gl_rwlock_unlock (_nl_state_lock);
Packit 6c4009
#ifdef _LIBC
Packit 6c4009
  __libc_rwlock_unlock (__libc_setlocale_lock);
Packit 6c4009
#endif
Packit 6c4009
#ifndef _LIBC
Packit 6c4009
  if (!ENABLE_SECURE)
Packit 6c4009
    {
Packit 6c4009
      extern void _nl_log_untranslated (const char *logfilename,
Packit 6c4009
					const char *domainname,
Packit 6c4009
					const char *msgid1, const char *msgid2,
Packit 6c4009
					int plural);
Packit 6c4009
      const char *logfilename = getenv ("GETTEXT_LOG_UNTRANSLATED");
Packit 6c4009
Packit 6c4009
      if (logfilename != NULL && logfilename[0] != '\0')
Packit 6c4009
	_nl_log_untranslated (logfilename, domainname, msgid1, msgid2, plural);
Packit 6c4009
    }
Packit 6c4009
#endif
Packit 6c4009
  __set_errno (saved_errno);
Packit 6c4009
  return (plural == 0
Packit 6c4009
	  ? (char *) msgid1
Packit 6c4009
	  /* Use the Germanic plural rule.  */
Packit 6c4009
	  : n == 1 ? (char *) msgid1 : (char *) msgid2);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Look up the translation of msgid within DOMAIN_FILE and DOMAINBINDING.
Packit 6c4009
   Return it if found.  Return NULL if not found or in case of a conversion
Packit 6c4009
   failure (problem in the particular message catalog).  Return (char *) -1
Packit 6c4009
   in case of a memory allocation failure during conversion (only if
Packit 6c4009
   ENCODING != NULL resp. CONVERT == true).  */
Packit 6c4009
char *
Packit 6c4009
#ifdef IN_LIBGLOCALE
Packit 6c4009
_nl_find_msg (struct loaded_l10nfile *domain_file,
Packit 6c4009
	      struct binding *domainbinding, const char *encoding,
Packit 6c4009
	      const char *msgid,
Packit 6c4009
	      size_t *lengthp)
Packit 6c4009
#else
Packit 6c4009
_nl_find_msg (struct loaded_l10nfile *domain_file,
Packit 6c4009
	      struct binding *domainbinding,
Packit 6c4009
	      const char *msgid, int convert,
Packit 6c4009
	      size_t *lengthp)
Packit 6c4009
#endif
Packit 6c4009
{
Packit 6c4009
  struct loaded_domain *domain;
Packit 6c4009
  nls_uint32 nstrings;
Packit 6c4009
  size_t act;
Packit 6c4009
  char *result;
Packit 6c4009
  size_t resultlen;
Packit 6c4009
Packit 6c4009
  if (domain_file->decided <= 0)
Packit 6c4009
    _nl_load_domain (domain_file, domainbinding);
Packit 6c4009
Packit 6c4009
  if (domain_file->data == NULL)
Packit 6c4009
    return NULL;
Packit 6c4009
Packit 6c4009
  domain = (struct loaded_domain *) domain_file->data;
Packit 6c4009
Packit 6c4009
  nstrings = domain->nstrings;
Packit 6c4009
Packit 6c4009
  /* Locate the MSGID and its translation.  */
Packit 6c4009
  if (domain->hash_tab != NULL)
Packit 6c4009
    {
Packit 6c4009
      /* Use the hashing table.  */
Packit 6c4009
      nls_uint32 len = strlen (msgid);
Packit 6c4009
      nls_uint32 hash_val = __hash_string (msgid);
Packit 6c4009
      nls_uint32 idx = hash_val % domain->hash_size;
Packit 6c4009
      nls_uint32 incr = 1 + (hash_val % (domain->hash_size - 2));
Packit 6c4009
Packit 6c4009
      while (1)
Packit 6c4009
	{
Packit 6c4009
	  nls_uint32 nstr =
Packit 6c4009
	    W (domain->must_swap_hash_tab, domain->hash_tab[idx]);
Packit 6c4009
Packit 6c4009
	  if (nstr == 0)
Packit 6c4009
	    /* Hash table entry is empty.  */
Packit 6c4009
	    return NULL;
Packit 6c4009
Packit 6c4009
	  nstr--;
Packit 6c4009
Packit 6c4009
	  /* Compare msgid with the original string at index nstr.
Packit 6c4009
	     We compare the lengths with >=, not ==, because plural entries
Packit 6c4009
	     are represented by strings with an embedded NUL.  */
Packit 6c4009
	  if (nstr < nstrings
Packit 6c4009
	      ? W (domain->must_swap, domain->orig_tab[nstr].length) >= len
Packit 6c4009
		&& (strcmp (msgid,
Packit 6c4009
			    domain->data + W (domain->must_swap,
Packit 6c4009
					      domain->orig_tab[nstr].offset))
Packit 6c4009
		    == 0)
Packit 6c4009
	      : domain->orig_sysdep_tab[nstr - nstrings].length > len
Packit 6c4009
		&& (strcmp (msgid,
Packit 6c4009
			    domain->orig_sysdep_tab[nstr - nstrings].pointer)
Packit 6c4009
		    == 0))
Packit 6c4009
	    {
Packit 6c4009
	      act = nstr;
Packit 6c4009
	      goto found;
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  if (idx >= domain->hash_size - incr)
Packit 6c4009
	    idx -= domain->hash_size - incr;
Packit 6c4009
	  else
Packit 6c4009
	    idx += incr;
Packit 6c4009
	}
Packit 6c4009
      /* NOTREACHED */
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      /* Try the default method:  binary search in the sorted array of
Packit 6c4009
	 messages.  */
Packit 6c4009
      size_t top, bottom;
Packit 6c4009
Packit 6c4009
      bottom = 0;
Packit 6c4009
      top = nstrings;
Packit 6c4009
      while (bottom < top)
Packit 6c4009
	{
Packit 6c4009
	  int cmp_val;
Packit 6c4009
Packit 6c4009
	  act = (bottom + top) / 2;
Packit 6c4009
	  cmp_val = strcmp (msgid, (domain->data
Packit 6c4009
				    + W (domain->must_swap,
Packit 6c4009
					 domain->orig_tab[act].offset)));
Packit 6c4009
	  if (cmp_val < 0)
Packit 6c4009
	    top = act;
Packit 6c4009
	  else if (cmp_val > 0)
Packit 6c4009
	    bottom = act + 1;
Packit 6c4009
	  else
Packit 6c4009
	    goto found;
Packit 6c4009
	}
Packit 6c4009
      /* No translation was found.  */
Packit 6c4009
      return NULL;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
 found:
Packit 6c4009
  /* The translation was found at index ACT.  If we have to convert the
Packit 6c4009
     string to use a different character set, this is the time.  */
Packit 6c4009
  if (act < nstrings)
Packit 6c4009
    {
Packit 6c4009
      result = (char *)
Packit 6c4009
	(domain->data + W (domain->must_swap, domain->trans_tab[act].offset));
Packit 6c4009
      resultlen = W (domain->must_swap, domain->trans_tab[act].length) + 1;
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      result = (char *) domain->trans_sysdep_tab[act - nstrings].pointer;
Packit 6c4009
      resultlen = domain->trans_sysdep_tab[act - nstrings].length;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
#if defined _LIBC || HAVE_ICONV
Packit 6c4009
# ifdef IN_LIBGLOCALE
Packit 6c4009
  if (encoding != NULL)
Packit 6c4009
# else
Packit 6c4009
  if (convert)
Packit 6c4009
# endif
Packit 6c4009
    {
Packit 6c4009
      /* We are supposed to do a conversion.  */
Packit 6c4009
# ifndef IN_LIBGLOCALE
Packit 6c4009
      const char *encoding = get_output_charset (domainbinding);
Packit 6c4009
# endif
Packit 6c4009
      size_t nconversions;
Packit 6c4009
      struct converted_domain *convd;
Packit 6c4009
      size_t i;
Packit 6c4009
Packit 6c4009
      /* Protect against reallocation of the table.  */
Packit 6c4009
      gl_rwlock_rdlock (domain->conversions_lock);
Packit 6c4009
Packit 6c4009
      /* Search whether a table with converted translations for this
Packit 6c4009
	 encoding has already been allocated.  */
Packit 6c4009
      nconversions = domain->nconversions;
Packit 6c4009
      convd = NULL;
Packit 6c4009
Packit 6c4009
      for (i = nconversions; i > 0; )
Packit 6c4009
	{
Packit 6c4009
	  i--;
Packit 6c4009
	  if (strcmp (domain->conversions[i].encoding, encoding) == 0)
Packit 6c4009
	    {
Packit 6c4009
	      convd = &domain->conversions[i];
Packit 6c4009
	      break;
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      gl_rwlock_unlock (domain->conversions_lock);
Packit 6c4009
Packit 6c4009
      if (convd == NULL)
Packit 6c4009
	{
Packit 6c4009
	  /* We have to allocate a new conversions table.  */
Packit 6c4009
	  gl_rwlock_wrlock (domain->conversions_lock);
Packit 6c4009
	  nconversions = domain->nconversions;
Packit 6c4009
Packit 6c4009
	  /* Maybe in the meantime somebody added the translation.
Packit 6c4009
	     Recheck.  */
Packit 6c4009
	  for (i = nconversions; i > 0; )
Packit 6c4009
	    {
Packit 6c4009
	      i--;
Packit 6c4009
	      if (strcmp (domain->conversions[i].encoding, encoding) == 0)
Packit 6c4009
		{
Packit 6c4009
		  convd = &domain->conversions[i];
Packit 6c4009
		  goto found_convd;
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  {
Packit 6c4009
	    /* Allocate a table for the converted translations for this
Packit 6c4009
	       encoding.  */
Packit 6c4009
	    struct converted_domain *new_conversions =
Packit 6c4009
	      (struct converted_domain *)
Packit 6c4009
	      (domain->conversions != NULL
Packit 6c4009
	       ? realloc (domain->conversions,
Packit 6c4009
			  (nconversions + 1) * sizeof (struct converted_domain))
Packit 6c4009
	       : malloc ((nconversions + 1) * sizeof (struct converted_domain)));
Packit 6c4009
Packit 6c4009
	    if (__builtin_expect (new_conversions == NULL, 0))
Packit 6c4009
	      {
Packit 6c4009
		/* Nothing we can do, no more memory.  We cannot use the
Packit 6c4009
		   translation because it might be encoded incorrectly.  */
Packit 6c4009
	      unlock_fail:
Packit 6c4009
		gl_rwlock_unlock (domain->conversions_lock);
Packit 6c4009
		return (char *) -1;
Packit 6c4009
	      }
Packit 6c4009
Packit 6c4009
	    domain->conversions = new_conversions;
Packit 6c4009
Packit 6c4009
	    /* Copy the 'encoding' string to permanent storage.  */
Packit 6c4009
	    encoding = strdup (encoding);
Packit 6c4009
	    if (__builtin_expect (encoding == NULL, 0))
Packit 6c4009
	      /* Nothing we can do, no more memory.  We cannot use the
Packit 6c4009
		 translation because it might be encoded incorrectly.  */
Packit 6c4009
	      goto unlock_fail;
Packit 6c4009
Packit 6c4009
	    convd = &new_conversions[nconversions];
Packit 6c4009
	    convd->encoding = encoding;
Packit 6c4009
Packit 6c4009
	    /* Find out about the character set the file is encoded with.
Packit 6c4009
	       This can be found (in textual form) in the entry "".  If this
Packit 6c4009
	       entry does not exist or if this does not contain the 'charset='
Packit 6c4009
	       information, we will assume the charset matches the one the
Packit 6c4009
	       current locale and we don't have to perform any conversion.  */
Packit 6c4009
# ifdef _LIBC
Packit 6c4009
	    convd->conv = (__gconv_t) -1;
Packit 6c4009
# else
Packit 6c4009
#  if HAVE_ICONV
Packit 6c4009
	    convd->conv = (iconv_t) -1;
Packit 6c4009
#  endif
Packit 6c4009
# endif
Packit 6c4009
	    {
Packit 6c4009
	      char *nullentry;
Packit 6c4009
	      size_t nullentrylen;
Packit 6c4009
Packit 6c4009
	      /* Get the header entry.  This is a recursion, but it doesn't
Packit 6c4009
		 reallocate domain->conversions because we pass
Packit 6c4009
		 encoding = NULL or convert = 0, respectively.  */
Packit 6c4009
	      nullentry =
Packit 6c4009
# ifdef IN_LIBGLOCALE
Packit 6c4009
		_nl_find_msg (domain_file, domainbinding, NULL, "",
Packit 6c4009
			      &nullentrylen);
Packit 6c4009
# else
Packit 6c4009
		_nl_find_msg (domain_file, domainbinding, "", 0, &nullentrylen);
Packit 6c4009
# endif
Packit 6c4009
Packit 6c4009
	      /* Resource problems are fatal.  If we continue onwards we will
Packit 6c4009
	         only attempt to calloc a new conv_tab and fail later.  */
Packit 6c4009
	      if (__builtin_expect (nullentry == (char *) -1, 0))
Packit 6c4009
	        return (char *) -1;
Packit 6c4009
Packit 6c4009
	      if (nullentry != NULL)
Packit 6c4009
		{
Packit 6c4009
		  const char *charsetstr;
Packit 6c4009
Packit 6c4009
		  charsetstr = strstr (nullentry, "charset=");
Packit 6c4009
		  if (charsetstr != NULL)
Packit 6c4009
		    {
Packit 6c4009
		      size_t len;
Packit 6c4009
		      char *charset;
Packit 6c4009
		      const char *outcharset;
Packit 6c4009
Packit 6c4009
		      charsetstr += strlen ("charset=");
Packit 6c4009
		      len = strcspn (charsetstr, " \t\n");
Packit 6c4009
Packit 6c4009
		      charset = (char *) alloca (len + 1);
Packit 6c4009
# if defined _LIBC || HAVE_MEMPCPY
Packit 6c4009
		      *((char *) mempcpy (charset, charsetstr, len)) = '\0';
Packit 6c4009
# else
Packit 6c4009
		      memcpy (charset, charsetstr, len);
Packit 6c4009
		      charset[len] = '\0';
Packit 6c4009
# endif
Packit 6c4009
Packit 6c4009
		      outcharset = encoding;
Packit 6c4009
Packit 6c4009
# ifdef _LIBC
Packit Service b53ea4
Packit Service 0a23a2
		      struct gconv_spec conv_spec;
Packit Service 0a23a2
Packit Service 0a23a2
                      __gconv_create_spec (&conv_spec, charset, outcharset);
Packit Service 0a23a2
Packit Service 0a23a2
		      /* We always want to use transliteration.  */
Packit Service 0a23a2
                      conv_spec.translit = true;
Packit Service 0a23a2
Packit Service b53ea4
		      int r = __gconv_open (&conv_spec, &convd->conv,
Packit Service b53ea4
		                            GCONV_AVOID_NOCONV);
Packit Service 0a23a2
Packit Service 0a23a2
                      __gconv_destroy_spec (&conv_spec);
Packit Service 0a23a2
Packit 6c4009
		      if (__builtin_expect (r != __GCONV_OK, 0))
Packit 6c4009
			{
Packit 6c4009
			  /* If the output encoding is the same there is
Packit 6c4009
			     nothing to do.  Otherwise do not use the
Packit 6c4009
			     translation at all.  */
Packit 6c4009
			  if (__builtin_expect (r != __GCONV_NULCONV, 1))
Packit 6c4009
			    {
Packit 6c4009
			      gl_rwlock_unlock (domain->conversions_lock);
Packit 6c4009
			      free ((char *) encoding);
Packit 6c4009
			      return NULL;
Packit 6c4009
			    }
Packit 6c4009
Packit 6c4009
			  convd->conv = (__gconv_t) -1;
Packit 6c4009
			}
Packit 6c4009
# else
Packit 6c4009
#  if HAVE_ICONV
Packit 6c4009
		      /* When using GNU libc >= 2.2 or GNU libiconv >= 1.5,
Packit 6c4009
			 we want to use transliteration.  */
Packit 6c4009
#   if (((__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2) || __GLIBC__ > 2) \
Packit 6c4009
	&& !defined __UCLIBC__) \
Packit 6c4009
       || _LIBICONV_VERSION >= 0x0105
Packit 6c4009
		      if (strchr (outcharset, '/') == NULL)
Packit 6c4009
			{
Packit 6c4009
			  char *tmp;
Packit 6c4009
Packit 6c4009
			  len = strlen (outcharset);
Packit 6c4009
			  tmp = (char *) alloca (len + 10 + 1);
Packit 6c4009
			  memcpy (tmp, outcharset, len);
Packit 6c4009
			  memcpy (tmp + len, "//TRANSLIT", 10 + 1);
Packit 6c4009
			  outcharset = tmp;
Packit 6c4009
Packit 6c4009
			  convd->conv = iconv_open (outcharset, charset);
Packit 6c4009
Packit 6c4009
			  freea (outcharset);
Packit 6c4009
			}
Packit 6c4009
		      else
Packit 6c4009
#   endif
Packit 6c4009
			convd->conv = iconv_open (outcharset, charset);
Packit 6c4009
#  endif
Packit 6c4009
# endif
Packit 6c4009
Packit 6c4009
		      freea (charset);
Packit 6c4009
		    }
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
	    convd->conv_tab = NULL;
Packit 6c4009
	    /* Here domain->conversions is still == new_conversions.  */
Packit 6c4009
	    domain->nconversions++;
Packit 6c4009
	  }
Packit 6c4009
Packit 6c4009
	found_convd:
Packit 6c4009
	  gl_rwlock_unlock (domain->conversions_lock);
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      if (
Packit 6c4009
# ifdef _LIBC
Packit 6c4009
	  convd->conv != (__gconv_t) -1
Packit 6c4009
# else
Packit 6c4009
#  if HAVE_ICONV
Packit 6c4009
	  convd->conv != (iconv_t) -1
Packit 6c4009
#  endif
Packit 6c4009
# endif
Packit 6c4009
	  )
Packit 6c4009
	{
Packit 6c4009
	  /* We are supposed to do a conversion.  First allocate an
Packit 6c4009
	     appropriate table with the same structure as the table
Packit 6c4009
	     of translations in the file, where we can put the pointers
Packit 6c4009
	     to the converted strings in.
Packit 6c4009
	     There is a slight complication with plural entries.  They
Packit 6c4009
	     are represented by consecutive NUL terminated strings.  We
Packit 6c4009
	     handle this case by converting RESULTLEN bytes, including
Packit 6c4009
	     NULs.  */
Packit 6c4009
Packit 6c4009
	  /* This lock primarily protects the memory management variables
Packit 6c4009
	     freemem, freemem_size.  It also protects write accesses to
Packit 6c4009
	     convd->conv_tab.  It's not worth using a separate lock (such
Packit 6c4009
	     as domain->conversions_lock) for this purpose, because when
Packit 6c4009
	     modifying convd->conv_tab, we also need to lock freemem,
Packit 6c4009
	     freemem_size for most of the time.  */
Packit 6c4009
	  __libc_lock_define_initialized (static, lock)
Packit 6c4009
Packit 6c4009
	  if (__builtin_expect (convd->conv_tab == NULL, 0))
Packit 6c4009
	    {
Packit 6c4009
	      __libc_lock_lock (lock);
Packit 6c4009
	      if (convd->conv_tab == NULL)
Packit 6c4009
		{
Packit 6c4009
		  convd->conv_tab =
Packit 6c4009
		    (char **) calloc (nstrings + domain->n_sysdep_strings,
Packit 6c4009
				      sizeof (char *));
Packit 6c4009
		  if (convd->conv_tab != NULL)
Packit 6c4009
		    goto not_translated_yet;
Packit 6c4009
		  /* Mark that we didn't succeed allocating a table.  */
Packit 6c4009
		  convd->conv_tab = (char **) -1;
Packit 6c4009
		}
Packit 6c4009
	      __libc_lock_unlock (lock);
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  if (__builtin_expect (convd->conv_tab == (char **) -1, 0))
Packit 6c4009
	    /* Nothing we can do, no more memory.  We cannot use the
Packit 6c4009
	       translation because it might be encoded incorrectly.  */
Packit 6c4009
	    return (char *) -1;
Packit 6c4009
Packit 6c4009
	  if (convd->conv_tab[act] == NULL)
Packit 6c4009
	    {
Packit 6c4009
	      /* We haven't used this string so far, so it is not
Packit 6c4009
		 translated yet.  Do this now.  */
Packit 6c4009
	      /* We use a bit more efficient memory handling.
Packit 6c4009
		 We allocate always larger blocks which get used over
Packit 6c4009
		 time.  This is faster than many small allocations.   */
Packit 6c4009
# define INITIAL_BLOCK_SIZE	4080
Packit 6c4009
	      static unsigned char *freemem;
Packit 6c4009
	      static size_t freemem_size;
Packit 6c4009
Packit 6c4009
	      const unsigned char *inbuf;
Packit 6c4009
	      unsigned char *outbuf;
Packit 6c4009
	      int malloc_count;
Packit 6c4009
# ifndef _LIBC
Packit 6c4009
	      transmem_block_t *transmem_list;
Packit 6c4009
# endif
Packit 6c4009
Packit 6c4009
	      __libc_lock_lock (lock);
Packit 6c4009
	    not_translated_yet:
Packit 6c4009
Packit 6c4009
	      inbuf = (const unsigned char *) result;
Packit 6c4009
	      outbuf = freemem + sizeof (size_t);
Packit 6c4009
# ifndef _LIBC
Packit 6c4009
	      transmem_list = NULL;
Packit 6c4009
# endif
Packit 6c4009
Packit 6c4009
	      malloc_count = 0;
Packit 6c4009
	      while (1)
Packit 6c4009
		{
Packit 6c4009
		  transmem_block_t *newmem;
Packit 6c4009
# ifdef _LIBC
Packit 6c4009
		  size_t non_reversible;
Packit 6c4009
		  int res;
Packit 6c4009
Packit 6c4009
		  if (freemem_size < sizeof (size_t))
Packit 6c4009
		    goto resize_freemem;
Packit 6c4009
Packit 6c4009
		  res = __gconv (convd->conv,
Packit 6c4009
				 &inbuf, inbuf + resultlen,
Packit 6c4009
				 &outbuf,
Packit 6c4009
				 outbuf + freemem_size - sizeof (size_t),
Packit 6c4009
				 &non_reversible);
Packit 6c4009
Packit 6c4009
		  if (res == __GCONV_OK || res == __GCONV_EMPTY_INPUT)
Packit 6c4009
		    break;
Packit 6c4009
Packit 6c4009
		  if (res != __GCONV_FULL_OUTPUT)
Packit 6c4009
		    {
Packit 6c4009
		      /* We should not use the translation at all, it
Packit 6c4009
			 is incorrectly encoded.  */
Packit 6c4009
		      __libc_lock_unlock (lock);
Packit 6c4009
		      return NULL;
Packit 6c4009
		    }
Packit 6c4009
Packit 6c4009
		  inbuf = (const unsigned char *) result;
Packit 6c4009
# else
Packit 6c4009
#  if HAVE_ICONV
Packit 6c4009
		  const char *inptr = (const char *) inbuf;
Packit 6c4009
		  size_t inleft = resultlen;
Packit 6c4009
		  char *outptr = (char *) outbuf;
Packit 6c4009
		  size_t outleft;
Packit 6c4009
Packit 6c4009
		  if (freemem_size < sizeof (size_t))
Packit 6c4009
		    goto resize_freemem;
Packit 6c4009
Packit 6c4009
		  outleft = freemem_size - sizeof (size_t);
Packit 6c4009
		  if (iconv (convd->conv,
Packit 6c4009
			     (ICONV_CONST char **) &inptr, &inleft,
Packit 6c4009
			     &outptr, &outleft)
Packit 6c4009
		      != (size_t) (-1))
Packit 6c4009
		    {
Packit 6c4009
		      outbuf = (unsigned char *) outptr;
Packit 6c4009
		      break;
Packit 6c4009
		    }
Packit 6c4009
		  if (errno != E2BIG)
Packit 6c4009
		    {
Packit 6c4009
		      __libc_lock_unlock (lock);
Packit 6c4009
		      return NULL;
Packit 6c4009
		    }
Packit 6c4009
#  endif
Packit 6c4009
# endif
Packit 6c4009
Packit 6c4009
		resize_freemem:
Packit 6c4009
		  /* We must allocate a new buffer or resize the old one.  */
Packit 6c4009
		  if (malloc_count > 0)
Packit 6c4009
		    {
Packit 6c4009
		      ++malloc_count;
Packit 6c4009
		      freemem_size = malloc_count * INITIAL_BLOCK_SIZE;
Packit 6c4009
		      newmem = (transmem_block_t *) realloc (transmem_list,
Packit 6c4009
							     freemem_size);
Packit 6c4009
# ifdef _LIBC
Packit 6c4009
		      if (newmem != NULL)
Packit 6c4009
			transmem_list = newmem;
Packit 6c4009
		      else
Packit 6c4009
			{
Packit 6c4009
			  struct transmem_list *old = transmem_list;
Packit 6c4009
Packit 6c4009
			  transmem_list = transmem_list->next;
Packit 6c4009
			  free (old);
Packit 6c4009
			}
Packit 6c4009
# endif
Packit 6c4009
		    }
Packit 6c4009
		  else
Packit 6c4009
		    {
Packit 6c4009
		      malloc_count = 1;
Packit 6c4009
		      freemem_size = INITIAL_BLOCK_SIZE;
Packit 6c4009
		      newmem = (transmem_block_t *) malloc (freemem_size);
Packit 6c4009
# ifdef _LIBC
Packit 6c4009
		      if (newmem != NULL)
Packit 6c4009
			{
Packit 6c4009
			  /* Add the block to the list of blocks we have to free
Packit 6c4009
			     at some point.  */
Packit 6c4009
			  newmem->next = transmem_list;
Packit 6c4009
			  transmem_list = newmem;
Packit 6c4009
			}
Packit 6c4009
		      /* Fall through and return -1.  */
Packit 6c4009
# endif
Packit 6c4009
		    }
Packit 6c4009
		  if (__builtin_expect (newmem == NULL, 0))
Packit 6c4009
		    {
Packit 6c4009
		      freemem = NULL;
Packit 6c4009
		      freemem_size = 0;
Packit 6c4009
		      __libc_lock_unlock (lock);
Packit 6c4009
		      return (char *) -1;
Packit 6c4009
		    }
Packit 6c4009
Packit 6c4009
# ifdef _LIBC
Packit 6c4009
		  freemem = (unsigned char *) newmem->data;
Packit 6c4009
		  freemem_size -= offsetof (struct transmem_list, data);
Packit 6c4009
# else
Packit 6c4009
		  transmem_list = newmem;
Packit 6c4009
		  freemem = newmem;
Packit 6c4009
# endif
Packit 6c4009
Packit 6c4009
		  outbuf = freemem + sizeof (size_t);
Packit 6c4009
		}
Packit 6c4009
Packit 6c4009
	      /* We have now in our buffer a converted string.  Put this
Packit 6c4009
		 into the table of conversions.  */
Packit 6c4009
	      *(size_t *) freemem = outbuf - freemem - sizeof (size_t);
Packit 6c4009
	      convd->conv_tab[act] = (char *) freemem;
Packit 6c4009
	      /* Shrink freemem, but keep it aligned.  */
Packit 6c4009
	      freemem_size -= outbuf - freemem;
Packit 6c4009
	      freemem = outbuf;
Packit 6c4009
	      freemem += freemem_size & (alignof (size_t) - 1);
Packit 6c4009
	      freemem_size = freemem_size & ~ (alignof (size_t) - 1);
Packit 6c4009
Packit 6c4009
	      __libc_lock_unlock (lock);
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  /* Now convd->conv_tab[act] contains the translation of all
Packit 6c4009
	     the plural variants.  */
Packit 6c4009
	  result = convd->conv_tab[act] + sizeof (size_t);
Packit 6c4009
	  resultlen = *(size_t *) convd->conv_tab[act];
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* The result string is converted.  */
Packit 6c4009
Packit 6c4009
#endif /* _LIBC || HAVE_ICONV */
Packit 6c4009
Packit 6c4009
  *lengthp = resultlen;
Packit 6c4009
  return result;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Look up a plural variant.  */
Packit 6c4009
static char *
Packit 6c4009
plural_lookup (struct loaded_l10nfile *domain, unsigned long int n,
Packit 6c4009
	       const char *translation, size_t translation_len)
Packit 6c4009
{
Packit 6c4009
  struct loaded_domain *domaindata = (struct loaded_domain *) domain->data;
Packit 6c4009
  unsigned long int index;
Packit 6c4009
  const char *p;
Packit 6c4009
Packit 6c4009
  index = plural_eval (domaindata->plural, n);
Packit 6c4009
  if (index >= domaindata->nplurals)
Packit 6c4009
    /* This should never happen.  It means the plural expression and the
Packit 6c4009
       given maximum value do not match.  */
Packit 6c4009
    index = 0;
Packit 6c4009
Packit 6c4009
  /* Skip INDEX strings at TRANSLATION.  */
Packit 6c4009
  p = translation;
Packit 6c4009
  while (index-- > 0)
Packit 6c4009
    {
Packit 6c4009
#ifdef _LIBC
Packit 6c4009
      p = __rawmemchr (p, '\0');
Packit 6c4009
#else
Packit 6c4009
      p = strchr (p, '\0');
Packit 6c4009
#endif
Packit 6c4009
      /* And skip over the NUL byte.  */
Packit 6c4009
      p++;
Packit 6c4009
Packit 6c4009
      if (p >= translation + translation_len)
Packit 6c4009
	/* This should never happen.  It means the plural expression
Packit 6c4009
	   evaluated to a value larger than the number of variants
Packit 6c4009
	   available for MSGID1.  */
Packit 6c4009
	return (char *) translation;
Packit 6c4009
    }
Packit 6c4009
  return (char *) p;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
#ifndef _LIBC
Packit 6c4009
/* Return string representation of locale CATEGORY.  */
Packit 6c4009
static const char *
Packit 6c4009
category_to_name (int category)
Packit 6c4009
{
Packit 6c4009
  const char *retval;
Packit 6c4009
Packit 6c4009
  switch (category)
Packit 6c4009
  {
Packit 6c4009
#ifdef LC_COLLATE
Packit 6c4009
  case LC_COLLATE:
Packit 6c4009
    retval = "LC_COLLATE";
Packit 6c4009
    break;
Packit 6c4009
#endif
Packit 6c4009
#ifdef LC_CTYPE
Packit 6c4009
  case LC_CTYPE:
Packit 6c4009
    retval = "LC_CTYPE";
Packit 6c4009
    break;
Packit 6c4009
#endif
Packit 6c4009
#ifdef LC_MONETARY
Packit 6c4009
  case LC_MONETARY:
Packit 6c4009
    retval = "LC_MONETARY";
Packit 6c4009
    break;
Packit 6c4009
#endif
Packit 6c4009
#ifdef LC_NUMERIC
Packit 6c4009
  case LC_NUMERIC:
Packit 6c4009
    retval = "LC_NUMERIC";
Packit 6c4009
    break;
Packit 6c4009
#endif
Packit 6c4009
#ifdef LC_TIME
Packit 6c4009
  case LC_TIME:
Packit 6c4009
    retval = "LC_TIME";
Packit 6c4009
    break;
Packit 6c4009
#endif
Packit 6c4009
#ifdef LC_MESSAGES
Packit 6c4009
  case LC_MESSAGES:
Packit 6c4009
    retval = "LC_MESSAGES";
Packit 6c4009
    break;
Packit 6c4009
#endif
Packit 6c4009
#ifdef LC_RESPONSE
Packit 6c4009
  case LC_RESPONSE:
Packit 6c4009
    retval = "LC_RESPONSE";
Packit 6c4009
    break;
Packit 6c4009
#endif
Packit 6c4009
#ifdef LC_ALL
Packit 6c4009
  case LC_ALL:
Packit 6c4009
    /* This might not make sense but is perhaps better than any other
Packit 6c4009
       value.  */
Packit 6c4009
    retval = "LC_ALL";
Packit 6c4009
    break;
Packit 6c4009
#endif
Packit 6c4009
  default:
Packit 6c4009
    /* If you have a better idea for a default value let me know.  */
Packit 6c4009
    retval = "LC_XXX";
Packit 6c4009
  }
Packit 6c4009
Packit 6c4009
  return retval;
Packit 6c4009
}
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* Guess value of current locale from value of the environment variables
Packit 6c4009
   or system-dependent defaults.  */
Packit 6c4009
static const char *
Packit 6c4009
#ifdef IN_LIBGLOCALE
Packit 6c4009
guess_category_value (int category, const char *categoryname,
Packit 6c4009
		      const char *locale)
Packit 6c4009
Packit 6c4009
#else
Packit 6c4009
guess_category_value (int category, const char *categoryname)
Packit 6c4009
#endif
Packit 6c4009
{
Packit 6c4009
  const char *language;
Packit 6c4009
#ifndef IN_LIBGLOCALE
Packit 6c4009
  const char *locale;
Packit 6c4009
# ifndef _LIBC
Packit 6c4009
  const char *language_default;
Packit 6c4009
  int locale_defaulted;
Packit 6c4009
# endif
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  /* We use the settings in the following order:
Packit 6c4009
     1. The value of the environment variable 'LANGUAGE'.  This is a GNU
Packit 6c4009
        extension.  Its value can be a colon-separated list of locale names.
Packit 6c4009
     2. The value of the environment variable 'LC_ALL', 'LC_xxx', or 'LANG'.
Packit 6c4009
        More precisely, the first among these that is set to a non-empty value.
Packit 6c4009
        This is how POSIX specifies it.  The value is a single locale name.
Packit 6c4009
     3. A system-dependent preference list of languages.  Its value can be a
Packit 6c4009
        colon-separated list of locale names.
Packit 6c4009
     4. A system-dependent default locale name.
Packit 6c4009
     This way:
Packit 6c4009
       - System-dependent settings can be overridden by environment variables.
Packit 6c4009
       - If the system provides both a list of languages and a default locale,
Packit 6c4009
         the former is used.  */
Packit 6c4009
Packit 6c4009
#ifndef IN_LIBGLOCALE
Packit 6c4009
  /* Fetch the locale name, through the POSIX method of looking to `LC_ALL',
Packit 6c4009
     `LC_xxx', and `LANG'.  On some systems this can be done by the
Packit 6c4009
     `setlocale' function itself.  */
Packit 6c4009
# ifdef _LIBC
Packit 6c4009
  locale = __current_locale_name (category);
Packit 6c4009
# else
Packit 6c4009
  locale_defaulted = 0;
Packit 6c4009
#  if HAVE_USELOCALE
Packit 6c4009
  locale = _nl_locale_name_thread_unsafe (category, categoryname);
Packit 6c4009
  if (locale == NULL)
Packit 6c4009
#  endif
Packit 6c4009
    {
Packit 6c4009
      locale = _nl_locale_name_posix (category, categoryname);
Packit 6c4009
      if (locale == NULL)
Packit 6c4009
	{
Packit 6c4009
	  locale = _nl_locale_name_default ();
Packit 6c4009
	  locale_defaulted = 1;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
# endif
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  /* Ignore LANGUAGE and its system-dependent analogon if the locale is set
Packit 6c4009
     to "C" because
Packit 6c4009
     1. "C" locale usually uses the ASCII encoding, and most international
Packit 6c4009
	messages use non-ASCII characters. These characters get displayed
Packit 6c4009
	as question marks (if using glibc's iconv()) or as invalid 8-bit
Packit 6c4009
	characters (because other iconv()s refuse to convert most non-ASCII
Packit 6c4009
	characters to ASCII). In any case, the output is ugly.
Packit 6c4009
     2. The precise output of some programs in the "C" locale is specified
Packit 6c4009
	by POSIX and should not depend on environment variables like
Packit 6c4009
	"LANGUAGE" or system-dependent information.  We allow such programs
Packit 6c4009
        to use gettext().  */
Packit 6c4009
  if (strcmp (locale, "C") == 0)
Packit 6c4009
    return locale;
Packit 6c4009
Packit 6c4009
  /* The highest priority value is the value of the 'LANGUAGE' environment
Packit 6c4009
     variable.  */
Packit 6c4009
  language = getenv ("LANGUAGE");
Packit 6c4009
  if (language != NULL && language[0] != '\0')
Packit 6c4009
    return language;
Packit 6c4009
#if !defined IN_LIBGLOCALE && !defined _LIBC
Packit 6c4009
  /* The next priority value is the locale name, if not defaulted.  */
Packit 6c4009
  if (locale_defaulted)
Packit 6c4009
    {
Packit 6c4009
      /* The next priority value is the default language preferences list. */
Packit 6c4009
      language_default = _nl_language_preferences_default ();
Packit 6c4009
      if (language_default != NULL)
Packit 6c4009
        return language_default;
Packit 6c4009
    }
Packit 6c4009
  /* The least priority value is the locale name, if defaulted.  */
Packit 6c4009
#endif
Packit 6c4009
  return locale;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
#if (defined _LIBC || HAVE_ICONV) && !defined IN_LIBGLOCALE
Packit 6c4009
/* Returns the output charset.  */
Packit 6c4009
static const char *
Packit 6c4009
get_output_charset (struct binding *domainbinding)
Packit 6c4009
{
Packit 6c4009
  /* The output charset should normally be determined by the locale.  But
Packit 6c4009
     sometimes the locale is not used or not correctly set up, so we provide
Packit 6c4009
     a possibility for the user to override this: the OUTPUT_CHARSET
Packit 6c4009
     environment variable.  Moreover, the value specified through
Packit 6c4009
     bind_textdomain_codeset overrides both.  */
Packit 6c4009
  if (domainbinding != NULL && domainbinding->codeset != NULL)
Packit 6c4009
    return domainbinding->codeset;
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      /* For speed reasons, we look at the value of OUTPUT_CHARSET only
Packit 6c4009
	 once.  This is a user variable that is not supposed to change
Packit 6c4009
	 during a program run.  */
Packit 6c4009
      static char *output_charset_cache;
Packit 6c4009
      static int output_charset_cached;
Packit 6c4009
Packit 6c4009
      if (!output_charset_cached)
Packit 6c4009
	{
Packit 6c4009
	  const char *value = getenv ("OUTPUT_CHARSET");
Packit 6c4009
Packit 6c4009
	  if (value != NULL && value[0] != '\0')
Packit 6c4009
	    {
Packit 6c4009
	      size_t len = strlen (value) + 1;
Packit 6c4009
	      char *value_copy = (char *) malloc (len);
Packit 6c4009
Packit 6c4009
	      if (value_copy != NULL)
Packit 6c4009
		memcpy (value_copy, value, len);
Packit 6c4009
	      output_charset_cache = value_copy;
Packit 6c4009
	    }
Packit 6c4009
	  output_charset_cached = 1;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      if (output_charset_cache != NULL)
Packit 6c4009
	return output_charset_cache;
Packit 6c4009
      else
Packit 6c4009
	{
Packit 6c4009
# ifdef _LIBC
Packit 6c4009
	  return _NL_CURRENT (LC_CTYPE, CODESET);
Packit 6c4009
# else
Packit 6c4009
#  if HAVE_ICONV
Packit 6c4009
	  return locale_charset ();
Packit 6c4009
#  endif
Packit 6c4009
# endif
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
#endif
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
Packit 6c4009
Packit 6c4009
#if !_LIBC && !HAVE_MEMPCPY
Packit 6c4009
static void *
Packit 6c4009
mempcpy (void *dest, const void *src, size_t n)
Packit 6c4009
{
Packit 6c4009
  return (void *) ((char *) memcpy (dest, src, n) + n);
Packit 6c4009
}
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#if !_LIBC && !HAVE_TSEARCH
Packit 6c4009
# include "tsearch.c"
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
Packit 6c4009
#ifdef _LIBC
Packit 6c4009
/* If we want to free all resources we have to do some work at
Packit 6c4009
   program's end.  */
Packit 6c4009
libc_freeres_fn (free_mem)
Packit 6c4009
{
Packit 6c4009
  void *old;
Packit 6c4009
Packit 6c4009
  while (_nl_domain_bindings != NULL)
Packit 6c4009
    {
Packit 6c4009
      struct binding *oldp = _nl_domain_bindings;
Packit 6c4009
      _nl_domain_bindings = _nl_domain_bindings->next;
Packit 6c4009
      if (oldp->dirname != _nl_default_dirname)
Packit 6c4009
	/* Yes, this is a pointer comparison.  */
Packit 6c4009
	free (oldp->dirname);
Packit 6c4009
      free (oldp->codeset);
Packit 6c4009
      free (oldp);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (_nl_current_default_domain != _nl_default_default_domain)
Packit 6c4009
    /* Yes, again a pointer comparison.  */
Packit 6c4009
    free ((char *) _nl_current_default_domain);
Packit 6c4009
Packit 6c4009
  /* Remove the search tree with the known translations.  */
Packit 6c4009
  __tdestroy (root, free);
Packit 6c4009
  root = NULL;
Packit 6c4009
Packit 6c4009
  while (transmem_list != NULL)
Packit 6c4009
    {
Packit 6c4009
      old = transmem_list;
Packit 6c4009
      transmem_list = transmem_list->next;
Packit 6c4009
      free (old);
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
#endif