Merge "Update to kernel headers v4.12.3."
diff --git a/libc/Android.bp b/libc/Android.bp
index d9b8a2b..701a1b0 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -1446,6 +1446,7 @@
         "bionic/getpriority.cpp",
         "bionic/gettid.cpp",
         "bionic/grp_pwd.cpp",
+        "bionic/icu_wrappers.cpp",
         "bionic/ifaddrs.cpp",
         "bionic/inotify_init.cpp",
         "bionic/ioctl.cpp",
@@ -1553,6 +1554,7 @@
         "bionic/wchar_l.cpp",
         "bionic/wcstod.cpp",
         "bionic/wctype.cpp",
+        "bionic/wcwidth.cpp",
         "bionic/wmempcpy.cpp",
     ],
 
@@ -1948,6 +1950,8 @@
 
     no_default_compiler_flags: true,
 
+    cflags: ["-Wno-gcc-compat", "-Werror"],
+
     arch: {
         arm: {
             local_include_dirs: ["arch-arm/include"],
@@ -1974,6 +1978,8 @@
 cc_defaults {
     name: "crt_so_defaults",
 
+    cflags: ["-Wno-gcc-compat", "-Werror"],
+
     vendor_available: true,
     arch: {
         mips: {
diff --git a/libc/bionic/grp_pwd.cpp b/libc/bionic/grp_pwd.cpp
index 078a0b3..2823662 100644
--- a/libc/bionic/grp_pwd.cpp
+++ b/libc/bionic/grp_pwd.cpp
@@ -467,7 +467,16 @@
 
 char* getlogin() { // NOLINT: implementing bad function.
   passwd *pw = getpwuid(getuid()); // NOLINT: implementing bad function in terms of bad function.
-  return (pw != NULL) ? pw->pw_name : NULL;
+  return pw ? pw->pw_name : nullptr;
+}
+
+int getlogin_r(char* buf, size_t size) {
+  char* login = getlogin();
+  if (login == nullptr) return errno;
+  size_t login_length = strlen(login) + 1;
+  if (login_length > size) return ERANGE;
+  memcpy(buf, login, login_length);
+  return 0;
 }
 
 void setpwent() {
diff --git a/libc/bionic/icu_wrappers.cpp b/libc/bionic/icu_wrappers.cpp
new file mode 100644
index 0000000..d9f2745
--- /dev/null
+++ b/libc/bionic/icu_wrappers.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "private/icu.h"
+
+int8_t __icu_charType(wint_t wc) {
+  typedef int8_t (*u_charType_t)(UChar32);
+  static auto u_charType = reinterpret_cast<u_charType_t>(__find_icu_symbol("u_charType"));
+  return u_charType ? u_charType(wc) : -1;
+}
+
+int32_t __icu_getIntPropertyValue(wint_t wc, UProperty property) {
+  typedef int32_t (*u_getIntPropertyValue_t)(UChar32, UProperty);
+  static auto u_getIntPropertyValue =
+      reinterpret_cast<u_getIntPropertyValue_t>(__find_icu_symbol("u_getIntPropertyValue"));
+  return u_getIntPropertyValue ? u_getIntPropertyValue(wc, property) : 0;
+}
+
+bool __icu_hasBinaryProperty(wint_t wc, UProperty property, int (*fallback)(int)) {
+  typedef UBool (*u_hasBinaryProperty_t)(UChar32, UProperty);
+  static auto u_hasBinaryProperty =
+      reinterpret_cast<u_hasBinaryProperty_t>(__find_icu_symbol("u_hasBinaryProperty"));
+  return u_hasBinaryProperty ? u_hasBinaryProperty(wc, property) : fallback(wc);
+}
diff --git a/libc/bionic/sysconf.cpp b/libc/bionic/sysconf.cpp
index 4a23fec..7325dbd 100644
--- a/libc/bionic/sysconf.cpp
+++ b/libc/bionic/sysconf.cpp
@@ -135,12 +135,12 @@
     case _SC_TIMERS:            return _POSIX_TIMERS;
     case _SC_GETGR_R_SIZE_MAX:  return 1024;
     case _SC_GETPW_R_SIZE_MAX:  return 1024;
-    case _SC_LOGIN_NAME_MAX:    return 256;   // Seems default on linux.
+    case _SC_LOGIN_NAME_MAX:    return LOGIN_NAME_MAX;
     case _SC_THREAD_DESTRUCTOR_ITERATIONS: return PTHREAD_DESTRUCTOR_ITERATIONS;
     case _SC_THREAD_KEYS_MAX:   return PTHREAD_KEYS_MAX;
     case _SC_THREAD_STACK_MIN:    return PTHREAD_STACK_MIN;
     case _SC_THREAD_THREADS_MAX:  return -1; // No specific limit.
-    case _SC_TTY_NAME_MAX:        return 32; // Seems default on linux.
+    case _SC_TTY_NAME_MAX:        return TTY_NAME_MAX;
     case _SC_THREADS:             return _POSIX_THREADS;
     case _SC_THREAD_ATTR_STACKADDR:   return _POSIX_THREAD_ATTR_STACKADDR;
     case _SC_THREAD_ATTR_STACKSIZE:   return _POSIX_THREAD_ATTR_STACKSIZE;
diff --git a/libc/bionic/wctype.cpp b/libc/bionic/wctype.cpp
index 8e2acef..05b6e19 100644
--- a/libc/bionic/wctype.cpp
+++ b/libc/bionic/wctype.cpp
@@ -53,12 +53,6 @@
   WC_TYPE_MAX
 };
 
-static bool __icu_hasBinaryProperty(wint_t wc, UProperty property, int (*fallback)(int)) {
-  typedef UBool (*FnT)(UChar32, UProperty);
-  static auto u_hasBinaryProperty = reinterpret_cast<FnT>(__find_icu_symbol("u_hasBinaryProperty"));
-  return u_hasBinaryProperty ? u_hasBinaryProperty(wc, property) : fallback(wc);
-}
-
 int iswalnum(wint_t wc) { return __icu_hasBinaryProperty(wc, UCHAR_POSIX_ALNUM, isalnum); }
 int iswalpha(wint_t wc) { return __icu_hasBinaryProperty(wc, UCHAR_ALPHABETIC, isalpha); }
 int iswblank(wint_t wc) { return __icu_hasBinaryProperty(wc, UCHAR_POSIX_BLANK, isblank); }
@@ -155,10 +149,6 @@
   return wctype(property);
 }
 
-int wcwidth(wchar_t wc) {
-  return (wc > 0);
-}
-
 static wctrans_t wctrans_tolower = wctrans_t(1);
 static wctrans_t wctrans_toupper = wctrans_t(2);
 
diff --git a/libc/bionic/wcwidth.cpp b/libc/bionic/wcwidth.cpp
new file mode 100644
index 0000000..9676b5a
--- /dev/null
+++ b/libc/bionic/wcwidth.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <wchar.h>
+
+#include "private/icu.h"
+
+int wcwidth(wchar_t wc) {
+  // Fast-path ASCII.
+  if (wc >= 0x20 && wc < 0x7f) return 1;
+
+  // ASCII NUL is a special case.
+  if (wc == 0) return 0;
+
+  // C0.
+  if (wc < ' ' || (wc >= 0x7f && wc <= 0xa0)) return -1;
+
+  // Now for the i18n part. This isn't defined or standardized, so a lot of the choices are
+  // pretty arbitrary. See https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c for more details.
+
+  // Fancy unicode control characters?
+  switch (__icu_charType(wc)) {
+   case -1:
+    // No icu4c available; give up.
+    return -1;
+   case U_CONTROL_CHAR:
+    return -1;
+   case U_NON_SPACING_MARK:
+   case U_ENCLOSING_MARK:
+   case U_FORMAT_CHAR:
+    return 0;
+  }
+  if (__icu_hasBinaryProperty(wc, UCHAR_DEFAULT_IGNORABLE_CODE_POINT, nullptr)) return 0;
+
+  // Medial and final jamo render as zero width when used correctly.
+  switch (__icu_getIntPropertyValue(wc, UCHAR_HANGUL_SYLLABLE_TYPE)) {
+   case U_HST_VOWEL_JAMO:
+   case U_HST_TRAILING_JAMO:
+    return 0;
+   case U_HST_LEADING_JAMO:
+   case U_HST_LV_SYLLABLE:
+   case U_HST_LVT_SYLLABLE:
+    return 2;
+  }
+
+  if (wc >= 0x3248 && wc <= 0x4dff) {
+    // Circled two-digit CJK "speed sign" numbers. EastAsianWidth is ambiguous,
+    // but wide makes more sense.
+    if (wc <= 0x324f) return 2;
+    // Hexagrams. EastAsianWidth is neutral, but wide seems better.
+    if (wc >= 0x4dc0) return 2;
+  }
+
+  // The EastAsianWidth property is at least defined by the Unicode standard!
+  switch (__icu_getIntPropertyValue(wc, UCHAR_EAST_ASIAN_WIDTH)) {
+   case U_EA_AMBIGUOUS:
+   case U_EA_HALFWIDTH:
+   case U_EA_NARROW:
+   case U_EA_NEUTRAL:
+    return 1;
+   case U_EA_FULLWIDTH:
+   case U_EA_WIDE:
+    return 2;
+  }
+
+  return 0;
+}
diff --git a/libc/include/android/api-level.h b/libc/include/android/api-level.h
index c83c18d..7e3aa99 100644
--- a/libc/include/android/api-level.h
+++ b/libc/include/android/api-level.h
@@ -41,6 +41,8 @@
 
 #ifndef __ANDROID_API__
 #define __ANDROID_API__ __ANDROID_API_FUTURE__
+#else
+#define __ANDROID_NDK__ 1
 #endif
 
 #define __ANDROID_API_G__ 9
diff --git a/libc/include/android/legacy_stdlib_inlines.h b/libc/include/android/legacy_stdlib_inlines.h
index 82186c7..e26e5f2 100644
--- a/libc/include/android/legacy_stdlib_inlines.h
+++ b/libc/include/android/legacy_stdlib_inlines.h
@@ -29,6 +29,8 @@
 #ifndef _ANDROID_LEGACY_STDLIB_INLINES_H_
 #define _ANDROID_LEGACY_STDLIB_INLINES_H_
 
+#include <errno.h>
+#include <float.h>
 #include <stdlib.h>
 #include <sys/cdefs.h>
 
@@ -36,10 +38,16 @@
 
 __BEGIN_DECLS
 
-__noreturn void _Exit(int) __RENAME(_exit);
-
-static __inline float strtof(const char *nptr, char **endptr) {
-  return (float)strtod(nptr, endptr);
+static __inline float strtof(const char* nptr, char** endptr) {
+  double d = strtod(nptr, endptr);
+  if (d > FLT_MAX) {
+    errno = ERANGE;
+    return __builtin_huge_valf();
+  } else if (d < -FLT_MAX) {
+    errno = ERANGE;
+    return -__builtin_huge_valf();
+  }
+  return __BIONIC_CAST(static_cast, float, d);
 }
 
 static __inline double atof(const char *nptr) { return (strtod(nptr, NULL)); }
diff --git a/libc/include/limits.h b/libc/include/limits.h
index 157f7a6..51f4fad 100644
--- a/libc/include/limits.h
+++ b/libc/include/limits.h
@@ -137,6 +137,8 @@
 #include <bits/posix_limits.h>
 
 #define HOST_NAME_MAX _POSIX_HOST_NAME_MAX
+#define LOGIN_NAME_MAX 256
+#define TTY_NAME_MAX 32
 
 #define _POSIX_VERSION 200809L
 #define _POSIX2_VERSION _POSIX_VERSION
diff --git a/libc/include/unistd.h b/libc/include/unistd.h
index e024527..8336976 100644
--- a/libc/include/unistd.h
+++ b/libc/include/unistd.h
@@ -118,6 +118,7 @@
 int getresuid(uid_t* __ruid, uid_t* __euid, uid_t* __suid);
 int getresgid(gid_t* __rgid, gid_t* __egid, gid_t* __sgid);
 char* getlogin(void);
+int getlogin_r(char* __buffer, size_t __buffer_size) __INTRODUCED_IN_FUTURE;
 
 long fpathconf(int __fd, int __name);
 long pathconf(const char* __path, int __name);
diff --git a/libc/include/wchar.h b/libc/include/wchar.h
index c9a78be..a86c8a9 100644
--- a/libc/include/wchar.h
+++ b/libc/include/wchar.h
@@ -42,6 +42,11 @@
 
 __BEGIN_DECLS
 
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnullability-completeness"
+#endif
+
 wint_t            btowc(int);
 int               fwprintf(FILE *, const wchar_t *, ...);
 int               fwscanf(FILE *, const wchar_t *, ...);
@@ -134,6 +139,10 @@
 wchar_t* wcsdup(const wchar_t*);
 size_t wcsnlen(const wchar_t*, size_t);
 
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
 __END_DECLS
 
 #endif /* _WCHAR_H_ */
diff --git a/libc/libc.arm.map b/libc/libc.arm.map
index a4212dd..45ca9eb 100644
--- a/libc/libc.arm.map
+++ b/libc/libc.arm.map
@@ -1316,6 +1316,11 @@
     wctrans_l; # introduced=26
 } LIBC_N;
 
+LIBC_P {
+  global:
+    getlogin_r; # future
+} LIBC_O;
+
 LIBC_PRIVATE {
   global:
     ___Unwind_Backtrace; # arm
@@ -1532,7 +1537,7 @@
     vfdprintf; # arm x86 mips
     wait3; # arm x86 mips
     wcswcs; # arm x86 mips
-} LIBC_O;
+} LIBC_P;
 
 LIBC_DEPRECATED {
   global:
@@ -1554,4 +1559,4 @@
     malloc_disable;
     malloc_enable;
     malloc_iterate;
-} LIBC_O;
+} LIBC_P;
diff --git a/libc/libc.arm64.map b/libc/libc.arm64.map
index bf0341a..ed1e82c 100644
--- a/libc/libc.arm64.map
+++ b/libc/libc.arm64.map
@@ -1238,6 +1238,11 @@
     wctrans_l; # introduced=26
 } LIBC_N;
 
+LIBC_P {
+  global:
+    getlogin_r; # future
+} LIBC_O;
+
 LIBC_PRIVATE {
   global:
     android_getaddrinfofornet;
@@ -1249,7 +1254,7 @@
     free_malloc_leak_info;
     get_malloc_leak_info;
     gMallocLeakZygoteChild;
-} LIBC_O;
+} LIBC_P;
 
 LIBC_DEPRECATED {
   global:
@@ -1271,4 +1276,4 @@
     malloc_disable;
     malloc_enable;
     malloc_iterate;
-} LIBC_O;
+} LIBC_P;
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index c271a57..8b1d6de 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -1341,6 +1341,11 @@
     wctrans_l; # introduced=26
 } LIBC_N;
 
