xlocale.c revision 231673
1132718Skan/*- 2132718Skan * Copyright (c) 2011 The FreeBSD Foundation 3132718Skan * All rights reserved. 4132718Skan * 5132718Skan * This software was developed by David Chisnall under sponsorship from 6132718Skan * the FreeBSD Foundation. 7132718Skan * 8132718Skan * Redistribution and use in source and binary forms, with or without 9132718Skan * modification, are permitted provided that the following conditions 10132718Skan * are met: 11132718Skan * 1. Redistributions of source code must retain the above copyright 12132718Skan * notice, this list of conditions and the following disclaimer. 13132718Skan * 2. Redistributions in binary form must reproduce the above copyright 14132718Skan * notice, this list of conditions and the following disclaimer in the 15132718Skan * documentation and/or other materials provided with the distribution. 16132718Skan * 17132718Skan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18132718Skan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19132718Skan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20132718Skan * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21132718Skan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22132718Skan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23132718Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24132718Skan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25132718Skan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26132718Skan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27132718Skan * SUCH DAMAGE. 28132718Skan * 29132718Skan * $FreeBSD: head/lib/libc/locale/xlocale.c 231673 2012-02-14 12:03:23Z theraven $ 30132718Skan */ 31132718Skan 32132718Skan#include <pthread.h> 33132718Skan#include <stdio.h> 34132718Skan#include <string.h> 35132718Skan#include <runetype.h> 36132718Skan#include "libc_private.h" 37132718Skan#include "xlocale_private.h" 38132718Skan 39132718Skan/** 40132718Skan * Each locale loader declares a global component. This is used by setlocale() 41132718Skan * and also by xlocale with LC_GLOBAL_LOCALE.. 42132718Skan */ 43132718Skanextern struct xlocale_component __xlocale_global_collate; 44132718Skanextern struct xlocale_component __xlocale_global_ctype; 45132718Skanextern struct xlocale_component __xlocale_global_monetary; 46132718Skanextern struct xlocale_component __xlocale_global_numeric; 47132718Skanextern struct xlocale_component __xlocale_global_time; 48132718Skanextern struct xlocale_component __xlocale_global_messages; 49132718Skan/* 50132718Skan * And another version for the statically-allocated C locale. We only have 51132718Skan * components for the parts that are expected to be sensible. 52132718Skan */ 53132718Skanextern struct xlocale_component __xlocale_C_collate; 54132718Skanextern struct xlocale_component __xlocale_C_ctype; 55132718Skan 56132718Skan#ifndef __NO_TLS 57132718Skan/* 58132718Skan * The locale for this thread. 59132718Skan */ 60132718Skan_Thread_local locale_t __thread_locale; 61132718Skan#endif 62132811Skan/* 63132811Skan * Flag indicating that one or more per-thread locales exist. 64132718Skan */ 65132718Skanint __has_thread_locale; 66132718Skan/* 67132718Skan * Private functions in setlocale.c. 68132718Skan */ 69132718Skanconst char * 70132718Skan__get_locale_env(int category); 71132718Skanint 72132718Skan__detect_path_locale(void); 73132718Skan 74132718Skanstruct _xlocale __xlocale_global_locale = { 75132718Skan {0}, 76132718Skan { 77132718Skan &__xlocale_global_collate, 78132718Skan &__xlocale_global_ctype, 79132718Skan &__xlocale_global_monetary, 80132718Skan &__xlocale_global_numeric, 81132718Skan &__xlocale_global_time, 82132718Skan &__xlocale_global_messages 83132718Skan }, 84132718Skan 1, 85132718Skan 0, 86132718Skan 1, 87132718Skan 0 88132718Skan}; 89132718Skan 90132718Skanstruct _xlocale __xlocale_C_locale = { 91132718Skan {0}, 92132718Skan { 93132718Skan &__xlocale_C_collate, 94132718Skan &__xlocale_C_ctype, 95132718Skan 0, 0, 0, 0 96132718Skan }, 97132718Skan 1, 98132718Skan 0, 99132718Skan 1, 100132718Skan 0 101132718Skan}; 102132718Skan 103132718Skanstatic void*(*constructors[])(const char*, locale_t) = 104132718Skan{ 105132718Skan __collate_load, 106132718Skan __ctype_load, 107132718Skan __monetary_load, 108132718Skan __numeric_load, 109132718Skan __time_load, 110132718Skan __messages_load 111132718Skan}; 112132718Skan 113132718Skanstatic pthread_key_t locale_info_key; 114132718Skanstatic int fake_tls; 115132718Skanstatic locale_t thread_local_locale; 116132718Skan 117132718Skanstatic void init_key(void) 118132718Skan{ 119132718Skan 120132718Skan pthread_key_create(&locale_info_key, xlocale_release); 121132718Skan pthread_setspecific(locale_info_key, (void*)42); 122132718Skan if (pthread_getspecific(locale_info_key) == (void*)42) { 123132718Skan pthread_setspecific(locale_info_key, 0); 124132718Skan } else { 125132718Skan fake_tls = 1; 126132718Skan } 127132718Skan /* At least one per-thread locale has now been set. */ 128132718Skan __has_thread_locale = 1; 129132718Skan __detect_path_locale(); 130132718Skan} 131132718Skan 132132718Skanstatic pthread_once_t once_control = PTHREAD_ONCE_INIT; 133132718Skan 134132718Skanstatic locale_t 135132718Skanget_thread_locale(void) 136132718Skan{ 137132718Skan 138132718Skan _once(&once_control, init_key); 139132718Skan 140132718Skan return (fake_tls ? thread_local_locale : 141132718Skan pthread_getspecific(locale_info_key)); 142132718Skan} 143132718Skan 144132718Skan#ifdef __NO_TLS 145132718Skanlocale_t 146132718Skan__get_locale(void) 147132718Skan{ 148132718Skan locale_t l = get_thread_locale(); 149132718Skan return (l ? l : &__xlocale_global_locale); 150132718Skan 151132718Skan} 152132718Skan#endif 153132718Skan 154132718Skanstatic void 155132718Skanset_thread_locale(locale_t loc) 156132718Skan{ 157132718Skan 158132718Skan _once(&once_control, init_key); 159132718Skan 160132718Skan if (NULL != loc) { 161132718Skan xlocale_retain((struct xlocale_refcounted*)loc); 162132718Skan } 163132718Skan locale_t old = pthread_getspecific(locale_info_key); 164132718Skan if ((NULL != old) && (loc != old)) { 165132718Skan xlocale_release((struct xlocale_refcounted*)old); 166132718Skan } 167132718Skan if (fake_tls) { 168132718Skan thread_local_locale = loc; 169132718Skan } else { 170132718Skan pthread_setspecific(locale_info_key, loc); 171132718Skan } 172132718Skan#ifndef __NO_TLS 173132718Skan __thread_locale = loc; 174132718Skan __set_thread_rune_locale(loc); 175132718Skan#endif 176132718Skan} 177132718Skan 178132718Skan/** 179132718Skan * Clean up a locale, once its reference count reaches zero. This function is 180132718Skan * called by xlocale_release(), it should not be called directly. 181132718Skan */ 182132718Skanstatic void 183132718Skandestruct_locale(void *l) 184132718Skan{ 185132718Skan locale_t loc = l; 186132718Skan 187132718Skan for (int type=0 ; type<XLC_LAST ; type++) { 188132718Skan if (loc->components[type]) { 189132718Skan xlocale_release(loc->components[type]); 190132718Skan } 191132718Skan } 192132718Skan if (loc->csym) { 193132718Skan free(loc->csym); 194132718Skan } 195132718Skan free(l); 196132718Skan} 197132718Skan 198132718Skan/** 199132718Skan * Allocates a new, uninitialised, locale. 200132718Skan */ 201132718Skanstatic locale_t 202132718Skanalloc_locale(void) 203132718Skan{ 204132718Skan locale_t new = calloc(sizeof(struct _xlocale), 1); 205132718Skan 206132718Skan new->header.destructor = destruct_locale; 207132718Skan new->monetary_locale_changed = 1; 208132718Skan new->numeric_locale_changed = 1; 209132718Skan return (new); 210132718Skan} 211132718Skanstatic void 212132718Skancopyflags(locale_t new, locale_t old) 213132718Skan{ 214132718Skan new->using_monetary_locale = old->using_monetary_locale; 215132718Skan new->using_numeric_locale = old->using_numeric_locale; 216132718Skan new->using_time_locale = old->using_time_locale; 217132718Skan new->using_messages_locale = old->using_messages_locale; 218132718Skan} 219132718Skan 220132718Skanstatic int dupcomponent(int type, locale_t base, locale_t new) 221132718Skan{ 222132718Skan /* Always copy from the global locale, since it has mutable components. 223132718Skan */ 224132718Skan struct xlocale_component *src = base->components[type]; 225132718Skan 226132718Skan if (&__xlocale_global_locale == base) { 227132718Skan new->components[type] = constructors[type](src->locale, new); 228132718Skan if (new->components[type]) { 229132718Skan strncpy(new->components[type]->locale, src->locale, 230132718Skan ENCODING_LEN); 231132718Skan } 232132718Skan } else if (base->components[type]) { 233132718Skan new->components[type] = xlocale_retain(base->components[type]); 234132718Skan } else { 235132718Skan /* If the component was NULL, return success - if base is a 236132718Skan * valid locale then the flag indicating that this isn't 237132718Skan * present should be set. If it isn't a valid locale, then 238132718Skan * we're stuck anyway. */ 239132718Skan return 1; 240132718Skan } 241132718Skan return (0 != new->components[type]); 242132718Skan} 243132718Skan 244132718Skan/* 245132718Skan * Public interfaces. These are the five public functions described by the 246132718Skan * xlocale interface. 247132718Skan */ 248132718Skan 249132718Skanlocale_t newlocale(int mask, const char *locale, locale_t base) 250132718Skan{ 251132718Skan int type; 252132718Skan const char *realLocale = locale; 253132718Skan int useenv = 0; 254132718Skan int success = 1; 255132718Skan 256132718Skan _once(&once_control, init_key); 257132718Skan 258132718Skan locale_t new = alloc_locale(); 259132718Skan if (NULL == new) { 260132718Skan return (NULL); 261132718Skan } 262132718Skan 263132718Skan FIX_LOCALE(base); 264132718Skan copyflags(new, base); 265132718Skan 266132718Skan if (NULL == locale) { 267132718Skan realLocale = "C"; 268132718Skan } else if ('\0' == locale[0]) { 269132718Skan useenv = 1; 270132718Skan } 271132718Skan 272132718Skan for (type=0 ; type<XLC_LAST ; type++) { 273132718Skan if (mask & 1) { 274132718Skan if (useenv) { 275132718Skan realLocale = __get_locale_env(type); 276132718Skan } 277132718Skan new->components[type] = 278132718Skan constructors[type](realLocale, new); 279132718Skan if (new->components[type]) { 280132718Skan strncpy(new->components[type]->locale, 281132718Skan realLocale, ENCODING_LEN); 282132718Skan } else { 283132718Skan success = 0; 284132718Skan break; 285132718Skan } 286132718Skan } else { 287132718Skan if (!dupcomponent(type, base, new)) { 288132718Skan success = 0; 289132718Skan break; 290132718Skan } 291132718Skan } 292132718Skan mask >>= 1; 293132718Skan } 294132718Skan if (0 == success) { 295132718Skan xlocale_release(new); 296132718Skan new = NULL; 297132718Skan } 298132718Skan 299132718Skan return (new); 300132718Skan} 301132718Skan 302132718Skanlocale_t duplocale(locale_t base) 303132718Skan{ 304132718Skan locale_t new = alloc_locale(); 305132718Skan int type; 306132718Skan 307132718Skan _once(&once_control, init_key); 308132718Skan 309132718Skan if (NULL == new) { 310132718Skan return (NULL); 311132718Skan } 312132718Skan 313132718Skan FIX_LOCALE(base); 314132718Skan copyflags(new, base); 315132718Skan 316132718Skan for (type=0 ; type<XLC_LAST ; type++) { 317132718Skan dupcomponent(type, base, new); 318132718Skan } 319132718Skan 320132718Skan return (new); 321132718Skan} 322132718Skan 323132718Skan/* 324132718Skan * Free a locale_t. This is quite a poorly named function. It actually 325132718Skan * disclaims a reference to a locale_t, rather than freeing it. 326132718Skan */ 327132718Skanint 328132718Skanfreelocale(locale_t loc) 329132718Skan{ 330132718Skan /* Fail if we're passed something that isn't a locale. */ 331132718Skan if ((NULL == loc) || (LC_GLOBAL_LOCALE == loc)) { 332132718Skan return (-1); 333132718Skan } 334132718Skan /* If we're passed the global locale, pretend that we freed it but don't 335132718Skan * actually do anything. */ 336132718Skan if (&__xlocale_global_locale == loc) { 337132718Skan return (0); 338132718Skan } 339132718Skan xlocale_release(loc); 340132718Skan return (0); 341132718Skan} 342132718Skan 343132718Skan/* 344132718Skan * Returns the name of the locale for a particular component of a locale_t. 345132718Skan */ 346132718Skanconst char *querylocale(int mask, locale_t loc) 347132718Skan{ 348132718Skan int type = ffs(mask) - 1; 349132718Skan FIX_LOCALE(loc); 350132718Skan if (type >= XLC_LAST) 351132718Skan return (NULL); 352132718Skan if (loc->components[type]) 353132718Skan return (loc->components[type]->locale); 354132718Skan return ("C"); 355132718Skan} 356132718Skan 357132718Skan/* 358132718Skan * Installs the specified locale_t as this thread's locale. 359132718Skan */ 360132718Skanlocale_t uselocale(locale_t loc) 361132718Skan{ 362132718Skan locale_t old = get_thread_locale(); 363132718Skan if (NULL != loc) { 364132718Skan if (LC_GLOBAL_LOCALE == loc) { 365132718Skan loc = NULL; 366132718Skan } 367132718Skan set_thread_locale(loc); 368132718Skan } 369132718Skan return (old ? old : LC_GLOBAL_LOCALE); 370132718Skan} 371132718Skan 372132718Skan