1227753Stheraven/*-
2227753Stheraven * Copyright (c) 2011 The FreeBSD Foundation
3227753Stheraven * All rights reserved.
4227753Stheraven *
5227753Stheraven * This software was developed by David Chisnall under sponsorship from
6227753Stheraven * the FreeBSD Foundation.
7227753Stheraven *
8227753Stheraven * Redistribution and use in source and binary forms, with or without
9235785Stheraven * modification, are permitted provided that the following conditions
10235785Stheraven * are met:
11235785Stheraven * 1. Redistributions of source code must retain the above copyright
12235785Stheraven *    notice, this list of conditions and the following disclaimer.
13235785Stheraven * 2. Redistributions in binary form must reproduce the above copyright
14235785Stheraven *    notice, this list of conditions and the following disclaimer in the
15235785Stheraven *    documentation and/or other materials provided with the distribution.
16227753Stheraven *
17227753Stheraven * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18227753Stheraven * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19227753Stheraven * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20235785Stheraven * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21227753Stheraven * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22227753Stheraven * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23227753Stheraven * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24227753Stheraven * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25227753Stheraven * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26227753Stheraven * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27227753Stheraven * SUCH DAMAGE.
28227753Stheraven *
29227753Stheraven * $FreeBSD$
30227753Stheraven */
31227753Stheraven
32227753Stheraven#include <pthread.h>
33227753Stheraven#include <stdio.h>
34227753Stheraven#include <string.h>
35235785Stheraven#include <runetype.h>
36227753Stheraven#include "libc_private.h"
37227753Stheraven#include "xlocale_private.h"
38227753Stheraven
39227753Stheraven/**
40227753Stheraven * Each locale loader declares a global component.  This is used by setlocale()
41227753Stheraven * and also by xlocale with LC_GLOBAL_LOCALE..
42227753Stheraven */
43227753Stheravenextern struct xlocale_component __xlocale_global_collate;
44227753Stheravenextern struct xlocale_component __xlocale_global_ctype;
45227753Stheravenextern struct xlocale_component __xlocale_global_monetary;
46227753Stheravenextern struct xlocale_component __xlocale_global_numeric;
47227753Stheravenextern struct xlocale_component __xlocale_global_time;
48227753Stheravenextern struct xlocale_component __xlocale_global_messages;
49227753Stheraven/*
50227753Stheraven * And another version for the statically-allocated C locale.  We only have
51227753Stheraven * components for the parts that are expected to be sensible.
52227753Stheraven */
53227753Stheravenextern struct xlocale_component __xlocale_C_collate;
54227753Stheravenextern struct xlocale_component __xlocale_C_ctype;
55235785Stheraven
56235785Stheraven#ifndef __NO_TLS
57227753Stheraven/*
58235785Stheraven * The locale for this thread.
59235785Stheraven */
60235785Stheraven_Thread_local locale_t __thread_locale;
61235785Stheraven#endif
62235785Stheraven/*
63235785Stheraven * Flag indicating that one or more per-thread locales exist.
64235785Stheraven */
65235785Stheravenint __has_thread_locale;
66235785Stheraven/*
67227753Stheraven * Private functions in setlocale.c.
68227753Stheraven */
69227753Stheravenconst char *
70227753Stheraven__get_locale_env(int category);
71227753Stheravenint
72227753Stheraven__detect_path_locale(void);
73227753Stheraven
74227753Stheravenstruct _xlocale __xlocale_global_locale = {
75227753Stheraven	{0},
76227753Stheraven	{
77227753Stheraven		&__xlocale_global_collate,
78227753Stheraven		&__xlocale_global_ctype,
79227753Stheraven		&__xlocale_global_monetary,
80227753Stheraven		&__xlocale_global_numeric,
81227753Stheraven		&__xlocale_global_time,
82227753Stheraven		&__xlocale_global_messages
83227753Stheraven	},
84227753Stheraven	1,
85227753Stheraven	0,
86227753Stheraven	1,
87227753Stheraven	0
88227753Stheraven};
89227753Stheraven
90227753Stheravenstruct _xlocale __xlocale_C_locale = {
91227753Stheraven	{0},
92227753Stheraven	{
93227753Stheraven		&__xlocale_C_collate,
94227753Stheraven		&__xlocale_C_ctype,
95227753Stheraven		0, 0, 0, 0
96227753Stheraven	},
97227753Stheraven	1,
98227753Stheraven	0,
99227753Stheraven	1,
100227753Stheraven	0
101227753Stheraven};
102227753Stheraven
103227753Stheravenstatic void*(*constructors[])(const char*, locale_t) =
104227753Stheraven{
105227753Stheraven	__collate_load,
106227753Stheraven	__ctype_load,
107227753Stheraven	__monetary_load,
108227753Stheraven	__numeric_load,
109227753Stheraven	__time_load,
110227753Stheraven	__messages_load
111227753Stheraven};
112227753Stheraven
113227753Stheravenstatic pthread_key_t locale_info_key;
114227753Stheravenstatic int fake_tls;
115227753Stheravenstatic locale_t thread_local_locale;
116227753Stheraven
117227753Stheravenstatic void init_key(void)
118227753Stheraven{
119235785Stheraven
120227753Stheraven	pthread_key_create(&locale_info_key, xlocale_release);
121227753Stheraven	pthread_setspecific(locale_info_key, (void*)42);
122227753Stheraven	if (pthread_getspecific(locale_info_key) == (void*)42) {
123227753Stheraven		pthread_setspecific(locale_info_key, 0);
124227753Stheraven	} else {
125227753Stheraven		fake_tls = 1;
126227753Stheraven	}
127235785Stheraven	/* At least one per-thread locale has now been set. */
128235785Stheraven	__has_thread_locale = 1;
129227753Stheraven	__detect_path_locale();
130227753Stheraven}
131227753Stheraven
132227753Stheravenstatic pthread_once_t once_control = PTHREAD_ONCE_INIT;
133227753Stheraven
134227753Stheravenstatic locale_t
135227753Stheravenget_thread_locale(void)
136227753Stheraven{
137235785Stheraven
138227753Stheraven	_once(&once_control, init_key);
139227753Stheraven
140227753Stheraven	return (fake_tls ? thread_local_locale :
141227753Stheraven		pthread_getspecific(locale_info_key));
142227753Stheraven}
143227753Stheraven
144235785Stheraven#ifdef __NO_TLS
145227753Stheravenlocale_t
146227753Stheraven__get_locale(void)
147227753Stheraven{
148227753Stheraven	locale_t l = get_thread_locale();
149227753Stheraven	return (l ? l : &__xlocale_global_locale);
150227753Stheraven
151227753Stheraven}
152235785Stheraven#endif
153227753Stheraven
154227753Stheravenstatic void
155227753Stheravenset_thread_locale(locale_t loc)
156227753Stheraven{
157284986Sdelphij	locale_t l = (loc == LC_GLOBAL_LOCALE) ? 0 : loc;
158235785Stheraven
159235785Stheraven	_once(&once_control, init_key);
160227753Stheraven
161284986Sdelphij	if (NULL != l) {
162284986Sdelphij		xlocale_retain((struct xlocale_refcounted*)l);
163227753Stheraven	}
164227753Stheraven	locale_t old = pthread_getspecific(locale_info_key);
165284986Sdelphij	if ((NULL != old) && (l != old)) {
166227753Stheraven		xlocale_release((struct xlocale_refcounted*)old);
167227753Stheraven	}
168227753Stheraven	if (fake_tls) {
169284986Sdelphij		thread_local_locale = l;
170227753Stheraven	} else {
171284986Sdelphij		pthread_setspecific(locale_info_key, l);
172227753Stheraven	}
173235785Stheraven#ifndef __NO_TLS
174284986Sdelphij	__thread_locale = l;
175235785Stheraven	__set_thread_rune_locale(loc);
176235785Stheraven#endif
177227753Stheraven}
178227753Stheraven
179227753Stheraven/**
180227753Stheraven * Clean up a locale, once its reference count reaches zero.  This function is
181227753Stheraven * called by xlocale_release(), it should not be called directly.
182227753Stheraven */
183227753Stheravenstatic void
184227753Stheravendestruct_locale(void *l)
185227753Stheraven{
186227753Stheraven	locale_t loc = l;
187235785Stheraven
188227753Stheraven	for (int type=0 ; type<XLC_LAST ; type++) {
189227753Stheraven		if (loc->components[type]) {
190227753Stheraven			xlocale_release(loc->components[type]);
191227753Stheraven		}
192227753Stheraven	}
193227753Stheraven	if (loc->csym) {
194227753Stheraven		free(loc->csym);
195227753Stheraven	}
196227753Stheraven	free(l);
197227753Stheraven}
198227753Stheraven
199227753Stheraven/**
200227753Stheraven * Allocates a new, uninitialised, locale.
201227753Stheraven */
202227753Stheravenstatic locale_t
203227753Stheravenalloc_locale(void)
204227753Stheraven{
205227753Stheraven	locale_t new = calloc(sizeof(struct _xlocale), 1);
206235785Stheraven
207227753Stheraven	new->header.destructor = destruct_locale;
208227753Stheraven	new->monetary_locale_changed = 1;
209227753Stheraven	new->numeric_locale_changed = 1;
210227753Stheraven	return (new);
211227753Stheraven}
212227753Stheravenstatic void
213227753Stheravencopyflags(locale_t new, locale_t old)
214227753Stheraven{
215227753Stheraven	new->using_monetary_locale = old->using_monetary_locale;
216227753Stheraven	new->using_numeric_locale = old->using_numeric_locale;
217227753Stheraven	new->using_time_locale = old->using_time_locale;
218227753Stheraven	new->using_messages_locale = old->using_messages_locale;
219227753Stheraven}
220227753Stheraven
221227753Stheravenstatic int dupcomponent(int type, locale_t base, locale_t new)
222227753Stheraven{
223235785Stheraven	/* Always copy from the global locale, since it has mutable components.
224235785Stheraven	 */
225227753Stheraven	struct xlocale_component *src = base->components[type];
226235785Stheraven
227227753Stheraven	if (&__xlocale_global_locale == base) {
228227753Stheraven		new->components[type] = constructors[type](src->locale, new);
229227753Stheraven		if (new->components[type]) {
230235785Stheraven			strncpy(new->components[type]->locale, src->locale,
231235785Stheraven			    ENCODING_LEN);
232227753Stheraven		}
233235785Stheraven	} else if (base->components[type]) {
234235785Stheraven		new->components[type] = xlocale_retain(base->components[type]);
235227753Stheraven	} else {
236235785Stheraven		/* If the component was NULL, return success - if base is a
237235785Stheraven		 * valid locale then the flag indicating that this isn't
238235785Stheraven		 * present should be set.  If it isn't a valid locale, then
239235785Stheraven		 * we're stuck anyway. */
240235785Stheraven		return 1;
241227753Stheraven	}
242227753Stheraven	return (0 != new->components[type]);
243227753Stheraven}
244227753Stheraven
245227753Stheraven/*
246227753Stheraven * Public interfaces.  These are the five public functions described by the
247227753Stheraven * xlocale interface.
248227753Stheraven */
249227753Stheraven
250227753Stheravenlocale_t newlocale(int mask, const char *locale, locale_t base)
251227753Stheraven{
252227753Stheraven	int type;
253227753Stheraven	const char *realLocale = locale;
254227753Stheraven	int useenv = 0;
255227753Stheraven	int success = 1;
256227753Stheraven
257227753Stheraven	_once(&once_control, init_key);
258227753Stheraven
259227753Stheraven	locale_t new = alloc_locale();
260227753Stheraven	if (NULL == new) {
261227753Stheraven		return (NULL);
262227753Stheraven	}
263227753Stheraven
264227753Stheraven	FIX_LOCALE(base);
265227753Stheraven	copyflags(new, base);
266227753Stheraven
267227753Stheraven	if (NULL == locale) {
268227753Stheraven		realLocale = "C";
269227753Stheraven	} else if ('\0' == locale[0]) {
270227753Stheraven		useenv = 1;
271227753Stheraven	}
272227753Stheraven
273227753Stheraven	for (type=0 ; type<XLC_LAST ; type++) {
274227753Stheraven		if (mask & 1) {
275227753Stheraven			if (useenv) {
276227753Stheraven				realLocale = __get_locale_env(type);
277227753Stheraven			}
278235785Stheraven			new->components[type] =
279235785Stheraven			     constructors[type](realLocale, new);
280227753Stheraven			if (new->components[type]) {
281235785Stheraven				strncpy(new->components[type]->locale,
282235785Stheraven				     realLocale, ENCODING_LEN);
283227753Stheraven			} else {
284227753Stheraven				success = 0;
285227753Stheraven				break;
286227753Stheraven			}
287227753Stheraven		} else {
288227753Stheraven			if (!dupcomponent(type, base, new)) {
289227753Stheraven				success = 0;
290227753Stheraven				break;
291227753Stheraven			}
292227753Stheraven		}
293227753Stheraven		mask >>= 1;
294227753Stheraven	}
295227753Stheraven	if (0 == success) {
296227753Stheraven		xlocale_release(new);
297227753Stheraven		new = NULL;
298227753Stheraven	}
299227753Stheraven
300227753Stheraven	return (new);
301227753Stheraven}
302227753Stheraven
303227753Stheravenlocale_t duplocale(locale_t base)
304227753Stheraven{
305227753Stheraven	locale_t new = alloc_locale();
306227753Stheraven	int type;
307227753Stheraven
308227753Stheraven	_once(&once_control, init_key);
309227753Stheraven
310227753Stheraven	if (NULL == new) {
311227753Stheraven		return (NULL);
312227753Stheraven	}
313227753Stheraven
314227753Stheraven	FIX_LOCALE(base);
315227753Stheraven	copyflags(new, base);
316227753Stheraven
317227753Stheraven	for (type=0 ; type<XLC_LAST ; type++) {
318227753Stheraven		dupcomponent(type, base, new);
319227753Stheraven	}
320227753Stheraven
321227753Stheraven	return (new);
322227753Stheraven}
323227753Stheraven
324227753Stheraven/*
325227753Stheraven * Free a locale_t.  This is quite a poorly named function.  It actually
326227753Stheraven * disclaims a reference to a locale_t, rather than freeing it.
327227753Stheraven */
328227753Stheravenint
329227753Stheravenfreelocale(locale_t loc)
330227753Stheraven{
331227753Stheraven	/* Fail if we're passed something that isn't a locale. */
332227753Stheraven	if ((NULL == loc) || (LC_GLOBAL_LOCALE == loc)) {
333227753Stheraven		return (-1);
334227753Stheraven	}
335227753Stheraven	/* If we're passed the global locale, pretend that we freed it but don't
336227753Stheraven	 * actually do anything. */
337227753Stheraven	if (&__xlocale_global_locale == loc) {
338227753Stheraven		return (0);
339227753Stheraven	}
340227753Stheraven	xlocale_release(loc);
341227753Stheraven	return (0);
342227753Stheraven}
343227753Stheraven
344227753Stheraven/*
345227753Stheraven * Returns the name of the locale for a particular component of a locale_t.
346227753Stheraven */
347227753Stheravenconst char *querylocale(int mask, locale_t loc)
348227753Stheraven{
349227753Stheraven	int type = ffs(mask) - 1;
350227753Stheraven	FIX_LOCALE(loc);
351227753Stheraven	if (type >= XLC_LAST)
352227753Stheraven		return (NULL);
353227753Stheraven	if (loc->components[type])
354227753Stheraven		return (loc->components[type]->locale);
355235785Stheraven	return ("C");
356227753Stheraven}
357227753Stheraven
358227753Stheraven/*
359227753Stheraven * Installs the specified locale_t as this thread's locale.
360227753Stheraven */
361227753Stheravenlocale_t uselocale(locale_t loc)
362227753Stheraven{
363227753Stheraven	locale_t old = get_thread_locale();
364227753Stheraven	if (NULL != loc) {
365227753Stheraven		set_thread_locale(loc);
366227753Stheraven	}
367227753Stheraven	return (old ? old : LC_GLOBAL_LOCALE);
368227753Stheraven}
369227753Stheraven
370