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