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
30 #include <pthread.h>
31 #include <stdio.h>
32 #include <string.h>
33
34 #include "runetype.h"
35 #include "xlocale_private.h"
36
37 /*
38 * Each locale loader declares a global component. This is used by setlocale()
39 * and also by xlocale with LC_GLOBAL_LOCALE..
40 */
41 extern struct xlocale_component __xlocale_global_collate;
42 extern struct xlocale_component __xlocale_global_ctype;
43 extern struct xlocale_component __xlocale_global_monetary;
44 extern struct xlocale_component __xlocale_global_numeric;
45 extern struct xlocale_component __xlocale_global_time;
46 extern struct xlocale_component __xlocale_global_messages;
47
48 /*
49 * And another version for the statically-allocated C locale. We only have
50 * components for the parts that are expected to be sensible.
51 */
52 extern struct xlocale_component __xlocale_C_collate;
53 extern struct xlocale_component __xlocale_C_ctype;
54
55 /*
56 * The locale for this thread.
57 */
58 __thread locale_t __thread_locale;
59
60 /*
61 * Flag indicating that one or more per-thread locales exist.
62 */
63 int __has_thread_locale;
64
65 struct _xlocale __xlocale_global_locale = {
66 {0},
67 {
68 &__xlocale_global_collate,
69 &__xlocale_global_ctype,
70 &__xlocale_global_monetary,
71 &__xlocale_global_numeric,
72 &__xlocale_global_time,
73 &__xlocale_global_messages
74 },
75 1,
76 0,
77 1,
78 0
79 };
80
81 struct _xlocale __xlocale_C_locale = {
82 {0},
83 {
84 &__xlocale_C_collate,
85 &__xlocale_C_ctype,
86 0, 0, 0, 0
87 },
88 1,
89 0,
90 1,
91 0
92 };
93
94 static void*(*constructors[])(const char*, locale_t) =
95 {
96 __collate_load,
97 __ctype_load,
98 __monetary_load,
99 __numeric_load,
100 __time_load,
101 __messages_load
102 };
103
104 static pthread_key_t locale_info_key;
105 static int fake_tls;
106 static locale_t thread_local_locale;
107
108 static void
109 init_key(void)
110 {
111 pthread_key_create(&locale_info_key, xlocale_release);
112 pthread_setspecific(locale_info_key, (void*)42);
113 if (pthread_getspecific(locale_info_key) == (void*)42) {
114 pthread_setspecific(locale_info_key, 0);
115 } else {
116 fake_tls = 1;
117 }
118 /* At least one per-thread locale has now been set. */
119 __has_thread_locale = 1;
120 __detect_path_locale();
121 }
122
123 static pthread_once_t once_control = PTHREAD_ONCE_INIT;
124
125 static locale_t
126 get_thread_locale(void)
127 {
128
129 // XXX _once(&once_control, init_key);
130 (void) pthread_once(&once_control, init_key);
131
132 return (fake_tls ? thread_local_locale :
133 pthread_getspecific(locale_info_key));
134 }
135
136 static void
137 set_thread_locale(locale_t loc)
138 {
139
140 // XXX _once(&once_control, init_key);
141 (void) pthread_once(&once_control, init_key);
142
143 if (NULL != loc) {
144 xlocale_retain((struct xlocale_refcounted*)loc);
145 }
146 locale_t old = pthread_getspecific(locale_info_key);
147 if ((NULL != old) && (loc != old)) {
148 xlocale_release((struct xlocale_refcounted*)old);
149 }
150 if (fake_tls) {
151 thread_local_locale = loc;
152 } else {
153 pthread_setspecific(locale_info_key, loc);
154 }
155 __thread_locale = loc;
156 __set_thread_rune_locale(loc);
157 }
158
159 /*
160 * Clean up a locale, once its reference count reaches zero. This function is
161 * called by xlocale_release(), it should not be called directly.
162 */
163 static void
164 destruct_locale(void *l)
165 {
166 locale_t loc = l;
167
168 for (int type = 0; type < XLC_LAST; type++) {
169 if (loc->components[type]) {
170 xlocale_release(loc->components[type]);
171 }
172 }
173 if (loc->csym) {
174 free(loc->csym);
175 }
176 free(l);
177 }
178
179 /*
180 * Allocates a new, uninitialised, locale.
181 */
182 static locale_t
183 alloc_locale(void)
184 {
185 locale_t new = calloc(sizeof(struct _xlocale), 1);
186
187 new->header.destructor = destruct_locale;
188 new->monetary_locale_changed = 1;
189 new->numeric_locale_changed = 1;
190 return (new);
191 }
192
193 static void
194 copyflags(locale_t new, locale_t old)
195 {
196 new->using_monetary_locale = old->using_monetary_locale;
197 new->using_numeric_locale = old->using_numeric_locale;
198 new->using_time_locale = old->using_time_locale;
199 new->using_messages_locale = old->using_messages_locale;
200 }
201
202 static int
203 dupcomponent(int type, locale_t base, locale_t new)
204 {
205 /*
206 * Always copy from the global locale, since it has mutable components.
207 */
208 struct xlocale_component *src = base->components[type];
209
210 if (&__xlocale_global_locale == base) {
211 new->components[type] = constructors[type](src->locale, new);
212 if (new->components[type]) {
213 strncpy(new->components[type]->locale, src->locale,
214 ENCODING_LEN);
215 }
216 } else if (base->components[type]) {
217 new->components[type] = xlocale_retain(base->components[type]);
218 } else {
219 /*
220 * If the component was NULL, return success - if base is a
221 * valid locale then the flag indicating that this isn't
222 * present should be set. If it isn't a valid locale, then
223 * we're stuck anyway.
224 */
225 return (1);
226 }
227 return (0 != new->components[type]);
228 }
229
230 /*
231 * Public interfaces. These are the five public functions described by the
232 * xlocale interface.
233 */
234 locale_t
235 newlocale(int mask, const char *locale, locale_t base)
236 {
237 int type;
238 const char *realLocale = locale;
239 int useenv = 0;
240 int success = 1;
241
242 // XXX _once(&once_control, init_key);
243 (void) pthread_once(&once_control, init_key);
244
245 locale_t new = alloc_locale();
246 if (NULL == new) {
247 return (NULL);
248 }
249
250 FIX_LOCALE(base);
251 copyflags(new, base);
252
253 if (NULL == locale) {
254 realLocale = "C";
255 } else if ('\0' == locale[0]) {
256 useenv = 1;
257 }
258
259 for (type = 0; type < XLC_LAST; type++) {
260 if (mask & 1) {
261 if (useenv) {
262 realLocale = __get_locale_env(type);
263 }
264 new->components[type] =
265 constructors[type](realLocale, new);
266 if (new->components[type]) {
267 strncpy(new->components[type]->locale,
268 realLocale, ENCODING_LEN);
269 } else {
270 success = 0;
271 break;
272 }
273 } else {
274 if (!dupcomponent(type, base, new)) {
275 success = 0;
276 break;
277 }
278 }
279 mask >>= 1;
280 }
281 if (0 == success) {
282 xlocale_release(new);
283 new = NULL;
284 }
285
286 return (new);
287 }
288
289 locale_t
290 duplocale(locale_t base)
291 {
292 locale_t new = alloc_locale();
293 int type;
294
295 // XXX _once(&once_control, init_key);
296 (void) pthread_once(&once_control, init_key);
297
298 if (NULL == new) {
299 return (NULL);
300 }
301
302 FIX_LOCALE(base);
303 copyflags(new, base);
304
305 for (type=0 ; type<XLC_LAST ; type++) {
306 dupcomponent(type, base, new);
307 }
308
309 return (new);
310 }
311
312 /*
313 * Free a locale_t. This is quite a poorly named function. It actually
314 * disclaims a reference to a locale_t, rather than freeing it.
315 */
316 int
317 freelocale(locale_t loc)
318 {
319 /* Fail if we're passed something that isn't a locale. */
320 if ((NULL == loc) || (LC_GLOBAL_LOCALE == loc)) {
321 return (-1);
322 }
323 /*
324 * If we're passed the global locale, pretend that we freed it but don't
325 * actually do anything.
326 */
327 if (&__xlocale_global_locale == loc) {
328 return (0);
329 }
330 xlocale_release(loc);
331 return (0);
332 }
333
334 /*
335 * Returns the name of the locale for a particular component of a locale_t.
336 */
337 const char *
338 querylocale(int mask, locale_t loc)
339 {
340 int type = ffs(mask) - 1;
341 FIX_LOCALE(loc);
342 if (type >= XLC_LAST)
343 return (NULL);
344 if (loc->components[type])
345 return (loc->components[type]->locale);
346 return ("C");
347 }
348
349 /*
350 * Installs the specified locale_t as this thread's locale.
351 */
352 locale_t
353 uselocale(locale_t loc)
354 {
355 locale_t old = get_thread_locale();
356 if (NULL != loc) {
357 if (LC_GLOBAL_LOCALE == loc) {
358 loc = NULL;
359 }
360 set_thread_locale(loc);
361 }
362 return (old ? old : LC_GLOBAL_LOCALE);
363 }