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 381