1 /* $NetBSD: bindtextdom.c,v 1.1.1.1 2016/01/10 21:36:17 christos Exp $ */
2
3 /* Implementation of the bindtextdomain(3) function
4 Copyright (C) 1995-1998, 2000, 2001 Free Software Foundation, Inc.
5
6 This program is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Library General Public License as published
8 by the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public
17 License along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
19 USA. */
20
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
24
25 #include <stddef.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #ifdef _LIBC
30 # include <libintl.h>
31 #else
32 # include "libgnuintl.h"
33 #endif
34 #include "gettextP.h"
35
36 #ifdef _LIBC
37 /* We have to handle multi-threaded applications. */
38 # include <bits/libc-lock.h>
39 #else
40 /* Provide dummy implementation if this is outside glibc. */
41 # define __libc_rwlock_define(CLASS, NAME)
42 # define __libc_rwlock_wrlock(NAME)
43 # define __libc_rwlock_unlock(NAME)
44 #endif
45
46 /* The internal variables in the standalone libintl.a must have different
47 names than the internal variables in GNU libc, otherwise programs
48 using libintl.a cannot be linked statically. */
49 #if !defined _LIBC
50 # define _nl_default_dirname _nl_default_dirname__
51 # define _nl_domain_bindings _nl_domain_bindings__
52 #endif
53
54 /* Some compilers, like SunOS4 cc, don't have offsetof in <stddef.h>. */
55 #ifndef offsetof
56 # define offsetof(type,ident) ((size_t)&(((type*)0)->ident))
57 #endif
58
59 /* @@ end of prolog @@ */
60
61 /* Contains the default location of the message catalogs. */
62 extern const char _nl_default_dirname[];
63
64 /* List with bindings of specific domains. */
65 extern struct binding *_nl_domain_bindings;
66
67 /* Lock variable to protect the global data in the gettext implementation. */
68 __libc_rwlock_define (extern, _nl_state_lock)
69
70
71 /* Names for the libintl functions are a problem. They must not clash
72 with existing names and they should follow ANSI C. But this source
73 code is also used in GNU C Library where the names have a __
74 prefix. So we have to make a difference here. */
75 #ifdef _LIBC
76 # define BINDTEXTDOMAIN __bindtextdomain
77 # define BIND_TEXTDOMAIN_CODESET __bind_textdomain_codeset
78 # ifndef strdup
79 # define strdup(str) __strdup (str)
80 # endif
81 #else
82 # define BINDTEXTDOMAIN bindtextdomain__
83 # define BIND_TEXTDOMAIN_CODESET bind_textdomain_codeset__
84 #endif
85
86 /* Prototypes for local functions. */
87 static void set_binding_values PARAMS ((const char *domainname,
88 const char **dirnamep,
89 const char **codesetp));
90
91 /* Specifies the directory name *DIRNAMEP and the output codeset *CODESETP
92 to be used for the DOMAINNAME message catalog.
93 If *DIRNAMEP or *CODESETP is NULL, the corresponding attribute is not
94 modified, only the current value is returned.
95 If DIRNAMEP or CODESETP is NULL, the corresponding attribute is neither
96 modified nor returned. */
97 static void
set_binding_values(domainname,dirnamep,codesetp)98 set_binding_values (domainname, dirnamep, codesetp)
99 const char *domainname;
100 const char **dirnamep;
101 const char **codesetp;
102 {
103 struct binding *binding;
104 int modified;
105
106 /* Some sanity checks. */
107 if (domainname == NULL || domainname[0] == '\0')
108 {
109 if (dirnamep)
110 *dirnamep = NULL;
111 if (codesetp)
112 *codesetp = NULL;
113 return;
114 }
115
116 __libc_rwlock_wrlock (_nl_state_lock);
117
118 modified = 0;
119
120 for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next)
121 {
122 int compare = strcmp (domainname, binding->domainname);
123 if (compare == 0)
124 /* We found it! */
125 break;
126 if (compare < 0)
127 {
128 /* It is not in the list. */
129 binding = NULL;
130 break;
131 }
132 }
133
134 if (binding != NULL)
135 {
136 if (dirnamep)
137 {
138 const char *dirname = *dirnamep;
139
140 if (dirname == NULL)
141 /* The current binding has be to returned. */
142 *dirnamep = binding->dirname;
143 else
144 {
145 /* The domain is already bound. If the new value and the old
146 one are equal we simply do nothing. Otherwise replace the
147 old binding. */
148 char *result = binding->dirname;
149 if (strcmp (dirname, result) != 0)
150 {
151 if (strcmp (dirname, _nl_default_dirname) == 0)
152 result = (char *) _nl_default_dirname;
153 else
154 {
155 #if defined _LIBC || defined HAVE_STRDUP
156 result = strdup (dirname);
157 #else
158 size_t len = strlen (dirname) + 1;
159 result = (char *) malloc (len);
160 if (__builtin_expect (result != NULL, 1))
161 memcpy (result, dirname, len);
162 #endif
163 }
164
165 if (__builtin_expect (result != NULL, 1))
166 {
167 if (binding->dirname != _nl_default_dirname)
168 free (binding->dirname);
169
170 binding->dirname = result;
171 modified = 1;
172 }
173 }
174 *dirnamep = result;
175 }
176 }
177
178 if (codesetp)
179 {
180 const char *codeset = *codesetp;
181
182 if (codeset == NULL)
183 /* The current binding has be to returned. */
184 *codesetp = binding->codeset;
185 else
186 {
187 /* The domain is already bound. If the new value and the old
188 one are equal we simply do nothing. Otherwise replace the
189 old binding. */
190 char *result = binding->codeset;
191 if (result == NULL || strcmp (codeset, result) != 0)
192 {
193 #if defined _LIBC || defined HAVE_STRDUP
194 result = strdup (codeset);
195 #else
196 size_t len = strlen (codeset) + 1;
197 result = (char *) malloc (len);
198 if (__builtin_expect (result != NULL, 1))
199 memcpy (result, codeset, len);
200 #endif
201
202 if (__builtin_expect (result != NULL, 1))
203 {
204 if (binding->codeset != NULL)
205 free (binding->codeset);
206
207 binding->codeset = result;
208 binding->codeset_cntr++;
209 modified = 1;
210 }
211 }
212 *codesetp = result;
213 }
214 }
215 }
216 else if ((dirnamep == NULL || *dirnamep == NULL)
217 && (codesetp == NULL || *codesetp == NULL))
218 {
219 /* Simply return the default values. */
220 if (dirnamep)
221 *dirnamep = _nl_default_dirname;
222 if (codesetp)
223 *codesetp = NULL;
224 }
225 else
226 {
227 /* We have to create a new binding. */
228 size_t len = strlen (domainname) + 1;
229 struct binding *new_binding =
230 (struct binding *) malloc (offsetof (struct binding, domainname) + len);
231
232 if (__builtin_expect (new_binding == NULL, 0))
233 goto failed;
234
235 memcpy (new_binding->domainname, domainname, len);
236
237 if (dirnamep)
238 {
239 const char *dirname = *dirnamep;
240
241 if (dirname == NULL)
242 /* The default value. */
243 dirname = _nl_default_dirname;
244 else
245 {
246 if (strcmp (dirname, _nl_default_dirname) == 0)
247 dirname = _nl_default_dirname;
248 else
249 {
250 char *result;
251 #if defined _LIBC || defined HAVE_STRDUP
252 result = strdup (dirname);
253 if (__builtin_expect (result == NULL, 0))
254 goto failed_dirname;
255 #else
256 size_t len = strlen (dirname) + 1;
257 result = (char *) malloc (len);
258 if (__builtin_expect (result == NULL, 0))
259 goto failed_dirname;
260 memcpy (result, dirname, len);
261 #endif
262 dirname = result;
263 }
264 }
265 *dirnamep = dirname;
266 new_binding->dirname = (char *) dirname;
267 }
268 else
269 /* The default value. */
270 new_binding->dirname = (char *) _nl_default_dirname;
271
272 new_binding->codeset_cntr = 0;
273
274 if (codesetp)
275 {
276 const char *codeset = *codesetp;
277
278 if (codeset != NULL)
279 {
280 char *result;
281
282 #if defined _LIBC || defined HAVE_STRDUP
283 result = strdup (codeset);
284 if (__builtin_expect (result == NULL, 0))
285 goto failed_codeset;
286 #else
287 size_t len = strlen (codeset) + 1;
288 result = (char *) malloc (len);
289 if (__builtin_expect (result == NULL, 0))
290 goto failed_codeset;
291 memcpy (result, codeset, len);
292 #endif
293 codeset = result;
294 new_binding->codeset_cntr++;
295 }
296 *codesetp = codeset;
297 new_binding->codeset = (char *) codeset;
298 }
299 else
300 new_binding->codeset = NULL;
301
302 /* Now enqueue it. */
303 if (_nl_domain_bindings == NULL
304 || strcmp (domainname, _nl_domain_bindings->domainname) < 0)
305 {
306 new_binding->next = _nl_domain_bindings;
307 _nl_domain_bindings = new_binding;
308 }
309 else
310 {
311 binding = _nl_domain_bindings;
312 while (binding->next != NULL
313 && strcmp (domainname, binding->next->domainname) > 0)
314 binding = binding->next;
315
316 new_binding->next = binding->next;
317 binding->next = new_binding;
318 }
319
320 modified = 1;
321
322 /* Here we deal with memory allocation failures. */
323 if (0)
324 {
325 failed_codeset:
326 if (new_binding->dirname != _nl_default_dirname)
327 free (new_binding->dirname);
328 failed_dirname:
329 free (new_binding);
330 failed:
331 if (dirnamep)
332 *dirnamep = NULL;
333 if (codesetp)
334 *codesetp = NULL;
335 }
336 }
337
338 /* If we modified any binding, we flush the caches. */
339 if (modified)
340 ++_nl_msg_cat_cntr;
341
342 __libc_rwlock_unlock (_nl_state_lock);
343 }
344
345 /* Specify that the DOMAINNAME message catalog will be found
346 in DIRNAME rather than in the system locale data base. */
347 char *
BINDTEXTDOMAIN(domainname,dirname)348 BINDTEXTDOMAIN (domainname, dirname)
349 const char *domainname;
350 const char *dirname;
351 {
352 set_binding_values (domainname, &dirname, NULL);
353 return (char *) dirname;
354 }
355
356 /* Specify the character encoding in which the messages from the
357 DOMAINNAME message catalog will be returned. */
358 char *
BIND_TEXTDOMAIN_CODESET(domainname,codeset)359 BIND_TEXTDOMAIN_CODESET (domainname, codeset)
360 const char *domainname;
361 const char *codeset;
362 {
363 set_binding_values (domainname, NULL, &codeset);
364 return (char *) codeset;
365 }
366
367 #ifdef _LIBC
368 /* Aliases for function names in GNU C Library. */
369 weak_alias (__bindtextdomain, bindtextdomain);
370 weak_alias (__bind_textdomain_codeset, bind_textdomain_codeset);
371 #endif
372