blob: 8aa291907397d4ee635e1eb4066319fc59740d70 [file] [log] [blame]
// -*- C++ -*-
//===-------------------- support/android/locale_support.c ------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <wctype.h>
// Contains an implementation of all locale-specific functions (those
// ending in _l, like strcoll_l()), as simple wrapper to the non-locale
// specific ones for now.
//
// That's because Android's C library doesn't support locales. Or more
// specifically, only supports the "C" one.
//
// TODO(digit): Write a more complete implementation that uses JNI to
// invoke the platform APIs to implement proper handling.
//
///////////////////////////////////////////////////////////////////////
// ctype.h declarations
# define define_char_wrapper_l(name) \
int name ## _l (int ch, locale_t loc) { \
return name (ch); \
}
define_char_wrapper_l (isalnum);
define_char_wrapper_l (isalpha);
define_char_wrapper_l (iscntrl);
define_char_wrapper_l (isdigit);
define_char_wrapper_l (islower);
define_char_wrapper_l (isgraph);
define_char_wrapper_l (isprint);
define_char_wrapper_l (ispunct);
define_char_wrapper_l (isspace);
define_char_wrapper_l (isupper);
define_char_wrapper_l (isxdigit);
define_char_wrapper_l (isblank);
define_char_wrapper_l (tolower)
define_char_wrapper_l (toupper)
// TODO(ajwong): This table is copied from bionic's ctype implementation.
// It doesn't support signed chars and will index out of bounds. The best way
// to fix is to patch bionic's ctype array to support both signed and
// unsigned char and then just directly reference it.
static char const real_ctype_c_mask_table[256] = {
0,
_C, _C, _C, _C, _C, _C, _C, _C,
_C, _C|_S, _C|_S, _C|_S, _C|_S, _C|_S, _C, _C,
_C, _C, _C, _C, _C, _C, _C, _C,
_C, _C, _C, _C, _C, _C, _C, _C,
_S|(char)_B, _P, _P, _P, _P, _P, _P, _P,
_P, _P, _P, _P, _P, _P, _P, _P,
_N, _N, _N, _N, _N, _N, _N, _N,
_N, _N, _P, _P, _P, _P, _P, _P,
_P, _U|_X, _U|_X, _U|_X, _U|_X, _U|_X, _U|_X, _U,
_U, _U, _U, _U, _U, _U, _U, _U,
_U, _U, _U, _U, _U, _U, _U, _U,
_U, _U, _U, _P, _P, _P, _P, _P,
_P, _L|_X, _L|_X, _L|_X, _L|_X, _L|_X, _L|_X, _L,
_L, _L, _L, _L, _L, _L, _L, _L,
_L, _L, _L, _L, _L, _L, _L, _L,
/* determine printability based on the IS0 8859 8-bit standard */
_L, _L, _L, _P, _P, _P, _P, _C,
_C, _C, _C, _C, _C, _C, _C, _C, /* 80 */
_C, _C, _C, _C, _C, _C, _C, _C, /* 88 */
_C, _C, _C, _C, _C, _C, _C, _C, /* 90 */
_C, _C, _C, _C, _C, _C, _C, _C, /* 98 */
_P, _P, _P, _P, _P, _P, _P, _P, /* A0 */
_P, _P, _P, _P, _P, _P, _P, _P, /* A8 */
_P, _P, _P, _P, _P, _P, _P, _P, /* B0 */
_P, _P, _P, _P, _P, _P, _P, _P, /* B8 */
_P, _P, _P, _P, _P, _P, _P, _P, /* C0 */
_P, _P, _P, _P, _P, _P, _P, _P, /* C8 */
_P, _P, _P, _P, _P, _P, _P, _P, /* D0 */
_P, _P, _P, _P, _P, _P, _P, _P, /* D8 */
_P, _P, _P, _P, _P, _P, _P, _P, /* E0 */
_P, _P, _P, _P, _P, _P, _P, _P, /* E8 */
_P, _P, _P, _P, _P, _P, _P, _P, /* F0 */
_P, _P, _P, _P, _P, _P, _P, _P /* F8 */
};
char const* const __ctype_c_mask_table = &real_ctype_c_mask_table[0];
///////////////////////////////////////////////////////////////////////
// stdio.h declarations
int vasprintf_l(char** strp, locale_t l, const char* fmt, va_list args) {
// Ignore locale.
return vasprintf(strp, fmt, args);
}
int asprintf_l(char** strp, locale_t locale, const char* fmt, ...) {
va_list args;
va_start(args, fmt);
int result = vasprintf_l(strp, locale, fmt, args);
va_end(args);
return result;
}
int vsprintf_l(char* str, locale_t l, const char* fmt, va_list args) {
// Ignore locale.
return vsprintf(str, fmt, args);
}
int sprintf_l(char* str, locale_t l, const char* fmt, ...) {
va_list args;
va_start(args, fmt);
int result = vsprintf_l(str, l, fmt, args);
va_end(args);
return result;
}
int vsnprintf_l(char* str, size_t size, locale_t l, const char* fmt, va_list args) {
return vsnprintf(str, size, fmt, args);
}
int snprintf_l(char* str, size_t size, locale_t l, const char* fmt, ...) {
va_list args;
va_start(args, fmt);
int result = vsnprintf_l(str, size, l, fmt, args);
va_end(args);
return result;
}
int vsscanf_l(const char* str, locale_t l, const char* fmt, va_list args) {
return vsscanf(str, fmt, args);
}
int sscanf_l(const char* str, locale_t l, const char* fmt, ...) {
va_list args;
va_start(args, fmt);
int result = vsscanf_l(str, l, fmt, args);
va_end(args);
return result;
}
///////////////////////////////////////////////////////////////////////
// stdlib.h declarations
long strtol_l(const char *nptr, char **endptr, int base, locale_t loc) {
return strtol(nptr, endptr, base);
}
long long strtoll_l(const char *nptr, char **endptr, int base, locale_t loc) {
return strtoll(nptr, endptr, base);
}
unsigned long strtoul_l(const char *nptr, char **endptr, int base, locale_t loc) {
return strtoul(nptr, endptr, base);
}
unsigned long long strtoull_l(const char *nptr, char **endptr, int base, locale_t loc) {
return strtoull(nptr, endptr, base);
}
long double strtold_l (const char *nptr, char **endptr, locale_t loc) {
return strtold(nptr, endptr);
}
///////////////////////////////////////////////////////////////////////
// string.h declarations
int strcoll_l(const char* s1, const char* s2, locale_t loc) {
return strcoll(s1, s2);
}
int strxfrm_l(char* dst, const char* src, size_t n, locale_t loc) {
return strxfrm(dst, src, n);
}
///////////////////////////////////////////////////////////////////////
// time.h declarations
size_t strftime_l(char *s, size_t maxsize, const char *format,
const struct tm * timeptr, locale_t loc) {
return strftime(s, maxsize, format, timeptr);
}
///////////////////////////////////////////////////////////////////////
// wchar.h declarations
int wcscoll_l(const wchar_t* s1, const wchar_t* s2, locale_t loc) {
return wcscoll(s1, s2);
}
int wcsxfrm_l(wchar_t* dst, const wchar_t* src, size_t n, locale_t loc) {
return wcsxfrm(dst, src, n);
}
///////////////////////////////////////////////////////////////////////
// wctype.h declarations
#define define_wchar_wrapper(name) \
int name ## _l (wint_t c, locale_t loc) { \
return name (c); \
}
define_wchar_wrapper(iswspace)
define_wchar_wrapper(iswprint)
define_wchar_wrapper(iswcntrl)
define_wchar_wrapper(iswupper)
define_wchar_wrapper(iswlower)
define_wchar_wrapper(iswalpha)
define_wchar_wrapper(iswdigit)
define_wchar_wrapper(iswpunct)
define_wchar_wrapper(iswxdigit)
define_wchar_wrapper(iswblank)
wint_t towlower_l(wint_t c, locale_t loc) {
return towlower(c);
}
wint_t towupper_l(wint_t c, locale_t loc) {
return towupper(c);
}
///////////////////////////////////////////////////////////////////////
// locale.h declarations
#define LC_NULL_LOCALE ((locale_t)0)
locale_t newlocale(int category_mask, const char* locale, locale_t base) {
if (base != LC_NULL_LOCALE)
return base;
locale_t loc = calloc(1, sizeof(*loc));
return loc;
}
locale_t duplocale(locale_t loc) {
if (loc == LC_GLOBAL_LOCALE)
return loc;
if (loc == LC_NULL_LOCALE) {
errno = EINVAL;
return LC_NULL_LOCALE;
}
locale_t copy = calloc(1, sizeof(*loc));
copy[0] = loc[0];
return copy;
}
// Static mutable variable because setlocale() is supposed to return
// a pointer to a writable C string.
static char g_C_LOCALE_SETTING[] = "C";
char *setlocale(int category, const char *locale) {
// Sanity check.
if (!locale) {
errno = EINVAL;
return NULL;
}
// Only accept "", "C" or "POSIX", all equivalent on Android.
if (strcmp(locale, "") && strcmp(locale, "C") && strcmp(locale, "POSIX")) {
errno = EINVAL;
return NULL;
}
return g_C_LOCALE_SETTING;
}
locale_t uselocale(locale_t loc) {
// If 'loc' is LC_GLOBAL_LOCALE, should return the global locale set
// through setlocale(). Since the implementation above doesn't modify
// anything, just return LC_GLOBAL_LOCALE too.
// If 'loc' is (locale_t)0, should return either LC_GLOBAL_LOCALE or
// or the global locale if setlocale() has been called at least once.
// Should return the previous value from a previous call, of
// LC_GLOBAL_LOCALE.
// So, in all cases, return LC_GLOBAL_LOCALE
return LC_GLOBAL_LOCALE;
}
void freelocale(locale_t loc) {
if (loc != LC_NULL_LOCALE && loc != LC_GLOBAL_LOCALE)
free(loc);
}
static struct lconv g_C_LCONV[1] = { {
.decimal_point = ".",
.thousands_sep = "",
.grouping = "",
.int_curr_symbol = "",
.currency_symbol = "",
.mon_decimal_point = "",
.mon_thousands_sep = "",
.mon_grouping = "",
.positive_sign = "",
.negative_sign = "",
.int_frac_digits = CHAR_MAX,
.frac_digits = CHAR_MAX,
.p_cs_precedes = CHAR_MAX,
.p_sep_by_space = CHAR_MAX,
.n_cs_precedes = CHAR_MAX,
.n_sep_by_space = CHAR_MAX,
.p_sign_posn = CHAR_MAX,
.n_sign_posn = CHAR_MAX,
.int_p_cs_precedes = CHAR_MAX,
.int_p_sep_by_space = CHAR_MAX,
.int_n_cs_precedes = CHAR_MAX,
.int_n_sep_by_space = CHAR_MAX,
.int_p_sign_posn = CHAR_MAX,
.int_n_sign_posn = CHAR_MAX,
} };
struct lconv* localeconv(void) {
return g_C_LCONV;
}