1 /* Implementation of the bindtextdomain(3) function
2    Copyright (C) 1995-2022 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.  */
gl_rwlock_define(extern,_nl_state_lock attribute_hidden)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 *
BINDTEXTDOMAIN(const char * domainname,const char * dirname)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 *
BIND_TEXTDOMAIN_CODESET(const char * domainname,const char * codeset)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
338