libc++: Add locale-specific wrappers for C library functions.

This patch adds trivial stubs to implement extended C library functions
like strcoll_l() by calling the respective non-locale specific function
(e.g. strcoll() in this example).

On Android, only the C/POSIX locale is supported. It might be useful
in the future to improve the implementation by allowing it to use
the platform APIs to implement all locale-specific operations properly.

However, this requires a JavaVM handle that cannot be accessed from
generic native code (the only way to get it is really through the
JNI_OnLoad() hook when loading a shared library with Dalvik).

This can be solved by doing some sort of lazy initialization, i.e.:

  1/ At first, only support the usual C/POSIX locale.

  2/ Provide a function like init_android_vm(JavaVM* vm) that can
     be called by client code to enable better locales.

Still a lot of work, so for the future.

Change-Id: I84268edaf9c3d4ad0d4ee237e270cbe1f0b6d58f
diff --git a/sources/cxx-stl/llvm-libc++/android/llvm-libc++/Android.mk b/sources/cxx-stl/llvm-libc++/android/llvm-libc++/Android.mk
index 4e2448a..7270b10 100644
--- a/sources/cxx-stl/llvm-libc++/android/llvm-libc++/Android.mk
+++ b/sources/cxx-stl/llvm-libc++/android/llvm-libc++/Android.mk
@@ -28,6 +28,9 @@
 	utility.cpp \
 	valarray.cpp
 
+llvm_libc++_sources += \
+    support/android/locale_support.c
+
 llvm_libc++_sources := $(llvm_libc++_sources:%=src/%)
 llvm_libc++_cxxflags := -std=c++11
 
diff --git a/sources/cxx-stl/llvm-libc++/include/support/android/ctype.h b/sources/cxx-stl/llvm-libc++/include/support/android/ctype.h
index 830fb78..847a340 100644
--- a/sources/cxx-stl/llvm-libc++/include/support/android/ctype.h
+++ b/sources/cxx-stl/llvm-libc++/include/support/android/ctype.h
@@ -4,7 +4,9 @@
 #include_next <ctype.h>
 #include <xlocale.h>
 
+#ifdef __cplusplus
 extern "C" {
+#endif
 
 # define __exctype_l(name)  extern int name (int, locale_t)
 
@@ -24,6 +26,8 @@
 int tolower_l(int c, locale_t);
 int toupper_l(int c, locale_t);
 
+#ifdef __cplusplus
 }  // extern "C"
+#endif
 
 #endif  // LLVM_LIBCXX_SUPPORT_ANDROID_CTYPE_H
diff --git a/sources/cxx-stl/llvm-libc++/include/support/android/locale.h b/sources/cxx-stl/llvm-libc++/include/support/android/locale.h
index 9270560..2f98dc5 100644
--- a/sources/cxx-stl/llvm-libc++/include/support/android/locale.h
+++ b/sources/cxx-stl/llvm-libc++/include/support/android/locale.h
Binary files differ
diff --git a/sources/cxx-stl/llvm-libc++/include/support/android/math.h b/sources/cxx-stl/llvm-libc++/include/support/android/math.h
index ec5e566..1890945 100644
--- a/sources/cxx-stl/llvm-libc++/include/support/android/math.h
+++ b/sources/cxx-stl/llvm-libc++/include/support/android/math.h
@@ -3,6 +3,10 @@
 
 #include_next <math.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 // TODO(digit): Check that this is not needed for Clang.
 typedef double      double_t;
 typedef double      float_t;
@@ -56,4 +60,8 @@
 float           log2f(float);
 double          log2(double);
 
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
 #endif  /* LLVM_LIBCXX_SUPPORT_ANDROID_MATH_H */
diff --git a/sources/cxx-stl/llvm-libc++/include/support/android/nl_types.h b/sources/cxx-stl/llvm-libc++/include/support/android/nl_types.h
index 46248c5..3346da8 100644
--- a/sources/cxx-stl/llvm-libc++/include/support/android/nl_types.h
+++ b/sources/cxx-stl/llvm-libc++/include/support/android/nl_types.h
@@ -4,6 +4,10 @@
 #define NL_SETD 1
 #define NL_CAT_LOCALE 1
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 typedef void* nl_catd;
 typedef int nl_item;
 
