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
9232498Stheraven * modification, are permitted provided that the following conditions
10232498Stheraven * are met:
11232498Stheraven * 1. Redistributions of source code must retain the above copyright
12232498Stheraven *    notice, this list of conditions and the following disclaimer.
13232498Stheraven * 2. Redistributions in binary form must reproduce the above copyright
14232498Stheraven *    notice, this list of conditions and the following disclaimer in the
15232498Stheraven *    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
20232498Stheraven * 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>
35232498Stheraven#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;
55232498Stheraven
56232498Stheraven#ifndef __NO_TLS
57227753Stheraven/*
58232498Stheraven * The locale for this thread.
59232498Stheraven */
60232498Stheraven_Thread_local locale_t __thread_locale;
61232498Stheraven#endif
62232498Stheraven/*
63232498Stheraven * Flag indicating that one or more per-thread locales exist.
64232498Stheraven */
65232498Stheravenint __has_thread_locale;
66232498Stheraven/*
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{
119232498Stheraven
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	}
127232498Stheraven	/* At least one per-thread locale has now been set. */
128232498Stheraven	__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{
137232498Stheraven
138227753Stheraven	_once(&once_control, init_key);
139227753Stheraven
140227753Stheraven	return (fake_tls ? thread_local_locale :
141227753Stheraven		pthread_getspecific(locale_info_key));
142227753Stheraven}
143227753Stheraven
144232498Stheraven#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}
152232498Stheraven#endif
153227753Stheraven
154227753Stheravenstatic void
155227753Stheravenset_thread_locale(locale_t loc)
156227753Stheraven{
157232498Stheraven
158232498Stheraven	_once(&once_control, init_key);
159227753Stheraven
160227753Stheraven	if (NULL != loc) {
161227753Stheraven		xlocale_retain((struct xlocale_refcounted*)loc);
162227753Stheraven	}
163227753Stheraven	locale_t old = pthread_getspecific(locale_info_key);
164227753Stheraven	if ((NULL != old) && (loc != old)) {
165227753Stheraven		xlocale_release((struct xlocale_refcounted*)old);
166227753Stheraven	}
167227753Stheraven	if (fake_tls) {
168227753Stheraven		thread_local_locale = loc;
169227753Stheraven	} else {
170227753Stheraven		pthread_setspecific(locale_info_key, loc);
171227753Stheraven	}
172232498Stheraven#ifndef __NO_TLS
173232498Stheraven	__thread_locale = loc;
174232498Stheraven	__set_thread_rune_locale(loc);
175232498Stheraven#endif
176227753Stheraven}
177227753Stheraven
178227753Stheraven/**
179227753Stheraven * Clean up a locale, once its reference count reaches zero.  This function is
180227753Stheraven * called by xlocale_release(), it should not be called directly.
181227753Stheraven */
182227753Stheravenstatic void
183227753Stheravendestruct_locale(void *l)
184227753Stheraven{
185227753Stheraven	locale_t loc = l;
186232498Stheraven
187227753Stheraven	for (int type=0 ; type<XLC_LAST ; type++) {
188227753Stheraven		if (loc->components[type]) {
189227753Stheraven			xlocale_release(loc->components[type]);
190227753Stheraven		}
191227753Stheraven	}
192227753Stheraven	if (loc->csym) {
193227753Stheraven		free(loc->csym);
194227753Stheraven	}
195227753Stheraven	free(l);
196227753Stheraven}
197227753Stheraven
198227753Stheraven/**
199227753Stheraven * Allocates a new, uninitialised, locale.
200227753Stheraven */
201227753Stheravenstatic locale_t
202227753Stheravenalloc_locale(void)
203227753Stheraven{
204227753Stheraven	locale_t new = calloc(sizeof(struct _xlocale), 1);
205232498Stheraven
206227753Stheraven	new->header.destructor = destruct_locale;
207227753Stheraven	new->monetary_locale_changed = 1;
208227753Stheraven	new->numeric_locale_changed = 1;
209227753Stheraven	return (new);
210227753Stheraven}
211227753Stheravenstatic void
212227753Stheravencopyflags(locale_t new, locale_t old)
213227753Stheraven{
214227753Stheraven	new->using_monetary_locale = old->using_monetary_locale;
215227753Stheraven	new->using_numeric_locale = old->using_numeric_locale;
216227753Stheraven	new->using_time_locale = old->using_time_locale;
217227753Stheraven	new->using_messages_locale = old->using_messages_locale;
218227753Stheraven}
219227753Stheraven
220227753Stheravenstatic int dupcomponent(int type, locale_t base, locale_t new)
221227753Stheraven{
222232498Stheraven	/* Always copy from the global locale, since it has mutable components.
223232498Stheraven	 */
224227753Stheraven	struct xlocale_component *src = base->components[type];
225232498Stheraven
226227753Stheraven	if (&__xlocale_global_locale == base) {
227227753Stheraven		new->components[type] = constructors[type](src->locale, new);
228227753Stheraven		if (new->components[type]) {
229232498Stheraven			strncpy(new->components[type]->locale, src->locale,
230232498Stheraven			    ENCODING_LEN);
231227753Stheraven		}
232227818Stheraven	} else if (base->components[type]) {
233227818Stheraven		new->components[type] = xlocale_retain(base->components[type]);
234227753Stheraven	} else {
235232498Stheraven		/* If the component was NULL, return success - if base is a
236232498Stheraven		 * valid locale then the flag indicating that this isn't
237232498Stheraven		 * present should be set.  If it isn't a valid locale, then
238232498Stheraven		 * we're stuck anyway. */
239227818Stheraven		return 1;
240227753Stheraven	}
241227753Stheraven	return (0 != new->components[type]);
242227753Stheraven}
243227753Stheraven
244227753Stheraven/*
245227753Stheraven * Public interfaces.  These are the five public functions described by the
246227753Stheraven * xlocale interface.
247227753Stheraven */
248227753Stheraven
249227753Stheravenlocale_t newlocale(int mask, const char *locale, locale_t base)
250227753Stheraven{
251227753Stheraven	int type;
252227753Stheraven	const char *realLocale = locale;
253227753Stheraven	int useenv = 0;
254227753Stheraven	int success = 1;
255227753Stheraven
256227753Stheraven	_once(&once_control, init_key);
257227753Stheraven
258227753Stheraven	locale_t new = alloc_locale();
259227753Stheraven	if (NULL == new) {
260227753Stheraven		return (NULL);
261227753Stheraven	}
262227753Stheraven
263227753Stheraven	FIX_LOCALE(base);
264227753Stheraven	copyflags(new, base);
265227753Stheraven
266227753Stheraven	if (NULL == locale) {
267227753Stheraven		realLocale = "C";
268227753Stheraven	} else if ('\0' == locale[0]) {
269227753Stheraven		useenv = 1;
270227753Stheraven	}
271227753Stheraven
272227753Stheraven	for (type=0 ; type<XLC_LAST ; type++) {
273227753Stheraven		if (mask & 1) {
274227753Stheraven			if (useenv) {
275227753Stheraven				realLocale = __get_locale_env(type);
276227753Stheraven			}
277232498Stheraven			new->components[type] =
278232498Stheraven			     constructors[type](realLocale, new);
279227753Stheraven			if (new->components[type]) {
280232498Stheraven				strncpy(new->components[type]->locale,
281232498Stheraven				     realLocale, ENCODING_LEN);
282227753Stheraven			} else {
283227753Stheraven				success = 0;
284227753Stheraven				break;
285227753Stheraven			}
286227753Stheraven		} else {
287227753Stheraven			if (!dupcomponent(type, base, new)) {
288227753Stheraven				success = 0;
289227753Stheraven				break;
290227753Stheraven			}
291227753Stheraven		}
292227753Stheraven		mask >>= 1;
293227753Stheraven	}
294227753Stheraven	if (0 == success) {
295227753Stheraven		xlocale_release(new);
296227753Stheraven		new = NULL;
297227753Stheraven	}
298227753Stheraven
299227753Stheraven	return (new);
300227753Stheraven}
301227753Stheraven
302227753Stheravenlocale_t duplocale(locale_t base)
303227753Stheraven{
304227753Stheraven	locale_t new = alloc_locale();
305227753Stheraven	int type;
306227753Stheraven
307227753Stheraven	_once(&once_control, init_key);
308227753Stheraven
309227753Stheraven	if (NULL == new) {
310227753Stheraven		return (NULL);
311227753Stheraven	}
312227753Stheraven
313227753Stheraven	FIX_LOCALE(base);
314227753Stheraven	copyflags(new, base);
315227753Stheraven
316227753Stheraven	for (type=0 ; type<XLC_LAST ; type++) {
317227753Stheraven		dupcomponent(type, base, new);
318227753Stheraven	}
319227753Stheraven
320227753Stheraven	return (new);
321227753Stheraven}
322227753Stheraven
323227753Stheraven/*
324227753Stheraven * Free a locale_t.  This is quite a poorly named function.  It actually
325227753Stheraven * disclaims a reference to a locale_t, rather than freeing it.
326227753Stheraven */
327227753Stheravenint
328227753Stheravenfreelocale(locale_t loc)
329227753Stheraven{
330227753Stheraven	/* Fail if we're passed something that isn't a locale. */
331227753Stheraven	if ((NULL == loc) || (LC_GLOBAL_LOCALE == loc)) {
332227753Stheraven		return (-1);
333227753Stheraven	}
334227753Stheraven	/* If we're passed the global locale, pretend that we freed it but don't
335227753Stheraven	 * actually do anything. */
336227753Stheraven	if (&__xlocale_global_locale == loc) {
337227753Stheraven		return (0);
338227753Stheraven	}
339227753Stheraven	xlocale_release(loc);
340227753Stheraven	return (0);
341227753Stheraven}
342227753Stheraven
343227753Stheraven/*
344227753Stheraven * Returns the name of the locale for a particular component of a locale_t.
345227753Stheraven */
346227753Stheravenconst char *querylocale(int mask, locale_t loc)
347227753Stheraven{
348227753Stheraven	int type = ffs(mask) - 1;
349227753Stheraven	FIX_LOCALE(loc);
350227753Stheraven	if (type >= XLC_LAST)
351227753Stheraven		return (NULL);
352227753Stheraven	if (loc->components[type])
353227753Stheraven		return (loc->components[type]->locale);
354232498Stheraven	return ("C");
355227753Stheraven}
356227753Stheraven
357227753Stheraven/*
358227753Stheraven * Installs the specified locale_t as this thread's locale.
359227753Stheraven */
360227753Stheravenlocale_t uselocale(locale_t loc)
361227753Stheraven{
362227753Stheraven	locale_t old = get_thread_locale();
363227753Stheraven	if (NULL != loc) {
364227753Stheraven		if (LC_GLOBAL_LOCALE == loc) {
365227753Stheraven			loc = NULL;
366227753Stheraven		}
367227753Stheraven		set_thread_locale(loc);
368227753Stheraven	}
369227753Stheraven	return (old ? old : LC_GLOBAL_LOCALE);
370227753Stheraven}
371227753Stheraven
372