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., 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/* Prototypes for local functions.  */
90static 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.  */
100static void
101set_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.  */
350char *
351BINDTEXTDOMAIN (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.  */
361char *
362BIND_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.  */
372weak_alias (__bindtextdomain, bindtextdomain);
373weak_alias (__bind_textdomain_codeset, bind_textdomain_codeset);
374#endif
375