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