(root)/
glibc-2.38/
intl/
bindtextdom.c
       1  /* Implementation of the bindtextdomain(3) function
       2     Copyright (C) 1995-2023 Free Software Foundation, Inc.
       3  
       4     This program is free software: you can redistribute it and/or modify
       5     it under the terms of the GNU Lesser General Public License as published by
       6     the Free Software Foundation; either version 2.1 of the License, or
       7     (at your option) any later version.
       8  
       9     This program is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12     GNU Lesser General Public License for more details.
      13  
      14     You should have received a copy of the GNU Lesser General Public License
      15     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      16  
      17  #ifdef HAVE_CONFIG_H
      18  # include <config.h>
      19  #endif
      20  
      21  #include <stddef.h>
      22  #include <stdlib.h>
      23  #include <string.h>
      24  
      25  #include "gettextP.h"
      26  #ifdef _LIBC
      27  # include <libintl.h>
      28  #else
      29  # include "libgnuintl.h"
      30  #endif
      31  
      32  /* Handle multi-threaded applications.  */
      33  #ifdef _LIBC
      34  # include <libc-lock.h>
      35  # define gl_rwlock_define __libc_rwlock_define
      36  # define gl_rwlock_wrlock __libc_rwlock_wrlock
      37  # define gl_rwlock_unlock __libc_rwlock_unlock
      38  #else
      39  # include "lock.h"
      40  #endif
      41  
      42  /* Some compilers, like SunOS4 cc, don't have offsetof in <stddef.h>.  */
      43  #ifndef offsetof
      44  # define offsetof(type,ident) ((size_t)&(((type*)0)->ident))
      45  #endif
      46  
      47  /* @@ end of prolog @@ */
      48  
      49  /* Lock variable to protect the global data in the gettext implementation.  */
      50  gl_rwlock_define (extern, _nl_state_lock attribute_hidden)
      51  
      52  
      53  /* Names for the libintl functions are a problem.  They must not clash
      54     with existing names and they should follow ANSI C.  But this source
      55     code is also used in GNU C Library where the names have a __
      56     prefix.  So we have to make a difference here.  */
      57  #ifdef _LIBC
      58  # define BINDTEXTDOMAIN __bindtextdomain
      59  # define BIND_TEXTDOMAIN_CODESET __bind_textdomain_codeset
      60  # ifndef strdup
      61  #  define strdup(str) __strdup (str)
      62  # endif
      63  #else
      64  # define BINDTEXTDOMAIN libintl_bindtextdomain
      65  # define BIND_TEXTDOMAIN_CODESET libintl_bind_textdomain_codeset
      66  #endif
      67  
      68  /* Specifies the directory name *DIRNAMEP and the output codeset *CODESETP
      69     to be used for the DOMAINNAME message catalog.
      70     If *DIRNAMEP or *CODESETP is NULL, the corresponding attribute is not
      71     modified, only the current value is returned.
      72     If DIRNAMEP or CODESETP is NULL, the corresponding attribute is neither
      73     modified nor returned.  */
      74  static void
      75  set_binding_values (const char *domainname,
      76  		    const char **dirnamep, const char **codesetp)
      77  {
      78    struct binding *binding;
      79    int modified;
      80  
      81    /* Some sanity checks.  */
      82    if (domainname == NULL || domainname[0] == '\0')
      83      {
      84        if (dirnamep)
      85  	*dirnamep = NULL;
      86        if (codesetp)
      87  	*codesetp = NULL;
      88        return;
      89      }
      90  
      91    gl_rwlock_wrlock (_nl_state_lock);
      92  
      93    modified = 0;
      94  
      95    for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next)
      96      {
      97        int compare = strcmp (domainname, binding->domainname);
      98        if (compare == 0)
      99  	/* We found it!  */
     100  	break;
     101        if (compare < 0)
     102  	{
     103  	  /* It is not in the list.  */
     104  	  binding = NULL;
     105  	  break;
     106  	}
     107      }
     108  
     109    if (binding != NULL)
     110      {
     111        if (dirnamep)
     112  	{
     113  	  const char *dirname = *dirnamep;
     114  
     115  	  if (dirname == NULL)
     116  	    /* The current binding has be to returned.  */
     117  	    *dirnamep = binding->dirname;
     118  	  else
     119  	    {
     120  	      /* The domain is already bound.  If the new value and the old
     121  		 one are equal we simply do nothing.  Otherwise replace the
     122  		 old binding.  */
     123  	      char *result = binding->dirname;
     124  	      if (strcmp (dirname, result) != 0)
     125  		{
     126  		  if (strcmp (dirname, _nl_default_dirname) == 0)
     127  		    result = (char *) _nl_default_dirname;
     128  		  else
     129  		    {
     130  #if defined _LIBC || defined HAVE_STRDUP
     131  		      result = strdup (dirname);
     132  #else
     133  		      size_t len = strlen (dirname) + 1;
     134  		      result = (char *) malloc (len);
     135  		      if (__builtin_expect (result != NULL, 1))
     136  			memcpy (result, dirname, len);
     137  #endif
     138  		    }
     139  
     140  		  if (__builtin_expect (result != NULL, 1))
     141  		    {
     142  		      if (binding->dirname != _nl_default_dirname)
     143  			free (binding->dirname);
     144  
     145  		      binding->dirname = result;
     146  		      modified = 1;
     147  		    }
     148  		}
     149  	      *dirnamep = result;
     150  	    }
     151  	}
     152  
     153        if (codesetp)
     154  	{
     155  	  const char *codeset = *codesetp;
     156  
     157  	  if (codeset == NULL)
     158  	    /* The current binding has be to returned.  */
     159  	    *codesetp = binding->codeset;
     160  	  else
     161  	    {
     162  	      /* The domain is already bound.  If the new value and the old
     163  		 one are equal we simply do nothing.  Otherwise replace the
     164  		 old binding.  */
     165  	      char *result = binding->codeset;
     166  	      if (result == NULL || strcmp (codeset, result) != 0)
     167  		{
     168  #if defined _LIBC || defined HAVE_STRDUP
     169  		  result = strdup (codeset);
     170  #else
     171  		  size_t len = strlen (codeset) + 1;
     172  		  result = (char *) malloc (len);
     173  		  if (__builtin_expect (result != NULL, 1))
     174  		    memcpy (result, codeset, len);
     175  #endif
     176  
     177  		  if (__builtin_expect (result != NULL, 1))
     178  		    {
     179  		      free (binding->codeset);
     180  
     181  		      binding->codeset = result;
     182  		      modified = 1;
     183  		    }
     184  		}
     185  	      *codesetp = result;
     186  	    }
     187  	}
     188      }
     189    else if ((dirnamep == NULL || *dirnamep == NULL)
     190  	   && (codesetp == NULL || *codesetp == NULL))
     191      {
     192        /* Simply return the default values.  */
     193        if (dirnamep)
     194  	*dirnamep = _nl_default_dirname;
     195        if (codesetp)
     196  	*codesetp = NULL;
     197      }
     198    else
     199      {
     200        /* We have to create a new binding.  */
     201        size_t len = strlen (domainname) + 1;
     202        struct binding *new_binding =
     203  	(struct binding *) malloc (offsetof (struct binding, domainname) + len);
     204  
     205        if (__builtin_expect (new_binding == NULL, 0))
     206  	goto failed;
     207  
     208        memcpy (new_binding->domainname, domainname, len);
     209  
     210        if (dirnamep)
     211  	{
     212  	  const char *dirname = *dirnamep;
     213  
     214  	  if (dirname == NULL)
     215  	    /* The default value.  */
     216  	    dirname = _nl_default_dirname;
     217  	  else
     218  	    {
     219  	      if (strcmp (dirname, _nl_default_dirname) == 0)
     220  		dirname = _nl_default_dirname;
     221  	      else
     222  		{
     223  		  char *result;
     224  #if defined _LIBC || defined HAVE_STRDUP
     225  		  result = strdup (dirname);
     226  		  if (__builtin_expect (result == NULL, 0))
     227  		    goto failed_dirname;
     228  #else
     229  		  size_t len = strlen (dirname) + 1;
     230  		  result = (char *) malloc (len);
     231  		  if (__builtin_expect (result == NULL, 0))
     232  		    goto failed_dirname;
     233  		  memcpy (result, dirname, len);
     234  #endif
     235  		  dirname = result;
     236  		}
     237  	    }
     238  	  *dirnamep = dirname;
     239  	  new_binding->dirname = (char *) dirname;
     240  	}
     241        else
     242  	/* The default value.  */
     243  	new_binding->dirname = (char *) _nl_default_dirname;
     244  
     245        if (codesetp)
     246  	{
     247  	  const char *codeset = *codesetp;
     248  
     249  	  if (codeset != NULL)
     250  	    {
     251  	      char *result;
     252  
     253  #if defined _LIBC || defined HAVE_STRDUP
     254  	      result = strdup (codeset);
     255  	      if (__builtin_expect (result == NULL, 0))
     256  		goto failed_codeset;
     257  #else
     258  	      size_t len = strlen (codeset) + 1;
     259  	      result = (char *) malloc (len);
     260  	      if (__builtin_expect (result == NULL, 0))
     261  		goto failed_codeset;
     262  	      memcpy (result, codeset, len);
     263  #endif
     264  	      codeset = result;
     265  	    }
     266  	  *codesetp = codeset;
     267  	  new_binding->codeset = (char *) codeset;
     268  	}
     269        else
     270  	new_binding->codeset = NULL;
     271  
     272        /* Now enqueue it.  */
     273        if (_nl_domain_bindings == NULL
     274  	  || strcmp (domainname, _nl_domain_bindings->domainname) < 0)
     275  	{
     276  	  new_binding->next = _nl_domain_bindings;
     277  	  _nl_domain_bindings = new_binding;
     278  	}
     279        else
     280  	{
     281  	  binding = _nl_domain_bindings;
     282  	  while (binding->next != NULL
     283  		 && strcmp (domainname, binding->next->domainname) > 0)
     284  	    binding = binding->next;
     285  
     286  	  new_binding->next = binding->next;
     287  	  binding->next = new_binding;
     288  	}
     289  
     290        modified = 1;
     291  
     292        /* Here we deal with memory allocation failures.  */
     293        if (0)
     294  	{
     295  	failed_codeset:
     296  	  if (new_binding->dirname != _nl_default_dirname)
     297  	    free (new_binding->dirname);
     298  	failed_dirname:
     299  	  free (new_binding);
     300  	failed:
     301  	  if (dirnamep)
     302  	    *dirnamep = NULL;
     303  	  if (codesetp)
     304  	    *codesetp = NULL;
     305  	}
     306      }
     307  
     308    /* If we modified any binding, we flush the caches.  */
     309    if (modified)
     310      ++_nl_msg_cat_cntr;
     311  
     312    gl_rwlock_unlock (_nl_state_lock);
     313  }
     314  
     315  /* Specify that the DOMAINNAME message catalog will be found
     316     in DIRNAME rather than in the system locale data base.  */
     317  char *
     318  BINDTEXTDOMAIN (const char *domainname, const char *dirname)
     319  {
     320    set_binding_values (domainname, &dirname, NULL);
     321    return (char *) dirname;
     322  }
     323  
     324  /* Specify the character encoding in which the messages from the
     325     DOMAINNAME message catalog will be returned.  */
     326  char *
     327  BIND_TEXTDOMAIN_CODESET (const char *domainname, const char *codeset)
     328  {
     329    set_binding_values (domainname, NULL, &codeset);
     330    return (char *) codeset;
     331  }
     332  
     333  #ifdef _LIBC
     334  /* Aliases for function names in GNU C Library.  */
     335  weak_alias (__bindtextdomain, bindtextdomain);
     336  weak_alias (__bind_textdomain_codeset, bind_textdomain_codeset);
     337  #endif