1/* bindtextdom.c - Implementation of the bindtextdomain(3) function */
2
3/* Copyright (C) 1995-1998, 2000, 2001, 2002, 2005-2009 Free Software Foundation, Inc.
4
5   This file is part of GNU Bash.
6
7   Bash is free software: you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation, either version 3 of the License, or
10   (at your option) any later version.
11
12   Bash is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with Bash.  If not, see <http://www.gnu.org/licenses/>.
19*/
20
21#ifdef HAVE_CONFIG_H
22# include <config.h>
23#endif
24
25#include <stddef.h>
26#include <stdlib.h>
27#include <string.h>
28
29#ifdef _LIBC
30# include <libintl.h>
31#else
32# include "libgnuintl.h"
33#endif
34#include "gettextP.h"
35
36#ifdef _LIBC
37/* We have to handle multi-threaded applications.  */
38# include <bits/libc-lock.h>
39#else
40/* Provide dummy implementation if this is outside glibc.  */
41# define __libc_rwlock_define(CLASS, NAME)
42# define __libc_rwlock_wrlock(NAME)
43# define __libc_rwlock_unlock(NAME)
44#endif
45
46/* The internal variables in the standalone libintl.a must have different
47   names than the internal variables in GNU libc, otherwise programs
48   using libintl.a cannot be linked statically.  */
49#if !defined _LIBC
50# define _nl_default_dirname libintl_nl_default_dirname
51# define _nl_domain_bindings libintl_nl_domain_bindings
52#endif
53
54/* Some compilers, like SunOS4 cc, don't have offsetof in <stddef.h>.  */
55#ifndef offsetof
56# define offsetof(type,ident) ((size_t)&(((type*)0)->ident))
57#endif
58
59/* @@ end of prolog @@ */
60
61/* Contains the default location of the message catalogs.  */
62extern const char _nl_default_dirname[];
63#ifdef _LIBC
64extern const char _nl_default_dirname_internal[] attribute_hidden;
65#else
66# define INTUSE(name) name
67#endif
68
69/* List with bindings of specific domains.  */
70extern struct binding *_nl_domain_bindings;
71
72/* Lock variable to protect the global data in the gettext implementation.  */
73__libc_rwlock_define (extern, _nl_state_lock attribute_hidden)
74
75
76/* Names for the libintl functions are a problem.  They must not clash
77   with existing names and they should follow ANSI C.  But this source
78   code is also used in GNU C Library where the names have a __
79   prefix.  So we have to make a difference here.  */
80#ifdef _LIBC
81# define BINDTEXTDOMAIN __bindtextdomain
82# define BIND_TEXTDOMAIN_CODESET __bind_textdomain_codeset
83# ifndef strdup
84#  define strdup(str) __strdup (str)
85# endif
86#else
87# define BINDTEXTDOMAIN libintl_bindtextdomain
88# define BIND_TEXTDOMAIN_CODESET libintl_bind_textdomain_codeset
89#endif
90
91/* Prototypes for local functions.  */
92static void set_binding_values PARAMS ((const char *domainname,
93					const char **dirnamep,
94					const char **codesetp));
95
96/* Specifies the directory name *DIRNAMEP and the output codeset *CODESETP
97   to be used for the DOMAINNAME message catalog.
98   If *DIRNAMEP or *CODESETP is NULL, the corresponding attribute is not
99   modified, only the current value is returned.
100   If DIRNAMEP or CODESETP is NULL, the corresponding attribute is neither
101   modified nor returned.  */
102static void
103set_binding_values (domainname, dirnamep, codesetp)
104     const char *domainname;
105     const char **dirnamep;
106     const char **codesetp;
107{
108  struct binding *binding;
109  int modified;
110
111  /* Some sanity checks.  */
112  if (domainname == NULL || domainname[0] == '\0')
113    {
114      if (dirnamep)
115	*dirnamep = NULL;
116      if (codesetp)
117	*codesetp = NULL;
118      return;
119    }
120
121  __libc_rwlock_wrlock (_nl_state_lock);
122
123  modified = 0;
124
125  for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next)
126    {
127      int compare = strcmp (domainname, binding->domainname);
128      if (compare == 0)
129	/* We found it!  */
130	break;
131      if (compare < 0)
132	{
133	  /* It is not in the list.  */
134	  binding = NULL;
135	  break;
136	}
137    }
138
139  if (binding != NULL)
140    {
141      if (dirnamep)
142	{
143	  const char *dirname = *dirnamep;
144
145	  if (dirname == NULL)
146	    /* The current binding has be to returned.  */
147	    *dirnamep = binding->dirname;
148	  else
149	    {
150	      /* The domain is already bound.  If the new value and the old
151		 one are equal we simply do nothing.  Otherwise replace the
152		 old binding.  */
153	      char *result = binding->dirname;
154	      if (strcmp (dirname, result) != 0)
155		{
156		  if (strcmp (dirname, INTUSE(_nl_default_dirname)) == 0)
157		    result = (char *) INTUSE(_nl_default_dirname);
158		  else
159		    {
160#if defined _LIBC || defined HAVE_STRDUP
161		      result = strdup (dirname);
162#else
163		      size_t len = strlen (dirname) + 1;
164		      result = (char *) malloc (len);
165		      if (__builtin_expect (result != NULL, 1))
166			memcpy (result, dirname, len);
167#endif
168		    }
169
170		  if (__builtin_expect (result != NULL, 1))
171		    {
172		      if (binding->dirname != INTUSE(_nl_default_dirname))
173			free (binding->dirname);
174
175		      binding->dirname = result;
176		      modified = 1;
177		    }
178		}
179	      *dirnamep = result;
180	    }
181	}
182
183      if (codesetp)
184	{
185	  const char *codeset = *codesetp;
186
187	  if (codeset == NULL)
188	    /* The current binding has be to returned.  */
189	    *codesetp = binding->codeset;
190	  else
191	    {
192	      /* The domain is already bound.  If the new value and the old
193		 one are equal we simply do nothing.  Otherwise replace the
194		 old binding.  */
195	      char *result = binding->codeset;
196	      if (result == NULL || strcmp (codeset, result) != 0)
197		{
198#if defined _LIBC || defined HAVE_STRDUP
199		  result = strdup (codeset);
200#else
201		  size_t len = strlen (codeset) + 1;
202		  result = (char *) malloc (len);
203		  if (__builtin_expect (result != NULL, 1))
204		    memcpy (result, codeset, len);
205#endif
206
207		  if (__builtin_expect (result != NULL, 1))
208		    {
209		      if (binding->codeset != NULL)
210			free (binding->codeset);
211
212		      binding->codeset = result;
213		      binding->codeset_cntr++;
214		      modified = 1;
215		    }
216		}
217	      *codesetp = result;
218	    }
219	}
220    }
221  else if ((dirnamep == NULL || *dirnamep == NULL)
222	   && (codesetp == NULL || *codesetp == NULL))
223    {
224      /* Simply return the default values.  */
225      if (dirnamep)
226	*dirnamep = INTUSE(_nl_default_dirname);
227      if (codesetp)
228	*codesetp = NULL;
229    }
230  else
231    {
232      /* We have to create a new binding.  */
233      size_t len = strlen (domainname) + 1;
234      struct binding *new_binding =
235	(struct binding *) malloc (offsetof (struct binding, domainname) + len);
236
237      if (__builtin_expect (new_binding == NULL, 0))
238	goto failed;
239
240      memcpy (new_binding->domainname, domainname, len);
241
242      if (dirnamep)
243	{
244	  const char *dirname = *dirnamep;
245
246	  if (dirname == NULL)
247	    /* The default value.  */
248	    dirname = INTUSE(_nl_default_dirname);
249	  else
250	    {
251	      if (strcmp (dirname, INTUSE(_nl_default_dirname)) == 0)
252		dirname = INTUSE(_nl_default_dirname);
253	      else
254		{
255		  char *result;
256#if defined _LIBC || defined HAVE_STRDUP
257		  result = strdup (dirname);
258		  if (__builtin_expect (result == NULL, 0))
259		    goto failed_dirname;
260#else
261		  size_t len = strlen (dirname) + 1;
262		  result = (char *) malloc (len);
263		  if (__builtin_expect (result == NULL, 0))
264		    goto failed_dirname;
265		  memcpy (result, dirname, len);
266#endif
267		  dirname = result;
268		}
269	    }
270	  *dirnamep = dirname;
271	  new_binding->dirname = (char *) dirname;
272	}
273      else
274	/* The default value.  */
275	new_binding->dirname = (char *) INTUSE(_nl_default_dirname);
276
277      new_binding->codeset_cntr = 0;
278
279      if (codesetp)
280	{
281	  const char *codeset = *codesetp;
282
283	  if (codeset != NULL)
284	    {
285	      char *result;
286
287#if defined _LIBC || defined HAVE_STRDUP
288	      result = strdup (codeset);
289	      if (__builtin_expect (result == NULL, 0))
290		goto failed_codeset;
291#else
292	      size_t len = strlen (codeset) + 1;
293	      result = (char *) malloc (len);
294	      if (__builtin_expect (result == NULL, 0))
295		goto failed_codeset;
296	      memcpy (result, codeset, len);
297#endif
298	      codeset = result;
299	      new_binding->codeset_cntr++;
300	    }
301	  *codesetp = codeset;
302	  new_binding->codeset = (char *) codeset;
303	}
304      else
305	new_binding->codeset = NULL;
306
307      /* Now enqueue it.  */
308      if (_nl_domain_bindings == NULL
309	  || strcmp (domainname, _nl_domain_bindings->domainname) < 0)
310	{
311	  new_binding->next = _nl_domain_bindings;
312	  _nl_domain_bindings = new_binding;
313	}
314      else
315	{
316	  binding = _nl_domain_bindings;
317	  while (binding->next != NULL
318		 && strcmp (domainname, binding->next->domainname) > 0)
319	    binding = binding->next;
320
321	  new_binding->next = binding->next;
322	  binding->next = new_binding;
323	}
324
325      modified = 1;
326
327      /* Here we deal with memory allocation failures.  */
328      if (0)
329	{
330	failed_codeset:
331	  if (new_binding->dirname != INTUSE(_nl_default_dirname))
332	    free (new_binding->dirname);
333	failed_dirname:
334	  free (new_binding);
335	failed:
336	  if (dirnamep)
337	    *dirnamep = NULL;
338	  if (codesetp)
339	    *codesetp = NULL;
340	}
341    }
342
343  /* If we modified any binding, we flush the caches.  */
344  if (modified)
345    ++_nl_msg_cat_cntr;
346
347  __libc_rwlock_unlock (_nl_state_lock);
348}
349
350/* Specify that the DOMAINNAME message catalog will be found
351   in DIRNAME rather than in the system locale data base.  */
352char *
353BINDTEXTDOMAIN (domainname, dirname)
354     const char *domainname;
355     const char *dirname;
356{
357  set_binding_values (domainname, &dirname, NULL);
358  return (char *) dirname;
359}
360
361/* Specify the character encoding in which the messages from the
362   DOMAINNAME message catalog will be returned.  */
363char *
364BIND_TEXTDOMAIN_CODESET (domainname, codeset)
365     const char *domainname;
366     const char *codeset;
367{
368  set_binding_values (domainname, NULL, &codeset);
369  return (char *) codeset;
370}
371
372#ifdef _LIBC
373/* Aliases for function names in GNU C Library.  */
374weak_alias (__bindtextdomain, bindtextdomain);
375weak_alias (__bind_textdomain_codeset, bind_textdomain_codeset);
376#endif
377