• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Implementation of the bindtextdomain(3) function
2    Copyright (C) 1995-2020 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 "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, the directory name *WDIRNAMEP
69    (only on native Windows), and the output codeset *CODESETP to be used
70    for the DOMAINNAME message catalog.
71    If *DIRNAMEP or *WDIRNAMEP or *CODESETP is NULL, the corresponding attribute
72    is not modified, only the current value is returned.
73    If DIRNAMEP or WDIRNAMEP or CODESETP is NULL, the corresponding attribute is
74    neither modified nor returned, except that setting WDIRNAME erases DIRNAME
75    and vice versa.  */
76 static void
77 set_binding_values (const char *domainname,
78 		    const char **dirnamep, const wchar_t **wdirnamep,
79 		    const char **codesetp)
80 {
81   struct binding *binding;
82   int modified;
83 
84   /* Some sanity checks.  */
85   if (domainname == NULL || domainname[0] == '\0')
86     {
87       if (dirnamep)
88 	*dirnamep = NULL;
89 #if defined _WIN32 && !defined __CYGWIN__
90       if (wdirnamep)
91 	*wdirnamep = NULL;
92 #endif
93       if (codesetp)
94 	*codesetp = NULL;
95       return;
96     }
97 
98   gl_rwlock_wrlock (_nl_state_lock);
99 
100   modified = 0;
101 
102   for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next)
103     {
104       int compare = strcmp (domainname, binding->domainname);
105       if (compare == 0)
106 	/* We found it!  */
107 	break;
108       if (compare < 0)
109 	{
110 	  /* It is not in the list.  */
111 	  binding = NULL;
112 	  break;
113 	}
114     }
115 
116   if (binding != NULL)
117     {
118       if (dirnamep)
119 	{
120 	  const char *dirname = *dirnamep;
121 
122 	  if (dirname == NULL)
123 	    /* The current binding has be to returned.  */
124 	    *dirnamep = binding->dirname;
125 	  else
126 	    {
127 	      /* The domain is already bound.  If the new value and the old
128 		 one are equal we simply do nothing.  Otherwise replace the
129 		 old binding.  */
130 	      char *result = binding->dirname;
131 	      if (result == NULL || strcmp (dirname, result) != 0)
132 		{
133 		  if (strcmp (dirname, _nl_default_dirname) == 0)
134 		    result = (char *) _nl_default_dirname;
135 		  else
136 		    {
137 #if defined _LIBC || defined HAVE_STRDUP
138 		      result = strdup (dirname);
139 #else
140 		      size_t len = strlen (dirname) + 1;
141 		      result = (char *) malloc (len);
142 		      if (__builtin_expect (result != NULL, 1))
143 			memcpy (result, dirname, len);
144 #endif
145 		    }
146 
147 		  if (__builtin_expect (result != NULL, 1))
148 		    {
149 		      if (binding->dirname != _nl_default_dirname)
150 			free (binding->dirname);
151 		      binding->dirname = result;
152 
153 #if defined _WIN32 && !defined __CYGWIN__
154 		      free (binding->wdirname);
155 		      binding->wdirname = NULL;
156 #endif
157 
158 		      modified = 1;
159 		    }
160 		}
161 	      *dirnamep = result;
162 	    }
163 	}
164 
165 #if defined _WIN32 && !defined __CYGWIN__
166       if (wdirnamep)
167 	{
168 	  const wchar_t *wdirname = *wdirnamep;
169 
170 	  if (wdirname == NULL)
171 	    /* The current binding has be to returned.  */
172 	    *wdirnamep = binding->wdirname;
173 	  else
174 	    {
175 	      /* The domain is already bound.  If the new value and the old
176 		 one are equal we simply do nothing.  Otherwise replace the
177 		 old binding.  */
178 	      wchar_t *result = binding->wdirname;
179 	      if (result == NULL || wcscmp (wdirname, result) != 0)
180 		{
181 		  result = _wcsdup (wdirname);
182 
183 		  if (__builtin_expect (result != NULL, 1))
184 		    {
185 		      if (binding->dirname != _nl_default_dirname)
186 			free (binding->dirname);
187 		      binding->dirname = NULL;
188 
189 		      free (binding->wdirname);
190 		      binding->wdirname = result;
191 
192 		      modified = 1;
193 		    }
194 		}
195 	      *wdirnamep = result;
196 	    }
197 	}
198 #endif
199 
200       if (codesetp)
201 	{
202 	  const char *codeset = *codesetp;
203 
204 	  if (codeset == NULL)
205 	    /* The current binding has be to returned.  */
206 	    *codesetp = binding->codeset;
207 	  else
208 	    {
209 	      /* The domain is already bound.  If the new value and the old
210 		 one are equal we simply do nothing.  Otherwise replace the
211 		 old binding.  */
212 	      char *result = binding->codeset;
213 	      if (result == NULL || strcmp (codeset, result) != 0)
214 		{
215 #if defined _LIBC || defined HAVE_STRDUP
216 		  result = strdup (codeset);
217 #else
218 		  size_t len = strlen (codeset) + 1;
219 		  result = (char *) malloc (len);
220 		  if (__builtin_expect (result != NULL, 1))
221 		    memcpy (result, codeset, len);
222 #endif
223 
224 		  if (__builtin_expect (result != NULL, 1))
225 		    {
226 		      free (binding->codeset);
227 
228 		      binding->codeset = result;
229 		      modified = 1;
230 		    }
231 		}
232 	      *codesetp = result;
233 	    }
234 	}
235     }
236   else if ((dirnamep == NULL || *dirnamep == NULL)
237 #if defined _WIN32 && !defined __CYGWIN__
238 	   && (wdirnamep == NULL || *wdirnamep == NULL)
239 #endif
240 	   && (codesetp == NULL || *codesetp == NULL))
241     {
242       /* Simply return the default values.  */
243       if (dirnamep)
244 	*dirnamep = _nl_default_dirname;
245 #if defined _WIN32 && !defined __CYGWIN__
246       if (wdirnamep)
247 	*wdirnamep = NULL;
248 #endif
249       if (codesetp)
250 	*codesetp = NULL;
251     }
252   else
253     {
254       /* We have to create a new binding.  */
255       size_t len = strlen (domainname) + 1;
256       struct binding *new_binding =
257 	(struct binding *) malloc (offsetof (struct binding, domainname) + len);
258 
259       if (__builtin_expect (new_binding == NULL, 0))
260 	goto failed;
261 
262       memcpy (new_binding->domainname, domainname, len);
263 
264       if (dirnamep)
265 	{
266 	  const char *dirname = *dirnamep;
267 
268 	  if (dirname == NULL)
269 	    {
270 #if defined _WIN32 && !defined __CYGWIN__
271 	      if (wdirnamep && *wdirnamep != NULL)
272 		dirname = NULL;
273 	      else
274 #endif
275 		/* The default value.  */
276 		dirname = _nl_default_dirname;
277 	    }
278 	  else
279 	    {
280 	      if (strcmp (dirname, _nl_default_dirname) == 0)
281 		dirname = _nl_default_dirname;
282 	      else
283 		{
284 		  char *result;
285 #if defined _LIBC || defined HAVE_STRDUP
286 		  result = strdup (dirname);
287 		  if (__builtin_expect (result == NULL, 0))
288 		    goto failed_dirname;
289 #else
290 		  size_t len = strlen (dirname) + 1;
291 		  result = (char *) malloc (len);
292 		  if (__builtin_expect (result == NULL, 0))
293 		    goto failed_dirname;
294 		  memcpy (result, dirname, len);
295 #endif
296 		  dirname = result;
297 		}
298 	    }
299 	  *dirnamep = dirname;
300 	  new_binding->dirname = (char *) dirname;
301 	}
302       else
303 	{
304 #if defined _WIN32 && !defined __CYGWIN__
305 	  if (wdirnamep && *wdirnamep != NULL)
306 	    new_binding->dirname = NULL;
307 	  else
308 #endif
309 	    /* The default value.  */
310 	    new_binding->dirname = (char *) _nl_default_dirname;
311 	}
312 
313 #if defined _WIN32 && !defined __CYGWIN__
314       if (wdirnamep)
315 	{
316 	  const wchar_t *wdirname = *wdirnamep;
317 
318 	  if (wdirname != NULL)
319 	    {
320 	      wchar_t *result = _wcsdup (wdirname);
321 	      if (__builtin_expect (result == NULL, 0))
322 		goto failed_wdirname;
323 	      wdirname = result;
324 	    }
325 	  *wdirnamep = wdirname;
326 	  new_binding->wdirname = (wchar_t *) wdirname;
327 	}
328       else
329 	new_binding->wdirname = NULL;
330 #endif
331 
332       if (codesetp)
333 	{
334 	  const char *codeset = *codesetp;
335 
336 	  if (codeset != NULL)
337 	    {
338 	      char *result;
339 
340 #if defined _LIBC || defined HAVE_STRDUP
341 	      result = strdup (codeset);
342 	      if (__builtin_expect (result == NULL, 0))
343 		goto failed_codeset;
344 #else
345 	      size_t len = strlen (codeset) + 1;
346 	      result = (char *) malloc (len);
347 	      if (__builtin_expect (result == NULL, 0))
348 		goto failed_codeset;
349 	      memcpy (result, codeset, len);
350 #endif
351 	      codeset = result;
352 	    }
353 	  *codesetp = codeset;
354 	  new_binding->codeset = (char *) codeset;
355 	}
356       else
357 	new_binding->codeset = NULL;
358 
359       /* Now enqueue it.  */
360       if (_nl_domain_bindings == NULL
361 	  || strcmp (domainname, _nl_domain_bindings->domainname) < 0)
362 	{
363 	  new_binding->next = _nl_domain_bindings;
364 	  _nl_domain_bindings = new_binding;
365 	}
366       else
367 	{
368 	  binding = _nl_domain_bindings;
369 	  while (binding->next != NULL
370 		 && strcmp (domainname, binding->next->domainname) > 0)
371 	    binding = binding->next;
372 
373 	  new_binding->next = binding->next;
374 	  binding->next = new_binding;
375 	}
376 
377       modified = 1;
378 
379       /* Here we deal with memory allocation failures.  */
380       if (0)
381 	{
382 	failed_codeset:
383 #if defined _WIN32 && !defined __CYGWIN__
384 	  free (new_binding->wdirname);
385 	failed_wdirname:
386 #endif
387 	  if (new_binding->dirname != _nl_default_dirname)
388 	    free (new_binding->dirname);
389 	failed_dirname:
390 	  free (new_binding);
391 	failed:
392 	  if (dirnamep)
393 	    *dirnamep = NULL;
394 #if defined _WIN32 && !defined __CYGWIN__
395 	  if (wdirnamep)
396 	    *wdirnamep = NULL;
397 #endif
398 	  if (codesetp)
399 	    *codesetp = NULL;
400 	}
401     }
402 
403   /* If we modified any binding, we flush the caches.  */
404   if (modified)
405     ++_nl_msg_cat_cntr;
406 
407   gl_rwlock_unlock (_nl_state_lock);
408 }
409 
410 /* Specify that the DOMAINNAME message catalog will be found
411    in DIRNAME rather than in the system locale data base.  */
412 char *
BINDTEXTDOMAIN(const char * domainname,const char * dirname)413 BINDTEXTDOMAIN (const char *domainname, const char *dirname)
414 {
415 #ifdef __EMX__
416   const char *saved_dirname = dirname;
417   char dirname_with_drive[_MAX_PATH];
418 
419 # ifdef __KLIBC__
420   if (dirname && strncmp (dirname, "/@unixroot", 10) == 0
421       && (dirname[10] == '\0' || dirname[10] == '/' || dirname[10] == '\\'))
422     /* kLIBC itself processes /@unixroot prefix */;
423   else
424 # endif
425   /* Resolve UNIXROOT into dirname if it is not resolved by os2compat.[ch]. */
426   if (dirname && (dirname[0] == '/' || dirname[0] == '\\' ))
427     {
428       const char *unixroot = getenv ("UNIXROOT");
429       size_t len = strlen (dirname) + 1;
430 
431       if (unixroot
432           && unixroot[0] != '\0'
433           && unixroot[1] == ':'
434           && unixroot[2] == '\0'
435           && 2 + len <= _MAX_PATH)
436         {
437           memcpy (dirname_with_drive, unixroot, 2);
438           memcpy (dirname_with_drive + 2, dirname, len);
439 
440           dirname = dirname_with_drive;
441         }
442     }
443 #endif
444   set_binding_values (domainname, &dirname, NULL, NULL);
445 #ifdef __EMX__
446   dirname = saved_dirname;
447 #endif
448   return (char *) dirname;
449 }
450 
451 #if defined _WIN32 && !defined __CYGWIN__
452 /* Specify that the DOMAINNAME message catalog will be found
453    in WDIRNAME rather than in the system locale data base.  */
454 wchar_t *
libintl_wbindtextdomain(const char * domainname,const wchar_t * wdirname)455 libintl_wbindtextdomain (const char *domainname, const wchar_t *wdirname)
456 {
457   set_binding_values (domainname, NULL, &wdirname, NULL);
458   return (wchar_t *) wdirname;
459 }
460 #endif
461 
462 /* Specify the character encoding in which the messages from the
463    DOMAINNAME message catalog will be returned.  */
464 char *
BIND_TEXTDOMAIN_CODESET(const char * domainname,const char * codeset)465 BIND_TEXTDOMAIN_CODESET (const char *domainname, const char *codeset)
466 {
467   set_binding_values (domainname, NULL, NULL, &codeset);
468   return (char *) codeset;
469 }
470 
471 #ifdef _LIBC
472 /* Aliases for function names in GNU C Library.  */
473 weak_alias (__bindtextdomain, bindtextdomain);
474 weak_alias (__bind_textdomain_codeset, bind_textdomain_codeset);
475 #endif
476