1227753Stheraven/*- 2227753Stheraven * Copyright (c) 2011 The FreeBSD Foundation 3227753Stheraven * All rights reserved. 4227753Stheraven * 5227753Stheraven * This software was developed by David Chisnall under sponsorship from 6227753Stheraven * the FreeBSD Foundation. 7227753Stheraven * 8227753Stheraven * Redistribution and use in source and binary forms, with or without 9232498Stheraven * modification, are permitted provided that the following conditions 10232498Stheraven * are met: 11232498Stheraven * 1. Redistributions of source code must retain the above copyright 12232498Stheraven * notice, this list of conditions and the following disclaimer. 13232498Stheraven * 2. Redistributions in binary form must reproduce the above copyright 14232498Stheraven * notice, this list of conditions and the following disclaimer in the 15232498Stheraven * documentation and/or other materials provided with the distribution. 16227753Stheraven * 17227753Stheraven * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18227753Stheraven * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19227753Stheraven * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20232498Stheraven * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21227753Stheraven * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22227753Stheraven * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23227753Stheraven * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24227753Stheraven * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25227753Stheraven * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26227753Stheraven * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27227753Stheraven * SUCH DAMAGE. 28227753Stheraven * 29227753Stheraven * $FreeBSD$ 30227753Stheraven */ 31227753Stheraven 32227753Stheraven#include <pthread.h> 33227753Stheraven#include <stdio.h> 34227753Stheraven#include <string.h> 35232498Stheraven#include <runetype.h> 36227753Stheraven#include "libc_private.h" 37227753Stheraven#include "xlocale_private.h" 38227753Stheraven 39227753Stheraven/** 40227753Stheraven * Each locale loader declares a global component. This is used by setlocale() 41227753Stheraven * and also by xlocale with LC_GLOBAL_LOCALE.. 42227753Stheraven */ 43227753Stheravenextern struct xlocale_component __xlocale_global_collate; 44227753Stheravenextern struct xlocale_component __xlocale_global_ctype; 45227753Stheravenextern struct xlocale_component __xlocale_global_monetary; 46227753Stheravenextern struct xlocale_component __xlocale_global_numeric; 47227753Stheravenextern struct xlocale_component __xlocale_global_time; 48227753Stheravenextern struct xlocale_component __xlocale_global_messages; 49227753Stheraven/* 50227753Stheraven * And another version for the statically-allocated C locale. We only have 51227753Stheraven * components for the parts that are expected to be sensible. 52227753Stheraven */ 53227753Stheravenextern struct xlocale_component __xlocale_C_collate; 54227753Stheravenextern struct xlocale_component __xlocale_C_ctype; 55232498Stheraven 56232498Stheraven#ifndef __NO_TLS 57227753Stheraven/* 58232498Stheraven * The locale for this thread. 59232498Stheraven */ 60232498Stheraven_Thread_local locale_t __thread_locale; 61232498Stheraven#endif 62232498Stheraven/* 63232498Stheraven * Flag indicating that one or more per-thread locales exist. 64232498Stheraven */ 65232498Stheravenint __has_thread_locale; 66232498Stheraven/* 67227753Stheraven * Private functions in setlocale.c. 68227753Stheraven */ 69227753Stheravenconst char * 70227753Stheraven__get_locale_env(int category); 71227753Stheravenint 72227753Stheraven__detect_path_locale(void); 73227753Stheraven 74227753Stheravenstruct _xlocale __xlocale_global_locale = { 75227753Stheraven {0}, 76227753Stheraven { 77227753Stheraven &__xlocale_global_collate, 78227753Stheraven &__xlocale_global_ctype, 79227753Stheraven &__xlocale_global_monetary, 80227753Stheraven &__xlocale_global_numeric, 81227753Stheraven &__xlocale_global_time, 82227753Stheraven &__xlocale_global_messages 83227753Stheraven }, 84227753Stheraven 1, 85227753Stheraven 0, 86227753Stheraven 1, 87227753Stheraven 0 88227753Stheraven}; 89227753Stheraven 90227753Stheravenstruct _xlocale __xlocale_C_locale = { 91227753Stheraven {0}, 92227753Stheraven { 93227753Stheraven &__xlocale_C_collate, 94227753Stheraven &__xlocale_C_ctype, 95227753Stheraven 0, 0, 0, 0 96227753Stheraven }, 97227753Stheraven 1, 98227753Stheraven 0, 99227753Stheraven 1, 100227753Stheraven 0 101227753Stheraven}; 102227753Stheraven 103227753Stheravenstatic void*(*constructors[])(const char*, locale_t) = 104227753Stheraven{ 105227753Stheraven __collate_load, 106227753Stheraven __ctype_load, 107227753Stheraven __monetary_load, 108227753Stheraven __numeric_load, 109227753Stheraven __time_load, 110227753Stheraven __messages_load 111227753Stheraven}; 112227753Stheraven 113227753Stheravenstatic pthread_key_t locale_info_key; 114227753Stheravenstatic int fake_tls; 115227753Stheravenstatic locale_t thread_local_locale; 116227753Stheraven 117227753Stheravenstatic void init_key(void) 118227753Stheraven{ 119232498Stheraven 120227753Stheraven pthread_key_create(&locale_info_key, xlocale_release); 121227753Stheraven pthread_setspecific(locale_info_key, (void*)42); 122227753Stheraven if (pthread_getspecific(locale_info_key) == (void*)42) { 123227753Stheraven pthread_setspecific(locale_info_key, 0); 124227753Stheraven } else { 125227753Stheraven fake_tls = 1; 126227753Stheraven } 127232498Stheraven /* At least one per-thread locale has now been set. */ 128232498Stheraven __has_thread_locale = 1; 129227753Stheraven __detect_path_locale(); 130227753Stheraven} 131227753Stheraven 132227753Stheravenstatic pthread_once_t once_control = PTHREAD_ONCE_INIT; 133227753Stheraven 134227753Stheravenstatic locale_t 135227753Stheravenget_thread_locale(void) 136227753Stheraven{ 137232498Stheraven 138227753Stheraven _once(&once_control, init_key); 139227753Stheraven 140227753Stheraven return (fake_tls ? thread_local_locale : 141227753Stheraven pthread_getspecific(locale_info_key)); 142227753Stheraven} 143227753Stheraven 144232498Stheraven#ifdef __NO_TLS 145227753Stheravenlocale_t 146227753Stheraven__get_locale(void) 147227753Stheraven{ 148227753Stheraven locale_t l = get_thread_locale(); 149227753Stheraven return (l ? l : &__xlocale_global_locale); 150227753Stheraven 151227753Stheraven} 152232498Stheraven#endif 153227753Stheraven 154227753Stheravenstatic void 155227753Stheravenset_thread_locale(locale_t loc) 156227753Stheraven{ 157232498Stheraven 158232498Stheraven _once(&once_control, init_key); 159227753Stheraven 160227753Stheraven if (NULL != loc) { 161227753Stheraven xlocale_retain((struct xlocale_refcounted*)loc); 162227753Stheraven } 163227753Stheraven locale_t old = pthread_getspecific(locale_info_key); 164227753Stheraven if ((NULL != old) && (loc != old)) { 165227753Stheraven xlocale_release((struct xlocale_refcounted*)old); 166227753Stheraven } 167227753Stheraven if (fake_tls) { 168227753Stheraven thread_local_locale = loc; 169227753Stheraven } else { 170227753Stheraven pthread_setspecific(locale_info_key, loc); 171227753Stheraven } 172232498Stheraven#ifndef __NO_TLS 173232498Stheraven __thread_locale = loc; 174232498Stheraven __set_thread_rune_locale(loc); 175232498Stheraven#endif 176227753Stheraven} 177227753Stheraven 178227753Stheraven/** 179227753Stheraven * Clean up a locale, once its reference count reaches zero. This function is 180227753Stheraven * called by xlocale_release(), it should not be called directly. 181227753Stheraven */ 182227753Stheravenstatic void 183227753Stheravendestruct_locale(void *l) 184227753Stheraven{ 185227753Stheraven locale_t loc = l; 186232498Stheraven 187227753Stheraven for (int type=0 ; type<XLC_LAST ; type++) { 188227753Stheraven if (loc->components[type]) { 189227753Stheraven xlocale_release(loc->components[type]); 190227753Stheraven } 191227753Stheraven } 192227753Stheraven if (loc->csym) { 193227753Stheraven free(loc->csym); 194227753Stheraven } 195227753Stheraven free(l); 196227753Stheraven} 197227753Stheraven 198227753Stheraven/** 199227753Stheraven * Allocates a new, uninitialised, locale. 200227753Stheraven */ 201227753Stheravenstatic locale_t 202227753Stheravenalloc_locale(void) 203227753Stheraven{ 204227753Stheraven locale_t new = calloc(sizeof(struct _xlocale), 1); 205232498Stheraven 206227753Stheraven new->header.destructor = destruct_locale; 207227753Stheraven new->monetary_locale_changed = 1; 208227753Stheraven new->numeric_locale_changed = 1; 209227753Stheraven return (new); 210227753Stheraven} 211227753Stheravenstatic void 212227753Stheravencopyflags(locale_t new, locale_t old) 213227753Stheraven{ 214227753Stheraven new->using_monetary_locale = old->using_monetary_locale; 215227753Stheraven new->using_numeric_locale = old->using_numeric_locale; 216227753Stheraven new->using_time_locale = old->using_time_locale; 217227753Stheraven new->using_messages_locale = old->using_messages_locale; 218227753Stheraven} 219227753Stheraven 220227753Stheravenstatic int dupcomponent(int type, locale_t base, locale_t new) 221227753Stheraven{ 222232498Stheraven /* Always copy from the global locale, since it has mutable components. 223232498Stheraven */ 224227753Stheraven struct xlocale_component *src = base->components[type]; 225232498Stheraven 226227753Stheraven if (&__xlocale_global_locale == base) { 227227753Stheraven new->components[type] = constructors[type](src->locale, new); 228227753Stheraven if (new->components[type]) { 229232498Stheraven strncpy(new->components[type]->locale, src->locale, 230232498Stheraven ENCODING_LEN); 231227753Stheraven } 232227818Stheraven } else if (base->components[type]) { 233227818Stheraven new->components[type] = xlocale_retain(base->components[type]); 234227753Stheraven } else { 235232498Stheraven /* If the component was NULL, return success - if base is a 236232498Stheraven * valid locale then the flag indicating that this isn't 237232498Stheraven * present should be set. If it isn't a valid locale, then 238232498Stheraven * we're stuck anyway. */ 239227818Stheraven return 1; 240227753Stheraven } 241227753Stheraven return (0 != new->components[type]); 242227753Stheraven} 243227753Stheraven 244227753Stheraven/* 245227753Stheraven * Public interfaces. These are the five public functions described by the 246227753Stheraven * xlocale interface. 247227753Stheraven */ 248227753Stheraven 249227753Stheravenlocale_t newlocale(int mask, const char *locale, locale_t base) 250227753Stheraven{ 251227753Stheraven int type; 252227753Stheraven const char *realLocale = locale; 253227753Stheraven int useenv = 0; 254227753Stheraven int success = 1; 255227753Stheraven 256227753Stheraven _once(&once_control, init_key); 257227753Stheraven 258227753Stheraven locale_t new = alloc_locale(); 259227753Stheraven if (NULL == new) { 260227753Stheraven return (NULL); 261227753Stheraven } 262227753Stheraven 263227753Stheraven FIX_LOCALE(base); 264227753Stheraven copyflags(new, base); 265227753Stheraven 266227753Stheraven if (NULL == locale) { 267227753Stheraven realLocale = "C"; 268227753Stheraven } else if ('\0' == locale[0]) { 269227753Stheraven useenv = 1; 270227753Stheraven } 271227753Stheraven 272227753Stheraven for (type=0 ; type<XLC_LAST ; type++) { 273227753Stheraven if (mask & 1) { 274227753Stheraven if (useenv) { 275227753Stheraven realLocale = __get_locale_env(type); 276227753Stheraven } 277232498Stheraven new->components[type] = 278232498Stheraven constructors[type](realLocale, new); 279227753Stheraven if (new->components[type]) { 280232498Stheraven strncpy(new->components[type]->locale, 281232498Stheraven realLocale, ENCODING_LEN); 282227753Stheraven } else { 283227753Stheraven success = 0; 284227753Stheraven break; 285227753Stheraven } 286227753Stheraven } else { 287227753Stheraven if (!dupcomponent(type, base, new)) { 288227753Stheraven success = 0; 289227753Stheraven break; 290227753Stheraven } 291227753Stheraven } 292227753Stheraven mask >>= 1; 293227753Stheraven } 294227753Stheraven if (0 == success) { 295227753Stheraven xlocale_release(new); 296227753Stheraven new = NULL; 297227753Stheraven } 298227753Stheraven 299227753Stheraven return (new); 300227753Stheraven} 301227753Stheraven 302227753Stheravenlocale_t duplocale(locale_t base) 303227753Stheraven{ 304227753Stheraven locale_t new = alloc_locale(); 305227753Stheraven int type; 306227753Stheraven 307227753Stheraven _once(&once_control, init_key); 308227753Stheraven 309227753Stheraven if (NULL == new) { 310227753Stheraven return (NULL); 311227753Stheraven } 312227753Stheraven 313227753Stheraven FIX_LOCALE(base); 314227753Stheraven copyflags(new, base); 315227753Stheraven 316227753Stheraven for (type=0 ; type<XLC_LAST ; type++) { 317227753Stheraven dupcomponent(type, base, new); 318227753Stheraven } 319227753Stheraven 320227753Stheraven return (new); 321227753Stheraven} 322227753Stheraven 323227753Stheraven/* 324227753Stheraven * Free a locale_t. This is quite a poorly named function. It actually 325227753Stheraven * disclaims a reference to a locale_t, rather than freeing it. 326227753Stheraven */ 327227753Stheravenint 328227753Stheravenfreelocale(locale_t loc) 329227753Stheraven{ 330227753Stheraven /* Fail if we're passed something that isn't a locale. */ 331227753Stheraven if ((NULL == loc) || (LC_GLOBAL_LOCALE == loc)) { 332227753Stheraven return (-1); 333227753Stheraven } 334227753Stheraven /* If we're passed the global locale, pretend that we freed it but don't 335227753Stheraven * actually do anything. */ 336227753Stheraven if (&__xlocale_global_locale == loc) { 337227753Stheraven return (0); 338227753Stheraven } 339227753Stheraven xlocale_release(loc); 340227753Stheraven return (0); 341227753Stheraven} 342227753Stheraven 343227753Stheraven/* 344227753Stheraven * Returns the name of the locale for a particular component of a locale_t. 345227753Stheraven */ 346227753Stheravenconst char *querylocale(int mask, locale_t loc) 347227753Stheraven{ 348227753Stheraven int type = ffs(mask) - 1; 349227753Stheraven FIX_LOCALE(loc); 350227753Stheraven if (type >= XLC_LAST) 351227753Stheraven return (NULL); 352227753Stheraven if (loc->components[type]) 353227753Stheraven return (loc->components[type]->locale); 354232498Stheraven return ("C"); 355227753Stheraven} 356227753Stheraven 357227753Stheraven/* 358227753Stheraven * Installs the specified locale_t as this thread's locale. 359227753Stheraven */ 360227753Stheravenlocale_t uselocale(locale_t loc) 361227753Stheraven{ 362227753Stheraven locale_t old = get_thread_locale(); 363227753Stheraven if (NULL != loc) { 364227753Stheraven if (LC_GLOBAL_LOCALE == loc) { 365227753Stheraven loc = NULL; 366227753Stheraven } 367227753Stheraven set_thread_locale(loc); 368227753Stheraven } 369227753Stheraven return (old ? old : LC_GLOBAL_LOCALE); 370227753Stheraven} 371227753Stheraven 372