Use good m_ functions, because it smell like a flower, version 2.
[apps/madmutt.git] / intl / bindtextdom.c
1 /* Implementation of the bindtextdomain(3) function
2    Copyright (C) 1995-1998, 2000-2003, 2005-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
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 #include "gettextP.h"
28 #ifdef _LIBC
29 # include <libintl.h>
30 #else
31 # include "libgnuintl.h"
32 #endif
33
34 /* Handle multi-threaded applications.  */
35 #ifdef _LIBC
36 # include <bits/libc-lock.h>
37 # define gl_rwlock_define __libc_rwlock_define
38 # define gl_rwlock_wrlock __libc_rwlock_wrlock
39 # define gl_rwlock_unlock __libc_rwlock_unlock
40 #else
41 # include "lock.h"
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
57 /* @@ end of prolog @@ */
58
59 /* Contains the default location of the message catalogs.  */
60 extern const char _nl_default_dirname[];
61 #ifdef _LIBC
62 libc_hidden_proto (_nl_default_dirname)
63 #endif
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 gl_rwlock_define (extern, _nl_state_lock attribute_hidden)
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 # define BIND_TEXTDOMAIN_CODESET __bind_textdomain_codeset
79 # ifndef strdup
80 #  define strdup(str) __strdup (str)
81 # endif
82 #else
83 # define BINDTEXTDOMAIN libintl_bindtextdomain
84 # define BIND_TEXTDOMAIN_CODESET libintl_bind_textdomain_codeset
85 #endif
86
87 /* Specifies the directory name *DIRNAMEP and the output codeset *CODESETP
88    to be used for the DOMAINNAME message catalog.
89    If *DIRNAMEP or *CODESETP is NULL, the corresponding attribute is not
90    modified, only the current value is returned.
91    If DIRNAMEP or CODESETP is NULL, the corresponding attribute is neither
92    modified nor returned.  */
93 static void
94 set_binding_values (const char *domainname,
95                     const char **dirnamep, const char **codesetp)
96 {
97   struct binding *binding;
98   int modified;
99
100   /* Some sanity checks.  */
101   if (domainname == NULL || domainname[0] == '\0')
102     {
103       if (dirnamep)
104         *dirnamep = NULL;
105       if (codesetp)
106         *codesetp = NULL;
107       return;
108     }
109
110   gl_rwlock_wrlock (_nl_state_lock);
111
112   modified = 0;
113
114   for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next)
115     {
116       int compare = strcmp (domainname, binding->domainname);
117       if (compare == 0)
118         /* We found it!  */
119         break;
120       if (compare < 0)
121         {
122           /* It is not in the list.  */
123           binding = NULL;
124           break;
125         }
126     }
127
128   if (binding != NULL)
129     {
130       if (dirnamep)
131         {
132           const char *dirname = *dirnamep;
133
134           if (dirname == NULL)
135             /* The current binding has be to returned.  */
136             *dirnamep = binding->dirname;
137           else
138             {
139               /* The domain is already bound.  If the new value and the old
140                  one are equal we simply do nothing.  Otherwise replace the
141                  old binding.  */
142               char *result = binding->dirname;
143               if (strcmp (dirname, result) != 0)
144                 {
145                   if (strcmp (dirname, _nl_default_dirname) == 0)
146                     result = (char *) _nl_default_dirname;
147                   else
148                     {
149 #if defined _LIBC || defined HAVE_STRDUP
150                       result = strdup (dirname);
151 #else
152                       size_t len = strlen (dirname) + 1;
153                       result = (char *) malloc (len);
154                       if (__builtin_expect (result != NULL, 1))
155                         memcpy (result, dirname, len);
156 #endif
157                     }
158
159                   if (__builtin_expect (result != NULL, 1))
160                     {
161                       if (binding->dirname != _nl_default_dirname)
162                         free (binding->dirname);
163
164                       binding->dirname = result;
165                       modified = 1;
166                     }
167                 }
168               *dirnamep = result;
169             }
170         }
171
172       if (codesetp)
173         {
174           const char *codeset = *codesetp;
175
176           if (codeset == NULL)
177             /* The current binding has be to returned.  */
178             *codesetp = binding->codeset;
179           else
180             {
181               /* The domain is already bound.  If the new value and the old
182                  one are equal we simply do nothing.  Otherwise replace the
183                  old binding.  */
184               char *result = binding->codeset;
185               if (result == NULL || strcmp (codeset, result) != 0)
186                 {
187 #if defined _LIBC || defined HAVE_STRDUP
188                   result = strdup (codeset);
189 #else
190                   size_t len = strlen (codeset) + 1;
191                   result = (char *) malloc (len);
192                   if (__builtin_expect (result != NULL, 1))
193                     memcpy (result, codeset, len);
194 #endif
195
196                   if (__builtin_expect (result != NULL, 1))
197                     {
198                       if (binding->codeset != NULL)
199                         free (binding->codeset);
200
201                       binding->codeset = result;
202                       modified = 1;
203                     }
204                 }
205               *codesetp = result;
206             }
207         }
208     }
209   else if ((dirnamep == NULL || *dirnamep == NULL)
210            && (codesetp == NULL || *codesetp == NULL))
211     {
212       /* Simply return the default values.  */
213       if (dirnamep)
214         *dirnamep = _nl_default_dirname;
215       if (codesetp)
216         *codesetp = NULL;
217     }
218   else
219     {
220       /* We have to create a new binding.  */
221       size_t len = strlen (domainname) + 1;
222       struct binding *new_binding =
223         (struct binding *) malloc (offsetof (struct binding, domainname) + len);
224
225       if (__builtin_expect (new_binding == NULL, 0))
226         goto failed;
227
228       memcpy (new_binding->domainname, domainname, len);
229
230       if (dirnamep)
231         {
232           const char *dirname = *dirnamep;
233
234           if (dirname == NULL)
235             /* The default value.  */
236             dirname = _nl_default_dirname;
237           else
238             {
239               if (strcmp (dirname, _nl_default_dirname) == 0)
240                 dirname = _nl_default_dirname;
241               else
242                 {
243                   char *result;
244 #if defined _LIBC || defined HAVE_STRDUP
245                   result = strdup (dirname);
246                   if (__builtin_expect (result == NULL, 0))
247                     goto failed_dirname;
248 #else
249                   size_t len = strlen (dirname) + 1;
250                   result = (char *) malloc (len);
251                   if (__builtin_expect (result == NULL, 0))
252                     goto failed_dirname;
253                   memcpy (result, dirname, len);
254 #endif
255                   dirname = result;
256                 }
257             }
258           *dirnamep = dirname;
259           new_binding->dirname = (char *) dirname;
260         }
261       else
262         /* The default value.  */
263         new_binding->dirname = (char *) _nl_default_dirname;
264
265       if (codesetp)
266         {
267           const char *codeset = *codesetp;
268
269           if (codeset != NULL)
270             {
271               char *result;
272
273 #if defined _LIBC || defined HAVE_STRDUP
274               result = strdup (codeset);
275               if (__builtin_expect (result == NULL, 0))
276                 goto failed_codeset;
277 #else
278               size_t len = strlen (codeset) + 1;
279               result = (char *) malloc (len);
280               if (__builtin_expect (result == NULL, 0))
281                 goto failed_codeset;
282               memcpy (result, codeset, len);
283 #endif
284               codeset = result;
285             }
286           *codesetp = codeset;
287           new_binding->codeset = (char *) codeset;
288         }
289       else
290         new_binding->codeset = NULL;
291
292       /* Now enqueue it.  */
293       if (_nl_domain_bindings == NULL
294           || strcmp (domainname, _nl_domain_bindings->domainname) < 0)
295         {
296           new_binding->next = _nl_domain_bindings;
297           _nl_domain_bindings = new_binding;
298         }
299       else
300         {
301           binding = _nl_domain_bindings;
302           while (binding->next != NULL
303                  && strcmp (domainname, binding->next->domainname) > 0)
304             binding = binding->next;
305
306           new_binding->next = binding->next;
307           binding->next = new_binding;
308         }
309
310       modified = 1;
311
312       /* Here we deal with memory allocation failures.  */
313       if (0)
314         {
315         failed_codeset:
316           if (new_binding->dirname != _nl_default_dirname)
317             free (new_binding->dirname);
318         failed_dirname:
319           free (new_binding);
320         failed:
321           if (dirnamep)
322             *dirnamep = NULL;
323           if (codesetp)
324             *codesetp = NULL;
325         }
326     }
327
328   /* If we modified any binding, we flush the caches.  */
329   if (modified)
330     ++_nl_msg_cat_cntr;
331
332   gl_rwlock_unlock (_nl_state_lock);
333 }
334
335 /* Specify that the DOMAINNAME message catalog will be found
336    in DIRNAME rather than in the system locale data base.  */
337 char *
338 BINDTEXTDOMAIN (const char *domainname, const char *dirname)
339 {
340   set_binding_values (domainname, &dirname, NULL);
341   return (char *) dirname;
342 }
343
344 /* Specify the character encoding in which the messages from the
345    DOMAINNAME message catalog will be returned.  */
346 char *
347 BIND_TEXTDOMAIN_CODESET (const char *domainname, const char *codeset)
348 {
349   set_binding_values (domainname, NULL, &codeset);
350   return (char *) codeset;
351 }
352
353 #ifdef _LIBC
354 /* Aliases for function names in GNU C Library.  */
355 weak_alias (__bindtextdomain, bindtextdomain);
356 weak_alias (__bind_textdomain_codeset, bind_textdomain_codeset);
357 #endif