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