2 * Copyright (c) 2005, 2008 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
24 #include "xlocale_private.h"
31 #define C_LOCALE_INITIALIZER { \
37 1, 0, 0, 0, 0, 0, 1, 1, 0, \
39 &_DefaultRuneXLocale, \
42 static char C
[] = "C";
43 static const struct _xlocale __c_locale
= C_LOCALE_INITIALIZER
;
44 const locale_t _c_locale
= (const locale_t
)&__c_locale
;
45 __private_extern__
struct _xlocale __global_locale
= C_LOCALE_INITIALIZER
;
46 __private_extern__ pthread_key_t __locale_key
= (pthread_key_t
)-1;
48 extern int __collate_load_tables(const char *, locale_t
);
49 extern int __detect_path_locale(void);
50 extern const char *__get_locale_env(int);
51 extern int __messages_load_locale(const char *, locale_t
);
52 extern int __monetary_load_locale(const char *, locale_t
);
53 extern int __numeric_load_locale(const char *, locale_t
);
54 extern int __setrunelocale(const char *, locale_t
);
55 extern int __time_load_locale(const char *, locale_t
);
57 static void _releaselocale(locale_t loc
);
60 * check that the encoding is the right size, isn't . or .. and doesn't
63 static inline __attribute__((always_inline
)) int
64 _checkencoding(const char *encoding
)
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;
73 * check that the locale has the right magic number
75 static inline __attribute__((always_inline
)) int
76 _checklocale(const locale_t loc
)
80 return (loc
== LC_GLOBAL_LOCALE
|| loc
->__magic
== XMAGIC
) ? 0 : -1;
84 * copy a locale_t except anything before the magic value
86 static inline __attribute__((always_inline
)) void
87 _copylocale(locale_t dst
, const locale_t src
)
89 memcpy(&dst
->__magic
, &src
->__magic
, sizeof(*dst
) - offsetof(struct _xlocale
, __magic
));
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
100 _duplocale(locale_t loc
)
104 if ((new = (locale_t
)malloc(sizeof(struct _xlocale
))) == NULL
)
107 new->__free_extra
= (__free_extra_t
)_releaselocale
;
108 new->__lock
= LOCK_INITIALIZER
;
110 loc
= __current_locale();
111 else if (loc
== LC_GLOBAL_LOCALE
)
112 loc
= &__global_locale
;
113 else if (loc
== &__c_locale
) {
118 _copylocale(new, loc
);
120 /* __mbs_mblen is the first of NMBSTATET mbstate_t buffers */
121 bzero(&new->__mbs_mblen
, offsetof(struct _xlocale
, __magic
)
122 - offsetof(struct _xlocale
, __mbs_mblen
));
124 XL_RETAIN(new->__lc_collate
);
126 XL_RETAIN(new->__lc_ctype
);
128 XL_RETAIN(new->__lc_messages
);
130 XL_RETAIN(new->__lc_monetary
);
132 XL_RETAIN(new->__lc_numeric
);
133 XL_RETAIN(new->__lc_numeric_loc
);
135 XL_RETAIN(new->__lc_time
);
141 * Modify a locale_t, setting the parts specified in the mask
142 * to the locale specified by the string. If the string is NULL, the C
143 * locale is used. If the string is empty, the value is determined from
144 * the environment. -1 is returned on error, and loc is in a partial state.
147 _modifylocale(locale_t loc
, int mask
, __const
char *locale
)
150 const char *enc
= NULL
;
156 ret
= __detect_path_locale();
164 for(m
= 1; m
<= _LC_LAST_MASK
; m
<<= 1) {
167 case LC_COLLATE_MASK
:
169 enc
= __get_locale_env(LC_COLLATE
);
170 if (_checkencoding(enc
) < 0) {
175 oenc
= (loc
->__collate_load_error
? C
: loc
->__lc_collate
->__encoding
);
176 if (strcmp(enc
, oenc
) != 0 && __collate_load_tables(enc
, loc
) == _LDP_ERROR
)
181 enc
= __get_locale_env(LC_CTYPE
);
182 if (_checkencoding(enc
) < 0) {
187 if (strcmp(enc
, loc
->__lc_ctype
->__ctype_encoding
) != 0) {
188 if ((ret
= __setrunelocale(enc
, loc
)) != 0) {
192 if (loc
->__numeric_fp_cvt
== LC_NUMERIC_FP_SAME_LOCALE
)
193 loc
->__numeric_fp_cvt
= LC_NUMERIC_FP_UNINITIALIZED
;
196 case LC_MESSAGES_MASK
:
198 enc
= __get_locale_env(LC_MESSAGES
);
199 if (_checkencoding(enc
) < 0) {
204 oenc
= (loc
->_messages_using_locale
? loc
->__lc_messages
->_messages_locale_buf
: C
);
205 if (strcmp(enc
, oenc
) != 0 && __messages_load_locale(enc
, loc
) == _LDP_ERROR
)
208 case LC_MONETARY_MASK
:
210 enc
= __get_locale_env(LC_MONETARY
);
211 if (_checkencoding(enc
) < 0) {
216 oenc
= (loc
->_monetary_using_locale
? loc
->__lc_monetary
->_monetary_locale_buf
: C
);
217 if (strcmp(enc
, oenc
) != 0 && __monetary_load_locale(enc
, loc
) == _LDP_ERROR
)
220 case LC_NUMERIC_MASK
:
222 enc
= __get_locale_env(LC_NUMERIC
);
223 if (_checkencoding(enc
) < 0) {
228 oenc
= (loc
->_numeric_using_locale
? loc
->__lc_numeric
->_numeric_locale_buf
: C
);
229 if (strcmp(enc
, oenc
) != 0) {
230 if (__numeric_load_locale(enc
, loc
) == _LDP_ERROR
)
232 loc
->__numeric_fp_cvt
= LC_NUMERIC_FP_UNINITIALIZED
;
233 XL_RELEASE(loc
->__lc_numeric_loc
);
234 loc
->__lc_numeric_loc
= NULL
;
239 enc
= __get_locale_env(LC_TIME
);
240 if (_checkencoding(enc
) < 0) {
245 oenc
= (loc
->_time_using_locale
? loc
->__lc_time
->_time_locale_buf
: C
);
246 if (strcmp(enc
, oenc
) != 0 && __time_load_locale(enc
, loc
) == _LDP_ERROR
)
256 * release all the memory objects (the memory will be freed when the refcount
260 _releaselocale(locale_t loc
)
263 XL_RELEASE(loc
->__lc_collate
);
265 XL_RELEASE(loc
->__lc_ctype
);
267 XL_RELEASE(loc
->__lc_messages
);
269 XL_RELEASE(loc
->__lc_monetary
);
271 XL_RELEASE(loc
->__lc_numeric
);
272 XL_RELEASE(loc
->__lc_numeric_loc
);
274 XL_RELEASE(loc
->__lc_time
);
278 * EXTERNAL: Duplicate a (non-NULL) locale_t. LC_GLOBAL_LOCALE means the
279 * global locale, while NULL means the current locale. NULL is returned
283 duplocale(locale_t loc
)
285 if (_checklocale(loc
) < 0) {
289 return _duplocale(loc
);
293 * EXTERNAL: Free a locale_t, releasing all memory objects. Don't free
294 * illegal locale_t's or the global locale.
297 freelocale(locale_t loc
)
299 if (!loc
|| _checklocale(loc
) < 0 || loc
== &__global_locale
300 || loc
== LC_GLOBAL_LOCALE
|| loc
== &__c_locale
) {
309 * EXTERNAL: Create a new locale_t, based on the base locale_t, and modified
310 * by the mask and locale string. If the base is NULL, the current locale
311 * is used as the base. If locale is NULL, changes are made from the C locale
312 * for categories set in mask.
315 newlocale(int mask
, __const
char *locale
, locale_t base
)
318 int lcmask
= (mask
& LC_ALL_MASK
);
320 if (_checkencoding(locale
) < 0) {
324 if (lcmask
== LC_ALL_MASK
)
325 base
= (locale_t
)&__c_locale
;
326 else if (_checklocale(base
) < 0) {
330 new = _duplocale(base
);
333 if (lcmask
== 0 || (lcmask
== LC_ALL_MASK
&& locale
== NULL
))
335 if (_modifylocale(new, lcmask
, locale
) < 0) {
343 * PRIVATE EXTERNAL: Returns the locale that can be used by wcstod and
344 * family, to convert the wide character string to a multi-byte string
345 * (the LC_NUMERIC and LC_CTYPE locales may be different).
347 __private_extern__ locale_t
348 __numeric_ctype(locale_t loc
)
350 switch(loc
->__numeric_fp_cvt
) {
351 case LC_NUMERIC_FP_UNINITIALIZED
: {
352 const char *ctype
= loc
->__lc_ctype
->__ctype_encoding
;
353 const char *numeric
= (loc
->_numeric_using_locale
? loc
->__lc_numeric
->_numeric_locale_buf
: C
);
354 if (strcmp(ctype
, numeric
) == 0) {
355 loc
->__numeric_fp_cvt
= LC_NUMERIC_FP_SAME_LOCALE
;
358 loc
->__lc_numeric_loc
= newlocale(LC_CTYPE_MASK
, numeric
, &__c_locale
);
359 if (loc
->__lc_numeric_loc
) {
360 loc
->__numeric_fp_cvt
= LC_NUMERIC_FP_USE_LOCALE
;
361 return loc
->__lc_numeric_loc
;
362 } else { /* shouldn't happen, but just use the same locale */
363 loc
->__numeric_fp_cvt
= LC_NUMERIC_FP_SAME_LOCALE
;
368 case LC_NUMERIC_FP_SAME_LOCALE
:
370 case LC_NUMERIC_FP_USE_LOCALE
:
371 return loc
->__lc_numeric_loc
;
373 return loc
; /* shouldn't happen */
377 * EXTERNAL: Returns the locale string for the part specified in mask. The
378 * least significant bit is used. If loc is NULL, the current per-thread
382 querylocale(int mask
, locale_t loc
)
387 if (_checklocale(loc
) < 0 || (mask
& LC_ALL_MASK
) == 0) {
392 loc
= __current_locale();
393 else if (loc
== LC_GLOBAL_LOCALE
)
394 loc
= &__global_locale
;
396 if (m
== 0 || m
> _LC_NUM_MASK
) {
401 switch(1 << (m
- 1)) {
402 case LC_COLLATE_MASK
:
403 ret
= (loc
->__collate_load_error
? C
: loc
->__lc_collate
->__encoding
);
406 ret
= loc
->__lc_ctype
->__ctype_encoding
;
408 case LC_MESSAGES_MASK
:
409 ret
= (loc
->_messages_using_locale
? loc
->__lc_messages
->_messages_locale_buf
: C
);
411 case LC_MONETARY_MASK
:
412 ret
= (loc
->_monetary_using_locale
? loc
->__lc_monetary
->_monetary_locale_buf
: C
);
414 case LC_NUMERIC_MASK
:
415 ret
= (loc
->_numeric_using_locale
? loc
->__lc_numeric
->_numeric_locale_buf
: C
);
418 ret
= (loc
->_time_using_locale
? loc
->__lc_time
->_time_locale_buf
: C
);
421 /* should never get here */
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.
438 uselocale(locale_t loc
)
443 orig
= (locale_t
)pthread_getspecific(__locale_key
);
445 if (_checklocale(loc
) < 0) {
449 if (loc
== &__global_locale
) /* should never happen */
450 loc
= LC_GLOBAL_LOCALE
;
451 orig
= pthread_getspecific(__locale_key
);
452 pthread_setspecific(__locale_key
, loc
== LC_GLOBAL_LOCALE
? NULL
: loc
);
454 return (orig
? orig
: LC_GLOBAL_LOCALE
);
458 * EXTERNAL: Used by the MB_CUR_MAX macro to determine the thread-specific
464 return __current_locale()->__lc_ctype
->__mb_cur_max
;
468 * EXTERNAL: Used by the MB_CUR_MAX_L macro to determine the thread-specific
469 * value, from the given locale_t.
472 ___mb_cur_max_l(locale_t loc
)
474 return __locale_ptr(loc
)->__lc_ctype
->__mb_cur_max
;
478 __xlocale_release(void *loc
)
480 XL_RELEASE((locale_t
)loc
);
484 * Called from the Libc initializer to setup the thread-specific key.
487 * Partition _pthread_keys in a lower part that dyld can use, and an upper
488 * part for libSystem. The libSystem part starts at __pthread_tsd_first = 10.
489 * dyld will set this value to 1.
492 __private_extern__
void
495 if (__locale_key
== (pthread_key_t
)-1) {
496 __locale_key
= __LIBC_PTHREAD_KEY_XLOCALE
;
497 pthread_key_init_np(__locale_key
, __xlocale_release
);