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/*
59 * The locale for this thread.
60 */
61_Thread_local locale_t __thread_locale;
62
63/*
64 * Flag indicating that one or more per-thread locales exist.
65 */
66int __has_thread_locale;
67/*
68 * Private functions in setlocale.c.
69 */
70const char *
71__get_locale_env(int category);
72int
73__detect_path_locale(void);
74
75struct _xlocale __xlocale_global_locale = {
76	{0},
77	{
78		&__xlocale_global_collate,
79		&__xlocale_global_ctype,
80		&__xlocale_global_monetary,
81		&__xlocale_global_numeric,
82		&__xlocale_global_time,
83		&__xlocale_global_messages
84	},
85	1,
86	0,
87	1,
88	0
89};
90
91struct _xlocale __xlocale_C_locale = {
92	{0},
93	{
94		&__xlocale_C_collate,
95		&__xlocale_C_ctype,
96		0, 0, 0, 0
97	},
98	1,
99	0,
100	1,
101	0
102};
103
104static void*(*constructors[])(const char*, locale_t) =
105{
106	__collate_load,
107	__ctype_load,
108	__monetary_load,
109	__numeric_load,
110	__time_load,
111	__messages_load
112};
113
114static pthread_key_t locale_info_key;
115static int fake_tls;
116static locale_t thread_local_locale;
117
118static void init_key(void)
119{
120
121	pthread_key_create(&locale_info_key, xlocale_release);
122	pthread_setspecific(locale_info_key, (void*)42);
123	if (pthread_getspecific(locale_info_key) == (void*)42) {
124		pthread_setspecific(locale_info_key, 0);
125	} else {
126		fake_tls = 1;
127	}
128	/* At least one per-thread locale has now been set. */
129	__has_thread_locale = 1;
130	__detect_path_locale();
131}
132
133static pthread_once_t once_control = PTHREAD_ONCE_INIT;
134
135static locale_t
136get_thread_locale(void)
137{
138
139	_once(&once_control, init_key);
140
141	return (fake_tls ? thread_local_locale :
142		pthread_getspecific(locale_info_key));
143}
144
145static void
146set_thread_locale(locale_t loc)
147{
148	locale_t l = (loc == LC_GLOBAL_LOCALE) ? 0 : loc;
149
150	_once(&once_control, init_key);
151
152	if (NULL != l) {
153		xlocale_retain((struct xlocale_refcounted*)l);
154	}
155	locale_t old = get_thread_locale();
156	if ((NULL != old) && (l != old)) {
157		xlocale_release((struct xlocale_refcounted*)old);
158	}
159	if (fake_tls) {
160		thread_local_locale = l;
161	} else {
162		pthread_setspecific(locale_info_key, l);
163	}
164	__thread_locale = l;
165	__set_thread_rune_locale(loc);
166}
167
168/**
169 * Clean up a locale, once its reference count reaches zero.  This function is
170 * called by xlocale_release(), it should not be called directly.
171 */
172static void
173destruct_locale(void *l)
174{
175	locale_t loc = l;
176
177	for (int type=0 ; type<XLC_LAST ; type++) {
178		if (loc->components[type]) {
179			xlocale_release(loc->components[type]);
180		}
181	}
182	if (loc->csym) {
183		free(loc->csym);
184	}
185	free(l);
186}
187
188/**
189 * Allocates a new, uninitialised, locale.
190 */
191static locale_t
192alloc_locale(void)
193{
194	locale_t new = calloc(sizeof(struct _xlocale), 1);
195
196	new->header.destructor = destruct_locale;
197	new->monetary_locale_changed = 1;
198	new->numeric_locale_changed = 1;
199	return (new);
200}
201static void
202copyflags(locale_t new, locale_t old)
203{
204	new->using_monetary_locale = old->using_monetary_locale;
205	new->using_numeric_locale = old->using_numeric_locale;
206	new->using_time_locale = old->using_time_locale;
207	new->using_messages_locale = old->using_messages_locale;
208}
209
210static int dupcomponent(int type, locale_t base, locale_t new)
211{
212	/* Always copy from the global locale, since it has mutable components.
213	 */
214	struct xlocale_component *src = base->components[type];
215
216	if (&__xlocale_global_locale == base) {
217		new->components[type] = constructors[type](src->locale, new);
218		if (new->components[type]) {
219			strncpy(new->components[type]->locale, src->locale,
220			    ENCODING_LEN);
221			strncpy(new->components[type]->version, src->version,
222			    XLOCALE_DEF_VERSION_LEN);
223		}
224	} else if (base->components[type]) {
225		new->components[type] = xlocale_retain(base->components[type]);
226	} else {
227		/* If the component was NULL, return success - if base is a
228		 * valid locale then the flag indicating that this isn't
229		 * present should be set.  If it isn't a valid locale, then
230		 * we're stuck anyway. */
231		return 1;
232	}
233	return (0 != new->components[type]);
234}
235
236/*
237 * Public interfaces.  These are the five public functions described by the
238 * xlocale interface.
239 */
240
241locale_t newlocale(int mask, const char *locale, locale_t base)
242{
243	locale_t orig_base;
244	int type;
245	const char *realLocale = locale;
246	int useenv = 0;
247	int success = 1;
248
249	_once(&once_control, init_key);
250
251	locale_t new = alloc_locale();
252	if (NULL == new) {
253		return (NULL);
254	}
255
256	orig_base = base;
257	FIX_LOCALE(base);
258	copyflags(new, base);
259
260	if (NULL == locale) {
261		realLocale = "C";
262	} else if ('\0' == locale[0]) {
263		useenv = 1;
264	}
265
266	for (type=0 ; type<XLC_LAST ; type++) {
267		if (mask & 1) {
268			if (useenv) {
269				realLocale = __get_locale_env(type + 1);
270			}
271			new->components[type] =
272			     constructors[type](realLocale, new);
273			if (new->components[type]) {
274				strncpy(new->components[type]->locale,
275				     realLocale, ENCODING_LEN);
276			} else {
277				success = 0;
278				break;
279			}
280		} else {
281			if (!dupcomponent(type, base, new)) {
282				success = 0;
283				break;
284			}
285		}
286		mask >>= 1;
287	}
288	if (0 == success) {
289		xlocale_release(new);
290		new = NULL;
291	} else if (base == orig_base) {
292		xlocale_release(base);
293	}
294
295	return (new);
296}
297
298locale_t duplocale(locale_t base)
299{
300	locale_t new = alloc_locale();
301	int type;
302
303	_once(&once_control, init_key);
304
305	if (NULL == new) {
306		return (NULL);
307	}
308
309	FIX_LOCALE(base);
310	copyflags(new, base);
311
312	for (type=0 ; type<XLC_LAST ; type++) {
313		dupcomponent(type, base, new);
314	}
315
316	return (new);
317}
318
319/*
320 * Free a locale_t.  This is quite a poorly named function.  It actually
321 * disclaims a reference to a locale_t, rather than freeing it.
322 */
323void
324freelocale(locale_t loc)
325{
326
327	/*
328	 * Fail if we're passed something that isn't a locale. If we're
329	 * passed the global locale, pretend that we freed it but don't
330	 * actually do anything.
331	 */
332	if (loc != NULL && loc != LC_GLOBAL_LOCALE &&
333	    loc != &__xlocale_global_locale)
334		xlocale_release(loc);
335}
336
337/*
338 * Returns the name or version of the locale for a particular component of a
339 * locale_t.
340 */
341const char *querylocale(int mask, locale_t loc)
342{
343	int type = ffs(mask & ~LC_VERSION_MASK) - 1;
344	FIX_LOCALE(loc);
345	if (type >= XLC_LAST)
346		return (NULL);
347	if (mask & LC_VERSION_MASK) {
348		if (loc->components[type])
349			return (loc->components[type]->version);
350		return ("");
351	} else {
352		if (loc->components[type])
353			return (loc->components[type]->locale);
354		return ("C");
355	}
356}
357
358/*
359 * Installs the specified locale_t as this thread's locale.
360 */
361locale_t uselocale(locale_t loc)
362{
363	locale_t old = get_thread_locale();
364	if (NULL != loc) {
365		set_thread_locale(loc);
366	}
367	return (old ? old : LC_GLOBAL_LOCALE);
368}
369
370