@@ -11,5 +15,9 @@
 char*    catgets(nl_catd, int, int, const char*);
 int      catclose(nl_catd);
 
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
 #endif  /* LLVM_LIBCXX_SUPPORT_ANDROID_NL_TYPES_H */
 
diff --git a/sources/cxx-stl/llvm-libc++/include/support/android/stdio.h b/sources/cxx-stl/llvm-libc++/include/support/android/stdio.h
index 8a2915b..8d0b3da 100644
--- a/sources/cxx-stl/llvm-libc++/include/support/android/stdio.h
+++ b/sources/cxx-stl/llvm-libc++/include/support/android/stdio.h
@@ -10,9 +10,11 @@
 #include <stdarg.h>
 #include <xlocale.h>
 
+#ifdef __cplusplus
 extern "C" {
+#endif
 
-char* asprintf_l(char**, locale_t, const char*, ...);
+int asprintf_l(char**, locale_t, const char*, ...);
 int sprintf_l(char*, locale_t, const char*, ...);
 int snprintf_l(char*, size_t, locale_t, const char*, ...);
 int sscanf_l(const char*, locale_t, const char*, ...);
@@ -21,6 +23,8 @@
 int vswscanf(const wchar_t *, const wchar_t *, va_list);
 int vwscanf(const wchar_t *, va_list);
 
+#ifdef __cplusplus
 }  // extern "C"
+#endif
 
 #endif  // LLVM_LIBCXX_SUPPORT_ANDROID_STDIO_H
diff --git a/sources/cxx-stl/llvm-libc++/include/support/android/stdlib.h b/sources/cxx-stl/llvm-libc++/include/support/android/stdlib.h
index bb01582..3ce85dd 100644
--- a/sources/cxx-stl/llvm-libc++/include/support/android/stdlib.h
+++ b/sources/cxx-stl/llvm-libc++/include/support/android/stdlib.h
@@ -4,7 +4,9 @@
 #include_next <stdlib.h>
 #include <xlocale.h>
 
+#ifdef __cplusplus
 extern "C" {
+#endif
 
 long long   strtoll(const char*, char**, int);
 long double strtold(const char*, char**);
@@ -16,6 +18,8 @@
 unsigned long long   strtoull_l(const char *nptr, char **endptr, int base, locale_t loc);
 long double          strtold_l (const char *nptr, char **endptr, locale_t loc);
 
+#ifdef __cplusplus
 }  // extern "C"
+#endif
 
 #endif  // LLVM_LIBCXX_SUPPORT_ANDROID_STDLIB_H
diff --git a/sources/cxx-stl/llvm-libc++/include/support/android/string.h b/sources/cxx-stl/llvm-libc++/include/support/android/string.h
index d6b8500..b78b494 100644
--- a/sources/cxx-stl/llvm-libc++/include/support/android/string.h
+++ b/sources/cxx-stl/llvm-libc++/include/support/android/string.h
@@ -4,11 +4,15 @@
 #include_next <string.h>
 #include <xlocale.h>
 
+#ifdef __cplusplus
 extern "C" {
+#endif
 
 int strcoll_l(const char*, const char*, locale_t);
 int strxfrm_l(char*, const char*, size_t, locale_t);
 
+#ifdef __cplusplus
 }  // extern "C"
+#endif
 
 #endif  // LLVM_LIBCXX_SUPPORT_ANDROID_STRING_H
diff --git a/sources/cxx-stl/llvm-libc++/include/support/android/time.h b/sources/cxx-stl/llvm-libc++/include/support/android/time.h
index 2715b3e..3706af8 100644
--- a/sources/cxx-stl/llvm-libc++/include/support/android/time.h
+++ b/sources/cxx-stl/llvm-libc++/include/support/android/time.h
@@ -4,11 +4,15 @@
 #include_next <time.h>
 #include <xlocale.h>
 
