Use rwlock to avoid surprising results in multithreaded applications.
[kopensolaris-gnu/glibc.git] / intl / bindtextdom.c
1 /* Implementation of the bindtextdomain(3) function
2    Copyright (C) 1995, 1996, 1997, 1998, 2000 Free Software Foundation, Inc.
3
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Library General Public License as
6    published by the Free Software Foundation; either version 2 of the
7    License, or (at your option) any later version.
8
9    The GNU C Library 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 the GNU C Library; see the file COPYING.LIB.  If not,
16    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17    Boston, MA 02111-1307, USA.  */
18
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22
23 #if defined STDC_HEADERS || defined _LIBC
24 # include <stdlib.h>
25 #else
26 # ifdef HAVE_MALLOC_H
27 #  include <malloc.h>
28 # else
29 void free ();
30 # endif
31 #endif
32
33 #if defined HAVE_STRING_H || defined _LIBC
34 # include <string.h>
35 #else
36 # include <strings.h>
37 # ifndef memcpy
38 #  define memcpy(Dst, Src, Num) bcopy (Src, Dst, Num)
39 # endif
40 #endif
41
42 #ifdef _LIBC
43 # include <libintl.h>
44 #else
45 # include "libgettext.h"
46 #endif
47 #include "gettext.h"
48 #include "gettextP.h"
49
50 #ifdef _LIBC
51 /* We have to handle multi-threaded applications.  */
52 # include <bits/libc-lock.h>
53 #else
54 /* Provide dummy implementation if this is outside glibc.  */
55 # define __libc_lock_define_initialized (CLASS, NAME)
56 # define __libc_lock_lock(NAME)
57 # define __libc_lock_unlock(NAME)
58 #endif
59
60 /* @@ end of prolog @@ */
61
62 /* Contains the default location of the message catalogs.  */
63 extern const char _nl_default_dirname[];
64
65 /* List with bindings of specific domains.  */
66 extern struct binding *_nl_domain_bindings;
67
68 /* Lock variable to protect the global data in the gettext implementation.  */
69 __libc_rwlock_define (extern, _nl_state_lock)
70
71
72 /* Names for the libintl functions are a problem.  They must not clash
73    with existing names and they should follow ANSI C.  But this source
74    code is also used in GNU C Library where the names have a __
75    prefix.  So we have to make a difference here.  */
76 #ifdef _LIBC
77 # define BINDTEXTDOMAIN __bindtextdomain
78 # ifndef strdup
79 #  define strdup(str) __strdup (str)
80 # endif
81 #else
82 # define BINDTEXTDOMAIN bindtextdomain__
83 #endif
84
85 /* Specify that the DOMAINNAME message catalog will be found
86    in DIRNAME rather than in the system locale data base.  */
87 char *
88 BINDTEXTDOMAIN (domainname, dirname)
89      const char *domainname;
90      const char *dirname;
91 {
92   struct binding *binding;
93   char *result;
94
95   /* Some sanity checks.  */
96   if (domainname == NULL || domainname[0] == '\0')
97     return NULL;
98
99   __libc_rwlock_wrlock (_nl_state_lock);
100
101   for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next)
102     {
103       int compare = strcmp (domainname, binding->domainname);
104       if (compare == 0)
105         /* We found it!  */
106         break;
107       if (compare < 0)
108         {
109           /* It is not in the list.  */
110           binding = NULL;
111           break;
112         }
113     }
114
115   if (dirname == NULL)
116     /* The current binding has be to returned.  */
117     result = binding == NULL ? (char *) _nl_default_dirname : binding->dirname;
118   else if (binding != NULL)
119     {
120       /* The domain is already bound.  If the new value and the old
121          one are equal we simply do nothing.  Otherwise replace the
122          old binding.  */
123       result = binding->dirname;
124       if (strcmp (dirname, result) != 0)
125         {
126           if (strcmp (dirname, _nl_default_dirname) == 0)
127             result = (char *) _nl_default_dirname;
128           else
129             {
130 #if defined _LIBC || defined HAVE_STRDUP
131               result = strdup (dirname);
132 #else
133               size_t len = strlen (dirname) + 1;
134               result = (char *) malloc (len);
135               if (result != NULL)
136                 memcpy (result, dirname, len);
137 #endif
138             }
139
140           if (result != NULL)
141             {
142               if (binding->dirname != _nl_default_dirname)
143                 free (binding->dirname);
144
145               binding->dirname = result;
146             }
147         }
148     }
149   else
150     {
151       /* We have to create a new binding.  */
152       size_t len = strlen (domainname) + 1;
153       struct binding *new_binding =
154         (struct binding *) malloc (sizeof (*new_binding) + len);
155
156       if (new_binding == NULL)
157         result = NULL;
158       else
159         {
160           memcpy (new_binding->domainname, domainname, len);
161
162           if (strcmp (dirname, _nl_default_dirname) == 0)
163             result = new_binding->dirname = (char *) _nl_default_dirname;
164           else
165             {
166 #if defined _LIBC || defined HAVE_STRDUP
167               result = new_binding->dirname = strdup (dirname);
168 #else
169               len = strlen (dirname) + 1;
170               result = new_binding->dirname = (char *) malloc (len);
171               if (result != NULL)
172                 memcpy (new_binding->dirname, dirname, len);
173 #endif
174             }
175         }
176
177       if (result != NULL)
178         {
179           /* Now enqueue it.  */
180           if (_nl_domain_bindings == NULL
181               || strcmp (domainname, _nl_domain_bindings->domainname) < 0)
182             {
183               new_binding->next = _nl_domain_bindings;
184               _nl_domain_bindings = new_binding;
185             }
186           else
187             {
188               binding = _nl_domain_bindings;
189               while (binding->next != NULL
190                      && strcmp (domainname, binding->next->domainname) > 0)
191                 binding = binding->next;
192
193               new_binding->next = binding->next;
194               binding->next = new_binding;
195             }
196         }
197       else if (new_binding != NULL)
198         free (new_binding);
199     }
200
201   /* For a succesful call we flush the caches.  */
202   if (result != NULL)
203     ++_nl_msg_cat_cntr;
204
205   __libc_rwlock_unlock (_nl_state_lock);
206
207   return result;
208 }
209
210 #ifdef _LIBC
211 /* Alias for function name in GNU C Library.  */
212 weak_alias (__bindtextdomain, bindtextdomain);
213 #endif