Merge "elf.h: add STN_UNDEF"
diff --git a/benchmarks/stdio_benchmark.cpp b/benchmarks/stdio_benchmark.cpp
index 0e7f668..97a03dc 100644
--- a/benchmarks/stdio_benchmark.cpp
+++ b/benchmarks/stdio_benchmark.cpp
@@ -171,3 +171,28 @@
   FopenFgetcFclose(state, true);
 }
 BIONIC_BENCHMARK(BM_stdio_fopen_fgetc_fclose_no_locking);
+
+static void BM_stdio_printf_literal(benchmark::State& state) {
+  while (state.KeepRunning()) {
+    char buf[BUFSIZ];
+    snprintf(buf, sizeof(buf), "this is just a literal string with no format specifiers");
+  }
+}
+BIONIC_BENCHMARK(BM_stdio_printf_literal);
+
+static void BM_stdio_printf_s(benchmark::State& state) {
+  while (state.KeepRunning()) {
+    char buf[BUFSIZ];
+    snprintf(buf, sizeof(buf), "this is a more typical error message with detail: %s",
+             "No such file or directory");
+  }
+}
+BIONIC_BENCHMARK(BM_stdio_printf_s);
+
+static void BM_stdio_printf_d(benchmark::State& state) {
+  while (state.KeepRunning()) {
+    char buf[BUFSIZ];
+    snprintf(buf, sizeof(buf), "this is a more typical error message with detail: %d", 123456);
+  }
+}
+BIONIC_BENCHMARK(BM_stdio_printf_d);
diff --git a/benchmarks/suites/full.xml b/benchmarks/suites/full.xml
index a7bd9d4..240b5e7 100644
--- a/benchmarks/suites/full.xml
+++ b/benchmarks/suites/full.xml
@@ -189,6 +189,15 @@
   <name>BM_stdio_fopen_getline_fclose_no_locking</name>
 </fn>
 <fn>
+  <name>BM_stdio_printf_literal</name>
+</fn>
+<fn>
+  <name>BM_stdio_printf_s</name>
+</fn>
+<fn>
+  <name>BM_stdio_printf_d</name>
+</fn>
+<fn>
   <name>BM_string_memcmp</name>
   <args>AT_ALIGNED_TWOBUF</args>
 </fn>
diff --git a/docs/status.md b/docs/status.md
index 32de91e..6467143 100644
--- a/docs/status.md
+++ b/docs/status.md
@@ -10,6 +10,7 @@
   * `__freading`/`__fwriting` (completing <stdio_ext.h>)
   * `endhostent`/endnetent`/`endprotoent`/`getnetent`/`getprotoent`/`sethostent`/`setnetent`/`setprotoent` (completing <netdb.h>)
   * `fexecve`
+  * `fflush_unlocked`/`fgetc_unlocked`/`fgets_unlocked`/`fputc_unlocked`/`fputs_unlocked`/`fread_unlocked`/`fwrite_unlocked`
   * `getentropy`/`getrandom` (adding <sys/random.h>)
   * `getlogin_r`
   * `glob`/`globfree` (adding <glob.h>)
diff --git a/libc/Android.bp b/libc/Android.bp
index 2e5ec00..050d2f5 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -14,7 +14,6 @@
     "bionic/siginterrupt.c",
     "bionic/sigsetmask.c",
     "stdio/fmemopen.cpp",
-    "stdio/fread.c",
     "stdio/parsefloat.c",
     "stdio/refill.c",
     "stdio/stdio.cpp",
@@ -439,28 +438,22 @@
         "upstream-openbsd/lib/libc/net/ntohl.c",
         "upstream-openbsd/lib/libc/net/ntohs.c",
         "upstream-openbsd/lib/libc/net/res_random.c",
-        "upstream-openbsd/lib/libc/stdio/fflush.c",
         "upstream-openbsd/lib/libc/stdio/fgetln.c",
-        "upstream-openbsd/lib/libc/stdio/fgets.c",
         "upstream-openbsd/lib/libc/stdio/fgetwc.c",
         "upstream-openbsd/lib/libc/stdio/fgetws.c",
         "upstream-openbsd/lib/libc/stdio/flags.c",
         "upstream-openbsd/lib/libc/stdio/fpurge.c",
-        "upstream-openbsd/lib/libc/stdio/fputs.c",
         "upstream-openbsd/lib/libc/stdio/fputwc.c",
         "upstream-openbsd/lib/libc/stdio/fputws.c",
         "upstream-openbsd/lib/libc/stdio/fvwrite.c",
         "upstream-openbsd/lib/libc/stdio/fwalk.c",
         "upstream-openbsd/lib/libc/stdio/fwide.c",
-        "upstream-openbsd/lib/libc/stdio/fwrite.c",
         "upstream-openbsd/lib/libc/stdio/getdelim.c",
         "upstream-openbsd/lib/libc/stdio/gets.c",
         "upstream-openbsd/lib/libc/stdio/makebuf.c",
         "upstream-openbsd/lib/libc/stdio/mktemp.c",
         "upstream-openbsd/lib/libc/stdio/open_memstream.c",
         "upstream-openbsd/lib/libc/stdio/open_wmemstream.c",
-        "upstream-openbsd/lib/libc/stdio/perror.c",
-        "upstream-openbsd/lib/libc/stdio/puts.c",
         "upstream-openbsd/lib/libc/stdio/rget.c",
         "upstream-openbsd/lib/libc/stdio/setvbuf.c",
         "upstream-openbsd/lib/libc/stdio/tempnam.c",
@@ -531,8 +524,8 @@
     name: "libc_openbsd_large_stack",
     defaults: ["libc_defaults"],
     srcs: [
-        "upstream-openbsd/lib/libc/stdio/vfprintf.c",
-        "upstream-openbsd/lib/libc/stdio/vfwprintf.c",
+        "stdio/vfprintf.c",
+        "stdio/vfwprintf.c",
     ],
     cflags: [
         "-include openbsd-compat.h",
@@ -541,10 +534,10 @@
     ],
 
     local_include_dirs: [
-        "stdio",
-        "upstream-openbsd/android/include",
-        "upstream-openbsd/lib/libc/include",
+        "upstream-openbsd/android/include/",
+        "upstream-openbsd/lib/libc/include/",
         "upstream-openbsd/lib/libc/gdtoa/",
+        "upstream-openbsd/lib/libc/stdio/",
     ],
 }
 
diff --git a/libc/arch-arm/bionic/setjmp.S b/libc/arch-arm/bionic/setjmp.S
index 6ee162e..b814124 100644
--- a/libc/arch-arm/bionic/setjmp.S
+++ b/libc/arch-arm/bionic/setjmp.S
@@ -76,11 +76,13 @@
 #define _JB_CHECKSUM    (_JB_CORE_BASE+10)
 
 ENTRY(setjmp)
+__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(setjmp)
   mov r1, #1
   b sigsetjmp
 END(setjmp)
 
 ENTRY(_setjmp)
+__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(_setjmp)
   mov r1, #0
   b sigsetjmp
 END(_setjmp)
@@ -117,6 +119,7 @@
 
 // int sigsetjmp(sigjmp_buf env, int save_signal_mask);
 ENTRY(sigsetjmp)
+__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(sigsetjmp)
   stmfd sp!, {r0, lr}
   .cfi_def_cfa_offset 8
   .cfi_rel_offset r0, 0
@@ -191,6 +194,7 @@
 
 // void siglongjmp(sigjmp_buf env, int value);
 ENTRY(siglongjmp)
+__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(siglongjmp)
   stmfd sp!, {r0, r1, lr}
   .cfi_def_cfa_offset 12
   .cfi_rel_offset r0, 0
@@ -262,4 +266,6 @@
 END(siglongjmp)
 
 ALIAS_SYMBOL(longjmp, siglongjmp)
+__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(longjmp)
 ALIAS_SYMBOL(_longjmp, siglongjmp)
+__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(_longjmp)
diff --git a/libc/arch-arm/bionic/vfork.S b/libc/arch-arm/bionic/vfork.S
index 1b7cbad..8329111 100644
--- a/libc/arch-arm/bionic/vfork.S
+++ b/libc/arch-arm/bionic/vfork.S
@@ -29,6 +29,7 @@
 #include <private/bionic_asm.h>
 
 ENTRY(vfork)
+__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(vfork)
     // __get_tls()[TLS_SLOT_THREAD_ID]->cached_pid_ = 0
     mrc     p15, 0, r3, c13, c0, 3
     ldr     r3, [r3, #4]
diff --git a/libc/arch-arm64/bionic/setjmp.S b/libc/arch-arm64/bionic/setjmp.S
index 2550134..ee618b1 100644
--- a/libc/arch-arm64/bionic/setjmp.S
+++ b/libc/arch-arm64/bionic/setjmp.S
@@ -99,17 +99,20 @@
 .endm
 
 ENTRY(setjmp)
+__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(setjmp)
   mov w1, #1
   b sigsetjmp
 END(setjmp)
 
 ENTRY(_setjmp)
+__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(_setjmp)
   mov w1, #0
   b sigsetjmp
 END(_setjmp)
 
 // int sigsetjmp(sigjmp_buf env, int save_signal_mask);
 ENTRY(sigsetjmp)
+__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(sigsetjmp)
   stp x0, x30, [sp, #-16]!
   .cfi_def_cfa_offset 16
   .cfi_rel_offset x0, 0
@@ -178,6 +181,7 @@
 
 // void siglongjmp(sigjmp_buf env, int value);
 ENTRY(siglongjmp)
+__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(siglongjmp)
 #if USE_CHECKSUM
   // Check the checksum before doing anything.
   m_calculate_checksum x12, x0, x2
@@ -256,4 +260,6 @@
 END(siglongjmp)
 
 ALIAS_SYMBOL(longjmp, siglongjmp)
+__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(longjmp)
 ALIAS_SYMBOL(_longjmp, siglongjmp)
+__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(_longjmp)
diff --git a/libc/arch-arm64/bionic/vfork.S b/libc/arch-arm64/bionic/vfork.S
index 92fa333..0a83cc7 100644
--- a/libc/arch-arm64/bionic/vfork.S
+++ b/libc/arch-arm64/bionic/vfork.S
@@ -31,6 +31,7 @@
 #include <linux/sched.h>
 
 ENTRY(vfork)
+__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(vfork)
     // __get_tls()[TLS_SLOT_THREAD_ID]->cached_pid_ = 0
     mrs     x0, tpidr_el0
     ldr     x0, [x0, #8]
diff --git a/libc/arch-mips/bionic/setjmp.S b/libc/arch-mips/bionic/setjmp.S
index 1728054..ffe3665 100644
--- a/libc/arch-mips/bionic/setjmp.S
+++ b/libc/arch-mips/bionic/setjmp.S
@@ -199,6 +199,7 @@
 RAOFF= FRAMESZ-1*REGSZ
 
 NON_LEAF(sigsetjmp, FRAMESZ, $ra)
+__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(sigsetjmp)
 	.mask	0x80000000, RAOFF
 	PTR_SUBU $sp, FRAMESZ			# allocate stack frame
 	SETUP_GP64(GPOFF, sigsetjmp)
@@ -288,6 +289,7 @@
 # Alternate entry points:
 
 NON_LEAF(setjmp, FRAMESZ, $ra)
+__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(setjmp)
 	.mask	0x80000000, RAOFF
 	PTR_SUBU $sp, FRAMESZ
 	SETUP_GP64(GPOFF, setjmp)		# can't share sigsetjmp's gp code
@@ -300,6 +302,7 @@
 
 
 NON_LEAF(_setjmp, FRAMESZ, $ra)
+__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(_setjmp)
 	.mask	0x80000000, RAOFF
 	PTR_SUBU $sp, FRAMESZ
 	SETUP_GP64(GPOFF, _setjmp)		# can't share sigsetjmp's gp code
@@ -312,6 +315,7 @@
 
 
 NON_LEAF(siglongjmp, FRAMESZ, $ra)
+__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(siglongjmp)
 	.mask	0x80000000, RAOFF
 	PTR_SUBU $sp, FRAMESZ
 	SETUP_GP64(GPOFF, siglongjmp)
@@ -409,4 +413,6 @@
 END(siglongjmp)
 
 ALIAS_SYMBOL(longjmp, siglongjmp)
+__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(longjmp)
 ALIAS_SYMBOL(_longjmp, siglongjmp)
+__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(_longjmp)
diff --git a/libc/arch-mips/bionic/vfork.S b/libc/arch-mips/bionic/vfork.S
index fdd6a69..c4f67c6 100644
--- a/libc/arch-mips/bionic/vfork.S
+++ b/libc/arch-mips/bionic/vfork.S
@@ -34,6 +34,7 @@
 #define SIGCHLD 18
 
 ENTRY(vfork)
+__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(vfork)
 	.set	noreorder
 	.cpload	$t9
 
diff --git a/libc/arch-mips64/bionic/vfork.S b/libc/arch-mips64/bionic/vfork.S
index 5d457a9..b5ff5d4 100644
--- a/libc/arch-mips64/bionic/vfork.S
+++ b/libc/arch-mips64/bionic/vfork.S
@@ -42,6 +42,7 @@
 #endif
 
 LEAF(vfork,FRAMESZ)
+__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(vfork)
 #if FRAMESZ!=0
 	PTR_SUBU $sp, FRAMESZ
 #endif
diff --git a/libc/arch-x86/bionic/setjmp.S b/libc/arch-x86/bionic/setjmp.S
index efb6459..6e6c365 100644
--- a/libc/arch-x86/bionic/setjmp.S
+++ b/libc/arch-x86/bionic/setjmp.S
@@ -78,18 +78,21 @@
 .endm
 
 ENTRY(setjmp)
+__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(setjmp)
   movl 4(%esp),%ecx
   mov $1,%eax
   jmp .L_sigsetjmp
 END(setjmp)
 
 ENTRY(_setjmp)
+__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(_setjmp)
   movl 4(%esp),%ecx
   movl $0,%eax
   jmp .L_sigsetjmp
 END(_setjmp)
 
 ENTRY(sigsetjmp)
+__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(sigsetjmp)
   movl 4(%esp),%ecx
   movl 8(%esp),%eax
 
@@ -142,6 +145,7 @@
 END(sigsetjmp)
 
 ENTRY(siglongjmp)
+__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(siglongjmp)
   movl 4(%esp),%edx
 
   // Check the checksum before doing anything.
@@ -205,4 +209,6 @@
 END(siglongjmp)
 
 ALIAS_SYMBOL(longjmp, siglongjmp)
+__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(longjmp)
 ALIAS_SYMBOL(_longjmp, siglongjmp)
+__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(_longjmp)
diff --git a/libc/arch-x86/bionic/vfork.S b/libc/arch-x86/bionic/vfork.S
index 5c71b8d..79d7899 100644
--- a/libc/arch-x86/bionic/vfork.S
+++ b/libc/arch-x86/bionic/vfork.S
@@ -31,6 +31,7 @@
 // This custom code preserves the return address across the system call.
 
 ENTRY(vfork)
+__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(vfork)
   popl    %ecx  // Grab the return address.
   .cfi_adjust_cfa_offset 4
   .cfi_rel_offset ecx, 0
diff --git a/libc/arch-x86_64/bionic/setjmp.S b/libc/arch-x86_64/bionic/setjmp.S
index 34b4365..1d34561 100644
--- a/libc/arch-x86_64/bionic/setjmp.S
+++ b/libc/arch-x86_64/bionic/setjmp.S
@@ -91,17 +91,20 @@
 .endm
 
 ENTRY(setjmp)
+__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(setjmp)
   movl $1,%esi
   jmp PIC_PLT(sigsetjmp)
 END(setjmp)
 
 ENTRY(_setjmp)
+__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(_setjmp)
   movl $0,%esi
   jmp PIC_PLT(sigsetjmp)
 END(_setjmp)
 
 // int sigsetjmp(sigjmp_buf env, int save_signal_mask);
 ENTRY(sigsetjmp)
+__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(sigsetjmp)
   pushq %rdi
   movq %rsi,%rdi
   call PIC_PLT(__bionic_setjmp_cookie_get)
@@ -148,6 +151,7 @@
 
 // void siglongjmp(sigjmp_buf env, int value);
 ENTRY(siglongjmp)
+__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(siglongjmp)
   movq %rdi,%r12
   pushq %rsi // Push 'value'.
 
@@ -206,4 +210,6 @@
 END(siglongjmp)
 
 ALIAS_SYMBOL(longjmp, siglongjmp)
+__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(longjmp)
 ALIAS_SYMBOL(_longjmp, siglongjmp)
+__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(_longjmp)
diff --git a/libc/arch-x86_64/bionic/vfork.S b/libc/arch-x86_64/bionic/vfork.S
index 3b32c66..ce96a8c 100644
--- a/libc/arch-x86_64/bionic/vfork.S
+++ b/libc/arch-x86_64/bionic/vfork.S
@@ -31,6 +31,7 @@
 // This custom code preserves the return address across the system call.
 
 ENTRY(vfork)
+__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(vfork)
   popq    %rdi  // Grab the return address.
 
   // __get_tls()[TLS_SLOT_THREAD_ID]->cached_pid_ = 0
diff --git a/libc/bionic/__cxa_thread_atexit_impl.cpp b/libc/bionic/__cxa_thread_atexit_impl.cpp
index 6284b12..f687fd1 100644
--- a/libc/bionic/__cxa_thread_atexit_impl.cpp
+++ b/libc/bionic/__cxa_thread_atexit_impl.cpp
@@ -15,6 +15,8 @@
  */
 #include <sys/cdefs.h>
 
+#include <private/bionic_defs.h>
+
 #include "pthread_internal.h"
 
 class thread_local_dtor {
@@ -25,7 +27,10 @@
   thread_local_dtor* next;
 };
 
-extern "C" int __cxa_thread_atexit_impl(void (*func) (void *), void *arg, void *dso_handle) {
+extern "C" int __cxa_thread_atexit_impl(void (*func) (void *), void *arg, void *dso_handle);
+
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
+int __cxa_thread_atexit_impl(void (*func) (void *), void *arg, void *dso_handle) {
   thread_local_dtor* dtor = new thread_local_dtor();
 
   dtor->func = func;
diff --git a/libc/bionic/__libc_init_main_thread.cpp b/libc/bionic/__libc_init_main_thread.cpp
index 5004e24..efa7dee 100644
--- a/libc/bionic/__libc_init_main_thread.cpp
+++ b/libc/bionic/__libc_init_main_thread.cpp
@@ -85,9 +85,14 @@
   // thread's stack rather than on the heap.
   // The main thread has no mmap allocated space for stack or pthread_internal_t.
   main_thread.mmap_size = 0;
+
   pthread_attr_init(&main_thread.attr);
-  main_thread.attr.guard_size = 0; // The main thread has no guard page.
-  main_thread.attr.stack_size = 0; // User code should never see this; we'll compute it when asked.
+  // We don't want to explicitly set the main thread's scheduler attributes (http://b/68328561).
+  pthread_attr_setinheritsched(&main_thread.attr, PTHREAD_INHERIT_SCHED);
+  // The main thread has no guard page.
+  pthread_attr_setguardsize(&main_thread.attr, 0);
+  // User code should never see this; we'll compute it when asked.
+  pthread_attr_setstacksize(&main_thread.attr, 0);
 
   // The TLS stack guard is set from the global, so ensure that we've initialized the global
   // before we initialize the TLS. Dynamic executables will initialize their copy of the global
diff --git a/libc/bionic/clone.cpp b/libc/bionic/clone.cpp
index 66ec503..b895305 100644
--- a/libc/bionic/clone.cpp
+++ b/libc/bionic/clone.cpp
@@ -34,6 +34,7 @@
 
 #include "pthread_internal.h"
 
+#include "private/bionic_defs.h"
 #include "private/bionic_macros.h"
 
 extern "C" pid_t __bionic_clone(uint32_t flags, void* child_stack, int* parent_tid, void* tls, int* child_tid, int (*fn)(void*), void* arg);
@@ -52,10 +53,11 @@
   __exit(status);
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int clone(int (*fn)(void*), void* child_stack, int flags, void* arg, ...) {
-  int* parent_tid = NULL;
-  void* new_tls = NULL;
-  int* child_tid = NULL;
+  int* parent_tid = nullptr;
+  void* new_tls = nullptr;
+  int* child_tid = nullptr;
 
   if (fn != nullptr && child_stack == nullptr) {
     errno = EINVAL;
diff --git a/libc/bionic/dirent.cpp b/libc/bionic/dirent.cpp
index 6fd3842..37a2fa7 100644
--- a/libc/bionic/dirent.cpp
+++ b/libc/bionic/dirent.cpp
@@ -36,6 +36,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include "private/bionic_fortify.h"
 #include "private/ErrnoRestorer.h"
 #include "private/ScopedPthreadMutexLocker.h"
 
@@ -56,6 +57,8 @@
   long current_pos_;
 };
 
+#define CHECK_DIR(d) if (d == nullptr) __fortify_fatal("%s: null DIR*", __FUNCTION__)
+
 static DIR* __allocate_DIR(int fd) {
   DIR* d = reinterpret_cast<DIR*>(malloc(sizeof(DIR)));
   if (d == NULL) {
@@ -69,8 +72,9 @@
   return d;
 }
 
-int dirfd(DIR* dirp) {
-  return dirp->fd_;
+int dirfd(DIR* d) {
+  CHECK_DIR(d);
+  return d->fd_;
 }
 
 DIR* fdopendir(int fd) {
@@ -93,6 +97,7 @@
 }
 
 static bool __fill_DIR(DIR* d) {
+  CHECK_DIR(d);
   int rc = TEMP_FAILURE_RETRY(__getdents64(d->fd_, d->buff_, sizeof(d->buff_)));
   if (rc <= 0) {
     return false;
@@ -117,12 +122,15 @@
 }
 
 dirent* readdir(DIR* d) {
+  CHECK_DIR(d);
   ScopedPthreadMutexLocker locker(&d->mutex_);
   return __readdir_locked(d);
 }
 __strong_alias(readdir64, readdir);
 
 int readdir_r(DIR* d, dirent* entry, dirent** result) {
+  CHECK_DIR(d);
+
   ErrnoRestorer errno_restorer;
 
   *result = NULL;
@@ -156,6 +164,8 @@
 }
 
 void rewinddir(DIR* d) {
+  CHECK_DIR(d);
+
   ScopedPthreadMutexLocker locker(&d->mutex_);
   lseek(d->fd_, 0, SEEK_SET);
   d->available_bytes_ = 0;
@@ -163,6 +173,8 @@
 }
 
 void seekdir(DIR* d, long offset) {
+  CHECK_DIR(d);
+
   ScopedPthreadMutexLocker locker(&d->mutex_);
   off_t ret = lseek(d->fd_, offset, SEEK_SET);
   if (ret != -1L) {
@@ -172,6 +184,8 @@
 }
 
 long telldir(DIR* d) {
+  CHECK_DIR(d);
+
   return d->current_pos_;
 }
 
diff --git a/libc/bionic/environ.cpp b/libc/bionic/environ.cpp
index 363c1fd..243af62 100644
--- a/libc/bionic/environ.cpp
+++ b/libc/bionic/environ.cpp
@@ -28,6 +28,8 @@
 
 #include <unistd.h>
 
+#include "private/bionic_defs.h"
 // Keep that variable in separate .o file to make sure programs which define
 // their own "environ" are compileable.
+__BIONIC_WEAK_VARIABLE_FOR_NATIVE_BRIDGE
 char** environ;
diff --git a/libc/bionic/fork.cpp b/libc/bionic/fork.cpp
index efcbb8c..33b7343 100644
--- a/libc/bionic/fork.cpp
+++ b/libc/bionic/fork.cpp
@@ -28,8 +28,10 @@
 
 #include <unistd.h>
 
+#include "private/bionic_defs.h"
 #include "pthread_internal.h"
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int fork() {
   __bionic_atfork_run_prepare();
 
diff --git a/libc/bionic/libc_init_common.cpp b/libc/bionic/libc_init_common.cpp
index 48fd670..c22f571 100644
--- a/libc/bionic/libc_init_common.cpp
+++ b/libc/bionic/libc_init_common.cpp
@@ -261,6 +261,7 @@
     "LD_PROFILE",
     "LD_SHOW_AUXV",
     "LD_USE_LOAD_BIAS",
+    "LIBC_DEBUG_MALLOC_OPTIONS",
     "LOCALDOMAIN",
     "LOCPATH",
     "MALLOC_CHECK_",
diff --git a/libc/bionic/pthread_attr.cpp b/libc/bionic/pthread_attr.cpp
index 0d79374..93177f1 100644
--- a/libc/bionic/pthread_attr.cpp
+++ b/libc/bionic/pthread_attr.cpp
@@ -35,10 +35,12 @@
 
 #include <async_safe/log.h>
 
+#include "private/bionic_defs.h"
 #include "private/bionic_string_utils.h"
 #include "private/ErrnoRestorer.h"
 #include "pthread_internal.h"
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int pthread_attr_init(pthread_attr_t* attr) {
   attr->flags = 0;
   attr->stack_base = NULL;
@@ -49,28 +51,40 @@
   return 0;
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int pthread_attr_destroy(pthread_attr_t* attr) {
   memset(attr, 0x42, sizeof(pthread_attr_t));
   return 0;
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int pthread_attr_setinheritsched(pthread_attr_t* attr, int flag) {
   if (flag == PTHREAD_EXPLICIT_SCHED) {
     attr->flags &= ~PTHREAD_ATTR_FLAG_INHERIT;
+    attr->flags |= PTHREAD_ATTR_FLAG_EXPLICIT;
   } else if (flag == PTHREAD_INHERIT_SCHED) {
     attr->flags |= PTHREAD_ATTR_FLAG_INHERIT;
+    attr->flags &= ~PTHREAD_ATTR_FLAG_EXPLICIT;
   } else {
     return EINVAL;
   }
   return 0;
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int pthread_attr_getinheritsched(const pthread_attr_t* attr, int* flag) {
-  *flag = (attr->flags & PTHREAD_ATTR_FLAG_INHERIT) ? PTHREAD_INHERIT_SCHED
-                                                    : PTHREAD_EXPLICIT_SCHED;
+  if ((attr->flags & PTHREAD_ATTR_FLAG_INHERIT) != 0) {
+    *flag = PTHREAD_INHERIT_SCHED;
+  } else if ((attr->flags & PTHREAD_ATTR_FLAG_EXPLICIT) != 0) {
+    *flag = PTHREAD_EXPLICIT_SCHED;
+  } else {
+    // Historical behavior before P, when pthread_attr_setinheritsched was added.
+    *flag = (attr->sched_policy != SCHED_NORMAL) ? PTHREAD_EXPLICIT_SCHED : PTHREAD_INHERIT_SCHED;
+  }
   return 0;
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int pthread_attr_setdetachstate(pthread_attr_t* attr, int state) {
   if (state == PTHREAD_CREATE_DETACHED) {
     attr->flags |= PTHREAD_ATTR_FLAG_DETACHED;
@@ -82,31 +96,37 @@
   return 0;
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int pthread_attr_getdetachstate(const pthread_attr_t* attr, int* state) {
   *state = (attr->flags & PTHREAD_ATTR_FLAG_DETACHED) ? PTHREAD_CREATE_DETACHED : PTHREAD_CREATE_JOINABLE;
   return 0;
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int pthread_attr_setschedpolicy(pthread_attr_t* attr, int policy) {
   attr->sched_policy = policy;
   return 0;
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int pthread_attr_getschedpolicy(const pthread_attr_t* attr, int* policy) {
   *policy = attr->sched_policy;
   return 0;
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int pthread_attr_setschedparam(pthread_attr_t* attr, const sched_param* param) {
   attr->sched_priority = param->sched_priority;
   return 0;
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int pthread_attr_getschedparam(const pthread_attr_t* attr, sched_param* param) {
   param->sched_priority = attr->sched_priority;
   return 0;
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int pthread_attr_setstacksize(pthread_attr_t* attr, size_t stack_size) {
   if (stack_size < PTHREAD_STACK_MIN) {
     return EINVAL;
@@ -115,11 +135,13 @@
   return 0;
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int pthread_attr_getstacksize(const pthread_attr_t* attr, size_t* stack_size) {
   void* unused;
   return pthread_attr_getstack(attr, &unused, stack_size);
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int pthread_attr_setstack(pthread_attr_t* attr, void* stack_base, size_t stack_size) {
   if ((stack_size & (PAGE_SIZE - 1) || stack_size < PTHREAD_STACK_MIN)) {
     return EINVAL;
@@ -198,22 +220,26 @@
   async_safe_fatal("Stack not found in /proc/self/maps");
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int pthread_attr_getstack(const pthread_attr_t* attr, void** stack_base, size_t* stack_size) {
   *stack_base = attr->stack_base;
   *stack_size = attr->stack_size;
   return 0;
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int pthread_attr_setguardsize(pthread_attr_t* attr, size_t guard_size) {
   attr->guard_size = guard_size;
   return 0;
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int pthread_attr_getguardsize(const pthread_attr_t* attr, size_t* guard_size) {
   *guard_size = attr->guard_size;
   return 0;
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int pthread_getattr_np(pthread_t t, pthread_attr_t* attr) {
   pthread_internal_t* thread = reinterpret_cast<pthread_internal_t*>(t);
   *attr = thread->attr;
@@ -230,6 +256,7 @@
   return 0;
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int pthread_attr_setscope(pthread_attr_t*, int scope) {
   if (scope == PTHREAD_SCOPE_SYSTEM) {
     return 0;
@@ -240,6 +267,7 @@
   return EINVAL;
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int pthread_attr_getscope(const pthread_attr_t*, int* scope) {
   *scope = PTHREAD_SCOPE_SYSTEM;
   return 0;
diff --git a/libc/bionic/pthread_create.cpp b/libc/bionic/pthread_create.cpp
index 11e148c..21a707b 100644
--- a/libc/bionic/pthread_create.cpp
+++ b/libc/bionic/pthread_create.cpp
@@ -37,6 +37,7 @@
 
 #include <async_safe/log.h>
 
+#include "private/bionic_defs.h"
 #include "private/bionic_macros.h"
 #include "private/bionic_prctl.h"
 #include "private/bionic_ssp.h"
@@ -121,7 +122,7 @@
   bool need_set = true;
   int policy;
   sched_param param;
-  if (thread->attr.flags & PTHREAD_ATTR_FLAG_INHERIT) {
+  if ((thread->attr.flags & PTHREAD_ATTR_FLAG_INHERIT) != 0) {
     // Unless the parent has SCHED_RESET_ON_FORK set, we've already inherited from the parent.
     policy = sched_getscheduler(0);
     need_set = ((policy & SCHED_RESET_ON_FORK) != 0);
@@ -141,6 +142,11 @@
     policy = thread->attr.sched_policy;
     param.sched_priority = thread->attr.sched_priority;
   }
+  // Backwards compatibility: before P, Android didn't have pthread_attr_setinheritsched,
+  // and our behavior was neither of the POSIX behaviors.
+  if ((thread->attr.flags & (PTHREAD_ATTR_FLAG_INHERIT|PTHREAD_ATTR_FLAG_EXPLICIT)) == 0) {
+    need_set = (thread->attr.sched_policy != SCHED_NORMAL);
+  }
   if (need_set) {
     if (sched_setscheduler(thread->tid, policy, &param) == -1) {
       async_safe_format_log(ANDROID_LOG_WARN, "libc",
@@ -258,6 +264,8 @@
   return NULL;
 }
 
+
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int pthread_create(pthread_t* thread_out, pthread_attr_t const* attr,
                    void* (*start_routine)(void*), void* arg) {
   ErrnoRestorer errno_restorer;
diff --git a/libc/bionic/pthread_detach.cpp b/libc/bionic/pthread_detach.cpp
index fb8e0dd..011e6c6 100644
--- a/libc/bionic/pthread_detach.cpp
+++ b/libc/bionic/pthread_detach.cpp
@@ -29,8 +29,10 @@
 #include <errno.h>
 #include <pthread.h>
 
+#include "private/bionic_defs.h"
 #include "pthread_internal.h"
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int pthread_detach(pthread_t t) {
   pthread_internal_t* thread = __pthread_internal_find(t);
   if (thread == NULL) {
diff --git a/libc/bionic/pthread_exit.cpp b/libc/bionic/pthread_exit.cpp
index ac29c1d..8b4c44e 100644
--- a/libc/bionic/pthread_exit.cpp
+++ b/libc/bionic/pthread_exit.cpp
@@ -33,6 +33,7 @@
 #include <string.h>
 #include <sys/mman.h>
 
+#include "private/bionic_defs.h"
 #include "pthread_internal.h"
 
 extern "C" __noreturn void _exit_with_stack_teardown(void*, size_t);
@@ -44,6 +45,7 @@
  *         and thread cancelation
  */
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 void __pthread_cleanup_push(__pthread_cleanup_t* c, __pthread_cleanup_func_t routine, void* arg) {
   pthread_internal_t* thread = __get_thread();
   c->__cleanup_routine = routine;
@@ -52,6 +54,7 @@
   thread->cleanup_stack = c;
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 void __pthread_cleanup_pop(__pthread_cleanup_t* c, int execute) {
   pthread_internal_t* thread = __get_thread();
   thread->cleanup_stack = c->__cleanup_prev;
@@ -60,6 +63,7 @@
   }
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 void pthread_exit(void* return_value) {
   // Call dtors for thread_local objects first.
   __cxa_thread_finalize();
diff --git a/libc/bionic/pthread_gettid_np.cpp b/libc/bionic/pthread_gettid_np.cpp
index fe85442..1beddc9 100644
--- a/libc/bionic/pthread_gettid_np.cpp
+++ b/libc/bionic/pthread_gettid_np.cpp
@@ -26,8 +26,10 @@
  * SUCH DAMAGE.
  */
 
+#include "private/bionic_defs.h"
 #include "pthread_internal.h"
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 pid_t pthread_gettid_np(pthread_t t) {
   pthread_internal_t* thread = __pthread_internal_find(t);
   return thread ? thread->tid : -1;
diff --git a/libc/bionic/pthread_internal.h b/libc/bionic/pthread_internal.h
index 2dd0cff..a4cba87 100644
--- a/libc/bionic/pthread_internal.h
+++ b/libc/bionic/pthread_internal.h
@@ -40,8 +40,10 @@
 // Has the thread been joined by another thread?
 #define PTHREAD_ATTR_FLAG_JOINED 0x00000002
 
-// Should we inherit scheduling attributes from the parent on pthread_create?
+// Used for pthread_attr_setinheritsched. We need two flags for this apparent
+// boolean because our historical behavior matches neither of the POSIX choices.
 #define PTHREAD_ATTR_FLAG_INHERIT 0x00000004
+#define PTHREAD_ATTR_FLAG_EXPLICIT 0x00000008
 
 class pthread_key_data_t {
  public:
diff --git a/libc/bionic/pthread_join.cpp b/libc/bionic/pthread_join.cpp
index 4d852cb..be76c20 100644
--- a/libc/bionic/pthread_join.cpp
+++ b/libc/bionic/pthread_join.cpp
@@ -28,9 +28,11 @@
 
 #include <errno.h>
 
+#include "private/bionic_defs.h"
 #include "private/bionic_futex.h"
 #include "pthread_internal.h"
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int pthread_join(pthread_t t, void** return_value) {
   if (t == pthread_self()) {
     return EDEADLK;
diff --git a/libc/bionic/pthread_key.cpp b/libc/bionic/pthread_key.cpp
index 6d77afa..baff9cc 100644
--- a/libc/bionic/pthread_key.cpp
+++ b/libc/bionic/pthread_key.cpp
@@ -30,6 +30,7 @@
 #include <pthread.h>
 #include <stdatomic.h>
 
+#include "private/bionic_defs.h"
 #include "private/bionic_tls.h"
 #include "pthread_internal.h"
 
@@ -115,6 +116,7 @@
   }
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int pthread_key_create(pthread_key_t* key, void (*key_destructor)(void*)) {
   for (size_t i = 0; i < BIONIC_PTHREAD_KEY_COUNT; ++i) {
     uintptr_t seq = atomic_load_explicit(&key_map[i].seq, memory_order_relaxed);
@@ -133,6 +135,7 @@
 // not call the destructors for non-NULL key values. Instead, it is the
 // responsibility of the caller to properly dispose of the corresponding data
 // and resources, using any means it finds suitable.
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int pthread_key_delete(pthread_key_t key) {
   if (__predict_false(!KeyInValidRange(key))) {
     return EINVAL;
@@ -148,6 +151,7 @@
   return EINVAL;
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 void* pthread_getspecific(pthread_key_t key) {
   if (__predict_false(!KeyInValidRange(key))) {
     return NULL;
@@ -166,6 +170,7 @@
   return NULL;
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int pthread_setspecific(pthread_key_t key, const void* ptr) {
   if (__predict_false(!KeyInValidRange(key))) {
     return EINVAL;
diff --git a/libc/bionic/system_properties.cpp b/libc/bionic/system_properties.cpp
index b87d7e8..1a7a359 100644
--- a/libc/bionic/system_properties.cpp
+++ b/libc/bionic/system_properties.cpp
@@ -56,6 +56,7 @@
 #include <async_safe/log.h>
 
 #include "private/ErrnoRestorer.h"
+#include "private/bionic_defs.h"
 #include "private/bionic_futex.h"
 #include "private/bionic_lock.h"
 #include "private/bionic_macros.h"
@@ -254,6 +255,7 @@
 static_assert(sizeof(prop_info) == 96, "size of struct prop_info must be 96 bytes");
 
 // This is public because it was exposed in the NDK. As of 2017-01, ~60 apps reference this symbol.
+__BIONIC_WEAK_VARIABLE_FOR_NATIVE_BRIDGE
 prop_area* __system_property_area__ = nullptr;
 
 static char property_filename[PROP_FILENAME_MAX] = PROP_FILENAME;
@@ -1149,6 +1151,7 @@
   }
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int __system_properties_init() {
   // This is called from __libc_init_common, and should leave errno at 0 (http://b/37248982).
   ErrnoRestorer errno_restorer;
@@ -1177,6 +1180,7 @@
   return 0;
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int __system_property_set_filename(const char* filename) {
   size_t len = strlen(filename);
   if (len >= sizeof(property_filename)) return -1;
@@ -1185,6 +1189,7 @@
   return 0;
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int __system_property_area_init() {
   free_and_unmap_contexts();
   mkdir(property_filename, S_IRWXU | S_IXGRP | S_IXOTH);
@@ -1206,6 +1211,7 @@
   return fsetxattr_failed ? -2 : 0;
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 uint32_t __system_property_area_serial() {
   prop_area* pa = __system_property_area__;
   if (!pa) {
@@ -1215,6 +1221,7 @@
   return atomic_load_explicit(pa->serial(), memory_order_acquire);
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 const prop_info* __system_property_find(const char* name) {
   if (!__system_property_area__) {
     return nullptr;
@@ -1233,6 +1240,7 @@
   return strncmp(name, "ro.", 3) == 0;
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int __system_property_read(const prop_info* pi, char* name, char* value) {
   while (true) {
     uint32_t serial = __system_property_serial(pi);  // acquire semantics
@@ -1270,6 +1278,7 @@
   }
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 void __system_property_read_callback(const prop_info* pi,
                                      void (*callback)(void* cookie,
                                                       const char* name,
@@ -1305,6 +1314,7 @@
   }
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int __system_property_get(const char* name, char* value) {
   const prop_info* pi = __system_property_find(name);
 
@@ -1341,6 +1351,7 @@
   }
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int __system_property_set(const char* key, const char* value) {
   if (key == nullptr) return -1;
   if (value == nullptr) value = "";
@@ -1418,6 +1429,7 @@
   }
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int __system_property_update(prop_info* pi, const char* value, unsigned int len) {
   if (len >= PROP_VALUE_MAX) {
     return -1;
@@ -1448,6 +1460,7 @@
   return 0;
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int __system_property_add(const char* name, unsigned int namelen, const char* value,
                           unsigned int valuelen) {
   if (valuelen >= PROP_VALUE_MAX && !is_read_only(name)) {
@@ -1485,6 +1498,7 @@
 }
 
 // Wait for non-locked serial, and retrieve it with acquire semantics.
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 uint32_t __system_property_serial(const prop_info* pi) {
   uint32_t serial = load_const_atomic(&pi->serial, memory_order_acquire);
   while (SERIAL_DIRTY(serial)) {
@@ -1494,12 +1508,14 @@
   return serial;
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 uint32_t __system_property_wait_any(uint32_t old_serial) {
   uint32_t new_serial;
   __system_property_wait(nullptr, old_serial, &new_serial, nullptr);
   return new_serial;
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 bool __system_property_wait(const prop_info* pi,
                             uint32_t old_serial,
                             uint32_t* new_serial_ptr,
@@ -1526,6 +1542,7 @@
   return true;
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 const prop_info* __system_property_find_nth(unsigned n) {
   struct find_nth {
     const uint32_t sought;
@@ -1542,6 +1559,7 @@
   return state.result;
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int __system_property_foreach(void (*propfn)(const prop_info* pi, void* cookie), void* cookie) {
   if (!__system_property_area__) {
     return -1;
diff --git a/libc/include/stdio.h b/libc/include/stdio.h
index 559e52a..2d45fc5 100644
--- a/libc/include/stdio.h
+++ b/libc/include/stdio.h
@@ -256,7 +256,20 @@
 int fileno_unlocked(FILE* __fp) __INTRODUCED_IN(24);
 #define fropen(cookie, fn) funopen(cookie, fn, 0, 0, 0)
 #define fwopen(cookie, fn) funopen(cookie, 0, fn, 0, 0)
-#endif /* __USE_BSD */
+#endif
+
+#if defined(__USE_BSD)
+int fflush_unlocked(FILE* __fp) __INTRODUCED_IN_FUTURE;
+int fgetc_unlocked(FILE* __fp) __INTRODUCED_IN_FUTURE;
+int fputc_unlocked(int __ch, FILE* __fp) __INTRODUCED_IN_FUTURE;
+size_t fread_unlocked(void* __buf, size_t __size, size_t __count, FILE* __fp) __INTRODUCED_IN_FUTURE;
+size_t fwrite_unlocked(const void* __buf, size_t __size, size_t __count, FILE* __fp) __INTRODUCED_IN_FUTURE;
+#endif
+
+#if defined(__USE_GNU)
+int fputs_unlocked(const char* __s, FILE* __fp) __INTRODUCED_IN_FUTURE;
+char* fgets_unlocked(char* __buf, int __size, FILE* __fp) __INTRODUCED_IN_FUTURE;
+#endif
 
 #if defined(__BIONIC_INCLUDE_FORTIFY_HEADERS)
 #include <bits/fortify/stdio.h>
diff --git a/libc/include/sys/cdefs.h b/libc/include/sys/cdefs.h
index 3656733..833ced0 100644
--- a/libc/include/sys/cdefs.h
+++ b/libc/include/sys/cdefs.h
@@ -261,8 +261,13 @@
 
 #if defined(_FORTIFY_SOURCE) && _FORTIFY_SOURCE > 0
 #  if defined(__clang__)
-/* FORTIFY's _chk functions effectively disable ASAN's stdlib interceptors. */
-#    if !__has_feature(address_sanitizer)
+/*
+ * FORTIFY's _chk functions effectively disable ASAN's stdlib interceptors.
+ * Additionally, the static analyzer/clang-tidy try to pattern match some
+ * standard library functions, and FORTIFY sometimes interferes with this. So,
+ * we turn FORTIFY off in both cases.
+ */
+#    if !__has_feature(address_sanitizer) && !defined(__clang_analyzer__)
 #      define __BIONIC_FORTIFY 1
 #    endif
 #  elif defined(__OPTIMIZE__) && __OPTIMIZE__ > 0
diff --git a/libc/libc.arm.map b/libc/libc.arm.map
index 981cea6..abab364 100644
--- a/libc/libc.arm.map
+++ b/libc/libc.arm.map
@@ -1326,6 +1326,13 @@
     endnetent;
     endprotoent;
     fexecve;
+    fflush_unlocked;
+    fgetc_unlocked;
+    fgets_unlocked;
+    fputc_unlocked;
+    fputs_unlocked;
+    fread_unlocked;
+    fwrite_unlocked;
     getentropy;
     getnetent;
     getprotoent;
diff --git a/libc/libc.arm64.map b/libc/libc.arm64.map
index 67fac4e..d464a0c 100644
--- a/libc/libc.arm64.map
+++ b/libc/libc.arm64.map
@@ -1246,6 +1246,13 @@
     endnetent;
     endprotoent;
     fexecve;
+    fflush_unlocked;
+    fgetc_unlocked;
+    fgets_unlocked;
+    fputc_unlocked;
+    fputs_unlocked;
+    fread_unlocked;
+    fwrite_unlocked;
     getentropy;
     getnetent;
     getprotoent;
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index 443d18c..97965ac 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -1351,6 +1351,13 @@
     endnetent;
     endprotoent;
     fexecve;
+    fflush_unlocked;
+    fgetc_unlocked;
+    fgets_unlocked;
+    fputc_unlocked;
+    fputs_unlocked;
+    fread_unlocked;
+    fwrite_unlocked;
     getentropy;
     getnetent;
     getprotoent;
diff --git a/libc/libc.mips.map b/libc/libc.mips.map
index 0f5efff..930a1ca 100644
--- a/libc/libc.mips.map
+++ b/libc/libc.mips.map
@@ -1310,6 +1310,13 @@
     endnetent;
     endprotoent;
     fexecve;
+    fflush_unlocked;
+    fgetc_unlocked;
+    fgets_unlocked;
+    fputc_unlocked;
+    fputs_unlocked;
+    fread_unlocked;
+    fwrite_unlocked;
     getentropy;
     getnetent;
     getprotoent;
diff --git a/libc/libc.mips64.map b/libc/libc.mips64.map
index 67fac4e..d464a0c 100644
--- a/libc/libc.mips64.map
+++ b/libc/libc.mips64.map
@@ -1246,6 +1246,13 @@
     endnetent;
     endprotoent;
     fexecve;
+    fflush_unlocked;
+    fgetc_unlocked;
+    fgets_unlocked;
+    fputc_unlocked;
+    fputs_unlocked;
+    fread_unlocked;
+    fwrite_unlocked;
     getentropy;
     getnetent;
     getprotoent;
diff --git a/libc/libc.x86.map b/libc/libc.x86.map
index a802aa1..27b9743 100644
--- a/libc/libc.x86.map
+++ b/libc/libc.x86.map
@@ -1308,6 +1308,13 @@
     endnetent;
     endprotoent;
     fexecve;
+    fflush_unlocked;
+    fgetc_unlocked;
+    fgets_unlocked;
+    fputc_unlocked;
+    fputs_unlocked;
+    fread_unlocked;
+    fwrite_unlocked;
     getentropy;
     getnetent;
     getprotoent;
diff --git a/libc/libc.x86_64.map b/libc/libc.x86_64.map
index 67fac4e..d464a0c 100644
--- a/libc/libc.x86_64.map
+++ b/libc/libc.x86_64.map
@@ -1246,6 +1246,13 @@
     endnetent;
     endprotoent;
     fexecve;
+    fflush_unlocked;
+    fgetc_unlocked;
+    fgets_unlocked;
+    fputc_unlocked;
+    fputs_unlocked;
+    fread_unlocked;
+    fwrite_unlocked;
     getentropy;
     getnetent;
     getprotoent;
diff --git a/libc/private/bionic_asm.h b/libc/private/bionic_asm.h
index 92f38aa..30842f4 100644
--- a/libc/private/bionic_asm.h
+++ b/libc/private/bionic_asm.h
@@ -78,6 +78,9 @@
     ENTRY_NO_DWARF(f); \
     .hidden f \
 
+#define __BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(f) \
+    .weak f; \
+
 #define ALIAS_SYMBOL(alias, original) \
     .globl alias; \
     .equ alias, original
diff --git a/libc/private/bionic_defs.h b/libc/private/bionic_defs.h
index d9e016c..1d4f86b 100644
--- a/libc/private/bionic_defs.h
+++ b/libc/private/bionic_defs.h
@@ -34,5 +34,6 @@
  * by native bridge implementation.
  */
 #define __BIONIC_WEAK_FOR_NATIVE_BRIDGE __attribute__((__weak__, __noinline__))
+#define __BIONIC_WEAK_VARIABLE_FOR_NATIVE_BRIDGE __attribute__((__weak__))
 
 #endif /* __BIONIC_PRIVATE_BIONIC_DEFS_H_ */
diff --git a/libc/private/bionic_fortify.h b/libc/private/bionic_fortify.h
index 8591117..7f22963 100644
--- a/libc/private/bionic_fortify.h
+++ b/libc/private/bionic_fortify.h
@@ -26,6 +26,8 @@
  * SUCH DAMAGE.
  */
 
+#pragma once
+
 #include <poll.h> // For struct pollfd.
 #include <stdarg.h>
 #include <stdlib.h>
diff --git a/libc/stdio/fread.c b/libc/stdio/fread.c
deleted file mode 100644
index b6a3077..0000000
--- a/libc/stdio/fread.c
+++ /dev/null
@@ -1,141 +0,0 @@
-/*	$OpenBSD: fread.c,v 1.12 2014/05/01 16:40:36 deraadt Exp $ */
-/*-
- * Copyright (c) 1990, 1993
- *	The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Chris Torek.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. 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.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 <stdio.h>
-#include <string.h>
-#include <stdint.h>
-#include <errno.h>
-#include <sys/param.h>
-#include "local.h"
-
-#define MUL_NO_OVERFLOW	(1UL << (sizeof(size_t) * 4))
-
-size_t
-fread(void *buf, size_t size, size_t count, FILE *fp) __overloadable
-{
-	CHECK_FP(fp);
-
-	/*
-	 * Extension:  Catch integer overflow.
-	 */
-	if ((size >= MUL_NO_OVERFLOW || count >= MUL_NO_OVERFLOW) &&
-	    size > 0 && SIZE_MAX / size < count) {
-		errno = EOVERFLOW;
-		fp->_flags |= __SERR;
-		return (0);
-	}
-
-	const size_t desired_total = count * size;
-	size_t total = desired_total;
-
-	/*
-	 * ANSI and SUSv2 require a return value of 0 if size or count are 0.
-	 */
-	if (total == 0) {
-		return (0);
-	}
-
-	FLOCKFILE(fp);
-	_SET_ORIENTATION(fp, -1);
-
-	// TODO: how can this ever happen?!
-	if (fp->_r < 0)
-		fp->_r = 0;
-
-	/*
-	 * Ensure _bf._size is valid.
-	 */
-	if (fp->_bf._base == NULL) {
-		__smakebuf(fp);
-	}
-
-	char* dst = buf;
-
-	while (total > 0) {
-		/*
-		 * Copy data out of the buffer.
-		 */
-		size_t buffered_bytes = MIN((size_t) fp->_r, total);
-		memcpy(dst, fp->_p, buffered_bytes);
-		fp->_p += buffered_bytes;
-		fp->_r -= buffered_bytes;
-		dst += buffered_bytes;
-		total -= buffered_bytes;
-
-		/*
-		 * Are we done?
-		 */
-		if (total == 0) {
-			goto out;
-		}
-
-		/*
-		 * Do we have so much more to read that we should
-		 * avoid copying it through the buffer?
-		 */
-		if (total > (size_t) fp->_bf._size) {
-			/*
-			 * Make sure that fseek doesn't think it can
-			 * reuse the buffer since we are going to read
-			 * directly from the file descriptor.
-			 */
-			fp->_flags |= __SMOD;
-			break;
-		}
-
-		/*
-		 * Less than a buffer to go, so refill the buffer and
-		 * go around the loop again.
-		 */
-		if (__srefill(fp)) {
-			goto out;
-		}
-	}
-
-	/*
-	 * Read directly into the caller's buffer.
-	 */
-	while (total > 0) {
-		ssize_t bytes_read = (*fp->_read)(fp->_cookie, dst, total);
-		if (bytes_read <= 0) {
-			fp->_flags |= (bytes_read == 0) ? __SEOF : __SERR;
-			break;
-		}
-		dst += bytes_read;
-		total -= bytes_read;
-	}
-
-out:
-	FUNLOCKFILE(fp);
-	return ((desired_total - total) / size);
-}
diff --git a/libc/stdio/local.h b/libc/stdio/local.h
index 02ea8f8..155c70b 100644
--- a/libc/stdio/local.h
+++ b/libc/stdio/local.h
@@ -39,7 +39,9 @@
 #include <stdbool.h>
 #include <wchar.h>
 
-#include <async_safe/log.h>
+#if defined(__cplusplus) // Until we fork all of stdio...
+#include "private/bionic_fortify.h"
+#endif
 
 #include "wcio.h"
 
@@ -143,11 +145,12 @@
 // #define __SOPT 0x0400 --- historical (do fseek() optimization).
 // #define __SNPT 0x0800 --- historical (do not do fseek() optimization).
 // #define __SOFF 0x1000 --- historical (set iff _offset is in fact correct).
-#define __SMOD 0x2000  // true => fgetln modified _p text.
+// #define __SMOD 0x2000 --- historical (set iff fgetln modified _p text).
 #define __SALC 0x4000  // Allocate string space dynamically.
 #define __SIGN 0x8000  // Ignore this file in _fwalk.
 
 // TODO: remove remaining references to these obsolete flags (see above).
+#define __SMOD 0
 #define __SNPT 0
 #define __SOPT 0
 
@@ -231,9 +234,6 @@
 #define FLOCKFILE(fp)   if (!_EXT(fp)->_caller_handles_locking) flockfile(fp)
 #define FUNLOCKFILE(fp) if (!_EXT(fp)->_caller_handles_locking) funlockfile(fp)
 
-#define FLOATING_POINT
-#define PRINTF_WIDE_CHAR
-#define SCANF_WIDE_CHAR
 #define NO_PRINTF_PERCENT_N
 
 /* OpenBSD exposes these in <stdio.h>, but we only want them exposed to the implementation. */
@@ -241,10 +241,18 @@
 #define __sclearerr(p) ((void)((p)->_flags &= ~(__SERR|__SEOF)))
 #define __sgetc(p) (--(p)->_r < 0 ? __srget(p) : (int)(*(p)->_p++))
 
-/* OpenBSD declares these in fvwrite.h but we want to ensure they're hidden. */
-struct __suio;
-extern int __sfvwrite(FILE *, struct __suio *);
-wint_t __fputwc_unlock(wchar_t wc, FILE *fp);
+/* OpenBSD declares these in fvwrite.h, but we share them with C++ parts of the implementation. */
+struct __siov {
+  void* iov_base;
+  size_t iov_len;
+};
+struct __suio {
+  struct __siov* uio_iov;
+  int uio_iovcnt;
+  size_t uio_resid;
+};
+int __sfvwrite(FILE*, struct __suio*);
+wint_t __fputwc_unlock(wchar_t wc, FILE* fp);
 
 /* Remove the if (!__sdidinit) __sinit() idiom from untouched upstream stdio code. */
 extern void __sinit(void); // Not actually implemented.
@@ -257,11 +265,6 @@
 
 // Sanity check a FILE* for nullptr, so we can emit a message while crashing
 // instead of doing a blind null-dereference.
-#define CHECK_FP(fp)                                                       \
-  do {                                                                     \
-    if (__predict_false(fp == 0)) {                                        \
-      async_safe_fatal("invalid FILE* %p passed to %s", fp, __FUNCTION__); \
-    }                                                                      \
-  } while (0)
+#define CHECK_FP(fp) if (fp == nullptr) __fortify_fatal("%s: null FILE*", __FUNCTION__)
 
 #endif
diff --git a/libc/stdio/refill.c b/libc/stdio/refill.c
index a7c4bff..1df4191 100644
--- a/libc/stdio/refill.c
+++ b/libc/stdio/refill.c
@@ -111,7 +111,6 @@
 	}
 	fp->_p = fp->_bf._base;
 	fp->_r = (*fp->_read)(fp->_cookie, (char *)fp->_p, fp->_bf._size);
-	fp->_flags &= ~__SMOD;	/* buffer contents are again pristine */
 	if (fp->_r <= 0) {
 		if (fp->_r == 0)
 			fp->_flags |= __SEOF;
diff --git a/libc/stdio/stdio.cpp b/libc/stdio/stdio.cpp
index cf97a3f..c10bc00 100644
--- a/libc/stdio/stdio.cpp
+++ b/libc/stdio/stdio.cpp
@@ -91,7 +91,7 @@
 FILE* stdout = &__sF[1];
 FILE* stderr = &__sF[2];
 
-struct glue __sglue = { NULL, 3, __sF };
+struct glue __sglue = { nullptr, 3, __sF };
 static struct glue* lastglue = &__sglue;
 
 class ScopedFileLock {
@@ -116,7 +116,7 @@
   glue* g = reinterpret_cast<glue*>(data);
   FILE* p = reinterpret_cast<FILE*>(ALIGN(data + sizeof(*g)));
   __sfileext* pext = reinterpret_cast<__sfileext*>(ALIGN(data + sizeof(*g)) + n * sizeof(FILE));
-  g->next = NULL;
+  g->next = nullptr;
   g->niobs = n;
   g->iobs = p;
   while (--n >= 0) {
@@ -144,7 +144,7 @@
 	struct glue *g;
 
 	_THREAD_PRIVATE_MUTEX_LOCK(__sfp_mutex);
-	for (g = &__sglue; g != NULL; g = g->next) {
+	for (g = &__sglue; g != nullptr; g = g->next) {
 		for (fp = g->iobs, n = g->niobs; --n >= 0; fp++)
 			if (fp->_flags == 0)
 				goto found;
@@ -152,8 +152,7 @@
 
 	/* release lock while mallocing */
 	_THREAD_PRIVATE_MUTEX_UNLOCK(__sfp_mutex);
-	if ((g = moreglue(NDYNAMIC)) == NULL)
-		return (NULL);
+	if ((g = moreglue(NDYNAMIC)) == nullptr) return nullptr;
 	_THREAD_PRIVATE_MUTEX_LOCK(__sfp_mutex);
 	lastglue->next = g;
 	lastglue = g;
@@ -161,15 +160,15 @@
 found:
 	fp->_flags = 1;		/* reserve this slot; caller sets real flags */
 	_THREAD_PRIVATE_MUTEX_UNLOCK(__sfp_mutex);
-	fp->_p = NULL;		/* no current pointer */
+	fp->_p = nullptr;		/* no current pointer */
 	fp->_w = 0;		/* nothing to read or write */
 	fp->_r = 0;
-	fp->_bf._base = NULL;	/* no buffer */
+	fp->_bf._base = nullptr;	/* no buffer */
 	fp->_bf._size = 0;
 	fp->_lbfsize = 0;	/* not line buffered */
 	fp->_file = -1;		/* no file */
 
-	fp->_lb._base = NULL;	/* no line buffer */
+	fp->_lb._base = nullptr;	/* no line buffer */
 	fp->_lb._size = 0;
 	_FILEEXT_INIT(fp);
 
@@ -288,8 +287,8 @@
     // Flush the stream; ANSI doesn't require this.
     if (fp->_flags & __SWR) __sflush(fp);
 
-    // If close is NULL, closing is a no-op, hence pointless.
-    isopen = fp->_close != NULL;
+    // If close is null, closing is a no-op, hence pointless.
+    isopen = (fp->_close != nullptr);
     if ((wantfd = fp->_file) < 0 && isopen) {
         (*fp->_close)(fp->_cookie);
         isopen = 0;
@@ -316,8 +315,8 @@
   if (fp->_flags & __SMBF) free(fp->_bf._base);
   fp->_w = 0;
   fp->_r = 0;
-  fp->_p = NULL;
-  fp->_bf._base = NULL;
+  fp->_p = nullptr;
+  fp->_bf._base = nullptr;
   fp->_bf._size = 0;
   fp->_lbfsize = 0;
   if (HASUB(fp)) FREEUB(fp);
@@ -374,7 +373,7 @@
   ScopedFileLock sfl(fp);
   WCIO_FREE(fp);
   int r = fp->_flags & __SWR ? __sflush(fp) : 0;
-  if (fp->_close != NULL && (*fp->_close)(fp->_cookie) < 0) {
+  if (fp->_close != nullptr && (*fp->_close)(fp->_cookie) < 0) {
     r = EOF;
   }
   if (fp->_flags & __SMBF) free(fp->_bf._base);
@@ -438,6 +437,36 @@
   return ferror_unlocked(fp);
 }
 
+int __sflush(FILE* fp) {
+  // Flushing a read-only file is a no-op.
+  if ((fp->_flags & __SWR) == 0) return 0;
+
+  // Flushing a file without a buffer is a no-op.
+  unsigned char* p = fp->_bf._base;
+  if (p == nullptr) return 0;
+
+  // Set these immediately to avoid problems with longjmp and to allow
+  // exchange buffering (via setvbuf) in user write function.
+  int n = fp->_p - p;
+  fp->_p = p;
+  fp->_w = (fp->_flags & (__SLBF|__SNBF)) ? 0 : fp->_bf._size;
+
+  while (n > 0) {
+    int written = (*fp->_write)(fp->_cookie, reinterpret_cast<char*>(p), n);
+    if (written <= 0) {
+      fp->_flags |= __SERR;
+      return EOF;
+    }
+    n -= written, p += written;
+  }
+  return 0;
+}
+
+int __sflush_locked(FILE* fp) {
+  ScopedFileLock sfl(fp);
+  return __sflush(fp);
+}
+
 int __sread(void* cookie, char* buf, int n) {
   FILE* fp = reinterpret_cast<FILE*>(cookie);
   return TEMP_FAILURE_RETRY(read(fp->_file, buf, n));
@@ -495,7 +524,7 @@
     // smaller than that in the underlying object.
     result -= fp->_r;
     if (HASUB(fp)) result -= fp->_ur;
-  } else if (fp->_flags & __SWR && fp->_p != NULL) {
+  } else if (fp->_flags & __SWR && fp->_p != nullptr) {
     // Writing.  Any buffered characters cause the
     // position to be greater than that in the
     // underlying object.
@@ -527,7 +556,7 @@
     return -1;
   }
 
-  if (fp->_bf._base == NULL) __smakebuf(fp);
+  if (fp->_bf._base == nullptr) __smakebuf(fp);
 
   // Flush unwritten data and attempt the seek.
   if (__sflush(fp) || __seek_unlocked(fp, offset, whence) == -1) {
@@ -670,11 +699,90 @@
   return getc(fp);
 }
 
+int fgetc_unlocked(FILE* fp) {
+  CHECK_FP(fp);
+  return getc_unlocked(fp);
+}
+
+/*
+ * Read at most n-1 characters from the given file.
+ * Stop when a newline has been read, or the count runs out.
+ * Return first argument, or NULL if no characters were read.
+ * Do not return NULL if n == 1.
+ */
+char* fgets(char* buf, int n, FILE* fp) __overloadable {
+  CHECK_FP(fp);
+  ScopedFileLock sfl(fp);
+  return fgets_unlocked(buf, n, fp);
+}
+
+char* fgets_unlocked(char* buf, int n, FILE* fp) {
+  if (n <= 0) {
+    errno = EINVAL;
+    return nullptr;
+  }
+
+  _SET_ORIENTATION(fp, -1);
+
+  char* s = buf;
+  n--; // Leave space for NUL.
+  while (n != 0) {
+    // If the buffer is empty, refill it.
+    if (fp->_r <= 0) {
+      if (__srefill(fp)) {
+        // EOF/error: stop with partial or no line.
+        if (s == buf) return nullptr;
+        break;
+      }
+    }
+    size_t len = fp->_r;
+    unsigned char* p = fp->_p;
+
+    // Scan through at most n bytes of the current buffer,
+    // looking for '\n'.  If found, copy up to and including
+    // newline, and stop.  Otherwise, copy entire chunk and loop.
+    if (len > static_cast<size_t>(n)) len = n;
+    unsigned char* t = static_cast<unsigned char*>(memchr(p, '\n', len));
+    if (t != nullptr) {
+      len = ++t - p;
+      fp->_r -= len;
+      fp->_p = t;
+      memcpy(s, p, len);
+      s[len] = '\0';
+      return buf;
+    }
+    fp->_r -= len;
+    fp->_p += len;
+    memcpy(s, p, len);
+    s += len;
+    n -= len;
+  }
+  *s = '\0';
+  return buf;
+}
+
 int fputc(int c, FILE* fp) {
   CHECK_FP(fp);
   return putc(c, fp);
 }
 
+int fputc_unlocked(int c, FILE* fp) {
+  CHECK_FP(fp);
+  return putc_unlocked(c, fp);
+}
+
+int fputs(const char* s, FILE* fp) {
+  CHECK_FP(fp);
+  ScopedFileLock sfl(fp);
+  return fputs_unlocked(s, fp);
+}
+
+int fputs_unlocked(const char* s, FILE* fp) {
+  CHECK_FP(fp);
+  size_t length = strlen(s);
+  return (fwrite_unlocked(s, 1, length, fp) == length) ? 0 : EOF;
+}
+
 int fscanf(FILE* fp, const char* fmt, ...) {
   CHECK_FP(fp);
   PRINTF_IMPL(vfscanf(fp, fmt, ap));
@@ -723,6 +831,11 @@
   return fgetwc(stdin);
 }
 
+void perror(const char* msg) {
+  if (msg == nullptr) msg = "";
+  fprintf(stderr, "%s%s%s\n", msg, (*msg == '\0') ? "" : ": ", strerror(errno));
+}
+
 int printf(const char* fmt, ...) {
   PRINTF_IMPL(vfprintf(stdout, fmt, ap));
 }
@@ -754,6 +867,13 @@
   return putc_unlocked(c, stdout);
 }
 
+int puts(const char* s) {
+  size_t length = strlen(s);
+  ScopedFileLock sfl(stdout);
+  return (fwrite_unlocked(s, 1, length, stdout) == length &&
+          putc_unlocked('\n', stdout) != EOF) ? 0 : EOF;
+}
+
 wint_t putwc(wchar_t wc, FILE* fp) {
   CHECK_FP(fp);
   return fputwc(wc, fp);
@@ -869,6 +989,116 @@
   PRINTF_IMPL(vfwscanf(stdin, fmt, ap));
 }
 
+static int fflush_all() {
+  return _fwalk(__sflush_locked);
+}
+
+int fflush(FILE* fp) {
+  if (fp == nullptr) return fflush_all();
+  ScopedFileLock sfl(fp);
+  return fflush_unlocked(fp);
+}
+
+int fflush_unlocked(FILE* fp) {
+  if (fp == nullptr) return fflush_all();
+  if ((fp->_flags & (__SWR | __SRW)) == 0) {
+    errno = EBADF;
+    return EOF;
+  }
+  return __sflush(fp);
+}
+
+size_t fread(void* buf, size_t size, size_t count, FILE* fp) __overloadable {
+  CHECK_FP(fp);
+  ScopedFileLock sfl(fp);
+  return fread_unlocked(buf, size, count, fp);
+}
+
+size_t fread_unlocked(void* buf, size_t size, size_t count, FILE* fp) {
+  CHECK_FP(fp);
+
+  size_t desired_total;
+  if (__builtin_mul_overflow(size, count, &desired_total)) {
+    errno = EOVERFLOW;
+    fp->_flags |= __SERR;
+    return 0;
+  }
+
+  size_t total = desired_total;
+  if (total == 0) return 0;
+
+  _SET_ORIENTATION(fp, -1);
+
+  // TODO: how can this ever happen?!
+  if (fp->_r < 0) fp->_r = 0;
+
+  // Ensure _bf._size is valid.
+  if (fp->_bf._base == nullptr) __smakebuf(fp);
+
+  char* dst = static_cast<char*>(buf);
+
+  while (total > 0) {
+    // Copy data out of the buffer.
+    size_t buffered_bytes = MIN(static_cast<size_t>(fp->_r), total);
+    memcpy(dst, fp->_p, buffered_bytes);
+    fp->_p += buffered_bytes;
+    fp->_r -= buffered_bytes;
+    dst += buffered_bytes;
+    total -= buffered_bytes;
+
+    // Are we done?
+    if (total == 0) goto out;
+
+    // Do we have so much more to read that we should avoid copying it through the buffer?
+    if (total > static_cast<size_t>(fp->_bf._size)) break;
+
+    // Less than a buffer to go, so refill the buffer and go around the loop again.
+    if (__srefill(fp)) goto out;
+  }
+
+  // Read directly into the caller's buffer.
+  while (total > 0) {
+    ssize_t bytes_read = (*fp->_read)(fp->_cookie, dst, total);
+    if (bytes_read <= 0) {
+      fp->_flags |= (bytes_read == 0) ? __SEOF : __SERR;
+      break;
+    }
+    dst += bytes_read;
+    total -= bytes_read;
+  }
+
+out:
+  return ((desired_total - total) / size);
+}
+
+size_t fwrite(const void* buf, size_t size, size_t count, FILE* fp) {
+  CHECK_FP(fp);
+  ScopedFileLock sfl(fp);
+  return fwrite_unlocked(buf, size, count, fp);
+}
+
+size_t fwrite_unlocked(const void* buf, size_t size, size_t count, FILE* fp) {
+  CHECK_FP(fp);
+
+  size_t n;
+  if (__builtin_mul_overflow(size, count, &n)) {
+    errno = EOVERFLOW;
+    fp->_flags |= __SERR;
+    return 0;
+  }
+
+  if (n == 0) return 0;
+
+  __siov iov = { .iov_base = const_cast<void*>(buf), .iov_len = n };
+  __suio uio = { .uio_iov = &iov, .uio_iovcnt = 1, .uio_resid = n };
+
+  _SET_ORIENTATION(fp, -1);
+
+  // The usual case is success (__sfvwrite returns 0); skip the divide if this happens,
+  // since divides are generally slow.
+  return (__sfvwrite(fp, &uio) == 0) ? count : ((n - uio.uio_resid) / size);
+}
+
 namespace {
 
 namespace phony {
diff --git a/libc/stdio/vfprintf.c b/libc/stdio/vfprintf.c
new file mode 100644
index 0000000..5713b44
--- /dev/null
+++ b/libc/stdio/vfprintf.c
@@ -0,0 +1,1467 @@
+/*	$OpenBSD: vfprintf.c,v 1.71 2016/01/04 15:47:47 schwarze Exp $	*/
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ */
+
+/*
+ * Actual printf innards.
+ *
+ * This code is large and complicated...
+ */
+
+#include <sys/mman.h>
+#include <sys/types.h>
+
+#include <errno.h>
+#include <langinfo.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <wchar.h>
+
+#include "fvwrite.h"
+#include "local.h"
+
+union arg {
+  int intarg;
+  unsigned int uintarg;
+  long longarg;
+  unsigned long ulongarg;
+  long long longlongarg;
+  unsigned long long ulonglongarg;
+  ptrdiff_t ptrdiffarg;
+  size_t sizearg;
+  ssize_t ssizearg;
+  intmax_t intmaxarg;
+  uintmax_t uintmaxarg;
+  void* pvoidarg;
+  char* pchararg;
+  signed char* pschararg;
+  short* pshortarg;
+  int* pintarg;
+  long* plongarg;
+  long long* plonglongarg;
+  ptrdiff_t* pptrdiffarg;
+  ssize_t* pssizearg;
+  intmax_t* pintmaxarg;
+  double doublearg;
+  long double longdoublearg;
+  wint_t wintarg;
+  wchar_t* pwchararg;
+};
+
+static int __find_arguments(const char* fmt0, va_list ap, union arg** argtable, size_t* argtablesiz);
+static int __grow_type_table(unsigned char** typetable, int* tablesize);
+
+/*
+ * Flush out all the vectors defined by the given uio,
+ * then reset it so that it can be reused.
+ */
+static int __sprint(FILE* fp, struct __suio* uio) {
+  int err;
+
+  if (uio->uio_resid == 0) {
+    uio->uio_iovcnt = 0;
+    return (0);
+  }
+  err = __sfvwrite(fp, uio);
+  uio->uio_resid = 0;
+  uio->uio_iovcnt = 0;
+  return (err);
+}
+
+/*
+ * Helper function for `fprintf to unbuffered unix file': creates a
+ * temporary buffer.  We only work on write-only files; this avoids
+ * worries about ungetc buffers and so forth.
+ */
+static int __sbprintf(FILE* fp, const char* fmt, va_list ap) {
+  int ret;
+  FILE fake;
+  struct __sfileext fakeext;
+  unsigned char buf[BUFSIZ];
+
+  _FILEEXT_SETUP(&fake, &fakeext);
+  /* copy the important variables */
+  fake._flags = fp->_flags & ~__SNBF;
+  fake._file = fp->_file;
+  fake._cookie = fp->_cookie;
+  fake._write = fp->_write;
+
+  /* set up the buffer */
+  fake._bf._base = fake._p = buf;
+  fake._bf._size = fake._w = sizeof(buf);
+  fake._lbfsize = 0; /* not actually used, but Just In Case */
+
+  /* do the work, then copy any error status */
+  ret = __vfprintf(&fake, fmt, ap);
+  if (ret >= 0 && __sflush(&fake)) ret = EOF;
+  if (fake._flags & __SERR) fp->_flags |= __SERR;
+  return (ret);
+}
+
+/*
+ * Convert a wide character string argument for the %ls format to a multibyte
+ * string representation. If not -1, prec specifies the maximum number of
+ * bytes to output, and also means that we can't assume that the wide char
+ * string is null-terminated.
+ */
+static char* __wcsconv(wchar_t* wcsarg, int prec) {
+  mbstate_t mbs;
+  char buf[MB_LEN_MAX];
+  wchar_t* p;
+  char* convbuf;
+  size_t clen, nbytes;
+
+  /* Allocate space for the maximum number of bytes we could output. */
+  if (prec < 0) {
+    memset(&mbs, 0, sizeof(mbs));
+    p = wcsarg;
+    nbytes = wcsrtombs(NULL, (const wchar_t**)&p, 0, &mbs);
+    if (nbytes == (size_t)-1) return (NULL);
+  } else {
+    /*
+     * Optimisation: if the output precision is small enough,
+     * just allocate enough memory for the maximum instead of
+     * scanning the string.
+     */
+    if (prec < 128)
+      nbytes = prec;
+    else {
+      nbytes = 0;
+      p = wcsarg;
+      memset(&mbs, 0, sizeof(mbs));
+      for (;;) {
+        clen = wcrtomb(buf, *p++, &mbs);
+        if (clen == 0 || clen == (size_t)-1 || nbytes + clen > (size_t)prec) break;
+        nbytes += clen;
+      }
+      if (clen == (size_t)-1) return (NULL);
+    }
+  }
+  if ((convbuf = malloc(nbytes + 1)) == NULL) return (NULL);
+
+  /* Fill the output buffer. */
+  p = wcsarg;
+  memset(&mbs, 0, sizeof(mbs));
+  if ((nbytes = wcsrtombs(convbuf, (const wchar_t**)&p, nbytes, &mbs)) == (size_t)-1) {
+    free(convbuf);
+    return (NULL);
+  }
+  convbuf[nbytes] = '\0';
+  return (convbuf);
+}
+
+#include <float.h>
+#include <locale.h>
+#include <math.h>
+#include "floatio.h"
+#include "gdtoa.h"
+
+#define DEFPREC 6
+
+static int exponent(char*, int, int);
+
+/*
+ * The size of the buffer we use as scratch space for integer
+ * conversions, among other things.  Technically, we would need the
+ * most space for base 10 conversions with thousands' grouping
+ * characters between each pair of digits.  100 bytes is a
+ * conservative overestimate even for a 128-bit uintmax_t.
+ */
+#define BUF 100
+
+#define STATIC_ARG_TBL_SIZE 8 /* Size of static argument table. */
+
+/*
+ * Macros for converting digits to letters and vice versa
+ */
+#define to_digit(c) ((c) - '0')
+#define is_digit(c) ((unsigned)to_digit(c) <= 9)
+#define to_char(n) ((n) + '0')
+
+/*
+ * Flags used during conversion.
+ */
+#define ALT 0x0001      /* alternate form */
+#define LADJUST 0x0004  /* left adjustment */
+#define LONGDBL 0x0008  /* long double */
+#define LONGINT 0x0010  /* long integer */
+#define LLONGINT 0x0020 /* long long integer */
+#define SHORTINT 0x0040 /* short integer */
+#define ZEROPAD 0x0080  /* zero (as opposed to blank) pad */
+#define FPT 0x0100      /* Floating point number */
+#define PTRINT 0x0200   /* (unsigned) ptrdiff_t */
+#define SIZEINT 0x0400  /* (signed) size_t */
+#define CHARINT 0x0800  /* 8 bit integer */
+#define MAXINT 0x1000   /* largest integer size (intmax_t) */
+
+int vfprintf(FILE* fp, const char* fmt0, __va_list ap) {
+  int ret;
+
+  FLOCKFILE(fp);
+  ret = __vfprintf(fp, fmt0, ap);
+  FUNLOCKFILE(fp);
+  return (ret);
+}
+DEF_STRONG(vfprintf);
+
+int __vfprintf(FILE* fp, const char* fmt0, __va_list ap) {
+  char* fmt;           /* format string */
+  int ch;              /* character from fmt */
+  int n, n2;           /* handy integers (short term usage) */
+  char* cp;            /* handy char pointer (short term usage) */
+  struct __siov* iovp; /* for PRINT macro */
+  int flags;           /* flags as above */
+  int ret;             /* return value accumulator */
+  int width;           /* width from format (%8d), or 0 */
+  int prec;            /* precision from format; <0 for N/A */
+  char sign;           /* sign prefix (' ', '+', '-', or \0) */
+  wchar_t wc;
+  mbstate_t ps;
+  /*
+   * We can decompose the printed representation of floating
+   * point numbers into several parts, some of which may be empty:
+   *
+   * [+|-| ] [0x|0X] MMM . NNN [e|E|p|P] [+|-] ZZ
+   *    A       B     ---C---      D       E   F
+   *
+   * A:	'sign' holds this value if present; '\0' otherwise
+   * B:	ox[1] holds the 'x' or 'X'; '\0' if not hexadecimal
+   * C:	cp points to the string MMMNNN.  Leading and trailing
+   *	zeros are not in the string and must be added.
+   * D:	expchar holds this character; '\0' if no exponent, e.g. %f
+   * F:	at least two digits for decimal, at least one digit for hex
+   */
+  char* decimal_point = NULL;
+  int signflag; /* true if float is negative */
+  union {       /* floating point arguments %[aAeEfFgG] */
+    double dbl;
+    long double ldbl;
+  } fparg;
+  int expt;                   /* integer value of exponent */
+  char expchar;               /* exponent character: [eEpP\0] */
+  char* dtoaend;              /* pointer to end of converted digits */
+  int expsize;                /* character count for expstr */
+  int lead;                   /* sig figs before decimal or group sep */
+  int ndig;                   /* actual number of digits returned by dtoa */
+  char expstr[MAXEXPDIG + 2]; /* buffer for exponent string: e+ZZZ */
+  char* dtoaresult = NULL;
+
+  uintmax_t _umax;             /* integer arguments %[diouxX] */
+  enum { OCT, DEC, HEX } base; /* base for %[diouxX] conversion */
+  int dprec;                   /* a copy of prec if %[diouxX], 0 otherwise */
+  int realsz;                  /* field size expanded by dprec */
+  int size;                    /* size of converted field or string */
+  const char* xdigs;           /* digits for %[xX] conversion */
+#define NIOV 8
+  struct __suio uio;       /* output information: summary */
+  struct __siov iov[NIOV]; /* ... and individual io vectors */
+  char buf[BUF];           /* buffer with space for digits of uintmax_t */
+  char ox[2];              /* space for 0x; ox[1] is either x, X, or \0 */
+  union arg* argtable;     /* args, built due to positional arg */
+  union arg statargtable[STATIC_ARG_TBL_SIZE];
+  size_t argtablesiz;
+  int nextarg;   /* 1-based argument index */
+  va_list orgap; /* original argument pointer */
+  char* convbuf; /* buffer for wide to multi-byte conversion */
+
+  /*
+   * Choose PADSIZE to trade efficiency vs. size.  If larger printf
+   * fields occur frequently, increase PADSIZE and make the initialisers
+   * below longer.
+   */
+#define PADSIZE 16 /* pad chunk size */
+  static char blanks[PADSIZE] = { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
+                                  ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' };
+  static char zeroes[PADSIZE] = { '0', '0', '0', '0', '0', '0', '0', '0',
+                                  '0', '0', '0', '0', '0', '0', '0', '0' };
+
+  static const char xdigs_lower[16] = "0123456789abcdef";
+  static const char xdigs_upper[16] = "0123456789ABCDEF";
+
+  /*
+   * BEWARE, these `goto error' on error, and PAD uses `n'.
+   */
+#define PRINT(ptr, len)                   \
+  do {                                    \
+    iovp->iov_base = (ptr);               \
+    iovp->iov_len = (len);                \
+    uio.uio_resid += (len);               \
+    iovp++;                               \
+    if (++uio.uio_iovcnt >= NIOV) {       \
+      if (__sprint(fp, &uio)) goto error; \
+      iovp = iov;                         \
+    }                                     \
+  } while (0)
+#define PAD(howmany, with)     \
+  do {                         \
+    if ((n = (howmany)) > 0) { \
+      while (n > PADSIZE) {    \
+        PRINT(with, PADSIZE);  \
+        n -= PADSIZE;          \
+      }                        \
+      PRINT(with, n);          \
+    }                          \
+  } while (0)
+#define PRINTANDPAD(p, ep, len, with)       \
+  do {                                      \
+    n2 = (ep) - (p);                        \
+    if (n2 > (len)) n2 = (len);             \
+    if (n2 > 0) PRINT((p), n2);             \
+    PAD((len) - (n2 > 0 ? n2 : 0), (with)); \
+  } while (0)
+#define FLUSH()                                          \
+  do {                                                   \
+    if (uio.uio_resid && __sprint(fp, &uio)) goto error; \
+    uio.uio_iovcnt = 0;                                  \
+    iovp = iov;                                          \
+  } while (0)
+
+  /*
+   * To extend shorts properly, we need both signed and unsigned
+   * argument extraction methods.
+   */
+#define SARG()                                                                               \
+  ((intmax_t)(flags & MAXINT                                                                 \
+                  ? GETARG(intmax_t)                                                         \
+                  : flags & LLONGINT                                                         \
+                        ? GETARG(long long)                                                  \
+                        : flags & LONGINT                                                    \
+                              ? GETARG(long)                                                 \
+                              : flags & PTRINT                                               \
+                                    ? GETARG(ptrdiff_t)                                      \
+                                    : flags & SIZEINT                                        \
+                                          ? GETARG(ssize_t)                                  \
+                                          : flags & SHORTINT                                 \
+                                                ? (short)GETARG(int)                         \
+                                                : flags & CHARINT ? (signed char)GETARG(int) \
+                                                                  : GETARG(int)))
+#define UARG()                                                                                \
+  ((uintmax_t)(flags & MAXINT                                                                 \
+                   ? GETARG(uintmax_t)                                                        \
+                   : flags & LLONGINT                                                         \
+                         ? GETARG(unsigned long long)                                         \
+                         : flags & LONGINT                                                    \
+                               ? GETARG(unsigned long)                                        \
+                               : flags & PTRINT ? (uintptr_t)GETARG(ptrdiff_t) : /* XXX */    \
+                                     flags & SIZEINT                                          \
+                                         ? GETARG(size_t)                                     \
+                                         : flags & SHORTINT                                   \
+                                               ? (unsigned short)GETARG(int)                  \
+                                               : flags & CHARINT ? (unsigned char)GETARG(int) \
+                                                                 : GETARG(unsigned int)))
+
+  /*
+   * Append a digit to a value and check for overflow.
+   */
+#define APPEND_DIGIT(val, dig)                            \
+  do {                                                    \
+    if ((val) > INT_MAX / 10) goto overflow;              \
+    (val) *= 10;                                          \
+    if ((val) > INT_MAX - to_digit((dig))) goto overflow; \
+    (val) += to_digit((dig));                             \
+  } while (0)
+
+  /*
+   * Get * arguments, including the form *nn$.  Preserve the nextarg
+   * that the argument can be gotten once the type is determined.
+   */
+#define GETASTER(val)                                                     \
+  n2 = 0;                                                                 \
+  cp = fmt;                                                               \
+  while (is_digit(*cp)) {                                                 \
+    APPEND_DIGIT(n2, *cp);                                                \
+    cp++;                                                                 \
+  }                                                                       \
+  if (*cp == '$') {                                                       \
+    int hold = nextarg;                                                   \
+    if (argtable == NULL) {                                               \
+      argtable = statargtable;                                            \
+      if (__find_arguments(fmt0, orgap, &argtable, &argtablesiz) == -1) { \
+        ret = -1;                                                         \
+        goto error;                                                       \
+      }                                                                   \
+    }                                                                     \
+    nextarg = n2;                                                         \
+    val = GETARG(int);                                                    \
+    nextarg = hold;                                                       \
+    fmt = ++cp;                                                           \
+  } else {                                                                \
+    val = GETARG(int);                                                    \
+  }
+
+/*
+ * Get the argument indexed by nextarg.   If the argument table is
+ * built, use it to get the argument.  If its not, get the next
+ * argument (and arguments must be gotten sequentially).
+ */
+#define GETARG(type) \
+  ((argtable != NULL) ? *((type*)(&argtable[nextarg++])) : (nextarg++, va_arg(ap, type)))
+
+  _SET_ORIENTATION(fp, -1);
+  /* sorry, fprintf(read_only_file, "") returns EOF, not 0 */
+  if (cantwrite(fp)) {
+    errno = EBADF;
+    return (EOF);
+  }
+
+  /* optimise fprintf(stderr) (and other unbuffered Unix files) */
+  if ((fp->_flags & (__SNBF | __SWR | __SRW)) == (__SNBF | __SWR) && fp->_file >= 0)
+    return (__sbprintf(fp, fmt0, ap));
+
+  fmt = (char*)fmt0;
+  argtable = NULL;
+  nextarg = 1;
+  va_copy(orgap, ap);
+  uio.uio_iov = iovp = iov;
+  uio.uio_resid = 0;
+  uio.uio_iovcnt = 0;
+  ret = 0;
+  convbuf = NULL;
+
+  memset(&ps, 0, sizeof(ps));
+  /*
+   * Scan the format for conversions (`%' character).
+   */
+  for (;;) {
+    cp = fmt;
+    while ((n = mbrtowc(&wc, fmt, MB_CUR_MAX, &ps)) > 0) {
+      fmt += n;
+      if (wc == '%') {
+        fmt--;
+        break;
+      }
+    }
+    if (n < 0) {
+      ret = -1;
+      goto error;
+    }
+    if (fmt != cp) {
+      ptrdiff_t m = fmt - cp;
+      if (m < 0 || m > INT_MAX - ret) goto overflow;
+      PRINT(cp, m);
+      ret += m;
+    }
+    if (n == 0) goto done;
+    fmt++; /* skip over '%' */
+
+    flags = 0;
+    dprec = 0;
+    width = 0;
+    prec = -1;
+    sign = '\0';
+    ox[1] = '\0';
+
+  rflag:
+    ch = *fmt++;
+  reswitch:
+    switch (ch) {
+      case ' ':
+        /*
+         * ``If the space and + flags both appear, the space
+         * flag will be ignored.''
+         *	-- ANSI X3J11
+         */
+        if (!sign) sign = ' ';
+        goto rflag;
+      case '#':
+        flags |= ALT;
+        goto rflag;
+      case '\'':
+        /* grouping not implemented */
+        goto rflag;
+      case '*':
+        /*
+         * ``A negative field width argument is taken as a
+         * - flag followed by a positive field width.''
+         *	-- ANSI X3J11
+         * They don't exclude field widths read from args.
+         */
+        GETASTER(width);
+        if (width >= 0) goto rflag;
+        if (width == INT_MIN) goto overflow;
+        width = -width;
+        /* FALLTHROUGH */
+      case '-':
+        flags |= LADJUST;
+        goto rflag;
+      case '+':
+        sign = '+';
+        goto rflag;
+      case '.':
+        if ((ch = *fmt++) == '*') {
+          GETASTER(n);
+          prec = n < 0 ? -1 : n;
+          goto rflag;
+        }
+        n = 0;
+        while (is_digit(ch)) {
+          APPEND_DIGIT(n, ch);
+          ch = *fmt++;
+        }
+        if (ch == '$') {
+          nextarg = n;
+          if (argtable == NULL) {
+            argtable = statargtable;
+            if (__find_arguments(fmt0, orgap, &argtable, &argtablesiz) == -1) {
+              ret = -1;
+              goto error;
+            }
+          }
+          goto rflag;
+        }
+        prec = n;
+        goto reswitch;
+      case '0':
+        /*
+         * ``Note that 0 is taken as a flag, not as the
+         * beginning of a field width.''
+         *	-- ANSI X3J11
+         */
+        flags |= ZEROPAD;
+        goto rflag;
+      case '1':
+      case '2':
+      case '3':
+      case '4':
+      case '5':
+      case '6':
+      case '7':
+      case '8':
+      case '9':
+        n = 0;
+        do {
+          APPEND_DIGIT(n, ch);
+          ch = *fmt++;
+        } while (is_digit(ch));
+        if (ch == '$') {
+          nextarg = n;
+          if (argtable == NULL) {
+            argtable = statargtable;
+            if (__find_arguments(fmt0, orgap, &argtable, &argtablesiz) == -1) {
+              ret = -1;
+              goto error;
+            }
+          }
+          goto rflag;
+        }
+        width = n;
+        goto reswitch;
+      case 'L':
+        flags |= LONGDBL;
+        goto rflag;
+      case 'h':
+        if (*fmt == 'h') {
+          fmt++;
+          flags |= CHARINT;
+        } else {
+          flags |= SHORTINT;
+        }
+        goto rflag;
+      case 'j':
+        flags |= MAXINT;
+        goto rflag;
+      case 'l':
+        if (*fmt == 'l') {
+          fmt++;
+          flags |= LLONGINT;
+        } else {
+          flags |= LONGINT;
+        }
+        goto rflag;
+      case 'q':
+        flags |= LLONGINT;
+        goto rflag;
+      case 't':
+        flags |= PTRINT;
+        goto rflag;
+      case 'z':
+        flags |= SIZEINT;
+        goto rflag;
+      case 'c':
+        if (flags & LONGINT) {
+          mbstate_t mbs;
+          size_t mbseqlen;
+
+          memset(&mbs, 0, sizeof(mbs));
+          mbseqlen = wcrtomb(buf, (wchar_t)GETARG(wint_t), &mbs);
+          if (mbseqlen == (size_t)-1) {
+            ret = -1;
+            goto error;
+          }
+          cp = buf;
+          size = (int)mbseqlen;
+        } else {
+          *(cp = buf) = GETARG(int);
+          size = 1;
+        }
+        sign = '\0';
+        break;
+      case 'D':
+        flags |= LONGINT;
+        /*FALLTHROUGH*/
+      case 'd':
+      case 'i':
+        _umax = SARG();
+        if ((intmax_t)_umax < 0) {
+          _umax = -_umax;
+          sign = '-';
+        }
+        base = DEC;
+        goto number;
+      case 'a':
+      case 'A':
+        if (ch == 'a') {
+          ox[1] = 'x';
+          xdigs = xdigs_lower;
+          expchar = 'p';
+        } else {
+          ox[1] = 'X';
+          xdigs = xdigs_upper;
+          expchar = 'P';
+        }
+        if (prec >= 0) prec++;
+        if (dtoaresult) __freedtoa(dtoaresult);
+        if (flags & LONGDBL) {
+          fparg.ldbl = GETARG(long double);
+          dtoaresult = cp = __hldtoa(fparg.ldbl, xdigs, prec, &expt, &signflag, &dtoaend);
+          if (dtoaresult == NULL) {
+            errno = ENOMEM;
+            goto error;
+          }
+        } else {
+          fparg.dbl = GETARG(double);
+          dtoaresult = cp = __hdtoa(fparg.dbl, xdigs, prec, &expt, &signflag, &dtoaend);
+          if (dtoaresult == NULL) {
+            errno = ENOMEM;
+            goto error;
+          }
+        }
+        if (prec < 0) prec = dtoaend - cp;
+        if (expt == INT_MAX) ox[1] = '\0';
+        goto fp_common;
+      case 'e':
+      case 'E':
+        expchar = ch;
+        if (prec < 0) /* account for digit before decpt */
+          prec = DEFPREC + 1;
+        else
+          prec++;
+        goto fp_begin;
+      case 'f':
+      case 'F':
+        expchar = '\0';
+        goto fp_begin;
+      case 'g':
+      case 'G':
+        expchar = ch - ('g' - 'e');
+        if (prec == 0) prec = 1;
+      fp_begin:
+        if (prec < 0) prec = DEFPREC;
+        if (dtoaresult) __freedtoa(dtoaresult);
+        if (flags & LONGDBL) {
+          fparg.ldbl = GETARG(long double);
+          dtoaresult = cp = __ldtoa(&fparg.ldbl, expchar ? 2 : 3, prec, &expt, &signflag, &dtoaend);
+          if (dtoaresult == NULL) {
+            errno = ENOMEM;
+            goto error;
+          }
+        } else {
+          fparg.dbl = GETARG(double);
+          dtoaresult = cp = __dtoa(fparg.dbl, expchar ? 2 : 3, prec, &expt, &signflag, &dtoaend);
+          if (dtoaresult == NULL) {
+            errno = ENOMEM;
+            goto error;
+          }
+          if (expt == 9999) expt = INT_MAX;
+        }
+      fp_common:
+        if (signflag) sign = '-';
+        if (expt == INT_MAX) { /* inf or nan */
+          if (*cp == 'N')
+            cp = (ch >= 'a') ? "nan" : "NAN";
+          else
+            cp = (ch >= 'a') ? "inf" : "INF";
+          size = 3;
+          flags &= ~ZEROPAD;
+          break;
+        }
+        flags |= FPT;
+        ndig = dtoaend - cp;
+        if (ch == 'g' || ch == 'G') {
+          if (expt > -4 && expt <= prec) {
+            /* Make %[gG] smell like %[fF] */
+            expchar = '\0';
+            if (flags & ALT)
+              prec -= expt;
+            else
+              prec = ndig - expt;
+            if (prec < 0) prec = 0;
+          } else {
+            /*
+             * Make %[gG] smell like %[eE], but
+             * trim trailing zeroes if no # flag.
+             */
+            if (!(flags & ALT)) prec = ndig;
+          }
+        }
+        if (expchar) {
+          expsize = exponent(expstr, expt - 1, expchar);
+          size = expsize + prec;
+          if (prec > 1 || flags & ALT) ++size;
+        } else {
+          /* space for digits before decimal point */
+          if (expt > 0)
+            size = expt;
+          else /* "0" */
+            size = 1;
+          /* space for decimal pt and following digits */
+          if (prec || flags & ALT) size += prec + 1;
+          lead = expt;
+        }
+        break;
+#ifndef NO_PRINTF_PERCENT_N
+      case 'n':
+        if (flags & LLONGINT)
+          *GETARG(long long*) = ret;
+        else if (flags & LONGINT)
+          *GETARG(long*) = ret;
+        else if (flags & SHORTINT)
+          *GETARG(short*) = ret;
+        else if (flags & CHARINT)
+          *GETARG(signed char*) = ret;
+        else if (flags & PTRINT)
+          *GETARG(ptrdiff_t*) = ret;
+        else if (flags & SIZEINT)
+          *GETARG(ssize_t*) = ret;
+        else if (flags & MAXINT)
+          *GETARG(intmax_t*) = ret;
+        else
+          *GETARG(int*) = ret;
+        continue; /* no output */
+#endif            /* NO_PRINTF_PERCENT_N */
+      case 'O':
+        flags |= LONGINT;
+        /*FALLTHROUGH*/
+      case 'o':
+        _umax = UARG();
+        base = OCT;
+        goto nosign;
+      case 'p':
+        /*
+         * ``The argument shall be a pointer to void.  The
+         * value of the pointer is converted to a sequence
+         * of printable characters, in an implementation-
+         * defined manner.''
+         *	-- ANSI X3J11
+         */
+        _umax = (u_long)GETARG(void*);
+        base = HEX;
+        xdigs = xdigs_lower;
+        ox[1] = 'x';
+        goto nosign;
+      case 's':
+        if (flags & LONGINT) {
+          wchar_t* wcp;
+
+          free(convbuf);
+          convbuf = NULL;
+          if ((wcp = GETARG(wchar_t*)) == NULL) {
+            cp = "(null)";
+          } else {
+            convbuf = __wcsconv(wcp, prec);
+            if (convbuf == NULL) {
+              ret = -1;
+              goto error;
+            }
+            cp = convbuf;
+          }
+        } else if ((cp = GETARG(char*)) == NULL)
+          cp = "(null)";
+        if (prec >= 0) {
+          /*
+           * can't use strlen; can only look for the
+           * NUL in the first `prec' characters, and
+           * strlen() will go further.
+           */
+          char* p = memchr(cp, 0, prec);
+
+          size = p ? (p - cp) : prec;
+        } else {
+          size_t len;
+
+          if ((len = strlen(cp)) > INT_MAX) goto overflow;
+          size = (int)len;
+        }
+        sign = '\0';
+        break;
+      case 'U':
+        flags |= LONGINT;
+        /*FALLTHROUGH*/
+      case 'u':
+        _umax = UARG();
+        base = DEC;
+        goto nosign;
+      case 'X':
+        xdigs = xdigs_upper;
+        goto hex;
+      case 'x':
+        xdigs = xdigs_lower;
+      hex:
+        _umax = UARG();
+        base = HEX;
+        /* leading 0x/X only if non-zero */
+        if (flags & ALT && _umax != 0) ox[1] = ch;
+
+        /* unsigned conversions */
+      nosign:
+        sign = '\0';
+        /*
+         * ``... diouXx conversions ... if a precision is
+         * specified, the 0 flag will be ignored.''
+         *	-- ANSI X3J11
+         */
+      number:
+        if ((dprec = prec) >= 0) flags &= ~ZEROPAD;
+
+        /*
+         * ``The result of converting a zero value with an
+         * explicit precision of zero is no characters.''
+         *	-- ANSI X3J11
+         */
+        cp = buf + BUF;
+        if (_umax != 0 || prec != 0) {
+          /*
+           * Unsigned mod is hard, and unsigned mod
+           * by a constant is easier than that by
+           * a variable; hence this switch.
+           */
+          switch (base) {
+            case OCT:
+              do {
+                *--cp = to_char(_umax & 7);
+                _umax >>= 3;
+              } while (_umax);
+              /* handle octal leading 0 */
+              if (flags & ALT && *cp != '0') *--cp = '0';
+              break;
+
+            case DEC:
+              /* many numbers are 1 digit */
+              while (_umax >= 10) {
+                *--cp = to_char(_umax % 10);
+                _umax /= 10;
+              }
+              *--cp = to_char(_umax);
+              break;
+
+            case HEX:
+              do {
+                *--cp = xdigs[_umax & 15];
+                _umax >>= 4;
+              } while (_umax);
+              break;
+
+            default:
+              cp = "bug in vfprintf: bad base";
+              size = strlen(cp);
+              goto skipsize;
+          }
+        }
+        size = buf + BUF - cp;
+        if (size > BUF) /* should never happen */
+          abort();
+      skipsize:
+        break;
+      default: /* "%?" prints ?, unless ? is NUL */
+        if (ch == '\0') goto done;
+        /* pretend it was %c with argument ch */
+        cp = buf;
+        *cp = ch;
+        size = 1;
+        sign = '\0';
+        break;
+    }
+
+    /*
+     * All reasonable formats wind up here.  At this point, `cp'
+     * points to a string which (if not flags&LADJUST) should be
+     * padded out to `width' places.  If flags&ZEROPAD, it should
+     * first be prefixed by any sign or other prefix; otherwise,
+     * it should be blank padded before the prefix is emitted.
+     * After any left-hand padding and prefixing, emit zeroes
+     * required by a decimal %[diouxX] precision, then print the
+     * string proper, then emit zeroes required by any leftover
+     * floating precision; finally, if LADJUST, pad with blanks.
+     *
+     * Compute actual size, so we know how much to pad.
+     * size excludes decimal prec; realsz includes it.
+     */
+    realsz = dprec > size ? dprec : size;
+    if (sign) realsz++;
+    if (ox[1]) realsz += 2;
+
+    /* right-adjusting blank padding */
+    if ((flags & (LADJUST | ZEROPAD)) == 0) PAD(width - realsz, blanks);
+
+    /* prefix */
+    if (sign) PRINT(&sign, 1);
+    if (ox[1]) { /* ox[1] is either x, X, or \0 */
+      ox[0] = '0';
+      PRINT(ox, 2);
+    }
+
+    /* right-adjusting zero padding */
+    if ((flags & (LADJUST | ZEROPAD)) == ZEROPAD) PAD(width - realsz, zeroes);
+
+    /* leading zeroes from decimal precision */
+    PAD(dprec - size, zeroes);
+
+    /* the string or number proper */
+    if ((flags & FPT) == 0) {
+      PRINT(cp, size);
+    } else { /* glue together f_p fragments */
+      if (decimal_point == NULL) decimal_point = nl_langinfo(RADIXCHAR);
+      if (!expchar) { /* %[fF] or sufficiently short %[gG] */
+        if (expt <= 0) {
+          PRINT(zeroes, 1);
+          if (prec || flags & ALT) PRINT(decimal_point, 1);
+          PAD(-expt, zeroes);
+          /* already handled initial 0's */
+          prec += expt;
+        } else {
+          PRINTANDPAD(cp, dtoaend, lead, zeroes);
+          cp += lead;
+          if (prec || flags & ALT) PRINT(decimal_point, 1);
+        }
+        PRINTANDPAD(cp, dtoaend, prec, zeroes);
+      } else { /* %[eE] or sufficiently long %[gG] */
+        if (prec > 1 || flags & ALT) {
+          buf[0] = *cp++;
+          buf[1] = *decimal_point;
+          PRINT(buf, 2);
+          PRINT(cp, ndig - 1);
+          PAD(prec - ndig, zeroes);
+        } else { /* XeYYY */
+          PRINT(cp, 1);
+        }
+        PRINT(expstr, expsize);
+      }
+    }
+    /* left-adjusting padding (always blank) */
+    if (flags & LADJUST) PAD(width - realsz, blanks);
+
+    /* finally, adjust ret */
+    if (width < realsz) width = realsz;
+    if (width > INT_MAX - ret) goto overflow;
+    ret += width;
+
+    FLUSH(); /* copy out the I/O vectors */
+  }
+done:
+  FLUSH();
+error:
+  va_end(orgap);
+  if (__sferror(fp)) ret = -1;
+  goto finish;
+
+overflow:
+  errno = ENOMEM;
+  ret = -1;
+
+finish:
+  free(convbuf);
+  if (dtoaresult) __freedtoa(dtoaresult);
+  if (argtable != NULL && argtable != statargtable) {
+    munmap(argtable, argtablesiz);
+    argtable = NULL;
+  }
+  return (ret);
+}
+
+/*
+ * Type ids for argument type table.
+ */
+#define T_UNUSED 0
+#define T_SHORT 1
+#define T_U_SHORT 2
+#define TP_SHORT 3
+#define T_INT 4
+#define T_U_INT 5
+#define TP_INT 6
+#define T_LONG 7
+#define T_U_LONG 8
+#define TP_LONG 9
+#define T_LLONG 10
+#define T_U_LLONG 11
+#define TP_LLONG 12
+#define T_DOUBLE 13
+#define T_LONG_DOUBLE 14
+#define TP_CHAR 15
+#define TP_VOID 16
+#define T_PTRINT 17
+#define TP_PTRINT 18
+#define T_SIZEINT 19
+#define T_SSIZEINT 20
+#define TP_SSIZEINT 21
+#define T_MAXINT 22
+#define T_MAXUINT 23
+#define TP_MAXINT 24
+#define T_CHAR 25
+#define T_U_CHAR 26
+#define T_WINT 27
+#define TP_WCHAR 28
+
+/*
+ * Find all arguments when a positional parameter is encountered.  Returns a
+ * table, indexed by argument number, of pointers to each arguments.  The
+ * initial argument table should be an array of STATIC_ARG_TBL_SIZE entries.
+ * It will be replaced with a mmap-ed one if it overflows (malloc cannot be
+ * used since we are attempting to make snprintf thread safe, and alloca is
+ * problematic since we have nested functions..)
+ */
+static int __find_arguments(const char* fmt0, va_list ap, union arg** argtable,
+                            size_t* argtablesiz) {
+  char* fmt;                /* format string */
+  int ch;                   /* character from fmt */
+  int n, n2;                /* handy integer (short term usage) */
+  char* cp;                 /* handy char pointer (short term usage) */
+  int flags;                /* flags as above */
+  unsigned char* typetable; /* table of types */
+  unsigned char stattypetable[STATIC_ARG_TBL_SIZE];
+  int tablesize; /* current size of type table */
+  int tablemax;  /* largest used index in table */
+  int nextarg;   /* 1-based argument index */
+  int ret = 0;   /* return value */
+  wchar_t wc;
+  mbstate_t ps;
+
+  /*
+   * Add an argument type to the table, expanding if necessary.
+   */
+#define ADDTYPE(type)                                                      \
+  ((nextarg >= tablesize) ? __grow_type_table(&typetable, &tablesize) : 0, \
+   (nextarg > tablemax) ? tablemax = nextarg : 0, typetable[nextarg++] = type)
+
+#define ADDSARG()                                                                             \
+  ((flags & MAXINT)                                                                           \
+       ? ADDTYPE(T_MAXINT)                                                                    \
+       : ((flags & PTRINT) ? ADDTYPE(T_PTRINT)                                                \
+                           : ((flags & SIZEINT)                                               \
+                                  ? ADDTYPE(T_SSIZEINT)                                       \
+                                  : ((flags & LLONGINT)                                       \
+                                         ? ADDTYPE(T_LLONG)                                   \
+                                         : ((flags & LONGINT)                                 \
+                                                ? ADDTYPE(T_LONG)                             \
+                                                : ((flags & SHORTINT)                         \
+                                                       ? ADDTYPE(T_SHORT)                     \
+                                                       : ((flags & CHARINT) ? ADDTYPE(T_CHAR) \
+                                                                            : ADDTYPE(T_INT))))))))
+
+#define ADDUARG()                                                                  \
+  ((flags & MAXINT)                                                                \
+       ? ADDTYPE(T_MAXUINT)                                                        \
+       : ((flags & PTRINT)                                                         \
+              ? ADDTYPE(T_PTRINT)                                                  \
+              : ((flags & SIZEINT)                                                 \
+                     ? ADDTYPE(T_SIZEINT)                                          \
+                     : ((flags & LLONGINT)                                         \
+                            ? ADDTYPE(T_U_LLONG)                                   \
+                            : ((flags & LONGINT)                                   \
+                                   ? ADDTYPE(T_U_LONG)                             \
+                                   : ((flags & SHORTINT)                           \
+                                          ? ADDTYPE(T_U_SHORT)                     \
+                                          : ((flags & CHARINT) ? ADDTYPE(T_U_CHAR) \
+                                                               : ADDTYPE(T_U_INT))))))))
+
+  /*
+   * Add * arguments to the type array.
+   */
+#define ADDASTER()         \
+  n2 = 0;                  \
+  cp = fmt;                \
+  while (is_digit(*cp)) {  \
+    APPEND_DIGIT(n2, *cp); \
+    cp++;                  \
+  }                        \
+  if (*cp == '$') {        \
+    int hold = nextarg;    \
+    nextarg = n2;          \
+    ADDTYPE(T_INT);        \
+    nextarg = hold;        \
+    fmt = ++cp;            \
+  } else {                 \
+    ADDTYPE(T_INT);        \
+  }
+  fmt = (char*)fmt0;
+  typetable = stattypetable;
+  tablesize = STATIC_ARG_TBL_SIZE;
+  tablemax = 0;
+  nextarg = 1;
+  memset(typetable, T_UNUSED, STATIC_ARG_TBL_SIZE);
+  memset(&ps, 0, sizeof(ps));
+
+  /*
+   * Scan the format for conversions (`%' character).
+   */
+  for (;;) {
+    cp = fmt;
+    while ((n = mbrtowc(&wc, fmt, MB_CUR_MAX, &ps)) > 0) {
+      fmt += n;
+      if (wc == '%') {
+        fmt--;
+        break;
+      }
+    }
+    if (n < 0) return (-1);
+    if (n == 0) goto done;
+    fmt++; /* skip over '%' */
+
+    flags = 0;
+
+  rflag:
+    ch = *fmt++;
+  reswitch:
+    switch (ch) {
+      case ' ':
+      case '#':
+      case '\'':
+        goto rflag;
+      case '*':
+        ADDASTER();
+        goto rflag;
+      case '-':
+      case '+':
+        goto rflag;
+      case '.':
+        if ((ch = *fmt++) == '*') {
+          ADDASTER();
+          goto rflag;
+        }
+        while (is_digit(ch)) {
+          ch = *fmt++;
+        }
+        goto reswitch;
+      case '0':
+        goto rflag;
+      case '1':
+      case '2':
+      case '3':
+      case '4':
+      case '5':
+      case '6':
+      case '7':
+      case '8':
+      case '9':
+        n = 0;
+        do {
+          APPEND_DIGIT(n, ch);
+          ch = *fmt++;
+        } while (is_digit(ch));
+        if (ch == '$') {
+          nextarg = n;
+          goto rflag;
+        }
+        goto reswitch;
+      case 'L':
+        flags |= LONGDBL;
+        goto rflag;
+      case 'h':
+        if (*fmt == 'h') {
+          fmt++;
+          flags |= CHARINT;
+        } else {
+          flags |= SHORTINT;
+        }
+        goto rflag;
+      case 'j':
+        flags |= MAXINT;
+        goto rflag;
+      case 'l':
+        if (*fmt == 'l') {
+          fmt++;
+          flags |= LLONGINT;
+        } else {
+          flags |= LONGINT;
+        }
+        goto rflag;
+      case 'q':
+        flags |= LLONGINT;
+        goto rflag;
+      case 't':
+        flags |= PTRINT;
+        goto rflag;
+      case 'z':
+        flags |= SIZEINT;
+        goto rflag;
+      case 'c':
+        if (flags & LONGINT)
+          ADDTYPE(T_WINT);
+        else
+          ADDTYPE(T_INT);
+        break;
+      case 'D':
+        flags |= LONGINT;
+        /*FALLTHROUGH*/
+      case 'd':
+      case 'i':
+        ADDSARG();
+        break;
+      case 'a':
+      case 'A':
+      case 'e':
+      case 'E':
+      case 'f':
+      case 'F':
+      case 'g':
+      case 'G':
+        if (flags & LONGDBL)
+          ADDTYPE(T_LONG_DOUBLE);
+        else
+          ADDTYPE(T_DOUBLE);
+        break;
+#ifndef NO_PRINTF_PERCENT_N
+      case 'n':
+        if (flags & LLONGINT)
+          ADDTYPE(TP_LLONG);
+        else if (flags & LONGINT)
+          ADDTYPE(TP_LONG);
+        else if (flags & SHORTINT)
+          ADDTYPE(TP_SHORT);
+        else if (flags & PTRINT)
+          ADDTYPE(TP_PTRINT);
+        else if (flags & SIZEINT)
+          ADDTYPE(TP_SSIZEINT);
+        else if (flags & MAXINT)
+          ADDTYPE(TP_MAXINT);
+        else
+          ADDTYPE(TP_INT);
+        continue; /* no output */
+#endif            /* NO_PRINTF_PERCENT_N */
+      case 'O':
+        flags |= LONGINT;
+        /*FALLTHROUGH*/
+      case 'o':
+        ADDUARG();
+        break;
+      case 'p':
+        ADDTYPE(TP_VOID);
+        break;
+      case 's':
+        if (flags & LONGINT)
+          ADDTYPE(TP_WCHAR);
+        else
+          ADDTYPE(TP_CHAR);
+        break;
+      case 'U':
+        flags |= LONGINT;
+        /*FALLTHROUGH*/
+      case 'u':
+      case 'X':
+      case 'x':
+        ADDUARG();
+        break;
+      default: /* "%?" prints ?, unless ? is NUL */
+        if (ch == '\0') goto done;
+        break;
+    }
+  }
+done:
+  /*
+   * Build the argument table.
+   */
+  if (tablemax >= STATIC_ARG_TBL_SIZE) {
+    *argtablesiz = sizeof(union arg) * (tablemax + 1);
+    *argtable = mmap(NULL, *argtablesiz, PROT_WRITE | PROT_READ, MAP_ANON | MAP_PRIVATE, -1, 0);
+    if (*argtable == MAP_FAILED) return (-1);
+  }
+
+#if 0
+	/* XXX is this required? */
+	(*argtable)[0].intarg = 0;
+#endif
+  for (n = 1; n <= tablemax; n++) {
+    switch (typetable[n]) {
+      case T_UNUSED:
+      case T_CHAR:
+      case T_U_CHAR:
+      case T_SHORT:
+      case T_U_SHORT:
+      case T_INT:
+        (*argtable)[n].intarg = va_arg(ap, int);
+        break;
+      case TP_SHORT:
+        (*argtable)[n].pshortarg = va_arg(ap, short*);
+        break;
+      case T_U_INT:
+        (*argtable)[n].uintarg = va_arg(ap, unsigned int);
+        break;
+      case TP_INT:
+        (*argtable)[n].pintarg = va_arg(ap, int*);
+        break;
+      case T_LONG:
+        (*argtable)[n].longarg = va_arg(ap, long);
+        break;
+      case T_U_LONG:
+        (*argtable)[n].ulongarg = va_arg(ap, unsigned long);
+        break;
+      case TP_LONG:
+        (*argtable)[n].plongarg = va_arg(ap, long*);
+        break;
+      case T_LLONG:
+        (*argtable)[n].longlongarg = va_arg(ap, long long);
+        break;
+      case T_U_LLONG:
+        (*argtable)[n].ulonglongarg = va_arg(ap, unsigned long long);
+        break;
+      case TP_LLONG:
+        (*argtable)[n].plonglongarg = va_arg(ap, long long*);
+        break;
+      case T_DOUBLE:
+        (*argtable)[n].doublearg = va_arg(ap, double);
+        break;
+      case T_LONG_DOUBLE:
+        (*argtable)[n].longdoublearg = va_arg(ap, long double);
+        break;
+      case TP_CHAR:
+        (*argtable)[n].pchararg = va_arg(ap, char*);
+        break;
+      case TP_VOID:
+        (*argtable)[n].pvoidarg = va_arg(ap, void*);
+        break;
+      case T_PTRINT:
+        (*argtable)[n].ptrdiffarg = va_arg(ap, ptrdiff_t);
+        break;
+      case TP_PTRINT:
+        (*argtable)[n].pptrdiffarg = va_arg(ap, ptrdiff_t*);
+        break;
+      case T_SIZEINT:
+        (*argtable)[n].sizearg = va_arg(ap, size_t);
+        break;
+      case T_SSIZEINT:
+        (*argtable)[n].ssizearg = va_arg(ap, ssize_t);
+        break;
+      case TP_SSIZEINT:
+        (*argtable)[n].pssizearg = va_arg(ap, ssize_t*);
+        break;
+      case T_MAXINT:
+        (*argtable)[n].intmaxarg = va_arg(ap, intmax_t);
+        break;
+      case T_MAXUINT:
+        (*argtable)[n].uintmaxarg = va_arg(ap, uintmax_t);
+        break;
+      case TP_MAXINT:
+        (*argtable)[n].pintmaxarg = va_arg(ap, intmax_t*);
+        break;
+      case T_WINT:
+        (*argtable)[n].wintarg = va_arg(ap, wint_t);
+        break;
+      case TP_WCHAR:
+        (*argtable)[n].pwchararg = va_arg(ap, wchar_t*);
+        break;
+    }
+  }
+  goto finish;
+
+overflow:
+  errno = ENOMEM;
+  ret = -1;
+
+finish:
+  if (typetable != NULL && typetable != stattypetable) {
+    munmap(typetable, *argtablesiz);
+    typetable = NULL;
+  }
+  return (ret);
+}
+
+/*
+ * Increase the size of the type table.
+ */
+static int __grow_type_table(unsigned char** typetable, int* tablesize) {
+  unsigned char* oldtable = *typetable;
+  int newsize = *tablesize * 2;
+
+  if (newsize < getpagesize()) newsize = getpagesize();
+
+  if (*tablesize == STATIC_ARG_TBL_SIZE) {
+    *typetable = mmap(NULL, newsize, PROT_WRITE | PROT_READ, MAP_ANON | MAP_PRIVATE, -1, 0);
+    if (*typetable == MAP_FAILED) return (-1);
+    bcopy(oldtable, *typetable, *tablesize);
+  } else {
+    unsigned char* new = mmap(NULL, newsize, PROT_WRITE | PROT_READ, MAP_ANON | MAP_PRIVATE, -1, 0);
+    if (new == MAP_FAILED) return (-1);
+    memmove(new, *typetable, *tablesize);
+    munmap(*typetable, *tablesize);
+    *typetable = new;
+  }
+  memset(*typetable + *tablesize, T_UNUSED, (newsize - *tablesize));
+
+  *tablesize = newsize;
+  return (0);
+}
+
+static int exponent(char* p0, int exp, int fmtch) {
+  char *p, *t;
+  char expbuf[MAXEXPDIG];
+
+  p = p0;
+  *p++ = fmtch;
+  if (exp < 0) {
+    exp = -exp;
+    *p++ = '-';
+  } else
+    *p++ = '+';
+  t = expbuf + MAXEXPDIG;
+  if (exp > 9) {
+    do {
+      *--t = to_char(exp % 10);
+    } while ((exp /= 10) > 9);
+    *--t = to_char(exp);
+    for (; t < expbuf + MAXEXPDIG; *p++ = *t++) /* nothing */;
+  } else {
+    /*
+     * Exponents for decimal floating point conversions
+     * (%[eEgG]) must be at least two characters long,
+     * whereas exponents for hexadecimal conversions can
+     * be only one character long.
+     */
+    if (fmtch == 'e' || fmtch == 'E') *p++ = '0';
+    *p++ = to_char(exp);
+  }
+  return (p - p0);
+}
diff --git a/libc/stdio/vfscanf.c b/libc/stdio/vfscanf.c
index e47d0df..c18e214 100644
--- a/libc/stdio/vfscanf.c
+++ b/libc/stdio/vfscanf.c
@@ -32,762 +32,724 @@
  */
 
 #include <ctype.h>
-#include <wctype.h>
 #include <inttypes.h>
 #include <stdarg.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <wctype.h>
 #include "local.h"
 
-#ifdef FLOATING_POINT
 #include "floatio.h"
-#endif
 
-#define	BUF		513	/* Maximum length of numeric string. */
+#define BUF 513 /* Maximum length of numeric string. */
 
 /*
  * Flags used during conversion.
  */
-#define	LONG		0x00001	/* l: long or double */
-#define	LONGDBL		0x00002	/* L: long double */
-#define	SHORT		0x00004	/* h: short */
-#define	SHORTSHORT	0x00008	/* hh: 8 bit integer */
-#define LLONG		0x00010	/* ll: long long (+ deprecated q: quad) */
-#define	POINTER		0x00020	/* p: void * (as hex) */
-#define	SIZEINT		0x00040	/* z: (signed) size_t */
-#define	MAXINT		0x00080	/* j: intmax_t */
-#define	PTRINT		0x00100	/* t: ptrdiff_t */
-#define	NOSKIP		0x00200	/* [ or c: do not skip blanks */
-#define	SUPPRESS	0x00400	/* *: suppress assignment */
-#define	UNSIGNED	0x00800	/* %[oupxX] conversions */
+#define LONG 0x00001       /* l: long or double */
+#define LONGDBL 0x00002    /* L: long double */
+#define SHORT 0x00004      /* h: short */
+#define SHORTSHORT 0x00008 /* hh: 8 bit integer */
+#define LLONG 0x00010      /* ll: long long (+ deprecated q: quad) */
+#define POINTER 0x00020    /* p: void * (as hex) */
+#define SIZEINT 0x00040    /* z: (signed) size_t */
+#define MAXINT 0x00080     /* j: intmax_t */
+#define PTRINT 0x00100     /* t: ptrdiff_t */
+#define NOSKIP 0x00200     /* [ or c: do not skip blanks */
+#define SUPPRESS 0x00400   /* *: suppress assignment */
+#define UNSIGNED 0x00800   /* %[oupxX] conversions */
 
 /*
  * The following are used in numeric conversions only:
  * SIGNOK, HAVESIGN, NDIGITS, DPTOK, and EXPOK are for floating point;
  * SIGNOK, HAVESIGN, NDIGITS, PFXOK, and NZDIGITS are for integral.
  */
-#define	SIGNOK		0x01000	/* +/- is (still) legal */
-#define	HAVESIGN	0x02000	/* sign detected */
-#define	NDIGITS		0x04000	/* no digits detected */
+#define SIGNOK 0x01000   /* +/- is (still) legal */
+#define HAVESIGN 0x02000 /* sign detected */
+#define NDIGITS 0x04000  /* no digits detected */
 
-#define	DPTOK		0x08000	/* (float) decimal point is still legal */
-#define	EXPOK		0x10000	/* (float) exponent (e+3, etc) still legal */
+#define DPTOK 0x08000 /* (float) decimal point is still legal */
+#define EXPOK 0x10000 /* (float) exponent (e+3, etc) still legal */
 
-#define	PFXOK		0x08000	/* 0x prefix is (still) legal */
-#define	NZDIGITS	0x10000	/* no zero digits detected */
+#define PFXOK 0x08000    /* 0x prefix is (still) legal */
+#define NZDIGITS 0x10000 /* no zero digits detected */
 
 /*
  * Conversion types.
  */
-#define	CT_CHAR		0	/* %c conversion */
-#define	CT_CCL		1	/* %[...] conversion */
-#define	CT_STRING	2	/* %s conversion */
-#define	CT_INT		3	/* integer, i.e., strtoimax or strtoumax */
-#define	CT_FLOAT	4	/* floating, i.e., strtod */
+#define CT_CHAR 0   /* %c conversion */
+#define CT_CCL 1    /* %[...] conversion */
+#define CT_STRING 2 /* %s conversion */
+#define CT_INT 3    /* integer, i.e., strtoimax or strtoumax */
+#define CT_FLOAT 4  /* floating, i.e., strtod */
 
 #define u_char unsigned char
 #define u_long unsigned long
 
-static u_char *__sccl(char *, u_char *);
+static u_char* __sccl(char*, u_char*);
 
 /*
  * Internal, unlocked version of vfscanf
  */
-int
-__svfscanf(FILE *fp, const char *fmt0, __va_list ap)
-{
-	u_char *fmt = (u_char *)fmt0;
-	int c;		/* character from format, or conversion */
-	size_t width;	/* field width, or 0 */
-	char *p;	/* points into all kinds of strings */
-	int n;		/* handy integer */
-	int flags;	/* flags as defined above */
-	char *p0;	/* saves original value of p when necessary */
-	int nassigned;		/* number of fields assigned */
-	int nread;		/* number of characters consumed from fp */
-	int base;		/* base argument to strtoimax/strtouimax */
-	char ccltab[256];	/* character class table for %[...] */
-	char buf[BUF];		/* buffer for numeric conversions */
-#ifdef SCANF_WIDE_CHAR
-	wchar_t *wcp;		/* handy wide character pointer */
-	size_t nconv;		/* length of multibyte sequence converted */
-	mbstate_t mbs;
-#endif
+int __svfscanf(FILE* fp, const char* fmt0, __va_list ap) {
+  u_char* fmt = (u_char*)fmt0;
+  int c;            /* character from format, or conversion */
+  size_t width;     /* field width, or 0 */
+  char* p;          /* points into all kinds of strings */
+  int n;            /* handy integer */
+  int flags;        /* flags as defined above */
+  char* p0;         /* saves original value of p when necessary */
+  int nassigned;    /* number of fields assigned */
+  int nread;        /* number of characters consumed from fp */
+  int base;         /* base argument to strtoimax/strtouimax */
+  char ccltab[256]; /* character class table for %[...] */
+  char buf[BUF];    /* buffer for numeric conversions */
+  wchar_t* wcp;     /* handy wide character pointer */
+  size_t nconv;     /* length of multibyte sequence converted */
+  mbstate_t mbs;
 
-	/* `basefix' is used to avoid `if' tests in the integer scanner */
-	static short basefix[17] =
-		{ 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
+  /* `basefix' is used to avoid `if' tests in the integer scanner */
+  static short basefix[17] = { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
 
-	_SET_ORIENTATION(fp, -1);
+  _SET_ORIENTATION(fp, -1);
 
-	nassigned = 0;
-	nread = 0;
-	base = 0;		/* XXX just to keep gcc happy */
-	for (;;) {
-		c = *fmt++;
-		if (c == 0)
-			return (nassigned);
-		if (isspace(c)) {
-			while ((fp->_r > 0 || __srefill(fp) == 0) &&
-			    isspace(*fp->_p))
-				nread++, fp->_r--, fp->_p++;
-			continue;
-		}
-		if (c != '%')
-			goto literal;
-		width = 0;
-		flags = 0;
-		/*
-		 * switch on the format.  continue if done;
-		 * break once format type is derived.
-		 */
-again:		c = *fmt++;
-		switch (c) {
-		case '%':
-literal:
-			if (fp->_r <= 0 && __srefill(fp))
-				goto input_failure;
-			if (*fp->_p != c)
-				goto match_failure;
-			fp->_r--, fp->_p++;
-			nread++;
-			continue;
+  nassigned = 0;
+  nread = 0;
+  base = 0; /* XXX just to keep gcc happy */
+  for (;;) {
+    c = *fmt++;
+    if (c == 0) return (nassigned);
+    if (isspace(c)) {
+      while ((fp->_r > 0 || __srefill(fp) == 0) && isspace(*fp->_p)) nread++, fp->_r--, fp->_p++;
+      continue;
+    }
+    if (c != '%') goto literal;
+    width = 0;
+    flags = 0;
+    /*
+     * switch on the format.  continue if done;
+     * break once format type is derived.
+     */
+  again:
+    c = *fmt++;
+    switch (c) {
+      case '%':
+      literal:
+        if (fp->_r <= 0 && __srefill(fp)) goto input_failure;
+        if (*fp->_p != c) goto match_failure;
+        fp->_r--, fp->_p++;
+        nread++;
+        continue;
 
-		case '*':
-			flags |= SUPPRESS;
-			goto again;
-		case 'j':
-			flags |= MAXINT;
-			goto again;
-		case 'L':
-			flags |= LONGDBL;
-			goto again;
-		case 'h':
-			if (*fmt == 'h') {
-				fmt++;
-				flags |= SHORTSHORT;
-			} else {
-				flags |= SHORT;
-			}
-			goto again;
-		case 'l':
-			if (*fmt == 'l') {
-				fmt++;
-				flags |= LLONG;
-			} else {
-				flags |= LONG;
-			}
-			goto again;
-		case 'q':
-			flags |= LLONG;		/* deprecated */
-			goto again;
-		case 't':
-			flags |= PTRINT;
-			goto again;
-		case 'z':
-			flags |= SIZEINT;
-			goto again;
+      case '*':
+        flags |= SUPPRESS;
+        goto again;
+      case 'j':
+        flags |= MAXINT;
+        goto again;
+      case 'L':
+        flags |= LONGDBL;
+        goto again;
+      case 'h':
+        if (*fmt == 'h') {
+          fmt++;
+          flags |= SHORTSHORT;
+        } else {
+          flags |= SHORT;
+        }
+        goto again;
+      case 'l':
+        if (*fmt == 'l') {
+          fmt++;
+          flags |= LLONG;
+        } else {
+          flags |= LONG;
+        }
+        goto again;
+      case 'q':
+        flags |= LLONG; /* deprecated */
+        goto again;
+      case 't':
+        flags |= PTRINT;
+        goto again;
+      case 'z':
+        flags |= SIZEINT;
+        goto again;
 
-		case '0': case '1': case '2': case '3': case '4':
-		case '5': case '6': case '7': case '8': case '9':
-			width = width * 10 + c - '0';
-			goto again;
+      case '0':
+      case '1':
+      case '2':
+      case '3':
+      case '4':
+      case '5':
+      case '6':
+      case '7':
+      case '8':
+      case '9':
+        width = width * 10 + c - '0';
+        goto again;
 
-		/*
-		 * Conversions.
-		 * Those marked `compat' are for 4.[123]BSD compatibility.
-		 *
-		 * (According to ANSI, E and X formats are supposed
-		 * to the same as e and x.  Sorry about that.)
-		 */
-		case 'D':	/* compat */
-			flags |= LONG;
-			/* FALLTHROUGH */
-		case 'd':
-			c = CT_INT;
-			base = 10;
-			break;
+      /*
+       * Conversions.
+       * Those marked `compat' are for 4.[123]BSD compatibility.
+       *
+       * (According to ANSI, E and X formats are supposed
+       * to the same as e and x.  Sorry about that.)
+       */
+      case 'D': /* compat */
+        flags |= LONG;
+        /* FALLTHROUGH */
+      case 'd':
+        c = CT_INT;
+        base = 10;
+        break;
 
-		case 'i':
-			c = CT_INT;
-			base = 0;
-			break;
+      case 'i':
+        c = CT_INT;
+        base = 0;
+        break;
 
-		case 'O':	/* compat */
-			flags |= LONG;
-			/* FALLTHROUGH */
-		case 'o':
-			c = CT_INT;
-			flags |= UNSIGNED;
-			base = 8;
-			break;
+      case 'O': /* compat */
+        flags |= LONG;
+        /* FALLTHROUGH */
+      case 'o':
+        c = CT_INT;
+        flags |= UNSIGNED;
+        base = 8;
+        break;
 
-		case 'u':
-			c = CT_INT;
-			flags |= UNSIGNED;
-			base = 10;
-			break;
+      case 'u':
+        c = CT_INT;
+        flags |= UNSIGNED;
+        base = 10;
+        break;
 
-		case 'X':
-		case 'x':
-			flags |= PFXOK;	/* enable 0x prefixing */
-			c = CT_INT;
-			flags |= UNSIGNED;
-			base = 16;
-			break;
+      case 'X':
+      case 'x':
+        flags |= PFXOK; /* enable 0x prefixing */
+        c = CT_INT;
+        flags |= UNSIGNED;
+        base = 16;
+        break;
 
-#ifdef FLOATING_POINT
-		case 'e': case 'E':
-		case 'f': case 'F':
-		case 'g': case 'G':
-		case 'a': case 'A':
-			c = CT_FLOAT;
-			break;
-#endif
+      case 'e':
+      case 'E':
+      case 'f':
+      case 'F':
+      case 'g':
+      case 'G':
+      case 'a':
+      case 'A':
+        c = CT_FLOAT;
+        break;
 
-		case 's':
-			c = CT_STRING;
-			break;
+      case 's':
+        c = CT_STRING;
+        break;
 
-		case '[':
-			fmt = __sccl(ccltab, fmt);
-			flags |= NOSKIP;
-			c = CT_CCL;
-			break;
+      case '[':
+        fmt = __sccl(ccltab, fmt);
+        flags |= NOSKIP;
+        c = CT_CCL;
+        break;
 
-		case 'c':
-			flags |= NOSKIP;
-			c = CT_CHAR;
-			break;
+      case 'c':
+        flags |= NOSKIP;
+        c = CT_CHAR;
+        break;
 
-		case 'p':	/* pointer format is like hex */
-			flags |= POINTER | PFXOK;
-			c = CT_INT;
-			flags |= UNSIGNED;
-			base = 16;
-			break;
+      case 'p': /* pointer format is like hex */
+        flags |= POINTER | PFXOK;
+        c = CT_INT;
+        flags |= UNSIGNED;
+        base = 16;
+        break;
 
-		case 'n':
-			if (flags & SUPPRESS)
-				continue;
-			if (flags & SHORTSHORT)
-				*va_arg(ap, signed char *) = nread;
-			else if (flags & SHORT)
-				*va_arg(ap, short *) = nread;
-			else if (flags & LONG)
-				*va_arg(ap, long *) = nread;
-			else if (flags & SIZEINT)
-				*va_arg(ap, ssize_t *) = nread;
-			else if (flags & PTRINT)
-				*va_arg(ap, ptrdiff_t *) = nread;
-			else if (flags & LLONG)
-				*va_arg(ap, long long *) = nread;
-			else if (flags & MAXINT)
-				*va_arg(ap, intmax_t *) = nread;
-			else
-				*va_arg(ap, int *) = nread;
-			continue;
+      case 'n':
+        if (flags & SUPPRESS) continue;
+        if (flags & SHORTSHORT)
+          *va_arg(ap, signed char*) = nread;
+        else if (flags & SHORT)
+          *va_arg(ap, short*) = nread;
+        else if (flags & LONG)
+          *va_arg(ap, long*) = nread;
+        else if (flags & SIZEINT)
+          *va_arg(ap, ssize_t*) = nread;
+        else if (flags & PTRINT)
+          *va_arg(ap, ptrdiff_t*) = nread;
+        else if (flags & LLONG)
+          *va_arg(ap, long long*) = nread;
+        else if (flags & MAXINT)
+          *va_arg(ap, intmax_t*) = nread;
+        else
+          *va_arg(ap, int*) = nread;
+        continue;
 
-		/*
-		 * Disgusting backwards compatibility hacks.	XXX
-		 */
-		case '\0':	/* compat */
-			return (EOF);
+      /*
+       * Disgusting backwards compatibility hacks.	XXX
+       */
+      case '\0': /* compat */
+        return (EOF);
 
-		default:	/* compat */
-			if (isupper(c))
-				flags |= LONG;
-			c = CT_INT;
-			base = 10;
-			break;
-		}
+      default: /* compat */
+        if (isupper(c)) flags |= LONG;
+        c = CT_INT;
+        base = 10;
+        break;
+    }
 
-		/*
-		 * We have a conversion that requires input.
-		 */
-		if (fp->_r <= 0 && __srefill(fp))
-			goto input_failure;
+    /*
+     * We have a conversion that requires input.
+     */
+    if (fp->_r <= 0 && __srefill(fp)) goto input_failure;
 
-		/*
-		 * Consume leading white space, except for formats
-		 * that suppress this.
-		 */
-		if ((flags & NOSKIP) == 0) {
-			while (isspace(*fp->_p)) {
-				nread++;
-				if (--fp->_r > 0)
-					fp->_p++;
-				else if (__srefill(fp))
-					goto input_failure;
-			}
-			/*
-			 * Note that there is at least one character in
-			 * the buffer, so conversions that do not set NOSKIP
-			 * ca no longer result in an input failure.
-			 */
-		}
+    /*
+     * Consume leading white space, except for formats
+     * that suppress this.
+     */
+    if ((flags & NOSKIP) == 0) {
+      while (isspace(*fp->_p)) {
+        nread++;
+        if (--fp->_r > 0)
+          fp->_p++;
+        else if (__srefill(fp))
+          goto input_failure;
+      }
+      /*
+       * Note that there is at least one character in
+       * the buffer, so conversions that do not set NOSKIP
+       * ca no longer result in an input failure.
+       */
+    }
 
-		/*
-		 * Do the conversion.
-		 */
-		switch (c) {
+    /*
+     * Do the conversion.
+     */
+    switch (c) {
+      case CT_CHAR:
+        /* scan arbitrary characters (sets NOSKIP) */
+        if (width == 0) width = 1;
+        if (flags & LONG) {
+          if ((flags & SUPPRESS) == 0)
+            wcp = va_arg(ap, wchar_t*);
+          else
+            wcp = NULL;
+          n = 0;
+          while (width != 0) {
+            if (n == (int)MB_CUR_MAX) {
+              fp->_flags |= __SERR;
+              goto input_failure;
+            }
+            buf[n++] = *fp->_p;
+            fp->_p++;
+            fp->_r--;
+            memset(&mbs, 0, sizeof(mbs));
+            nconv = mbrtowc(wcp, buf, n, &mbs);
+            if (nconv == (size_t)-1) {
+              fp->_flags |= __SERR;
+              goto input_failure;
+            }
+            if (nconv == 0 && !(flags & SUPPRESS)) *wcp = L'\0';
+            if (nconv != (size_t)-2) {
+              nread += n;
+              width--;
+              if (!(flags & SUPPRESS)) wcp++;
+              n = 0;
+            }
+            if (fp->_r <= 0 && __srefill(fp)) {
+              if (n != 0) {
+                fp->_flags |= __SERR;
+                goto input_failure;
+              }
+              break;
+            }
+          }
+          if (!(flags & SUPPRESS)) nassigned++;
+        } else if (flags & SUPPRESS) {
+          size_t sum = 0;
+          for (;;) {
+            if ((n = fp->_r) < (int)width) {
+              sum += n;
+              width -= n;
+              fp->_p += n;
+              if (__srefill(fp)) {
+                if (sum == 0) goto input_failure;
+                break;
+              }
+            } else {
+              sum += width;
+              fp->_r -= width;
+              fp->_p += width;
+              break;
+            }
+          }
+          nread += sum;
+        } else {
+          size_t r = fread((void*)va_arg(ap, char*), 1, width, fp);
 
-		case CT_CHAR:
-			/* scan arbitrary characters (sets NOSKIP) */
-			if (width == 0)
-				width = 1;
-#ifdef SCANF_WIDE_CHAR
-			if (flags & LONG) {
-				if ((flags & SUPPRESS) == 0)
-					wcp = va_arg(ap, wchar_t *);
-				else
-					wcp = NULL;
-				n = 0;
-				while (width != 0) {
-					if (n == (int)MB_CUR_MAX) {
-						fp->_flags |= __SERR;
-						goto input_failure;
-					}
-					buf[n++] = *fp->_p;
-					fp->_p++;
-					fp->_r--;
-					memset(&mbs, 0, sizeof(mbs));
-					nconv = mbrtowc(wcp, buf, n, &mbs);
-					if (nconv == (size_t)-1) {
-						fp->_flags |= __SERR;
-						goto input_failure;
-					}
-					if (nconv == 0 && !(flags & SUPPRESS))
-						*wcp = L'\0';
-					if (nconv != (size_t)-2) {
-						nread += n;
-						width--;
-						if (!(flags & SUPPRESS))
-							wcp++;
-						n = 0;
-					}
-					if (fp->_r <= 0 && __srefill(fp)) {
-						if (n != 0) {
-							fp->_flags |= __SERR;
-							goto input_failure;
-						}
-						break;
-					}
-				}
-				if (!(flags & SUPPRESS))
-					nassigned++;
-			} else
-#endif /* SCANF_WIDE_CHAR */
-			if (flags & SUPPRESS) {
-				size_t sum = 0;
-				for (;;) {
-					if ((n = fp->_r) < (int)width) {
-						sum += n;
-						width -= n;
-						fp->_p += n;
-						if (__srefill(fp)) {
-							if (sum == 0)
-							    goto input_failure;
-							break;
-						}
-					} else {
-						sum += width;
-						fp->_r -= width;
-						fp->_p += width;
-						break;
-					}
-				}
-				nread += sum;
-			} else {
-				size_t r = fread((void *)va_arg(ap, char *), 1,
-				    width, fp);
+          if (r == 0) goto input_failure;
+          nread += r;
+          nassigned++;
+        }
+        break;
 
-				if (r == 0)
-					goto input_failure;
-				nread += r;
-				nassigned++;
-			}
-			break;
+      case CT_CCL:
+        /* scan a (nonempty) character class (sets NOSKIP) */
+        if (width == 0) width = (size_t)~0; /* `infinity' */
+        /* take only those things in the class */
+        if (flags & LONG) {
+          wchar_t twc;
+          int nchars;
 
-		case CT_CCL:
-			/* scan a (nonempty) character class (sets NOSKIP) */
-			if (width == 0)
-				width = (size_t)~0;	/* `infinity' */
-#ifdef SCANF_WIDE_CHAR
-			/* take only those things in the class */
-			if (flags & LONG) {
-				wchar_t twc;
-				int nchars;
+          if ((flags & SUPPRESS) == 0)
+            wcp = va_arg(ap, wchar_t*);
+          else
+            wcp = &twc;
+          n = 0;
+          nchars = 0;
+          while (width != 0) {
+            if (n == (int)MB_CUR_MAX) {
+              fp->_flags |= __SERR;
+              goto input_failure;
+            }
+            buf[n++] = *fp->_p;
+            fp->_p++;
+            fp->_r--;
+            memset(&mbs, 0, sizeof(mbs));
+            nconv = mbrtowc(wcp, buf, n, &mbs);
+            if (nconv == (size_t)-1) {
+              fp->_flags |= __SERR;
+              goto input_failure;
+            }
+            if (nconv == 0) *wcp = L'\0';
+            if (nconv != (size_t)-2) {
+              if (wctob(*wcp) != EOF && !ccltab[wctob(*wcp)]) {
+                while (n != 0) {
+                  n--;
+                  ungetc(buf[n], fp);
+                }
+                break;
+              }
+              nread += n;
+              width--;
+              if (!(flags & SUPPRESS)) wcp++;
+              nchars++;
+              n = 0;
+            }
+            if (fp->_r <= 0 && __srefill(fp)) {
+              if (n != 0) {
+                fp->_flags |= __SERR;
+                goto input_failure;
+              }
+              break;
+            }
+          }
+          if (n != 0) {
+            fp->_flags |= __SERR;
+            goto input_failure;
+          }
+          n = nchars;
+          if (n == 0) goto match_failure;
+          if (!(flags & SUPPRESS)) {
+            *wcp = L'\0';
+            nassigned++;
+          }
+        } else
+            /* take only those things in the class */
+            if (flags & SUPPRESS) {
+          n = 0;
+          while (ccltab[*fp->_p]) {
+            n++, fp->_r--, fp->_p++;
+            if (--width == 0) break;
+            if (fp->_r <= 0 && __srefill(fp)) {
+              if (n == 0) goto input_failure;
+              break;
+            }
+          }
+          if (n == 0) goto match_failure;
+        } else {
+          p0 = p = va_arg(ap, char*);
+          while (ccltab[*fp->_p]) {
+            fp->_r--;
+            *p++ = *fp->_p++;
+            if (--width == 0) break;
+            if (fp->_r <= 0 && __srefill(fp)) {
+              if (p == p0) goto input_failure;
+              break;
+            }
+          }
+          n = p - p0;
+          if (n == 0) goto match_failure;
+          *p = '\0';
+          nassigned++;
+        }
+        nread += n;
+        break;
 
-				if ((flags & SUPPRESS) == 0)
-					wcp = va_arg(ap, wchar_t *);
-				else
-					wcp = &twc;
-				n = 0;
-				nchars = 0;
-				while (width != 0) {
-					if (n == (int)MB_CUR_MAX) {
-						fp->_flags |= __SERR;
-						goto input_failure;
-					}
-					buf[n++] = *fp->_p;
-					fp->_p++;
-					fp->_r--;
-					memset(&mbs, 0, sizeof(mbs));
-					nconv = mbrtowc(wcp, buf, n, &mbs);
-					if (nconv == (size_t)-1) {
-						fp->_flags |= __SERR;
-						goto input_failure;
-					}
-					if (nconv == 0)
-						*wcp = L'\0';
-					if (nconv != (size_t)-2) {
-						if (wctob(*wcp) != EOF &&
-						    !ccltab[wctob(*wcp)]) {
-							while (n != 0) {
-								n--;
-								ungetc(buf[n],
-								    fp);
-							}
-							break;
-						}
-						nread += n;
-						width--;
-						if (!(flags & SUPPRESS))
-							wcp++;
-						nchars++;
-						n = 0;
-					}
-					if (fp->_r <= 0 && __srefill(fp)) {
-						if (n != 0) {
-							fp->_flags |= __SERR;
-							goto input_failure;
-						}
-						break;
-					}
-				}
-				if (n != 0) {
-					fp->_flags |= __SERR;
-					goto input_failure;
-				}
-				n = nchars;
-				if (n == 0)
-					goto match_failure;
-				if (!(flags & SUPPRESS)) {
-					*wcp = L'\0';
-					nassigned++;
-				}
-			} else
-#endif /* SCANF_WIDE_CHAR */
-			/* take only those things in the class */
-			if (flags & SUPPRESS) {
-				n = 0;
-				while (ccltab[*fp->_p]) {
-					n++, fp->_r--, fp->_p++;
-					if (--width == 0)
-						break;
-					if (fp->_r <= 0 && __srefill(fp)) {
-						if (n == 0)
-							goto input_failure;
-						break;
-					}
-				}
-				if (n == 0)
-					goto match_failure;
-			} else {
-				p0 = p = va_arg(ap, char *);
-				while (ccltab[*fp->_p]) {
-					fp->_r--;
-					*p++ = *fp->_p++;
-					if (--width == 0)
-						break;
-					if (fp->_r <= 0 && __srefill(fp)) {
-						if (p == p0)
-							goto input_failure;
-						break;
-					}
-				}
-				n = p - p0;
-				if (n == 0)
-					goto match_failure;
-				*p = '\0';
-				nassigned++;
-			}
-			nread += n;
-			break;
+      case CT_STRING:
+        /* like CCL, but zero-length string OK, & no NOSKIP */
+        if (width == 0) width = (size_t)~0;
+        if (flags & LONG) {
+          wchar_t twc;
 
-		case CT_STRING:
-			/* like CCL, but zero-length string OK, & no NOSKIP */
-			if (width == 0)
-				width = (size_t)~0;
-#ifdef SCANF_WIDE_CHAR
-			if (flags & LONG) {
-				wchar_t twc;
+          if ((flags & SUPPRESS) == 0)
+            wcp = va_arg(ap, wchar_t*);
+          else
+            wcp = &twc;
+          n = 0;
+          while (!isspace(*fp->_p) && width != 0) {
+            if (n == (int)MB_CUR_MAX) {
+              fp->_flags |= __SERR;
+              goto input_failure;
+            }
+            buf[n++] = *fp->_p;
+            fp->_p++;
+            fp->_r--;
+            memset(&mbs, 0, sizeof(mbs));
+            nconv = mbrtowc(wcp, buf, n, &mbs);
+            if (nconv == (size_t)-1) {
+              fp->_flags |= __SERR;
+              goto input_failure;
+            }
+            if (nconv == 0) *wcp = L'\0';
+            if (nconv != (size_t)-2) {
+              if (iswspace(*wcp)) {
+                while (n != 0) {
+                  n--;
+                  ungetc(buf[n], fp);
+                }
+                break;
+              }
+              nread += n;
+              width--;
+              if (!(flags & SUPPRESS)) wcp++;
+              n = 0;
+            }
+            if (fp->_r <= 0 && __srefill(fp)) {
+              if (n != 0) {
+                fp->_flags |= __SERR;
+                goto input_failure;
+              }
+              break;
+            }
+          }
+          if (!(flags & SUPPRESS)) {
+            *wcp = L'\0';
+            nassigned++;
+          }
+        } else if (flags & SUPPRESS) {
+          n = 0;
+          while (!isspace(*fp->_p)) {
+            n++, fp->_r--, fp->_p++;
+            if (--width == 0) break;
+            if (fp->_r <= 0 && __srefill(fp)) break;
+          }
+          nread += n;
+        } else {
+          p0 = p = va_arg(ap, char*);
+          while (!isspace(*fp->_p)) {
+            fp->_r--;
+            *p++ = *fp->_p++;
+            if (--width == 0) break;
+            if (fp->_r <= 0 && __srefill(fp)) break;
+          }
+          *p = '\0';
+          nread += p - p0;
+          nassigned++;
+        }
+        continue;
 
-				if ((flags & SUPPRESS) == 0)
-					wcp = va_arg(ap, wchar_t *);
-				else
-					wcp = &twc;
-				n = 0;
-				while (!isspace(*fp->_p) && width != 0) {
-					if (n == (int)MB_CUR_MAX) {
-						fp->_flags |= __SERR;
-						goto input_failure;
-					}
-					buf[n++] = *fp->_p;
-					fp->_p++;
-					fp->_r--;
-					memset(&mbs, 0, sizeof(mbs));
-					nconv = mbrtowc(wcp, buf, n, &mbs);
-					if (nconv == (size_t)-1) {
-						fp->_flags |= __SERR;
-						goto input_failure;
-					}
-					if (nconv == 0)
-						*wcp = L'\0';
-					if (nconv != (size_t)-2) {
-						if (iswspace(*wcp)) {
-							while (n != 0) {
-								n--;
-								ungetc(buf[n],
-								    fp);
-							}
-							break;
-						}
-						nread += n;
-						width--;
-						if (!(flags & SUPPRESS))
-							wcp++;
-						n = 0;
-					}
-					if (fp->_r <= 0 && __srefill(fp)) {
-						if (n != 0) {
-							fp->_flags |= __SERR;
-							goto input_failure;
-						}
-						break;
-					}
-				}
-				if (!(flags & SUPPRESS)) {
-					*wcp = L'\0';
-					nassigned++;
-				}
-			} else
-#endif /* SCANF_WIDE_CHAR */
-			if (flags & SUPPRESS) {
-				n = 0;
-				while (!isspace(*fp->_p)) {
-					n++, fp->_r--, fp->_p++;
-					if (--width == 0)
-						break;
-					if (fp->_r <= 0 && __srefill(fp))
-						break;
-				}
-				nread += n;
-			} else {
-				p0 = p = va_arg(ap, char *);
-				while (!isspace(*fp->_p)) {
-					fp->_r--;
-					*p++ = *fp->_p++;
-					if (--width == 0)
-						break;
-					if (fp->_r <= 0 && __srefill(fp))
-						break;
-				}
-				*p = '\0';
-				nread += p - p0;
-				nassigned++;
-			}
-			continue;
-
-		case CT_INT:
-			/* scan an integer as if by strtoimax/strtoumax */
+      case CT_INT:
+        /* scan an integer as if by strtoimax/strtoumax */
 #ifdef hardway
-			if (width == 0 || width > sizeof(buf) - 1)
-				width = sizeof(buf) - 1;
+        if (width == 0 || width > sizeof(buf) - 1) width = sizeof(buf) - 1;
 #else
-			/* size_t is unsigned, hence this optimisation */
-			if (--width > sizeof(buf) - 2)
-				width = sizeof(buf) - 2;
-			width++;
+        /* size_t is unsigned, hence this optimisation */
+        if (--width > sizeof(buf) - 2) width = sizeof(buf) - 2;
+        width++;
 #endif
-			flags |= SIGNOK | NDIGITS | NZDIGITS;
-			for (p = buf; width; width--) {
-				c = *fp->_p;
-				/*
-				 * Switch on the character; `goto ok'
-				 * if we accept it as a part of number.
-				 */
-				switch (c) {
+        flags |= SIGNOK | NDIGITS | NZDIGITS;
+        for (p = buf; width; width--) {
+          c = *fp->_p;
+          /*
+           * Switch on the character; `goto ok'
+           * if we accept it as a part of number.
+           */
+          switch (c) {
+            /*
+             * The digit 0 is always legal, but is
+             * special.  For %i conversions, if no
+             * digits (zero or nonzero) have been
+             * scanned (only signs), we will have
+             * base==0.  In that case, we should set
+             * it to 8 and enable 0x prefixing.
+             * Also, if we have not scanned zero digits
+             * before this, do not turn off prefixing
+             * (someone else will turn it off if we
+             * have scanned any nonzero digits).
+             */
+            case '0':
+              if (base == 0) {
+                base = 8;
+                flags |= PFXOK;
+              }
+              if (flags & NZDIGITS)
+                flags &= ~(SIGNOK | NZDIGITS | NDIGITS);
+              else
+                flags &= ~(SIGNOK | PFXOK | NDIGITS);
+              goto ok;
 
-				/*
-				 * The digit 0 is always legal, but is
-				 * special.  For %i conversions, if no
-				 * digits (zero or nonzero) have been
-				 * scanned (only signs), we will have
-				 * base==0.  In that case, we should set
-				 * it to 8 and enable 0x prefixing.
-				 * Also, if we have not scanned zero digits
-				 * before this, do not turn off prefixing
-				 * (someone else will turn it off if we
-				 * have scanned any nonzero digits).
-				 */
-				case '0':
-					if (base == 0) {
-						base = 8;
-						flags |= PFXOK;
-					}
-					if (flags & NZDIGITS)
-					    flags &= ~(SIGNOK|NZDIGITS|NDIGITS);
-					else
-					    flags &= ~(SIGNOK|PFXOK|NDIGITS);
-					goto ok;
+            /* 1 through 7 always legal */
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+              base = basefix[base];
+              flags &= ~(SIGNOK | PFXOK | NDIGITS);
+              goto ok;
 
-				/* 1 through 7 always legal */
-				case '1': case '2': case '3':
-				case '4': case '5': case '6': case '7':
-					base = basefix[base];
-					flags &= ~(SIGNOK | PFXOK | NDIGITS);
-					goto ok;
+            /* digits 8 and 9 ok iff decimal or hex */
+            case '8':
+            case '9':
+              base = basefix[base];
+              if (base <= 8) break; /* not legal here */
+              flags &= ~(SIGNOK | PFXOK | NDIGITS);
+              goto ok;
 
-				/* digits 8 and 9 ok iff decimal or hex */
-				case '8': case '9':
-					base = basefix[base];
-					if (base <= 8)
-						break;	/* not legal here */
-					flags &= ~(SIGNOK | PFXOK | NDIGITS);
-					goto ok;
+            /* letters ok iff hex */
+            case 'A':
+            case 'B':
+            case 'C':
+            case 'D':
+            case 'E':
+            case 'F':
+            case 'a':
+            case 'b':
+            case 'c':
+            case 'd':
+            case 'e':
+            case 'f':
+              /* no need to fix base here */
+              if (base <= 10) break; /* not legal here */
+              flags &= ~(SIGNOK | PFXOK | NDIGITS);
+              goto ok;
 
-				/* letters ok iff hex */
-				case 'A': case 'B': case 'C':
-				case 'D': case 'E': case 'F':
-				case 'a': case 'b': case 'c':
-				case 'd': case 'e': case 'f':
-					/* no need to fix base here */
-					if (base <= 10)
-						break;	/* not legal here */
-					flags &= ~(SIGNOK | PFXOK | NDIGITS);
-					goto ok;
+            /* sign ok only as first character */
+            case '+':
+            case '-':
+              if (flags & SIGNOK) {
+                flags &= ~SIGNOK;
+                flags |= HAVESIGN;
+                goto ok;
+              }
+              break;
 
-				/* sign ok only as first character */
-				case '+': case '-':
-					if (flags & SIGNOK) {
-						flags &= ~SIGNOK;
-						flags |= HAVESIGN;
-						goto ok;
-					}
-					break;
+            /*
+             * x ok iff flag still set and 2nd char (or
+             * 3rd char if we have a sign).
+             */
+            case 'x':
+            case 'X':
+              if ((flags & PFXOK) && p == buf + 1 + !!(flags & HAVESIGN)) {
+                base = 16; /* if %i */
+                flags &= ~PFXOK;
+                goto ok;
+              }
+              break;
+          }
 
-				/*
-				 * x ok iff flag still set and 2nd char (or
-				 * 3rd char if we have a sign).
-				 */
-				case 'x': case 'X':
-					if ((flags & PFXOK) && p ==
-					    buf + 1 + !!(flags & HAVESIGN)) {
-						base = 16;	/* if %i */
-						flags &= ~PFXOK;
-						goto ok;
-					}
-					break;
-				}
+          /*
+           * If we got here, c is not a legal character
+           * for a number.  Stop accumulating digits.
+           */
+          break;
+        ok:
+          /*
+           * c is legal: store it and look at the next.
+           */
+          *p++ = c;
+          if (--fp->_r > 0)
+            fp->_p++;
+          else if (__srefill(fp))
+            break; /* EOF */
+        }
+        /*
+         * If we had only a sign, it is no good; push
+         * back the sign.  If the number ends in `x',
+         * it was [sign] '0' 'x', so push back the x
+         * and treat it as [sign] '0'.
+         */
+        if (flags & NDIGITS) {
+          if (p > buf) (void)ungetc(*(u_char*)--p, fp);
+          goto match_failure;
+        }
+        c = ((u_char*)p)[-1];
+        if (c == 'x' || c == 'X') {
+          --p;
+          (void)ungetc(c, fp);
+        }
+        if ((flags & SUPPRESS) == 0) {
+          uintmax_t res;
 
-				/*
-				 * If we got here, c is not a legal character
-				 * for a number.  Stop accumulating digits.
-				 */
-				break;
-		ok:
-				/*
-				 * c is legal: store it and look at the next.
-				 */
-				*p++ = c;
-				if (--fp->_r > 0)
-					fp->_p++;
-				else if (__srefill(fp))
-					break;		/* EOF */
-			}
-			/*
-			 * If we had only a sign, it is no good; push
-			 * back the sign.  If the number ends in `x',
-			 * it was [sign] '0' 'x', so push back the x
-			 * and treat it as [sign] '0'.
-			 */
-			if (flags & NDIGITS) {
-				if (p > buf)
-					(void) ungetc(*(u_char *)--p, fp);
-				goto match_failure;
-			}
-			c = ((u_char *)p)[-1];
-			if (c == 'x' || c == 'X') {
-				--p;
-				(void) ungetc(c, fp);
-			}
-			if ((flags & SUPPRESS) == 0) {
-				uintmax_t res;
+          *p = '\0';
+          if (flags & UNSIGNED)
+            res = strtoumax(buf, NULL, base);
+          else
+            res = strtoimax(buf, NULL, base);
+          if (flags & POINTER)
+            *va_arg(ap, void**) = (void*)(uintptr_t)res;
+          else if (flags & MAXINT)
+            *va_arg(ap, intmax_t*) = res;
+          else if (flags & LLONG)
+            *va_arg(ap, long long*) = res;
+          else if (flags & SIZEINT)
+            *va_arg(ap, ssize_t*) = res;
+          else if (flags & PTRINT)
+            *va_arg(ap, ptrdiff_t*) = res;
+          else if (flags & LONG)
+            *va_arg(ap, long*) = res;
+          else if (flags & SHORT)
+            *va_arg(ap, short*) = res;
+          else if (flags & SHORTSHORT)
+            *va_arg(ap, signed char*) = res;
+          else
+            *va_arg(ap, int*) = res;
+          nassigned++;
+        }
+        nread += p - buf;
+        break;
 
-				*p = '\0';
-				if (flags & UNSIGNED)
-					res = strtoumax(buf, NULL, base);
-				else
-					res = strtoimax(buf, NULL, base);
-				if (flags & POINTER)
-					*va_arg(ap, void **) =
-					    (void *)(uintptr_t)res;
-				else if (flags & MAXINT)
-					*va_arg(ap, intmax_t *) = res;
-				else if (flags & LLONG)
-					*va_arg(ap, long long *) = res;
-				else if (flags & SIZEINT)
-					*va_arg(ap, ssize_t *) = res;
-				else if (flags & PTRINT)
-					*va_arg(ap, ptrdiff_t *) = res;
-				else if (flags & LONG)
-					*va_arg(ap, long *) = res;
-				else if (flags & SHORT)
-					*va_arg(ap, short *) = res;
-				else if (flags & SHORTSHORT)
-					*va_arg(ap, signed char *) = res;
-				else
-					*va_arg(ap, int *) = res;
-				nassigned++;
-			}
-			nread += p - buf;
-			break;
-
-#ifdef FLOATING_POINT
-		case CT_FLOAT:
-			/* scan a floating point number as if by strtod */
-			if (width == 0 || width > sizeof(buf) - 1)
-				width = sizeof(buf) - 1;
-			if ((width = parsefloat(fp, buf, buf + width)) == 0)
-				goto match_failure;
-			if ((flags & SUPPRESS) == 0) {
-				if (flags & LONGDBL) {
-					long double res = strtold(buf, &p);
-					*va_arg(ap, long double *) = res;
-				} else if (flags & LONG) {
-					double res = strtod(buf, &p);
-					*va_arg(ap, double *) = res;
-				} else {
-					float res = strtof(buf, &p);
-					*va_arg(ap, float *) = res;
-				}
-				if ((size_t)(p - buf) != width) abort();
-				nassigned++;
-			}
-			nread += width;
-			break;
-#endif /* FLOATING_POINT */
-		}
-	}
+      case CT_FLOAT:
+        /* scan a floating point number as if by strtod */
+        if (width == 0 || width > sizeof(buf) - 1) width = sizeof(buf) - 1;
+        if ((width = parsefloat(fp, buf, buf + width)) == 0) goto match_failure;
+        if ((flags & SUPPRESS) == 0) {
+          if (flags & LONGDBL) {
+            long double res = strtold(buf, &p);
+            *va_arg(ap, long double*) = res;
+          } else if (flags & LONG) {
+            double res = strtod(buf, &p);
+            *va_arg(ap, double*) = res;
+          } else {
+            float res = strtof(buf, &p);
+            *va_arg(ap, float*) = res;
+          }
+          if ((size_t)(p - buf) != width) abort();
+          nassigned++;
+        }
+        nread += width;
+        break;
+    }
+  }
 input_failure:
-	if (nassigned == 0)
-		nassigned = -1;
+  if (nassigned == 0) nassigned = -1;
 match_failure:
-	return (nassigned);
+  return (nassigned);
 }
 
 /*
@@ -796,103 +758,94 @@
  * closing `]'.  The table has a 1 wherever characters should be
  * considered part of the scanset.
  */
-static u_char *
-__sccl(char *tab, u_char *fmt)
-{
-	int c, n, v;
+static u_char* __sccl(char* tab, u_char* fmt) {
+  int c, n, v;
 
-	/* first `clear' the whole table */
-	c = *fmt++;		/* first char hat => negated scanset */
-	if (c == '^') {
-		v = 1;		/* default => accept */
-		c = *fmt++;	/* get new first char */
-	} else
-		v = 0;		/* default => reject */
-	/* should probably use memset here */
-	for (n = 0; n < 256; n++)
-		tab[n] = v;
-	if (c == 0)
-		return (fmt - 1);/* format ended before closing ] */
+  /* first `clear' the whole table */
+  c = *fmt++; /* first char hat => negated scanset */
+  if (c == '^') {
+    v = 1;      /* default => accept */
+    c = *fmt++; /* get new first char */
+  } else
+    v = 0; /* default => reject */
+  /* should probably use memset here */
+  for (n = 0; n < 256; n++) tab[n] = v;
+  if (c == 0) return (fmt - 1); /* format ended before closing ] */
 
-	/*
-	 * Now set the entries corresponding to the actual scanset
-	 * to the opposite of the above.
-	 *
-	 * The first character may be ']' (or '-') without being special;
-	 * the last character may be '-'.
-	 */
-	v = 1 - v;
-	for (;;) {
-		tab[c] = v;		/* take character c */
-doswitch:
-		n = *fmt++;		/* and examine the next */
-		switch (n) {
+  /*
+   * Now set the entries corresponding to the actual scanset
+   * to the opposite of the above.
+   *
+   * The first character may be ']' (or '-') without being special;
+   * the last character may be '-'.
+   */
+  v = 1 - v;
+  for (;;) {
+    tab[c] = v; /* take character c */
+  doswitch:
+    n = *fmt++; /* and examine the next */
+    switch (n) {
+      case 0: /* format ended too soon */
+        return (fmt - 1);
 
-		case 0:			/* format ended too soon */
-			return (fmt - 1);
-
-		case '-':
-			/*
-			 * A scanset of the form
-			 *	[01+-]
-			 * is defined as `the digit 0, the digit 1,
-			 * the character +, the character -', but
-			 * the effect of a scanset such as
-			 *	[a-zA-Z0-9]
-			 * is implementation defined.  The V7 Unix
-			 * scanf treats `a-z' as `the letters a through
-			 * z', but treats `a-a' as `the letter a, the
-			 * character -, and the letter a'.
-			 *
-			 * For compatibility, the `-' is not considerd
-			 * to define a range if the character following
-			 * it is either a close bracket (required by ANSI)
-			 * or is not numerically greater than the character
-			 * we just stored in the table (c).
-			 */
-			n = *fmt;
-			if (n == ']' || n < c) {
-				c = '-';
-				break;	/* resume the for(;;) */
-			}
-			fmt++;
-			do {		/* fill in the range */
-				tab[++c] = v;
-			} while (c < n);
-#if 1	/* XXX another disgusting compatibility hack */
-			/*
-			 * Alas, the V7 Unix scanf also treats formats
-			 * such as [a-c-e] as `the letters a through e'.
-			 * This too is permitted by the standard....
-			 */
-			goto doswitch;
+      case '-':
+        /*
+         * A scanset of the form
+         *	[01+-]
+         * is defined as `the digit 0, the digit 1,
+         * the character +, the character -', but
+         * the effect of a scanset such as
+         *	[a-zA-Z0-9]
+         * is implementation defined.  The V7 Unix
+         * scanf treats `a-z' as `the letters a through
+         * z', but treats `a-a' as `the letter a, the
+         * character -, and the letter a'.
+         *
+         * For compatibility, the `-' is not considerd
+         * to define a range if the character following
+         * it is either a close bracket (required by ANSI)
+         * or is not numerically greater than the character
+         * we just stored in the table (c).
+         */
+        n = *fmt;
+        if (n == ']' || n < c) {
+          c = '-';
+          break; /* resume the for(;;) */
+        }
+        fmt++;
+        do { /* fill in the range */
+          tab[++c] = v;
+        } while (c < n);
+#if 1 /* XXX another disgusting compatibility hack */
+        /*
+         * Alas, the V7 Unix scanf also treats formats
+         * such as [a-c-e] as `the letters a through e'.
+         * This too is permitted by the standard....
+         */
+        goto doswitch;
 #else
-			c = *fmt++;
-			if (c == 0)
-				return (fmt - 1);
-			if (c == ']')
-				return (fmt);
+        c = *fmt++;
+        if (c == 0) return (fmt - 1);
+        if (c == ']') return (fmt);
 #endif
-			break;
+        break;
 
-		case ']':		/* end of scanset */
-			return (fmt);
+      case ']': /* end of scanset */
+        return (fmt);
 
-		default:		/* just another character */
-			c = n;
-			break;
-		}
-	}
-	/* NOTREACHED */
+      default: /* just another character */
+        c = n;
+        break;
+    }
+  }
+  /* NOTREACHED */
 }
 
-int
-vfscanf(FILE *fp, const char *fmt0, __va_list ap)
-{
-	int r;
+int vfscanf(FILE* fp, const char* fmt0, __va_list ap) {
+  int r;
 
-	FLOCKFILE(fp);
-	r = __svfscanf(fp, fmt0, ap);
-	FUNLOCKFILE(fp);
-	return (r);
+  FLOCKFILE(fp);
+  r = __svfscanf(fp, fmt0, ap);
+  FUNLOCKFILE(fp);
+  return (r);
 }
diff --git a/libc/stdio/vfwprintf.c b/libc/stdio/vfwprintf.c
new file mode 100644
index 0000000..647b59a
--- /dev/null
+++ b/libc/stdio/vfwprintf.c
@@ -0,0 +1,1436 @@
+/*	$OpenBSD: vfwprintf.c,v 1.15 2015/12/28 22:08:18 mmcc Exp $ */
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ */
+
+/*
+ * Actual wprintf innards.
+ *
+ * This code is large and complicated...
+ */
+
+#include <sys/mman.h>
+#include <sys/types.h>
+
+#include <errno.h>
+#include <langinfo.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <wchar.h>
+
+#include "fvwrite.h"
+#include "local.h"
+
+union arg {
+  int intarg;
+  unsigned int uintarg;
+  long longarg;
+  unsigned long ulongarg;
+  long long longlongarg;
+  unsigned long long ulonglongarg;
+  ptrdiff_t ptrdiffarg;
+  size_t sizearg;
+  ssize_t ssizearg;
+  intmax_t intmaxarg;
+  uintmax_t uintmaxarg;
+  void* pvoidarg;
+  char* pchararg;
+  signed char* pschararg;
+  short* pshortarg;
+  int* pintarg;
+  long* plongarg;
+  long long* plonglongarg;
+  ptrdiff_t* pptrdiffarg;
+  ssize_t* pssizearg;
+  intmax_t* pintmaxarg;
+  double doublearg;
+  long double longdoublearg;
+  wint_t wintarg;
+  wchar_t* pwchararg;
+};
+
+static int __find_arguments(const wchar_t* fmt0, va_list ap, union arg** argtable,
+                            size_t* argtablesiz);
+static int __grow_type_table(unsigned char** typetable, int* tablesize);
+
+/*
+ * Helper function for `fprintf to unbuffered unix file': creates a
+ * temporary buffer.  We only work on write-only files; this avoids
+ * worries about ungetc buffers and so forth.
+ */
+static int __sbprintf(FILE* fp, const wchar_t* fmt, va_list ap) {
+  int ret;
+  FILE fake;
+  struct __sfileext fakeext;
+  unsigned char buf[BUFSIZ];
+
+  _FILEEXT_SETUP(&fake, &fakeext);
+  /* copy the important variables */
+  fake._flags = fp->_flags & ~__SNBF;
+  fake._file = fp->_file;
+  fake._cookie = fp->_cookie;
+  fake._write = fp->_write;
+
+  /* set up the buffer */
+  fake._bf._base = fake._p = buf;
+  fake._bf._size = fake._w = sizeof(buf);
+  fake._lbfsize = 0; /* not actually used, but Just In Case */
+
+  /* do the work, then copy any error status */
+  ret = __vfwprintf(&fake, fmt, ap);
+  if (ret >= 0 && __sflush(&fake)) ret = EOF;
+  if (fake._flags & __SERR) fp->_flags |= __SERR;
+  return (ret);
+}
+
+/*
+ * Like __fputwc_unlock, but handles fake string (__SSTR) files properly.
+ * File must already be locked.
+ */
+static wint_t __xfputwc(wchar_t wc, FILE* fp) {
+  mbstate_t mbs;
+  char buf[MB_LEN_MAX];
+  struct __suio uio;
+  struct __siov iov;
+  size_t len;
+
+  if ((fp->_flags & __SSTR) == 0) return (__fputwc_unlock(wc, fp));
+
+  bzero(&mbs, sizeof(mbs));
+  len = wcrtomb(buf, wc, &mbs);
+  if (len == (size_t)-1) {
+    fp->_flags |= __SERR;
+    errno = EILSEQ;
+    return (WEOF);
+  }
+  uio.uio_iov = &iov;
+  uio.uio_resid = len;
+  uio.uio_iovcnt = 1;
+  iov.iov_base = buf;
+  iov.iov_len = len;
+  return (__sfvwrite(fp, &uio) != EOF ? (wint_t)wc : WEOF);
+}
+
+/*
+ * Convert a multibyte character string argument for the %s format to a wide
+ * string representation. ``prec'' specifies the maximum number of bytes
+ * to output. If ``prec'' is greater than or equal to zero, we can't assume
+ * that the multibyte character string ends in a null character.
+ *
+ * Returns NULL on failure.
+ * To find out what happened check errno for ENOMEM, EILSEQ and EINVAL.
+ */
+static wchar_t* __mbsconv(char* mbsarg, int prec) {
+  mbstate_t mbs;
+  wchar_t *convbuf, *wcp;
+  const char* p;
+  size_t insize, nchars, nconv;
+
+  if (mbsarg == NULL) return (NULL);
+
+  /*
+   * Supplied argument is a multibyte string; convert it to wide
+   * characters first.
+   */
+  if (prec >= 0) {
+    /*
+     * String is not guaranteed to be NUL-terminated. Find the
+     * number of characters to print.
+     */
+    p = mbsarg;
+    insize = nchars = nconv = 0;
+    bzero(&mbs, sizeof(mbs));
+    while (nchars != (size_t)prec) {
+      nconv = mbrlen(p, MB_CUR_MAX, &mbs);
+      if (nconv == (size_t)0 || nconv == (size_t)-1 || nconv == (size_t)-2) break;
+      p += nconv;
+      nchars++;
+      insize += nconv;
+    }
+    if (nconv == (size_t)-1 || nconv == (size_t)-2) return (NULL);
+  } else
+    insize = strlen(mbsarg);
+
+  /*
+   * Allocate buffer for the result and perform the conversion,
+   * converting at most `size' bytes of the input multibyte string to
+   * wide characters for printing.
+   */
+  convbuf = calloc(insize + 1, sizeof(*convbuf));
+  if (convbuf == NULL) return (NULL);
+  wcp = convbuf;
+  p = mbsarg;
+  bzero(&mbs, sizeof(mbs));
+  nconv = 0;
+  while (insize != 0) {
+    nconv = mbrtowc(wcp, p, insize, &mbs);
+    if (nconv == 0 || nconv == (size_t)-1 || nconv == (size_t)-2) break;
+    wcp++;
+    p += nconv;
+    insize -= nconv;
+  }
+  if (nconv == (size_t)-1 || nconv == (size_t)-2) {
+    free(convbuf);
+    return (NULL);
+  }
+  *wcp = '\0';
+
+  return (convbuf);
+}
+
+#include <float.h>
+#include <locale.h>
+#include <math.h>
+#include "floatio.h"
+#include "gdtoa.h"
+
+#define DEFPREC 6
+
+static int exponent(wchar_t*, int, int);
+
+/*
+ * The size of the buffer we use as scratch space for integer
+ * conversions, among other things.  Technically, we would need the
+ * most space for base 10 conversions with thousands' grouping
+ * characters between each pair of digits.  100 bytes is a
+ * conservative overestimate even for a 128-bit uintmax_t.
+ */
+#define BUF 100
+
+#define STATIC_ARG_TBL_SIZE 8 /* Size of static argument table. */
+
+/*
+ * Macros for converting digits to letters and vice versa
+ */
+#define to_digit(c) ((c) - '0')
+#define is_digit(c) ((unsigned)to_digit(c) <= 9)
+#define to_char(n) ((wchar_t)((n) + '0'))
+
+/*
+ * Flags used during conversion.
+ */
+#define ALT 0x0001      /* alternate form */
+#define LADJUST 0x0004  /* left adjustment */
+#define LONGDBL 0x0008  /* long double */
+#define LONGINT 0x0010  /* long integer */
+#define LLONGINT 0x0020 /* long long integer */
+#define SHORTINT 0x0040 /* short integer */
+#define ZEROPAD 0x0080  /* zero (as opposed to blank) pad */
+#define FPT 0x0100      /* Floating point number */
+#define PTRINT 0x0200   /* (unsigned) ptrdiff_t */
+#define SIZEINT 0x0400  /* (signed) size_t */
+#define CHARINT 0x0800  /* 8 bit integer */
+#define MAXINT 0x1000   /* largest integer size (intmax_t) */
+
+int __vfwprintf(FILE* __restrict fp, const wchar_t* __restrict fmt0, __va_list ap) {
+  wchar_t* fmt;  /* format string */
+  wchar_t ch;    /* character from fmt */
+  int n, n2, n3; /* handy integers (short term usage) */
+  wchar_t* cp;   /* handy char pointer (short term usage) */
+  int flags;     /* flags as above */
+  int ret;       /* return value accumulator */
+  int width;     /* width from format (%8d), or 0 */
+  int prec;      /* precision from format; <0 for N/A */
+  wchar_t sign;  /* sign prefix (' ', '+', '-', or \0) */
+  /*
+   * We can decompose the printed representation of floating
+   * point numbers into several parts, some of which may be empty:
+   *
+   * [+|-| ] [0x|0X] MMM . NNN [e|E|p|P] [+|-] ZZ
+   *    A       B     ---C---      D       E   F
+   *
+   * A:	'sign' holds this value if present; '\0' otherwise
+   * B:	ox[1] holds the 'x' or 'X'; '\0' if not hexadecimal
+   * C:	cp points to the string MMMNNN.  Leading and trailing
+   *	zeros are not in the string and must be added.
+   * D:	expchar holds this character; '\0' if no exponent, e.g. %f
+   * F:	at least two digits for decimal, at least one digit for hex
+   */
+  char* decimal_point = NULL;
+  int signflag; /* true if float is negative */
+  union {       /* floating point arguments %[aAeEfFgG] */
+    double dbl;
+    long double ldbl;
+  } fparg;
+  int expt;                      /* integer value of exponent */
+  char expchar;                  /* exponent character: [eEpP\0] */
+  char* dtoaend;                 /* pointer to end of converted digits */
+  int expsize;                   /* character count for expstr */
+  int lead;                      /* sig figs before decimal or group sep */
+  int ndig;                      /* actual number of digits returned by dtoa */
+  wchar_t expstr[MAXEXPDIG + 2]; /* buffer for exponent string: e+ZZZ */
+  char* dtoaresult = NULL;
+
+  uintmax_t _umax;             /* integer arguments %[diouxX] */
+  enum { OCT, DEC, HEX } base; /* base for %[diouxX] conversion */
+  int dprec;                   /* a copy of prec if %[diouxX], 0 otherwise */
+  int realsz;                  /* field size expanded by dprec */
+  int size;                    /* size of converted field or string */
+  const char* xdigs;           /* digits for %[xX] conversion */
+  wchar_t buf[BUF];            /* buffer with space for digits of uintmax_t */
+  wchar_t ox[2];               /* space for 0x; ox[1] is either x, X, or \0 */
+  union arg* argtable;         /* args, built due to positional arg */
+  union arg statargtable[STATIC_ARG_TBL_SIZE];
+  size_t argtablesiz;
+  int nextarg;      /* 1-based argument index */
+  va_list orgap;    /* original argument pointer */
+  wchar_t* convbuf; /* buffer for multibyte to wide conversion */
+
+  /*
+   * Choose PADSIZE to trade efficiency vs. size.  If larger printf
+   * fields occur frequently, increase PADSIZE and make the initialisers
+   * below longer.
+   */
+#define PADSIZE 16 /* pad chunk size */
+  static wchar_t blanks[PADSIZE] = { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
+                                     ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' };
+  static wchar_t zeroes[PADSIZE] = { '0', '0', '0', '0', '0', '0', '0', '0',
+                                     '0', '0', '0', '0', '0', '0', '0', '0' };
+
+  static const char xdigs_lower[16] = "0123456789abcdef";
+  static const char xdigs_upper[16] = "0123456789ABCDEF";
+
+  /*
+   * BEWARE, these `goto error' on error, PRINT uses 'n3',
+   * PAD uses `n' and 'n3', and PRINTANDPAD uses 'n', 'n2', and 'n3'.
+   */
+#define PRINT(ptr, len)                                   \
+  do {                                                    \
+    for (n3 = 0; n3 < (len); n3++) {                      \
+      if ((__xfputwc((ptr)[n3], fp)) == WEOF) goto error; \
+    }                                                     \
+  } while (0)
+#define PAD(howmany, with)     \
+  do {                         \
+    if ((n = (howmany)) > 0) { \
+      while (n > PADSIZE) {    \
+        PRINT(with, PADSIZE);  \
+        n -= PADSIZE;          \
+      }                        \
+      PRINT(with, n);          \
+    }                          \
+  } while (0)
+#define PRINTANDPAD(p, ep, len, with)       \
+  do {                                      \
+    n2 = (ep) - (p);                        \
+    if (n2 > (len)) n2 = (len);             \
+    if (n2 > 0) PRINT((p), n2);             \
+    PAD((len) - (n2 > 0 ? n2 : 0), (with)); \
+  } while (0)
+
+  /*
+   * To extend shorts properly, we need both signed and unsigned
+   * argument extraction methods.
+   */
+#define SARG()                                                                               \
+  ((intmax_t)(flags & MAXINT                                                                 \
+                  ? GETARG(intmax_t)                                                         \
+                  : flags & LLONGINT                                                         \
+                        ? GETARG(long long)                                                  \
+                        : flags & LONGINT                                                    \
+                              ? GETARG(long)                                                 \
+                              : flags & PTRINT                                               \
+                                    ? GETARG(ptrdiff_t)                                      \
+                                    : flags & SIZEINT                                        \
+                                          ? GETARG(ssize_t)                                  \
+                                          : flags & SHORTINT                                 \
+                                                ? (short)GETARG(int)                         \
+                                                : flags & CHARINT ? (signed char)GETARG(int) \
+                                                                  : GETARG(int)))
+#define UARG()                                                                                \
+  ((uintmax_t)(flags & MAXINT                                                                 \
+                   ? GETARG(uintmax_t)                                                        \
+                   : flags & LLONGINT                                                         \
+                         ? GETARG(unsigned long long)                                         \
+                         : flags & LONGINT                                                    \
+                               ? GETARG(unsigned long)                                        \
+                               : flags & PTRINT ? (uintptr_t)GETARG(ptrdiff_t) : /* XXX */    \
+                                     flags & SIZEINT                                          \
+                                         ? GETARG(size_t)                                     \
+                                         : flags & SHORTINT                                   \
+                                               ? (unsigned short)GETARG(int)                  \
+                                               : flags & CHARINT ? (unsigned char)GETARG(int) \
+                                                                 : GETARG(unsigned int)))
+
+  /*
+   * Append a digit to a value and check for overflow.
+   */
+#define APPEND_DIGIT(val, dig)                            \
+  do {                                                    \
+    if ((val) > INT_MAX / 10) goto overflow;              \
+    (val) *= 10;                                          \
+    if ((val) > INT_MAX - to_digit((dig))) goto overflow; \
+    (val) += to_digit((dig));                             \
+  } while (0)
+
+  /*
+   * Get * arguments, including the form *nn$.  Preserve the nextarg
+   * that the argument can be gotten once the type is determined.
+   */
+#define GETASTER(val)                                         \
+  n2 = 0;                                                     \
+  cp = fmt;                                                   \
+  while (is_digit(*cp)) {                                     \
+    APPEND_DIGIT(n2, *cp);                                    \
+    cp++;                                                     \
+  }                                                           \
+  if (*cp == '$') {                                           \
+    int hold = nextarg;                                       \
+    if (argtable == NULL) {                                   \
+      argtable = statargtable;                                \
+      __find_arguments(fmt0, orgap, &argtable, &argtablesiz); \
+    }                                                         \
+    nextarg = n2;                                             \
+    val = GETARG(int);                                        \
+    nextarg = hold;                                           \
+    fmt = ++cp;                                               \
+  } else {                                                    \
+    val = GETARG(int);                                        \
+  }
+
+/*
+ * Get the argument indexed by nextarg.   If the argument table is
+ * built, use it to get the argument.  If its not, get the next
+ * argument (and arguments must be gotten sequentially).
+ */
+#define GETARG(type) \
+  ((argtable != NULL) ? *((type*)(&argtable[nextarg++])) : (nextarg++, va_arg(ap, type)))
+
+  _SET_ORIENTATION(fp, 1);
+  /* sorry, fwprintf(read_only_file, "") returns EOF, not 0 */
+  if (cantwrite(fp)) {
+    errno = EBADF;
+    return (EOF);
+  }
+
+  /* optimise fwprintf(stderr) (and other unbuffered Unix files) */
+  if ((fp->_flags & (__SNBF | __SWR | __SRW)) == (__SNBF | __SWR) && fp->_file >= 0)
+    return (__sbprintf(fp, fmt0, ap));
+
+  fmt = (wchar_t*)fmt0;
+  argtable = NULL;
+  nextarg = 1;
+  va_copy(orgap, ap);
+  ret = 0;
+  convbuf = NULL;
+
+  /*
+   * Scan the format for conversions (`%' character).
+   */
+  for (;;) {
+    for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) continue;
+    if (fmt != cp) {
+      ptrdiff_t m = fmt - cp;
+      if (m < 0 || m > INT_MAX - ret) goto overflow;
+      PRINT(cp, m);
+      ret += m;
+    }
+    if (ch == '\0') goto done;
+    fmt++; /* skip over '%' */
+
+    flags = 0;
+    dprec = 0;
+    width = 0;
+    prec = -1;
+    sign = '\0';
+    ox[1] = '\0';
+
+  rflag:
+    ch = *fmt++;
+  reswitch:
+    switch (ch) {
+      case ' ':
+        /*
+         * ``If the space and + flags both appear, the space
+         * flag will be ignored.''
+         *	-- ANSI X3J11
+         */
+        if (!sign) sign = ' ';
+        goto rflag;
+      case '#':
+        flags |= ALT;
+        goto rflag;
+      case '\'':
+        /* grouping not implemented */
+        goto rflag;
+      case '*':
+        /*
+         * ``A negative field width argument is taken as a
+         * - flag followed by a positive field width.''
+         *	-- ANSI X3J11
+         * They don't exclude field widths read from args.
+         */
+        GETASTER(width);
+        if (width >= 0) goto rflag;
+        if (width == INT_MIN) goto overflow;
+        width = -width;
+        /* FALLTHROUGH */
+      case '-':
+        flags |= LADJUST;
+        goto rflag;
+      case '+':
+        sign = '+';
+        goto rflag;
+      case '.':
+        if ((ch = *fmt++) == '*') {
+          GETASTER(n);
+          prec = n < 0 ? -1 : n;
+          goto rflag;
+        }
+        n = 0;
+        while (is_digit(ch)) {
+          APPEND_DIGIT(n, ch);
+          ch = *fmt++;
+        }
+        if (ch == '$') {
+          nextarg = n;
+          if (argtable == NULL) {
+            argtable = statargtable;
+            __find_arguments(fmt0, orgap, &argtable, &argtablesiz);
+          }
+          goto rflag;
+        }
+        prec = n;
+        goto reswitch;
+      case '0':
+        /*
+         * ``Note that 0 is taken as a flag, not as the
+         * beginning of a field width.''
+         *	-- ANSI X3J11
+         */
+        flags |= ZEROPAD;
+        goto rflag;
+      case '1':
+      case '2':
+      case '3':
+      case '4':
+      case '5':
+      case '6':
+      case '7':
+      case '8':
+      case '9':
+        n = 0;
+        do {
+          APPEND_DIGIT(n, ch);
+          ch = *fmt++;
+        } while (is_digit(ch));
+        if (ch == '$') {
+          nextarg = n;
+          if (argtable == NULL) {
+            argtable = statargtable;
+            __find_arguments(fmt0, orgap, &argtable, &argtablesiz);
+          }
+          goto rflag;
+        }
+        width = n;
+        goto reswitch;
+      case 'L':
+        flags |= LONGDBL;
+        goto rflag;
+      case 'h':
+        if (*fmt == 'h') {
+          fmt++;
+          flags |= CHARINT;
+        } else {
+          flags |= SHORTINT;
+        }
+        goto rflag;
+      case 'j':
+        flags |= MAXINT;
+        goto rflag;
+      case 'l':
+        if (*fmt == 'l') {
+          fmt++;
+          flags |= LLONGINT;
+        } else {
+          flags |= LONGINT;
+        }
+        goto rflag;
+      case 'q':
+        flags |= LLONGINT;
+        goto rflag;
+      case 't':
+        flags |= PTRINT;
+        goto rflag;
+      case 'z':
+        flags |= SIZEINT;
+        goto rflag;
+      case 'C':
+        flags |= LONGINT;
+        /*FALLTHROUGH*/
+      case 'c':
+        if (flags & LONGINT)
+          *(cp = buf) = (wchar_t)GETARG(wint_t);
+        else
+          *(cp = buf) = (wchar_t)btowc(GETARG(int));
+        size = 1;
+        sign = '\0';
+        break;
+      case 'D':
+        flags |= LONGINT;
+        /*FALLTHROUGH*/
+      case 'd':
+      case 'i':
+        _umax = SARG();
+        if ((intmax_t)_umax < 0) {
+          _umax = -_umax;
+          sign = '-';
+        }
+        base = DEC;
+        goto number;
+      case 'a':
+      case 'A':
+        if (ch == 'a') {
+          ox[1] = 'x';
+          xdigs = xdigs_lower;
+          expchar = 'p';
+        } else {
+          ox[1] = 'X';
+          xdigs = xdigs_upper;
+          expchar = 'P';
+        }
+        if (prec >= 0) prec++;
+        if (dtoaresult) __freedtoa(dtoaresult);
+        if (flags & LONGDBL) {
+          fparg.ldbl = GETARG(long double);
+          dtoaresult = __hldtoa(fparg.ldbl, xdigs, prec, &expt, &signflag, &dtoaend);
+          if (dtoaresult == NULL) {
+            errno = ENOMEM;
+            goto error;
+          }
+        } else {
+          fparg.dbl = GETARG(double);
+          dtoaresult = __hdtoa(fparg.dbl, xdigs, prec, &expt, &signflag, &dtoaend);
+          if (dtoaresult == NULL) {
+            errno = ENOMEM;
+            goto error;
+          }
+        }
+        if (prec < 0) prec = dtoaend - dtoaresult;
+        if (expt == INT_MAX) ox[1] = '\0';
+        free(convbuf);
+        cp = convbuf = __mbsconv(dtoaresult, -1);
+        if (cp == NULL) goto error;
+        ndig = dtoaend - dtoaresult;
+        goto fp_common;
+      case 'e':
+      case 'E':
+        expchar = ch;
+        if (prec < 0) /* account for digit before decpt */
+          prec = DEFPREC + 1;
+        else
+          prec++;
+        goto fp_begin;
+      case 'f':
+      case 'F':
+        expchar = '\0';
+        goto fp_begin;
+      case 'g':
+      case 'G':
+        expchar = ch - ('g' - 'e');
+        if (prec == 0) prec = 1;
+      fp_begin:
+        if (prec < 0) prec = DEFPREC;
+        if (dtoaresult) __freedtoa(dtoaresult);
+        if (flags & LONGDBL) {
+          fparg.ldbl = GETARG(long double);
+          dtoaresult = __ldtoa(&fparg.ldbl, expchar ? 2 : 3, prec, &expt, &signflag, &dtoaend);
+          if (dtoaresult == NULL) {
+            errno = ENOMEM;
+            goto error;
+          }
+        } else {
+          fparg.dbl = GETARG(double);
+          dtoaresult = __dtoa(fparg.dbl, expchar ? 2 : 3, prec, &expt, &signflag, &dtoaend);
+          if (dtoaresult == NULL) {
+            errno = ENOMEM;
+            goto error;
+          }
+          if (expt == 9999) expt = INT_MAX;
+        }
+        free(convbuf);
+        cp = convbuf = __mbsconv(dtoaresult, -1);
+        if (cp == NULL) goto error;
+        ndig = dtoaend - dtoaresult;
+      fp_common:
+        if (signflag) sign = '-';
+        if (expt == INT_MAX) { /* inf or nan */
+          if (*cp == 'N')
+            cp = (ch >= 'a') ? L"nan" : L"NAN";
+          else
+            cp = (ch >= 'a') ? L"inf" : L"INF";
+          size = 3;
+          flags &= ~ZEROPAD;
+          break;
+        }
+        flags |= FPT;
+        if (ch == 'g' || ch == 'G') {
+          if (expt > -4 && expt <= prec) {
+            /* Make %[gG] smell like %[fF] */
+            expchar = '\0';
+            if (flags & ALT)
+              prec -= expt;
+            else
+              prec = ndig - expt;
+            if (prec < 0) prec = 0;
+          } else {
+            /*
+             * Make %[gG] smell like %[eE], but
+             * trim trailing zeroes if no # flag.
+             */
+            if (!(flags & ALT)) prec = ndig;
+          }
+        }
+        if (expchar) {
+          expsize = exponent(expstr, expt - 1, expchar);
+          size = expsize + prec;
+          if (prec > 1 || flags & ALT) ++size;
+        } else {
+          /* space for digits before decimal point */
+          if (expt > 0)
+            size = expt;
+          else /* "0" */
+            size = 1;
+          /* space for decimal pt and following digits */
+          if (prec || flags & ALT) size += prec + 1;
+          lead = expt;
+        }
+        break;
+#ifndef NO_PRINTF_PERCENT_N
+      case 'n':
+        if (flags & LLONGINT)
+          *GETARG(long long*) = ret;
+        else if (flags & LONGINT)
+          *GETARG(long*) = ret;
+        else if (flags & SHORTINT)
+          *GETARG(short*) = ret;
+        else if (flags & CHARINT)
+          *GETARG(signed char*) = ret;
+        else if (flags & PTRINT)
+          *GETARG(ptrdiff_t*) = ret;
+        else if (flags & SIZEINT)
+          *GETARG(ssize_t*) = ret;
+        else if (flags & MAXINT)
+          *GETARG(intmax_t*) = ret;
+        else
+          *GETARG(int*) = ret;
+        continue; /* no output */
+#endif            /* NO_PRINTF_PERCENT_N */
+      case 'O':
+        flags |= LONGINT;
+        /*FALLTHROUGH*/
+      case 'o':
+        _umax = UARG();
+        base = OCT;
+        goto nosign;
+      case 'p':
+        /*
+         * ``The argument shall be a pointer to void.  The
+         * value of the pointer is converted to a sequence
+         * of printable characters, in an implementation-
+         * defined manner.''
+         *	-- ANSI X3J11
+         */
+        _umax = (u_long)GETARG(void*);
+        base = HEX;
+        xdigs = xdigs_lower;
+        ox[1] = 'x';
+        goto nosign;
+      case 'S':
+        flags |= LONGINT;
+        /*FALLTHROUGH*/
+      case 's':
+        if (flags & LONGINT) {
+          if ((cp = GETARG(wchar_t*)) == NULL) cp = L"(null)";
+        } else {
+          char* mbsarg;
+          if ((mbsarg = GETARG(char*)) == NULL) mbsarg = "(null)";
+          free(convbuf);
+          convbuf = __mbsconv(mbsarg, prec);
+          if (convbuf == NULL) {
+            fp->_flags |= __SERR;
+            goto error;
+          } else
+            cp = convbuf;
+        }
+        if (prec >= 0) {
+          /*
+           * can't use wcslen; can only look for the
+           * NUL in the first `prec' characters, and
+           * wcslen() will go further.
+           */
+          wchar_t* p = wmemchr(cp, 0, prec);
+
+          size = p ? (p - cp) : prec;
+        } else {
+          size_t len;
+
+          if ((len = wcslen(cp)) > INT_MAX) goto overflow;
+          size = (int)len;
+        }
+        sign = '\0';
+        break;
+      case 'U':
+        flags |= LONGINT;
+        /*FALLTHROUGH*/
+      case 'u':
+        _umax = UARG();
+        base = DEC;
+        goto nosign;
+      case 'X':
+        xdigs = xdigs_upper;
+        goto hex;
+      case 'x':
+        xdigs = xdigs_lower;
+      hex:
+        _umax = UARG();
+        base = HEX;
+        /* leading 0x/X only if non-zero */
+        if (flags & ALT && _umax != 0) ox[1] = ch;
+
+        /* unsigned conversions */
+      nosign:
+        sign = '\0';
+        /*
+         * ``... diouXx conversions ... if a precision is
+         * specified, the 0 flag will be ignored.''
+         *	-- ANSI X3J11
+         */
+      number:
+        if ((dprec = prec) >= 0) flags &= ~ZEROPAD;
+
+        /*
+         * ``The result of converting a zero value with an
+         * explicit precision of zero is no characters.''
+         *	-- ANSI X3J11
+         */
+        cp = buf + BUF;
+        if (_umax != 0 || prec != 0) {
+          /*
+           * Unsigned mod is hard, and unsigned mod
+           * by a constant is easier than that by
+           * a variable; hence this switch.
+           */
+          switch (base) {
+            case OCT:
+              do {
+                *--cp = to_char(_umax & 7);
+                _umax >>= 3;
+              } while (_umax);
+              /* handle octal leading 0 */
+              if (flags & ALT && *cp != '0') *--cp = '0';
+              break;
+
+            case DEC:
+              /* many numbers are 1 digit */
+              while (_umax >= 10) {
+                *--cp = to_char(_umax % 10);
+                _umax /= 10;
+              }
+              *--cp = to_char(_umax);
+              break;
+
+            case HEX:
+              do {
+                *--cp = xdigs[_umax & 15];
+                _umax >>= 4;
+              } while (_umax);
+              break;
+
+            default:
+              cp = L"bug in vfwprintf: bad base";
+              size = wcslen(cp);
+              goto skipsize;
+          }
+        }
+        size = buf + BUF - cp;
+        if (size > BUF) /* should never happen */
+          abort();
+      skipsize:
+        break;
+      default: /* "%?" prints ?, unless ? is NUL */
+        if (ch == '\0') goto done;
+        /* pretend it was %c with argument ch */
+        cp = buf;
+        *cp = ch;
+        size = 1;
+        sign = '\0';
+        break;
+    }
+
+    /*
+     * All reasonable formats wind up here.  At this point, `cp'
+     * points to a string which (if not flags&LADJUST) should be
+     * padded out to `width' places.  If flags&ZEROPAD, it should
+     * first be prefixed by any sign or other prefix; otherwise,
+     * it should be blank padded before the prefix is emitted.
+     * After any left-hand padding and prefixing, emit zeroes
+     * required by a decimal %[diouxX] precision, then print the
+     * string proper, then emit zeroes required by any leftover
+     * floating precision; finally, if LADJUST, pad with blanks.
+     *
+     * Compute actual size, so we know how much to pad.
+     * size excludes decimal prec; realsz includes it.
+     */
+    realsz = dprec > size ? dprec : size;
+    if (sign) realsz++;
+    if (ox[1]) realsz += 2;
+
+    /* right-adjusting blank padding */
+    if ((flags & (LADJUST | ZEROPAD)) == 0) PAD(width - realsz, blanks);
+
+    /* prefix */
+    if (sign) PRINT(&sign, 1);
+    if (ox[1]) { /* ox[1] is either x, X, or \0 */
+      ox[0] = '0';
+      PRINT(ox, 2);
+    }
+
+    /* right-adjusting zero padding */
+    if ((flags & (LADJUST | ZEROPAD)) == ZEROPAD) PAD(width - realsz, zeroes);
+
+    /* leading zeroes from decimal precision */
+    PAD(dprec - size, zeroes);
+
+    /* the string or number proper */
+    if ((flags & FPT) == 0) {
+      PRINT(cp, size);
+    } else { /* glue together f_p fragments */
+      if (decimal_point == NULL) decimal_point = nl_langinfo(RADIXCHAR);
+      if (!expchar) { /* %[fF] or sufficiently short %[gG] */
+        if (expt <= 0) {
+          PRINT(zeroes, 1);
+          if (prec || flags & ALT) PRINT(decimal_point, 1);
+          PAD(-expt, zeroes);
+          /* already handled initial 0's */
+          prec += expt;
+        } else {
+          PRINTANDPAD(cp, convbuf + ndig, lead, zeroes);
+          cp += lead;
+          if (prec || flags & ALT) PRINT(decimal_point, 1);
+        }
+        PRINTANDPAD(cp, convbuf + ndig, prec, zeroes);
+      } else { /* %[eE] or sufficiently long %[gG] */
+        if (prec > 1 || flags & ALT) {
+          buf[0] = *cp++;
+          buf[1] = *decimal_point;
+          PRINT(buf, 2);
+          PRINT(cp, ndig - 1);
+          PAD(prec - ndig, zeroes);
+        } else { /* XeYYY */
+          PRINT(cp, 1);
+        }
+        PRINT(expstr, expsize);
+      }
+    }
+    /* left-adjusting padding (always blank) */
+    if (flags & LADJUST) PAD(width - realsz, blanks);
+
+    /* finally, adjust ret */
+    if (width < realsz) width = realsz;
+    if (width > INT_MAX - ret) goto overflow;
+    ret += width;
+  }
+done:
+error:
+  va_end(orgap);
+  if (__sferror(fp)) ret = -1;
+  goto finish;
+
+overflow:
+  errno = ENOMEM;
+  ret = -1;
+
+finish:
+  free(convbuf);
+  if (dtoaresult) __freedtoa(dtoaresult);
+  if (argtable != NULL && argtable != statargtable) {
+    munmap(argtable, argtablesiz);
+    argtable = NULL;
+  }
+  return (ret);
+}
+
+int vfwprintf(FILE* __restrict fp, const wchar_t* __restrict fmt0, __va_list ap) {
+  int r;
+
+  FLOCKFILE(fp);
+  r = __vfwprintf(fp, fmt0, ap);
+  FUNLOCKFILE(fp);
+
+  return (r);
+}
+DEF_STRONG(vfwprintf);
+
+/*
+ * Type ids for argument type table.
+ */
+#define T_UNUSED 0
+#define T_SHORT 1
+#define T_U_SHORT 2
+#define TP_SHORT 3
+#define T_INT 4
+#define T_U_INT 5
+#define TP_INT 6
+#define T_LONG 7
+#define T_U_LONG 8
+#define TP_LONG 9
+#define T_LLONG 10
+#define T_U_LLONG 11
+#define TP_LLONG 12
+#define T_DOUBLE 13
+#define T_LONG_DOUBLE 14
+#define TP_CHAR 15
+#define TP_VOID 16
+#define T_PTRINT 17
+#define TP_PTRINT 18
+#define T_SIZEINT 19
+#define T_SSIZEINT 20
+#define TP_SSIZEINT 21
+#define T_MAXINT 22
+#define T_MAXUINT 23
+#define TP_MAXINT 24
+#define T_CHAR 25
+#define T_U_CHAR 26
+#define T_WINT 27
+#define TP_WCHAR 28
+
+/*
+ * Find all arguments when a positional parameter is encountered.  Returns a
+ * table, indexed by argument number, of pointers to each arguments.  The
+ * initial argument table should be an array of STATIC_ARG_TBL_SIZE entries.
+ * It will be replaced with a mmap-ed one if it overflows (malloc cannot be
+ * used since we are attempting to make snprintf thread safe, and alloca is
+ * problematic since we have nested functions..)
+ */
+static int __find_arguments(const wchar_t* fmt0, va_list ap, union arg** argtable,
+                            size_t* argtablesiz) {
+  wchar_t* fmt;             /* format string */
+  int ch;                   /* character from fmt */
+  int n, n2;                /* handy integer (short term usage) */
+  wchar_t* cp;              /* handy char pointer (short term usage) */
+  int flags;                /* flags as above */
+  unsigned char* typetable; /* table of types */
+  unsigned char stattypetable[STATIC_ARG_TBL_SIZE];
+  int tablesize; /* current size of type table */
+  int tablemax;  /* largest used index in table */
+  int nextarg;   /* 1-based argument index */
+  int ret = 0;   /* return value */
+
+  /*
+   * Add an argument type to the table, expanding if necessary.
+   */
+#define ADDTYPE(type)                                                      \
+  ((nextarg >= tablesize) ? __grow_type_table(&typetable, &tablesize) : 0, \
+   (nextarg > tablemax) ? tablemax = nextarg : 0, typetable[nextarg++] = type)
+
+#define ADDSARG()                                                                             \
+  ((flags & MAXINT)                                                                           \
+       ? ADDTYPE(T_MAXINT)                                                                    \
+       : ((flags & PTRINT) ? ADDTYPE(T_PTRINT)                                                \
+                           : ((flags & SIZEINT)                                               \
+                                  ? ADDTYPE(T_SSIZEINT)                                       \
+                                  : ((flags & LLONGINT)                                       \
+                                         ? ADDTYPE(T_LLONG)                                   \
+                                         : ((flags & LONGINT)                                 \
+                                                ? ADDTYPE(T_LONG)                             \
+                                                : ((flags & SHORTINT)                         \
+                                                       ? ADDTYPE(T_SHORT)                     \
+                                                       : ((flags & CHARINT) ? ADDTYPE(T_CHAR) \
+                                                                            : ADDTYPE(T_INT))))))))
+
+#define ADDUARG()                                                                  \
+  ((flags & MAXINT)                                                                \
+       ? ADDTYPE(T_MAXUINT)                                                        \
+       : ((flags & PTRINT)                                                         \
+              ? ADDTYPE(T_PTRINT)                                                  \
+              : ((flags & SIZEINT)                                                 \
+                     ? ADDTYPE(T_SIZEINT)                                          \
+                     : ((flags & LLONGINT)                                         \
+                            ? ADDTYPE(T_U_LLONG)                                   \
+                            : ((flags & LONGINT)                                   \
+                                   ? ADDTYPE(T_U_LONG)                             \
+                                   : ((flags & SHORTINT)                           \
+                                          ? ADDTYPE(T_U_SHORT)                     \
+                                          : ((flags & CHARINT) ? ADDTYPE(T_U_CHAR) \
+                                                               : ADDTYPE(T_U_INT))))))))
+
+  /*
+   * Add * arguments to the type array.
+   */
+#define ADDASTER()         \
+  n2 = 0;                  \
+  cp = fmt;                \
+  while (is_digit(*cp)) {  \
+    APPEND_DIGIT(n2, *cp); \
+    cp++;                  \
+  }                        \
+  if (*cp == '$') {        \
+    int hold = nextarg;    \
+    nextarg = n2;          \
+    ADDTYPE(T_INT);        \
+    nextarg = hold;        \
+    fmt = ++cp;            \
+  } else {                 \
+    ADDTYPE(T_INT);        \
+  }
+  fmt = (wchar_t*)fmt0;
+  typetable = stattypetable;
+  tablesize = STATIC_ARG_TBL_SIZE;
+  tablemax = 0;
+  nextarg = 1;
+  memset(typetable, T_UNUSED, STATIC_ARG_TBL_SIZE);
+
+  /*
+   * Scan the format for conversions (`%' character).
+   */
+  for (;;) {
+    for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) continue;
+    if (ch == '\0') goto done;
+    fmt++; /* skip over '%' */
+
+    flags = 0;
+
+  rflag:
+    ch = *fmt++;
+  reswitch:
+    switch (ch) {
+      case ' ':
+      case '#':
+      case '\'':
+        goto rflag;
+      case '*':
+        ADDASTER();
+        goto rflag;
+      case '-':
+      case '+':
+        goto rflag;
+      case '.':
+        if ((ch = *fmt++) == '*') {
+          ADDASTER();
+          goto rflag;
+        }
+        while (is_digit(ch)) {
+          ch = *fmt++;
+        }
+        goto reswitch;
+      case '0':
+        goto rflag;
+      case '1':
+      case '2':
+      case '3':
+      case '4':
+      case '5':
+      case '6':
+      case '7':
+      case '8':
+      case '9':
+        n = 0;
+        do {
+          APPEND_DIGIT(n, ch);
+          ch = *fmt++;
+        } while (is_digit(ch));
+        if (ch == '$') {
+          nextarg = n;
+          goto rflag;
+        }
+        goto reswitch;
+      case 'L':
+        flags |= LONGDBL;
+        goto rflag;
+      case 'h':
+        if (*fmt == 'h') {
+          fmt++;
+          flags |= CHARINT;
+        } else {
+          flags |= SHORTINT;
+        }
+        goto rflag;
+      case 'l':
+        if (*fmt == 'l') {
+          fmt++;
+          flags |= LLONGINT;
+        } else {
+          flags |= LONGINT;
+        }
+        goto rflag;
+      case 'q':
+        flags |= LLONGINT;
+        goto rflag;
+      case 't':
+        flags |= PTRINT;
+        goto rflag;
+      case 'z':
+        flags |= SIZEINT;
+        goto rflag;
+      case 'C':
+        flags |= LONGINT;
+        /*FALLTHROUGH*/
+      case 'c':
+        if (flags & LONGINT)
+          ADDTYPE(T_WINT);
+        else
+          ADDTYPE(T_INT);
+        break;
+      case 'D':
+        flags |= LONGINT;
+        /*FALLTHROUGH*/
+      case 'd':
+      case 'i':
+        ADDSARG();
+        break;
+      case 'a':
+      case 'A':
+      case 'e':
+      case 'E':
+      case 'f':
+      case 'F':
+      case 'g':
+      case 'G':
+        if (flags & LONGDBL)
+          ADDTYPE(T_LONG_DOUBLE);
+        else
+          ADDTYPE(T_DOUBLE);
+        break;
+#ifndef NO_PRINTF_PERCENT_N
+      case 'n':
+        if (flags & LLONGINT)
+          ADDTYPE(TP_LLONG);
+        else if (flags & LONGINT)
+          ADDTYPE(TP_LONG);
+        else if (flags & SHORTINT)
+          ADDTYPE(TP_SHORT);
+        else if (flags & PTRINT)
+          ADDTYPE(TP_PTRINT);
+        else if (flags & SIZEINT)
+          ADDTYPE(TP_SSIZEINT);
+        else if (flags & MAXINT)
+          ADDTYPE(TP_MAXINT);
+        else
+          ADDTYPE(TP_INT);
+        continue; /* no output */
+#endif            /* NO_PRINTF_PERCENT_N */
+      case 'O':
+        flags |= LONGINT;
+        /*FALLTHROUGH*/
+      case 'o':
+        ADDUARG();
+        break;
+      case 'p':
+        ADDTYPE(TP_VOID);
+        break;
+      case 'S':
+        flags |= LONGINT;
+        /*FALLTHROUGH*/
+      case 's':
+        if (flags & LONGINT)
+          ADDTYPE(TP_CHAR);
+        else
+          ADDTYPE(TP_WCHAR);
+        break;
+      case 'U':
+        flags |= LONGINT;
+        /*FALLTHROUGH*/
+      case 'u':
+      case 'X':
+      case 'x':
+        ADDUARG();
+        break;
+      default: /* "%?" prints ?, unless ? is NUL */
+        if (ch == '\0') goto done;
+        break;
+    }
+  }
+done:
+  /*
+   * Build the argument table.
+   */
+  if (tablemax >= STATIC_ARG_TBL_SIZE) {
+    *argtablesiz = sizeof(union arg) * (tablemax + 1);
+    *argtable = mmap(NULL, *argtablesiz, PROT_WRITE | PROT_READ, MAP_ANON | MAP_PRIVATE, -1, 0);
+    if (*argtable == MAP_FAILED) return (-1);
+  }
+
+#if 0
+	/* XXX is this required? */
+	(*argtable)[0].intarg = 0;
+#endif
+  for (n = 1; n <= tablemax; n++) {
+    switch (typetable[n]) {
+      case T_UNUSED:
+      case T_CHAR:
+      case T_U_CHAR:
+      case T_SHORT:
+      case T_U_SHORT:
+      case T_INT:
+        (*argtable)[n].intarg = va_arg(ap, int);
+        break;
+      case TP_SHORT:
+        (*argtable)[n].pshortarg = va_arg(ap, short*);
+        break;
+      case T_U_INT:
+        (*argtable)[n].uintarg = va_arg(ap, unsigned int);
+        break;
+      case TP_INT:
+        (*argtable)[n].pintarg = va_arg(ap, int*);
+        break;
+      case T_LONG:
+        (*argtable)[n].longarg = va_arg(ap, long);
+        break;
+      case T_U_LONG:
+        (*argtable)[n].ulongarg = va_arg(ap, unsigned long);
+        break;
+      case TP_LONG:
+        (*argtable)[n].plongarg = va_arg(ap, long*);
+        break;
+      case T_LLONG:
+        (*argtable)[n].longlongarg = va_arg(ap, long long);
+        break;
+      case T_U_LLONG:
+        (*argtable)[n].ulonglongarg = va_arg(ap, unsigned long long);
+        break;
+      case TP_LLONG:
+        (*argtable)[n].plonglongarg = va_arg(ap, long long*);
+        break;
+      case T_DOUBLE:
+        (*argtable)[n].doublearg = va_arg(ap, double);
+        break;
+      case T_LONG_DOUBLE:
+        (*argtable)[n].longdoublearg = va_arg(ap, long double);
+        break;
+      case TP_CHAR:
+        (*argtable)[n].pchararg = va_arg(ap, char*);
+        break;
+      case TP_VOID:
+        (*argtable)[n].pvoidarg = va_arg(ap, void*);
+        break;
+      case T_PTRINT:
+        (*argtable)[n].ptrdiffarg = va_arg(ap, ptrdiff_t);
+        break;
+      case TP_PTRINT:
+        (*argtable)[n].pptrdiffarg = va_arg(ap, ptrdiff_t*);
+        break;
+      case T_SIZEINT:
+        (*argtable)[n].sizearg = va_arg(ap, size_t);
+        break;
+      case T_SSIZEINT:
+        (*argtable)[n].ssizearg = va_arg(ap, ssize_t);
+        break;
+      case TP_SSIZEINT:
+        (*argtable)[n].pssizearg = va_arg(ap, ssize_t*);
+        break;
+      case TP_MAXINT:
+        (*argtable)[n].intmaxarg = va_arg(ap, intmax_t);
+        break;
+      case T_WINT:
+        (*argtable)[n].wintarg = va_arg(ap, wint_t);
+        break;
+      case TP_WCHAR:
+        (*argtable)[n].pwchararg = va_arg(ap, wchar_t*);
+        break;
+    }
+  }
+  goto finish;
+
+overflow:
+  errno = ENOMEM;
+  ret = -1;
+
+finish:
+  if (typetable != NULL && typetable != stattypetable) {
+    munmap(typetable, *argtablesiz);
+    typetable = NULL;
+  }
+  return (ret);
+}
+
+/*
+ * Increase the size of the type table.
+ */
+static int __grow_type_table(unsigned char** typetable, int* tablesize) {
+  unsigned char* oldtable = *typetable;
+  int newsize = *tablesize * 2;
+
+  if (newsize < getpagesize()) newsize = getpagesize();
+
+  if (*tablesize == STATIC_ARG_TBL_SIZE) {
+    *typetable = mmap(NULL, newsize, PROT_WRITE | PROT_READ, MAP_ANON | MAP_PRIVATE, -1, 0);
+    if (*typetable == MAP_FAILED) return (-1);
+    bcopy(oldtable, *typetable, *tablesize);
+  } else {
+    unsigned char* new = mmap(NULL, newsize, PROT_WRITE | PROT_READ, MAP_ANON | MAP_PRIVATE, -1, 0);
+    if (new == MAP_FAILED) return (-1);
+    memmove(new, *typetable, *tablesize);
+    munmap(*typetable, *tablesize);
+    *typetable = new;
+  }
+  memset(*typetable + *tablesize, T_UNUSED, (newsize - *tablesize));
+
+  *tablesize = newsize;
+  return (0);
+}
+
+static int exponent(wchar_t* p0, int exp, int fmtch) {
+  wchar_t *p, *t;
+  wchar_t expbuf[MAXEXPDIG];
+
+  p = p0;
+  *p++ = fmtch;
+  if (exp < 0) {
+    exp = -exp;
+    *p++ = '-';
+  } else
+    *p++ = '+';
+  t = expbuf + MAXEXPDIG;
+  if (exp > 9) {
+    do {
+      *--t = to_char(exp % 10);
+    } while ((exp /= 10) > 9);
+    *--t = to_char(exp);
+    for (; t < expbuf + MAXEXPDIG; *p++ = *t++) /* nothing */;
+  } else {
+    /*
+     * Exponents for decimal floating point conversions
+     * (%[eEgG]) must be at least two characters long,
+     * whereas exponents for hexadecimal conversions can
+     * be only one character long.
+     */
+    if (fmtch == 'e' || fmtch == 'E') *p++ = '0';
+    *p++ = to_char(exp);
+  }
+  return (p - p0);
+}
diff --git a/libc/stdio/vfwscanf.c b/libc/stdio/vfwscanf.c
index 0a7bfa9..c74c6e7 100644
--- a/libc/stdio/vfwscanf.c
+++ b/libc/stdio/vfwscanf.c
@@ -42,58 +42,56 @@
 #include <wctype.h>
 #include "local.h"
 
-#ifdef FLOATING_POINT
 #include "floatio.h"
-#endif
 
-#define	BUF		513	/* Maximum length of numeric string. */
+#define BUF 513 /* Maximum length of numeric string. */
 
 /*
  * Flags used during conversion.
  */
-#define	LONG		0x00001	/* l: long or double */
-#define	LONGDBL		0x00002	/* L: long double */
-#define	SHORT		0x00004	/* h: short */
-#define	SHORTSHORT	0x00008	/* hh: 8 bit integer */
-#define LLONG		0x00010	/* ll: long long (+ deprecated q: quad) */
-#define	POINTER		0x00020	/* p: void * (as hex) */
-#define	SIZEINT		0x00040	/* z: (signed) size_t */
-#define	MAXINT		0x00080	/* j: intmax_t */
-#define	PTRINT		0x00100	/* t: ptrdiff_t */
-#define	NOSKIP		0x00200	/* [ or c: do not skip blanks */
-#define	SUPPRESS	0x00400	/* *: suppress assignment */
-#define	UNSIGNED	0x00800	/* %[oupxX] conversions */
+#define LONG 0x00001       /* l: long or double */
+#define LONGDBL 0x00002    /* L: long double */
+#define SHORT 0x00004      /* h: short */
+#define SHORTSHORT 0x00008 /* hh: 8 bit integer */
+#define LLONG 0x00010      /* ll: long long (+ deprecated q: quad) */
+#define POINTER 0x00020    /* p: void * (as hex) */
+#define SIZEINT 0x00040    /* z: (signed) size_t */
+#define MAXINT 0x00080     /* j: intmax_t */
+#define PTRINT 0x00100     /* t: ptrdiff_t */
+#define NOSKIP 0x00200     /* [ or c: do not skip blanks */
+#define SUPPRESS 0x00400   /* *: suppress assignment */
+#define UNSIGNED 0x00800   /* %[oupxX] conversions */
 
 /*
  * The following are used in numeric conversions only:
  * SIGNOK, HAVESIGN, NDIGITS, DPTOK, and EXPOK are for floating point;
  * SIGNOK, HAVESIGN, NDIGITS, PFXOK, and NZDIGITS are for integral.
  */
-#define	SIGNOK		0x01000	/* +/- is (still) legal */
-#define	HAVESIGN	0x02000	/* sign detected */
-#define	NDIGITS		0x04000	/* no digits detected */
+#define SIGNOK 0x01000   /* +/- is (still) legal */
+#define HAVESIGN 0x02000 /* sign detected */
+#define NDIGITS 0x04000  /* no digits detected */
 
-#define	DPTOK		0x08000	/* (float) decimal point is still legal */
-#define	EXPOK		0x10000	/* (float) exponent (e+3, etc) still legal */
+#define DPTOK 0x08000 /* (float) decimal point is still legal */
+#define EXPOK 0x10000 /* (float) exponent (e+3, etc) still legal */
 
-#define	PFXOK		0x08000	/* 0x prefix is (still) legal */
-#define	NZDIGITS	0x10000	/* no zero digits detected */
+#define PFXOK 0x08000    /* 0x prefix is (still) legal */
+#define NZDIGITS 0x10000 /* no zero digits detected */
 
 /*
  * Conversion types.
  */
-#define	CT_CHAR		0	/* %c conversion */
-#define	CT_CCL		1	/* %[...] conversion */
-#define	CT_STRING	2	/* %s conversion */
-#define	CT_INT		3	/* integer, i.e., strtoimax or strtoumax */
-#define	CT_FLOAT	4	/* floating, i.e., strtod */
+#define CT_CHAR 0   /* %c conversion */
+#define CT_CCL 1    /* %[...] conversion */
+#define CT_STRING 2 /* %s conversion */
+#define CT_INT 3    /* integer, i.e., strtoimax or strtoumax */
+#define CT_FLOAT 4  /* floating, i.e., strtod */
 
 #define u_char unsigned char
 #define u_long unsigned long
 
-#define	INCCL(_c)	\
-	(cclcompl ? (wmemchr(ccls, (_c), ccle - ccls) == NULL) : \
-	(wmemchr(ccls, (_c), ccle - ccls) != NULL))
+#define INCCL(_c)                                        \
+  (cclcompl ? (wmemchr(ccls, (_c), ccle - ccls) == NULL) \
+            : (wmemchr(ccls, (_c), ccle - ccls) != NULL))
 
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wframe-larger-than="
@@ -101,627 +99,569 @@
 /*
  * vfwscanf
  */
-int
-__vfwscanf(FILE * __restrict fp, const wchar_t * __restrict fmt, __va_list ap)
-{
-	wint_t c;	/* character from format, or conversion */
-	size_t width;	/* field width, or 0 */
-	wchar_t *p;	/* points into all kinds of strings */
-	int n;		/* handy integer */
-	int flags;	/* flags as defined above */
-	wchar_t *p0;	/* saves original value of p when necessary */
-	int nassigned;		/* number of fields assigned */
-	int nconversions;	/* number of conversions */
-	int nread;		/* number of characters consumed from fp */
-	int base;		/* base argument to strtoimax/strtouimax */
-	wchar_t buf[BUF];	/* buffer for numeric conversions */
-	const wchar_t *ccls;	/* character class start */
-	const wchar_t *ccle;	/* character class end */
-	int cclcompl;		/* ccl is complemented? */
-	wint_t wi;		/* handy wint_t */
-	char *mbp;		/* multibyte string pointer for %c %s %[ */
-	size_t nconv;		/* number of bytes in mb. conversion */
-	char mbbuf[MB_LEN_MAX];	/* temporary mb. character buffer */
- 	mbstate_t mbs;
+int __vfwscanf(FILE* __restrict fp, const wchar_t* __restrict fmt, __va_list ap) {
+  wint_t c;               /* character from format, or conversion */
+  size_t width;           /* field width, or 0 */
+  wchar_t* p;             /* points into all kinds of strings */
+  int n;                  /* handy integer */
+  int flags;              /* flags as defined above */
+  wchar_t* p0;            /* saves original value of p when necessary */
+  int nassigned;          /* number of fields assigned */
+  int nconversions;       /* number of conversions */
+  int nread;              /* number of characters consumed from fp */
+  int base;               /* base argument to strtoimax/strtouimax */
+  wchar_t buf[BUF];       /* buffer for numeric conversions */
+  const wchar_t* ccls;    /* character class start */
+  const wchar_t* ccle;    /* character class end */
+  int cclcompl;           /* ccl is complemented? */
+  wint_t wi;              /* handy wint_t */
+  char* mbp;              /* multibyte string pointer for %c %s %[ */
+  size_t nconv;           /* number of bytes in mb. conversion */
+  char mbbuf[MB_LEN_MAX]; /* temporary mb. character buffer */
+  mbstate_t mbs;
 
-	/* `basefix' is used to avoid `if' tests in the integer scanner */
-	static short basefix[17] =
-		{ 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
+  /* `basefix' is used to avoid `if' tests in the integer scanner */
+  static short basefix[17] = { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
 
-	_SET_ORIENTATION(fp, 1);
+  _SET_ORIENTATION(fp, 1);
 
-	nassigned = 0;
-	nconversions = 0;
-	nread = 0;
-	base = 0;		/* XXX just to keep gcc happy */
-	ccls = ccle = NULL;
-	for (;;) {
-		c = *fmt++;
-		if (c == 0) {
-			return (nassigned);
-		}
-		if (iswspace(c)) {
-			while ((c = __fgetwc_unlock(fp)) != WEOF &&
-			    iswspace(c))
-				;
-			if (c != WEOF)
-				__ungetwc(c, fp);
-			continue;
-		}
-		if (c != '%')
-			goto literal;
-		width = 0;
-		flags = 0;
-		/*
-		 * switch on the format.  continue if done;
-		 * break once format type is derived.
-		 */
-again:		c = *fmt++;
-		switch (c) {
-		case '%':
-literal:
-			if ((wi = __fgetwc_unlock(fp)) == WEOF)
-				goto input_failure;
-			if (wi != c) {
-				__ungetwc(wi, fp);
-				goto input_failure;
-			}
-			nread++;
-			continue;
+  nassigned = 0;
+  nconversions = 0;
+  nread = 0;
+  base = 0; /* XXX just to keep gcc happy */
+  ccls = ccle = NULL;
+  for (;;) {
+    c = *fmt++;
+    if (c == 0) {
+      return (nassigned);
+    }
+    if (iswspace(c)) {
+      while ((c = __fgetwc_unlock(fp)) != WEOF && iswspace(c))
+        ;
+      if (c != WEOF) __ungetwc(c, fp);
+      continue;
+    }
+    if (c != '%') goto literal;
+    width = 0;
+    flags = 0;
+    /*
+     * switch on the format.  continue if done;
+     * break once format type is derived.
+     */
+  again:
+    c = *fmt++;
+    switch (c) {
+      case '%':
+      literal:
+        if ((wi = __fgetwc_unlock(fp)) == WEOF) goto input_failure;
+        if (wi != c) {
+          __ungetwc(wi, fp);
+          goto input_failure;
+        }
+        nread++;
+        continue;
 
-		case '*':
-			flags |= SUPPRESS;
-			goto again;
-		case 'j':
-			flags |= MAXINT;
-			goto again;
-		case 'L':
-			flags |= LONGDBL;
-			goto again;
-		case 'h':
-			if (*fmt == 'h') {
-				fmt++;
-				flags |= SHORTSHORT;
-			} else {
-				flags |= SHORT;
-			}
-			goto again;
-		case 'l':
-			if (*fmt == 'l') {
-				fmt++;
-				flags |= LLONG;
-			} else {
-				flags |= LONG;
-			}
-			goto again;
-		case 'q':
-			flags |= LLONG;		/* deprecated */
-			goto again;
-		case 't':
-			flags |= PTRINT;
-			goto again;
-		case 'z':
-			flags |= SIZEINT;
-			goto again;
+      case '*':
+        flags |= SUPPRESS;
+        goto again;
+      case 'j':
+        flags |= MAXINT;
+        goto again;
+      case 'L':
+        flags |= LONGDBL;
+        goto again;
+      case 'h':
+        if (*fmt == 'h') {
+          fmt++;
+          flags |= SHORTSHORT;
+        } else {
+          flags |= SHORT;
+        }
+        goto again;
+      case 'l':
+        if (*fmt == 'l') {
+          fmt++;
+          flags |= LLONG;
+        } else {
+          flags |= LONG;
+        }
+        goto again;
+      case 'q':
+        flags |= LLONG; /* deprecated */
+        goto again;
+      case 't':
+        flags |= PTRINT;
+        goto again;
+      case 'z':
+        flags |= SIZEINT;
+        goto again;
 
-		case '0': case '1': case '2': case '3': case '4':
-		case '5': case '6': case '7': case '8': case '9':
-			width = width * 10 + c - '0';
-			goto again;
+      case '0':
+      case '1':
+      case '2':
+      case '3':
+      case '4':
+      case '5':
+      case '6':
+      case '7':
+      case '8':
+      case '9':
+        width = width * 10 + c - '0';
+        goto again;
 
-		/*
-		 * Conversions.
-		 * Those marked `compat' are for 4.[123]BSD compatibility.
-		 *
-		 * (According to ANSI, E and X formats are supposed
-		 * to the same as e and x.  Sorry about that.)
-		 */
-		case 'D':	/* compat */
-			flags |= LONG;
-			/* FALLTHROUGH */
-		case 'd':
-			c = CT_INT;
-			base = 10;
-			break;
+      /*
+       * Conversions.
+       * Those marked `compat' are for 4.[123]BSD compatibility.
+       *
+       * (According to ANSI, E and X formats are supposed
+       * to the same as e and x.  Sorry about that.)
+       */
+      case 'D': /* compat */
+        flags |= LONG;
+        /* FALLTHROUGH */
+      case 'd':
+        c = CT_INT;
+        base = 10;
+        break;
 
-		case 'i':
-			c = CT_INT;
-			base = 0;
-			break;
+      case 'i':
+        c = CT_INT;
+        base = 0;
+        break;
 
-		case 'O':	/* compat */
-			flags |= LONG;
-			/* FALLTHROUGH */
-		case 'o':
-			c = CT_INT;
-			flags |= UNSIGNED;
-			base = 8;
-			break;
+      case 'O': /* compat */
+        flags |= LONG;
+        /* FALLTHROUGH */
+      case 'o':
+        c = CT_INT;
+        flags |= UNSIGNED;
+        base = 8;
+        break;
 
-		case 'u':
-			c = CT_INT;
-			flags |= UNSIGNED;
-			base = 10;
-			break;
+      case 'u':
+        c = CT_INT;
+        flags |= UNSIGNED;
+        base = 10;
+        break;
 
-		case 'X':
-		case 'x':
-			flags |= PFXOK;	/* enable 0x prefixing */
-			c = CT_INT;
-			flags |= UNSIGNED;
-			base = 16;
-			break;
+      case 'X':
+      case 'x':
+        flags |= PFXOK; /* enable 0x prefixing */
+        c = CT_INT;
+        flags |= UNSIGNED;
+        base = 16;
+        break;
 
-#ifdef FLOATING_POINT
-		case 'e': case 'E':
-		case 'f': case 'F':
-		case 'g': case 'G':
-		case 'a': case 'A':
-			c = CT_FLOAT;
-			break;
-#endif
+      case 'e':
+      case 'E':
+      case 'f':
+      case 'F':
+      case 'g':
+      case 'G':
+      case 'a':
+      case 'A':
+        c = CT_FLOAT;
+        break;
 
-		case 's':
-			c = CT_STRING;
-			break;
+      case 's':
+        c = CT_STRING;
+        break;
 
-		case '[':
-			ccls = fmt;
-			if (*fmt == '^') {
-				cclcompl = 1;
-				fmt++;
-			} else
-				cclcompl = 0;
-			if (*fmt == ']')
-				fmt++;
-			while (*fmt != '\0' && *fmt != ']')
-				fmt++;
-			ccle = fmt;
-			fmt++;
-			flags |= NOSKIP;
-			c = CT_CCL;
-			break;
+      case '[':
+        ccls = fmt;
+        if (*fmt == '^') {
+          cclcompl = 1;
+          fmt++;
+        } else
+          cclcompl = 0;
+        if (*fmt == ']') fmt++;
+        while (*fmt != '\0' && *fmt != ']') fmt++;
+        ccle = fmt;
+        fmt++;
+        flags |= NOSKIP;
+        c = CT_CCL;
+        break;
 
-		case 'c':
-			flags |= NOSKIP;
-			c = CT_CHAR;
-			break;
+      case 'c':
+        flags |= NOSKIP;
+        c = CT_CHAR;
+        break;
 
-		case 'p':	/* pointer format is like hex */
-			flags |= POINTER | PFXOK;
-			c = CT_INT;
-			flags |= UNSIGNED;
-			base = 16;
-			break;
+      case 'p': /* pointer format is like hex */
+        flags |= POINTER | PFXOK;
+        c = CT_INT;
+        flags |= UNSIGNED;
+        base = 16;
+        break;
 
-		case 'n':
-			nconversions++;
-			if (flags & SUPPRESS)
-				continue;
-			if (flags & SHORTSHORT)
-				*va_arg(ap, signed char *) = nread;
-			else if (flags & SHORT)
-				*va_arg(ap, short *) = nread;
-			else if (flags & LONG)
-				*va_arg(ap, long *) = nread;
-			else if (flags & SIZEINT)
-				*va_arg(ap, ssize_t *) = nread;
-			else if (flags & PTRINT)
-				*va_arg(ap, ptrdiff_t *) = nread;
-			else if (flags & LLONG)
-				*va_arg(ap, long long *) = nread;
-			else if (flags & MAXINT)
-				*va_arg(ap, intmax_t *) = nread;
-			else
-				*va_arg(ap, int *) = nread;
-			continue;
+      case 'n':
+        nconversions++;
+        if (flags & SUPPRESS) continue;
+        if (flags & SHORTSHORT)
+          *va_arg(ap, signed char*) = nread;
+        else if (flags & SHORT)
+          *va_arg(ap, short*) = nread;
+        else if (flags & LONG)
+          *va_arg(ap, long*) = nread;
+        else if (flags & SIZEINT)
+          *va_arg(ap, ssize_t*) = nread;
+        else if (flags & PTRINT)
+          *va_arg(ap, ptrdiff_t*) = nread;
+        else if (flags & LLONG)
+          *va_arg(ap, long long*) = nread;
+        else if (flags & MAXINT)
+          *va_arg(ap, intmax_t*) = nread;
+        else
+          *va_arg(ap, int*) = nread;
+        continue;
 
-		/*
-		 * Disgusting backwards compatibility hacks.	XXX
-		 */
-		case '\0':	/* compat */
-			return (EOF);
+      /*
+       * Disgusting backwards compatibility hacks.	XXX
+       */
+      case '\0': /* compat */
+        return (EOF);
 
-		default:	/* compat */
-			if (iswupper(c))
-				flags |= LONG;
-			c = CT_INT;
-			base = 10;
-			break;
-		}
+      default: /* compat */
+        if (iswupper(c)) flags |= LONG;
+        c = CT_INT;
+        base = 10;
+        break;
+    }
 
-		/*
-		 * Consume leading white space, except for formats
-		 * that suppress this.
-		 */
-		if ((flags & NOSKIP) == 0) {
-			while ((wi = __fgetwc_unlock(fp)) != WEOF &&
-			    iswspace(wi))
-				nread++;
-			if (wi == WEOF)
-				goto input_failure;
-			__ungetwc(wi, fp);
-		}
+    /*
+     * Consume leading white space, except for formats
+     * that suppress this.
+     */
+    if ((flags & NOSKIP) == 0) {
+      while ((wi = __fgetwc_unlock(fp)) != WEOF && iswspace(wi)) nread++;
+      if (wi == WEOF) goto input_failure;
+      __ungetwc(wi, fp);
+    }
 
-		/*
-		 * Do the conversion.
-		 */
-		switch (c) {
+    /*
+     * Do the conversion.
+     */
+    switch (c) {
+      case CT_CHAR:
+        /* scan arbitrary characters (sets NOSKIP) */
+        if (width == 0) width = 1;
+        if (flags & LONG) {
+          if (!(flags & SUPPRESS)) p = va_arg(ap, wchar_t*);
+          n = 0;
+          while (width-- != 0 && (wi = __fgetwc_unlock(fp)) != WEOF) {
+            if (!(flags & SUPPRESS)) *p++ = (wchar_t)wi;
+            n++;
+          }
+          if (n == 0) goto input_failure;
+          nread += n;
+          if (!(flags & SUPPRESS)) nassigned++;
+        } else {
+          if (!(flags & SUPPRESS)) mbp = va_arg(ap, char*);
+          n = 0;
+          memset(&mbs, 0, sizeof(mbs));
+          while (width != 0 && (wi = __fgetwc_unlock(fp)) != WEOF) {
+            if (width >= MB_CUR_MAX && !(flags & SUPPRESS)) {
+              nconv = wcrtomb(mbp, wi, &mbs);
+              if (nconv == (size_t)-1) goto input_failure;
+            } else {
+              nconv = wcrtomb(mbbuf, wi, &mbs);
+              if (nconv == (size_t)-1) goto input_failure;
+              if (nconv > width) {
+                __ungetwc(wi, fp);
+                break;
+              }
+              if (!(flags & SUPPRESS)) memcpy(mbp, mbbuf, nconv);
+            }
+            if (!(flags & SUPPRESS)) mbp += nconv;
+            width -= nconv;
+            n++;
+          }
+          if (n == 0) goto input_failure;
+          nread += n;
+          if (!(flags & SUPPRESS)) nassigned++;
+        }
+        nconversions++;
+        break;
 
-		case CT_CHAR:
-			/* scan arbitrary characters (sets NOSKIP) */
-			if (width == 0)
-				width = 1;
- 			if (flags & LONG) {
-				if (!(flags & SUPPRESS))
-					p = va_arg(ap, wchar_t *);
-				n = 0;
-				while (width-- != 0 &&
-				    (wi = __fgetwc_unlock(fp)) != WEOF) {
-					if (!(flags & SUPPRESS))
-						*p++ = (wchar_t)wi;
-					n++;
-				}
-				if (n == 0)
-					goto input_failure;
-				nread += n;
- 				if (!(flags & SUPPRESS))
- 					nassigned++;
-			} else {
-				if (!(flags & SUPPRESS))
-					mbp = va_arg(ap, char *);
-				n = 0;
-				memset(&mbs, 0, sizeof(mbs));
-				while (width != 0 &&
-				    (wi = __fgetwc_unlock(fp)) != WEOF) {
-					if (width >= MB_CUR_MAX &&
-					    !(flags & SUPPRESS)) {
-						nconv = wcrtomb(mbp, wi, &mbs);
-						if (nconv == (size_t)-1)
-							goto input_failure;
-					} else {
-						nconv = wcrtomb(mbbuf, wi,
-						    &mbs);
-						if (nconv == (size_t)-1)
-							goto input_failure;
-						if (nconv > width) {
-							__ungetwc(wi, fp);
- 							break;
- 						}
-						if (!(flags & SUPPRESS))
-							memcpy(mbp, mbbuf,
-							    nconv);
- 					}
-					if (!(flags & SUPPRESS))
-						mbp += nconv;
-					width -= nconv;
-					n++;
- 				}
-				if (n == 0)
- 					goto input_failure;
-				nread += n;
-				if (!(flags & SUPPRESS))
-					nassigned++;
-			}
-			nconversions++;
-			break;
+      case CT_CCL:
+        /* scan a (nonempty) character class (sets NOSKIP) */
+        if (width == 0) width = (size_t)~0; /* `infinity' */
+        /* take only those things in the class */
+        if ((flags & SUPPRESS) && (flags & LONG)) {
+          n = 0;
+          while ((wi = __fgetwc_unlock(fp)) != WEOF && width-- != 0 && INCCL(wi)) n++;
+          if (wi != WEOF) __ungetwc(wi, fp);
+          if (n == 0) goto match_failure;
+        } else if (flags & LONG) {
+          p0 = p = va_arg(ap, wchar_t*);
+          while ((wi = __fgetwc_unlock(fp)) != WEOF && width-- != 0 && INCCL(wi))
+            *p++ = (wchar_t)wi;
+          if (wi != WEOF) __ungetwc(wi, fp);
+          n = p - p0;
+          if (n == 0) goto match_failure;
+          *p = 0;
+          nassigned++;
+        } else {
+          if (!(flags & SUPPRESS)) mbp = va_arg(ap, char*);
+          n = 0;
+          memset(&mbs, 0, sizeof(mbs));
+          while ((wi = __fgetwc_unlock(fp)) != WEOF && width != 0 && INCCL(wi)) {
+            if (width >= MB_CUR_MAX && !(flags & SUPPRESS)) {
+              nconv = wcrtomb(mbp, wi, &mbs);
+              if (nconv == (size_t)-1) goto input_failure;
+            } else {
+              nconv = wcrtomb(mbbuf, wi, &mbs);
+              if (nconv == (size_t)-1) goto input_failure;
+              if (nconv > width) break;
+              if (!(flags & SUPPRESS)) memcpy(mbp, mbbuf, nconv);
+            }
+            if (!(flags & SUPPRESS)) mbp += nconv;
+            width -= nconv;
+            n++;
+          }
+          if (wi != WEOF) __ungetwc(wi, fp);
+          if (!(flags & SUPPRESS)) {
+            *mbp = 0;
+            nassigned++;
+          }
+        }
+        nread += n;
+        nconversions++;
+        break;
 
-		case CT_CCL:
-			/* scan a (nonempty) character class (sets NOSKIP) */
-			if (width == 0)
-				width = (size_t)~0;	/* `infinity' */
-			/* take only those things in the class */
-			if ((flags & SUPPRESS) && (flags & LONG)) {
-				n = 0;
-				while ((wi = __fgetwc_unlock(fp)) != WEOF &&
-				    width-- != 0 && INCCL(wi))
-					n++;
-				if (wi != WEOF)
-					__ungetwc(wi, fp);
-				if (n == 0)
-					goto match_failure;
-			} else if (flags & LONG) {
-				p0 = p = va_arg(ap, wchar_t *);
-				while ((wi = __fgetwc_unlock(fp)) != WEOF &&
-				    width-- != 0 && INCCL(wi))
-					*p++ = (wchar_t)wi;
-				if (wi != WEOF)
-					__ungetwc(wi, fp);
-				n = p - p0;
-				if (n == 0)
-					goto match_failure;
-				*p = 0;
-				nassigned++;
-			} else {
-				if (!(flags & SUPPRESS))
-					mbp = va_arg(ap, char *);
-				n = 0;
-				memset(&mbs, 0, sizeof(mbs));
-				while ((wi = __fgetwc_unlock(fp)) != WEOF &&
-				    width != 0 && INCCL(wi)) {
-					if (width >= MB_CUR_MAX &&
-					   !(flags & SUPPRESS)) {
-						nconv = wcrtomb(mbp, wi, &mbs);
-						if (nconv == (size_t)-1)
-							goto input_failure;
-					} else {
-						nconv = wcrtomb(mbbuf, wi,
-						    &mbs);
-						if (nconv == (size_t)-1)
-							goto input_failure;
-						if (nconv > width)
-							break;
-						if (!(flags & SUPPRESS))
-							memcpy(mbp, mbbuf,
-							    nconv);
-					}
-					if (!(flags & SUPPRESS))
-						mbp += nconv;
-					width -= nconv;
-					n++;
-				}
-				if (wi != WEOF)
-					__ungetwc(wi, fp);
-				if (!(flags & SUPPRESS)) {
-					*mbp = 0;
-					nassigned++;
-				}
- 			}
-			nread += n;
-			nconversions++;
-			break;
+      case CT_STRING:
+        /* like CCL, but zero-length string OK, & no NOSKIP */
+        if (width == 0) width = (size_t)~0;
+        if ((flags & SUPPRESS) && (flags & LONG)) {
+          while ((wi = __fgetwc_unlock(fp)) != WEOF && width-- != 0 && !iswspace(wi)) nread++;
+          if (wi != WEOF) __ungetwc(wi, fp);
+        } else if (flags & LONG) {
+          p0 = p = va_arg(ap, wchar_t*);
+          while ((wi = __fgetwc_unlock(fp)) != WEOF && width-- != 0 && !iswspace(wi)) {
+            *p++ = (wchar_t)wi;
+            nread++;
+          }
+          if (wi != WEOF) __ungetwc(wi, fp);
+          *p = 0;
+          nassigned++;
+        } else {
+          if (!(flags & SUPPRESS)) mbp = va_arg(ap, char*);
+          memset(&mbs, 0, sizeof(mbs));
+          while ((wi = __fgetwc_unlock(fp)) != WEOF && width != 0 && !iswspace(wi)) {
+            if (width >= MB_CUR_MAX && !(flags & SUPPRESS)) {
+              nconv = wcrtomb(mbp, wi, &mbs);
+              if (nconv == (size_t)-1) goto input_failure;
+            } else {
+              nconv = wcrtomb(mbbuf, wi, &mbs);
+              if (nconv == (size_t)-1) goto input_failure;
+              if (nconv > width) break;
+              if (!(flags & SUPPRESS)) memcpy(mbp, mbbuf, nconv);
+            }
+            if (!(flags & SUPPRESS)) mbp += nconv;
+            width -= nconv;
+            nread++;
+          }
+          if (wi != WEOF) __ungetwc(wi, fp);
+          if (!(flags & SUPPRESS)) {
+            *mbp = 0;
+            nassigned++;
+          }
+        }
+        nconversions++;
+        continue;
 
-		case CT_STRING:
-			/* like CCL, but zero-length string OK, & no NOSKIP */
-			if (width == 0)
-				width = (size_t)~0;
-			if ((flags & SUPPRESS) && (flags & LONG)) {
-				while ((wi = __fgetwc_unlock(fp)) != WEOF &&
-				    width-- != 0 &&
-				    !iswspace(wi))
-					nread++;
-				if (wi != WEOF)
-					__ungetwc(wi, fp);
-			} else if (flags & LONG) {
-				p0 = p = va_arg(ap, wchar_t *);
-				while ((wi = __fgetwc_unlock(fp)) != WEOF &&
-				    width-- != 0 &&
-				    !iswspace(wi)) {
-					*p++ = (wchar_t)wi;
-					nread++;
-				}
-				if (wi != WEOF)
-					__ungetwc(wi, fp);
-				*p = 0;
-				nassigned++;
-			} else {
-				if (!(flags & SUPPRESS))
-					mbp = va_arg(ap, char *);
-				memset(&mbs, 0, sizeof(mbs));
-				while ((wi = __fgetwc_unlock(fp)) != WEOF &&
-				    width != 0 &&
-				    !iswspace(wi)) {
-					if (width >= MB_CUR_MAX &&
-					    !(flags & SUPPRESS)) {
-						nconv = wcrtomb(mbp, wi, &mbs);
-						if (nconv == (size_t)-1)
-							goto input_failure;
-					} else {
-						nconv = wcrtomb(mbbuf, wi,
-						    &mbs);
-						if (nconv == (size_t)-1)
-							goto input_failure;
-						if (nconv > width)
-							break;
-						if (!(flags & SUPPRESS))
-							memcpy(mbp, mbbuf,
-							    nconv);
-					}
-					if (!(flags & SUPPRESS))
-						mbp += nconv;
-					width -= nconv;
-					nread++;
-				}
-				if (wi != WEOF)
-					__ungetwc(wi, fp);
-				if (!(flags & SUPPRESS)) {
-					*mbp = 0;
- 					nassigned++;
- 				}
-			}
-			nconversions++;
-			continue;
+      case CT_INT:
+        /* scan an integer as if by strtoimax/strtoumax */
+        if (width == 0 || width > sizeof(buf) / sizeof(*buf) - 1)
+          width = sizeof(buf) / sizeof(*buf) - 1;
+        flags |= SIGNOK | NDIGITS | NZDIGITS;
+        for (p = buf; width; width--) {
+          c = __fgetwc_unlock(fp);
+          /*
+           * Switch on the character; `goto ok'
+           * if we accept it as a part of number.
+           */
+          switch (c) {
+            /*
+             * The digit 0 is always legal, but is
+             * special.  For %i conversions, if no
+             * digits (zero or nonzero) have been
+             * scanned (only signs), we will have
+             * base==0.  In that case, we should set
+             * it to 8 and enable 0x prefixing.
+             * Also, if we have not scanned zero digits
+             * before this, do not turn off prefixing
+             * (someone else will turn it off if we
+             * have scanned any nonzero digits).
+             */
+            case '0':
+              if (base == 0) {
+                base = 8;
+                flags |= PFXOK;
+              }
+              if (flags & NZDIGITS)
+                flags &= ~(SIGNOK | NZDIGITS | NDIGITS);
+              else
+                flags &= ~(SIGNOK | PFXOK | NDIGITS);
+              goto ok;
 
-		case CT_INT:
-			/* scan an integer as if by strtoimax/strtoumax */
-			if (width == 0 || width > sizeof(buf) /
-			    sizeof(*buf) - 1)
-				width = sizeof(buf) / sizeof(*buf) - 1;
-			flags |= SIGNOK | NDIGITS | NZDIGITS;
-			for (p = buf; width; width--) {
-				c = __fgetwc_unlock(fp);
-				/*
-				 * Switch on the character; `goto ok'
-				 * if we accept it as a part of number.
-				 */
-				switch (c) {
+            /* 1 through 7 always legal */
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+              base = basefix[base];
+              flags &= ~(SIGNOK | PFXOK | NDIGITS);
+              goto ok;
 
-				/*
-				 * The digit 0 is always legal, but is
-				 * special.  For %i conversions, if no
-				 * digits (zero or nonzero) have been
-				 * scanned (only signs), we will have
-				 * base==0.  In that case, we should set
-				 * it to 8 and enable 0x prefixing.
-				 * Also, if we have not scanned zero digits
-				 * before this, do not turn off prefixing
-				 * (someone else will turn it off if we
-				 * have scanned any nonzero digits).
-				 */
-				case '0':
-					if (base == 0) {
-						base = 8;
-						flags |= PFXOK;
-					}
-					if (flags & NZDIGITS)
-					    flags &= ~(SIGNOK|NZDIGITS|NDIGITS);
-					else
-					    flags &= ~(SIGNOK|PFXOK|NDIGITS);
-					goto ok;
+            /* digits 8 and 9 ok iff decimal or hex */
+            case '8':
+            case '9':
+              base = basefix[base];
+              if (base <= 8) break; /* not legal here */
+              flags &= ~(SIGNOK | PFXOK | NDIGITS);
+              goto ok;
 
-				/* 1 through 7 always legal */
-				case '1': case '2': case '3':
-				case '4': case '5': case '6': case '7':
-					base = basefix[base];
-					flags &= ~(SIGNOK | PFXOK | NDIGITS);
-					goto ok;
+            /* letters ok iff hex */
+            case 'A':
+            case 'B':
+            case 'C':
+            case 'D':
+            case 'E':
+            case 'F':
+            case 'a':
+            case 'b':
+            case 'c':
+            case 'd':
+            case 'e':
+            case 'f':
+              /* no need to fix base here */
+              if (base <= 10) break; /* not legal here */
+              flags &= ~(SIGNOK | PFXOK | NDIGITS);
+              goto ok;
 
-				/* digits 8 and 9 ok iff decimal or hex */
-				case '8': case '9':
-					base = basefix[base];
-					if (base <= 8)
-						break;	/* not legal here */
-					flags &= ~(SIGNOK | PFXOK | NDIGITS);
-					goto ok;
+            /* sign ok only as first character */
+            case '+':
+            case '-':
+              if (flags & SIGNOK) {
+                flags &= ~SIGNOK;
+                flags |= HAVESIGN;
+                goto ok;
+              }
+              break;
 
-				/* letters ok iff hex */
-				case 'A': case 'B': case 'C':
-				case 'D': case 'E': case 'F':
-				case 'a': case 'b': case 'c':
-				case 'd': case 'e': case 'f':
-					/* no need to fix base here */
-					if (base <= 10)
-						break;	/* not legal here */
-					flags &= ~(SIGNOK | PFXOK | NDIGITS);
-					goto ok;
+            /*
+             * x ok iff flag still set and 2nd char (or
+             * 3rd char if we have a sign).
+             */
+            case 'x':
+            case 'X':
+              if ((flags & PFXOK) && p == buf + 1 + !!(flags & HAVESIGN)) {
+                base = 16; /* if %i */
+                flags &= ~PFXOK;
+                goto ok;
+              }
+              break;
+          }
 
-				/* sign ok only as first character */
-				case '+': case '-':
-					if (flags & SIGNOK) {
-						flags &= ~SIGNOK;
-						flags |= HAVESIGN;
-						goto ok;
-					}
-					break;
+          /*
+           * If we got here, c is not a legal character
+           * for a number.  Stop accumulating digits.
+           */
+          if (c != WEOF) __ungetwc(c, fp);
+          break;
+        ok:
+          /*
+           * c is legal: store it and look at the next.
+           */
+          *p++ = (wchar_t)c;
+        }
+        /*
+         * If we had only a sign, it is no good; push
+         * back the sign.  If the number ends in `x',
+         * it was [sign] '0' 'x', so push back the x
+         * and treat it as [sign] '0'.
+         */
+        if (flags & NDIGITS) {
+          if (p > buf) __ungetwc(*--p, fp);
+          goto match_failure;
+        }
+        c = p[-1];
+        if (c == 'x' || c == 'X') {
+          --p;
+          __ungetwc(c, fp);
+        }
+        if ((flags & SUPPRESS) == 0) {
+          uintmax_t res;
 
-				/*
-				 * x ok iff flag still set and 2nd char (or
-				 * 3rd char if we have a sign).
-				 */
-				case 'x': case 'X':
-					if ((flags & PFXOK) && p ==
-					    buf + 1 + !!(flags & HAVESIGN)) {
-						base = 16;	/* if %i */
-						flags &= ~PFXOK;
-						goto ok;
-					}
-					break;
-				}
+          *p = '\0';
+          if (flags & UNSIGNED)
+            res = wcstoimax(buf, NULL, base);
+          else
+            res = wcstoumax(buf, NULL, base);
+          if (flags & POINTER)
+            *va_arg(ap, void**) = (void*)(uintptr_t)res;
+          else if (flags & MAXINT)
+            *va_arg(ap, intmax_t*) = res;
+          else if (flags & LLONG)
+            *va_arg(ap, long long*) = res;
+          else if (flags & SIZEINT)
+            *va_arg(ap, ssize_t*) = res;
+          else if (flags & PTRINT)
+            *va_arg(ap, ptrdiff_t*) = res;
+          else if (flags & LONG)
+            *va_arg(ap, long*) = res;
+          else if (flags & SHORT)
+            *va_arg(ap, short*) = res;
+          else if (flags & SHORTSHORT)
+            *va_arg(ap, signed char*) = res;
+          else
+            *va_arg(ap, int*) = res;
+          nassigned++;
+        }
+        nread += p - buf;
+        nconversions++;
+        break;
 
-				/*
-				 * If we got here, c is not a legal character
-				 * for a number.  Stop accumulating digits.
-				 */
-				if (c != WEOF)
-					__ungetwc(c, fp);
-				break;
-		ok:
-				/*
-				 * c is legal: store it and look at the next.
-				 */
-				*p++ = (wchar_t)c;
-			}
-			/*
-			 * If we had only a sign, it is no good; push
-			 * back the sign.  If the number ends in `x',
-			 * it was [sign] '0' 'x', so push back the x
-			 * and treat it as [sign] '0'.
-			 */
-			if (flags & NDIGITS) {
-				if (p > buf)
-					__ungetwc(*--p, fp);
-				goto match_failure;
-			}
-			c = p[-1];
-			if (c == 'x' || c == 'X') {
-				--p;
-				__ungetwc(c, fp);
-			}
-			if ((flags & SUPPRESS) == 0) {
-				uintmax_t res;
-
-				*p = '\0';
-				if (flags & UNSIGNED)
-					res = wcstoimax(buf, NULL, base);
-				else
-					res = wcstoumax(buf, NULL, base);
-				if (flags & POINTER)
-					*va_arg(ap, void **) =
-					    (void *)(uintptr_t)res;
-				else if (flags & MAXINT)
-					*va_arg(ap, intmax_t *) = res;
-				else if (flags & LLONG)
-					*va_arg(ap, long long *) = res;
-				else if (flags & SIZEINT)
-					*va_arg(ap, ssize_t *) = res;
-				else if (flags & PTRINT)
-					*va_arg(ap, ptrdiff_t *) = res;
-				else if (flags & LONG)
-					*va_arg(ap, long *) = res;
-				else if (flags & SHORT)
-					*va_arg(ap, short *) = res;
-				else if (flags & SHORTSHORT)
-					*va_arg(ap, signed char *) = res;
-				else
-					*va_arg(ap, int *) = res;
-				nassigned++;
-			}
-			nread += p - buf;
-			nconversions++;
-			break;
-
-#ifdef FLOATING_POINT
-		case CT_FLOAT:
-			/* scan a floating point number as if by strtod */
-			if (width == 0 || width > sizeof(buf) /
-			    sizeof(*buf) - 1)
-				width = sizeof(buf) / sizeof(*buf) - 1;
-			if ((width = wparsefloat(fp, buf, buf + width)) == 0)
-				goto match_failure;
-			if ((flags & SUPPRESS) == 0) {
-				if (flags & LONGDBL) {
-					long double res = wcstold(buf, &p);
-					*va_arg(ap, long double *) = res;
-				} else if (flags & LONG) {
-					double res = wcstod(buf, &p);
-					*va_arg(ap, double *) = res;
-				} else {
-					float res = wcstof(buf, &p);
-					*va_arg(ap, float *) = res;
-				}
-				if (p - buf != (ptrdiff_t)width) abort();
-				nassigned++;
-			}
-			nread += width;
-			nconversions++;
-			break;
-#endif /* FLOATING_POINT */
-		}
-	}
+      case CT_FLOAT:
+        /* scan a floating point number as if by strtod */
+        if (width == 0 || width > sizeof(buf) / sizeof(*buf) - 1)
+          width = sizeof(buf) / sizeof(*buf) - 1;
+        if ((width = wparsefloat(fp, buf, buf + width)) == 0) goto match_failure;
+        if ((flags & SUPPRESS) == 0) {
+          if (flags & LONGDBL) {
+            long double res = wcstold(buf, &p);
+            *va_arg(ap, long double*) = res;
+          } else if (flags & LONG) {
+            double res = wcstod(buf, &p);
+            *va_arg(ap, double*) = res;
+          } else {
+            float res = wcstof(buf, &p);
+            *va_arg(ap, float*) = res;
+          }
+          if (p - buf != (ptrdiff_t)width) abort();
+          nassigned++;
+        }
+        nread += width;
+        nconversions++;
+        break;
+    }
+  }
 input_failure:
-	return (nconversions != 0 ? nassigned : EOF);
+  return (nconversions != 0 ? nassigned : EOF);
 match_failure:
-	return (nassigned);
+  return (nassigned);
 }
 #pragma GCC diagnostic pop
 
-int
-vfwscanf(FILE * __restrict fp, const wchar_t * __restrict fmt, __va_list ap)
-{
-	int r;
+int vfwscanf(FILE* __restrict fp, const wchar_t* __restrict fmt, __va_list ap) {
+  int r;
 
-	FLOCKFILE(fp);
-	r = __vfwscanf(fp, fmt, ap);
-	FUNLOCKFILE(fp);
-	return (r);
+  FLOCKFILE(fp);
+  r = __vfwscanf(fp, fmt, ap);
+  FUNLOCKFILE(fp);
+  return (r);
 }
diff --git a/libc/stdlib/exit.c b/libc/stdlib/exit.c
index 510cb83..e301a2a 100644
--- a/libc/stdlib/exit.c
+++ b/libc/stdlib/exit.c
@@ -29,9 +29,12 @@
 
 #include <unistd.h>
 
+#include "private/bionic_defs.h"
+
 extern void __cxa_finalize(void* dso_handle);
 extern void __cxa_thread_finalize();
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 void exit(int status) {
   __cxa_thread_finalize();
   __cxa_finalize(NULL);
diff --git a/libc/upstream-openbsd/lib/libc/stdio/fflush.c b/libc/upstream-openbsd/lib/libc/stdio/fflush.c
deleted file mode 100644
index fd1a4b3..0000000
--- a/libc/upstream-openbsd/lib/libc/stdio/fflush.c
+++ /dev/null
@@ -1,98 +0,0 @@
-/*	$OpenBSD: fflush.c,v 1.9 2015/08/31 02:53:57 guenther Exp $ */
-/*-
- * Copyright (c) 1990, 1993
- *	The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Chris Torek.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. 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.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 <errno.h>
-#include <stdio.h>
-#include "local.h"
-
-/* Flush a single file, or (if fp is NULL) all files.  */
-int
-fflush(FILE *fp)
-{
-	int	r;
-
-	if (fp == NULL)
-		return (_fwalk(__sflush_locked));
-	FLOCKFILE(fp);
-	if ((fp->_flags & (__SWR | __SRW)) == 0) {
-		errno = EBADF;
-		r = EOF;
-	} else
-		r = __sflush(fp);
-	FUNLOCKFILE(fp);
-	return (r);
-}
-DEF_STRONG(fflush);
-
-int
-__sflush(FILE *fp)
-{
-	unsigned char *p;
-	int n, t;
-
-	t = fp->_flags;
-	if ((t & __SWR) == 0)
-		return (0);
-
-	if ((p = fp->_bf._base) == NULL)
-		return (0);
-
-	n = fp->_p - p;		/* write this much */
-
-	/*
-	 * Set these immediately to avoid problems with longjmp and to allow
-	 * exchange buffering (via setvbuf) in user write function.
-	 */
-	fp->_p = p;
-	fp->_w = t & (__SLBF|__SNBF) ? 0 : fp->_bf._size;
-
-	for (; n > 0; n -= t, p += t) {
-		t = (*fp->_write)(fp->_cookie, (char *)p, n);
-		if (t <= 0) {
-			fp->_flags |= __SERR;
-			return (EOF);
-		}
-	}
-	return (0);
-}
-
-int
-__sflush_locked(FILE *fp)
-{
-	int	r;
-
-	FLOCKFILE(fp);
-	r = __sflush(fp);
-	FUNLOCKFILE(fp);
-	return (r);
-}
diff --git a/libc/upstream-openbsd/lib/libc/stdio/fgets.c b/libc/upstream-openbsd/lib/libc/stdio/fgets.c
deleted file mode 100644
index 3cea8f7..0000000
--- a/libc/upstream-openbsd/lib/libc/stdio/fgets.c
+++ /dev/null
@@ -1,106 +0,0 @@
-/*	$OpenBSD: fgets.c,v 1.16 2016/09/21 04:38:56 guenther Exp $ */
-/*-
- * Copyright (c) 1990, 1993
- *	The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Chris Torek.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. 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.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 <errno.h>
-#include <stdio.h>
-#include <string.h>
-#include "local.h"
-
-/*
- * Read at most n-1 characters from the given file.
- * Stop when a newline has been read, or the count runs out.
- * Return first argument, or NULL if no characters were read.
- * Do not return NULL if n == 1.
- */
-char *
-fgets(char *buf, int n, FILE *fp) __overloadable
-{
-	size_t len;
-	char *s;
-	unsigned char *p, *t;
-
-	if (n <= 0) {		/* sanity check */
-		errno = EINVAL;
-		return (NULL);
-	}
-
-	FLOCKFILE(fp);
-	_SET_ORIENTATION(fp, -1);
-	s = buf;
-	n--;			/* leave space for NUL */
-	while (n != 0) {
-		/*
-		 * If the buffer is empty, refill it.
-		 */
-		if (fp->_r <= 0) {
-			if (__srefill(fp)) {
-				/* EOF/error: stop with partial or no line */
-				if (s == buf) {
-					FUNLOCKFILE(fp);
-					return (NULL);
-				}
-				break;
-			}
-		}
-		len = fp->_r;
-		p = fp->_p;
-
-		/*
-		 * Scan through at most n bytes of the current buffer,
-		 * looking for '\n'.  If found, copy up to and including
-		 * newline, and stop.  Otherwise, copy entire chunk
-		 * and loop.
-		 */
-		if (len > n)
-			len = n;
-		t = memchr(p, '\n', len);
-		if (t != NULL) {
-			len = ++t - p;
-			fp->_r -= len;
-			fp->_p = t;
-			(void)memcpy(s, p, len);
-			s[len] = '\0';
-			FUNLOCKFILE(fp);
-			return (buf);
-		}
-		fp->_r -= len;
-		fp->_p += len;
-		(void)memcpy(s, p, len);
-		s += len;
-		n -= len;
-	}
-	*s = '\0';
-	FUNLOCKFILE(fp);
-	return (buf);
-}
-DEF_STRONG(fgets);
diff --git a/libc/upstream-openbsd/lib/libc/stdio/fputs.c b/libc/upstream-openbsd/lib/libc/stdio/fputs.c
deleted file mode 100644
index 05ead5c..0000000
--- a/libc/upstream-openbsd/lib/libc/stdio/fputs.c
+++ /dev/null
@@ -1,59 +0,0 @@
-/*	$OpenBSD: fputs.c,v 1.11 2015/08/31 02:53:57 guenther Exp $ */
-/*-
- * Copyright (c) 1990, 1993
- *	The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Chris Torek.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. 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.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 <stdio.h>
-#include <string.h>
-#include "local.h"
-#include "fvwrite.h"
-
-/*
- * Write the given string to the given file.
- */
-int
-fputs(const char *s, FILE *fp)
-{
-	struct __suio uio;
-	struct __siov iov;
-	int ret;
-
-	iov.iov_base = (void *)s;
-	iov.iov_len = uio.uio_resid = strlen(s);
-	uio.uio_iov = &iov;
-	uio.uio_iovcnt = 1;
-	FLOCKFILE(fp);
-	_SET_ORIENTATION(fp, -1);
-	ret = __sfvwrite(fp, &uio);
-	FUNLOCKFILE(fp);
-	return (ret);
-}
-DEF_STRONG(fputs);
diff --git a/libc/upstream-openbsd/lib/libc/stdio/fvwrite.h b/libc/upstream-openbsd/lib/libc/stdio/fvwrite.h
index f04565b..d2257cc 100644
--- a/libc/upstream-openbsd/lib/libc/stdio/fvwrite.h
+++ b/libc/upstream-openbsd/lib/libc/stdio/fvwrite.h
@@ -32,20 +32,4 @@
  * SUCH DAMAGE.
  */
 
-/*
- * I/O descriptors for __sfvwrite().
- */
-struct __siov {
-	void	*iov_base;
-	size_t	iov_len;
-};
-struct __suio {
-	struct	__siov *uio_iov;
-	int	uio_iovcnt;
-	int	uio_resid;
-};
-
-__BEGIN_HIDDEN_DECLS
-extern int __sfvwrite(FILE *, struct __suio *);
-wint_t __fputwc_unlock(wchar_t wc, FILE *fp);
-__END_HIDDEN_DECLS
+/* Moved to "local.h". */
diff --git a/libc/upstream-openbsd/lib/libc/stdio/fwrite.c b/libc/upstream-openbsd/lib/libc/stdio/fwrite.c
deleted file mode 100644
index f829398..0000000
--- a/libc/upstream-openbsd/lib/libc/stdio/fwrite.c
+++ /dev/null
@@ -1,89 +0,0 @@
-/*	$OpenBSD: fwrite.c,v 1.12 2015/08/31 02:53:57 guenther Exp $ */
-/*-
- * Copyright (c) 1990, 1993
- *	The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Chris Torek.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. 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.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <errno.h>
-#include "local.h"
-#include "fvwrite.h"
-
-#define MUL_NO_OVERFLOW	(1UL << (sizeof(size_t) * 4))
-
-/*
- * Write `count' objects (each size `size') from memory to the given file.
- * Return the number of whole objects written.
- */
-size_t
-fwrite(const void *buf, size_t size, size_t count, FILE *fp) __overloadable
-{
-	size_t n;
-	struct __suio uio;
-	struct __siov iov;
-	int ret;
-
-	/*
-	 * Extension:  Catch integer overflow
-	 */
-	if ((size >= MUL_NO_OVERFLOW || count >= MUL_NO_OVERFLOW) &&
-	    size > 0 && SIZE_MAX / size < count) {
-		errno = EOVERFLOW;
-		fp->_flags |= __SERR;
-		return (0);
-	}
-
-	/*
-	 * ANSI and SUSv2 require a return value of 0 if size or count are 0.
-	 */
-	if ((n = count * size) == 0)
-		return (0);
-
-	iov.iov_base = (void *)buf;
-	uio.uio_resid = iov.iov_len = n;
-	uio.uio_iov = &iov;
-	uio.uio_iovcnt = 1;
-
-	/*
-	 * The usual case is success (__sfvwrite returns 0);
-	 * skip the divide if this happens, since divides are
-	 * generally slow and since this occurs whenever size==0.
-	 */
-	FLOCKFILE(fp);
-	_SET_ORIENTATION(fp, -1);
-	ret = __sfvwrite(fp, &uio);
-	FUNLOCKFILE(fp);
-	if (ret == 0)
-		return (count);
-	return ((n - uio.uio_resid) / size);
-}
-DEF_STRONG(fwrite);
diff --git a/libc/upstream-openbsd/lib/libc/stdio/perror.c b/libc/upstream-openbsd/lib/libc/stdio/perror.c
deleted file mode 100644
index fdd6120..0000000
--- a/libc/upstream-openbsd/lib/libc/stdio/perror.c
+++ /dev/null
@@ -1,63 +0,0 @@
-/*	$OpenBSD: perror.c,v 1.9 2015/08/31 02:53:57 guenther Exp $ */
-/*
- * Copyright (c) 1988, 1993
- *	The Regents of the University of California.  All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. 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.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 <sys/types.h>
-#include <sys/uio.h>
-#include <unistd.h>
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-#include <limits.h>
-
-void
-perror(const char *s)
-{
-	struct iovec *v;
-	struct iovec iov[4];
-	char buf[NL_TEXTMAX];
-
-	v = iov;
-	if (s && *s) {
-		v->iov_base = (char *)s;
-		v->iov_len = strlen(s);
-		v++;
-		v->iov_base = ": ";
-		v->iov_len = 2;
-		v++;
-	}
-	(void)strerror_r(errno, buf, sizeof(buf));
-	v->iov_base = buf;
-	v->iov_len = strlen(v->iov_base);
-	v++;
-	v->iov_base = "\n";
-	v->iov_len = 1;
-	(void)writev(STDERR_FILENO, iov, (v - iov) + 1);
-}
-DEF_STRONG(perror);
diff --git a/libc/upstream-openbsd/lib/libc/stdio/puts.c b/libc/upstream-openbsd/lib/libc/stdio/puts.c
deleted file mode 100644
index 57d4b78..0000000
--- a/libc/upstream-openbsd/lib/libc/stdio/puts.c
+++ /dev/null
@@ -1,63 +0,0 @@
-/*	$OpenBSD: puts.c,v 1.12 2015/08/31 02:53:57 guenther Exp $ */
-/*-
- * Copyright (c) 1990, 1993
- *	The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Chris Torek.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. 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.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 <stdio.h>
-#include <string.h>
-#include "local.h"
-#include "fvwrite.h"
-
-/*
- * Write the given string to stdout, appending a newline.
- */
-int
-puts(const char *s)
-{
-	size_t c = strlen(s);
-	struct __suio uio;
-	struct __siov iov[2];
-	int ret;
-
-	iov[0].iov_base = (void *)s;
-	iov[0].iov_len = c;
-	iov[1].iov_base = "\n";
-	iov[1].iov_len = 1;
-	uio.uio_resid = c + 1;
-	uio.uio_iov = &iov[0];
-	uio.uio_iovcnt = 2;
-	FLOCKFILE(stdout);
-	_SET_ORIENTATION(stdout, -1);
-	ret = __sfvwrite(stdout, &uio);
-	FUNLOCKFILE(stdout);
-	return (ret ? EOF : '\n');
-}
-DEF_STRONG(puts);
diff --git a/libc/upstream-openbsd/lib/libc/stdio/vfprintf.c b/libc/upstream-openbsd/lib/libc/stdio/vfprintf.c
deleted file mode 100644
index 3d7d41e..0000000
--- a/libc/upstream-openbsd/lib/libc/stdio/vfprintf.c
+++ /dev/null
@@ -1,1561 +0,0 @@
-/*	$OpenBSD: vfprintf.c,v 1.71 2016/01/04 15:47:47 schwarze Exp $	*/
-/*-
- * Copyright (c) 1990 The Regents of the University of California.
- * All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Chris Torek.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. 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.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
- */
-
-/*
- * Actual printf innards.
- *
- * This code is large and complicated...
- */
-
-#include <sys/types.h>
-#include <sys/mman.h>
-
-#include <errno.h>
-#include <langinfo.h>
-#include <limits.h>
-#include <stdarg.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <wchar.h>
-
-#include "local.h"
-#include "fvwrite.h"
-
-union arg {
-	int			intarg;
-	unsigned int		uintarg;
-	long			longarg;
-	unsigned long		ulongarg;
-	long long		longlongarg;
-	unsigned long long	ulonglongarg;
-	ptrdiff_t		ptrdiffarg;
-	size_t			sizearg;
-	ssize_t			ssizearg;
-	intmax_t		intmaxarg;
-	uintmax_t		uintmaxarg;
-	void			*pvoidarg;
-	char			*pchararg;
-	signed char		*pschararg;
-	short			*pshortarg;
-	int			*pintarg;
-	long			*plongarg;
-	long long		*plonglongarg;
-	ptrdiff_t		*pptrdiffarg;
-	ssize_t			*pssizearg;
-	intmax_t		*pintmaxarg;
-#ifdef FLOATING_POINT
-	double			doublearg;
-	long double		longdoublearg;
-#endif
-#ifdef PRINTF_WIDE_CHAR
-	wint_t			wintarg;
-	wchar_t			*pwchararg;
-#endif
-};
-
-static int __find_arguments(const char *fmt0, va_list ap, union arg **argtable,
-    size_t *argtablesiz);
-static int __grow_type_table(unsigned char **typetable, int *tablesize);
-
-/*
- * Flush out all the vectors defined by the given uio,
- * then reset it so that it can be reused.
- */
-static int
-__sprint(FILE *fp, struct __suio *uio)
-{
-	int err;
-
-	if (uio->uio_resid == 0) {
-		uio->uio_iovcnt = 0;
-		return (0);
-	}
-	err = __sfvwrite(fp, uio);
-	uio->uio_resid = 0;
-	uio->uio_iovcnt = 0;
-	return (err);
-}
-
-/*
- * Helper function for `fprintf to unbuffered unix file': creates a
- * temporary buffer.  We only work on write-only files; this avoids
- * worries about ungetc buffers and so forth.
- */
-static int
-__sbprintf(FILE *fp, const char *fmt, va_list ap)
-{
-	int ret;
-	FILE fake;
-	struct __sfileext fakeext;
-	unsigned char buf[BUFSIZ];
-
-	_FILEEXT_SETUP(&fake, &fakeext);
-	/* copy the important variables */
-	fake._flags = fp->_flags & ~__SNBF;
-	fake._file = fp->_file;
-	fake._cookie = fp->_cookie;
-	fake._write = fp->_write;
-
-	/* set up the buffer */
-	fake._bf._base = fake._p = buf;
-	fake._bf._size = fake._w = sizeof(buf);
-	fake._lbfsize = 0;	/* not actually used, but Just In Case */
-
-	/* do the work, then copy any error status */
-	ret = __vfprintf(&fake, fmt, ap);
-	if (ret >= 0 && __sflush(&fake))
-		ret = EOF;
-	if (fake._flags & __SERR)
-		fp->_flags |= __SERR;
-	return (ret);
-}
-
-#ifdef PRINTF_WIDE_CHAR
-/*
- * Convert a wide character string argument for the %ls format to a multibyte
- * string representation. If not -1, prec specifies the maximum number of
- * bytes to output, and also means that we can't assume that the wide char
- * string is null-terminated.
- */
-static char *
-__wcsconv(wchar_t *wcsarg, int prec)
-{
-	mbstate_t mbs;
-	char buf[MB_LEN_MAX];
-	wchar_t *p;
-	char *convbuf;
-	size_t clen, nbytes;
-
-	/* Allocate space for the maximum number of bytes we could output. */
-	if (prec < 0) {
-		memset(&mbs, 0, sizeof(mbs));
-		p = wcsarg;
-		nbytes = wcsrtombs(NULL, (const wchar_t **)&p, 0, &mbs);
-		if (nbytes == (size_t)-1)
-			return (NULL);
-	} else {
-		/*
-		 * Optimisation: if the output precision is small enough,
-		 * just allocate enough memory for the maximum instead of
-		 * scanning the string.
-		 */
-		if (prec < 128)
-			nbytes = prec;
-		else {
-			nbytes = 0;
-			p = wcsarg;
-			memset(&mbs, 0, sizeof(mbs));
-			for (;;) {
-				clen = wcrtomb(buf, *p++, &mbs);
-				if (clen == 0 || clen == (size_t)-1 ||
-				    nbytes + clen > (size_t)prec)
-					break;
-				nbytes += clen;
-			}
-			if (clen == (size_t)-1)
-				return (NULL);
-		}
-	}
-	if ((convbuf = malloc(nbytes + 1)) == NULL)
-		return (NULL);
-
-	/* Fill the output buffer. */
-	p = wcsarg;
-	memset(&mbs, 0, sizeof(mbs));
-	if ((nbytes = wcsrtombs(convbuf, (const wchar_t **)&p,
-	    nbytes, &mbs)) == (size_t)-1) {
-		free(convbuf);
-		return (NULL);
-	}
-	convbuf[nbytes] = '\0';
-	return (convbuf);
-}
-#endif
-
-#ifdef FLOATING_POINT
-#include <float.h>
-#include <locale.h>
-#include <math.h>
-#include "floatio.h"
-#include "gdtoa.h"
-
-#define	DEFPREC		6
-
-static int exponent(char *, int, int);
-#endif /* FLOATING_POINT */
-
-/*
- * The size of the buffer we use as scratch space for integer
- * conversions, among other things.  Technically, we would need the
- * most space for base 10 conversions with thousands' grouping
- * characters between each pair of digits.  100 bytes is a
- * conservative overestimate even for a 128-bit uintmax_t.
- */
-#define BUF	100
-
-#define STATIC_ARG_TBL_SIZE 8	/* Size of static argument table. */
-
-
-/*
- * Macros for converting digits to letters and vice versa
- */
-#define	to_digit(c)	((c) - '0')
-#define is_digit(c)	((unsigned)to_digit(c) <= 9)
-#define	to_char(n)	((n) + '0')
-
-/*
- * Flags used during conversion.
- */
-#define	ALT		0x0001		/* alternate form */
-#define	LADJUST		0x0004		/* left adjustment */
-#define	LONGDBL		0x0008		/* long double */
-#define	LONGINT		0x0010		/* long integer */
-#define	LLONGINT	0x0020		/* long long integer */
-#define	SHORTINT	0x0040		/* short integer */
-#define	ZEROPAD		0x0080		/* zero (as opposed to blank) pad */
-#define FPT		0x0100		/* Floating point number */
-#define PTRINT		0x0200		/* (unsigned) ptrdiff_t */
-#define SIZEINT		0x0400		/* (signed) size_t */
-#define CHARINT		0x0800		/* 8 bit integer */
-#define MAXINT		0x1000		/* largest integer size (intmax_t) */
-
-int
-vfprintf(FILE *fp, const char *fmt0, __va_list ap)
-{
-	int ret;
-
-	FLOCKFILE(fp);
-	ret = __vfprintf(fp, fmt0, ap);
-	FUNLOCKFILE(fp);
-	return (ret);
-}
-DEF_STRONG(vfprintf);
-
-int
-__vfprintf(FILE *fp, const char *fmt0, __va_list ap)
-{
-	char *fmt;		/* format string */
-	int ch;			/* character from fmt */
-	int n, n2;		/* handy integers (short term usage) */
-	char *cp;		/* handy char pointer (short term usage) */
-	struct __siov *iovp;	/* for PRINT macro */
-	int flags;		/* flags as above */
-	int ret;		/* return value accumulator */
-	int width;		/* width from format (%8d), or 0 */
-	int prec;		/* precision from format; <0 for N/A */
-	char sign;		/* sign prefix (' ', '+', '-', or \0) */
-	wchar_t wc;
-	mbstate_t ps;
-#ifdef FLOATING_POINT
-	/*
-	 * We can decompose the printed representation of floating
-	 * point numbers into several parts, some of which may be empty:
-	 *
-	 * [+|-| ] [0x|0X] MMM . NNN [e|E|p|P] [+|-] ZZ
-	 *    A       B     ---C---      D       E   F
-	 *
-	 * A:	'sign' holds this value if present; '\0' otherwise
-	 * B:	ox[1] holds the 'x' or 'X'; '\0' if not hexadecimal
-	 * C:	cp points to the string MMMNNN.  Leading and trailing
-	 *	zeros are not in the string and must be added.
-	 * D:	expchar holds this character; '\0' if no exponent, e.g. %f
-	 * F:	at least two digits for decimal, at least one digit for hex
-	 */
-	char *decimal_point = NULL;
-	int signflag;		/* true if float is negative */
-	union {			/* floating point arguments %[aAeEfFgG] */
-		double dbl;
-		long double ldbl;
-	} fparg;
-	int expt;		/* integer value of exponent */
-	char expchar;		/* exponent character: [eEpP\0] */
-	char *dtoaend;		/* pointer to end of converted digits */
-	int expsize;		/* character count for expstr */
-	int lead;		/* sig figs before decimal or group sep */
-	int ndig;		/* actual number of digits returned by dtoa */
-	char expstr[MAXEXPDIG+2];	/* buffer for exponent string: e+ZZZ */
-	char *dtoaresult = NULL;
-#endif
-
-	uintmax_t _umax;	/* integer arguments %[diouxX] */
-	enum { OCT, DEC, HEX } base;	/* base for %[diouxX] conversion */
-	int dprec;		/* a copy of prec if %[diouxX], 0 otherwise */
-	int realsz;		/* field size expanded by dprec */
-	int size;		/* size of converted field or string */
-	const char *xdigs;	/* digits for %[xX] conversion */
-#define NIOV 8
-	struct __suio uio;	/* output information: summary */
-	struct __siov iov[NIOV];/* ... and individual io vectors */
-	char buf[BUF];		/* buffer with space for digits of uintmax_t */
-	char ox[2];		/* space for 0x; ox[1] is either x, X, or \0 */
-	union arg *argtable;	/* args, built due to positional arg */
-	union arg statargtable[STATIC_ARG_TBL_SIZE];
-	size_t argtablesiz;
-	int nextarg;		/* 1-based argument index */
-	va_list orgap;		/* original argument pointer */
-#ifdef PRINTF_WIDE_CHAR
-	char *convbuf;		/* buffer for wide to multi-byte conversion */
-#endif
-
-	/*
-	 * Choose PADSIZE to trade efficiency vs. size.  If larger printf
-	 * fields occur frequently, increase PADSIZE and make the initialisers
-	 * below longer.
-	 */
-#define	PADSIZE	16		/* pad chunk size */
-	static char blanks[PADSIZE] =
-	 {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
-	static char zeroes[PADSIZE] =
-	 {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
-
-	static const char xdigs_lower[16] = "0123456789abcdef";
-	static const char xdigs_upper[16] = "0123456789ABCDEF";
-
-	/*
-	 * BEWARE, these `goto error' on error, and PAD uses `n'.
-	 */
-#define	PRINT(ptr, len) do { \
-	iovp->iov_base = (ptr); \
-	iovp->iov_len = (len); \
-	uio.uio_resid += (len); \
-	iovp++; \
-	if (++uio.uio_iovcnt >= NIOV) { \
-		if (__sprint(fp, &uio)) \
-			goto error; \
-		iovp = iov; \
-	} \
-} while (0)
-#define	PAD(howmany, with) do { \
-	if ((n = (howmany)) > 0) { \
-		while (n > PADSIZE) { \
-			PRINT(with, PADSIZE); \
-			n -= PADSIZE; \
-		} \
-		PRINT(with, n); \
-	} \
-} while (0)
-#define	PRINTANDPAD(p, ep, len, with) do {	\
-	n2 = (ep) - (p);       			\
-	if (n2 > (len))				\
-		n2 = (len);			\
-	if (n2 > 0)				\
-		PRINT((p), n2);			\
-	PAD((len) - (n2 > 0 ? n2 : 0), (with));	\
-} while(0)
-#define	FLUSH() do { \
-	if (uio.uio_resid && __sprint(fp, &uio)) \
-		goto error; \
-	uio.uio_iovcnt = 0; \
-	iovp = iov; \
-} while (0)
-
-	/*
-	 * To extend shorts properly, we need both signed and unsigned
-	 * argument extraction methods.
-	 */
-#define	SARG() \
-	((intmax_t)(flags&MAXINT ? GETARG(intmax_t) : \
-	    flags&LLONGINT ? GETARG(long long) : \
-	    flags&LONGINT ? GETARG(long) : \
-	    flags&PTRINT ? GETARG(ptrdiff_t) : \
-	    flags&SIZEINT ? GETARG(ssize_t) : \
-	    flags&SHORTINT ? (short)GETARG(int) : \
-	    flags&CHARINT ? (signed char)GETARG(int) : \
-	    GETARG(int)))
-#define	UARG() \
-	((uintmax_t)(flags&MAXINT ? GETARG(uintmax_t) : \
-	    flags&LLONGINT ? GETARG(unsigned long long) : \
-	    flags&LONGINT ? GETARG(unsigned long) : \
-	    flags&PTRINT ? (uintptr_t)GETARG(ptrdiff_t) : /* XXX */ \
-	    flags&SIZEINT ? GETARG(size_t) : \
-	    flags&SHORTINT ? (unsigned short)GETARG(int) : \
-	    flags&CHARINT ? (unsigned char)GETARG(int) : \
-	    GETARG(unsigned int)))
-
-	/*
-	 * Append a digit to a value and check for overflow.
-	 */
-#define APPEND_DIGIT(val, dig) do { \
-	if ((val) > INT_MAX / 10) \
-		goto overflow; \
-	(val) *= 10; \
-	if ((val) > INT_MAX - to_digit((dig))) \
-		goto overflow; \
-	(val) += to_digit((dig)); \
-} while (0)
-
-	 /*
-	  * Get * arguments, including the form *nn$.  Preserve the nextarg
-	  * that the argument can be gotten once the type is determined.
-	  */
-#define GETASTER(val) \
-	n2 = 0; \
-	cp = fmt; \
-	while (is_digit(*cp)) { \
-		APPEND_DIGIT(n2, *cp); \
-		cp++; \
-	} \
-	if (*cp == '$') { \
-		int hold = nextarg; \
-		if (argtable == NULL) { \
-			argtable = statargtable; \
-			if (__find_arguments(fmt0, orgap, &argtable, \
-			    &argtablesiz) == -1) { \
-				ret = -1; \
-				goto error; \
-			} \
-		} \
-		nextarg = n2; \
-		val = GETARG(int); \
-		nextarg = hold; \
-		fmt = ++cp; \
-	} else { \
-		val = GETARG(int); \
-	}
-
-/*
-* Get the argument indexed by nextarg.   If the argument table is
-* built, use it to get the argument.  If its not, get the next
-* argument (and arguments must be gotten sequentially).
-*/
-#define GETARG(type) \
-	((argtable != NULL) ? *((type*)(&argtable[nextarg++])) : \
-		(nextarg++, va_arg(ap, type)))
-
-	_SET_ORIENTATION(fp, -1);
-	/* sorry, fprintf(read_only_file, "") returns EOF, not 0 */
-	if (cantwrite(fp)) {
-		errno = EBADF;
-		return (EOF);
-	}
-
-	/* optimise fprintf(stderr) (and other unbuffered Unix files) */
-	if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) &&
-	    fp->_file >= 0)
-		return (__sbprintf(fp, fmt0, ap));
-
-	fmt = (char *)fmt0;
-	argtable = NULL;
-	nextarg = 1;
-	va_copy(orgap, ap);
-	uio.uio_iov = iovp = iov;
-	uio.uio_resid = 0;
-	uio.uio_iovcnt = 0;
-	ret = 0;
-#ifdef PRINTF_WIDE_CHAR
-	convbuf = NULL;
-#endif
-
-	memset(&ps, 0, sizeof(ps));
-	/*
-	 * Scan the format for conversions (`%' character).
-	 */
-	for (;;) {
-		cp = fmt;
-		while ((n = mbrtowc(&wc, fmt, MB_CUR_MAX, &ps)) > 0) {
-			fmt += n;
-			if (wc == '%') {
-				fmt--;
-				break;
-			}
-		}
-		if (n < 0) {
-			ret = -1;
-			goto error;
-		}
-		if (fmt != cp) {
-			ptrdiff_t m = fmt - cp;
-			if (m < 0 || m > INT_MAX - ret)
-				goto overflow;
-			PRINT(cp, m);
-			ret += m;
-		}
-		if (n == 0)
-			goto done;
-		fmt++;		/* skip over '%' */
-
-		flags = 0;
-		dprec = 0;
-		width = 0;
-		prec = -1;
-		sign = '\0';
-		ox[1] = '\0';
-
-rflag:		ch = *fmt++;
-reswitch:	switch (ch) {
-		case ' ':
-			/*
-			 * ``If the space and + flags both appear, the space
-			 * flag will be ignored.''
-			 *	-- ANSI X3J11
-			 */
-			if (!sign)
-				sign = ' ';
-			goto rflag;
-		case '#':
-			flags |= ALT;
-			goto rflag;
-		case '\'':
-			/* grouping not implemented */
-			goto rflag;
-		case '*':
-			/*
-			 * ``A negative field width argument is taken as a
-			 * - flag followed by a positive field width.''
-			 *	-- ANSI X3J11
-			 * They don't exclude field widths read from args.
-			 */
-			GETASTER(width);
-			if (width >= 0)
-				goto rflag;
-			if (width == INT_MIN)
-				goto overflow;
-			width = -width;
-			/* FALLTHROUGH */
-		case '-':
-			flags |= LADJUST;
-			goto rflag;
-		case '+':
-			sign = '+';
-			goto rflag;
-		case '.':
-			if ((ch = *fmt++) == '*') {
-				GETASTER(n);
-				prec = n < 0 ? -1 : n;
-				goto rflag;
-			}
-			n = 0;
-			while (is_digit(ch)) {
-				APPEND_DIGIT(n, ch);
-				ch = *fmt++;
-			}
-			if (ch == '$') {
-				nextarg = n;
-				if (argtable == NULL) {
-					argtable = statargtable;
-					if (__find_arguments(fmt0, orgap,
-					    &argtable, &argtablesiz) == -1) {
-						ret = -1;
-						goto error;
-					}
-				}
-				goto rflag;
-			}
-			prec = n;
-			goto reswitch;
-		case '0':
-			/*
-			 * ``Note that 0 is taken as a flag, not as the
-			 * beginning of a field width.''
-			 *	-- ANSI X3J11
-			 */
-			flags |= ZEROPAD;
-			goto rflag;
-		case '1': case '2': case '3': case '4':
-		case '5': case '6': case '7': case '8': case '9':
-			n = 0;
-			do {
-				APPEND_DIGIT(n, ch);
-				ch = *fmt++;
-			} while (is_digit(ch));
-			if (ch == '$') {
-				nextarg = n;
-				if (argtable == NULL) {
-					argtable = statargtable;
-					if (__find_arguments(fmt0, orgap,
-					    &argtable, &argtablesiz) == -1) {
-						ret = -1;
-						goto error;
-					}
-				}
-				goto rflag;
-			}
-			width = n;
-			goto reswitch;
-#ifdef FLOATING_POINT
-		case 'L':
-			flags |= LONGDBL;
-			goto rflag;
-#endif
-		case 'h':
-			if (*fmt == 'h') {
-				fmt++;
-				flags |= CHARINT;
-			} else {
-				flags |= SHORTINT;
-			}
-			goto rflag;
-		case 'j':
-			flags |= MAXINT;
-			goto rflag;
-		case 'l':
-			if (*fmt == 'l') {
-				fmt++;
-				flags |= LLONGINT;
-			} else {
-				flags |= LONGINT;
-			}
-			goto rflag;
-		case 'q':
-			flags |= LLONGINT;
-			goto rflag;
-		case 't':
-			flags |= PTRINT;
-			goto rflag;
-		case 'z':
-			flags |= SIZEINT;
-			goto rflag;
-		case 'c':
-#ifdef PRINTF_WIDE_CHAR
-			if (flags & LONGINT) {
-				mbstate_t mbs;
-				size_t mbseqlen;
-
-				memset(&mbs, 0, sizeof(mbs));
-				mbseqlen = wcrtomb(buf,
-				    (wchar_t)GETARG(wint_t), &mbs);
-				if (mbseqlen == (size_t)-1) {
-					ret = -1;
-					goto error;
-				}
-				cp = buf;
-				size = (int)mbseqlen;
-			} else {
-#endif
-				*(cp = buf) = GETARG(int);
-				size = 1;
-#ifdef PRINTF_WIDE_CHAR
-			}
-#endif
-			sign = '\0';
-			break;
-		case 'D':
-			flags |= LONGINT;
-			/*FALLTHROUGH*/
-		case 'd':
-		case 'i':
-			_umax = SARG();
-			if ((intmax_t)_umax < 0) {
-				_umax = -_umax;
-				sign = '-';
-			}
-			base = DEC;
-			goto number;
-#ifdef FLOATING_POINT
-		case 'a':
-		case 'A':
-			if (ch == 'a') {
-				ox[1] = 'x';
-				xdigs = xdigs_lower;
-				expchar = 'p';
-			} else {
-				ox[1] = 'X';
-				xdigs = xdigs_upper;
-				expchar = 'P';
-			}
-			if (prec >= 0)
-				prec++;
-			if (dtoaresult)
-				__freedtoa(dtoaresult);
-			if (flags & LONGDBL) {
-				fparg.ldbl = GETARG(long double);
-				dtoaresult = cp =
-				    __hldtoa(fparg.ldbl, xdigs, prec,
-				    &expt, &signflag, &dtoaend);
-				if (dtoaresult == NULL) {
-					errno = ENOMEM;
-					goto error;
-				}
-			} else {
-				fparg.dbl = GETARG(double);
-				dtoaresult = cp =
-				    __hdtoa(fparg.dbl, xdigs, prec,
-				    &expt, &signflag, &dtoaend);
-				if (dtoaresult == NULL) {
-					errno = ENOMEM;
-					goto error;
-				}
-			}
-			if (prec < 0)
-				prec = dtoaend - cp;
-			if (expt == INT_MAX)
-				ox[1] = '\0';
-			goto fp_common;
-		case 'e':
-		case 'E':
-			expchar = ch;
-			if (prec < 0)	/* account for digit before decpt */
-				prec = DEFPREC + 1;
-			else
-				prec++;
-			goto fp_begin;
-		case 'f':
-		case 'F':
-			expchar = '\0';
-			goto fp_begin;
-		case 'g':
-		case 'G':
-			expchar = ch - ('g' - 'e');
- 			if (prec == 0)
- 				prec = 1;
-fp_begin:
-			if (prec < 0)
-				prec = DEFPREC;
-			if (dtoaresult)
-				__freedtoa(dtoaresult);
-			if (flags & LONGDBL) {
-				fparg.ldbl = GETARG(long double);
-				dtoaresult = cp =
-				    __ldtoa(&fparg.ldbl, expchar ? 2 : 3, prec,
-				    &expt, &signflag, &dtoaend);
-				if (dtoaresult == NULL) {
-					errno = ENOMEM;
-					goto error;
-				}
-			} else {
-				fparg.dbl = GETARG(double);
-				dtoaresult = cp =
-				    __dtoa(fparg.dbl, expchar ? 2 : 3, prec,
-				    &expt, &signflag, &dtoaend);
-				if (dtoaresult == NULL) {
-					errno = ENOMEM;
-					goto error;
-				}
-				if (expt == 9999)
-					expt = INT_MAX;
- 			}
-fp_common:
-			if (signflag)
-				sign = '-';
-			if (expt == INT_MAX) {	/* inf or nan */
-				if (*cp == 'N')
-					cp = (ch >= 'a') ? "nan" : "NAN";
-				else
-					cp = (ch >= 'a') ? "inf" : "INF";
- 				size = 3;
-				flags &= ~ZEROPAD;
- 				break;
- 			}
-			flags |= FPT;
-			ndig = dtoaend - cp;
- 			if (ch == 'g' || ch == 'G') {
-				if (expt > -4 && expt <= prec) {
-					/* Make %[gG] smell like %[fF] */
-					expchar = '\0';
-					if (flags & ALT)
-						prec -= expt;
-					else
-						prec = ndig - expt;
-					if (prec < 0)
-						prec = 0;
-				} else {
-					/*
-					 * Make %[gG] smell like %[eE], but
-					 * trim trailing zeroes if no # flag.
-					 */
-					if (!(flags & ALT))
-						prec = ndig;
-				}
- 			}
-			if (expchar) {
-				expsize = exponent(expstr, expt - 1, expchar);
-				size = expsize + prec;
-				if (prec > 1 || flags & ALT)
- 					++size;
-			} else {
-				/* space for digits before decimal point */
-				if (expt > 0)
-					size = expt;
-				else	/* "0" */
-					size = 1;
-				/* space for decimal pt and following digits */
-				if (prec || flags & ALT)
-					size += prec + 1;
-				lead = expt;
-			}
-			break;
-#endif /* FLOATING_POINT */
-#ifndef NO_PRINTF_PERCENT_N
-		case 'n':
-			if (flags & LLONGINT)
-				*GETARG(long long *) = ret;
-			else if (flags & LONGINT)
-				*GETARG(long *) = ret;
-			else if (flags & SHORTINT)
-				*GETARG(short *) = ret;
-			else if (flags & CHARINT)
-				*GETARG(signed char *) = ret;
-			else if (flags & PTRINT)
-				*GETARG(ptrdiff_t *) = ret;
-			else if (flags & SIZEINT)
-				*GETARG(ssize_t *) = ret;
-			else if (flags & MAXINT)
-				*GETARG(intmax_t *) = ret;
-			else
-				*GETARG(int *) = ret;
-			continue;	/* no output */
-#endif /* NO_PRINTF_PERCENT_N */
-		case 'O':
-			flags |= LONGINT;
-			/*FALLTHROUGH*/
-		case 'o':
-			_umax = UARG();
-			base = OCT;
-			goto nosign;
-		case 'p':
-			/*
-			 * ``The argument shall be a pointer to void.  The
-			 * value of the pointer is converted to a sequence
-			 * of printable characters, in an implementation-
-			 * defined manner.''
-			 *	-- ANSI X3J11
-			 */
-			_umax = (u_long)GETARG(void *);
-			base = HEX;
-			xdigs = xdigs_lower;
-			ox[1] = 'x';
-			goto nosign;
-		case 's':
-#ifdef PRINTF_WIDE_CHAR
-			if (flags & LONGINT) {
-				wchar_t *wcp;
-
-				free(convbuf);
-				convbuf = NULL;
-				if ((wcp = GETARG(wchar_t *)) == NULL) {
-					cp = "(null)";
-				} else {
-					convbuf = __wcsconv(wcp, prec);
-					if (convbuf == NULL) {
-						ret = -1;
-						goto error;
-					}
-					cp = convbuf;
-				}
-			} else
-#endif /* PRINTF_WIDE_CHAR */
-			if ((cp = GETARG(char *)) == NULL)
-				cp = "(null)";
-			if (prec >= 0) {
-				/*
-				 * can't use strlen; can only look for the
-				 * NUL in the first `prec' characters, and
-				 * strlen() will go further.
-				 */
-				char *p = memchr(cp, 0, prec);
-
-				size = p ? (p - cp) : prec;
-			} else {
-				size_t len;
-
-				if ((len = strlen(cp)) > INT_MAX)
-					goto overflow;
-				size = (int)len;
-			}
-			sign = '\0';
-			break;
-		case 'U':
-			flags |= LONGINT;
-			/*FALLTHROUGH*/
-		case 'u':
-			_umax = UARG();
-			base = DEC;
-			goto nosign;
-		case 'X':
-			xdigs = xdigs_upper;
-			goto hex;
-		case 'x':
-			xdigs = xdigs_lower;
-hex:			_umax = UARG();
-			base = HEX;
-			/* leading 0x/X only if non-zero */
-			if (flags & ALT && _umax != 0)
-				ox[1] = ch;
-
-			/* unsigned conversions */
-nosign:			sign = '\0';
-			/*
-			 * ``... diouXx conversions ... if a precision is
-			 * specified, the 0 flag will be ignored.''
-			 *	-- ANSI X3J11
-			 */
-number:			if ((dprec = prec) >= 0)
-				flags &= ~ZEROPAD;
-
-			/*
-			 * ``The result of converting a zero value with an
-			 * explicit precision of zero is no characters.''
-			 *	-- ANSI X3J11
-			 */
-			cp = buf + BUF;
-			if (_umax != 0 || prec != 0) {
-				/*
-				 * Unsigned mod is hard, and unsigned mod
-				 * by a constant is easier than that by
-				 * a variable; hence this switch.
-				 */
-				switch (base) {
-				case OCT:
-					do {
-						*--cp = to_char(_umax & 7);
-						_umax >>= 3;
-					} while (_umax);
-					/* handle octal leading 0 */
-					if (flags & ALT && *cp != '0')
-						*--cp = '0';
-					break;
-
-				case DEC:
-					/* many numbers are 1 digit */
-					while (_umax >= 10) {
-						*--cp = to_char(_umax % 10);
-						_umax /= 10;
-					}
-					*--cp = to_char(_umax);
-					break;
-
-				case HEX:
-					do {
-						*--cp = xdigs[_umax & 15];
-						_umax >>= 4;
-					} while (_umax);
-					break;
-
-				default:
-					cp = "bug in vfprintf: bad base";
-					size = strlen(cp);
-					goto skipsize;
-				}
-			}
-			size = buf + BUF - cp;
-			if (size > BUF)	/* should never happen */
-				abort();
-		skipsize:
-			break;
-		default:	/* "%?" prints ?, unless ? is NUL */
-			if (ch == '\0')
-				goto done;
-			/* pretend it was %c with argument ch */
-			cp = buf;
-			*cp = ch;
-			size = 1;
-			sign = '\0';
-			break;
-		}
-
-		/*
-		 * All reasonable formats wind up here.  At this point, `cp'
-		 * points to a string which (if not flags&LADJUST) should be
-		 * padded out to `width' places.  If flags&ZEROPAD, it should
-		 * first be prefixed by any sign or other prefix; otherwise,
-		 * it should be blank padded before the prefix is emitted.
-		 * After any left-hand padding and prefixing, emit zeroes
-		 * required by a decimal %[diouxX] precision, then print the
-		 * string proper, then emit zeroes required by any leftover
-		 * floating precision; finally, if LADJUST, pad with blanks.
-		 *
-		 * Compute actual size, so we know how much to pad.
-		 * size excludes decimal prec; realsz includes it.
-		 */
-		realsz = dprec > size ? dprec : size;
-		if (sign)
-			realsz++;
-		if (ox[1])
-			realsz+= 2;
-
-		/* right-adjusting blank padding */
-		if ((flags & (LADJUST|ZEROPAD)) == 0)
-			PAD(width - realsz, blanks);
-
-		/* prefix */
-		if (sign)
-			PRINT(&sign, 1);
-		if (ox[1]) {	/* ox[1] is either x, X, or \0 */
-			ox[0] = '0';
-			PRINT(ox, 2);
-		}
-
-		/* right-adjusting zero padding */
-		if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
-			PAD(width - realsz, zeroes);
-
-		/* leading zeroes from decimal precision */
-		PAD(dprec - size, zeroes);
-
-		/* the string or number proper */
-#ifdef FLOATING_POINT
-		if ((flags & FPT) == 0) {
-			PRINT(cp, size);
-		} else {	/* glue together f_p fragments */
-			if (decimal_point == NULL)
-				decimal_point = nl_langinfo(RADIXCHAR);
-			if (!expchar) {	/* %[fF] or sufficiently short %[gG] */
-				if (expt <= 0) {
-					PRINT(zeroes, 1);
-					if (prec || flags & ALT)
-						PRINT(decimal_point, 1);
-					PAD(-expt, zeroes);
-					/* already handled initial 0's */
-					prec += expt;
- 				} else {
-					PRINTANDPAD(cp, dtoaend, lead, zeroes);
-					cp += lead;
-					if (prec || flags & ALT)
-						PRINT(decimal_point, 1);
-				}
-				PRINTANDPAD(cp, dtoaend, prec, zeroes);
-			} else {	/* %[eE] or sufficiently long %[gG] */
-				if (prec > 1 || flags & ALT) {
-					buf[0] = *cp++;
-					buf[1] = *decimal_point;
-					PRINT(buf, 2);
-					PRINT(cp, ndig-1);
-					PAD(prec - ndig, zeroes);
-				} else { /* XeYYY */
-					PRINT(cp, 1);
-				}
-				PRINT(expstr, expsize);
-			}
-		}
-#else
-		PRINT(cp, size);
-#endif
-		/* left-adjusting padding (always blank) */
-		if (flags & LADJUST)
-			PAD(width - realsz, blanks);
-
-		/* finally, adjust ret */
-		if (width < realsz)
-			width = realsz;
-		if (width > INT_MAX - ret)
-			goto overflow;
-		ret += width;
-
-		FLUSH();	/* copy out the I/O vectors */
-	}
-done:
-	FLUSH();
-error:
-	va_end(orgap);
-	if (__sferror(fp))
-		ret = -1;
-	goto finish;
-
-overflow:
-	errno = ENOMEM;
-	ret = -1;
-
-finish:
-#ifdef PRINTF_WIDE_CHAR
-	free(convbuf);
-#endif
-#ifdef FLOATING_POINT
-	if (dtoaresult)
-		__freedtoa(dtoaresult);
-#endif
-	if (argtable != NULL && argtable != statargtable) {
-		munmap(argtable, argtablesiz);
-		argtable = NULL;
-	}
-	return (ret);
-}
-
-/*
- * Type ids for argument type table.
- */
-#define T_UNUSED	0
-#define T_SHORT		1
-#define T_U_SHORT	2
-#define TP_SHORT	3
-#define T_INT		4
-#define T_U_INT		5
-#define TP_INT		6
-#define T_LONG		7
-#define T_U_LONG	8
-#define TP_LONG		9
-#define T_LLONG		10
-#define T_U_LLONG	11
-#define TP_LLONG	12
-#define T_DOUBLE	13
-#define T_LONG_DOUBLE	14
-#define TP_CHAR		15
-#define TP_VOID		16
-#define T_PTRINT	17
-#define TP_PTRINT	18
-#define T_SIZEINT	19
-#define T_SSIZEINT	20
-#define TP_SSIZEINT	21
-#define T_MAXINT	22
-#define T_MAXUINT	23
-#define TP_MAXINT	24
-#define T_CHAR		25
-#define T_U_CHAR	26
-#define T_WINT		27
-#define TP_WCHAR	28
-
-/*
- * Find all arguments when a positional parameter is encountered.  Returns a
- * table, indexed by argument number, of pointers to each arguments.  The
- * initial argument table should be an array of STATIC_ARG_TBL_SIZE entries.
- * It will be replaced with a mmap-ed one if it overflows (malloc cannot be
- * used since we are attempting to make snprintf thread safe, and alloca is
- * problematic since we have nested functions..)
- */
-static int
-__find_arguments(const char *fmt0, va_list ap, union arg **argtable,
-    size_t *argtablesiz)
-{
-	char *fmt;		/* format string */
-	int ch;			/* character from fmt */
-	int n, n2;		/* handy integer (short term usage) */
-	char *cp;		/* handy char pointer (short term usage) */
-	int flags;		/* flags as above */
-	unsigned char *typetable; /* table of types */
-	unsigned char stattypetable[STATIC_ARG_TBL_SIZE];
-	int tablesize;		/* current size of type table */
-	int tablemax;		/* largest used index in table */
-	int nextarg;		/* 1-based argument index */
-	int ret = 0;		/* return value */
-	wchar_t wc;
-	mbstate_t ps;
-
-	/*
-	 * Add an argument type to the table, expanding if necessary.
-	 */
-#define ADDTYPE(type) \
-	((nextarg >= tablesize) ? \
-		__grow_type_table(&typetable, &tablesize) : 0, \
-	(nextarg > tablemax) ? tablemax = nextarg : 0, \
-	typetable[nextarg++] = type)
-
-#define	ADDSARG() \
-        ((flags&MAXINT) ? ADDTYPE(T_MAXINT) : \
-	    ((flags&PTRINT) ? ADDTYPE(T_PTRINT) : \
-	    ((flags&SIZEINT) ? ADDTYPE(T_SSIZEINT) : \
-	    ((flags&LLONGINT) ? ADDTYPE(T_LLONG) : \
-	    ((flags&LONGINT) ? ADDTYPE(T_LONG) : \
-	    ((flags&SHORTINT) ? ADDTYPE(T_SHORT) : \
-	    ((flags&CHARINT) ? ADDTYPE(T_CHAR) : ADDTYPE(T_INT))))))))
-
-#define	ADDUARG() \
-        ((flags&MAXINT) ? ADDTYPE(T_MAXUINT) : \
-	    ((flags&PTRINT) ? ADDTYPE(T_PTRINT) : \
-	    ((flags&SIZEINT) ? ADDTYPE(T_SIZEINT) : \
-	    ((flags&LLONGINT) ? ADDTYPE(T_U_LLONG) : \
-	    ((flags&LONGINT) ? ADDTYPE(T_U_LONG) : \
-	    ((flags&SHORTINT) ? ADDTYPE(T_U_SHORT) : \
-	    ((flags&CHARINT) ? ADDTYPE(T_U_CHAR) : ADDTYPE(T_U_INT))))))))
-
-	/*
-	 * Add * arguments to the type array.
-	 */
-#define ADDASTER() \
-	n2 = 0; \
-	cp = fmt; \
-	while (is_digit(*cp)) { \
-		APPEND_DIGIT(n2, *cp); \
-		cp++; \
-	} \
-	if (*cp == '$') { \
-		int hold = nextarg; \
-		nextarg = n2; \
-		ADDTYPE(T_INT); \
-		nextarg = hold; \
-		fmt = ++cp; \
-	} else { \
-		ADDTYPE(T_INT); \
-	}
-	fmt = (char *)fmt0;
-	typetable = stattypetable;
-	tablesize = STATIC_ARG_TBL_SIZE;
-	tablemax = 0;
-	nextarg = 1;
-	memset(typetable, T_UNUSED, STATIC_ARG_TBL_SIZE);
-	memset(&ps, 0, sizeof(ps));
-
-	/*
-	 * Scan the format for conversions (`%' character).
-	 */
-	for (;;) {
-		cp = fmt;
-		while ((n = mbrtowc(&wc, fmt, MB_CUR_MAX, &ps)) > 0) {
-			fmt += n;
-			if (wc == '%') {
-				fmt--;
-				break;
-			}
-		}
-		if (n < 0)
-			return (-1);
-		if (n == 0)
-			goto done;
-		fmt++;		/* skip over '%' */
-
-		flags = 0;
-
-rflag:		ch = *fmt++;
-reswitch:	switch (ch) {
-		case ' ':
-		case '#':
-		case '\'':
-			goto rflag;
-		case '*':
-			ADDASTER();
-			goto rflag;
-		case '-':
-		case '+':
-			goto rflag;
-		case '.':
-			if ((ch = *fmt++) == '*') {
-				ADDASTER();
-				goto rflag;
-			}
-			while (is_digit(ch)) {
-				ch = *fmt++;
-			}
-			goto reswitch;
-		case '0':
-			goto rflag;
-		case '1': case '2': case '3': case '4':
-		case '5': case '6': case '7': case '8': case '9':
-			n = 0;
-			do {
-				APPEND_DIGIT(n ,ch);
-				ch = *fmt++;
-			} while (is_digit(ch));
-			if (ch == '$') {
-				nextarg = n;
-				goto rflag;
-			}
-			goto reswitch;
-#ifdef FLOATING_POINT
-		case 'L':
-			flags |= LONGDBL;
-			goto rflag;
-#endif
-		case 'h':
-			if (*fmt == 'h') {
-				fmt++;
-				flags |= CHARINT;
-			} else {
-				flags |= SHORTINT;
-			}
-			goto rflag;
-		case 'j':
-			flags |= MAXINT;
-			goto rflag;
-		case 'l':
-			if (*fmt == 'l') {
-				fmt++;
-				flags |= LLONGINT;
-			} else {
-				flags |= LONGINT;
-			}
-			goto rflag;
-		case 'q':
-			flags |= LLONGINT;
-			goto rflag;
-		case 't':
-			flags |= PTRINT;
-			goto rflag;
-		case 'z':
-			flags |= SIZEINT;
-			goto rflag;
-		case 'c':
-#ifdef PRINTF_WIDE_CHAR
-			if (flags & LONGINT)
-				ADDTYPE(T_WINT);
-			else
-#endif
-				ADDTYPE(T_INT);
-			break;
-		case 'D':
-			flags |= LONGINT;
-			/*FALLTHROUGH*/
-		case 'd':
-		case 'i':
-			ADDSARG();
-			break;
-#ifdef FLOATING_POINT
-		case 'a':
-		case 'A':
-		case 'e':
-		case 'E':
-		case 'f':
-		case 'F':
-		case 'g':
-		case 'G':
-			if (flags & LONGDBL)
-				ADDTYPE(T_LONG_DOUBLE);
-			else
-				ADDTYPE(T_DOUBLE);
-			break;
-#endif /* FLOATING_POINT */
-#ifndef NO_PRINTF_PERCENT_N
-		case 'n':
-			if (flags & LLONGINT)
-				ADDTYPE(TP_LLONG);
-			else if (flags & LONGINT)
-				ADDTYPE(TP_LONG);
-			else if (flags & SHORTINT)
-				ADDTYPE(TP_SHORT);
-			else if (flags & PTRINT)
-				ADDTYPE(TP_PTRINT);
-			else if (flags & SIZEINT)
-				ADDTYPE(TP_SSIZEINT);
-			else if (flags & MAXINT)
-				ADDTYPE(TP_MAXINT);
-			else
-				ADDTYPE(TP_INT);
-			continue;	/* no output */
-#endif /* NO_PRINTF_PERCENT_N */
-		case 'O':
-			flags |= LONGINT;
-			/*FALLTHROUGH*/
-		case 'o':
-			ADDUARG();
-			break;
-		case 'p':
-			ADDTYPE(TP_VOID);
-			break;
-		case 's':
-#ifdef PRINTF_WIDE_CHAR
-			if (flags & LONGINT)
-				ADDTYPE(TP_WCHAR);
-			else
-#endif
-				ADDTYPE(TP_CHAR);
-			break;
-		case 'U':
-			flags |= LONGINT;
-			/*FALLTHROUGH*/
-		case 'u':
-		case 'X':
-		case 'x':
-			ADDUARG();
-			break;
-		default:	/* "%?" prints ?, unless ? is NUL */
-			if (ch == '\0')
-				goto done;
-			break;
-		}
-	}
-done:
-	/*
-	 * Build the argument table.
-	 */
-	if (tablemax >= STATIC_ARG_TBL_SIZE) {
-		*argtablesiz = sizeof(union arg) * (tablemax + 1);
-		*argtable = mmap(NULL, *argtablesiz,
-		    PROT_WRITE|PROT_READ, MAP_ANON|MAP_PRIVATE, -1, 0);
-		if (*argtable == MAP_FAILED)
-			return (-1);
-	}
-
-#if 0
-	/* XXX is this required? */
-	(*argtable)[0].intarg = 0;
-#endif
-	for (n = 1; n <= tablemax; n++) {
-		switch (typetable[n]) {
-		case T_UNUSED:
-		case T_CHAR:
-		case T_U_CHAR:
-		case T_SHORT:
-		case T_U_SHORT:
-		case T_INT:
-			(*argtable)[n].intarg = va_arg(ap, int);
-			break;
-		case TP_SHORT:
-			(*argtable)[n].pshortarg = va_arg(ap, short *);
-			break;
-		case T_U_INT:
-			(*argtable)[n].uintarg = va_arg(ap, unsigned int);
-			break;
-		case TP_INT:
-			(*argtable)[n].pintarg = va_arg(ap, int *);
-			break;
-		case T_LONG:
-			(*argtable)[n].longarg = va_arg(ap, long);
-			break;
-		case T_U_LONG:
-			(*argtable)[n].ulongarg = va_arg(ap, unsigned long);
-			break;
-		case TP_LONG:
-			(*argtable)[n].plongarg = va_arg(ap, long *);
-			break;
-		case T_LLONG:
-			(*argtable)[n].longlongarg = va_arg(ap, long long);
-			break;
-		case T_U_LLONG:
-			(*argtable)[n].ulonglongarg = va_arg(ap, unsigned long long);
-			break;
-		case TP_LLONG:
-			(*argtable)[n].plonglongarg = va_arg(ap, long long *);
-			break;
-#ifdef FLOATING_POINT
-		case T_DOUBLE:
-			(*argtable)[n].doublearg = va_arg(ap, double);
-			break;
-		case T_LONG_DOUBLE:
-			(*argtable)[n].longdoublearg = va_arg(ap, long double);
-			break;
-#endif
-		case TP_CHAR:
-			(*argtable)[n].pchararg = va_arg(ap, char *);
-			break;
-		case TP_VOID:
-			(*argtable)[n].pvoidarg = va_arg(ap, void *);
-			break;
-		case T_PTRINT:
-			(*argtable)[n].ptrdiffarg = va_arg(ap, ptrdiff_t);
-			break;
-		case TP_PTRINT:
-			(*argtable)[n].pptrdiffarg = va_arg(ap, ptrdiff_t *);
-			break;
-		case T_SIZEINT:
-			(*argtable)[n].sizearg = va_arg(ap, size_t);
-			break;
-		case T_SSIZEINT:
-			(*argtable)[n].ssizearg = va_arg(ap, ssize_t);
-			break;
-		case TP_SSIZEINT:
-			(*argtable)[n].pssizearg = va_arg(ap, ssize_t *);
-			break;
-		case T_MAXINT:
-			(*argtable)[n].intmaxarg = va_arg(ap, intmax_t);
-			break;
-		case T_MAXUINT:
-			(*argtable)[n].uintmaxarg = va_arg(ap, uintmax_t);
-			break;
-		case TP_MAXINT:
-			(*argtable)[n].pintmaxarg = va_arg(ap, intmax_t *);
-			break;
-#ifdef PRINTF_WIDE_CHAR
-		case T_WINT:
-			(*argtable)[n].wintarg = va_arg(ap, wint_t);
-			break;
-		case TP_WCHAR:
-			(*argtable)[n].pwchararg = va_arg(ap, wchar_t *);
-			break;
-#endif
-		}
-	}
-	goto finish;
-
-overflow:
-	errno = ENOMEM;
-	ret = -1;
-
-finish:
-	if (typetable != NULL && typetable != stattypetable) {
-		munmap(typetable, *argtablesiz);
-		typetable = NULL;
-	}
-	return (ret);
-}
-
-/*
- * Increase the size of the type table.
- */
-static int
-__grow_type_table(unsigned char **typetable, int *tablesize)
-{
-	unsigned char *oldtable = *typetable;
-	int newsize = *tablesize * 2;
-
-	if (newsize < getpagesize())
-		newsize = getpagesize();
-
-	if (*tablesize == STATIC_ARG_TBL_SIZE) {
-		*typetable = mmap(NULL, newsize, PROT_WRITE|PROT_READ,
-		    MAP_ANON|MAP_PRIVATE, -1, 0);
-		if (*typetable == MAP_FAILED)
-			return (-1);
-		bcopy(oldtable, *typetable, *tablesize);
-	} else {
-		unsigned char *new = mmap(NULL, newsize, PROT_WRITE|PROT_READ,
-		    MAP_ANON|MAP_PRIVATE, -1, 0);
-		if (new == MAP_FAILED)
-			return (-1);
-		memmove(new, *typetable, *tablesize);
-		munmap(*typetable, *tablesize);
-		*typetable = new;
-	}
-	memset(*typetable + *tablesize, T_UNUSED, (newsize - *tablesize));
-
-	*tablesize = newsize;
-	return (0);
-}
-
- 
-#ifdef FLOATING_POINT
-static int
-exponent(char *p0, int exp, int fmtch)
-{
-	char *p, *t;
-	char expbuf[MAXEXPDIG];
-
-	p = p0;
-	*p++ = fmtch;
-	if (exp < 0) {
-		exp = -exp;
-		*p++ = '-';
-	} else
-		*p++ = '+';
-	t = expbuf + MAXEXPDIG;
-	if (exp > 9) {
-		do {
-			*--t = to_char(exp % 10);
-		} while ((exp /= 10) > 9);
-		*--t = to_char(exp);
-		for (; t < expbuf + MAXEXPDIG; *p++ = *t++)
-			/* nothing */;
-	} else {
-		/*
-		 * Exponents for decimal floating point conversions
-		 * (%[eEgG]) must be at least two characters long,
-		 * whereas exponents for hexadecimal conversions can
-		 * be only one character long.
-		 */
-		if (fmtch == 'e' || fmtch == 'E')
-			*p++ = '0';
-		*p++ = to_char(exp);
-	}
-	return (p - p0);
-}
-#endif /* FLOATING_POINT */
diff --git a/libc/upstream-openbsd/lib/libc/stdio/vfwprintf.c b/libc/upstream-openbsd/lib/libc/stdio/vfwprintf.c
deleted file mode 100644
index 520c8bc..0000000
--- a/libc/upstream-openbsd/lib/libc/stdio/vfwprintf.c
+++ /dev/null
@@ -1,1510 +0,0 @@
-/*	$OpenBSD: vfwprintf.c,v 1.15 2015/12/28 22:08:18 mmcc Exp $ */
-/*-
- * Copyright (c) 1990 The Regents of the University of California.
- * All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Chris Torek.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. 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.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
- */
-
-/*
- * Actual wprintf innards.
- *
- * This code is large and complicated...
- */
-
-#include <sys/types.h>
-#include <sys/mman.h>
-
-#include <errno.h>
-#include <langinfo.h>
-#include <limits.h>
-#include <stdarg.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "local.h"
-#include "fvwrite.h"
-
-union arg {
-	int			intarg;
-	unsigned int		uintarg;
-	long			longarg;
-	unsigned long		ulongarg;
-	long long		longlongarg;
-	unsigned long long	ulonglongarg;
-	ptrdiff_t		ptrdiffarg;
-	size_t			sizearg;
-	ssize_t			ssizearg;
-	intmax_t		intmaxarg;
-	uintmax_t		uintmaxarg;
-	void			*pvoidarg;
-	char			*pchararg;
-	signed char		*pschararg;
-	short			*pshortarg;
-	int			*pintarg;
-	long			*plongarg;
-	long long		*plonglongarg;
-	ptrdiff_t		*pptrdiffarg;
-	ssize_t			*pssizearg;
-	intmax_t		*pintmaxarg;
-#ifdef FLOATING_POINT
-	double			doublearg;
-	long double		longdoublearg;
-#endif
-	wint_t			wintarg;
-	wchar_t			*pwchararg;
-};
-
-static int __find_arguments(const wchar_t *fmt0, va_list ap, union arg **argtable,
-    size_t *argtablesiz);
-static int __grow_type_table(unsigned char **typetable, int *tablesize);
-
-/*
- * Helper function for `fprintf to unbuffered unix file': creates a
- * temporary buffer.  We only work on write-only files; this avoids
- * worries about ungetc buffers and so forth.
- */
-static int
-__sbprintf(FILE *fp, const wchar_t *fmt, va_list ap)
-{
-	int ret;
-	FILE fake;
-	struct __sfileext fakeext;
-	unsigned char buf[BUFSIZ];
-
-	_FILEEXT_SETUP(&fake, &fakeext);
-	/* copy the important variables */
-	fake._flags = fp->_flags & ~__SNBF;
-	fake._file = fp->_file;
-	fake._cookie = fp->_cookie;
-	fake._write = fp->_write;
-
-	/* set up the buffer */
-	fake._bf._base = fake._p = buf;
-	fake._bf._size = fake._w = sizeof(buf);
-	fake._lbfsize = 0;	/* not actually used, but Just In Case */
-
-	/* do the work, then copy any error status */
-	ret = __vfwprintf(&fake, fmt, ap);
-	if (ret >= 0 && __sflush(&fake))
-		ret = EOF;
-	if (fake._flags & __SERR)
-		fp->_flags |= __SERR;
-	return (ret);
-}
-
-/*
- * Like __fputwc_unlock, but handles fake string (__SSTR) files properly.
- * File must already be locked.
- */
-static wint_t
-__xfputwc(wchar_t wc, FILE *fp)
-{
-	mbstate_t mbs;
-	char buf[MB_LEN_MAX];
-	struct __suio uio;
-	struct __siov iov;
-	size_t len;
-
-	if ((fp->_flags & __SSTR) == 0)
-		return (__fputwc_unlock(wc, fp));
-
-	bzero(&mbs, sizeof(mbs));
-	len = wcrtomb(buf, wc, &mbs);
-	if (len == (size_t)-1) {
-		fp->_flags |= __SERR;
-		errno = EILSEQ;
-		return (WEOF);
-	}
-	uio.uio_iov = &iov;
-	uio.uio_resid = len;
-	uio.uio_iovcnt = 1;
-	iov.iov_base = buf;
-	iov.iov_len = len;
-	return (__sfvwrite(fp, &uio) != EOF ? (wint_t)wc : WEOF);
-}
-
-/*
- * Convert a multibyte character string argument for the %s format to a wide
- * string representation. ``prec'' specifies the maximum number of bytes
- * to output. If ``prec'' is greater than or equal to zero, we can't assume
- * that the multibyte character string ends in a null character.
- * 
- * Returns NULL on failure.
- * To find out what happened check errno for ENOMEM, EILSEQ and EINVAL.
- */
-static wchar_t *
-__mbsconv(char *mbsarg, int prec)
-{
-	mbstate_t mbs;
-	wchar_t *convbuf, *wcp;
-	const char *p;
-	size_t insize, nchars, nconv;
-
-	if (mbsarg == NULL)
-		return (NULL);
-
-	/*
-	 * Supplied argument is a multibyte string; convert it to wide
-	 * characters first.
-	 */
-	if (prec >= 0) {
-		/*
-		 * String is not guaranteed to be NUL-terminated. Find the
-		 * number of characters to print.
-		 */
-		p = mbsarg;
-		insize = nchars = nconv = 0;
-		bzero(&mbs, sizeof(mbs));
-		while (nchars != (size_t)prec) {
-			nconv = mbrlen(p, MB_CUR_MAX, &mbs);
-			if (nconv == (size_t)0 || nconv == (size_t)-1 ||
-			    nconv == (size_t)-2)
-				break;
-			p += nconv;
-			nchars++;
-			insize += nconv;
-		}
-		if (nconv == (size_t)-1 || nconv == (size_t)-2)
-			return (NULL);
-	} else
-		insize = strlen(mbsarg);
-
-	/*
-	 * Allocate buffer for the result and perform the conversion,
-	 * converting at most `size' bytes of the input multibyte string to
-	 * wide characters for printing.
-	 */
-	convbuf = calloc(insize + 1, sizeof(*convbuf));
-	if (convbuf == NULL)
-		return (NULL);
-	wcp = convbuf;
-	p = mbsarg;
-	bzero(&mbs, sizeof(mbs));
-	nconv = 0;
-	while (insize != 0) {
-		nconv = mbrtowc(wcp, p, insize, &mbs);
-		if (nconv == 0 || nconv == (size_t)-1 || nconv == (size_t)-2)
-			break;
-		wcp++;
-		p += nconv;
-		insize -= nconv;
-	}
-	if (nconv == (size_t)-1 || nconv == (size_t)-2) {
-		free(convbuf);
-		return (NULL);
-	}
-	*wcp = '\0';
-
-	return (convbuf);
-}
-
-#ifdef FLOATING_POINT
-#include <float.h>
-#include <locale.h>
-#include <math.h>
-#include "floatio.h"
-#include "gdtoa.h"
-
-#define	DEFPREC		6
-
-static int exponent(wchar_t *, int, int);
-#endif /* FLOATING_POINT */
-
-/*
- * The size of the buffer we use as scratch space for integer
- * conversions, among other things.  Technically, we would need the
- * most space for base 10 conversions with thousands' grouping
- * characters between each pair of digits.  100 bytes is a
- * conservative overestimate even for a 128-bit uintmax_t.
- */
-#define BUF	100
-
-#define STATIC_ARG_TBL_SIZE 8	/* Size of static argument table. */
-
-
-/*
- * Macros for converting digits to letters and vice versa
- */
-#define	to_digit(c)	((c) - '0')
-#define is_digit(c)	((unsigned)to_digit(c) <= 9)
-#define	to_char(n)	((wchar_t)((n) + '0'))
-
-/*
- * Flags used during conversion.
- */
-#define	ALT		0x0001		/* alternate form */
-#define	LADJUST		0x0004		/* left adjustment */
-#define	LONGDBL		0x0008		/* long double */
-#define	LONGINT		0x0010		/* long integer */
-#define	LLONGINT	0x0020		/* long long integer */
-#define	SHORTINT	0x0040		/* short integer */
-#define	ZEROPAD		0x0080		/* zero (as opposed to blank) pad */
-#define FPT		0x0100		/* Floating point number */
-#define PTRINT		0x0200		/* (unsigned) ptrdiff_t */
-#define SIZEINT		0x0400		/* (signed) size_t */
-#define CHARINT		0x0800		/* 8 bit integer */
-#define MAXINT		0x1000		/* largest integer size (intmax_t) */
-
-int
-__vfwprintf(FILE * __restrict fp, const wchar_t * __restrict fmt0, __va_list ap)
-{
-	wchar_t *fmt;		/* format string */
-	wchar_t ch;		/* character from fmt */
-	int n, n2, n3;		/* handy integers (short term usage) */
-	wchar_t *cp;		/* handy char pointer (short term usage) */
-	int flags;		/* flags as above */
-	int ret;		/* return value accumulator */
-	int width;		/* width from format (%8d), or 0 */
-	int prec;		/* precision from format; <0 for N/A */
-	wchar_t sign;		/* sign prefix (' ', '+', '-', or \0) */
-#ifdef FLOATING_POINT
-	/*
-	 * We can decompose the printed representation of floating
-	 * point numbers into several parts, some of which may be empty:
-	 *
-	 * [+|-| ] [0x|0X] MMM . NNN [e|E|p|P] [+|-] ZZ
-	 *    A       B     ---C---      D       E   F
-	 *
-	 * A:	'sign' holds this value if present; '\0' otherwise
-	 * B:	ox[1] holds the 'x' or 'X'; '\0' if not hexadecimal
-	 * C:	cp points to the string MMMNNN.  Leading and trailing
-	 *	zeros are not in the string and must be added.
-	 * D:	expchar holds this character; '\0' if no exponent, e.g. %f
-	 * F:	at least two digits for decimal, at least one digit for hex
-	 */
-	char *decimal_point = NULL;
-	int signflag;		/* true if float is negative */
-	union {			/* floating point arguments %[aAeEfFgG] */
-		double dbl;
-		long double ldbl;
-	} fparg;
-	int expt;		/* integer value of exponent */
-	char expchar;		/* exponent character: [eEpP\0] */
-	char *dtoaend;		/* pointer to end of converted digits */
-	int expsize;		/* character count for expstr */
-	int lead;		/* sig figs before decimal or group sep */
-	int ndig;		/* actual number of digits returned by dtoa */
-	wchar_t expstr[MAXEXPDIG+2];	/* buffer for exponent string: e+ZZZ */
-	char *dtoaresult = NULL;
-#endif
-
-	uintmax_t _umax;	/* integer arguments %[diouxX] */
-	enum { OCT, DEC, HEX } base;	/* base for %[diouxX] conversion */
-	int dprec;		/* a copy of prec if %[diouxX], 0 otherwise */
-	int realsz;		/* field size expanded by dprec */
-	int size;		/* size of converted field or string */
-	const char *xdigs;	/* digits for %[xX] conversion */
-	wchar_t buf[BUF];	/* buffer with space for digits of uintmax_t */
-	wchar_t ox[2];		/* space for 0x; ox[1] is either x, X, or \0 */
-	union arg *argtable;	/* args, built due to positional arg */
-	union arg statargtable[STATIC_ARG_TBL_SIZE];
-	size_t argtablesiz;
-	int nextarg;		/* 1-based argument index */
-	va_list orgap;		/* original argument pointer */
-	wchar_t *convbuf;	/* buffer for multibyte to wide conversion */
-
-	/*
-	 * Choose PADSIZE to trade efficiency vs. size.  If larger printf
-	 * fields occur frequently, increase PADSIZE and make the initialisers
-	 * below longer.
-	 */
-#define	PADSIZE	16		/* pad chunk size */
-	static wchar_t blanks[PADSIZE] =
-	 {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
-	static wchar_t zeroes[PADSIZE] =
-	 {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
-
-	static const char xdigs_lower[16] = "0123456789abcdef";
-	static const char xdigs_upper[16] = "0123456789ABCDEF";
-
-	/*
-	 * BEWARE, these `goto error' on error, PRINT uses 'n3',
-	 * PAD uses `n' and 'n3', and PRINTANDPAD uses 'n', 'n2', and 'n3'.
-	 */
-#define	PRINT(ptr, len)	do {	\
-	for (n3 = 0; n3 < (len); n3++) {	\
-		if ((__xfputwc((ptr)[n3], fp)) == WEOF)	\
-			goto error; \
-	} \
-} while (0)
-#define	PAD(howmany, with) do { \
-	if ((n = (howmany)) > 0) { \
-		while (n > PADSIZE) { \
-			PRINT(with, PADSIZE); \
-			n -= PADSIZE; \
-		} \
-		PRINT(with, n); \
-	} \
-} while (0)
-#define	PRINTANDPAD(p, ep, len, with) do {	\
-	n2 = (ep) - (p);       			\
-	if (n2 > (len))				\
-		n2 = (len);			\
-	if (n2 > 0)				\
-		PRINT((p), n2);			\
-	PAD((len) - (n2 > 0 ? n2 : 0), (with));	\
-} while(0)
-
-	/*
-	 * To extend shorts properly, we need both signed and unsigned
-	 * argument extraction methods.
-	 */
-#define	SARG() \
-	((intmax_t)(flags&MAXINT ? GETARG(intmax_t) : \
-	    flags&LLONGINT ? GETARG(long long) : \
-	    flags&LONGINT ? GETARG(long) : \
-	    flags&PTRINT ? GETARG(ptrdiff_t) : \
-	    flags&SIZEINT ? GETARG(ssize_t) : \
-	    flags&SHORTINT ? (short)GETARG(int) : \
-	    flags&CHARINT ? (signed char)GETARG(int) : \
-	    GETARG(int)))
-#define	UARG() \
-	((uintmax_t)(flags&MAXINT ? GETARG(uintmax_t) : \
-	    flags&LLONGINT ? GETARG(unsigned long long) : \
-	    flags&LONGINT ? GETARG(unsigned long) : \
-	    flags&PTRINT ? (uintptr_t)GETARG(ptrdiff_t) : /* XXX */ \
-	    flags&SIZEINT ? GETARG(size_t) : \
-	    flags&SHORTINT ? (unsigned short)GETARG(int) : \
-	    flags&CHARINT ? (unsigned char)GETARG(int) : \
-	    GETARG(unsigned int)))
-
-	/*
-	 * Append a digit to a value and check for overflow.
-	 */
-#define APPEND_DIGIT(val, dig) do { \
-	if ((val) > INT_MAX / 10) \
-		goto overflow; \
-	(val) *= 10; \
-	if ((val) > INT_MAX - to_digit((dig))) \
-		goto overflow; \
-	(val) += to_digit((dig)); \
-} while (0)
-
-	 /*
-	  * Get * arguments, including the form *nn$.  Preserve the nextarg
-	  * that the argument can be gotten once the type is determined.
-	  */
-#define GETASTER(val) \
-	n2 = 0; \
-	cp = fmt; \
-	while (is_digit(*cp)) { \
-		APPEND_DIGIT(n2, *cp); \
-		cp++; \
-	} \
-	if (*cp == '$') { \
-		int hold = nextarg; \
-		if (argtable == NULL) { \
-			argtable = statargtable; \
-			__find_arguments(fmt0, orgap, &argtable, &argtablesiz); \
-		} \
-		nextarg = n2; \
-		val = GETARG(int); \
-		nextarg = hold; \
-		fmt = ++cp; \
-	} else { \
-		val = GETARG(int); \
-	}
-
-/*
-* Get the argument indexed by nextarg.   If the argument table is
-* built, use it to get the argument.  If its not, get the next
-* argument (and arguments must be gotten sequentially).
-*/
-#define GETARG(type) \
-	((argtable != NULL) ? *((type*)(&argtable[nextarg++])) : \
-		(nextarg++, va_arg(ap, type)))
-
-	_SET_ORIENTATION(fp, 1);
-	/* sorry, fwprintf(read_only_file, "") returns EOF, not 0 */
-	if (cantwrite(fp)) {
-		errno = EBADF;
-		return (EOF);
-	}
-
-	/* optimise fwprintf(stderr) (and other unbuffered Unix files) */
-	if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) &&
-	    fp->_file >= 0)
-		return (__sbprintf(fp, fmt0, ap));
-
-	fmt = (wchar_t *)fmt0;
-	argtable = NULL;
-	nextarg = 1;
-	va_copy(orgap, ap);
-	ret = 0;
-	convbuf = NULL;
-
-	/*
-	 * Scan the format for conversions (`%' character).
-	 */
-	for (;;) {
-		for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
-			continue;
-		if (fmt != cp) {
-			ptrdiff_t m = fmt - cp;
-			if (m < 0 || m > INT_MAX - ret)
-				goto overflow;
-			PRINT(cp, m);
-			ret += m;
-		}
-		if (ch == '\0')
-			goto done;
-		fmt++;		/* skip over '%' */
-
-		flags = 0;
-		dprec = 0;
-		width = 0;
-		prec = -1;
-		sign = '\0';
-		ox[1] = '\0';
-
-rflag:		ch = *fmt++;
-reswitch:	switch (ch) {
-		case ' ':
-			/*
-			 * ``If the space and + flags both appear, the space
-			 * flag will be ignored.''
-			 *	-- ANSI X3J11
-			 */
-			if (!sign)
-				sign = ' ';
-			goto rflag;
-		case '#':
-			flags |= ALT;
-			goto rflag;
-		case '\'':
-			/* grouping not implemented */
-			goto rflag;
-		case '*':
-			/*
-			 * ``A negative field width argument is taken as a
-			 * - flag followed by a positive field width.''
-			 *	-- ANSI X3J11
-			 * They don't exclude field widths read from args.
-			 */
-			GETASTER(width);
-			if (width >= 0)
-				goto rflag;
-			if (width == INT_MIN)
-				goto overflow;
-			width = -width;
-			/* FALLTHROUGH */
-		case '-':
-			flags |= LADJUST;
-			goto rflag;
-		case '+':
-			sign = '+';
-			goto rflag;
-		case '.':
-			if ((ch = *fmt++) == '*') {
-				GETASTER(n);
-				prec = n < 0 ? -1 : n;
-				goto rflag;
-			}
-			n = 0;
-			while (is_digit(ch)) {
-				APPEND_DIGIT(n, ch);
-				ch = *fmt++;
-			}
-			if (ch == '$') {
-				nextarg = n;
-				if (argtable == NULL) {
-					argtable = statargtable;
-					__find_arguments(fmt0, orgap,
-					    &argtable, &argtablesiz);
-				}
-				goto rflag;
-			}
-			prec = n;
-			goto reswitch;
-		case '0':
-			/*
-			 * ``Note that 0 is taken as a flag, not as the
-			 * beginning of a field width.''
-			 *	-- ANSI X3J11
-			 */
-			flags |= ZEROPAD;
-			goto rflag;
-		case '1': case '2': case '3': case '4':
-		case '5': case '6': case '7': case '8': case '9':
-			n = 0;
-			do {
-				APPEND_DIGIT(n, ch);
-				ch = *fmt++;
-			} while (is_digit(ch));
-			if (ch == '$') {
-				nextarg = n;
-				if (argtable == NULL) {
-					argtable = statargtable;
-					__find_arguments(fmt0, orgap,
-					    &argtable, &argtablesiz);
-				}
-				goto rflag;
-			}
-			width = n;
-			goto reswitch;
-#ifdef FLOATING_POINT
-		case 'L':
-			flags |= LONGDBL;
-			goto rflag;
-#endif
-		case 'h':
-			if (*fmt == 'h') {
-				fmt++;
-				flags |= CHARINT;
-			} else {
-				flags |= SHORTINT;
-			}
-			goto rflag;
-		case 'j':
-			flags |= MAXINT;
-			goto rflag;
-		case 'l':
-			if (*fmt == 'l') {
-				fmt++;
-				flags |= LLONGINT;
-			} else {
-				flags |= LONGINT;
-			}
-			goto rflag;
-		case 'q':
-			flags |= LLONGINT;
-			goto rflag;
-		case 't':
-			flags |= PTRINT;
-			goto rflag;
-		case 'z':
-			flags |= SIZEINT;
-			goto rflag;
-		case 'C':
-			flags |= LONGINT;
-			/*FALLTHROUGH*/
-		case 'c':
-			if (flags & LONGINT)
-				*(cp = buf) = (wchar_t)GETARG(wint_t);
-			else
-				*(cp = buf) = (wchar_t)btowc(GETARG(int));
-			size = 1;
-			sign = '\0';
-			break;
-		case 'D':
-			flags |= LONGINT;
-			/*FALLTHROUGH*/
-		case 'd':
-		case 'i':
-			_umax = SARG();
-			if ((intmax_t)_umax < 0) {
-				_umax = -_umax;
-				sign = '-';
-			}
-			base = DEC;
-			goto number;
-#ifdef FLOATING_POINT
-		case 'a':
-		case 'A':
-			if (ch == 'a') {
-				ox[1] = 'x';
-				xdigs = xdigs_lower;
-				expchar = 'p';
-			} else {
-				ox[1] = 'X';
-				xdigs = xdigs_upper;
-				expchar = 'P';
-			}
-			if (prec >= 0)
-				prec++;
-			if (dtoaresult)
-				__freedtoa(dtoaresult);
-			if (flags & LONGDBL) {
-				fparg.ldbl = GETARG(long double);
-				dtoaresult =
-				    __hldtoa(fparg.ldbl, xdigs, prec,
-				    &expt, &signflag, &dtoaend);
-				if (dtoaresult == NULL) {
-					errno = ENOMEM;
-					goto error;
-				}
-			} else {
-				fparg.dbl = GETARG(double);
-				dtoaresult =
-				    __hdtoa(fparg.dbl, xdigs, prec,
-				    &expt, &signflag, &dtoaend);
-				if (dtoaresult == NULL) {
-					errno = ENOMEM;
-					goto error;
-				}
-			}
-			if (prec < 0)
-				prec = dtoaend - dtoaresult;
-			if (expt == INT_MAX)
-				ox[1] = '\0';
-			free(convbuf);
-			cp = convbuf = __mbsconv(dtoaresult, -1);
-			if (cp == NULL)
-				goto error;
-			ndig = dtoaend - dtoaresult;
-			goto fp_common;
-		case 'e':
-		case 'E':
-			expchar = ch;
-			if (prec < 0)	/* account for digit before decpt */
-				prec = DEFPREC + 1;
-			else
-				prec++;
-			goto fp_begin;
-		case 'f':
-		case 'F':
-			expchar = '\0';
-			goto fp_begin;
-		case 'g':
-		case 'G':
-			expchar = ch - ('g' - 'e');
- 			if (prec == 0)
- 				prec = 1;
-fp_begin:
-			if (prec < 0)
-				prec = DEFPREC;
-			if (dtoaresult)
-				__freedtoa(dtoaresult);
-			if (flags & LONGDBL) {
-				fparg.ldbl = GETARG(long double);
-				dtoaresult =
-				    __ldtoa(&fparg.ldbl, expchar ? 2 : 3, prec,
-				    &expt, &signflag, &dtoaend);
-				if (dtoaresult == NULL) {
-					errno = ENOMEM;
-					goto error;
-				}
-			} else {
-				fparg.dbl = GETARG(double);
-				dtoaresult =
-				    __dtoa(fparg.dbl, expchar ? 2 : 3, prec,
-				    &expt, &signflag, &dtoaend);
-				if (dtoaresult == NULL) {
-					errno = ENOMEM;
-					goto error;
-				}
-				if (expt == 9999)
-					expt = INT_MAX;
- 			}
-			free(convbuf);
-			cp = convbuf = __mbsconv(dtoaresult, -1);
-			if (cp == NULL)
-				goto error;
-			ndig = dtoaend - dtoaresult;
-fp_common:
-			if (signflag)
-				sign = '-';
-			if (expt == INT_MAX) {	/* inf or nan */
-				if (*cp == 'N')
-					cp = (ch >= 'a') ? L"nan" : L"NAN";
-				else
-					cp = (ch >= 'a') ? L"inf" : L"INF";
- 				size = 3;
-				flags &= ~ZEROPAD;
- 				break;
- 			}
-			flags |= FPT;
- 			if (ch == 'g' || ch == 'G') {
-				if (expt > -4 && expt <= prec) {
-					/* Make %[gG] smell like %[fF] */
-					expchar = '\0';
-					if (flags & ALT)
-						prec -= expt;
-					else
-						prec = ndig - expt;
-					if (prec < 0)
-						prec = 0;
-				} else {
-					/*
-					 * Make %[gG] smell like %[eE], but
-					 * trim trailing zeroes if no # flag.
-					 */
-					if (!(flags & ALT))
-						prec = ndig;
-				}
- 			}
-			if (expchar) {
-				expsize = exponent(expstr, expt - 1, expchar);
-				size = expsize + prec;
-				if (prec > 1 || flags & ALT)
- 					++size;
-			} else {
-				/* space for digits before decimal point */
-				if (expt > 0)
-					size = expt;
-				else	/* "0" */
-					size = 1;
-				/* space for decimal pt and following digits */
-				if (prec || flags & ALT)
-					size += prec + 1;
-				lead = expt;
-			}
-			break;
-#endif /* FLOATING_POINT */
-#ifndef NO_PRINTF_PERCENT_N
-		case 'n':
-			if (flags & LLONGINT)
-				*GETARG(long long *) = ret;
-			else if (flags & LONGINT)
-				*GETARG(long *) = ret;
-			else if (flags & SHORTINT)
-				*GETARG(short *) = ret;
-			else if (flags & CHARINT)
-				*GETARG(signed char *) = ret;
-			else if (flags & PTRINT)
-				*GETARG(ptrdiff_t *) = ret;
-			else if (flags & SIZEINT)
-				*GETARG(ssize_t *) = ret;
-			else if (flags & MAXINT)
-				*GETARG(intmax_t *) = ret;
-			else
-				*GETARG(int *) = ret;
-			continue;	/* no output */
-#endif /* NO_PRINTF_PERCENT_N */
-		case 'O':
-			flags |= LONGINT;
-			/*FALLTHROUGH*/
-		case 'o':
-			_umax = UARG();
-			base = OCT;
-			goto nosign;
-		case 'p':
-			/*
-			 * ``The argument shall be a pointer to void.  The
-			 * value of the pointer is converted to a sequence
-			 * of printable characters, in an implementation-
-			 * defined manner.''
-			 *	-- ANSI X3J11
-			 */
-			_umax = (u_long)GETARG(void *);
-			base = HEX;
-			xdigs = xdigs_lower;
-			ox[1] = 'x';
-			goto nosign;
-		case 'S':
-			flags |= LONGINT;
-			/*FALLTHROUGH*/
-		case 's':
-			if (flags & LONGINT) {
-				if ((cp = GETARG(wchar_t *)) == NULL)
-					cp = L"(null)";
-			} else {
-				char *mbsarg;
-				if ((mbsarg = GETARG(char *)) == NULL)
-					mbsarg = "(null)";
-				free(convbuf);
-				convbuf = __mbsconv(mbsarg, prec);
-				if (convbuf == NULL) {
-					fp->_flags |= __SERR;
-					goto error;
-				} else
-					cp = convbuf;
-			}
-			if (prec >= 0) {
-				/*
-				 * can't use wcslen; can only look for the
-				 * NUL in the first `prec' characters, and
-				 * wcslen() will go further.
-				 */
-				wchar_t *p = wmemchr(cp, 0, prec);
-
-				size = p ? (p - cp) : prec;
-			} else {
-				size_t len;
-
-				if ((len = wcslen(cp)) > INT_MAX)
-					goto overflow;
-				size = (int)len;
-			}
-			sign = '\0';
-			break;
-		case 'U':
-			flags |= LONGINT;
-			/*FALLTHROUGH*/
-		case 'u':
-			_umax = UARG();
-			base = DEC;
-			goto nosign;
-		case 'X':
-			xdigs = xdigs_upper;
-			goto hex;
-		case 'x':
-			xdigs = xdigs_lower;
-hex:			_umax = UARG();
-			base = HEX;
-			/* leading 0x/X only if non-zero */
-			if (flags & ALT && _umax != 0)
-				ox[1] = ch;
-
-			/* unsigned conversions */
-nosign:			sign = '\0';
-			/*
-			 * ``... diouXx conversions ... if a precision is
-			 * specified, the 0 flag will be ignored.''
-			 *	-- ANSI X3J11
-			 */
-number:			if ((dprec = prec) >= 0)
-				flags &= ~ZEROPAD;
-
-			/*
-			 * ``The result of converting a zero value with an
-			 * explicit precision of zero is no characters.''
-			 *	-- ANSI X3J11
-			 */
-			cp = buf + BUF;
-			if (_umax != 0 || prec != 0) {
-				/*
-				 * Unsigned mod is hard, and unsigned mod
-				 * by a constant is easier than that by
-				 * a variable; hence this switch.
-				 */
-				switch (base) {
-				case OCT:
-					do {
-						*--cp = to_char(_umax & 7);
-						_umax >>= 3;
-					} while (_umax);
-					/* handle octal leading 0 */
-					if (flags & ALT && *cp != '0')
-						*--cp = '0';
-					break;
-
-				case DEC:
-					/* many numbers are 1 digit */
-					while (_umax >= 10) {
-						*--cp = to_char(_umax % 10);
-						_umax /= 10;
-					}
-					*--cp = to_char(_umax);
-					break;
-
-				case HEX:
-					do {
-						*--cp = xdigs[_umax & 15];
-						_umax >>= 4;
-					} while (_umax);
-					break;
-
-				default:
-					cp = L"bug in vfwprintf: bad base";
-					size = wcslen(cp);
-					goto skipsize;
-				}
-			}
-			size = buf + BUF - cp;
-			if (size > BUF)	/* should never happen */
-				abort();
-		skipsize:
-			break;
-		default:	/* "%?" prints ?, unless ? is NUL */
-			if (ch == '\0')
-				goto done;
-			/* pretend it was %c with argument ch */
-			cp = buf;
-			*cp = ch;
-			size = 1;
-			sign = '\0';
-			break;
-		}
-
-		/*
-		 * All reasonable formats wind up here.  At this point, `cp'
-		 * points to a string which (if not flags&LADJUST) should be
-		 * padded out to `width' places.  If flags&ZEROPAD, it should
-		 * first be prefixed by any sign or other prefix; otherwise,
-		 * it should be blank padded before the prefix is emitted.
-		 * After any left-hand padding and prefixing, emit zeroes
-		 * required by a decimal %[diouxX] precision, then print the
-		 * string proper, then emit zeroes required by any leftover
-		 * floating precision; finally, if LADJUST, pad with blanks.
-		 *
-		 * Compute actual size, so we know how much to pad.
-		 * size excludes decimal prec; realsz includes it.
-		 */
-		realsz = dprec > size ? dprec : size;
-		if (sign)
-			realsz++;
-		if (ox[1])
-			realsz+= 2;
-
-		/* right-adjusting blank padding */
-		if ((flags & (LADJUST|ZEROPAD)) == 0)
-			PAD(width - realsz, blanks);
-
-		/* prefix */
-		if (sign)
-			PRINT(&sign, 1);
-		if (ox[1]) {	/* ox[1] is either x, X, or \0 */
-			ox[0] = '0';
-			PRINT(ox, 2);
-		}
-
-		/* right-adjusting zero padding */
-		if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
-			PAD(width - realsz, zeroes);
-
-		/* leading zeroes from decimal precision */
-		PAD(dprec - size, zeroes);
-
-		/* the string or number proper */
-#ifdef FLOATING_POINT
-		if ((flags & FPT) == 0) {
-			PRINT(cp, size);
-		} else {	/* glue together f_p fragments */
-			if (decimal_point == NULL)
-				decimal_point = nl_langinfo(RADIXCHAR);
-			if (!expchar) {	/* %[fF] or sufficiently short %[gG] */
-				if (expt <= 0) {
-					PRINT(zeroes, 1);
-					if (prec || flags & ALT)
-						PRINT(decimal_point, 1);
-					PAD(-expt, zeroes);
-					/* already handled initial 0's */
-					prec += expt;
- 				} else {
-					PRINTANDPAD(cp, convbuf + ndig,
-					    lead, zeroes);
-					cp += lead;
-					if (prec || flags & ALT)
-						PRINT(decimal_point, 1);
-				}
-				PRINTANDPAD(cp, convbuf + ndig, prec, zeroes);
-			} else {	/* %[eE] or sufficiently long %[gG] */
-				if (prec > 1 || flags & ALT) {
-					buf[0] = *cp++;
-					buf[1] = *decimal_point;
-					PRINT(buf, 2);
-					PRINT(cp, ndig-1);
-					PAD(prec - ndig, zeroes);
-				} else { /* XeYYY */
-					PRINT(cp, 1);
-				}
-				PRINT(expstr, expsize);
-			}
-		}
-#else
-		PRINT(cp, size);
-#endif
-		/* left-adjusting padding (always blank) */
-		if (flags & LADJUST)
-			PAD(width - realsz, blanks);
-
-		/* finally, adjust ret */
-		if (width < realsz)
-			width = realsz;
-		if (width > INT_MAX - ret)
-			goto overflow;
-		ret += width;
-	}
-done:
-error:
-	va_end(orgap);
-	if (__sferror(fp))
-		ret = -1;
-	goto finish;
-
-overflow:
-	errno = ENOMEM;
-	ret = -1;
-
-finish:
-	free(convbuf);
-#ifdef FLOATING_POINT
-	if (dtoaresult)
-		__freedtoa(dtoaresult);
-#endif
-	if (argtable != NULL && argtable != statargtable) {
-		munmap(argtable, argtablesiz);
-		argtable = NULL;
-	}
-	return (ret);
-}
-
-int
-vfwprintf(FILE * __restrict fp, const wchar_t * __restrict fmt0, __va_list ap)
-{
-	int r;
-
-	FLOCKFILE(fp);
-	r = __vfwprintf(fp, fmt0, ap);
-	FUNLOCKFILE(fp);
-
-	return (r);
-}
-DEF_STRONG(vfwprintf);
-
-/*
- * Type ids for argument type table.
- */
-#define T_UNUSED	0
-#define T_SHORT		1
-#define T_U_SHORT	2
-#define TP_SHORT	3
-#define T_INT		4
-#define T_U_INT		5
-#define TP_INT		6
-#define T_LONG		7
-#define T_U_LONG	8
-#define TP_LONG		9
-#define T_LLONG		10
-#define T_U_LLONG	11
-#define TP_LLONG	12
-#define T_DOUBLE	13
-#define T_LONG_DOUBLE	14
-#define TP_CHAR		15
-#define TP_VOID		16
-#define T_PTRINT	17
-#define TP_PTRINT	18
-#define T_SIZEINT	19
-#define T_SSIZEINT	20
-#define TP_SSIZEINT	21
-#define T_MAXINT	22
-#define T_MAXUINT	23
-#define TP_MAXINT	24
-#define T_CHAR		25
-#define T_U_CHAR	26
-#define T_WINT		27
-#define TP_WCHAR	28
-
-/*
- * Find all arguments when a positional parameter is encountered.  Returns a
- * table, indexed by argument number, of pointers to each arguments.  The
- * initial argument table should be an array of STATIC_ARG_TBL_SIZE entries.
- * It will be replaced with a mmap-ed one if it overflows (malloc cannot be
- * used since we are attempting to make snprintf thread safe, and alloca is
- * problematic since we have nested functions..)
- */
-static int
-__find_arguments(const wchar_t *fmt0, va_list ap, union arg **argtable,
-    size_t *argtablesiz)
-{
-	wchar_t *fmt;		/* format string */
-	int ch;			/* character from fmt */
-	int n, n2;		/* handy integer (short term usage) */
-	wchar_t *cp;		/* handy char pointer (short term usage) */
-	int flags;		/* flags as above */
-	unsigned char *typetable; /* table of types */
-	unsigned char stattypetable[STATIC_ARG_TBL_SIZE];
-	int tablesize;		/* current size of type table */
-	int tablemax;		/* largest used index in table */
-	int nextarg;		/* 1-based argument index */
-	int ret = 0;		/* return value */
-
-	/*
-	 * Add an argument type to the table, expanding if necessary.
-	 */
-#define ADDTYPE(type) \
-	((nextarg >= tablesize) ? \
-		__grow_type_table(&typetable, &tablesize) : 0, \
-	(nextarg > tablemax) ? tablemax = nextarg : 0, \
-	typetable[nextarg++] = type)
-
-#define	ADDSARG() \
-        ((flags&MAXINT) ? ADDTYPE(T_MAXINT) : \
-	    ((flags&PTRINT) ? ADDTYPE(T_PTRINT) : \
-	    ((flags&SIZEINT) ? ADDTYPE(T_SSIZEINT) : \
-	    ((flags&LLONGINT) ? ADDTYPE(T_LLONG) : \
-	    ((flags&LONGINT) ? ADDTYPE(T_LONG) : \
-	    ((flags&SHORTINT) ? ADDTYPE(T_SHORT) : \
-	    ((flags&CHARINT) ? ADDTYPE(T_CHAR) : ADDTYPE(T_INT))))))))
-
-#define	ADDUARG() \
-        ((flags&MAXINT) ? ADDTYPE(T_MAXUINT) : \
-	    ((flags&PTRINT) ? ADDTYPE(T_PTRINT) : \
-	    ((flags&SIZEINT) ? ADDTYPE(T_SIZEINT) : \
-	    ((flags&LLONGINT) ? ADDTYPE(T_U_LLONG) : \
-	    ((flags&LONGINT) ? ADDTYPE(T_U_LONG) : \
-	    ((flags&SHORTINT) ? ADDTYPE(T_U_SHORT) : \
-	    ((flags&CHARINT) ? ADDTYPE(T_U_CHAR) : ADDTYPE(T_U_INT))))))))
-
-	/*
-	 * Add * arguments to the type array.
-	 */
-#define ADDASTER() \
-	n2 = 0; \
-	cp = fmt; \
-	while (is_digit(*cp)) { \
-		APPEND_DIGIT(n2, *cp); \
-		cp++; \
-	} \
-	if (*cp == '$') { \
-		int hold = nextarg; \
-		nextarg = n2; \
-		ADDTYPE(T_INT); \
-		nextarg = hold; \
-		fmt = ++cp; \
-	} else { \
-		ADDTYPE(T_INT); \
-	}
-	fmt = (wchar_t *)fmt0;
-	typetable = stattypetable;
-	tablesize = STATIC_ARG_TBL_SIZE;
-	tablemax = 0;
-	nextarg = 1;
-	memset(typetable, T_UNUSED, STATIC_ARG_TBL_SIZE);
-
-	/*
-	 * Scan the format for conversions (`%' character).
-	 */
-	for (;;) {
-		for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
-			continue;
-		if (ch == '\0')
-			goto done;
-		fmt++;		/* skip over '%' */
-
-		flags = 0;
-
-rflag:		ch = *fmt++;
-reswitch:	switch (ch) {
-		case ' ':
-		case '#':
-		case '\'':
-			goto rflag;
-		case '*':
-			ADDASTER();
-			goto rflag;
-		case '-':
-		case '+':
-			goto rflag;
-		case '.':
-			if ((ch = *fmt++) == '*') {
-				ADDASTER();
-				goto rflag;
-			}
-			while (is_digit(ch)) {
-				ch = *fmt++;
-			}
-			goto reswitch;
-		case '0':
-			goto rflag;
-		case '1': case '2': case '3': case '4':
-		case '5': case '6': case '7': case '8': case '9':
-			n = 0;
-			do {
-				APPEND_DIGIT(n ,ch);
-				ch = *fmt++;
-			} while (is_digit(ch));
-			if (ch == '$') {
-				nextarg = n;
-				goto rflag;
-			}
-			goto reswitch;
-#ifdef FLOATING_POINT
-		case 'L':
-			flags |= LONGDBL;
-			goto rflag;
-#endif
-		case 'h':
-			if (*fmt == 'h') {
-				fmt++;
-				flags |= CHARINT;
-			} else {
-				flags |= SHORTINT;
-			}
-			goto rflag;
-		case 'l':
-			if (*fmt == 'l') {
-				fmt++;
-				flags |= LLONGINT;
-			} else {
-				flags |= LONGINT;
-			}
-			goto rflag;
-		case 'q':
-			flags |= LLONGINT;
-			goto rflag;
-		case 't':
-			flags |= PTRINT;
-			goto rflag;
-		case 'z':
-			flags |= SIZEINT;
-			goto rflag;
-		case 'C':
-			flags |= LONGINT;
-			/*FALLTHROUGH*/
-		case 'c':
-			if (flags & LONGINT)
-				ADDTYPE(T_WINT);
-			else
-				ADDTYPE(T_INT);
-			break;
-		case 'D':
-			flags |= LONGINT;
-			/*FALLTHROUGH*/
-		case 'd':
-		case 'i':
-			ADDSARG();
-			break;
-#ifdef FLOATING_POINT
-		case 'a':
-		case 'A':
-		case 'e':
-		case 'E':
-		case 'f':
-		case 'F':
-		case 'g':
-		case 'G':
-			if (flags & LONGDBL)
-				ADDTYPE(T_LONG_DOUBLE);
-			else
-				ADDTYPE(T_DOUBLE);
-			break;
-#endif /* FLOATING_POINT */
-#ifndef NO_PRINTF_PERCENT_N
-		case 'n':
-			if (flags & LLONGINT)
-				ADDTYPE(TP_LLONG);
-			else if (flags & LONGINT)
-				ADDTYPE(TP_LONG);
-			else if (flags & SHORTINT)
-				ADDTYPE(TP_SHORT);
-			else if (flags & PTRINT)
-				ADDTYPE(TP_PTRINT);
-			else if (flags & SIZEINT)
-				ADDTYPE(TP_SSIZEINT);
-			else if (flags & MAXINT)
-				ADDTYPE(TP_MAXINT);
-			else
-				ADDTYPE(TP_INT);
-			continue;	/* no output */
-#endif /* NO_PRINTF_PERCENT_N */
-		case 'O':
-			flags |= LONGINT;
-			/*FALLTHROUGH*/
-		case 'o':
-			ADDUARG();
-			break;
-		case 'p':
-			ADDTYPE(TP_VOID);
-			break;
-		case 'S':
-			flags |= LONGINT;
-			/*FALLTHROUGH*/
-		case 's':
-			if (flags & LONGINT)
-				ADDTYPE(TP_CHAR);
-			else
-				ADDTYPE(TP_WCHAR);
-			break;
-		case 'U':
-			flags |= LONGINT;
-			/*FALLTHROUGH*/
-		case 'u':
-		case 'X':
-		case 'x':
-			ADDUARG();
-			break;
-		default:	/* "%?" prints ?, unless ? is NUL */
-			if (ch == '\0')
-				goto done;
-			break;
-		}
-	}
-done:
-	/*
-	 * Build the argument table.
-	 */
-	if (tablemax >= STATIC_ARG_TBL_SIZE) {
-		*argtablesiz = sizeof(union arg) * (tablemax + 1);
-		*argtable = mmap(NULL, *argtablesiz,
-		    PROT_WRITE|PROT_READ, MAP_ANON|MAP_PRIVATE, -1, 0);
-		if (*argtable == MAP_FAILED)
-			return (-1);
-	}
-
-#if 0
-	/* XXX is this required? */
-	(*argtable)[0].intarg = 0;
-#endif
-	for (n = 1; n <= tablemax; n++) {
-		switch (typetable[n]) {
-		case T_UNUSED:
-		case T_CHAR:
-		case T_U_CHAR:
-		case T_SHORT:
-		case T_U_SHORT:
-		case T_INT:
-			(*argtable)[n].intarg = va_arg(ap, int);
-			break;
-		case TP_SHORT:
-			(*argtable)[n].pshortarg = va_arg(ap, short *);
-			break;
-		case T_U_INT:
-			(*argtable)[n].uintarg = va_arg(ap, unsigned int);
-			break;
-		case TP_INT:
-			(*argtable)[n].pintarg = va_arg(ap, int *);
-			break;
-		case T_LONG:
-			(*argtable)[n].longarg = va_arg(ap, long);
-			break;
-		case T_U_LONG:
-			(*argtable)[n].ulongarg = va_arg(ap, unsigned long);
-			break;
-		case TP_LONG:
-			(*argtable)[n].plongarg = va_arg(ap, long *);
-			break;
-		case T_LLONG:
-			(*argtable)[n].longlongarg = va_arg(ap, long long);
-			break;
-		case T_U_LLONG:
-			(*argtable)[n].ulonglongarg = va_arg(ap, unsigned long long);
-			break;
-		case TP_LLONG:
-			(*argtable)[n].plonglongarg = va_arg(ap, long long *);
-			break;
-#ifdef FLOATING_POINT
-		case T_DOUBLE:
-			(*argtable)[n].doublearg = va_arg(ap, double);
-			break;
-		case T_LONG_DOUBLE:
-			(*argtable)[n].longdoublearg = va_arg(ap, long double);
-			break;
-#endif
-		case TP_CHAR:
-			(*argtable)[n].pchararg = va_arg(ap, char *);
-			break;
-		case TP_VOID:
-			(*argtable)[n].pvoidarg = va_arg(ap, void *);
-			break;
-		case T_PTRINT:
-			(*argtable)[n].ptrdiffarg = va_arg(ap, ptrdiff_t);
-			break;
-		case TP_PTRINT:
-			(*argtable)[n].pptrdiffarg = va_arg(ap, ptrdiff_t *);
-			break;
-		case T_SIZEINT:
-			(*argtable)[n].sizearg = va_arg(ap, size_t);
-			break;
-		case T_SSIZEINT:
-			(*argtable)[n].ssizearg = va_arg(ap, ssize_t);
-			break;
-		case TP_SSIZEINT:
-			(*argtable)[n].pssizearg = va_arg(ap, ssize_t *);
-			break;
-		case TP_MAXINT:
-			(*argtable)[n].intmaxarg = va_arg(ap, intmax_t);
-			break;
-		case T_WINT:
-			(*argtable)[n].wintarg = va_arg(ap, wint_t);
-			break;
-		case TP_WCHAR:
-			(*argtable)[n].pwchararg = va_arg(ap, wchar_t *);
-			break;
-		}
-	}
-	goto finish;
-
-overflow:
-	errno = ENOMEM;
-	ret = -1;
-
-finish:
-	if (typetable != NULL && typetable != stattypetable) {
-		munmap(typetable, *argtablesiz);
-		typetable = NULL;
-	}
-	return (ret);
-}
-
-/*
- * Increase the size of the type table.
- */
-static int
-__grow_type_table(unsigned char **typetable, int *tablesize)
-{
-	unsigned char *oldtable = *typetable;
-	int newsize = *tablesize * 2;
-
-	if (newsize < getpagesize())
-		newsize = getpagesize();
-
-	if (*tablesize == STATIC_ARG_TBL_SIZE) {
-		*typetable = mmap(NULL, newsize, PROT_WRITE|PROT_READ,
-		    MAP_ANON|MAP_PRIVATE, -1, 0);
-		if (*typetable == MAP_FAILED)
-			return (-1);
-		bcopy(oldtable, *typetable, *tablesize);
-	} else {
-		unsigned char *new = mmap(NULL, newsize, PROT_WRITE|PROT_READ,
-		    MAP_ANON|MAP_PRIVATE, -1, 0);
-		if (new == MAP_FAILED)
-			return (-1);
-		memmove(new, *typetable, *tablesize);
-		munmap(*typetable, *tablesize);
-		*typetable = new;
-	}
-	memset(*typetable + *tablesize, T_UNUSED, (newsize - *tablesize));
-
-	*tablesize = newsize;
-	return (0);
-}
-
- 
-#ifdef FLOATING_POINT
-static int
-exponent(wchar_t *p0, int exp, int fmtch)
-{
-	wchar_t *p, *t;
-	wchar_t expbuf[MAXEXPDIG];
-
-	p = p0;
-	*p++ = fmtch;
-	if (exp < 0) {
-		exp = -exp;
-		*p++ = '-';
-	} else
-		*p++ = '+';
-	t = expbuf + MAXEXPDIG;
-	if (exp > 9) {
-		do {
-			*--t = to_char(exp % 10);
-		} while ((exp /= 10) > 9);
-		*--t = to_char(exp);
-		for (; t < expbuf + MAXEXPDIG; *p++ = *t++)
-			/* nothing */;
-	} else {
-		/*
-		 * Exponents for decimal floating point conversions
-		 * (%[eEgG]) must be at least two characters long,
-		 * whereas exponents for hexadecimal conversions can
-		 * be only one character long.
-		 */
-		if (fmtch == 'e' || fmtch == 'E')
-			*p++ = '0';
-		*p++ = to_char(exp);
-	}
-	return (p - p0);
-}
-#endif /* FLOATING_POINT */
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 5f906c8..85376e0 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -1727,15 +1727,23 @@
   return si;
 }
 
-static void soinfo_unload(soinfo* root) {
-  if (root->is_linked()) {
-    root = root->get_local_group_root();
-  }
+static void soinfo_unload(soinfo* si) {
+  soinfo* root = si->is_linked() ? si->get_local_group_root() : si;
+
+  LD_LOG(kLogDlopen,
+         "... dlclose(realpath=\"%s\"@%p) ... load group root is \"%s\"@%p",
+         si->get_realpath(),
+         si,
+         root->get_realpath(),
+         root);
 
   ScopedTrace trace((std::string("unload ") + root->get_realpath()).c_str());
 
   if (!root->can_unload()) {
-    TRACE("not unloading \"%s\" - the binary is flagged with NODELETE", root->get_realpath());
+    LD_LOG(kLogDlopen,
+           "... dlclose(root=\"%s\"@%p) ... not unloading - the load group is flagged with NODELETE",
+           root->get_realpath(),
+           root);
     return;
   }
 
@@ -1762,11 +1770,17 @@
       if (ref_count == 0) {
         unload_list.push_back(si);
       } else {
-        TRACE("not unloading '%s' group, decrementing ref_count to %zd",
-            si->get_realpath(), ref_count);
+        LD_LOG(kLogDlopen,
+               "... dlclose(root=\"%s\"@%p) ... not unloading - decrementing ref_count to %zd",
+               si->get_realpath(),
+               si,
+               ref_count);
       }
     } else {
-      TRACE("not unloading '%s' - the binary is flagged with NODELETE", si->get_realpath());
+      LD_LOG(kLogDlopen,
+             "... dlclose(root=\"%s\"@%p) ... not unloading - the load group is flagged with NODELETE",
+             si->get_realpath(),
+             si);
       return;
     }
   }
@@ -1837,16 +1851,32 @@
   }
 
   local_unload_list.for_each([](soinfo* si) {
+    LD_LOG(kLogDlopen,
+           "... dlclose: calling destructors for \"%s\"@%p ... ",
+           si->get_realpath(),
+           si);
     si->call_destructors();
+    LD_LOG(kLogDlopen,
+           "... dlclose: calling destructors for \"%s\"@%p ... done",
+           si->get_realpath(),
+           si);
   });
 
   while ((si = local_unload_list.pop_front()) != nullptr) {
+    LD_LOG(kLogDlopen,
+           "... dlclose: unloading \"%s\"@%p ...",
+           si->get_realpath(),
+           si);
     notify_gdb_of_unload(si);
     get_cfi_shadow()->BeforeUnload(si);
     soinfo_free(si);
   }
 
   while ((si = external_unload_list.pop_front()) != nullptr) {
+    LD_LOG(kLogDlopen,
+           "... dlclose: unloading external reference \"%s\"@%p ...",
+           si->get_realpath(),
+           si);
     soinfo_unload(si);
   }
 }
@@ -2132,7 +2162,15 @@
     return -1;
   }
 
+  LD_LOG(kLogDlopen,
+         "dlclose(handle=%p, realpath=\"%s\"@%p) ...",
+         handle,
+         si->get_realpath(),
+         si);
   soinfo_unload(si);
+  LD_LOG(kLogDlopen,
+         "dlclose(handle=%p) ... done",
+         handle);
   return 0;
 }
 
diff --git a/tests/Android.bp b/tests/Android.bp
index 7094d77..742f44a 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -221,7 +221,21 @@
     ],
     // Ignore that we don't have ASAN symbols linked in.
     allow_undefined_symbols: true,
-    srcs: ["fortify_compilation_test.cpp"],
+    srcs: ["fortify_filecheck_diagnostics_test.cpp"],
+}
+
+// Ensure we don't use FORTIFY'ed functions with the static analyzer/clang-tidy:
+// it can confuse these tools pretty easily. If this builds successfully, then
+// __clang_analyzer__ overrode FORTIFY. Otherwise, FORTIFY was incorrectly
+// enabled. The library that results from building this is meant to be unused.
+cc_test_library {
+    name: "fortify_disabled_for_tidy",
+    cflags: [
+        "-Werror",
+        "-D_FORTIFY_SOURCE=2",
+        "-D__clang_analyzer__",
+    ],
+    srcs: ["fortify_filecheck_diagnostics_test.cpp"],
 }
 
 cc_test_library {
diff --git a/tests/Android.mk b/tests/Android.mk
index 24ff7f2..98216d7 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -78,7 +78,7 @@
 LOCAL_CPPFLAGS := -Wall
 # Disable color diagnostics so the warnings output matches the source
 LOCAL_CPPFLAGS += -fdiagnostics-color=never
-LOCAL_SRC_FILES := fortify_compilation_test.cpp
+LOCAL_SRC_FILES := fortify_filecheck_diagnostics_test.cpp
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
@@ -96,7 +96,7 @@
 LOCAL_MODULE := bionic-compile-time-tests-clang++
 LOCAL_CPPFLAGS := -Wall
 LOCAL_CPPFLAGS += -fno-color-diagnostics -ferror-limit=10000
-LOCAL_SRC_FILES := fortify_compilation_test.cpp
+LOCAL_SRC_FILES := fortify_filecheck_diagnostics_test.cpp
 include $(BUILD_STATIC_LIBRARY)
 
 endif # linux-x86
diff --git a/tests/fortify_compilation_test.cpp b/tests/fortify_filecheck_diagnostics_test.cpp
similarity index 100%
rename from tests/fortify_compilation_test.cpp
rename to tests/fortify_filecheck_diagnostics_test.cpp
diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp
index fb2a679..5eef35a 100755
--- a/tests/pthread_test.cpp
+++ b/tests/pthread_test.cpp
@@ -2210,9 +2210,14 @@
   ASSERT_EQ(0, pthread_create(&t, &attr, IdFn, nullptr));
   ASSERT_EQ(0, pthread_join(t, nullptr));
 
-  // If we ask to use them, though...
+#if defined(__LP64__)
+  // If we ask to use them, though, we'll see a failure...
   ASSERT_EQ(0, pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED));
   ASSERT_EQ(EINVAL, pthread_create(&t, &attr, IdFn, nullptr));
+#else
+  // For backwards compatibility with broken apps, we just ignore failures
+  // to set scheduler attributes on LP32.
+#endif
 }
 
 TEST(pthread, pthread_attr_setinheritsched_PTHREAD_INHERIT_SCHED_takes_effect) {
diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp
index 7b7737d..97b222c 100644
--- a/tests/stdio_test.cpp
+++ b/tests/stdio_test.cpp
@@ -1962,3 +1962,61 @@
   ASSERT_LE(FILENAME_MAX, PATH_MAX);
   ASSERT_EQ(L_tmpnam, PATH_MAX);
 }
+
+TEST(STDIO_TEST, perror) {
+  ExecTestHelper eth;
+  eth.Run([&]() { errno = EINVAL; perror("a b c"); exit(0); }, 0, "a b c: Invalid argument\n");
+  eth.Run([&]() { errno = EINVAL; perror(nullptr); exit(0); }, 0, "Invalid argument\n");
+  eth.Run([&]() { errno = EINVAL; perror(""); exit(0); }, 0, "Invalid argument\n");
+}
+
+TEST(STDIO_TEST, puts) {
+  ExecTestHelper eth;
+  eth.Run([&]() { exit(puts("a b c")); }, 0, "a b c\n");
+}
+
+TEST(STDIO_TEST, unlocked) {
+  TemporaryFile tf;
+
+  FILE* fp = fopen(tf.filename, "w+");
+  ASSERT_TRUE(fp != nullptr);
+
+  clearerr_unlocked(fp);
+  ASSERT_FALSE(feof_unlocked(fp));
+  ASSERT_FALSE(ferror_unlocked(fp));
+
+  ASSERT_EQ(fileno(fp), fileno_unlocked(fp));
+
+  ASSERT_NE(EOF, putc_unlocked('a', fp));
+  ASSERT_NE(EOF, putc('b', fp));
+  ASSERT_NE(EOF, fputc_unlocked('c', fp));
+  ASSERT_NE(EOF, fputc('d', fp));
+
+  rewind(fp);
+  ASSERT_EQ('a', getc_unlocked(fp));
+  ASSERT_EQ('b', getc(fp));
+  ASSERT_EQ('c', fgetc_unlocked(fp));
+  ASSERT_EQ('d', fgetc(fp));
+
+  rewind(fp);
+  ASSERT_EQ(2U, fwrite_unlocked("AB", 1, 2, fp));
+  ASSERT_EQ(2U, fwrite("CD", 1, 2, fp));
+  ASSERT_EQ(0, fflush_unlocked(fp));
+
+  rewind(fp);
+  char buf[BUFSIZ] = {};
+  ASSERT_EQ(2U, fread_unlocked(&buf[0], 1, 2, fp));
+  ASSERT_EQ(2U, fread(&buf[2], 1, 2, fp));
+  ASSERT_STREQ("ABCD", buf);
+
+  rewind(fp);
+  ASSERT_NE(EOF, fputs("hello ", fp));
+  ASSERT_NE(EOF, fputs_unlocked("world", fp));
+  ASSERT_NE(EOF, fputc('\n', fp));
+
+  rewind(fp);
+  ASSERT_TRUE(fgets_unlocked(buf, sizeof(buf), fp) != nullptr);
+  ASSERT_STREQ("hello world\n", buf);
+
+  ASSERT_EQ(0, fclose(fp));
+}
diff --git a/tools/versioner/src/Arch.h b/tools/versioner/src/Arch.h
index 0c6f514..ab05d5c 100644
--- a/tools/versioner/src/Arch.h
+++ b/tools/versioner/src/Arch.h
@@ -136,7 +136,7 @@
   { Arch::x86_64, "x86_64-linux-android" },
 };
 
-static const std::set<int> supported_levels = { 9, 12, 13, 14, 15, 16, 17, 18, 19, 21, 23, 24 };
+static const std::set<int> default_levels = { 14, 15, 16, 17, 18, 19, 21, 23, 24, 25, 26, 27 };
 
 static const ArchMap<int> arch_min_api = {
   { Arch::arm, 9 },
diff --git a/tools/versioner/src/SymbolDatabase.cpp b/tools/versioner/src/SymbolDatabase.cpp
index bd4b99d..8521f4d 100644
--- a/tools/versioner/src/SymbolDatabase.cpp
+++ b/tools/versioner/src/SymbolDatabase.cpp
@@ -70,11 +70,6 @@
   int api_level = type.api_level;
   std::ifstream stream;
   while (api_level >= arch_min_api[type.arch]) {
-    if (supported_levels.count(api_level) == 0) {
-      --api_level;
-      continue;
-    }
-
     std::string path = std::string(platform_dir) + "/android-" + std::to_string(api_level) +
                        "/arch-" + to_string(type.arch) + "/symbols/" + filename;
 
diff --git a/tools/versioner/src/versioner.cpp b/tools/versioner/src/versioner.cpp
index 8db75d7..83b4027 100644
--- a/tools/versioner/src/versioner.cpp
+++ b/tools/versioner/src/versioner.cpp
@@ -451,7 +451,7 @@
     fprintf(stderr, "\n");
     fprintf(stderr, "Target specification (defaults to all):\n");
     fprintf(stderr, "  -a API_LEVEL\tbuild with specified API level (can be repeated)\n");
-    fprintf(stderr, "    \t\tvalid levels are %s\n", Join(supported_levels).c_str());
+    fprintf(stderr, "    \t\tdefaults to %s\n", Join(default_levels).c_str());
     fprintf(stderr, "  -r ARCH\tbuild with specified architecture (can be repeated)\n");
     fprintf(stderr, "    \t\tvalid architectures are %s\n", Join(supported_archs).c_str());
     fprintf(stderr, "\n");
@@ -501,10 +501,6 @@
           usage();
         }
 
-        if (supported_levels.count(api_level) == 0) {
-          errx(1, "unsupported API level %d", api_level);
-        }
-
         selected_levels.insert(api_level);
         break;
       }
@@ -629,7 +625,7 @@
   }
 
   if (selected_levels.empty()) {
-    selected_levels = supported_levels;
+    selected_levels = default_levels;
   }
 
   if (selected_architectures.empty()) {
diff --git a/tools/versioner/tests/preprocessor/run.sh b/tools/versioner/tests/preprocessor/run.sh
index 60e7024..50d9b5c 100644
--- a/tools/versioner/tests/preprocessor/run.sh
+++ b/tools/versioner/tests/preprocessor/run.sh
@@ -4,7 +4,7 @@
   SRC=$1
   DST=$2
   rm -rf $2
-  versioner $1 -i -o $2
+  versioner -a 9 -a 12 -a 13 -a 14 -a 15 $1 -i -o $2
   diff -q -w -B $2 expected
 }
 
diff --git a/tools/versioner/tests/preprocessor_file_offset_bits/run.sh b/tools/versioner/tests/preprocessor_file_offset_bits/run.sh
index c503867..d974cba 100644
--- a/tools/versioner/tests/preprocessor_file_offset_bits/run.sh
+++ b/tools/versioner/tests/preprocessor_file_offset_bits/run.sh
@@ -1,5 +1,5 @@
 set -e
 
 rm -rf out
-versioner headers -i -o out
+versioner headers -a 9 -a 12 -a 13 -i -o out
 diff -q -w -B out expected
diff --git a/tools/versioner/tests/preprocessor_merging/run.sh b/tools/versioner/tests/preprocessor_merging/run.sh
index 1b0aae2..1929757 100644
--- a/tools/versioner/tests/preprocessor_merging/run.sh
+++ b/tools/versioner/tests/preprocessor_merging/run.sh
@@ -1,4 +1,4 @@
 rm -rf out
 set -e
-versioner headers -i -o out
+versioner headers -a 9 -a 10 -a 11 -i -o out
 diff -q -w -B out expected