1 /*-
2 * Copyright (c) 2011 The FreeBSD Foundation
3 * All rights reserved.
4 *
5 * This software was developed by David Chisnall under sponsorship from
6 * the FreeBSD Foundation.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * $FreeBSD$
30 */
31
32 #include <pthread.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <runetype.h>
36 #include "libc_private.h"
37 #include "xlocale_private.h"
38
39 /**
40 * Each locale loader declares a global component. This is used by setlocale()
41 * and also by xlocale with LC_GLOBAL_LOCALE..
42 */
43 extern struct xlocale_component __xlocale_global_collate;
44 extern struct xlocale_component __xlocale_global_ctype;
45 extern struct xlocale_component __xlocale_global_monetary;
46 extern struct xlocale_component __xlocale_global_numeric;
47 extern struct xlocale_component __xlocale_global_time;
48 extern struct xlocale_component __xlocale_global_messages;
49 /*
50 * And another version for the statically-allocated C locale. We only have
51 * components for the parts that are expected to be sensible.
52 */
53 extern struct xlocale_component __xlocale_C_collate;
54 extern struct xlocale_component __xlocale_C_ctype;
55
56 #ifndef __NO_TLS
57 /*
58 * The locale for this thread.
59 */
60 _Thread_local locale_t __thread_locale;
61 #endif
62 /*
63 * Flag indicating that one or more per-thread locales exist.
64 */
65 int __has_thread_locale;
66 /*
67 * Private functions in setlocale.c.
68 */
69 const char *
70 __get_locale_env(int category);
71 int
72 __detect_path_locale(void);
73
74 struct _xlocale __xlocale_global_locale = {
75 {0},
76 {
77 &__xlocale_global_collate,
78 &__xlocale_global_ctype,
79 &__xlocale_global_monetary,
80 &__xlocale_global_numeric,
81 &__xlocale_global_time,
82 &__xlocale_global_messages
83 },
84 1,
85 0,
86 1,
87 0
88 };
89
90 struct _xlocale __xlocale_C_locale = {
91 {0},
92 {
93 &__xlocale_C_collate,
94 &__xlocale_C_ctype,
95 0, 0, 0, 0
96 },
97 1,
98 0,
99 1,
100 0
101 };
102
103 static void*(*constructors[])(const char*, locale_t) =
104 {
105 __collate_load,
106 __ctype_load,
107 __monetary_load,
108 __numeric_load,
109 __time_load,
110 __messages_load
111 };
112
113 static pthread_key_t locale_info_key;
114 static int fake_tls;
115 static locale_t thread_local_locale;
116
init_key(void)117 static void init_key(void)
118 {
119
120 pthread_key_create(&locale_info_key, xlocale_release);
121 pthread_setspecific(locale_info_key, (void*)42);
122 if (pthread_getspecific(locale_info_key) == (void*)42) {
123 pthread_setspecific(locale_info_key, 0);
124 } else {
125 fake_tls = 1;
126 }
127 /* At least one per-thread locale has now been set. */
128 __has_thread_locale = 1;
129 __detect_path_locale();
130 }
131
132 static pthread_once_t once_control = PTHREAD_ONCE_INIT;
133
134 static locale_t
get_thread_locale(void)135 get_thread_locale(void)
136 {
137
138 _once(&once_control, init_key);
139
140 return (fake_tls ? thread_local_locale :
141 pthread_getspecific(locale_info_key));
142 }
143
144 #ifdef __NO_TLS
145 locale_t
__get_locale(void)146 __get_locale(void)
147 {
148 locale_t l = get_thread_locale();
149 return (l ? l : &__xlocale_global_locale);
150
151 }
152 #endif
153
154 static void
set_thread_locale(locale_t loc)155 set_thread_locale(locale_t loc)
156 {
157
158 _once(&once_control, init_key);
159
160 if (NULL != loc) {
161 xlocale_retain((struct xlocale_refcounted*)loc);
162 }
163 locale_t old = pthread_getspecific(locale_info_key);
164 if ((NULL != old) && (loc != old)) {
165 xlocale_release((struct xlocale_refcounted*)old);
166 }
167 if (fake_tls) {
168 thread_local_locale = loc;
169 } else {
170 pthread_setspecific(locale_info_key, loc);
171 }
172 #ifndef __NO_TLS
173 __thread_locale = loc;
174 __set_thread_rune_locale(loc);
175 #endif
176 }
177
178 /**
179 * Clean up a locale, once its reference count reaches zero. This function is
180 * called by xlocale_release(), it should not be called directly.
181 */
182 static void
destruct_locale(void * l)183 destruct_locale(void *l)
184 {
185 locale_t loc = l;
186
187 for (int type=0 ; type<XLC_LAST ; type++) {
188 if (loc->components[type]) {
189 xlocale_release(loc->components[type]);
190 }
191 }
192 if (loc->csym) {
193 free(loc->csym);
194 }
195 free(l);
196 }
197
198 /**
199 * Allocates a new, uninitialised, locale.
200 */
201 static locale_t
alloc_locale(void)202 alloc_locale(void)
203 {
204 locale_t new = calloc(sizeof(struct _xlocale), 1);
205
206 new->header.destructor = destruct_locale;
207 new->monetary_locale_changed = 1;
208 new->numeric_locale_changed = 1;
209 return (new);
210 }
211 static void
copyflags(locale_t new,locale_t old)212 copyflags(locale_t new, locale_t old)
213 {
214 new->using_monetary_locale = old->using_monetary_locale;
215 new->using_numeric_locale = old->using_numeric_locale;
216 new->using_time_locale = old->using_time_locale;
217 new->using_messages_locale = old->using_messages_locale;
218 }
219
dupcomponent(int type,locale_t base,locale_t new)220 static int dupcomponent(int type, locale_t base, locale_t new)
221 {
222 /* Always copy from the global locale, since it has mutable components.
223 */
224 struct xlocale_component *src = base->components[type];
225
226 if (&__xlocale_global_locale == base) {
227 new->components[type] = constructors[type](src->locale, new);
228 if (new->components[type]) {
229 strncpy(new->components[type]->locale, src->locale,
230 ENCODING_LEN);
231 }
232 } else if (base->components[type]) {
233 new->components[type] = xlocale_retain(base->components[type]);
234 } else {
235 /* If the component was NULL, return success - if base is a
236 * valid locale then the flag indicating that this isn't
237 * present should be set. If it isn't a valid locale, then
238 * we're stuck anyway. */
239 return 1;
240 }
241 return (0 != new->components[type]);
242 }
243
244 /*
245 * Public interfaces. These are the five public functions described by the
246 * xlocale interface.
247 */
248
newlocale(int mask,const char * locale,locale_t base)249 locale_t newlocale(int mask, const char *locale, locale_t base)
250 {
251 int type;
252 const char *realLocale = locale;
253 int useenv = 0;
254 int success = 1;
255
256 _once(&once_control, init_key);
257
258 locale_t new = alloc_locale();
259 if (NULL == new) {
260 return (NULL);
261 }
262
263 FIX_LOCALE(base);
264 copyflags(new, base);
265
266 if (NULL == locale) {
267 realLocale = "C";
268 } else if ('\0' == locale[0]) {
269 useenv = 1;
270 }
271
272 for (type=0 ; type<XLC_LAST ; type++) {
273 if (mask & 1) {
274 if (useenv) {
275 realLocale = __get_locale_env(type);
276 }
277 new->components[type] =
278 constructors[type](realLocale, new);
279 if (new->components[type]) {
280 strncpy(new->components[type]->locale,
281 realLocale, ENCODING_LEN);
282 } else {
283 success = 0;
284 break;
285 }
286 } else {
287 if (!dupcomponent(type, base, new)) {
288 success = 0;
289 break;
290 }
291 }
292 mask >>= 1;
293 }
294 if (0 == success) {
295 xlocale_release(new);
296 new = NULL;
297 }
298
299 return (new);
300 }
301
duplocale(locale_t base)302 locale_t duplocale(locale_t base)
303 {
304 locale_t new = alloc_locale();
305 int type;
306
307 _once(&once_control, init_key);
308
309 if (NULL == new) {
310 return (NULL);
311 }
312
313 FIX_LOCALE(base);
314 copyflags(new, base);
315
316 for (type=0 ; type<XLC_LAST ; type++) {
317 dupcomponent(type, base, new);
318 }
319
320 return (new);
321 }
322
323 /*
324 * Free a locale_t. This is quite a poorly named function. It actually
325 * disclaims a reference to a locale_t, rather than freeing it.
326 */
327 int
freelocale(locale_t loc)328 freelocale(locale_t loc)
329 {
330 /* Fail if we're passed something that isn't a locale. */
331 if ((NULL == loc) || (LC_GLOBAL_LOCALE == loc)) {
332 return (-1);
333 }
334 /* If we're passed the global locale, pretend that we freed it but don't
335 * actually do anything. */
336 if (&__xlocale_global_locale == loc) {
337 return (0);
338 }
339 xlocale_release(loc);
340 return (0);
341 }
342
343 /*
344 * Returns the name of the locale for a particular component of a locale_t.
345 */
querylocale(int mask,locale_t loc)346 const char *querylocale(int mask, locale_t loc)
347 {
348 int type = ffs(mask) - 1;
349 FIX_LOCALE(loc);
350 if (type >= XLC_LAST)
351 return (NULL);
352 if (loc->components[type])
353 return (loc->components[type]->locale);
354 return ("C");
355 }
356
357 /*
358 * Installs the specified locale_t as this thread's locale.
359 */
uselocale(locale_t loc)360 locale_t uselocale(locale_t loc)
361 {
362 locale_t old = get_thread_locale();
363 if (NULL != loc) {
364 if (LC_GLOBAL_LOCALE == loc) {
365 loc = NULL;
366 }
367 set_thread_locale(loc);
368 }
369 return (old ? old : LC_GLOBAL_LOCALE);
370 }
371
372