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