1/* 2 * Copyright (c) 2005, 2008 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24#include "xlocale_private.h" 25#include <errno.h> 26#include <stddef.h> 27#include <string.h> 28#include "ldpart.h" 29 30#define NMBSTATET 10 31#define C_LOCALE_INITIALIZER { \ 32 0, XPERMANENT, \ 33 {}, {}, {}, {}, {}, \ 34 {}, {}, {}, {}, {}, \ 35 LOCK_INITIALIZER, \ 36 XMAGIC, \ 37 1, 0, 0, 0, 0, 0, 1, 1, 0, \ 38 NULL, \ 39 &_DefaultRuneXLocale, \ 40} 41 42static char C[] = "C"; 43static struct _xlocale __c_locale = C_LOCALE_INITIALIZER; 44const locale_t _c_locale = (const locale_t)&__c_locale; 45struct _xlocale __global_locale = C_LOCALE_INITIALIZER; 46pthread_key_t __locale_key = (pthread_key_t)-1; 47 48extern int __collate_load_tables(const char *, locale_t); 49extern int __detect_path_locale(void); 50extern const char *__get_locale_env(int); 51extern int __messages_load_locale(const char *, locale_t); 52extern int __monetary_load_locale(const char *, locale_t); 53extern int __numeric_load_locale(const char *, locale_t); 54extern int __setrunelocale(const char *, locale_t); 55extern int __time_load_locale(const char *, locale_t); 56 57static void _releaselocale(locale_t loc); 58 59/* 60 * check that the encoding is the right size, isn't . or .. and doesn't 61 * contain any slashes 62 */ 63static inline __attribute__((always_inline)) int 64_checkencoding(const char *encoding) 65{ 66 return (encoding && (strlen(encoding) > ENCODING_LEN 67 || (encoding[0] == '.' && (encoding[1] == 0 68 || (encoding[1] == '.' && encoding[2] == 0))) 69 || strchr(encoding, '/') != NULL)) ? -1 : 0; 70} 71 72/* 73 * check that the locale has the right magic number 74 */ 75static inline __attribute__((always_inline)) int 76_checklocale(const locale_t loc) 77{ 78 if (!loc) 79 return 0; 80 return (loc == LC_GLOBAL_LOCALE || loc->__magic == XMAGIC) ? 0 : -1; 81} 82 83/* 84 * copy a locale_t except anything before the magic value 85 */ 86static inline __attribute__((always_inline)) void 87_copylocale(locale_t dst, const locale_t src) 88{ 89 memcpy(&dst->__magic, &src->__magic, sizeof(*dst) - offsetof(struct _xlocale, __magic)); 90} 91 92/* 93 * Make a copy of a locale_t, locking/unlocking the source. 94 * A NULL locale_t means to make a copy of the current 95 * locale while LC_GLOBAL_LOCALE means to copy the global locale. If 96 * &__c_locale is passed (meaning a C locale is desired), just make 97 * a copy. 98 */ 99static locale_t 100_duplocale(locale_t loc) 101{ 102 locale_t new; 103 104 if ((new = (locale_t)malloc(sizeof(struct _xlocale))) == NULL) 105 return NULL; 106 new->__refcount = 1; 107 new->__free_extra = (__free_extra_t)_releaselocale; 108 new->__lock = LOCK_INITIALIZER; 109 if (loc == NULL) 110 loc = __current_locale(); 111 else if (loc == LC_GLOBAL_LOCALE) 112 loc = &__global_locale; 113 else if (loc == &__c_locale) { 114 *new = __c_locale; 115 new->__refcount = 1; 116 new->__free_extra = (__free_extra_t)_releaselocale; 117 new->__lock = LOCK_INITIALIZER; 118 return new; 119 } 120 XL_LOCK(loc); 121 _copylocale(new, loc); 122 XL_UNLOCK(loc); 123 /* __mbs_mblen is the first of NMBSTATET mbstate_t buffers */ 124 bzero(&new->__mbs_mblen, offsetof(struct _xlocale, __magic) 125 - offsetof(struct _xlocale, __mbs_mblen)); 126 /* collate */ 127 XL_RETAIN(new->__lc_collate); 128 /* ctype */ 129 XL_RETAIN(new->__lc_ctype); 130 /* messages */ 131 XL_RETAIN(new->__lc_messages); 132 /* monetary */ 133 XL_RETAIN(new->__lc_monetary); 134 /* numeric */ 135 XL_RETAIN(new->__lc_numeric); 136 XL_RETAIN(new->__lc_numeric_loc); 137 /* time */ 138 XL_RETAIN(new->__lc_time); 139 140 return new; 141} 142 143/* 144 * Modify a locale_t, setting the parts specified in the mask 145 * to the locale specified by the string. If the string is NULL, the C 146 * locale is used. If the string is empty, the value is determined from 147 * the environment. -1 is returned on error, and loc is in a partial state. 148 */ 149static int 150_modifylocale(locale_t loc, int mask, __const char *locale) 151{ 152 int m, ret; 153 const char *enc = NULL; 154 char *oenc; 155 156 if (!locale) 157 locale = C; 158 159 ret = __detect_path_locale(); 160 if (ret) { 161 errno = ret; 162 return -1; 163 } 164 165 if (*locale) 166 enc = locale; 167 for(m = 1; m <= _LC_LAST_MASK; m <<= 1) { 168 if (m & mask) { 169 switch(m) { 170 case LC_COLLATE_MASK: 171 if (!*locale) { 172 enc = __get_locale_env(LC_COLLATE); 173 if (_checkencoding(enc) < 0) { 174 errno = EINVAL; 175 return -1; 176 } 177 } 178 oenc = (loc->__collate_load_error ? C : loc->__lc_collate->__encoding); 179 if (strcmp(enc, oenc) != 0 && __collate_load_tables(enc, loc) == _LDP_ERROR) 180 return -1; 181 break; 182 case LC_CTYPE_MASK: 183 if (!*locale) { 184 enc = __get_locale_env(LC_CTYPE); 185 if (_checkencoding(enc) < 0) { 186 errno = EINVAL; 187 return -1; 188 } 189 } 190 if (strcmp(enc, loc->__lc_ctype->__ctype_encoding) != 0) { 191 if ((ret = __setrunelocale(enc, loc)) != 0) { 192 errno = ret; 193 return -1; 194 } 195 if (loc->__numeric_fp_cvt == LC_NUMERIC_FP_SAME_LOCALE) 196 loc->__numeric_fp_cvt = LC_NUMERIC_FP_UNINITIALIZED; 197 } 198 break; 199 case LC_MESSAGES_MASK: 200 if (!*locale) { 201 enc = __get_locale_env(LC_MESSAGES); 202 if (_checkencoding(enc) < 0) { 203 errno = EINVAL; 204 return -1; 205 } 206 } 207 oenc = (loc->_messages_using_locale ? loc->__lc_messages->_messages_locale_buf : C); 208 if (strcmp(enc, oenc) != 0 && __messages_load_locale(enc, loc) == _LDP_ERROR) 209 return -1; 210 break; 211 case LC_MONETARY_MASK: 212 if (!*locale) { 213 enc = __get_locale_env(LC_MONETARY); 214 if (_checkencoding(enc) < 0) { 215 errno = EINVAL; 216 return -1; 217 } 218 } 219 oenc = (loc->_monetary_using_locale ? loc->__lc_monetary->_monetary_locale_buf : C); 220 if (strcmp(enc, oenc) != 0 && __monetary_load_locale(enc, loc) == _LDP_ERROR) 221 return -1; 222 break; 223 case LC_NUMERIC_MASK: 224 if (!*locale) { 225 enc = __get_locale_env(LC_NUMERIC); 226 if (_checkencoding(enc) < 0) { 227 errno = EINVAL; 228 return -1; 229 } 230 } 231 oenc = (loc->_numeric_using_locale ? loc->__lc_numeric->_numeric_locale_buf : C); 232 if (strcmp(enc, oenc) != 0) { 233 if (__numeric_load_locale(enc, loc) == _LDP_ERROR) 234 return -1; 235 loc->__numeric_fp_cvt = LC_NUMERIC_FP_UNINITIALIZED; 236 XL_RELEASE(loc->__lc_numeric_loc); 237 loc->__lc_numeric_loc = NULL; 238 } 239 break; 240 case LC_TIME_MASK: 241 if (!*locale) { 242 enc = __get_locale_env(LC_TIME); 243 if (_checkencoding(enc) < 0) { 244 errno = EINVAL; 245 return -1; 246 } 247 } 248 oenc = (loc->_time_using_locale ? loc->__lc_time->_time_locale_buf : C); 249 if (strcmp(enc, oenc) != 0 && __time_load_locale(enc, loc) == _LDP_ERROR) 250 return -1; 251 break; 252 } 253 } 254 } 255 return 0; 256} 257 258/* 259 * release all the memory objects (the memory will be freed when the refcount 260 * becomes zero) 261 */ 262static void 263_releaselocale(locale_t loc) 264{ 265 /* collate */ 266 XL_RELEASE(loc->__lc_collate); 267 /* ctype */ 268 XL_RELEASE(loc->__lc_ctype); 269 /* messages */ 270 XL_RELEASE(loc->__lc_messages); 271 /* monetary */ 272 XL_RELEASE(loc->__lc_monetary); 273 /* numeric */ 274 XL_RELEASE(loc->__lc_numeric); 275 XL_RELEASE(loc->__lc_numeric_loc); 276 /* time */ 277 XL_RELEASE(loc->__lc_time); 278} 279 280/* 281 * EXTERNAL: Duplicate a (non-NULL) locale_t. LC_GLOBAL_LOCALE means the 282 * global locale, while NULL means the current locale. NULL is returned 283 * on error. 284 */ 285locale_t 286duplocale(locale_t loc) 287{ 288 if (_checklocale(loc) < 0) { 289 errno = EINVAL; 290 return NULL; 291 } 292 return _duplocale(loc); 293} 294 295/* 296 * EXTERNAL: Free a locale_t, releasing all memory objects. Don't free 297 * illegal locale_t's or the global locale. 298 */ 299int 300freelocale(locale_t loc) 301{ 302 if (!loc || _checklocale(loc) < 0 || loc == &__global_locale 303 || loc == LC_GLOBAL_LOCALE || loc == &__c_locale) { 304 errno = EINVAL; 305 return -1; 306 } 307 XL_RELEASE(loc); 308 return 0; 309} 310 311/* 312 * EXTERNAL: Create a new locale_t, based on the base locale_t, and modified 313 * by the mask and locale string. If the base is NULL, the current locale 314 * is used as the base. If locale is NULL, changes are made from the C locale 315 * for categories set in mask. 316 */ 317locale_t 318newlocale(int mask, __const char *locale, locale_t base) 319{ 320 locale_t new; 321 int lcmask = (mask & LC_ALL_MASK); 322 323 if (_checkencoding(locale) < 0) { 324 errno = EINVAL; 325 return NULL; 326 } 327 if (lcmask == LC_ALL_MASK) 328 base = (locale_t)&__c_locale; 329 else if (_checklocale(base) < 0) { 330 errno = EINVAL; 331 return NULL; 332 } 333 new = _duplocale(base); 334 if (new == NULL) 335 return NULL; 336 if (lcmask == 0 || (lcmask == LC_ALL_MASK && locale == NULL)) 337 return new; 338 if (_modifylocale(new, lcmask, locale) < 0) { 339 freelocale(new); 340 return NULL; 341 } 342 return new; 343} 344 345/* 346 * PRIVATE EXTERNAL: Returns the locale that can be used by wcstod and 347 * family, to convert the wide character string to a multi-byte string 348 * (the LC_NUMERIC and LC_CTYPE locales may be different). 349 */ 350__private_extern__ locale_t 351__numeric_ctype(locale_t loc) 352{ 353 switch(loc->__numeric_fp_cvt) { 354 case LC_NUMERIC_FP_UNINITIALIZED: { 355 const char *ctype = loc->__lc_ctype->__ctype_encoding; 356 const char *numeric = (loc->_numeric_using_locale ? loc->__lc_numeric->_numeric_locale_buf : C); 357 if (strcmp(ctype, numeric) == 0) { 358 loc->__numeric_fp_cvt = LC_NUMERIC_FP_SAME_LOCALE; 359 return loc; 360 } else { 361 loc->__lc_numeric_loc = newlocale(LC_CTYPE_MASK, numeric, (locale_t)&__c_locale); 362 if (loc->__lc_numeric_loc) { 363 loc->__numeric_fp_cvt = LC_NUMERIC_FP_USE_LOCALE; 364 return loc->__lc_numeric_loc; 365 } else { /* shouldn't happen, but just use the same locale */ 366 loc->__numeric_fp_cvt = LC_NUMERIC_FP_SAME_LOCALE; 367 return loc; 368 } 369 } 370 } 371 case LC_NUMERIC_FP_SAME_LOCALE: 372 return loc; 373 case LC_NUMERIC_FP_USE_LOCALE: 374 return loc->__lc_numeric_loc; 375 } 376 return loc; /* shouldn't happen */ 377} 378 379/* 380 * EXTERNAL: Returns the locale string for the part specified in mask. The 381 * least significant bit is used. If loc is NULL, the current per-thread 382 * locale is used. 383 */ 384const char * 385querylocale(int mask, locale_t loc) 386{ 387 int m; 388 const char *ret; 389 390 if (_checklocale(loc) < 0 || (mask & LC_ALL_MASK) == 0) { 391 errno = EINVAL; 392 return NULL; 393 } 394 DEFAULT_CURRENT_LOCALE(loc); 395 m = ffs(mask); 396 if (m == 0 || m > _LC_NUM_MASK) { 397 errno = EINVAL; 398 return NULL; 399 } 400 XL_LOCK(loc); 401 switch(1 << (m - 1)) { 402 case LC_COLLATE_MASK: 403 ret = (loc->__collate_load_error ? C : loc->__lc_collate->__encoding); 404 break; 405 case LC_CTYPE_MASK: 406 ret = loc->__lc_ctype->__ctype_encoding; 407 break; 408 case LC_MESSAGES_MASK: 409 ret = (loc->_messages_using_locale ? loc->__lc_messages->_messages_locale_buf : C); 410 break; 411 case LC_MONETARY_MASK: 412 ret = (loc->_monetary_using_locale ? loc->__lc_monetary->_monetary_locale_buf : C); 413 break; 414 case LC_NUMERIC_MASK: 415 ret = (loc->_numeric_using_locale ? loc->__lc_numeric->_numeric_locale_buf : C); 416 break; 417 case LC_TIME_MASK: 418 ret = (loc->_time_using_locale ? loc->__lc_time->_time_locale_buf : C); 419 break; 420 default: 421 /* should never get here */ 422 XL_UNLOCK(loc); 423 errno = EINVAL; 424 return NULL; 425 } 426 XL_UNLOCK(loc); 427 return ret; 428} 429 430/* 431 * EXTERNAL: Set the thread-specific locale. The previous locale is returned. 432 * Use LC_GLOBAL_LOCALE to set the global locale. LC_GLOBAL_LOCALE 433 * may also be returned if there was no previous thread-specific locale in 434 * effect. If loc is NULL, the current locale is returned, but no locale 435 * chance is made. NULL is returned on error. 436 */ 437locale_t 438uselocale(locale_t loc) 439{ 440 locale_t orig; 441 442 if (loc == NULL) 443 orig = (locale_t)pthread_getspecific(__locale_key); 444 else { 445 if (_checklocale(loc) < 0) { 446 errno = EINVAL; 447 return NULL; 448 } 449 if (loc == LC_GLOBAL_LOCALE || 450 loc == &__global_locale) /* should never happen */ 451 loc = NULL; 452 XL_RETAIN(loc); 453 orig = pthread_getspecific(__locale_key); 454 pthread_setspecific(__locale_key, loc); 455 XL_RELEASE(orig); 456 } 457 return (orig ? orig : LC_GLOBAL_LOCALE); 458} 459 460/* 461 * EXTERNAL: Used by the MB_CUR_MAX macro to determine the thread-specific 462 * value. 463 */ 464int 465___mb_cur_max(void) 466{ 467 return __current_locale()->__lc_ctype->__mb_cur_max; 468} 469 470/* 471 * EXTERNAL: Used by the MB_CUR_MAX_L macro to determine the thread-specific 472 * value, from the given locale_t. 473 */ 474int 475___mb_cur_max_l(locale_t loc) 476{ 477 return __locale_ptr(loc)->__lc_ctype->__mb_cur_max; 478} 479 480static void 481__xlocale_release(void *loc) 482{ 483 XL_RELEASE((locale_t)loc); 484} 485 486/* 487 * Called from the Libc initializer to setup the thread-specific key. 488 */ 489__private_extern__ void 490__xlocale_init(void) 491{ 492 if (__locale_key == (pthread_key_t)-1) { 493 __locale_key = __LIBC_PTHREAD_KEY_XLOCALE; 494 pthread_key_init_np(__locale_key, __xlocale_release); 495 } 496} 497 498