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 }