]> git.saurik.com Git - apple/libc.git/blame - locale/xlocale.c
Libc-1244.50.9.tar.gz
[apple/libc.git] / locale / xlocale.c
CommitLineData
3d9156a7 1/*
34e8f829 2 * Copyright (c) 2005, 2008 Apple Inc. All rights reserved.
3d9156a7
A
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 { \
ad3c9f2a 32 0, XPERMANENT, \
3d9156a7
A
33 {}, {}, {}, {}, {}, \
34 {}, {}, {}, {}, {}, \
b061a43b 35 OS_UNFAIR_LOCK_INIT, \
3d9156a7 36 XMAGIC, \
224c7076 37 1, 0, 0, 0, 0, 0, 1, 1, 0, \
3d9156a7
A
38 NULL, \
39 &_DefaultRuneXLocale, \
40}
41
42static char C[] = "C";
23e20b00 43static struct _xlocale __c_locale = C_LOCALE_INITIALIZER;
3d9156a7 44const locale_t _c_locale = (const locale_t)&__c_locale;
23e20b00
A
45struct _xlocale __global_locale = C_LOCALE_INITIALIZER;
46pthread_key_t __locale_key = (pthread_key_t)-1;
3d9156a7
A
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
224c7076
A
57static void _releaselocale(locale_t loc);
58
3d9156a7
A
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/*
34e8f829
A
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
3d9156a7
A
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;
224c7076
A
106 new->__refcount = 1;
107 new->__free_extra = (__free_extra_t)_releaselocale;
b061a43b 108 new->__lock = OS_UNFAIR_LOCK_INIT;
3d9156a7
A
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;
51282358
A
115 new->__refcount = 1;
116 new->__free_extra = (__free_extra_t)_releaselocale;
b061a43b 117 new->__lock = OS_UNFAIR_LOCK_INIT;
3d9156a7
A
118 return new;
119 }
34e8f829 120 XL_LOCK(loc);
3d9156a7 121 _copylocale(new, loc);
34e8f829 122 XL_UNLOCK(loc);
3d9156a7 123 /* __mbs_mblen is the first of NMBSTATET mbstate_t buffers */
224c7076
A
124 bzero(&new->__mbs_mblen, offsetof(struct _xlocale, __magic)
125 - offsetof(struct _xlocale, __mbs_mblen));
3d9156a7
A
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);
224c7076 136 XL_RETAIN(new->__lc_numeric_loc);
3d9156a7
A
137 /* time */
138 XL_RETAIN(new->__lc_time);
3d9156a7
A
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 }
224c7076
A
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;
3d9156a7
A
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);
224c7076
A
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 }
3d9156a7
A
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);
224c7076 275 XL_RELEASE(loc->__lc_numeric_loc);
3d9156a7
A
276 /* time */
277 XL_RELEASE(loc->__lc_time);
3d9156a7
A
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
224c7076 303 || loc == LC_GLOBAL_LOCALE || loc == &__c_locale) {
3d9156a7
A
304 errno = EINVAL;
305 return -1;
306 }
224c7076 307 XL_RELEASE(loc);
3d9156a7
A
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
224c7076
A
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 {
1f2f436a 361 loc->__lc_numeric_loc = newlocale(LC_CTYPE_MASK, numeric, (locale_t)&__c_locale);
224c7076
A
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
3d9156a7
A
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;
34e8f829 388 const char *ret;
3d9156a7
A
389
390 if (_checklocale(loc) < 0 || (mask & LC_ALL_MASK) == 0) {
391 errno = EINVAL;
392 return NULL;
393 }
6465356a 394 DEFAULT_CURRENT_LOCALE(loc);
34e8f829
A
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;
3d9156a7 425 }
34e8f829
A
426 XL_UNLOCK(loc);
427 return ret;
3d9156a7
A
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 }
51282358
A
449 if (loc == LC_GLOBAL_LOCALE ||
450 loc == &__global_locale) /* should never happen */
451 loc = NULL;
452 XL_RETAIN(loc);
3d9156a7 453 orig = pthread_getspecific(__locale_key);
51282358
A
454 pthread_setspecific(__locale_key, loc);
455 XL_RELEASE(orig);
3d9156a7
A
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
34e8f829
A
480static void
481__xlocale_release(void *loc)
482{
974e3884
A
483 locale_t l = loc;
484 XL_RELEASE(l);
34e8f829
A
485}
486
3d9156a7
A
487/*
488 * Called from the Libc initializer to setup the thread-specific key.
489 */
490__private_extern__ void
491__xlocale_init(void)
492{
34e8f829
A
493 if (__locale_key == (pthread_key_t)-1) {
494 __locale_key = __LIBC_PTHREAD_KEY_XLOCALE;
495 pthread_key_init_np(__locale_key, __xlocale_release);
496 }
3d9156a7 497}
224c7076 498