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