+LIBC_P {
+  global:
+    getlogin_r; # future
+} LIBC_O;
+
 LIBC_PRIVATE {
   global:
     ___Unwind_Backtrace; # arm
@@ -1558,7 +1563,7 @@
     vfdprintf; # arm x86 mips
     wait3; # arm x86 mips
     wcswcs; # arm x86 mips
-} LIBC_O;
+} LIBC_P;
 
 LIBC_DEPRECATED {
   global:
@@ -1580,4 +1585,4 @@
     malloc_disable;
     malloc_enable;
     malloc_iterate;
-} LIBC_O;
+} LIBC_P;
diff --git a/libc/libc.mips.map b/libc/libc.mips.map
index 214c7f5..256ca9e 100644
--- a/libc/libc.mips.map
+++ b/libc/libc.mips.map
@@ -1300,6 +1300,11 @@
     wctrans_l; # introduced=26
 } LIBC_N;
 
+LIBC_P {
+  global:
+    getlogin_r; # future
+} LIBC_O;
+
 LIBC_PRIVATE {
   global:
     __accept4; # arm x86 mips
@@ -1373,7 +1378,7 @@
     vfdprintf; # arm x86 mips
     wait3; # arm x86 mips
     wcswcs; # arm x86 mips
-} LIBC_O;
+} LIBC_P;
 
 LIBC_DEPRECATED {
   global:
@@ -1395,4 +1400,4 @@
     malloc_disable;
     malloc_enable;
     malloc_iterate;
-} LIBC_O;
+} LIBC_P;
diff --git a/libc/libc.mips64.map b/libc/libc.mips64.map
index bf0341a..ed1e82c 100644
--- a/libc/libc.mips64.map
+++ b/libc/libc.mips64.map
@@ -1238,6 +1238,11 @@
     wctrans_l; # introduced=26
 } LIBC_N;
 