+#ifdef __cplusplus
 extern "C" {
+#endif
 
 size_t strftime_l(char *s, size_t maxsize, const char *format,
                   const struct tm * timeptr, locale_t locale);
 
+#ifdef __cplusplus
 }  // extern "C"
+#endif
 
 #endif  // LLVM_LIBCXX_SUPPORT_ANDROID_TIME_H
diff --git a/sources/cxx-stl/llvm-libc++/include/support/android/wchar.h b/sources/cxx-stl/llvm-libc++/include/support/android/wchar.h
index a438d05..f80f592 100644
--- a/sources/cxx-stl/llvm-libc++/include/support/android/wchar.h
+++ b/sources/cxx-stl/llvm-libc++/include/support/android/wchar.h
@@ -4,7 +4,9 @@
 #include_next <wchar.h>
 #include <xlocale.h>
 
+#ifdef __cplusplus
 extern "C" {
+#endif
 
 // Add missing declarations that are not in the NDK.
 float               wcstof(const wchar_t*, wchar_t**);
@@ -25,6 +27,8 @@
 int wcscoll_l(const wchar_t*, const wchar_t*, locale_t);
 int wcsxfrm_l(wchar_t*, const wchar_t*, size_t, locale_t);
 
+#ifdef __cplusplus
 }  // extern "C"
+#endif
 
 #endif  // LLVM_LIBCXX_SUPPORT_ANDROID_WCHAR_H
diff --git a/sources/cxx-stl/llvm-libc++/include/support/android/wctype.h b/sources/cxx-stl/llvm-libc++/include/support/android/wctype.h
index 7dd272f..4291f8a 100644
--- a/sources/cxx-stl/llvm-libc++/include/support/android/wctype.h
+++ b/sources/cxx-stl/llvm-libc++/include/support/android/wctype.h
@@ -4,7 +4,9 @@
 #include_next <wctype.h>
 #include <xlocale.h>
 
+#ifdef __cplusplus
 extern "C" {
+#endif
 
 // Add missing declarations from the NDK header. Implemented under
 // src/android/wctype.cc
@@ -25,6 +27,8 @@
 wint_t towlower_l(wint_t, locale_t);
 wint_t towupper_l(wint_t, locale_t);
 
+#ifdef __cplusplus
 }  // extern "C"
+#endif
 
 #endif  // LLVM_LIBCXX_SUPPORT_ANDROID_WCTYPES_H
diff --git a/sources/cxx-stl/llvm-libc++/include/support/android/xlocale.h b/sources/cxx-stl/llvm-libc++/include/support/android/xlocale.h
index 23e2def..c391c57 100644
--- a/sources/cxx-stl/llvm-libc++/include/support/android/xlocale.h
+++ b/sources/cxx-stl/llvm-libc++/include/support/android/xlocale.h
@@ -1,7 +1,9 @@
 #ifndef LLVM_LIBCXX_SUPPORT_ANDROID_XLOCALE_H
 #define LLVM_LIBCXX_SUPPORT_ANDROID_XLOCALE_H
 
+#ifdef __cplusplus
 extern "C" {
+#endif
 
 typedef struct locale_struct*  locale_t;
 
@@ -9,6 +11,8 @@
     void* dummy;
 };
 
+#ifdef __cplusplus
 }  // extern "C"
+#endif
 
 #endif  // LLVM_LIBCXX_SUPPORT_ANDROID_XLOCALE_H
diff --git a/sources/cxx-stl/llvm-libc++/src/support/android/locale_support.c b/sources/cxx-stl/llvm-libc++/src/support/android/locale_support.c
new file mode 100644
index 0000000..7bad7ec
--- /dev/null
+++ b/sources/cxx-stl/llvm-libc++/src/support/android/locale_support.c
@@ -0,0 +1,280 @@
+// -*- 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)
+
+///////////////////////////////////////////////////////////////////////
+// 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;
+}