xlocale.c revision 227753
150276Speter/*-
262449Speter * Copyright (c) 2011 The FreeBSD Foundation
350276Speter * All rights reserved.
450276Speter *
550276Speter * This software was developed by David Chisnall under sponsorship from
650276Speter * the FreeBSD Foundation.
750276Speter *
850276Speter * Redistribution and use in source and binary forms, with or without
950276Speter * modification, are permitted provided that the following conditions * are met:
1050276Speter * 1.  Redistributions of source code must retain the above copyright notice,
1150276Speter *     this list of conditions and the following disclaimer.
1250276Speter * 2. Redistributions in binary form must reproduce the above copyright notice,
1350276Speter *    this list of conditions and the following disclaimer in the documentation
1450276Speter *    and/or other materials provided with the distribution.
1550276Speter *
1650276Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1750276Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1850276Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1950276Speter * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2050276Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2150276Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2250276Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2350276Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2450276Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2550276Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2650276Speter * SUCH DAMAGE.
2750276Speter *
2850276Speter * $FreeBSD: head/lib/libc/locale/xlocale.c 227753 2011-11-20 14:45:42Z theraven $
2950276Speter */
3050276Speter
3150276Speter#include <pthread.h>
3250276Speter#include <stdio.h>
3397049Speter#include <string.h>
3450276Speter#include "libc_private.h"
3550276Speter#include "xlocale_private.h"
3650276Speter
3750276Speter/**
3850276Speter * Each locale loader declares a global component.  This is used by setlocale()
3950276Speter * and also by xlocale with LC_GLOBAL_LOCALE..
4050276Speter */
4150276Speterextern struct xlocale_component __xlocale_global_collate;
4266963Speterextern struct xlocale_component __xlocale_global_ctype;
4350276Speterextern struct xlocale_component __xlocale_global_monetary;
4450276Speterextern struct xlocale_component __xlocale_global_numeric;
4550276Speterextern struct xlocale_component __xlocale_global_time;
4650276Speterextern struct xlocale_component __xlocale_global_messages;
4750276Speter/*
4850276Speter * And another version for the statically-allocated C locale.  We only have
4950276Speter * components for the parts that are expected to be sensible.
5050276Speter */
5150276Speterextern struct xlocale_component __xlocale_C_collate;
5250276Speterextern struct xlocale_component __xlocale_C_ctype;
5350276Speter/*
5450276Speter * Private functions in setlocale.c.
5550276Speter */
5650276Speterconst char *
5750276Speter__get_locale_env(int category);
5850276Speterint
5950276Speter__detect_path_locale(void);
6050276Speter
6150276Speterstruct _xlocale __xlocale_global_locale = {
6250276Speter	{0},
6350276Speter	{
6450276Speter		&__xlocale_global_collate,
6550276Speter		&__xlocale_global_ctype,
6650276Speter		&__xlocale_global_monetary,
6750276Speter		&__xlocale_global_numeric,
6850276Speter		&__xlocale_global_time,
6950276Speter		&__xlocale_global_messages
7050276Speter	},
7150276Speter	1,
7250276Speter	0,
7350276Speter	1,
7450276Speter	0
7550276Speter};
7650276Speter
7750276Speterstruct _xlocale __xlocale_C_locale = {
7850276Speter	{0},
7950276Speter	{
8050276Speter		&__xlocale_C_collate,
8150276Speter		&__xlocale_C_ctype,
8250276Speter		0, 0, 0, 0
8350276Speter	},
8450276Speter	1,
8550276Speter	0,
8650276Speter	1,
8750276Speter	0
8850276Speter};
8950276Speter
9050276Speterstatic void*(*constructors[])(const char*, locale_t) =
9150276Speter{
9250276Speter	__collate_load,
9350276Speter	__ctype_load,
9450276Speter	__monetary_load,
9550276Speter	__numeric_load,
9650276Speter	__time_load,
9750276Speter	__messages_load
9850276Speter};
9950276Speter
10050276Speterstatic pthread_key_t locale_info_key;
10150276Speterstatic int fake_tls;
10250276Speterstatic locale_t thread_local_locale;
10350276Speter
10450276Speterstatic void init_key(void)
10550276Speter{
10650276Speter	pthread_key_create(&locale_info_key, xlocale_release);
10750276Speter	pthread_setspecific(locale_info_key, (void*)42);
10850276Speter	if (pthread_getspecific(locale_info_key) == (void*)42) {
10950276Speter		pthread_setspecific(locale_info_key, 0);
11050276Speter	} else {
11150276Speter		fake_tls = 1;
11250276Speter	}
11350276Speter	__detect_path_locale();
11450276Speter}
11550276Speter
11650276Speterstatic pthread_once_t once_control = PTHREAD_ONCE_INIT;
11750276Speter
11850276Speterstatic locale_t
11950276Speterget_thread_locale(void)
12050276Speter{
12150276Speter	_once(&once_control, init_key);
12250276Speter
12350276Speter	return (fake_tls ? thread_local_locale :
12450276Speter		pthread_getspecific(locale_info_key));
12550276Speter}
12662449Speter
12762449Speterlocale_t
12862449Speter__get_locale(void)
12962449Speter{
13062449Speter	locale_t l = get_thread_locale();
13162449Speter	return (l ? l : &__xlocale_global_locale);
13262449Speter
13362449Speter}
13462449Speter
13562449Speterstatic void
13662449Speterset_thread_locale(locale_t loc)
13762449Speter{
13850276Speter	pthread_once(&once_control, init_key);
13950276Speter
14050276Speter	if (NULL != loc) {
14150276Speter		xlocale_retain((struct xlocale_refcounted*)loc);
14250276Speter	}
14350276Speter	locale_t old = pthread_getspecific(locale_info_key);
14450276Speter	if ((NULL != old) && (loc != old)) {
14550276Speter		xlocale_release((struct xlocale_refcounted*)old);
14650276Speter	}
14750276Speter	if (fake_tls) {
14850276Speter		thread_local_locale = loc;
14950276Speter	} else {
15050276Speter		pthread_setspecific(locale_info_key, loc);
15150276Speter	}
15250276Speter}
15350276Speter
15450276Speter/**
15550276Speter * Clean up a locale, once its reference count reaches zero.  This function is
15650276Speter * called by xlocale_release(), it should not be called directly.
15750276Speter */
15850276Speterstatic void
15950276Speterdestruct_locale(void *l)
16050276Speter{
16150276Speter	locale_t loc = l;
16250276Speter	for (int type=0 ; type<XLC_LAST ; type++) {
16350276Speter		if (loc->components[type]) {
16450276Speter			xlocale_release(loc->components[type]);
16566963Speter		}
16650276Speter	}
16750276Speter	if (loc->csym) {
16897049Speter		free(loc->csym);
16950276Speter	}
17050276Speter	free(l);
17150276Speter}
17250276Speter
17366963Speter/**
17497049Speter * Allocates a new, uninitialised, locale.
17576726Speter */
17666963Speterstatic 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 {
204		new->components[type] = xlocale_retain(base->components[type]);
205	}
206	return (0 != new->components[type]);
207}
208
209/*
210 * Public interfaces.  These are the five public functions described by the
211 * xlocale interface.
212 */
213
214locale_t newlocale(int mask, const char *locale, locale_t base)
215{
216	int type;
217	const char *realLocale = locale;
218	int useenv = 0;
219	int success = 1;
220
221	_once(&once_control, init_key);
222
223	locale_t new = alloc_locale();
224	if (NULL == new) {
225		return (NULL);
226	}
227
228	FIX_LOCALE(base);
229	copyflags(new, base);
230
231	if (NULL == locale) {
232		realLocale = "C";
233	} else if ('\0' == locale[0]) {
234		useenv = 1;
235	}
236
237	for (type=0 ; type<XLC_LAST ; type++) {
238		if (mask & 1) {
239			if (useenv) {
240				realLocale = __get_locale_env(type);
241			}
242			new->components[type] = constructors[type](realLocale, new);
243			if (new->components[type]) {
244				strncpy(new->components[type]->locale, realLocale, ENCODING_LEN);
245			} else {
246				success = 0;
247				break;
248			}
249		} else {
250			if (!dupcomponent(type, base, new)) {
251				success = 0;
252				break;
253			}
254		}
255		mask >>= 1;
256	}
257	if (0 == success) {
258		xlocale_release(new);
259		new = NULL;
260	}
261
262	return (new);
263}
264
265locale_t duplocale(locale_t base)
266{
267	locale_t new = alloc_locale();
268	int type;
269
270	_once(&once_control, init_key);
271
272	if (NULL == new) {
273		return (NULL);
274	}
275
276	FIX_LOCALE(base);
277	copyflags(new, base);
278
279	for (type=0 ; type<XLC_LAST ; type++) {
280		dupcomponent(type, base, new);
281	}
282
283	return (new);
284}
285
286/*
287 * Free a locale_t.  This is quite a poorly named function.  It actually
288 * disclaims a reference to a locale_t, rather than freeing it.
289 */
290int
291freelocale(locale_t loc)
292{
293	/* Fail if we're passed something that isn't a locale. */
294	if ((NULL == loc) || (LC_GLOBAL_LOCALE == loc)) {
295		return (-1);
296	}
297	/* If we're passed the global locale, pretend that we freed it but don't
298	 * actually do anything. */
299	if (&__xlocale_global_locale == loc) {
300		return (0);
301	}
302	xlocale_release(loc);
303	return (0);
304}
305
306/*
307 * Returns the name of the locale for a particular component of a locale_t.
308 */
309const char *querylocale(int mask, locale_t loc)
310{
311	int type = ffs(mask) - 1;
312	FIX_LOCALE(loc);
313	if (type >= XLC_LAST)
314		return (NULL);
315	if (loc->components[type])
316		return (loc->components[type]->locale);
317	return "C";
318}
319
320/*
321 * Installs the specified locale_t as this thread's locale.
322 */
323locale_t uselocale(locale_t loc)
324{
325	locale_t old = get_thread_locale();
326	if (NULL != loc) {
327		if (LC_GLOBAL_LOCALE == loc) {
328			loc = NULL;
329		}
330		set_thread_locale(loc);
331	}
332	return (old ? old : LC_GLOBAL_LOCALE);
333}
334
335