+LIBC_P {
+  global:
+    getlogin_r; # future
+} LIBC_O;
+
 LIBC_PRIVATE {
   global:
     android_getaddrinfofornet;
@@ -1249,7 +1254,7 @@
     free_malloc_leak_info;
     get_malloc_leak_info;
     gMallocLeakZygoteChild;
-} LIBC_O;
+} LIBC_P;
 
 LIBC_DEPRECATED {
   global:
@@ -1271,4 +1276,4 @@
     malloc_disable;
     malloc_enable;
     malloc_iterate;
-} LIBC_O;
+} LIBC_P;
diff --git a/libc/libc.x86.map b/libc/libc.x86.map
index 145b64e..2ebc86c 100644
--- a/libc/libc.x86.map
+++ b/libc/libc.x86.map
@@ -1298,6 +1298,11 @@
     wctrans_l; # introduced=26
 } LIBC_N;
 
+LIBC_P {
+  global:
+    getlogin_r; # future
+} LIBC_O;
+
 LIBC_PRIVATE {
   global:
     __accept4; # arm x86 mips
@@ -1372,7 +1377,7 @@
     vfdprintf; # arm x86 mips
     wait3; # arm x86 mips
     wcswcs; # arm x86 mips
-} LIBC_O;
+} LIBC_P;
 
 LIBC_DEPRECATED {
   global:
@@ -1394,4 +1399,4 @@
     malloc_disable;
     malloc_enable;
     malloc_iterate;
-} LIBC_O;
+} LIBC_P;
diff --git a/libc/libc.x86_64.map b/libc/libc.x86_64.map
index bf0341a..ed1e82c 100644
--- a/libc/libc.x86_64.map
+++ b/libc/libc.x86_64.map
@@ -1238,6 +1238,11 @@
     wctrans_l; # introduced=26
 } LIBC_N;
 
