xlocale.c revision 231673
1132718Skan/*-
2132718Skan * Copyright (c) 2011 The FreeBSD Foundation
3132718Skan * All rights reserved.
4132718Skan *
5132718Skan * This software was developed by David Chisnall under sponsorship from
6132718Skan * the FreeBSD Foundation.
7132718Skan *
8132718Skan * Redistribution and use in source and binary forms, with or without
9132718Skan * modification, are permitted provided that the following conditions
10132718Skan * are met:
11132718Skan * 1. Redistributions of source code must retain the above copyright
12132718Skan *    notice, this list of conditions and the following disclaimer.
13132718Skan * 2. Redistributions in binary form must reproduce the above copyright
14132718Skan *    notice, this list of conditions and the following disclaimer in the
15132718Skan *    documentation and/or other materials provided with the distribution.
16132718Skan *
17132718Skan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18132718Skan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19132718Skan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20132718Skan * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21132718Skan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22132718Skan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23132718Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24132718Skan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25132718Skan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26132718Skan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27132718Skan * SUCH DAMAGE.
28132718Skan *
29132718Skan * $FreeBSD: head/lib/libc/locale/xlocale.c 231673 2012-02-14 12:03:23Z theraven $
30132718Skan */
31132718Skan
32132718Skan#include <pthread.h>
33132718Skan#include <stdio.h>
34132718Skan#include <string.h>
35132718Skan#include <runetype.h>
36132718Skan#include "libc_private.h"
37132718Skan#include "xlocale_private.h"
38132718Skan
39132718Skan/**
40132718Skan * Each locale loader declares a global component.  This is used by setlocale()
41132718Skan * and also by xlocale with LC_GLOBAL_LOCALE..
42132718Skan */
43132718Skanextern struct xlocale_component __xlocale_global_collate;
44132718Skanextern struct xlocale_component __xlocale_global_ctype;
45132718Skanextern struct xlocale_component __xlocale_global_monetary;
46132718Skanextern struct xlocale_component __xlocale_global_numeric;
47132718Skanextern struct xlocale_component __xlocale_global_time;
48132718Skanextern struct xlocale_component __xlocale_global_messages;
49132718Skan/*
50132718Skan * And another version for the statically-allocated C locale.  We only have
51132718Skan * components for the parts that are expected to be sensible.
52132718Skan */
53132718Skanextern struct xlocale_component __xlocale_C_collate;
54132718Skanextern struct xlocale_component __xlocale_C_ctype;
55132718Skan
56132718Skan#ifndef __NO_TLS
57132718Skan/*
58132718Skan * The locale for this thread.
59132718Skan */
60132718Skan_Thread_local locale_t __thread_locale;
61132718Skan#endif
62132811Skan/*
63132811Skan * Flag indicating that one or more per-thread locales exist.
64132718Skan */
65132718Skanint __has_thread_locale;
66132718Skan/*
67132718Skan * Private functions in setlocale.c.
68132718Skan */
69132718Skanconst char *
70132718Skan__get_locale_env(int category);
71132718Skanint
72132718Skan__detect_path_locale(void);
73132718Skan
74132718Skanstruct _xlocale __xlocale_global_locale = {
75132718Skan	{0},
76132718Skan	{
77132718Skan		&__xlocale_global_collate,
78132718Skan		&__xlocale_global_ctype,
79132718Skan		&__xlocale_global_monetary,
80132718Skan		&__xlocale_global_numeric,
81132718Skan		&__xlocale_global_time,
82132718Skan		&__xlocale_global_messages
83132718Skan	},
84132718Skan	1,
85132718Skan	0,
86132718Skan	1,
87132718Skan	0
88132718Skan};
89132718Skan
90132718Skanstruct _xlocale __xlocale_C_locale = {
91132718Skan	{0},
92132718Skan	{
93132718Skan		&__xlocale_C_collate,
94132718Skan		&__xlocale_C_ctype,
95132718Skan		0, 0, 0, 0
96132718Skan	},
97132718Skan	1,
98132718Skan	0,
99132718Skan	1,
100132718Skan	0
101132718Skan};
102132718Skan
103132718Skanstatic void*(*constructors[])(const char*, locale_t) =
104132718Skan{
105132718Skan	__collate_load,
106132718Skan	__ctype_load,
107132718Skan	__monetary_load,
108132718Skan	__numeric_load,
109132718Skan	__time_load,
110132718Skan	__messages_load
111132718Skan};
112132718Skan
113132718Skanstatic pthread_key_t locale_info_key;
114132718Skanstatic int fake_tls;
115132718Skanstatic locale_t thread_local_locale;
116132718Skan
117132718Skanstatic void init_key(void)
118132718Skan{
119132718Skan
120132718Skan	pthread_key_create(&locale_info_key, xlocale_release);
121132718Skan	pthread_setspecific(locale_info_key, (void*)42);
122132718Skan	if (pthread_getspecific(locale_info_key) == (void*)42) {
123132718Skan		pthread_setspecific(locale_info_key, 0);
124132718Skan	} else {
125132718Skan		fake_tls = 1;
126132718Skan	}
127132718Skan	/* At least one per-thread locale has now been set. */
128132718Skan	__has_thread_locale = 1;
129132718Skan	__detect_path_locale();
130132718Skan}
131132718Skan
132132718Skanstatic pthread_once_t once_control = PTHREAD_ONCE_INIT;
133132718Skan
134132718Skanstatic locale_t
135132718Skanget_thread_locale(void)
136132718Skan{
137132718Skan
138132718Skan	_once(&once_control, init_key);
139132718Skan
140132718Skan	return (fake_tls ? thread_local_locale :
141132718Skan		pthread_getspecific(locale_info_key));
142132718Skan}
143132718Skan
144132718Skan#ifdef __NO_TLS
145132718Skanlocale_t
146132718Skan__get_locale(void)
147132718Skan{
148132718Skan	locale_t l = get_thread_locale();
149132718Skan	return (l ? l : &__xlocale_global_locale);
150132718Skan
151132718Skan}
152132718Skan#endif
153132718Skan
154132718Skanstatic void
155132718Skanset_thread_locale(locale_t loc)
156132718Skan{
157132718Skan
158132718Skan	_once(&once_control, init_key);
159132718Skan
160132718Skan	if (NULL != loc) {
161132718Skan		xlocale_retain((struct xlocale_refcounted*)loc);
162132718Skan	}
163132718Skan	locale_t old = pthread_getspecific(locale_info_key);
164132718Skan	if ((NULL != old) && (loc != old)) {
165132718Skan		xlocale_release((struct xlocale_refcounted*)old);
166132718Skan	}
167132718Skan	if (fake_tls) {
168132718Skan		thread_local_locale = loc;
169132718Skan	} else {
170132718Skan		pthread_setspecific(locale_info_key, loc);
171132718Skan	}
172132718Skan#ifndef __NO_TLS
173132718Skan	__thread_locale = loc;
174132718Skan	__set_thread_rune_locale(loc);
175132718Skan#endif
176132718Skan}
177132718Skan
178132718Skan/**
179132718Skan * Clean up a locale, once its reference count reaches zero.  This function is
180132718Skan * called by xlocale_release(), it should not be called directly.
181132718Skan */
182132718Skanstatic void
183132718Skandestruct_locale(void *l)
184132718Skan{
185132718Skan	locale_t loc = l;
186132718Skan
187132718Skan	for (int type=0 ; type<XLC_LAST ; type++) {
188132718Skan		if (loc->components[type]) {
189132718Skan			xlocale_release(loc->components[type]);
190132718Skan		}
191132718Skan	}
192132718Skan	if (loc->csym) {
193132718Skan		free(loc->csym);
194132718Skan	}
195132718Skan	free(l);
196132718Skan}
197132718Skan
198132718Skan/**
199132718Skan * Allocates a new, uninitialised, locale.
200132718Skan */
201132718Skanstatic locale_t
202132718Skanalloc_locale(void)
203132718Skan{
204132718Skan	locale_t new = calloc(sizeof(struct _xlocale), 1);
205132718Skan
206132718Skan	new->header.destructor = destruct_locale;
207132718Skan	new->monetary_locale_changed = 1;
208132718Skan	new->numeric_locale_changed = 1;
209132718Skan	return (new);
210132718Skan}
211132718Skanstatic void
212132718Skancopyflags(locale_t new, locale_t old)
213132718Skan{
214132718Skan	new->using_monetary_locale = old->using_monetary_locale;
215132718Skan	new->using_numeric_locale = old->using_numeric_locale;
216132718Skan	new->using_time_locale = old->using_time_locale;
217132718Skan	new->using_messages_locale = old->using_messages_locale;
218132718Skan}
219132718Skan
220132718Skanstatic int dupcomponent(int type, locale_t base, locale_t new)
221132718Skan{
222132718Skan	/* Always copy from the global locale, since it has mutable components.
223132718Skan	 */
224132718Skan	struct xlocale_component *src = base->components[type];
225132718Skan
226132718Skan	if (&__xlocale_global_locale == base) {
227132718Skan		new->components[type] = constructors[type](src->locale, new);
228132718Skan		if (new->components[type]) {
229132718Skan			strncpy(new->components[type]->locale, src->locale,
230132718Skan			    ENCODING_LEN);
231132718Skan		}
232132718Skan	} else if (base->components[type]) {
233132718Skan		new->components[type] = xlocale_retain(base->components[type]);
234132718Skan	} else {
235132718Skan		/* If the component was NULL, return success - if base is a
236132718Skan		 * valid locale then the flag indicating that this isn't
237132718Skan		 * present should be set.  If it isn't a valid locale, then
238132718Skan		 * we're stuck anyway. */
239132718Skan		return 1;
240132718Skan	}
241132718Skan	return (0 != new->components[type]);
242132718Skan}
243132718Skan
244132718Skan/*
245132718Skan * Public interfaces.  These are the five public functions described by the
246132718Skan * xlocale interface.
247132718Skan */
248132718Skan
249132718Skanlocale_t newlocale(int mask, const char *locale, locale_t base)
250132718Skan{
251132718Skan	int type;
252132718Skan	const char *realLocale = locale;
253132718Skan	int useenv = 0;
254132718Skan	int success = 1;
255132718Skan
256132718Skan	_once(&once_control, init_key);
257132718Skan
258132718Skan	locale_t new = alloc_locale();
259132718Skan	if (NULL == new) {
260132718Skan		return (NULL);
261132718Skan	}
262132718Skan
263132718Skan	FIX_LOCALE(base);
264132718Skan	copyflags(new, base);
265132718Skan
266132718Skan	if (NULL == locale) {
267132718Skan		realLocale = "C";
268132718Skan	} else if ('\0' == locale[0]) {
269132718Skan		useenv = 1;
270132718Skan	}
271132718Skan
272132718Skan	for (type=0 ; type<XLC_LAST ; type++) {
273132718Skan		if (mask & 1) {
274132718Skan			if (useenv) {
275132718Skan				realLocale = __get_locale_env(type);
276132718Skan			}
277132718Skan			new->components[type] =
278132718Skan			     constructors[type](realLocale, new);
279132718Skan			if (new->components[type]) {
280132718Skan				strncpy(new->components[type]->locale,
281132718Skan				     realLocale, ENCODING_LEN);
282132718Skan			} else {
283132718Skan				success = 0;
284132718Skan				break;
285132718Skan			}
286132718Skan		} else {
287132718Skan			if (!dupcomponent(type, base, new)) {
288132718Skan				success = 0;
289132718Skan				break;
290132718Skan			}
291132718Skan		}
292132718Skan		mask >>= 1;
293132718Skan	}
294132718Skan	if (0 == success) {
295132718Skan		xlocale_release(new);
296132718Skan		new = NULL;
297132718Skan	}
298132718Skan
299132718Skan	return (new);
300132718Skan}
301132718Skan
302132718Skanlocale_t duplocale(locale_t base)
303132718Skan{
304132718Skan	locale_t new = alloc_locale();
305132718Skan	int type;
306132718Skan
307132718Skan	_once(&once_control, init_key);
308132718Skan
309132718Skan	if (NULL == new) {
310132718Skan		return (NULL);
311132718Skan	}
312132718Skan
313132718Skan	FIX_LOCALE(base);
314132718Skan	copyflags(new, base);
315132718Skan
316132718Skan	for (type=0 ; type<XLC_LAST ; type++) {
317132718Skan		dupcomponent(type, base, new);
318132718Skan	}
319132718Skan
320132718Skan	return (new);
321132718Skan}
322132718Skan
323132718Skan/*
324132718Skan * Free a locale_t.  This is quite a poorly named function.  It actually
325132718Skan * disclaims a reference to a locale_t, rather than freeing it.
326132718Skan */
327132718Skanint
328132718Skanfreelocale(locale_t loc)
329132718Skan{
330132718Skan	/* Fail if we're passed something that isn't a locale. */
331132718Skan	if ((NULL == loc) || (LC_GLOBAL_LOCALE == loc)) {
332132718Skan		return (-1);
333132718Skan	}
334132718Skan	/* If we're passed the global locale, pretend that we freed it but don't
335132718Skan	 * actually do anything. */
336132718Skan	if (&__xlocale_global_locale == loc) {
337132718Skan		return (0);
338132718Skan	}
339132718Skan	xlocale_release(loc);
340132718Skan	return (0);
341132718Skan}
342132718Skan
343132718Skan/*
344132718Skan * Returns the name of the locale for a particular component of a locale_t.
345132718Skan */
346132718Skanconst char *querylocale(int mask, locale_t loc)
347132718Skan{
348132718Skan	int type = ffs(mask) - 1;
349132718Skan	FIX_LOCALE(loc);
350132718Skan	if (type >= XLC_LAST)
351132718Skan		return (NULL);
352132718Skan	if (loc->components[type])
353132718Skan		return (loc->components[type]->locale);
354132718Skan	return ("C");
355132718Skan}
356132718Skan
357132718Skan/*
358132718Skan * Installs the specified locale_t as this thread's locale.
359132718Skan */
360132718Skanlocale_t uselocale(locale_t loc)
361132718Skan{
362132718Skan	locale_t old = get_thread_locale();
363132718Skan	if (NULL != loc) {
364132718Skan		if (LC_GLOBAL_LOCALE == loc) {
365132718Skan			loc = NULL;
366132718Skan		}
367132718Skan		set_thread_locale(loc);
368132718Skan	}
369132718Skan	return (old ? old : LC_GLOBAL_LOCALE);
370132718Skan}
371132718Skan
372132718Skan