Blame intl/bindtextdom.c

Packit 6c4009
/* Implementation of the bindtextdomain(3) 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
#ifdef HAVE_CONFIG_H
Packit 6c4009
# include <config.h>
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#include <stddef.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
Packit 6c4009
#include "gettextP.h"
Packit 6c4009
#ifdef _LIBC
Packit 6c4009
# include <libintl.h>
Packit 6c4009
#else
Packit 6c4009
# include "libgnuintl.h"
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* Handle multi-threaded applications.  */
Packit 6c4009
#ifdef _LIBC
Packit 6c4009
# include <libc-lock.h>
Packit 6c4009
# define gl_rwlock_define __libc_rwlock_define
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
/* 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
/* Lock variable to protect the global data in the gettext implementation.  */
Packit 6c4009
gl_rwlock_define (extern, _nl_state_lock attribute_hidden)
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 BINDTEXTDOMAIN __bindtextdomain
Packit 6c4009
# define BIND_TEXTDOMAIN_CODESET __bind_textdomain_codeset
Packit 6c4009
# ifndef strdup
Packit 6c4009
#  define strdup(str) __strdup (str)
Packit 6c4009
# endif
Packit 6c4009
#else
Packit 6c4009
# define BINDTEXTDOMAIN libintl_bindtextdomain
Packit 6c4009
# define BIND_TEXTDOMAIN_CODESET libintl_bind_textdomain_codeset
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* Specifies the directory name *DIRNAMEP and the output codeset *CODESETP
Packit 6c4009
   to be used for the DOMAINNAME message catalog.
Packit 6c4009
   If *DIRNAMEP or *CODESETP is NULL, the corresponding attribute is not
Packit 6c4009
   modified, only the current value is returned.
Packit 6c4009
   If DIRNAMEP or CODESETP is NULL, the corresponding attribute is neither
Packit 6c4009
   modified nor returned.  */
Packit 6c4009
static void
Packit 6c4009
set_binding_values (const char *domainname,
Packit 6c4009
		    const char **dirnamep, const char **codesetp)