+LIBC_P {
+  global:
+    getlogin_r; # future
+} LIBC_O;
+
 LIBC_PRIVATE {
   global:
     android_getaddrinfofornet;
@@ -1249,7 +1254,7 @@
     free_malloc_leak_info;
     get_malloc_leak_info;
     gMallocLeakZygoteChild;
-} LIBC_O;
+} LIBC_P;
 
 LIBC_DEPRECATED {
   global:
@@ -1271,4 +1276,4 @@
     malloc_disable;
     malloc_enable;
     malloc_iterate;
-} LIBC_O;
+} LIBC_P;
diff --git a/libc/private/icu.h b/libc/private/icu.h
index 03fdf66..ae253fa 100644
--- a/libc/private/icu.h
+++ b/libc/private/icu.h
@@ -30,12 +30,14 @@
 #define _PRIVATE_ICU_H
 
 #include <stdint.h>
+#include <wchar.h>
 
 typedef int8_t UBool;
 typedef int32_t UChar32;
 
 enum UProperty {
   UCHAR_ALPHABETIC = 0,
+  UCHAR_DEFAULT_IGNORABLE_CODE_POINT = 5,
   UCHAR_LOWERCASE = 22,
   UCHAR_POSIX_ALNUM = 44,
   UCHAR_POSIX_BLANK = 45,
@@ -44,12 +46,39 @@
   UCHAR_POSIX_XDIGIT = 48,
   UCHAR_UPPERCASE = 30,
   UCHAR_WHITE_SPACE = 31,
+  UCHAR_EAST_ASIAN_WIDTH = 0x1004,
+  UCHAR_HANGUL_SYLLABLE_TYPE = 0x100b,
 };
 
 enum UCharCategory {
+  U_NON_SPACING_MARK = 6,
+  U_ENCLOSING_MARK = 7,
   U_CONTROL_CHAR = 15,
+  U_FORMAT_CHAR = 16,
 };
 
+enum UEastAsianWidth {
+  U_EA_NEUTRAL,
+  U_EA_AMBIGUOUS,
+  U_EA_HALFWIDTH,
+  U_EA_FULLWIDTH,
+  U_EA_NARROW,
+  U_EA_WIDE,
+};
+
+enum UHangulSyllableType {
+  U_HST_NOT_APPLICABLE,
+  U_HST_LEADING_JAMO,
+  U_HST_VOWEL_JAMO,
+  U_HST_TRAILING_JAMO,
+  U_HST_LV_SYLLABLE,
+  U_HST_LVT_SYLLABLE,
+};
+
+int8_t __icu_charType(wint_t wc);
+int32_t __icu_getIntPropertyValue(wint_t wc, UProperty property);
+bool __icu_hasBinaryProperty(wint_t wc, UProperty property, int (*fallback)(int));
+
 void* __find_icu_symbol(const char* symbol_name);
 
 #endif  // _PRIVATE_ICU_H
