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, ¶m) == -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