1 /* Implementation of the bindtextdomain(3) function
2    Copyright (C) 1995-1998, 2000, 2001, 2002 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.  */
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 /* Prototypes for local functions.  */
90 static void set_binding_values PARAMS ((const char *domainname,
91 					const char **dirnamep,
92 					const char **codesetp));
93 
94 /* Specifies the directory name *DIRNAMEP and the output codeset *CODESETP
95    to be used for the DOMAINNAME message catalog.
96    If *DIRNAMEP or *CODESETP is NULL, the corresponding attribute is not
97    modified, only the current value is returned.
98    If DIRNAMEP or CODESETP is NULL, the corresponding attribute is neither
99    modified nor returned.  */
100 static void
set_binding_values(domainname,dirnamep,codesetp)101 set_binding_values (domainname, dirnamep, codesetp)
102      const char *domainname;
103      const char **dirnamep;
104      const char **codesetp;
105 {
106   struct binding *binding;
107   int modified;
108 
109   /* Some sanity checks.  */
110   if (domainname == NULL || domainname[0] == '\0')
111     {
112       if (dirnamep)
113 	*dirnamep = NULL;
114       if (codesetp)
115 	*codesetp = NULL;
116       return;
117     }
118 
119   __libc_rwlock_wrlock (_nl_state_lock);
120 
121   modified = 0;
122 
123   for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next)
124     {
125       int compare = strcmp (domainname, binding->domainname);
126       if (compare == 0)
127 	/* We found it!  */
128 	break;
129       if (compare < 0)
130 	{
131 	  /* It is not in the list.  */
132 	  binding = NULL;
133 	  break;
134 	}
135     }
136 
137   if (binding != NULL)
138     {
139       if (dirnamep)
140 	{
141 	  const char *dirname = *dirnamep;
142 
143 	  if (dirname == NULL)
144 	    /* The current binding has be to returned.  */
145 	    *dirnamep = binding->dirname;
146 	  else
147 	    {
148 	      /* The domain is already bound.  If the new value and the old
149 		 one are equal we simply do nothing.  Otherwise replace the
150 		 old binding.  */
151 	      char *result = binding->dirname;
152 	      if (strcmp (dirname, result) != 0)
153 		{
154 		  if (strcmp (dirname, INTUSE(_nl_default_dirname)) == 0)
155 		    result = (char *) INTUSE(_nl_default_dirname);
156 		  else
157 		    {
158 #if defined _LIBC || defined HAVE_STRDUP
159 		      result = strdup (dirname);
160 #else
161 		      size_t len = strlen (dirname) + 1;
162 		      result = (char *) malloc (len);
163 		      if (__builtin_expect (result != NULL, 1))
164 			memcpy (result, dirname, len);
165 #endif
166 		    }
167 
168 		  if (__builtin_expect (result != NULL, 1))
169 		    {
170 		      if (binding->dirname != INTUSE(_nl_default_dirname))
171 			free (binding->dirname);
172 
173 		      binding->dirname = result;
174 		      modified = 1;
175 		    }
176 		}
177 	      *dirnamep = result;
178 	    }
179 	}
180 
181       if (codesetp)
182 	{
183 	  const char *codeset = *codesetp;
184 
185 	  if (codeset == NULL)
186 	    /* The current binding has be to returned.  */
187 	    *codesetp = binding->codeset;
188 	  else
189 	    {
190 	      /* The domain is already bound.  If the new value and the old
191 		 one are equal we simply do nothing.  Otherwise replace the
192 		 old binding.  */
193 	      char *result = binding->codeset;
194 	      if (result == NULL || strcmp (codeset, result) != 0)
195 		{
196 #if defined _LIBC || defined HAVE_STRDUP
197 		  result = strdup (codeset);
198 #else
199 		  size_t len = strlen (codeset) + 1;
200 		  result = (char *) malloc (len);
201 		  if (__builtin_expect (result != NULL, 1))
202 		    memcpy (result, codeset, len);
203 #endif
204 
205 		  if (__builtin_expect (result != NULL, 1))
206 		    {
207 		      if (binding->codeset != NULL)
208 			free (binding->codeset);
209 
210 		      binding->codeset = result;
211 		      binding->codeset_cntr++;
212 		      modified = 1;
213 		    }
214 		}
215 	      *codesetp = result;
216 	    }
217 	}
218     }
219   else if ((dirnamep == NULL || *dirnamep == NULL)
220 	   && (codesetp == NULL || *codesetp == NULL))
221     {
222       /* Simply return the default values.  */
223       if (dirnamep)
224 	*dirnamep = INTUSE(_nl_default_dirname);
225       if (codesetp)
226 	*codesetp = NULL;
227     }
228   else
229     {
230       /* We have to create a new binding.  */
231       size_t len = strlen (domainname) + 1;
232       struct binding *new_binding =
233 	(struct binding *) malloc (offsetof (struct binding, domainname) + len);
234 
235       if (__builtin_expect (new_binding == NULL, 0))
236 	goto failed;
237 
238       memcpy (new_binding->domainname, domainname, len);
239 
240       if (dirnamep)
241 	{
242 	  const char *dirname = *dirnamep;
243 
244 	  if (dirname == NULL)
245 	    /* The default value.  */
246 	    dirname = INTUSE(_nl_default_dirname);
247 	  else
248 	    {
249 	      if (strcmp (dirname, INTUSE(_nl_default_dirname)) == 0)
250 		dirname = INTUSE(_nl_default_dirname);
251 	      else
252 		{
253 		  char *result;
254 #if defined _LIBC || defined HAVE_STRDUP
255 		  result = strdup (dirname);
256 		  if (__builtin_expect (result == NULL, 0))
257 		    goto failed_dirname;
258 #else
259 		  size_t len = strlen (dirname) + 1;
260 		  result = (char *) malloc (len);
261 		  if (__builtin_expect (result == NULL, 0))
262 		    goto failed_dirname;
263 		  memcpy (result, dirname, len);
264 #endif
265 		  dirname = result;
266 		}
267 	    }
268 	  *dirnamep = dirname;
269 	  new_binding->dirname = (char *) dirname;
270 	}
271       else
272 	/* The default value.  */
273 	new_binding->dirname = (char *) INTUSE(_nl_default_dirname);
274 
275       new_binding->codeset_cntr = 0;
276 
277       if (codesetp)
278 	{
279 	  const char *codeset = *codesetp;
280 
281 	  if (codeset != NULL)
282 	    {
283 	      char *result;
284 
285 #if defined _LIBC || defined HAVE_STRDUP
286 	      result = strdup (codeset);
287 	      if (__builtin_expect (result == NULL, 0))
288 		goto failed_codeset;
289 #else
290 	      size_t len = strlen (codeset) + 1;
291 	      result = (char *) malloc (len);
292 	      if (__builtin_expect (result == NULL, 0))
293 		goto failed_codeset;
294 	      memcpy (result, codeset, len);
295 #endif
296 	      codeset = result;
297 	      new_binding->codeset_cntr++;
298 	    }
299 	  *codesetp = codeset;
300 	  new_binding->codeset = (char *) codeset;
301 	}
302       else
303 	new_binding->codeset = NULL;
304 
305       /* Now enqueue it.  */
306       if (_nl_domain_bindings == NULL
307 	  || strcmp (domainname, _nl_domain_bindings->domainname) < 0)
308 	{
309 	  new_binding->next = _nl_domain_bindings;
310 	  _nl_domain_bindings = new_binding;
311 	}
312       else
313 	{
314 	  binding = _nl_domain_bindings;
315 	  while (binding->next != NULL
316 		 && strcmp (domainname, binding->next->domainname) > 0)
317 	    binding = binding->next;
318 
319 	  new_binding->next = binding->next;
320 	  binding->next = new_binding;
321 	}
322 
323       modified = 1;
324 
325       /* Here we deal with memory allocation failures.  */
326       if (0)
327 	{
328 	failed_codeset:
329 	  if (new_binding->dirname != INTUSE(_nl_default_dirname))
330 	    free (new_binding->dirname);
331 	failed_dirname:
332 	  free (new_binding);
333 	failed:
334 	  if (dirnamep)
335 	    *dirnamep = NULL;
336 	  if (codesetp)
337 	    *codesetp = NULL;
338 	}
339     }
340 
341   /* If we modified any binding, we flush the caches.  */
342   if (modified)
343     ++_nl_msg_cat_cntr;
344 
345   __libc_rwlock_unlock (_nl_state_lock);
346 }
347 
348 /* Specify that the DOMAINNAME message catalog will be found
349    in DIRNAME rather than in the system locale data base.  */
350 char *
BINDTEXTDOMAIN(domainname,dirname)351 BINDTEXTDOMAIN (domainname, dirname)
352      const char *domainname;
353      const char *dirname;
354 {
355   set_binding_values (domainname, &dirname, NULL);
356   return (char *) dirname;
357 }
358 
359 /* Specify the character encoding in which the messages from the
360    DOMAINNAME message catalog will be returned.  */
361 char *
BIND_TEXTDOMAIN_CODESET(domainname,codeset)362 BIND_TEXTDOMAIN_CODESET (domainname, codeset)
363      const char *domainname;
364      const char *codeset;
365 {
366   set_binding_values (domainname, NULL, &codeset);
367   return (char *) codeset;
368 }
369 
370 #ifdef _LIBC
371 /* Aliases for function names in GNU C Library.  */
372 weak_alias (__bindtextdomain, bindtextdomain);
373 weak_alias (__bind_textdomain_codeset, bind_textdomain_codeset);
374 #endif
375