diff --git a/libc/stdio/local.h b/libc/stdio/local.h
index d8b1a57..afb02ca 100644
--- a/libc/stdio/local.h
+++ b/libc/stdio/local.h
@@ -134,7 +134,7 @@
 #define __SEOF 0x0020  // Found EOF.
 #define __SERR 0x0040  // Found error.
 #define __SMBF 0x0080  // `_buf` is from malloc.
-#define __SAPP 0x0100  // fdopen()ed in append mode.
+// #define __SAPP 0x0100 --- historical (fdopen()ed in append mode).
 #define __SSTR 0x0200  // This is an sprintf/snprintf string.
 // #define __SOPT 0x0400 --- historical (do fseek() optimization).
 // #define __SNPT 0x0800 --- historical (do not do fseek() optimization).
diff --git a/libc/stdio/stdio.cpp b/libc/stdio/stdio.cpp
index cd4be2e..4d6438b 100644
--- a/libc/stdio/stdio.cpp
+++ b/libc/stdio/stdio.cpp
@@ -68,14 +68,8 @@
 
 _THREAD_PRIVATE_MUTEX(__sfp_mutex);
 
-// TODO: when we no longer have to support both clang and GCC, we can simplify all this.
-#define SBUF_INIT {0,0}
-#if defined(__LP64__)
-#define MBSTATE_T_INIT {{0},{0}}
-#else
-#define MBSTATE_T_INIT {{0}}
-#endif
-#define WCHAR_IO_DATA_INIT {MBSTATE_T_INIT,MBSTATE_T_INIT,{0},0,0}
+#define SBUF_INIT {}
+#define WCHAR_IO_DATA_INIT {}
 
 static struct __sfileext __sFext[3] = {
   { SBUF_INIT, WCHAR_IO_DATA_INIT, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP, false, __sseek64 },
@@ -212,11 +206,11 @@
 }
 
 FILE* fopen(const char* file, const char* mode) {
-  int oflags;
-  int flags = __sflags(mode, &oflags);
+  int mode_flags;
+  int flags = __sflags(mode, &mode_flags);
   if (flags == 0) return nullptr;
 
-  int fd = open(file, oflags, DEFFILEMODE);
+  int fd = open(file, mode_flags, DEFFILEMODE);
   if (fd == -1) {
     return nullptr;
   }
@@ -228,41 +222,34 @@
     return nullptr;
   }
 
