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