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 /* Some compilers, like SunOS4 cc, don't have offsetof in <stddef.h>.  */
45 #ifndef offsetof
46 # define offsetof(type,ident) ((size_t)&(((type*)0)->ident))
47 #endif
48 
49 /* @@ end of prolog @@ */
50 
51 /* Lock variable to protect the global data in the gettext implementation.  */
gl_rwlock_define(extern,_nl_state_lock attribute_hidden)52 gl_rwlock_define (extern, _nl_state_lock attribute_hidden)
53 
54 
55 /* Names for the libintl functions are a problem.  They must not clash
56    with existing names and they should follow ANSI C.  But this source
57    code is also used in GNU C Library where the names have a __
58    prefix.  So we have to make a difference here.  */
59 #ifdef _LIBC
60 # define BINDTEXTDOMAIN __bindtextdomain
61 # define BIND_TEXTDOMAIN_CODESET __bind_textdomain_codeset
62 # ifndef strdup
63 #  define strdup(str) __strdup (str)
64 # endif
65 #else
66 # define BINDTEXTDOMAIN libintl_bindtextdomain
67 # define BIND_TEXTDOMAIN_CODESET libintl_bind_textdomain_codeset
68 #endif
69 
70 /* Specifies the directory name *DIRNAMEP and the output codeset *CODESETP
71    to be used for the DOMAINNAME message catalog.
72    If *DIRNAMEP or *CODESETP is NULL, the corresponding attribute is not
73    modified, only the current value is returned.
74    If DIRNAMEP or CODESETP is NULL, the corresponding attribute is neither
75    modified nor returned.  */
76 static void
77 set_binding_values (const char *domainname,
78 		    const char **dirnamep, const char **codesetp)
79 {
80   struct binding *binding;
81   int modified;
82 
83   /* Some sanity checks.  */
84   if (domainname == NULL || domainname[0] == '\0')
85     {
86       if (dirnamep)
87 	*dirnamep = NULL;
88       if (codesetp)
89 	*codesetp = NULL;
90       return;
91     }
92 
93   gl_rwlock_wrlock (_nl_state_lock);
94 
95   modified = 0;
96 
97   for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next)
98     {
99       int compare = strcmp (domainname, binding->domainname);
100       if (compare == 0)
101 	/* We found it!  */
102 	break;
103       if (compare < 0)
104 	{
105 	  /* It is not in the list.  */
106 	  binding = NULL;
107 	  break;
108 	}
109     }
110 
111   if (binding != NULL)
112     {
113       if (dirnamep)
114 	{
115 	  const char *dirname = *dirnamep;
116 
117 	  if (dirname == NULL)
118 	    /* The current binding has be to returned.  */
119 	    *dirnamep = binding->dirname;
120 	  else
121 	    {
122 	      /* The domain is already bound.  If the new value and the old
123 		 one are equal we simply do nothing.  Otherwise replace the
124 		 old binding.  */
125 	      char *result = binding->dirname;
126 	      if (strcmp (dirname, result) != 0)
127 		{
128 		  if (strcmp (dirname, _nl_default_dirname) == 0)
129 		    result = (char *) _nl_default_dirname;
130 		  else
131 		    {
132 #if defined _LIBC || defined HAVE_STRDUP
133 		      result = strdup (dirname);
134 #else
135 		      size_t len = strlen (dirname) + 1;
136 		      result = (char *) malloc (len);
137 		      if (__builtin_expect (result != NULL, 1))
138 			memcpy (result, dirname, len);
139 #endif
140 		    }
141 
142 		  if (__builtin_expect (result != NULL, 1))
143 		    {
144 		      if (binding->dirname != _nl_default_dirname)
145 			free (binding->dirname);
146 
147 		      binding->dirname = result;
148 		      modified = 1;
149 		    }
150 		}
151 	      *dirnamep = result;
152 	    }
153 	}
154 
155       if (codesetp)
156 	{
157 	  const char *codeset = *codesetp;
158 
159 	  if (codeset == NULL)
160 	    /* The current binding has be to returned.  */
161 	    *codesetp = binding->codeset;
162 	  else
163 	    {
164 	      /* The domain is already bound.  If the new value and the old
165 		 one are equal we simply do nothing.  Otherwise replace the
166 		 old binding.  */
167 	      char *result = binding->codeset;
168 	      if (result == NULL || strcmp (codeset, result) != 0)
169 		{
170 #if defined _LIBC || defined HAVE_STRDUP
171 		  result = strdup (codeset);
172 #else
173 		  size_t len = strlen (codeset) + 1;
174 		  result = (char *) malloc (len);
175 		  if (__builtin_expect (result != NULL, 1))
176 		    memcpy (result, codeset, len);
177 #endif
178 
179 		  if (__builtin_expect (result != NULL, 1))
180 		    {
181 		      if (binding->codeset != NULL)
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