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