Home | History | Annotate | Download | only in bionic
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  *  * Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  *  * Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in
     12  *    the documentation and/or other materials provided with the
     13  *    distribution.
     14  *
     15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     16  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     17  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
     18  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
     19  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
     20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
     22  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     25  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  * SUCH DAMAGE.
     27  */
     28 
     29 #include <errno.h>
     30 #include <locale.h>
     31 #include <pthread.h>
     32 #include <stdlib.h>
     33 #include <string.h>
     34 #include <strings.h>
     35 #include <time.h>
     36 #include <wchar.h>
     37 
     38 #include "private/bionic_macros.h"
     39 
     40 // We currently support a single locale, the "C" locale (also known as "POSIX").
     41 
     42 static bool __bionic_current_locale_is_utf8 = true;
     43 
     44 struct __locale_t {
     45   size_t mb_cur_max;
     46 
     47   __locale_t(size_t mb_cur_max) : mb_cur_max(mb_cur_max) {
     48   }
     49 
     50   __locale_t(const __locale_t* other) {
     51     if (other == LC_GLOBAL_LOCALE) {
     52       mb_cur_max = __bionic_current_locale_is_utf8 ? 4 : 1;
     53     } else {
     54       mb_cur_max = other->mb_cur_max;
     55     }
     56   }
     57 
     58   DISALLOW_COPY_AND_ASSIGN(__locale_t);
     59 };
     60 
     61 size_t __ctype_get_mb_cur_max() {
     62   locale_t l = uselocale(NULL);
     63   if (l == LC_GLOBAL_LOCALE) {
     64     return __bionic_current_locale_is_utf8 ? 4 : 1;
     65   } else {
     66     return l->mb_cur_max;
     67   }
     68 }
     69 
     70 static pthread_once_t g_locale_once = PTHREAD_ONCE_INIT;
     71 static lconv g_locale;
     72 
     73 static void __locale_init() {
     74   g_locale.decimal_point = const_cast<char*>(".");
     75 
     76   char* not_available = const_cast<char*>("");
     77   g_locale.thousands_sep = not_available;
     78   g_locale.grouping = not_available;
     79   g_locale.int_curr_symbol = not_available;
     80   g_locale.currency_symbol = not_available;
     81   g_locale.mon_decimal_point = not_available;
     82   g_locale.mon_thousands_sep = not_available;
     83   g_locale.mon_grouping = not_available;
     84   g_locale.positive_sign = not_available;
     85   g_locale.negative_sign = not_available;
     86 
     87   g_locale.int_frac_digits = CHAR_MAX;
     88   g_locale.frac_digits = CHAR_MAX;
     89   g_locale.p_cs_precedes = CHAR_MAX;
     90   g_locale.p_sep_by_space = CHAR_MAX;
     91   g_locale.n_cs_precedes = CHAR_MAX;
     92   g_locale.n_sep_by_space = CHAR_MAX;
     93   g_locale.p_sign_posn = CHAR_MAX;
     94   g_locale.n_sign_posn = CHAR_MAX;
     95   g_locale.int_p_cs_precedes = CHAR_MAX;
     96   g_locale.int_p_sep_by_space = CHAR_MAX;
     97   g_locale.int_n_cs_precedes = CHAR_MAX;
     98   g_locale.int_n_sep_by_space = CHAR_MAX;
     99   g_locale.int_p_sign_posn = CHAR_MAX;
    100   g_locale.int_n_sign_posn = CHAR_MAX;
    101 }
    102 
    103 static bool __is_supported_locale(const char* locale) {
    104   return (strcmp(locale, "") == 0 ||
    105           strcmp(locale, "C") == 0 ||
    106           strcmp(locale, "C.UTF-8") == 0 ||
    107           strcmp(locale, "en_US.UTF-8") == 0 ||
    108           strcmp(locale, "POSIX") == 0);
    109 }
    110 
    111 lconv* localeconv() {
    112   pthread_once(&g_locale_once, __locale_init);
    113   return &g_locale;
    114 }
    115 
    116 locale_t duplocale(locale_t l) {
    117   return new __locale_t(l);
    118 }
    119 
    120 void freelocale(locale_t l) {
    121   delete l;
    122 }
    123 
    124 locale_t newlocale(int category_mask, const char* locale_name, locale_t /*base*/) {
    125   // Are 'category_mask' and 'locale_name' valid?
    126   if ((category_mask & ~LC_ALL_MASK) != 0 || locale_name == NULL) {
    127     errno = EINVAL;
    128     return NULL;
    129   }
    130 
    131   if (!__is_supported_locale(locale_name)) {
    132     errno = ENOENT;
    133     return NULL;
    134   }
    135 
    136   return new __locale_t(strstr(locale_name, "UTF-8") != NULL ? 4 : 1);
    137 }
    138 
    139 char* setlocale(int category, const char* locale_name) {
    140   // Is 'category' valid?
    141   if (category < LC_CTYPE || category > LC_IDENTIFICATION) {
    142     errno = EINVAL;
    143     return NULL;
    144   }
    145 
    146   // Caller wants to set the locale rather than just query?
    147   if (locale_name != NULL) {
    148     if (!__is_supported_locale(locale_name)) {
    149       // We don't support this locale.
    150       errno = ENOENT;
    151       return NULL;
    152     }
    153     __bionic_current_locale_is_utf8 = (strstr(locale_name, "UTF-8") != NULL);
    154   }
    155 
    156   return const_cast<char*>(__bionic_current_locale_is_utf8 ? "C.UTF-8" : "C");
    157 }
    158 
    159 // We can't use a constructor to create g_uselocal_key, because it may be used in constructors.
    160 static pthread_once_t g_uselocale_once = PTHREAD_ONCE_INIT;
    161 static pthread_key_t g_uselocale_key;
    162 
    163 static void g_uselocale_key_init() {
    164   pthread_key_create(&g_uselocale_key, NULL);
    165 }
    166 
    167 locale_t uselocale(locale_t new_locale) {
    168   pthread_once(&g_uselocale_once, g_uselocale_key_init);
    169   locale_t old_locale = static_cast<locale_t>(pthread_getspecific(g_uselocale_key));
    170 
    171   // If this is the first call to uselocale(3) on this thread, we return LC_GLOBAL_LOCALE.
    172   if (old_locale == NULL) {
    173     old_locale = LC_GLOBAL_LOCALE;
    174   }
    175 
    176   if (new_locale != NULL) {
    177     pthread_setspecific(g_uselocale_key, new_locale);
    178   }
    179 
    180   return old_locale;
    181 }
    182 
    183 int strcasecmp_l(const char* s1, const char* s2, locale_t) {
    184   return strcasecmp(s1, s2);
    185 }
    186 
    187 int strcoll_l(const char* s1, const char* s2, locale_t) {
    188   return strcoll(s1, s2);
    189 }
    190 
    191 char* strerror_l(int error, locale_t) {
    192   return strerror(error);
    193 }
    194 
    195 size_t strftime_l(char* s, size_t max, const char* format, const struct tm* tm, locale_t) {
    196   return strftime(s, max, format, tm);
    197 }
    198 
    199 int strncasecmp_l(const char* s1, const char* s2, size_t n, locale_t) {
    200   return strncasecmp(s1, s2, n);
    201 }
    202 
    203 long double strtold_l(const char* s, char** end_ptr, locale_t) {
    204   return strtold(s, end_ptr);
    205 }
    206 
    207 long long strtoll_l(const char* s, char** end_ptr, int base, locale_t) {
    208   return strtoll(s, end_ptr, base);
    209 }
    210 
    211 unsigned long long strtoull_l(const char* s, char** end_ptr, int base, locale_t) {
    212   return strtoull(s, end_ptr, base);
    213 }
    214 
    215 size_t strxfrm_l(char* dst, const char* src, size_t n, locale_t) {
    216   return strxfrm(dst, src, n);
    217 }
    218 
    219 int wcscasecmp_l(const wchar_t* ws1, const wchar_t* ws2, locale_t) {
    220   return wcscasecmp(ws1, ws2);
    221 }
    222 
    223 int wcsncasecmp_l(const wchar_t* ws1, const wchar_t* ws2, size_t n, locale_t) {
    224   return wcsncasecmp(ws1, ws2, n);
    225 }
    226