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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
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.  */
60extern const char _nl_default_dirname[];
61#ifdef _LIBC
62extern 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.  */
68extern 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/* 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.  */
95static void
96set_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		      if (binding->codeset != NULL)
201			free (binding->codeset);
202
203		      binding->codeset = result;
204		      binding->codeset_cntr++;
205		      modified = 1;
206		    }
207		}
208	      *codesetp = result;
209	    }
210	}
211    }
212  else if ((dirnamep == NULL || *dirnamep == NULL)
213	   && (codesetp == NULL || *codesetp == NULL))
214    {
215      /* Simply return the default values.  */
216      if (dirnamep)
217	*dirnamep = INTUSE(_nl_default_dirname);
218      if (codesetp)
219	*codesetp = NULL;
220    }
221  else
222    {
223      /* We have to create a new binding.  */
224      size_t len = strlen (domainname) + 1;
225      struct binding *new_binding =
226	(struct binding *) malloc (offsetof (struct binding, domainname) + len);
227
228      if (__builtin_expect (new_binding == NULL, 0))
229	goto failed;
230
231      memcpy (new_binding->domainname, domainname, len);
232
233      if (dirnamep)
234	{
235	  const char *dirname = *dirnamep;
236
237	  if (dirname == NULL)
238	    /* The default value.  */
239	    dirname = INTUSE(_nl_default_dirname);
240	  else
241	    {
242	      if (strcmp (dirname, INTUSE(_nl_default_dirname)) == 0)
243		dirname = INTUSE(_nl_default_dirname);
244	      else
245		{
246		  char *result;
247#if defined _LIBC || defined HAVE_STRDUP
248		  result = strdup (dirname);
249		  if (__builtin_expect (result == NULL, 0))
250		    goto failed_dirname;
251#else
252		  size_t len = strlen (dirname) + 1;
253		  result = (char *) malloc (len);
254		  if (__builtin_expect (result == NULL, 0))
255		    goto failed_dirname;
256		  memcpy (result, dirname, len);
257#endif
258		  dirname = result;
259		}
260	    }
261	  *dirnamep = dirname;
262	  new_binding->dirname = (char *) dirname;
263	}
264      else
265	/* The default value.  */
266	new_binding->dirname = (char *) INTUSE(_nl_default_dirname);
267
268      new_binding->codeset_cntr = 0;
269
270      if (codesetp)
271	{
272	  const char *codeset = *codesetp;
273
274	  if (codeset != NULL)
275	    {
276	      char *result;
277
278#if defined _LIBC || defined HAVE_STRDUP
279	      result = strdup (codeset);
280	      if (__builtin_expect (result == NULL, 0))
281		goto failed_codeset;
282#else
283	      size_t len = strlen (codeset) + 1;
284	      result = (char *) malloc (len);
285	      if (__builtin_expect (result == NULL, 0))
286		goto failed_codeset;
287	      memcpy (result, codeset, len);
288#endif
289	      codeset = result;
290	      new_binding->codeset_cntr++;
291	    }
292	  *codesetp = codeset;
293	  new_binding->codeset = (char *) codeset;
294	}
295      else
296	new_binding->codeset = NULL;
297
298      /* Now enqueue it.  */
299      if (_nl_domain_bindings == NULL
300	  || strcmp (domainname, _nl_domain_bindings->domainname) < 0)
301	{
302	  new_binding->next = _nl_domain_bindings;
303	  _nl_domain_bindings = new_binding;
304	}
305      else
306	{
307	  binding = _nl_domain_bindings;
308	  while (binding->next != NULL
309		 && strcmp (domainname, binding->next->domainname) > 0)
310	    binding = binding->next;
311
312	  new_binding->next = binding->next;
313	  binding->next = new_binding;
314	}
315
316      modified = 1;
317
318      /* Here we deal with memory allocation failures.  */
319      if (0)
320	{
321	failed_codeset:
322	  if (new_binding->dirname != INTUSE(_nl_default_dirname))
323	    free (new_binding->dirname);
324	failed_dirname:
325	  free (new_binding);
326	failed:
327	  if (dirnamep)
328	    *dirnamep = NULL;
329	  if (codesetp)
330	    *codesetp = NULL;
331	}
332    }
333
334  /* If we modified any binding, we flush the caches.  */
335  if (modified)
336    ++_nl_msg_cat_cntr;
337
338  __libc_rwlock_unlock (_nl_state_lock);
339}
340
341/* Specify that the DOMAINNAME message catalog will be found
342   in DIRNAME rather than in the system locale data base.  */
343char *
344BINDTEXTDOMAIN (const char *domainname, const char *dirname)
345{
346  set_binding_values (domainname, &dirname, NULL);
347  return (char *) dirname;
348}
349
350/* Specify the character encoding in which the messages from the
351   DOMAINNAME message catalog will be returned.  */
352char *
353BIND_TEXTDOMAIN_CODESET (const char *domainname, const char *codeset)
354{
355  set_binding_values (domainname, NULL, &codeset);
356  return (char *) codeset;
357}
358
359#ifdef _LIBC
360/* Aliases for function names in GNU C Library.  */
361weak_alias (__bindtextdomain, bindtextdomain);
362weak_alias (__bind_textdomain_codeset, bind_textdomain_codeset);
363#endif
364