128ea187bSespie /* Implementation of the bindtextdomain(3) function
2*a1acfa9bSespie Copyright (C) 1995-1998, 2000-2003 Free Software Foundation, Inc.
3840175f0Skstailey
43fb98d4aSespie This program is free software; you can redistribute it and/or modify it
53fb98d4aSespie under the terms of the GNU Library General Public License as published
63fb98d4aSespie by the Free Software Foundation; either version 2, or (at your option)
7840175f0Skstailey any later version.
8840175f0Skstailey
9840175f0Skstailey This program is distributed in the hope that it will be useful,
10840175f0Skstailey but WITHOUT ANY WARRANTY; without even the implied warranty of
113fb98d4aSespie MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
123fb98d4aSespie Library General Public License for more details.
13840175f0Skstailey
143fb98d4aSespie You should have received a copy of the GNU Library General Public
153fb98d4aSespie License along with this program; if not, write to the Free Software
163fb98d4aSespie Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
173fb98d4aSespie USA. */
18840175f0Skstailey
19840175f0Skstailey #ifdef HAVE_CONFIG_H
20840175f0Skstailey # include <config.h>
21840175f0Skstailey #endif
22840175f0Skstailey
233fb98d4aSespie #include <stddef.h>
24840175f0Skstailey #include <stdlib.h>
25840175f0Skstailey #include <string.h>
26840175f0Skstailey
27840175f0Skstailey #ifdef _LIBC
28840175f0Skstailey # include <libintl.h>
29840175f0Skstailey #else
303fb98d4aSespie # include "libgnuintl.h"
31840175f0Skstailey #endif
32840175f0Skstailey #include "gettextP.h"
33840175f0Skstailey
343fb98d4aSespie #ifdef _LIBC
353fb98d4aSespie /* We have to handle multi-threaded applications. */
363fb98d4aSespie # include <bits/libc-lock.h>
373fb98d4aSespie #else
383fb98d4aSespie /* Provide dummy implementation if this is outside glibc. */
393fb98d4aSespie # define __libc_rwlock_define(CLASS, NAME)
403fb98d4aSespie # define __libc_rwlock_wrlock(NAME)
413fb98d4aSespie # define __libc_rwlock_unlock(NAME)
423fb98d4aSespie #endif
433fb98d4aSespie
443fb98d4aSespie /* The internal variables in the standalone libintl.a must have different
453fb98d4aSespie names than the internal variables in GNU libc, otherwise programs
463fb98d4aSespie using libintl.a cannot be linked statically. */
473fb98d4aSespie #if !defined _LIBC
48*a1acfa9bSespie # define _nl_default_dirname libintl_nl_default_dirname
49*a1acfa9bSespie # define _nl_domain_bindings libintl_nl_domain_bindings
503fb98d4aSespie #endif
513fb98d4aSespie
523fb98d4aSespie /* Some compilers, like SunOS4 cc, don't have offsetof in <stddef.h>. */
533fb98d4aSespie #ifndef offsetof
543fb98d4aSespie # define offsetof(type,ident) ((size_t)&(((type*)0)->ident))
553fb98d4aSespie #endif
563fb98d4aSespie
57840175f0Skstailey /* @@ end of prolog @@ */
58840175f0Skstailey
59840175f0Skstailey /* Contains the default location of the message catalogs. */
60840175f0Skstailey extern const char _nl_default_dirname[];
61*a1acfa9bSespie #ifdef _LIBC
62*a1acfa9bSespie extern const char _nl_default_dirname_internal[] attribute_hidden;
63*a1acfa9bSespie #else
64*a1acfa9bSespie # define INTUSE(name) name
65*a1acfa9bSespie #endif
66840175f0Skstailey
67840175f0Skstailey /* List with bindings of specific domains. */
68840175f0Skstailey extern struct binding *_nl_domain_bindings;
69840175f0Skstailey
703fb98d4aSespie /* Lock variable to protect the global data in the gettext implementation. */
__libc_rwlock_define(extern,_nl_state_lock attribute_hidden)71*a1acfa9bSespie __libc_rwlock_define (extern, _nl_state_lock attribute_hidden)
723fb98d4aSespie
73840175f0Skstailey
74840175f0Skstailey /* Names for the libintl functions are a problem. They must not clash
75840175f0Skstailey with existing names and they should follow ANSI C. But this source
76840175f0Skstailey code is also used in GNU C Library where the names have a __
77840175f0Skstailey prefix. So we have to make a difference here. */
78840175f0Skstailey #ifdef _LIBC
79840175f0Skstailey # define BINDTEXTDOMAIN __bindtextdomain
803fb98d4aSespie # define BIND_TEXTDOMAIN_CODESET __bind_textdomain_codeset
811cc83814Sespie # ifndef strdup
8228ea187bSespie # define strdup(str) __strdup (str)
831cc83814Sespie # endif
84840175f0Skstailey #else
85*a1acfa9bSespie # define BINDTEXTDOMAIN libintl_bindtextdomain
86*a1acfa9bSespie # define BIND_TEXTDOMAIN_CODESET libintl_bind_textdomain_codeset
87840175f0Skstailey #endif
88840175f0Skstailey
893fb98d4aSespie /* Specifies the directory name *DIRNAMEP and the output codeset *CODESETP
903fb98d4aSespie to be used for the DOMAINNAME message catalog.
913fb98d4aSespie If *DIRNAMEP or *CODESETP is NULL, the corresponding attribute is not
923fb98d4aSespie modified, only the current value is returned.
933fb98d4aSespie If DIRNAMEP or CODESETP is NULL, the corresponding attribute is neither
943fb98d4aSespie modified nor returned. */
953fb98d4aSespie static void
96*a1acfa9bSespie set_binding_values (const char *domainname,
97*a1acfa9bSespie const char **dirnamep, const char **codesetp)
98840175f0Skstailey {
99840175f0Skstailey struct binding *binding;
1003fb98d4aSespie int modified;
101840175f0Skstailey
102840175f0Skstailey /* Some sanity checks. */
103840175f0Skstailey if (domainname == NULL || domainname[0] == '\0')
1043fb98d4aSespie {
1053fb98d4aSespie if (dirnamep)
1063fb98d4aSespie *dirnamep = NULL;
1073fb98d4aSespie if (codesetp)
1083fb98d4aSespie *codesetp = NULL;
1093fb98d4aSespie return;
1103fb98d4aSespie }
1113fb98d4aSespie
1123fb98d4aSespie __libc_rwlock_wrlock (_nl_state_lock);
1133fb98d4aSespie
1143fb98d4aSespie modified = 0;
115840175f0Skstailey
116840175f0Skstailey for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next)
117840175f0Skstailey {
118840175f0Skstailey int compare = strcmp (domainname, binding->domainname);
119840175f0Skstailey if (compare == 0)
120840175f0Skstailey /* We found it! */
121840175f0Skstailey break;
122840175f0Skstailey if (compare < 0)
123840175f0Skstailey {
124840175f0Skstailey /* It is not in the list. */
125840175f0Skstailey binding = NULL;
126840175f0Skstailey break;
127840175f0Skstailey }
128840175f0Skstailey }
129840175f0Skstailey
1303fb98d4aSespie if (binding != NULL)
1313fb98d4aSespie {
1323fb98d4aSespie if (dirnamep)
1333fb98d4aSespie {
1343fb98d4aSespie const char *dirname = *dirnamep;
1353fb98d4aSespie
136840175f0Skstailey if (dirname == NULL)
137840175f0Skstailey /* The current binding has be to returned. */
1383fb98d4aSespie *dirnamep = binding->dirname;
1393fb98d4aSespie else
140840175f0Skstailey {
14128ea187bSespie /* The domain is already bound. If the new value and the old
14228ea187bSespie one are equal we simply do nothing. Otherwise replace the
14328ea187bSespie old binding. */
1443fb98d4aSespie char *result = binding->dirname;
1453fb98d4aSespie if (strcmp (dirname, result) != 0)
14628ea187bSespie {
147*a1acfa9bSespie if (strcmp (dirname, INTUSE(_nl_default_dirname)) == 0)
148*a1acfa9bSespie result = (char *) INTUSE(_nl_default_dirname);
149840175f0Skstailey else
150840175f0Skstailey {
15128ea187bSespie #if defined _LIBC || defined HAVE_STRDUP
1523fb98d4aSespie result = strdup (dirname);
15328ea187bSespie #else
154840175f0Skstailey size_t len = strlen (dirname) + 1;
1553fb98d4aSespie result = (char *) malloc (len);
1563fb98d4aSespie if (__builtin_expect (result != NULL, 1))
1573fb98d4aSespie memcpy (result, dirname, len);
15828ea187bSespie #endif
159840175f0Skstailey }
160840175f0Skstailey
1613fb98d4aSespie if (__builtin_expect (result != NULL, 1))
1623fb98d4aSespie {
163*a1acfa9bSespie if (binding->dirname != INTUSE(_nl_default_dirname))
164840175f0Skstailey free (binding->dirname);
165840175f0Skstailey
1663fb98d4aSespie binding->dirname = result;
1673fb98d4aSespie modified = 1;
168840175f0Skstailey }
16928ea187bSespie }
1703fb98d4aSespie *dirnamep = result;
1713fb98d4aSespie }
1723fb98d4aSespie }
1733fb98d4aSespie
1743fb98d4aSespie if (codesetp)
1753fb98d4aSespie {
1763fb98d4aSespie const char *codeset = *codesetp;
1773fb98d4aSespie
1783fb98d4aSespie if (codeset == NULL)
1793fb98d4aSespie /* The current binding has be to returned. */
1803fb98d4aSespie *codesetp = binding->codeset;
1813fb98d4aSespie else
1823fb98d4aSespie {
1833fb98d4aSespie /* The domain is already bound. If the new value and the old
1843fb98d4aSespie one are equal we simply do nothing. Otherwise replace the
1853fb98d4aSespie old binding. */
1863fb98d4aSespie char *result = binding->codeset;
1873fb98d4aSespie if (result == NULL || strcmp (codeset, result) != 0)
1883fb98d4aSespie {
1893fb98d4aSespie #if defined _LIBC || defined HAVE_STRDUP
1903fb98d4aSespie result = strdup (codeset);
1913fb98d4aSespie #else
1923fb98d4aSespie size_t len = strlen (codeset) + 1;
1933fb98d4aSespie result = (char *) malloc (len);
1943fb98d4aSespie if (__builtin_expect (result != NULL, 1))
1953fb98d4aSespie memcpy (result, codeset, len);
1963fb98d4aSespie #endif
1973fb98d4aSespie
1983fb98d4aSespie if (__builtin_expect (result != NULL, 1))
1993fb98d4aSespie {
2003fb98d4aSespie if (binding->codeset != NULL)
2013fb98d4aSespie free (binding->codeset);
2023fb98d4aSespie
2033fb98d4aSespie binding->codeset = result;
2043fb98d4aSespie binding->codeset_cntr++;
2053fb98d4aSespie modified = 1;
2063fb98d4aSespie }
2073fb98d4aSespie }
2083fb98d4aSespie *codesetp = result;
2093fb98d4aSespie }
2103fb98d4aSespie }
2113fb98d4aSespie }
2123fb98d4aSespie else if ((dirnamep == NULL || *dirnamep == NULL)
2133fb98d4aSespie && (codesetp == NULL || *codesetp == NULL))
2143fb98d4aSespie {
2153fb98d4aSespie /* Simply return the default values. */
2163fb98d4aSespie if (dirnamep)
217*a1acfa9bSespie *dirnamep = INTUSE(_nl_default_dirname);
2183fb98d4aSespie if (codesetp)
2193fb98d4aSespie *codesetp = NULL;
2203fb98d4aSespie }
221840175f0Skstailey else
222840175f0Skstailey {
223840175f0Skstailey /* We have to create a new binding. */
2243fb98d4aSespie size_t len = strlen (domainname) + 1;
225840175f0Skstailey struct binding *new_binding =
2263fb98d4aSespie (struct binding *) malloc (offsetof (struct binding, domainname) + len);
227840175f0Skstailey
2283fb98d4aSespie if (__builtin_expect (new_binding == NULL, 0))
2293fb98d4aSespie goto failed;
230840175f0Skstailey
231840175f0Skstailey memcpy (new_binding->domainname, domainname, len);
232840175f0Skstailey
2333fb98d4aSespie if (dirnamep)
2343fb98d4aSespie {
2353fb98d4aSespie const char *dirname = *dirnamep;
2363fb98d4aSespie
2373fb98d4aSespie if (dirname == NULL)
2383fb98d4aSespie /* The default value. */
239*a1acfa9bSespie dirname = INTUSE(_nl_default_dirname);
240840175f0Skstailey else
241840175f0Skstailey {
242*a1acfa9bSespie if (strcmp (dirname, INTUSE(_nl_default_dirname)) == 0)
243*a1acfa9bSespie dirname = INTUSE(_nl_default_dirname);
2443fb98d4aSespie else
2453fb98d4aSespie {
2463fb98d4aSespie char *result;
24728ea187bSespie #if defined _LIBC || defined HAVE_STRDUP
2483fb98d4aSespie result = strdup (dirname);
2493fb98d4aSespie if (__builtin_expect (result == NULL, 0))
2503fb98d4aSespie goto failed_dirname;
25128ea187bSespie #else
2523fb98d4aSespie size_t len = strlen (dirname) + 1;
2533fb98d4aSespie result = (char *) malloc (len);
2543fb98d4aSespie if (__builtin_expect (result == NULL, 0))
2553fb98d4aSespie goto failed_dirname;
2563fb98d4aSespie memcpy (result, dirname, len);
25728ea187bSespie #endif
2583fb98d4aSespie dirname = result;
259840175f0Skstailey }
2603fb98d4aSespie }
2613fb98d4aSespie *dirnamep = dirname;
2623fb98d4aSespie new_binding->dirname = (char *) dirname;
2633fb98d4aSespie }
2643fb98d4aSespie else
2653fb98d4aSespie /* The default value. */
266*a1acfa9bSespie new_binding->dirname = (char *) INTUSE(_nl_default_dirname);
2673fb98d4aSespie
2683fb98d4aSespie new_binding->codeset_cntr = 0;
2693fb98d4aSespie
2703fb98d4aSespie if (codesetp)
2713fb98d4aSespie {
2723fb98d4aSespie const char *codeset = *codesetp;
2733fb98d4aSespie
2743fb98d4aSespie if (codeset != NULL)
2753fb98d4aSespie {
2763fb98d4aSespie char *result;
2773fb98d4aSespie
2783fb98d4aSespie #if defined _LIBC || defined HAVE_STRDUP
2793fb98d4aSespie result = strdup (codeset);
2803fb98d4aSespie if (__builtin_expect (result == NULL, 0))
2813fb98d4aSespie goto failed_codeset;
2823fb98d4aSespie #else
2833fb98d4aSespie size_t len = strlen (codeset) + 1;
2843fb98d4aSespie result = (char *) malloc (len);
2853fb98d4aSespie if (__builtin_expect (result == NULL, 0))
2863fb98d4aSespie goto failed_codeset;
2873fb98d4aSespie memcpy (result, codeset, len);
2883fb98d4aSespie #endif
2893fb98d4aSespie codeset = result;
2903fb98d4aSespie new_binding->codeset_cntr++;
2913fb98d4aSespie }
2923fb98d4aSespie *codesetp = codeset;
2933fb98d4aSespie new_binding->codeset = (char *) codeset;
2943fb98d4aSespie }
2953fb98d4aSespie else
2963fb98d4aSespie new_binding->codeset = NULL;
297840175f0Skstailey
298840175f0Skstailey /* Now enqueue it. */
299840175f0Skstailey if (_nl_domain_bindings == NULL
300840175f0Skstailey || strcmp (domainname, _nl_domain_bindings->domainname) < 0)
301840175f0Skstailey {
302840175f0Skstailey new_binding->next = _nl_domain_bindings;
303840175f0Skstailey _nl_domain_bindings = new_binding;
304840175f0Skstailey }
305840175f0Skstailey else
306840175f0Skstailey {
307840175f0Skstailey binding = _nl_domain_bindings;
308840175f0Skstailey while (binding->next != NULL
309840175f0Skstailey && strcmp (domainname, binding->next->domainname) > 0)
310840175f0Skstailey binding = binding->next;
311840175f0Skstailey
312840175f0Skstailey new_binding->next = binding->next;
313840175f0Skstailey binding->next = new_binding;
314840175f0Skstailey }
315840175f0Skstailey
3163fb98d4aSespie modified = 1;
3173fb98d4aSespie
3183fb98d4aSespie /* Here we deal with memory allocation failures. */
3193fb98d4aSespie if (0)
3203fb98d4aSespie {
3213fb98d4aSespie failed_codeset:
322*a1acfa9bSespie if (new_binding->dirname != INTUSE(_nl_default_dirname))
3233fb98d4aSespie free (new_binding->dirname);
3243fb98d4aSespie failed_dirname:
3253fb98d4aSespie free (new_binding);
3263fb98d4aSespie failed:
3273fb98d4aSespie if (dirnamep)
3283fb98d4aSespie *dirnamep = NULL;
3293fb98d4aSespie if (codesetp)
3303fb98d4aSespie *codesetp = NULL;
3313fb98d4aSespie }
332840175f0Skstailey }
333840175f0Skstailey
3343fb98d4aSespie /* If we modified any binding, we flush the caches. */
3353fb98d4aSespie if (modified)
3363fb98d4aSespie ++_nl_msg_cat_cntr;
3373fb98d4aSespie
3383fb98d4aSespie __libc_rwlock_unlock (_nl_state_lock);
3393fb98d4aSespie }
3403fb98d4aSespie
3413fb98d4aSespie /* Specify that the DOMAINNAME message catalog will be found
3423fb98d4aSespie in DIRNAME rather than in the system locale data base. */
3433fb98d4aSespie char *
BINDTEXTDOMAIN(const char * domainname,const char * dirname)344*a1acfa9bSespie BINDTEXTDOMAIN (const char *domainname, const char *dirname)
3453fb98d4aSespie {
3463fb98d4aSespie set_binding_values (domainname, &dirname, NULL);
3473fb98d4aSespie return (char *) dirname;
3483fb98d4aSespie }
3493fb98d4aSespie
3503fb98d4aSespie /* Specify the character encoding in which the messages from the
3513fb98d4aSespie DOMAINNAME message catalog will be returned. */
3523fb98d4aSespie char *
BIND_TEXTDOMAIN_CODESET(const char * domainname,const char * codeset)353*a1acfa9bSespie BIND_TEXTDOMAIN_CODESET (const char *domainname, const char *codeset)
3543fb98d4aSespie {
3553fb98d4aSespie set_binding_values (domainname, NULL, &codeset);
3563fb98d4aSespie return (char *) codeset;
357840175f0Skstailey }
358840175f0Skstailey
359840175f0Skstailey #ifdef _LIBC
3603fb98d4aSespie /* Aliases for function names in GNU C Library. */
361840175f0Skstailey weak_alias (__bindtextdomain, bindtextdomain);
3623fb98d4aSespie weak_alias (__bind_textdomain_codeset, bind_textdomain_codeset);
363840175f0Skstailey #endif
364