X-Git-Url: https://git.saurik.com/apple/libc.git/blobdiff_plain/3d9156a7a519a5e3aa1b92e9d9d4b991f1aed7ff..7b00c0c43f52e9d27168e67a26aac19065cdb40c:/locale/xlocale.c diff --git a/locale/xlocale.c b/locale/xlocale.c index 12de5b4..c0fa5af 100644 --- a/locale/xlocale.c +++ b/locale/xlocale.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005, 2008 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -29,10 +29,12 @@ #define NMBSTATET 10 #define C_LOCALE_INITIALIZER { \ + 0, NULL, \ {}, {}, {}, {}, {}, \ {}, {}, {}, {}, {}, \ + LOCK_INITIALIZER, \ XMAGIC, \ - 1, 0, 0, 0, 0, 0, 1, 1, \ + 1, 0, 0, 0, 0, 0, 1, 1, 0, \ NULL, \ &_DefaultRuneXLocale, \ } @@ -52,6 +54,8 @@ extern int __numeric_load_locale(const char *, locale_t); extern int __setrunelocale(const char *, locale_t); extern int __time_load_locale(const char *, locale_t); +static void _releaselocale(locale_t loc); + /* * check that the encoding is the right size, isn't . or .. and doesn't * contain any slashes @@ -86,8 +90,8 @@ _copylocale(locale_t dst, const locale_t src) } /* - * Make a copy of a locale_t, locking/unlocking the source as determined - * by the lock flag. A NULL locale_t means to make a copy of the current + * Make a copy of a locale_t, locking/unlocking the source. + * A NULL locale_t means to make a copy of the current * locale while LC_GLOBAL_LOCALE means to copy the global locale. If * &__c_locale is passed (meaning a C locale is desired), just make * a copy. @@ -99,17 +103,26 @@ _duplocale(locale_t loc) if ((new = (locale_t)malloc(sizeof(struct _xlocale))) == NULL) return NULL; + new->__refcount = 1; + new->__free_extra = (__free_extra_t)_releaselocale; + new->__lock = LOCK_INITIALIZER; if (loc == NULL) loc = __current_locale(); else if (loc == LC_GLOBAL_LOCALE) loc = &__global_locale; else if (loc == &__c_locale) { *new = __c_locale; + new->__refcount = 1; + new->__free_extra = (__free_extra_t)_releaselocale; + new->__lock = LOCK_INITIALIZER; return new; } + XL_LOCK(loc); _copylocale(new, loc); + XL_UNLOCK(loc); /* __mbs_mblen is the first of NMBSTATET mbstate_t buffers */ - bzero(&new->__mbs_mblen, NMBSTATET * sizeof(new->__mbs_mblen)); + bzero(&new->__mbs_mblen, offsetof(struct _xlocale, __magic) + - offsetof(struct _xlocale, __mbs_mblen)); /* collate */ XL_RETAIN(new->__lc_collate); /* ctype */ @@ -120,10 +133,9 @@ _duplocale(locale_t loc) XL_RETAIN(new->__lc_monetary); /* numeric */ XL_RETAIN(new->__lc_numeric); + XL_RETAIN(new->__lc_numeric_loc); /* time */ XL_RETAIN(new->__lc_time); - /* newale_t */ - XL_RETAIN(new->__lc_localeconv); return new; } @@ -175,9 +187,13 @@ _modifylocale(locale_t loc, int mask, __const char *locale) return -1; } } - if (strcmp(enc, loc->__lc_ctype->__ctype_encoding) != 0 && (ret = __setrunelocale(enc, loc)) != 0) { - errno = ret; - return -1; + if (strcmp(enc, loc->__lc_ctype->__ctype_encoding) != 0) { + if ((ret = __setrunelocale(enc, loc)) != 0) { + errno = ret; + return -1; + } + if (loc->__numeric_fp_cvt == LC_NUMERIC_FP_SAME_LOCALE) + loc->__numeric_fp_cvt = LC_NUMERIC_FP_UNINITIALIZED; } break; case LC_MESSAGES_MASK: @@ -213,8 +229,13 @@ _modifylocale(locale_t loc, int mask, __const char *locale) } } oenc = (loc->_numeric_using_locale ? loc->__lc_numeric->_numeric_locale_buf : C); - if (strcmp(enc, oenc) != 0 && __numeric_load_locale(enc, loc) == _LDP_ERROR) - return -1; + if (strcmp(enc, oenc) != 0) { + if (__numeric_load_locale(enc, loc) == _LDP_ERROR) + return -1; + loc->__numeric_fp_cvt = LC_NUMERIC_FP_UNINITIALIZED; + XL_RELEASE(loc->__lc_numeric_loc); + loc->__lc_numeric_loc = NULL; + } break; case LC_TIME_MASK: if (!*locale) { @@ -251,10 +272,9 @@ _releaselocale(locale_t loc) XL_RELEASE(loc->__lc_monetary); /* numeric */ XL_RELEASE(loc->__lc_numeric); + XL_RELEASE(loc->__lc_numeric_loc); /* time */ XL_RELEASE(loc->__lc_time); - /* locale_t */ - XL_RELEASE(loc->__lc_localeconv); } /* @@ -280,12 +300,11 @@ int freelocale(locale_t loc) { if (!loc || _checklocale(loc) < 0 || loc == &__global_locale - || loc == LC_GLOBAL_LOCALE) { + || loc == LC_GLOBAL_LOCALE || loc == &__c_locale) { errno = EINVAL; return -1; } - _releaselocale(loc); - free(loc); + XL_RELEASE(loc); return 0; } @@ -323,6 +342,40 @@ newlocale(int mask, __const char *locale, locale_t base) return new; } +/* + * PRIVATE EXTERNAL: Returns the locale that can be used by wcstod and + * family, to convert the wide character string to a multi-byte string + * (the LC_NUMERIC and LC_CTYPE locales may be different). + */ +__private_extern__ locale_t +__numeric_ctype(locale_t loc) +{ + switch(loc->__numeric_fp_cvt) { + case LC_NUMERIC_FP_UNINITIALIZED: { + const char *ctype = loc->__lc_ctype->__ctype_encoding; + const char *numeric = (loc->_numeric_using_locale ? loc->__lc_numeric->_numeric_locale_buf : C); + if (strcmp(ctype, numeric) == 0) { + loc->__numeric_fp_cvt = LC_NUMERIC_FP_SAME_LOCALE; + return loc; + } else { + loc->__lc_numeric_loc = newlocale(LC_CTYPE_MASK, numeric, (locale_t)&__c_locale); + if (loc->__lc_numeric_loc) { + loc->__numeric_fp_cvt = LC_NUMERIC_FP_USE_LOCALE; + return loc->__lc_numeric_loc; + } else { /* shouldn't happen, but just use the same locale */ + loc->__numeric_fp_cvt = LC_NUMERIC_FP_SAME_LOCALE; + return loc; + } + } + } + case LC_NUMERIC_FP_SAME_LOCALE: + return loc; + case LC_NUMERIC_FP_USE_LOCALE: + return loc->__lc_numeric_loc; + } + return loc; /* shouldn't happen */ +} + /* * EXTERNAL: Returns the locale string for the part specified in mask. The * least significant bit is used. If loc is NULL, the current per-thread @@ -332,6 +385,7 @@ const char * querylocale(int mask, locale_t loc) { int m; + const char *ret; if (_checklocale(loc) < 0 || (mask & LC_ALL_MASK) == 0) { errno = EINVAL; @@ -341,27 +395,39 @@ querylocale(int mask, locale_t loc) loc = __current_locale(); else if (loc == LC_GLOBAL_LOCALE) loc = &__global_locale; - for(m = 1; m <= _LC_LAST_MASK; m <<= 1) { - if (m & mask) { - switch(m) { - case LC_COLLATE_MASK: - return (loc->__collate_load_error ? C : loc->__lc_collate->__encoding); - case LC_CTYPE_MASK: - return loc->__lc_ctype->__ctype_encoding; - case LC_MESSAGES_MASK: - return (loc->_messages_using_locale ? loc->__lc_messages->_messages_locale_buf : C); - case LC_MONETARY_MASK: - return (loc->_monetary_using_locale ? loc->__lc_monetary->_monetary_locale_buf : C); - case LC_NUMERIC_MASK: - return (loc->_numeric_using_locale ? loc->__lc_numeric->_numeric_locale_buf : C); - case LC_TIME_MASK: - return (loc->_time_using_locale ? loc->__lc_time->_time_locale_buf : C); - } - } + m = ffs(mask); + if (m == 0 || m > _LC_NUM_MASK) { + errno = EINVAL; + return NULL; + } + XL_LOCK(loc); + switch(1 << (m - 1)) { + case LC_COLLATE_MASK: + ret = (loc->__collate_load_error ? C : loc->__lc_collate->__encoding); + break; + case LC_CTYPE_MASK: + ret = loc->__lc_ctype->__ctype_encoding; + break; + case LC_MESSAGES_MASK: + ret = (loc->_messages_using_locale ? loc->__lc_messages->_messages_locale_buf : C); + break; + case LC_MONETARY_MASK: + ret = (loc->_monetary_using_locale ? loc->__lc_monetary->_monetary_locale_buf : C); + break; + case LC_NUMERIC_MASK: + ret = (loc->_numeric_using_locale ? loc->__lc_numeric->_numeric_locale_buf : C); + break; + case LC_TIME_MASK: + ret = (loc->_time_using_locale ? loc->__lc_time->_time_locale_buf : C); + break; + default: + /* should never get here */ + XL_UNLOCK(loc); + errno = EINVAL; + return NULL; } - /* should never get here */ - errno = EINVAL; - return NULL; + XL_UNLOCK(loc); + return ret; } /* @@ -383,10 +449,13 @@ uselocale(locale_t loc) errno = EINVAL; return NULL; } - if (loc == &__global_locale) /* should never happen */ - loc = LC_GLOBAL_LOCALE; + if (loc == LC_GLOBAL_LOCALE || + loc == &__global_locale) /* should never happen */ + loc = NULL; + XL_RETAIN(loc); orig = pthread_getspecific(__locale_key); - pthread_setspecific(__locale_key, loc == LC_GLOBAL_LOCALE ? NULL : loc); + pthread_setspecific(__locale_key, loc); + XL_RELEASE(orig); } return (orig ? orig : LC_GLOBAL_LOCALE); } @@ -411,12 +480,27 @@ ___mb_cur_max_l(locale_t loc) return __locale_ptr(loc)->__lc_ctype->__mb_cur_max; } +static void +__xlocale_release(void *loc) +{ + XL_RELEASE((locale_t)loc); +} + /* * Called from the Libc initializer to setup the thread-specific key. */ +/* + * Partition _pthread_keys in a lower part that dyld can use, and an upper + * part for libSystem. The libSystem part starts at __pthread_tsd_first = 10. + * dyld will set this value to 1. + */ + __private_extern__ void __xlocale_init(void) { - if (__locale_key == (pthread_key_t)-1) - pthread_key_create(&__locale_key, NULL); + if (__locale_key == (pthread_key_t)-1) { + __locale_key = __LIBC_PTHREAD_KEY_XLOCALE; + pthread_key_init_np(__locale_key, __xlocale_release); + } } +