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