]> git.saurik.com Git - apple/libc.git/blob - locale/xlocale.c
Libc-498.1.5.tar.gz
[apple/libc.git] / locale / xlocale.c
1 /*
2 * Copyright (c) 2005 Apple Computer, 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, NULL, \
33 {}, {}, {}, {}, {}, \
34 {}, {}, {}, {}, {}, \
35 XMAGIC, \
36 1, 0, 0, 0, 0, 0, 1, 1, 0, \
37 NULL, \
38 &_DefaultRuneXLocale, \
39 }
40
41 static char C[] = "C";
42 static const struct _xlocale __c_locale = C_LOCALE_INITIALIZER;
43 const locale_t _c_locale = (const locale_t)&__c_locale;
44 __private_extern__ struct _xlocale __global_locale = C_LOCALE_INITIALIZER;
45 __private_extern__ pthread_key_t __locale_key = (pthread_key_t)-1;
46
47 extern int __collate_load_tables(const char *, locale_t);
48 extern int __detect_path_locale(void);
49 extern const char *__get_locale_env(int);
50 extern int __messages_load_locale(const char *, locale_t);
51 extern int __monetary_load_locale(const char *, locale_t);
52 extern int __numeric_load_locale(const char *, locale_t);
53 extern int __setrunelocale(const char *, locale_t);
54 extern int __time_load_locale(const char *, locale_t);
55
56 static void _releaselocale(locale_t loc);
57
58 /*
59 * check that the encoding is the right size, isn't . or .. and doesn't
60 * contain any slashes
61 */
62 static inline __attribute__((always_inline)) int
63 _checkencoding(const char *encoding)
64 {
65 return (encoding && (strlen(encoding) > ENCODING_LEN
66 || (encoding[0] == '.' && (encoding[1] == 0
67 || (encoding[1] == '.' && encoding[2] == 0)))
68 || strchr(encoding, '/') != NULL)) ? -1 : 0;
69 }
70
71 /*
72 * check that the locale has the right magic number
73 */
74 static inline __attribute__((always_inline)) int
75 _checklocale(const locale_t loc)
76 {
77 if (!loc)
78 return 0;
79 return (loc == LC_GLOBAL_LOCALE || loc->__magic == XMAGIC) ? 0 : -1;
80 }
81
82 /*
83 * copy a locale_t except anything before the magic value
84 */
85 static inline __attribute__((always_inline)) void
86 _copylocale(locale_t dst, const locale_t src)
87 {
88 memcpy(&dst->__magic, &src->__magic, sizeof(*dst) - offsetof(struct _xlocale, __magic));
89 }
90
91 /*
92 * Make a copy of a locale_t, locking/unlocking the source as determined
93 * by the lock flag. A NULL locale_t means to make a copy of the current
94 * locale while LC_GLOBAL_LOCALE means to copy the global locale. If
95 * &__c_locale is passed (meaning a C locale is desired), just make
96 * a copy.
97 */
98 static locale_t
99 _duplocale(locale_t loc)
100 {
101 locale_t new;
102
103 if ((new = (locale_t)malloc(sizeof(struct _xlocale))) == NULL)
104 return NULL;
105 new->__refcount = 1;
106 new->__free_extra = (__free_extra_t)_releaselocale;
107 if (loc == NULL)
108 loc = __current_locale();
109 else if (loc == LC_GLOBAL_LOCALE)
110 loc = &__global_locale;
111 else if (loc == &__c_locale) {
112 *new = __c_locale;
113 return new;
114 }
115 _copylocale(new, loc);
116 /* __mbs_mblen is the first of NMBSTATET mbstate_t buffers */
117 bzero(&new->__mbs_mblen, offsetof(struct _xlocale, __magic)
118 - offsetof(struct _xlocale, __mbs_mblen));
119 /* collate */
120 XL_RETAIN(new->__lc_collate);
121 /* ctype */
122 XL_RETAIN(new->__lc_ctype);
123 /* messages */
124 XL_RETAIN(new->__lc_messages);
125 /* monetary */
126 XL_RETAIN(new->__lc_monetary);
127 /* numeric */
128 XL_RETAIN(new->__lc_numeric);
129 XL_RETAIN(new->__lc_numeric_loc);
130 /* time */
131 XL_RETAIN(new->__lc_time);
132 /* newale_t */
133 XL_RETAIN(new->__lc_localeconv);
134
135 return new;
136 }
137
138 /*
139 * Modify a locale_t, setting the parts specified in the mask
140 * to the locale specified by the string. If the string is NULL, the C
141 * locale is used. If the string is empty, the value is determined from
142 * the environment. -1 is returned on error, and loc is in a partial state.
143 */
144 static int
145 _modifylocale(locale_t loc, int mask, __const char *locale)
146 {
147 int m, ret;
148 const char *enc = NULL;
149 char *oenc;
150
151 if (!locale)
152 locale = C;
153
154 ret = __detect_path_locale();
155 if (ret) {
156 errno = ret;
157 return -1;
158 }
159
160 if (*locale)
161 enc = locale;
162 for(m = 1; m <= _LC_LAST_MASK; m <<= 1) {
163 if (m & mask) {
164 switch(m) {
165 case LC_COLLATE_MASK:
166 if (!*locale) {
167 enc = __get_locale_env(LC_COLLATE);
168 if (_checkencoding(enc) < 0) {
169 errno = EINVAL;
170 return -1;
171 }
172 }
173 oenc = (loc->__collate_load_error ? C : loc->__lc_collate->__encoding);
174 if (strcmp(enc, oenc) != 0 && __collate_load_tables(enc, loc) == _LDP_ERROR)
175 return -1;
176 break;
177 case LC_CTYPE_MASK:
178 if (!*locale) {
179 enc = __get_locale_env(LC_CTYPE);
180 if (_checkencoding(enc) < 0) {
181 errno = EINVAL;
182 return -1;
183 }
184 }
185 if (strcmp(enc, loc->__lc_ctype->__ctype_encoding) != 0) {
186 if ((ret = __setrunelocale(enc, loc)) != 0) {
187 errno = ret;
188 return -1;
189 }
190 if (loc->__numeric_fp_cvt == LC_NUMERIC_FP_SAME_LOCALE)
191 loc->__numeric_fp_cvt = LC_NUMERIC_FP_UNINITIALIZED;
192 }
193 break;
194 case LC_MESSAGES_MASK:
195 if (!*locale) {
196 enc = __get_locale_env(LC_MESSAGES);
197 if (_checkencoding(enc) < 0) {
198 errno = EINVAL;
199 return -1;
200 }
201 }
202 oenc = (loc->_messages_using_locale ? loc->__lc_messages->_messages_locale_buf : C);
203 if (strcmp(enc, oenc) != 0 && __messages_load_locale(enc, loc) == _LDP_ERROR)
204 return -1;
205 break;
206 case LC_MONETARY_MASK:
207 if (!*locale) {
208 enc = __get_locale_env(LC_MONETARY);
209 if (_checkencoding(enc) < 0) {
210 errno = EINVAL;
211 return -1;
212 }
213 }
214 oenc = (loc->_monetary_using_locale ? loc->__lc_monetary->_monetary_locale_buf : C);
215 if (strcmp(enc, oenc) != 0 && __monetary_load_locale(enc, loc) == _LDP_ERROR)
216 return -1;
217 break;
218 case LC_NUMERIC_MASK:
219 if (!*locale) {
220 enc = __get_locale_env(LC_NUMERIC);
221 if (_checkencoding(enc) < 0) {
222 errno = EINVAL;
223 return -1;
224 }
225 }
226 oenc = (loc->_numeric_using_locale ? loc->__lc_numeric->_numeric_locale_buf : C);
227 if (strcmp(enc, oenc) != 0) {
228 if (__numeric_load_locale(enc, loc) == _LDP_ERROR)
229 return -1;
230 loc->__numeric_fp_cvt = LC_NUMERIC_FP_UNINITIALIZED;
231 XL_RELEASE(loc->__lc_numeric_loc);
232 loc->__lc_numeric_loc = NULL;
233 }
234 break;
235 case LC_TIME_MASK:
236 if (!*locale) {
237 enc = __get_locale_env(LC_TIME);
238 if (_checkencoding(enc) < 0) {
239 errno = EINVAL;
240 return -1;
241 }
242 }
243 oenc = (loc->_time_using_locale ? loc->__lc_time->_time_locale_buf : C);
244 if (strcmp(enc, oenc) != 0 && __time_load_locale(enc, loc) == _LDP_ERROR)
245 return -1;
246 break;
247 }
248 }
249 }
250 return 0;
251 }
252
253 /*
254 * release all the memory objects (the memory will be freed when the refcount
255 * becomes zero)
256 */
257 static void
258 _releaselocale(locale_t loc)
259 {
260 /* collate */
261 XL_RELEASE(loc->__lc_collate);
262 /* ctype */
263 XL_RELEASE(loc->__lc_ctype);
264 /* messages */
265 XL_RELEASE(loc->__lc_messages);
266 /* monetary */
267 XL_RELEASE(loc->__lc_monetary);
268 /* numeric */
269 XL_RELEASE(loc->__lc_numeric);
270 XL_RELEASE(loc->__lc_numeric_loc);
271 /* time */
272 XL_RELEASE(loc->__lc_time);
273 /* locale_t */
274 XL_RELEASE(loc->__lc_localeconv);
275 }
276
277 /*
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
280 * on error.
281 */
282 locale_t
283 duplocale(locale_t loc)
284 {
285 if (_checklocale(loc) < 0) {
286 errno = EINVAL;
287 return NULL;
288 }
289 return _duplocale(loc);
290 }
291
292 /*
293 * EXTERNAL: Free a locale_t, releasing all memory objects. Don't free
294 * illegal locale_t's or the global locale.
295 */
296 int
297 freelocale(locale_t loc)
298 {
299 if (!loc || _checklocale(loc) < 0 || loc == &__global_locale
300 || loc == LC_GLOBAL_LOCALE || loc == &__c_locale) {
301 errno = EINVAL;
302 return -1;
303 }
304 XL_RELEASE(loc);
305 return 0;
306 }
307
308 /*
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.
313 */
314 locale_t
315 newlocale(int mask, __const char *locale, locale_t base)
316 {
317 locale_t new;
318 int lcmask = (mask & LC_ALL_MASK);
319
320 if (_checkencoding(locale) < 0) {
321 errno = EINVAL;
322 return NULL;
323 }
324 if (lcmask == LC_ALL_MASK)
325 base = (locale_t)&__c_locale;
326 else if (_checklocale(base) < 0) {
327 errno = EINVAL;
328 return NULL;
329 }
330 new = _duplocale(base);
331 if (new == NULL)
332 return NULL;
333 if (lcmask == 0 || (lcmask == LC_ALL_MASK && locale == NULL))
334 return new;
335 if (_modifylocale(new, lcmask, locale) < 0) {
336 freelocale(new);
337 return NULL;
338 }
339 return new;
340 }
341
342 /*
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).
346 */
347 __private_extern__ locale_t
348 __numeric_ctype(locale_t loc)
349 {
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;
356 return loc;
357 } else {
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;
364 return loc;
365 }
366 }
367 }
368 case LC_NUMERIC_FP_SAME_LOCALE:
369 return loc;
370 case LC_NUMERIC_FP_USE_LOCALE:
371 return loc->__lc_numeric_loc;
372 }
373 return loc; /* shouldn't happen */
374 }
375
376 /*
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
379 * locale is used.
380 */
381 const char *
382 querylocale(int mask, locale_t loc)
383 {
384 int m;
385
386 if (_checklocale(loc) < 0 || (mask & LC_ALL_MASK) == 0) {
387 errno = EINVAL;
388 return NULL;
389 }
390 if (loc == NULL)
391 loc = __current_locale();
392 else if (loc == LC_GLOBAL_LOCALE)
393 loc = &__global_locale;
394 for(m = 1; m <= _LC_LAST_MASK; m <<= 1) {
395 if (m & mask) {
396 switch(m) {
397 case LC_COLLATE_MASK:
398 return (loc->__collate_load_error ? C : loc->__lc_collate->__encoding);
399 case LC_CTYPE_MASK:
400 return loc->__lc_ctype->__ctype_encoding;
401 case LC_MESSAGES_MASK:
402 return (loc->_messages_using_locale ? loc->__lc_messages->_messages_locale_buf : C);
403 case LC_MONETARY_MASK:
404 return (loc->_monetary_using_locale ? loc->__lc_monetary->_monetary_locale_buf : C);
405 case LC_NUMERIC_MASK:
406 return (loc->_numeric_using_locale ? loc->__lc_numeric->_numeric_locale_buf : C);
407 case LC_TIME_MASK:
408 return (loc->_time_using_locale ? loc->__lc_time->_time_locale_buf : C);
409 }
410 }
411 }
412 /* should never get here */
413 errno = EINVAL;
414 return NULL;
415 }
416
417 /*
418 * EXTERNAL: Set the thread-specific locale. The previous locale is returned.
419 * Use LC_GLOBAL_LOCALE to set the global locale. LC_GLOBAL_LOCALE
420 * may also be returned if there was no previous thread-specific locale in
421 * effect. If loc is NULL, the current locale is returned, but no locale
422 * chance is made. NULL is returned on error.
423 */
424 locale_t
425 uselocale(locale_t loc)
426 {
427 locale_t orig;
428
429 if (loc == NULL)
430 orig = (locale_t)pthread_getspecific(__locale_key);
431 else {
432 if (_checklocale(loc) < 0) {
433 errno = EINVAL;
434 return NULL;
435 }
436 if (loc == &__global_locale) /* should never happen */
437 loc = LC_GLOBAL_LOCALE;
438 orig = pthread_getspecific(__locale_key);
439 pthread_setspecific(__locale_key, loc == LC_GLOBAL_LOCALE ? NULL : loc);
440 }
441 return (orig ? orig : LC_GLOBAL_LOCALE);
442 }
443
444 /*
445 * EXTERNAL: Used by the MB_CUR_MAX macro to determine the thread-specific
446 * value.
447 */
448 int
449 ___mb_cur_max(void)
450 {
451 return __current_locale()->__lc_ctype->__mb_cur_max;
452 }
453
454 /*
455 * EXTERNAL: Used by the MB_CUR_MAX_L macro to determine the thread-specific
456 * value, from the given locale_t.
457 */
458 int
459 ___mb_cur_max_l(locale_t loc)
460 {
461 return __locale_ptr(loc)->__lc_ctype->__mb_cur_max;
462 }
463
464 /*
465 * Called from the Libc initializer to setup the thread-specific key.
466 */
467 /*
468 * Partition _pthread_keys in a lower part that dyld can use, and an upper
469 * part for libSystem. The libSystem part starts at __pthread_tsd_first = 10.
470 * dyld will set this value to 1.
471 */
472 extern int __pthread_tsd_first;
473
474 __private_extern__ void
475 __xlocale_init(void)
476 {
477 if (__locale_key == (pthread_key_t)-1)
478 __locale_key = __pthread_tsd_first;
479 }
480