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