Packit 6c4009
{
Packit 6c4009
  struct binding *binding;
Packit 6c4009
  int modified;
Packit 6c4009
Packit 6c4009
  /* Some sanity checks.  */
Packit 6c4009
  if (domainname == NULL || domainname[0] == '\0')
Packit 6c4009
    {
Packit 6c4009
      if (dirnamep)
Packit 6c4009
	*dirnamep = NULL;
Packit 6c4009
      if (codesetp)
Packit 6c4009
	*codesetp = NULL;
Packit 6c4009
      return;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  gl_rwlock_wrlock (_nl_state_lock);
Packit 6c4009
Packit 6c4009
  modified = 0;
Packit 6c4009
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
    {
Packit 6c4009
      if (dirnamep)
Packit 6c4009
	{
Packit 6c4009
	  const char *dirname = *dirnamep;
Packit 6c4009
Packit 6c4009
	  if (dirname == NULL)
Packit 6c4009
	    /* The current binding has be to returned.  */
Packit 6c4009
	    *dirnamep = binding->dirname;
Packit 6c4009
	  else
Packit 6c4009
	    {
Packit 6c4009
	      /* The domain is already bound.  If the new value and the old
Packit 6c4009
		 one are equal we simply do nothing.  Otherwise replace the
Packit 6c4009
		 old binding.  */
Packit 6c4009
	      char *result = binding->dirname;
Packit 6c4009
	      if (strcmp (dirname, result) != 0)
Packit 6c4009
		{
Packit 6c4009
		  if (strcmp (dirname, _nl_default_dirname) == 0)
Packit 6c4009
		    result = (char *) _nl_default_dirname;
Packit 6c4009
		  else
Packit 6c4009
		    {
Packit 6c4009
#if defined _LIBC || defined HAVE_STRDUP
Packit 6c4009
		      result = strdup (dirname);
Packit 6c4009
#else
Packit 6c4009
		      size_t len = strlen (dirname) + 1;
Packit 6c4009
		      result = (char *) malloc (len);
Packit 6c4009
		      if (__builtin_expect (result != NULL, 1))
Packit 6c4009
			memcpy (result, dirname, len);
Packit 6c4009
#endif
Packit 6c4009
		    }
Packit 6c4009
Packit 6c4009
		  if (__builtin_expect (result != NULL, 1))
Packit 6c4009
		    {
Packit 6c4009
		      if (binding->dirname != _nl_default_dirname)
Packit 6c4009
			free (binding->dirname);
Packit 6c4009
Packit 6c4009
		      binding->dirname = result;
Packit 6c4009
		      modified = 1;
Packit 6c4009
		    }
Packit 6c4009
		}
Packit 6c4009
	      *dirnamep = result;
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      if (codesetp)
Packit 6c4009
	{
Packit 6c4009
	  const char *codeset = *codesetp;
Packit 6c4009
Packit 6c4009
	  if (codeset == NULL)
Packit 6c4009
	    /* The current binding has be to returned.  */
Packit 6c4009
	    *codesetp = binding->codeset;
Packit 6c4009
	  else
Packit 6c4009
	    {
Packit 6c4009
	      /* The domain is already bound.  If the new value and the old
Packit 6c4009
		 one are equal we simply do nothing.  Otherwise replace the
Packit 6c4009
		 old binding.  */
Packit 6c4009
	      char *result = binding->codeset;
Packit 6c4009
	      if (result == NULL || strcmp (codeset, result) != 0)
Packit 6c4009
		{
Packit 6c4009
#if defined _LIBC || defined HAVE_STRDUP
Packit 6c4009
		  result = strdup (codeset);
Packit 6c4009
#else
Packit 6c4009
		  size_t len = strlen (codeset) + 1;
Packit 6c4009
		  result = (char *) malloc (len);
Packit 6c4009
		  if (__builtin_expect (result != NULL, 1))
Packit 6c4009
		    memcpy (result, codeset, len);
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
		  if (__builtin_expect (result != NULL, 1))
Packit 6c4009
		    {
Packit 6c4009
		      free (binding->codeset);
Packit 6c4009
Packit 6c4009
		      binding->codeset = result;
Packit 6c4009
		      modified = 1;
Packit 6c4009
		    }
Packit 6c4009
		}
Packit 6c4009
	      *codesetp = result;
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
  else if ((dirnamep == NULL || *dirnamep == NULL)
Packit 6c4009
	   && (codesetp == NULL || *codesetp == NULL))
Packit 6c4009
    {
Packit 6c4009
      /* Simply return the default values.  */
Packit 6c4009
      if (dirnamep)
Packit 6c4009
	*dirnamep = _nl_default_dirname;
Packit 6c4009
      if (codesetp)
Packit 6c4009
	*codesetp = NULL;
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      /* We have to create a new binding.  */
Packit 6c4009
      size_t len = strlen (domainname) + 1;
Packit 6c4009
      struct binding *new_binding =
Packit 6c4009
	(struct binding *) malloc (offsetof (struct binding, domainname) + len);
Packit 6c4009
Packit 6c4009
      if (__builtin_expect (new_binding == NULL, 0))
Packit 6c4009
	goto failed;
Packit 6c4009
Packit 6c4009
      memcpy (new_binding->domainname, domainname, len);
Packit 6c4009
Packit 6c4009
      if (dirnamep)
Packit 6c4009
	{
Packit 6c4009
	  const char *dirname = *dirnamep;
Packit 6c4009
Packit 6c4009
	  if (dirname == NULL)
Packit 6c4009
	    /* The default value.  */
Packit 6c4009
	    dirname = _nl_default_dirname;
Packit 6c4009
	  else
Packit 6c4009
	    {
Packit 6c4009
	      if (strcmp (dirname, _nl_default_dirname) == 0)
Packit 6c4009
		dirname = _nl_default_dirname;
Packit 6c4009
	      else
Packit 6c4009
		{
Packit 6c4009
		  char *result;
Packit 6c4009
#if defined _LIBC || defined HAVE_STRDUP
Packit 6c4009
		  result = strdup (dirname);
Packit 6c4009
		  if (__builtin_expect (result == NULL, 0))
Packit 6c4009
		    goto failed_dirname;
Packit 6c4009
#else
Packit 6c4009
		  size_t len = strlen (dirname) + 1;
Packit 6c4009
		  result = (char *) malloc (len);
Packit 6c4009
		  if (__builtin_expect (result == NULL, 0))
Packit 6c4009
		    goto failed_dirname;
Packit 6c4009
		  memcpy (result, dirname, len);
Packit 6c4009
#endif
Packit 6c4009
		  dirname = result;
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
	  *dirnamep = dirname;
Packit 6c4009
	  new_binding->dirname = (char *) dirname;
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
	/* The default value.  */
Packit 6c4009
	new_binding->dirname = (char *) _nl_default_dirname;
Packit 6c4009
Packit 6c4009
      if (codesetp)
Packit 6c4009
	{
Packit 6c4009
	  const char *codeset = *codesetp;
Packit 6c4009
Packit 6c4009
	  if (codeset != NULL)
Packit 6c4009
	    {
Packit 6c4009
	      char *result;
Packit 6c4009
Packit 6c4009
#if defined _LIBC || defined HAVE_STRDUP
Packit 6c4009
	      result = strdup (codeset);
Packit 6c4009
	      if (__builtin_expect (result == NULL, 0))
Packit 6c4009
		goto failed_codeset;
Packit 6c4009
#else
Packit 6c4009
	      size_t len = strlen (codeset) + 1;
Packit 6c4009
	      result = (char *) malloc (len);
Packit 6c4009
	      if (__builtin_expect (result == NULL, 0))
Packit 6c4009
		goto failed_codeset;
Packit 6c4009
	      memcpy (result, codeset, len);
Packit 6c4009
#endif
Packit 6c4009
	      codeset = result;
Packit 6c4009
	    }
Packit 6c4009
	  *codesetp = codeset;
Packit 6c4009
	  new_binding->codeset = (char *) codeset;
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
	new_binding->codeset = NULL;
Packit 6c4009
Packit 6c4009
      /* Now enqueue it.  */
Packit 6c4009
      if (_nl_domain_bindings == NULL
Packit 6c4009
	  || strcmp (domainname, _nl_domain_bindings->domainname) < 0)
Packit 6c4009
	{
Packit 6c4009
	  new_binding->next = _nl_domain_bindings;
Packit 6c4009
	  _nl_domain_bindings = new_binding;
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
	{
Packit 6c4009
	  binding = _nl_domain_bindings;
Packit 6c4009
	  while (binding->next != NULL
Packit 6c4009
		 && strcmp (domainname, binding->next->domainname) > 0)
Packit 6c4009
	    binding = binding->next;
Packit 6c4009
Packit 6c4009
	  new_binding->next = binding->next;
Packit 6c4009
	  binding->next = new_binding;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      modified = 1;
Packit 6c4009
Packit 6c4009
      /* Here we deal with memory allocation failures.  */
Packit 6c4009
      if (0)
Packit 6c4009
	{
Packit 6c4009
	failed_codeset:
Packit 6c4009
	  if (new_binding->dirname != _nl_default_dirname)
Packit 6c4009
	    free (new_binding->dirname);
Packit 6c4009
	failed_dirname:
Packit 6c4009
	  free (new_binding);
Packit 6c4009
	failed:
Packit 6c4009
	  if (dirnamep)
Packit 6c4009
	    *dirnamep = NULL;
Packit 6c4009
	  if (codesetp)
Packit 6c4009
	    *codesetp = NULL;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* If we modified any binding, we flush the caches.  */
Packit 6c4009
  if (modified)
Packit 6c4009
    ++_nl_msg_cat_cntr;
Packit 6c4009
Packit 6c4009
  gl_rwlock_unlock (_nl_state_lock);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Specify that the DOMAINNAME message catalog will be found
Packit 6c4009
   in DIRNAME rather than in the system locale data base.  */
Packit 6c4009
char *
Packit 6c4009
BINDTEXTDOMAIN (const char *domainname, const char *dirname)
Packit 6c4009
{
Packit 6c4009
  set_binding_values (domainname, &dirname, NULL);
Packit 6c4009
  return (char *) dirname;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Specify the character encoding in which the messages from the
Packit 6c4009
   DOMAINNAME message catalog will be returned.  */
Packit 6c4009
char *
Packit 6c4009
BIND_TEXTDOMAIN_CODESET (const char *domainname, const char *codeset)
Packit 6c4009
{
Packit 6c4009
  set_binding_values (domainname, NULL, &codeset);
Packit 6c4009
  return (char *) codeset;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
#ifdef _LIBC
Packit 6c4009
/* Aliases for function names in GNU C Library.  */
Packit 6c4009
weak_alias (__bindtextdomain, bindtextdomain);
Packit 6c4009
weak_alias (__bind_textdomain_codeset, bind_textdomain_codeset);
Packit 6c4009
#endif