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