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