blob: 828422654ed85a4cb893c423d8b89ffe69eb540d [file] [log] [blame]
Theodore Ts'o5bc5a892000-02-08 20:20:46 +00001/* Implementation of the bindtextdomain(3) function
Theodore Ts'ob0cacab2004-11-30 19:00:19 -05002 Copyright (C) 1995-1998, 2000-2003 Free Software Foundation, Inc.
Theodore Ts'o5bc5a892000-02-08 20:20:46 +00003
Theodore Ts'oa04eba32003-05-03 16:35:17 -04004 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)
Theodore Ts'o5bc5a892000-02-08 20:20:46 +00007 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
Theodore Ts'oa04eba32003-05-03 16:35:17 -040011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
Theodore Ts'o5bc5a892000-02-08 20:20:46 +000013
Theodore Ts'oa04eba32003-05-03 16:35:17 -040014 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. */
Theodore Ts'o5bc5a892000-02-08 20:20:46 +000018
19#ifdef HAVE_CONFIG_H
20# include <config.h>
21#endif
22
Theodore Ts'oa04eba32003-05-03 16:35:17 -040023#include <stddef.h>
24#include <stdlib.h>
25#include <string.h>
Theodore Ts'o5bc5a892000-02-08 20:20:46 +000026
27#ifdef _LIBC
28# include <libintl.h>
29#else
Theodore Ts'oa04eba32003-05-03 16:35:17 -040030# include "libgnuintl.h"
Theodore Ts'o5bc5a892000-02-08 20:20:46 +000031#endif
Theodore Ts'o5bc5a892000-02-08 20:20:46 +000032#include "gettextP.h"
33
Theodore Ts'oa04eba32003-05-03 16:35:17 -040034#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
Theodore Ts'o5bc5a892000-02-08 20:20:46 +000057/* @@ end of prolog @@ */
58
59/* Contains the default location of the message catalogs. */
60extern const char _nl_default_dirname[];
Theodore Ts'ob0cacab2004-11-30 19:00:19 -050061#ifdef _LIBC
62extern const char _nl_default_dirname_internal[] attribute_hidden;
63#else
64# define INTUSE(name) name
65#endif
Theodore Ts'o5bc5a892000-02-08 20:20:46 +000066
67/* List with bindings of specific domains. */
68extern struct binding *_nl_domain_bindings;
69
Theodore Ts'oa04eba32003-05-03 16:35:17 -040070/* Lock variable to protect the global data in the gettext implementation. */
71__libc_rwlock_define (extern, _nl_state_lock attribute_hidden)
72
Theodore Ts'o5bc5a892000-02-08 20:20:46 +000073
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
Theodore Ts'oa04eba32003-05-03 16:35:17 -040080# define BIND_TEXTDOMAIN_CODESET __bind_textdomain_codeset
Theodore Ts'o5bc5a892000-02-08 20:20:46 +000081# ifndef strdup
82# define strdup(str) __strdup (str)
83# endif
84#else
Theodore Ts'oa04eba32003-05-03 16:35:17 -040085# define BINDTEXTDOMAIN libintl_bindtextdomain
86# define BIND_TEXTDOMAIN_CODESET libintl_bind_textdomain_codeset
Theodore Ts'o5bc5a892000-02-08 20:20:46 +000087#endif
88
Theodore Ts'oa04eba32003-05-03 16:35:17 -040089/* 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
Theodore Ts'ob0cacab2004-11-30 19:00:19 -050096set_binding_values (const char *domainname,
97 const char **dirnamep, const char **codesetp)
Theodore Ts'o5bc5a892000-02-08 20:20:46 +000098{
99 struct binding *binding;
Theodore Ts'oa04eba32003-05-03 16:35:17 -0400100 int modified;
Theodore Ts'o5bc5a892000-02-08 20:20:46 +0000101
102 /* Some sanity checks. */
103 if (domainname == NULL || domainname[0] == '\0')
Theodore Ts'oa04eba32003-05-03 16:35:17 -0400104 {
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;
Theodore Ts'o5bc5a892000-02-08 20:20:46 +0000115
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
Theodore Ts'o5bc5a892000-02-08 20:20:46 +0000130 if (binding != NULL)
131 {
Theodore Ts'oa04eba32003-05-03 16:35:17 -0400132 if (dirnamep)
Theodore Ts'o5bc5a892000-02-08 20:20:46 +0000133 {
Theodore Ts'oa04eba32003-05-03 16:35:17 -0400134 const char *dirname = *dirnamep;
Theodore Ts'o5bc5a892000-02-08 20:20:46 +0000135
Theodore Ts'oa04eba32003-05-03 16:35:17 -0400136 if (dirname == NULL)
137 /* The current binding has be to returned. */
138 *dirnamep = binding->dirname;
Theodore Ts'o5bc5a892000-02-08 20:20:46 +0000139 else
140 {
Theodore Ts'oa04eba32003-05-03 16:35:17 -0400141 /* 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 {
Theodore Ts'ob0cacab2004-11-30 19:00:19 -0500147 if (strcmp (dirname, INTUSE(_nl_default_dirname)) == 0)
148 result = (char *) INTUSE(_nl_default_dirname);
Theodore Ts'oa04eba32003-05-03 16:35:17 -0400149 else
150 {
Theodore Ts'o5bc5a892000-02-08 20:20:46 +0000151#if defined _LIBC || defined HAVE_STRDUP
Theodore Ts'oa04eba32003-05-03 16:35:17 -0400152 result = strdup (dirname);
Theodore Ts'o5bc5a892000-02-08 20:20:46 +0000153#else
Theodore Ts'oa04eba32003-05-03 16:35:17 -0400154 size_t len = strlen (dirname) + 1;
155 result = (char *) malloc (len);
156 if (__builtin_expect (result != NULL, 1))
157 memcpy (result, dirname, len);
Theodore Ts'o5bc5a892000-02-08 20:20:46 +0000158#endif
Theodore Ts'oa04eba32003-05-03 16:35:17 -0400159 }
160
161 if (__builtin_expect (result != NULL, 1))
162 {
Theodore Ts'ob0cacab2004-11-30 19:00:19 -0500163 if (binding->dirname != INTUSE(_nl_default_dirname))
Theodore Ts'oa04eba32003-05-03 16:35:17 -0400164 free (binding->dirname);
165
166 binding->dirname = result;
167 modified = 1;
168 }
169 }
170 *dirnamep = result;
Theodore Ts'o5bc5a892000-02-08 20:20:46 +0000171 }
Theodore Ts'o5bc5a892000-02-08 20:20:46 +0000172 }
Theodore Ts'oa04eba32003-05-03 16:35:17 -0400173
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 {
Jim Meyering45e338f2009-02-23 18:07:50 +0100200 free (binding->codeset);
Theodore Ts'oa04eba32003-05-03 16:35:17 -0400201
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)
Theodore Ts'ob0cacab2004-11-30 19:00:19 -0500216 *dirnamep = INTUSE(_nl_default_dirname);
Theodore Ts'oa04eba32003-05-03 16:35:17 -0400217 if (codesetp)
218 *codesetp = NULL;
Theodore Ts'o5bc5a892000-02-08 20:20:46 +0000219 }
220 else
221 {
222 /* We have to create a new binding. */
Theodore Ts'oa04eba32003-05-03 16:35:17 -0400223 size_t len = strlen (domainname) + 1;
Theodore Ts'o5bc5a892000-02-08 20:20:46 +0000224 struct binding *new_binding =
Theodore Ts'oa04eba32003-05-03 16:35:17 -0400225 (struct binding *) malloc (offsetof (struct binding, domainname) + len);
Theodore Ts'o5bc5a892000-02-08 20:20:46 +0000226
Theodore Ts'oa04eba32003-05-03 16:35:17 -0400227 if (__builtin_expect (new_binding == NULL, 0))
228 goto failed;
Theodore Ts'o5bc5a892000-02-08 20:20:46 +0000229
Theodore Ts'o5bc5a892000-02-08 20:20:46 +0000230 memcpy (new_binding->domainname, domainname, len);
Theodore Ts'o5bc5a892000-02-08 20:20:46 +0000231
Theodore Ts'oa04eba32003-05-03 16:35:17 -0400232 if (dirnamep)
Theodore Ts'o5bc5a892000-02-08 20:20:46 +0000233 {
Theodore Ts'oa04eba32003-05-03 16:35:17 -0400234 const char *dirname = *dirnamep;
235
236 if (dirname == NULL)
237 /* The default value. */
Theodore Ts'ob0cacab2004-11-30 19:00:19 -0500238 dirname = INTUSE(_nl_default_dirname);
Theodore Ts'oa04eba32003-05-03 16:35:17 -0400239 else
240 {
Theodore Ts'ob0cacab2004-11-30 19:00:19 -0500241 if (strcmp (dirname, INTUSE(_nl_default_dirname)) == 0)
242 dirname = INTUSE(_nl_default_dirname);
Theodore Ts'oa04eba32003-05-03 16:35:17 -0400243 else
244 {
245 char *result;
Theodore Ts'o5bc5a892000-02-08 20:20:46 +0000246#if defined _LIBC || defined HAVE_STRDUP
Theodore Ts'oa04eba32003-05-03 16:35:17 -0400247 result = strdup (dirname);
248 if (__builtin_expect (result == NULL, 0))
249 goto failed_dirname;
Theodore Ts'o5bc5a892000-02-08 20:20:46 +0000250#else
Theodore Ts'oa04eba32003-05-03 16:35:17 -0400251 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);
Theodore Ts'o5bc5a892000-02-08 20:20:46 +0000256#endif
Theodore Ts'oa04eba32003-05-03 16:35:17 -0400257 dirname = result;
258 }
259 }
260 *dirnamep = dirname;
261 new_binding->dirname = (char *) dirname;
Theodore Ts'o5bc5a892000-02-08 20:20:46 +0000262 }
Theodore Ts'oa04eba32003-05-03 16:35:17 -0400263 else
264 /* The default value. */
Theodore Ts'ob0cacab2004-11-30 19:00:19 -0500265 new_binding->dirname = (char *) INTUSE(_nl_default_dirname);
Theodore Ts'oa04eba32003-05-03 16:35:17 -0400266
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;
Theodore Ts'o5bc5a892000-02-08 20:20:46 +0000296
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
Theodore Ts'oa04eba32003-05-03 16:35:17 -0400315 modified = 1;
316
317 /* Here we deal with memory allocation failures. */
318 if (0)
319 {
320 failed_codeset:
Theodore Ts'ob0cacab2004-11-30 19:00:19 -0500321 if (new_binding->dirname != INTUSE(_nl_default_dirname))
Theodore Ts'oa04eba32003-05-03 16:35:17 -0400322 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 }
Theodore Ts'o5bc5a892000-02-08 20:20:46 +0000331 }
332
Theodore Ts'oa04eba32003-05-03 16:35:17 -0400333 /* 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 *
Theodore Ts'ob0cacab2004-11-30 19:00:19 -0500343BINDTEXTDOMAIN (const char *domainname, const char *dirname)
Theodore Ts'oa04eba32003-05-03 16:35:17 -0400344{
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 *
Theodore Ts'ob0cacab2004-11-30 19:00:19 -0500352BIND_TEXTDOMAIN_CODESET (const char *domainname, const char *codeset)
Theodore Ts'oa04eba32003-05-03 16:35:17 -0400353{
354 set_binding_values (domainname, NULL, &codeset);
355 return (char *) codeset;
Theodore Ts'o5bc5a892000-02-08 20:20:46 +0000356}
357
358#ifdef _LIBC
Theodore Ts'oa04eba32003-05-03 16:35:17 -0400359/* Aliases for function names in GNU C Library. */
Theodore Ts'o5bc5a892000-02-08 20:20:46 +0000360weak_alias (__bindtextdomain, bindtextdomain);
Theodore Ts'oa04eba32003-05-03 16:35:17 -0400361weak_alias (__bind_textdomain_codeset, bind_textdomain_codeset);
Theodore Ts'o5bc5a892000-02-08 20:20:46 +0000362#endif