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{ 157284524Sdelphij locale_t l = (loc == LC_GLOBAL_LOCALE) ? 0 : loc; 158232498Stheraven 159232498Stheraven _once(&once_control, init_key); 160227753Stheraven 161284524Sdelphij if (NULL != l) { 162284524Sdelphij xlocale_retain((struct xlocale_refcounted*)l); 163227753Stheraven } 164227753Stheraven locale_t old = pthread_getspecific(locale_info_key); 165284524Sdelphij if ((NULL != old) && (l != old)) { 166227753Stheraven xlocale_release((struct xlocale_refcounted*)old); 167227753Stheraven } 168227753Stheraven if (fake_tls) { 169284524Sdelphij thread_local_locale = l; 170227753Stheraven } else { 171284524Sdelphij pthread_setspecific(locale_info_key, l); 172227753Stheraven } 173232498Stheraven#ifndef __NO_TLS 174284524Sdelphij __thread_locale = l; 175232498Stheraven __set_thread_rune_locale(loc); 176232498Stheraven#endif 177227753Stheraven} 178227753Stheraven 179227753Stheraven/** 180227753Stheraven * Clean up a locale, once its reference count reaches zero. This function is 181227753Stheraven * called by xlocale_release(), it should not be called directly. 182227753Stheraven */ 183227753Stheravenstatic void 184227753Stheravendestruct_locale(void *l) 185227753Stheraven{ 186227753Stheraven locale_t loc = l; 187232498Stheraven 188227753Stheraven for (int type=0 ; type<XLC_LAST ; type++) { 189227753Stheraven if (loc->components[type]) { 190227753Stheraven xlocale_release(loc->components[type]); 191227753Stheraven } 192227753Stheraven } 193227753Stheraven if (loc->csym) { 194227753Stheraven free(loc->csym); 195227753Stheraven } 196227753Stheraven free(l); 197227753Stheraven} 198227753Stheraven 199227753Stheraven/** 200227753Stheraven * Allocates a new, uninitialised, locale. 201227753Stheraven */ 202227753Stheravenstatic locale_t 203227753Stheravenalloc_locale(void) 204227753Stheraven{ 205227753Stheraven locale_t new = calloc(sizeof(struct _xlocale), 1); 206232498Stheraven 207227753Stheraven new->header.destructor = destruct_locale; 208227753Stheraven new->monetary_locale_changed = 1; 209227753Stheraven new->numeric_locale_changed = 1; 210227753Stheraven return (new); 211227753Stheraven} 212227753Stheravenstatic void 213227753Stheravencopyflags(locale_t new, locale_t old) 214227753Stheraven{ 215227753Stheraven new->using_monetary_locale = old->using_monetary_locale; 216227753Stheraven new->using_numeric_locale = old->using_numeric_locale; 217227753Stheraven new->using_time_locale = old->using_time_locale; 218227753Stheraven new->using_messages_locale = old->using_messages_locale; 219227753Stheraven} 220227753Stheraven 221227753Stheravenstatic int dupcomponent(int type, locale_t base, locale_t new) 222227753Stheraven{ 223232498Stheraven /* Always copy from the global locale, since it has mutable components. 224232498Stheraven */ 225227753Stheraven struct xlocale_component *src = base->components[type]; 226232498Stheraven 227227753Stheraven if (&__xlocale_global_locale == base) { 228227753Stheraven new->components[type] = constructors[type](src->locale, new); 229227753Stheraven if (new->components[type]) { 230232498Stheraven strncpy(new->components[type]->locale, src->locale, 231232498Stheraven ENCODING_LEN); 232227753Stheraven } 233227818Stheraven } else if (base->components[type]) { 234227818Stheraven new->components[type] = xlocale_retain(base->components[type]); 235227753Stheraven } else { 236232498Stheraven /* If the component was NULL, return success - if base is a 237232498Stheraven * valid locale then the flag indicating that this isn't 238232498Stheraven * present should be set. If it isn't a valid locale, then 239232498Stheraven * we're stuck anyway. */ 240227818Stheraven return 1; 241227753Stheraven } 242227753Stheraven return (0 != new->components[type]); 243227753Stheraven} 244227753Stheraven 245227753Stheraven/* 246227753Stheraven * Public interfaces. These are the five public functions described by the 247227753Stheraven * xlocale interface. 248227753Stheraven */ 249227753Stheraven 250227753Stheravenlocale_t newlocale(int mask, const char *locale, locale_t base) 251227753Stheraven{ 252227753Stheraven int type; 253227753Stheraven const char *realLocale = locale; 254227753Stheraven int useenv = 0; 255227753Stheraven int success = 1; 256227753Stheraven 257227753Stheraven _once(&once_control, init_key); 258227753Stheraven 259227753Stheraven locale_t new = alloc_locale(); 260227753Stheraven if (NULL == new) { 261227753Stheraven return (NULL); 262227753Stheraven } 263227753Stheraven 264227753Stheraven FIX_LOCALE(base); 265227753Stheraven copyflags(new, base); 266227753Stheraven 267227753Stheraven if (NULL == locale) { 268227753Stheraven realLocale = "C"; 269227753Stheraven } else if ('\0' == locale[0]) { 270227753Stheraven useenv = 1; 271227753Stheraven } 272227753Stheraven 273227753Stheraven for (type=0 ; type<XLC_LAST ; type++) { 274227753Stheraven if (mask & 1) { 275227753Stheraven if (useenv) { 276227753Stheraven realLocale = __get_locale_env(type); 277227753Stheraven } 278232498Stheraven new->components[type] = 279232498Stheraven constructors[type](realLocale, new); 280227753Stheraven if (new->components[type]) { 281232498Stheraven strncpy(new->components[type]->locale, 282232498Stheraven realLocale, ENCODING_LEN); 283227753Stheraven } else { 284227753Stheraven success = 0; 285227753Stheraven break; 286227753Stheraven } 287227753Stheraven } else { 288227753Stheraven if (!dupcomponent(type, base, new)) { 289227753Stheraven success = 0; 290227753Stheraven break; 291227753Stheraven } 292227753Stheraven } 293227753Stheraven mask >>= 1; 294227753Stheraven } 295227753Stheraven if (0 == success) { 296227753Stheraven xlocale_release(new); 297227753Stheraven new = NULL; 298227753Stheraven } 299227753Stheraven 300227753Stheraven return (new); 301227753Stheraven} 302227753Stheraven 303227753Stheravenlocale_t duplocale(locale_t base) 304227753Stheraven{ 305227753Stheraven locale_t new = alloc_locale(); 306227753Stheraven int type; 307227753Stheraven 308227753Stheraven _once(&once_control, init_key); 309227753Stheraven 310227753Stheraven if (NULL == new) { 311227753Stheraven return (NULL); 312227753Stheraven } 313227753Stheraven 314227753Stheraven FIX_LOCALE(base); 315227753Stheraven copyflags(new, base); 316227753Stheraven 317227753Stheraven for (type=0 ; type<XLC_LAST ; type++) { 318227753Stheraven dupcomponent(type, base, new); 319227753Stheraven } 320227753Stheraven 321227753Stheraven return (new); 322227753Stheraven} 323227753Stheraven 324227753Stheraven/* 325227753Stheraven * Free a locale_t. This is quite a poorly named function. It actually 326227753Stheraven * disclaims a reference to a locale_t, rather than freeing it. 327227753Stheraven */ 328227753Stheravenint 329227753Stheravenfreelocale(locale_t loc) 330227753Stheraven{ 331227753Stheraven /* Fail if we're passed something that isn't a locale. */ 332227753Stheraven if ((NULL == loc) || (LC_GLOBAL_LOCALE == loc)) { 333227753Stheraven return (-1); 334227753Stheraven } 335227753Stheraven /* If we're passed the global locale, pretend that we freed it but don't 336227753Stheraven * actually do anything. */ 337227753Stheraven if (&__xlocale_global_locale == loc) { 338227753Stheraven return (0); 339227753Stheraven } 340227753Stheraven xlocale_release(loc); 341227753Stheraven return (0); 342227753Stheraven} 343227753Stheraven 344227753Stheraven/* 345227753Stheraven * Returns the name of the locale for a particular component of a locale_t. 346227753Stheraven */ 347227753Stheravenconst char *querylocale(int mask, locale_t loc) 348227753Stheraven{ 349227753Stheraven int type = ffs(mask) - 1; 350227753Stheraven FIX_LOCALE(loc); 351227753Stheraven if (type >= XLC_LAST) 352227753Stheraven return (NULL); 353227753Stheraven if (loc->components[type]) 354227753Stheraven return (loc->components[type]->locale); 355232498Stheraven return ("C"); 356227753Stheraven} 357227753Stheraven 358227753Stheraven/* 359227753Stheraven * Installs the specified locale_t as this thread's locale. 360227753Stheraven */ 361227753Stheravenlocale_t uselocale(locale_t loc) 362227753Stheraven{ 363227753Stheraven locale_t old = get_thread_locale(); 364227753Stheraven if (NULL != loc) { 365227753Stheraven set_thread_locale(loc); 366227753Stheraven } 367227753Stheraven return (old ? old : LC_GLOBAL_LOCALE); 368227753Stheraven} 369227753Stheraven 370