xref: /openbsd/gnu/usr.bin/texinfo/intl/bindtextdom.c (revision 404b540a)
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.  */
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 /* 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.  */
95 static void
96 set_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.  */
343 char *
344 BINDTEXTDOMAIN (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.  */
352 char *
353 BIND_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.  */
361 weak_alias (__bindtextdomain, bindtextdomain);
362 weak_alias (__bind_textdomain_codeset, bind_textdomain_codeset);
363 #endif
364