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