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