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