-  // When opening in append mode, even though we use O_APPEND,
-  // we need to seek to the end so that ftell() gets the right
-  // answer.  If the user then alters the seek pointer, or
-  // the file extends, this will fail, but there is not much
-  // we can do about this.  (We could set __SAPP and check in
-  // fseek and ftell.)
-  // TODO: check in __sseek instead.
-  if (oflags & O_APPEND) __sseek64(fp, 0, SEEK_END);
-
+  // For append mode, even though we use O_APPEND, we need to seek to the end now.
+  if ((mode_flags & O_APPEND) != 0) __sseek64(fp, 0, SEEK_END);
   return fp;
 }
 __strong_alias(fopen64, fopen);
 
 FILE* fdopen(int fd, const char* mode) {
-  int oflags;
-  int flags = __sflags(mode, &oflags);
+  int mode_flags;
+  int flags = __sflags(mode, &mode_flags);
   if (flags == 0) return nullptr;
 
   // Make sure the mode the user wants is a subset of the actual mode.
-  int fdflags = fcntl(fd, F_GETFL, 0);
-  if (fdflags < 0) return nullptr;
-  int tmp = fdflags & O_ACCMODE;
-  if (tmp != O_RDWR && (tmp != (oflags & O_ACCMODE))) {
+  int fd_flags = fcntl(fd, F_GETFL, 0);
+  if (fd_flags == -1) return nullptr;
+  int tmp = fd_flags & O_ACCMODE;
+  if (tmp != O_RDWR && (tmp != (mode_flags & O_ACCMODE))) {
     errno = EINVAL;
     return nullptr;
   }
 
-  // If opened for appending, but underlying descriptor does not have
-  // O_APPEND bit set, assert __SAPP so that __swrite() will lseek to
-  // end before each write.
-  // TODO: use fcntl(2) to set O_APPEND instead.
-  if ((oflags & O_APPEND) && !(fdflags & O_APPEND)) flags |= __SAPP;
+  // Make sure O_APPEND is set on the underlying fd if our mode has 'a'.
+  // POSIX says we just take the current offset of the underlying fd.
+  if ((mode_flags & O_APPEND) && !(fd_flags & O_APPEND)) {
+    if (fcntl(fd, F_SETFL, fd_flags | O_APPEND) == -1) return nullptr;
+  }
 
-  // If close-on-exec was requested, then turn it on if not already.
-  if ((oflags & O_CLOEXEC) && !((tmp = fcntl(fd, F_GETFD)) & FD_CLOEXEC)) {
+  // Make sure O_CLOEXEC is set on the underlying fd if our mode has 'x'.
+  if ((mode_flags & O_CLOEXEC) && !((tmp = fcntl(fd, F_GETFD)) & FD_CLOEXEC)) {
     fcntl(fd, F_SETFD, tmp | FD_CLOEXEC);
   }
 
@@ -274,8 +261,8 @@
 // all possible, no matter what.
 // TODO: rewrite this mess completely.
 FILE* freopen(const char* file, const char* mode, FILE* fp) {
-  int oflags;
-  int flags = __sflags(mode, &oflags);
+  int mode_flags;
+  int flags = __sflags(mode, &mode_flags);
   if (flags == 0) {
     fclose(fp);
     return nullptr;
@@ -307,13 +294,13 @@
   }
 
   // Get a new descriptor to refer to the new file.
-  int fd = open(file, oflags, DEFFILEMODE);
+  int fd = open(file, mode_flags, DEFFILEMODE);
   if (fd < 0 && isopen) {
     // If out of fd's close the old one and try again.
     if (errno == ENFILE || errno == EMFILE) {
       (*fp->_close)(fp->_cookie);
       isopen = 0;
-      fd = open(file, oflags, DEFFILEMODE);
+      fd = open(file, mode_flags, DEFFILEMODE);
     }
   }
 
@@ -346,7 +333,7 @@
   // to maintain the descriptor.  Various C library routines (perror)
   // assume stderr is always fd STDERR_FILENO, even if being freopen'd.
   if (wantfd >= 0 && fd != wantfd) {
-    if (dup3(fd, wantfd, oflags & O_CLOEXEC) >= 0) {
+    if (dup3(fd, wantfd, mode_flags & O_CLOEXEC) >= 0) {
       close(fd);
       fd = wantfd;
     }
@@ -367,13 +354,8 @@
   fp->_close = __sclose;
   _EXT(fp)->_seek64 = __sseek64;
 
-  // When opening in append mode, even though we use O_APPEND,
-  // we need to seek to the end so that ftell() gets the right
-  // answer.  If the user then alters the seek pointer, or
-  // the file extends, this will fail, but there is not much
-  // we can do about this.  (We could set __SAPP and check in
-  // fseek and ftell.)
-  if (oflags & O_APPEND) __sseek64(fp, 0, SEEK_END);
+  // For append mode, even though we use O_APPEND, we need to seek to the end now.
+  if ((mode_flags & O_APPEND) != 0) __sseek64(fp, 0, SEEK_END);
   return fp;
 }
 __strong_alias(freopen64, freopen);
@@ -452,12 +434,6 @@
 
 int __swrite(void* cookie, const char* buf, int n) {
   FILE* fp = reinterpret_cast<FILE*>(cookie);
-  if (fp->_flags & __SAPP) {
-    // The FILE* is in append mode, but the underlying fd doesn't have O_APPEND set.
-    // We need to seek manually.
-    // TODO: use fcntl(2) to set O_APPEND in fdopen(3) instead?
-    TEMP_FAILURE_RETRY(lseek64(fp->_file, 0, SEEK_END));
-  }
   return TEMP_FAILURE_RETRY(write(fp->_file, buf, n));
 }
 
@@ -496,6 +472,7 @@
 static off64_t __ftello64_unlocked(FILE* fp) {
   // Find offset of underlying I/O object, then adjust for buffered bytes.
   __sflush(fp);  // May adjust seek offset on append stream.
+
   off64_t result = __seek_unlocked(fp, 0, SEEK_CUR);
   if (result == -1) {
     return -1;
diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp
index 1f27c83..dac7056 100644
--- a/tests/stdio_test.cpp
+++ b/tests/stdio_test.cpp
@@ -47,6 +47,24 @@
 class stdio_DeathTest : public BionicDeathTest {};
 class stdio_nofortify_DeathTest : public BionicDeathTest {};
 
+static void SetFileTo(const char* path, const char* content) {
+  FILE* fp;
+  ASSERT_NE(nullptr, fp = fopen(path, "w"));
+  ASSERT_NE(EOF, fputs(content, fp));
+  ASSERT_EQ(0, fclose(fp));
+}
+
+static void AssertFileIs(const char* path, const char* expected) {
+  FILE* fp;
+  ASSERT_NE(nullptr, fp = fopen(path, "r"));
+  char* line = nullptr;
+  size_t length;
+  ASSERT_NE(EOF, getline(&line, &length, fp));
+  ASSERT_EQ(0, fclose(fp));
+  ASSERT_STREQ(expected, line);
+  free(line);
+}
+
 static void AssertFileIs(FILE* fp, const char* expected, bool is_fmemopen = false) {
   rewind(fp);
 
@@ -1846,3 +1864,57 @@
   sprintf(&buf[0], "hello");
   ASSERT_EQ(buf, "hello");
 }
+
+TEST(STDIO_TEST, fopen_append_mode_and_ftell) {
+  TemporaryFile tf;
+  SetFileTo(tf.filename, "0123456789");
+  FILE* fp = fopen(tf.filename, "a");
+  EXPECT_EQ(10, ftell(fp));
+  ASSERT_EQ(0, fseek(fp, 2, SEEK_SET));
+  EXPECT_EQ(2, ftell(fp));
+  ASSERT_NE(EOF, fputs("xxx", fp));
+  ASSERT_EQ(0, fflush(fp));
+  EXPECT_EQ(13, ftell(fp));
+  ASSERT_EQ(0, fseek(fp, 0, SEEK_END));
+  EXPECT_EQ(13, ftell(fp));
+  ASSERT_EQ(0, fclose(fp));
+  AssertFileIs(tf.filename, "0123456789xxx");
+}
+
+TEST(STDIO_TEST, fdopen_append_mode_and_ftell) {
+  TemporaryFile tf;
+  SetFileTo(tf.filename, "0123456789");
+  int fd = open(tf.filename, O_RDWR);
+  ASSERT_NE(-1, fd);
+  // POSIX: "The file position indicator associated with the new stream is set to the position
+  // indicated by the file offset associated with the file descriptor."
+  ASSERT_EQ(4, lseek(fd, 4, SEEK_SET));
+  FILE* fp = fdopen(fd, "a");
+  EXPECT_EQ(4, ftell(fp));
+  ASSERT_EQ(0, fseek(fp, 2, SEEK_SET));
+  EXPECT_EQ(2, ftell(fp));
+  ASSERT_NE(EOF, fputs("xxx", fp));
+  ASSERT_EQ(0, fflush(fp));
+  EXPECT_EQ(13, ftell(fp));
+  ASSERT_EQ(0, fseek(fp, 0, SEEK_END));
+  EXPECT_EQ(13, ftell(fp));
+  ASSERT_EQ(0, fclose(fp));
+  AssertFileIs(tf.filename, "0123456789xxx");
+}
+
+TEST(STDIO_TEST, freopen_append_mode_and_ftell) {
+  TemporaryFile tf;
+  SetFileTo(tf.filename, "0123456789");
+  FILE* other_fp = fopen("/proc/version", "r");
+  FILE* fp = freopen(tf.filename, "a", other_fp);
+  EXPECT_EQ(10, ftell(fp));
+  ASSERT_EQ(0, fseek(fp, 2, SEEK_SET));
+  EXPECT_EQ(2, ftell(fp));
+  ASSERT_NE(EOF, fputs("xxx", fp));
+  ASSERT_EQ(0, fflush(fp));
+  EXPECT_EQ(13, ftell(fp));
+  ASSERT_EQ(0, fseek(fp, 0, SEEK_END));
+  EXPECT_EQ(13, ftell(fp));
+  ASSERT_EQ(0, fclose(fp));
+  AssertFileIs(tf.filename, "0123456789xxx");
+}
diff --git a/tests/unistd_test.cpp b/tests/unistd_test.cpp
index a81f112..9b811ed 100644
--- a/tests/unistd_test.cpp
+++ b/tests/unistd_test.cpp
@@ -837,6 +837,7 @@
   VERIFY_SYSCONF_UNSUPPORTED(_SC_2_FORT_DEV);
   VERIFY_SYSCONF_UNSUPPORTED(_SC_2_FORT_RUN);
   VERIFY_SYSCONF_UNSUPPORTED(_SC_2_UPE);
+  VERIFY_SYSCONF_POSIX_VERSION(_SC_2_VERSION);
   VERIFY_SYSCONF_POSITIVE(_SC_JOB_CONTROL);
   VERIFY_SYSCONF_POSITIVE(_SC_SAVED_IDS);
   VERIFY_SYSCONF_POSIX_VERSION(_SC_VERSION);
@@ -952,7 +953,6 @@
   VERIFY_SYSCONF_UNSUPPORTED(_SC_2_CHAR_TERM);
   VERIFY_SYSCONF_UNSUPPORTED(_SC_2_LOCALEDEF);
   VERIFY_SYSCONF_UNSUPPORTED(_SC_2_SW_DEV);
-  VERIFY_SYSCONF_UNSUPPORTED(_SC_2_VERSION);
 
   VERIFY_SYSCONF_UNSUPPORTED(_SC_XOPEN_CRYPT);
   VERIFY_SYSCONF_UNSUPPORTED(_SC_XOPEN_ENH_I18N);
@@ -1372,3 +1372,10 @@
   ASSERT_EXIT(execve("/system/bin/run-as", args, envs), testing::ExitedWithCode(1),
               "<unknown>: usage: run-as");
 }
+
+TEST(UNISTD_TEST, getlogin_r) {
+  char buf[LOGIN_NAME_MAX] = {};
+  EXPECT_EQ(ERANGE, getlogin_r(buf, 0));
+  EXPECT_EQ(0, getlogin_r(buf, sizeof(buf)));
+  EXPECT_STREQ(getlogin(), buf);
+}
diff --git a/tests/wchar_test.cpp b/tests/wchar_test.cpp
index 097647f..a795d2c 100644
--- a/tests/wchar_test.cpp
+++ b/tests/wchar_test.cpp
@@ -754,3 +754,80 @@
 TEST(wchar, wcstold) {
   CheckWcsToFloat(wcstold);
 }
+
+static void AssertWcwidthRange(wchar_t begin, wchar_t end, int expected) {
+  for (wchar_t i = begin; i < end; ++i) {
+    EXPECT_EQ(expected, wcwidth(i)) << static_cast<int>(i);
+  }
+}
+
+TEST(wchar, wcwidth_NUL) {
+  // NUL is defined to return 0 rather than -1, despite being a C0 control.
+  EXPECT_EQ(0, wcwidth(0));
+}
+
+TEST(wchar, wcwidth_ascii) {
+  AssertWcwidthRange(0x20, 0x7f, 1); // Non-C0 non-DEL ASCII.
+}
+
+TEST(wchar, wcwidth_controls) {
+  AssertWcwidthRange(0x01, 0x20, -1); // C0 controls.
+  EXPECT_EQ(-1, wcwidth(0x7f)); // DEL.
+  AssertWcwidthRange(0x80, 0xa0, -1); // C1 controls.
+}
+
+TEST(wchar, wcwidth_non_spacing_and_enclosing_marks_and_format) {
+  EXPECT_EQ(0, wcwidth(0x0300)); // Combining grave.
+  EXPECT_EQ(0, wcwidth(0x20dd)); // Combining enclosing circle.
+  EXPECT_EQ(0, wcwidth(0x00ad)); // Soft hyphen (SHY).
+  EXPECT_EQ(0, wcwidth(0x200b)); // Zero width space.
+}
+
+TEST(wchar, wcwidth_cjk) {
+  EXPECT_EQ(2, wcwidth(0x4e00)); // Start of CJK unified block.
+  EXPECT_EQ(2, wcwidth(0x9fff)); // End of CJK unified block.
+  EXPECT_EQ(2, wcwidth(0x3400)); // Start of CJK extension A block.
+  EXPECT_EQ(2, wcwidth(0x4dbf)); // End of CJK extension A block.
+  EXPECT_EQ(2, wcwidth(0x20000)); // Start of CJK extension B block.
+  EXPECT_EQ(2, wcwidth(0x2a6df)); // End of CJK extension B block.
+}
+
+TEST(wchar, wcwidth_korean_combining_jamo) {
+  AssertWcwidthRange(0x1160, 0x1200, 0); // Original range.
+  EXPECT_EQ(0, wcwidth(0xd7b0)); // Newer.
+  EXPECT_EQ(0, wcwidth(0xd7cb));
+}
+
+TEST(wchar, wcwidth_korean_jeongeul_syllables) {
+  EXPECT_EQ(2, wcwidth(0xac00)); // Start of block.
+  EXPECT_EQ(2, wcwidth(0xd7a3)); // End of defined code points in Unicode 7.
+  // Undefined characters at the end of the block have width 1.
+}
+
+TEST(wchar, wcwidth_kana) {
+  // Hiragana (most, not undefined).
+  AssertWcwidthRange(0x3041, 0x3097, 2);
+  // Katakana.
+  AssertWcwidthRange(0x30a0, 0x3100, 2);
+}
+
+TEST(wchar, wcwidth_circled_two_digit_cjk) {
+  // Circled two-digit CJK "speed sign" numbers are wide,
+  // though EastAsianWidth is ambiguous.
+  AssertWcwidthRange(0x3248, 0x3250, 2);
+}
+
+TEST(wchar, wcwidth_hexagrams) {
+  // Hexagrams are wide, though EastAsianWidth is neutral.
+  AssertWcwidthRange(0x4dc0, 0x4e00, 2);
+}
+
+TEST(wchar, wcwidth_default_ignorables) {
+  AssertWcwidthRange(0xfff0, 0xfff8, 0); // Unassigned by default ignorable.
+  EXPECT_EQ(0, wcwidth(0xe0000)); // ...through 0xe0fff.
+}
+
+TEST(wchar, wcwidth_korean_common_non_syllables) {
+  EXPECT_EQ(2, wcwidth(L'ㅜ')); // Korean "crying" emoticon.
+  EXPECT_EQ(2, wcwidth(L'ㅋ')); // Korean "laughing" emoticon.
+}
diff --git a/tools/versioner/src/Driver.cpp b/tools/versioner/src/Driver.cpp
index 1b631b6..a094818 100644
--- a/tools/versioner/src/Driver.cpp
+++ b/tools/versioner/src/Driver.cpp
@@ -100,11 +100,12 @@
   std::vector<std::string> cmd = { "versioner" };
   cmd.push_back("-std=c11");
   cmd.push_back("-x");
-  cmd.push_back("c-header");
+  cmd.push_back("c");
   cmd.push_back("-fsyntax-only");
 
   cmd.push_back("-Wall");
   cmd.push_back("-Wextra");
+  cmd.push_back("-Weverything");
   cmd.push_back("-Werror");
   cmd.push_back("-Wundef");
   cmd.push_back("-Wno-unused-macros");
@@ -134,7 +135,9 @@
     cmd.push_back(dir);
   }
 
+  cmd.push_back("-include");
   cmd.push_back(filename_placeholder);
+  cmd.push_back("-");
 
   auto diags = constructDiags();
   driver::Driver driver("versioner", llvm::sys::getDefaultTargetTriple(), *diags, vfs);
diff --git a/tools/versioner/src/versioner.cpp b/tools/versioner/src/versioner.cpp
index 735ea04..747c767 100644
--- a/tools/versioner/src/versioner.cpp
+++ b/tools/versioner/src/versioner.cpp
@@ -18,6 +18,7 @@
 #include <err.h>
 #include <limits.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -190,6 +191,21 @@
     }
   }
 
+  // Dup an empty file to stdin, so that we can use `clang -include a.h -` instead of `clang a.h`,
+  // since some warnings don't get generated in files that are compiled directly.
+  FILE* empty_file = tmpfile();
+  if (!empty_file) {
+    err(1, "failed to create temporary file");
+  }
+
+  int empty_file_fd = fileno(empty_file);
+  if (empty_file_fd == -1) {
+    errx(1, "fileno failed on tmpfile");
+  }
+
+  dup2(empty_file_fd, STDIN_FILENO);
+  fclose(empty_file);
+
   thread_count = std::min(thread_count, jobs.size());
 
   if (thread_count == 1) {