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		      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.  */
342char *
343BINDTEXTDOMAIN (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.  */
351char *
352BIND_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.  */
360weak_alias (__bindtextdomain, bindtextdomain);
361weak_alias (__bind_textdomain_codeset, bind_textdomain_codeset);
362#endif
363