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