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