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