(root)/
gettext-0.22.4/
gettext-runtime/
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 <bits/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 "glthread/lock.h"
      40  #endif
      41  
      42  #include "flexmember.h"
      43  
      44  /* @@ end of prolog @@ */
      45  
      46  /* Lock variable to protect the global data in the gettext implementation.  */
      47  gl_rwlock_define (extern, _nl_state_lock attribute_hidden)
      48  
      49  
      50  /* Names for the libintl functions are a problem.  They must not clash
      51     with existing names and they should follow ANSI C.  But this source
      52     code is also used in GNU C Library where the names have a __
      53     prefix.  So we have to make a difference here.  */
      54  #ifdef _LIBC
      55  # define BINDTEXTDOMAIN __bindtextdomain
      56  # define BIND_TEXTDOMAIN_CODESET __bind_textdomain_codeset
      57  # ifndef strdup
      58  #  define strdup(str) __strdup (str)
      59  # endif
      60  #else
      61  # define BINDTEXTDOMAIN libintl_bindtextdomain
      62  # define BIND_TEXTDOMAIN_CODESET libintl_bind_textdomain_codeset
      63  #endif
      64  
      65  /* Specifies the directory name *DIRNAMEP, the directory name *WDIRNAMEP
      66     (only on native Windows), and the output codeset *CODESETP to be used
      67     for the DOMAINNAME message catalog.
      68     If *DIRNAMEP or *WDIRNAMEP or *CODESETP is NULL, the corresponding attribute
      69     is not modified, only the current value is returned.
      70     If DIRNAMEP or WDIRNAMEP or CODESETP is NULL, the corresponding attribute is
      71     neither modified nor returned, except that setting WDIRNAME erases DIRNAME
      72     and vice versa.  */
      73  static void
      74  set_binding_values (const char *domainname,
      75  		    const char **dirnamep, const wchar_t **wdirnamep,
      76  		    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 defined _WIN32 && !defined __CYGWIN__
      87        if (wdirnamep)
      88  	*wdirnamep = NULL;
      89  #endif
      90        if (codesetp)
      91  	*codesetp = NULL;
      92        return;
      93      }
      94  
      95    gl_rwlock_wrlock (_nl_state_lock);
      96  
      97    modified = 0;
      98  
      99    for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next)
     100      {
     101        int compare = strcmp (domainname, binding->domainname);
     102        if (compare == 0)
     103  	/* We found it!  */
     104  	break;
     105        if (compare < 0)
     106  	{
     107  	  /* It is not in the list.  */
     108  	  binding = NULL;
     109  	  break;
     110  	}
     111      }
     112  
     113    if (binding != NULL)
     114      {
     115        if (dirnamep)
     116  	{
     117  	  const char *dirname = *dirnamep;
     118  
     119  	  if (dirname == NULL)
     120  	    /* The current binding has be to returned.  */
     121  	    *dirnamep = binding->dirname;
     122  	  else
     123  	    {
     124  	      /* The domain is already bound.  If the new value and the old
     125  		 one are equal we simply do nothing.  Otherwise replace the
     126  		 old binding.  */
     127  	      char *result = binding->dirname;
     128  	      if (result == NULL || strcmp (dirname, result) != 0)
     129  		{
     130  		  if (strcmp (dirname, _nl_default_dirname) == 0)
     131  		    result = (char *) _nl_default_dirname;
     132  		  else
     133  		    result = strdup (dirname);
     134  
     135  		  if (__builtin_expect (result != NULL, 1))
     136  		    {
     137  		      if (binding->dirname != _nl_default_dirname)
     138  			free (binding->dirname);
     139  		      binding->dirname = result;
     140  
     141  #if defined _WIN32 && !defined __CYGWIN__
     142  		      free (binding->wdirname);
     143  		      binding->wdirname = NULL;
     144  #endif
     145  
     146  		      modified = 1;
     147  		    }
     148  		}
     149  	      *dirnamep = result;
     150  	    }
     151  	}
     152  
     153  #if defined _WIN32 && !defined __CYGWIN__
     154        if (wdirnamep)
     155  	{
     156  	  const wchar_t *wdirname = *wdirnamep;
     157  
     158  	  if (wdirname == NULL)
     159  	    /* The current binding has be to returned.  */
     160  	    *wdirnamep = binding->wdirname;
     161  	  else
     162  	    {
     163  	      /* The domain is already bound.  If the new value and the old
     164  		 one are equal we simply do nothing.  Otherwise replace the
     165  		 old binding.  */
     166  	      wchar_t *result = binding->wdirname;
     167  	      if (result == NULL || wcscmp (wdirname, result) != 0)
     168  		{
     169  		  result = _wcsdup (wdirname);
     170  
     171  		  if (__builtin_expect (result != NULL, 1))
     172  		    {
     173  		      if (binding->dirname != _nl_default_dirname)
     174  			free (binding->dirname);
     175  		      binding->dirname = NULL;
     176  
     177  		      free (binding->wdirname);
     178  		      binding->wdirname = result;
     179  
     180  		      modified = 1;
     181  		    }
     182  		}
     183  	      *wdirnamep = result;
     184  	    }
     185  	}
     186  #endif
     187  
     188        if (codesetp)
     189  	{
     190  	  const char *codeset = *codesetp;
     191  
     192  	  if (codeset == NULL)
     193  	    /* The current binding has be to returned.  */
     194  	    *codesetp = binding->codeset;
     195  	  else
     196  	    {
     197  	      /* The domain is already bound.  If the new value and the old
     198  		 one are equal we simply do nothing.  Otherwise replace the
     199  		 old binding.  */
     200  	      char *result = binding->codeset;
     201  	      if (result == NULL || strcmp (codeset, result) != 0)
     202  		{
     203  		  result = strdup (codeset);
     204  		  if (__builtin_expect (result != NULL, 1))
     205  		    {
     206  		      free (binding->codeset);
     207  
     208  		      binding->codeset = result;
     209  		      modified = 1;
     210  		    }
     211  		}
     212  	      *codesetp = result;
     213  	    }
     214  	}
     215      }
     216    else if ((dirnamep == NULL || *dirnamep == NULL)
     217  #if defined _WIN32 && !defined __CYGWIN__
     218  	   && (wdirnamep == NULL || *wdirnamep == NULL)
     219  #endif
     220  	   && (codesetp == NULL || *codesetp == NULL))
     221      {
     222        /* Simply return the default values.  */
     223        if (dirnamep)
     224  	*dirnamep = _nl_default_dirname;
     225  #if defined _WIN32 && !defined __CYGWIN__
     226        if (wdirnamep)
     227  	*wdirnamep = NULL;
     228  #endif
     229        if (codesetp)
     230  	*codesetp = NULL;
     231      }
     232    else
     233      {
     234        /* We have to create a new binding.  */
     235        size_t len = strlen (domainname) + 1;
     236        struct binding *new_binding =
     237  	(struct binding *)
     238  	malloc (FLEXNSIZEOF (struct binding, domainname, len));
     239  
     240  
     241        if (__builtin_expect (new_binding == NULL, 0))
     242  	goto failed;
     243  
     244        memcpy (new_binding->domainname, domainname, len);
     245  
     246        if (dirnamep)
     247  	{
     248  	  const char *dirname = *dirnamep;
     249  
     250  	  if (dirname == NULL)
     251  	    {
     252  #if defined _WIN32 && !defined __CYGWIN__
     253  	      if (wdirnamep && *wdirnamep != NULL)
     254  		dirname = NULL;
     255  	      else
     256  #endif
     257  		/* The default value.  */
     258  		dirname = _nl_default_dirname;
     259  	    }
     260  	  else
     261  	    {
     262  	      if (strcmp (dirname, _nl_default_dirname) == 0)
     263  		dirname = _nl_default_dirname;
     264  	      else
     265  		{
     266  		  char *result = strdup (dirname);
     267  		  if (__builtin_expect (result == NULL, 0))
     268  		    goto failed_dirname;
     269  		  dirname = result;
     270  		}
     271  	    }
     272  	  *dirnamep = dirname;
     273  	  new_binding->dirname = (char *) dirname;
     274  	}
     275        else
     276  	{
     277  #if defined _WIN32 && !defined __CYGWIN__
     278  	  if (wdirnamep && *wdirnamep != NULL)
     279  	    new_binding->dirname = NULL;
     280  	  else
     281  #endif
     282  	    /* The default value.  */
     283  	    new_binding->dirname = (char *) _nl_default_dirname;
     284  	}
     285  
     286  #if defined _WIN32 && !defined __CYGWIN__
     287        if (wdirnamep)
     288  	{
     289  	  const wchar_t *wdirname = *wdirnamep;
     290  
     291  	  if (wdirname != NULL)
     292  	    {
     293  	      wchar_t *result = _wcsdup (wdirname);
     294  	      if (__builtin_expect (result == NULL, 0))
     295  		goto failed_wdirname;
     296  	      wdirname = result;
     297  	    }
     298  	  *wdirnamep = wdirname;
     299  	  new_binding->wdirname = (wchar_t *) wdirname;
     300  	}
     301        else
     302  	new_binding->wdirname = NULL;
     303  #endif
     304  
     305        if (codesetp)
     306  	{
     307  	  const char *codeset = *codesetp;
     308  
     309  	  if (codeset != NULL)
     310  	    {
     311  	      char *result = strdup (codeset);
     312  	      if (__builtin_expect (result == NULL, 0))
     313  		goto failed_codeset;
     314  	      codeset = result;
     315  	    }
     316  	  *codesetp = codeset;
     317  	  new_binding->codeset = (char *) codeset;
     318  	}
     319        else
     320  	new_binding->codeset = NULL;
     321  
     322        /* Now enqueue it.  */
     323        if (_nl_domain_bindings == NULL
     324  	  || strcmp (domainname, _nl_domain_bindings->domainname) < 0)
     325  	{
     326  	  new_binding->next = _nl_domain_bindings;
     327  	  _nl_domain_bindings = new_binding;
     328  	}
     329        else
     330  	{
     331  	  binding = _nl_domain_bindings;
     332  	  while (binding->next != NULL
     333  		 && strcmp (domainname, binding->next->domainname) > 0)
     334  	    binding = binding->next;
     335  
     336  	  new_binding->next = binding->next;
     337  	  binding->next = new_binding;
     338  	}
     339  
     340        modified = 1;
     341  
     342        /* Here we deal with memory allocation failures.  */
     343        if (0)
     344  	{
     345  	failed_codeset:
     346  #if defined _WIN32 && !defined __CYGWIN__
     347  	  free (new_binding->wdirname);
     348  	failed_wdirname:
     349  #endif
     350  	  if (new_binding->dirname != _nl_default_dirname)
     351  	    free (new_binding->dirname);
     352  	failed_dirname:
     353  	  free (new_binding);
     354  	failed:
     355  	  if (dirnamep)
     356  	    *dirnamep = NULL;
     357  #if defined _WIN32 && !defined __CYGWIN__
     358  	  if (wdirnamep)
     359  	    *wdirnamep = NULL;
     360  #endif
     361  	  if (codesetp)
     362  	    *codesetp = NULL;
     363  	}
     364      }
     365  
     366    /* If we modified any binding, we flush the caches.  */
     367    if (modified)
     368      ++_nl_msg_cat_cntr;
     369  
     370    gl_rwlock_unlock (_nl_state_lock);
     371  }
     372  
     373  /* Specify that the DOMAINNAME message catalog will be found
     374     in DIRNAME rather than in the system locale data base.  */
     375  char *
     376  BINDTEXTDOMAIN (const char *domainname, const char *dirname)
     377  {
     378  #ifdef __EMX__
     379    const char *saved_dirname = dirname;
     380    char dirname_with_drive[_MAX_PATH];
     381  
     382  # ifdef __KLIBC__
     383    if (dirname && strncmp (dirname, "/@unixroot", 10) == 0
     384        && (dirname[10] == '\0' || dirname[10] == '/' || dirname[10] == '\\'))
     385      /* kLIBC itself processes /@unixroot prefix */;
     386    else
     387  # endif
     388    /* Resolve UNIXROOT into dirname if it is not resolved by os2compat.[ch]. */
     389    if (dirname && (dirname[0] == '/' || dirname[0] == '\\' ))
     390      {
     391        const char *unixroot = getenv ("UNIXROOT");
     392        size_t len = strlen (dirname) + 1;
     393  
     394        if (unixroot
     395            && unixroot[0] != '\0'
     396            && unixroot[1] == ':'
     397            && unixroot[2] == '\0'
     398            && 2 + len <= _MAX_PATH)
     399          {
     400            memcpy (dirname_with_drive, unixroot, 2);
     401            memcpy (dirname_with_drive + 2, dirname, len);
     402  
     403            dirname = dirname_with_drive;
     404          }
     405      }
     406  #endif
     407    set_binding_values (domainname, &dirname, NULL, NULL);
     408  #ifdef __EMX__
     409    dirname = saved_dirname;
     410  #endif
     411    return (char *) dirname;
     412  }
     413  
     414  #if defined _WIN32 && !defined __CYGWIN__
     415  /* Specify that the DOMAINNAME message catalog will be found
     416     in WDIRNAME rather than in the system locale data base.  */
     417  wchar_t *
     418  libintl_wbindtextdomain (const char *domainname, const wchar_t *wdirname)
     419  {
     420    set_binding_values (domainname, NULL, &wdirname, NULL);
     421    return (wchar_t *) wdirname;
     422  }
     423  #endif
     424  
     425  /* Specify the character encoding in which the messages from the
     426     DOMAINNAME message catalog will be returned.  */
     427  char *
     428  BIND_TEXTDOMAIN_CODESET (const char *domainname, const char *codeset)
     429  {
     430    set_binding_values (domainname, NULL, NULL, &codeset);
     431    return (char *) codeset;
     432  }
     433  
     434  #ifdef _LIBC
     435  /* Aliases for function names in GNU C Library.  */
     436  weak_alias (__bindtextdomain, bindtextdomain);
     437  weak_alias (__bind_textdomain_codeset, bind_textdomain_codeset);
     438  #endif