From 8bdaddd81d64d6250324590d1515e95130b0e9bd Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Thu, 7 Sep 2023 16:03:19 -0700 Subject: [PATCH] Make the Windows Console work better The stdio reader thread now appears to be working recursively along cosmopolitan subprocesses. For example, it's now possible to launch vim.com from the unbourne.com bestline repl, thanks to hacks plus a bug fix to select() timeouts. --- dsp/tty/ident.c | 2 +- examples/clear.c | 16 +++ examples/ctrlc.c | 8 +- examples/ttyinfo.c | 4 + libc/calls/calls.mk | 6 ++ libc/calls/fdatasync-nt.c | 9 +- libc/calls/fdatasync.c | 31 +++--- libc/calls/fsync-fake.c | 36 +++++++ libc/calls/fsync.c | 27 +++-- libc/calls/open-nt.c | 21 ++-- libc/calls/pread.c | 2 +- libc/calls/preadv.c | 2 +- libc/calls/read-nt.c | 54 ++++++---- libc/calls/read.c | 4 +- libc/calls/readv-metal.c | 9 +- libc/calls/readv-nt.c | 6 +- libc/calls/readv-serial.c | 9 +- libc/calls/readv.c | 4 +- libc/calls/struct/iovec.internal.h | 8 +- libc/calls/struct/timeval.h | 1 + libc/calls/syscall-nt.internal.h | 4 +- libc/calls/tcflush.c | 58 +++++++++-- libc/calls/tcgetattr-nt.c | 132 +++++++++++++----------- libc/calls/tcsetattr-nt.c | 154 ++++++++++++++-------------- libc/calls/winstdin1.c | 104 +++++++++++-------- libc/calls/winstdin2.c | 2 +- libc/fmt/fmt.h | 12 --- libc/intrin/strace.internal.h | 4 +- libc/isystem/linux/param.h | 19 ++++ libc/isystem/stdarg.h | 1 + libc/sock/gethostips.c | 2 - libc/sock/recv-nt.c | 15 +-- libc/sock/recv.c | 4 +- libc/sock/recvfrom-nt.c | 14 +-- libc/sock/recvfrom.c | 6 +- libc/sock/recvmsg.c | 6 +- libc/sock/select-nt.c | 7 +- libc/sock/syscall_fd.internal.h | 6 +- libc/stdio/gcvt.c | 1 + libc/stdio/snprintf.c | 2 +- libc/stdio/sscanf.c | 1 + libc/stdio/stdio.h | 11 ++ libc/stdio/vappendf.c | 2 +- libc/stdio/vasprintf.c | 2 +- libc/stdio/vsprintf.c | 2 +- libc/testlib/comborunner.c | 2 +- libc/testlib/fixturerunner.c | 2 +- libc/testlib/formatint.c | 2 +- test/libc/calls/fchdir_test.c | 37 +++++++ test/libc/calls/setrlimit_test.c | 2 +- test/libc/calls/symlinkat_test.c | 2 +- test/libc/intrin/kprintf_test.c | 2 +- test/libc/log/backtrace_test.c | 12 +-- test/libc/stdio/dirstream_test.c | 2 +- test/libc/stdio/palandprintf_test.c | 2 +- test/libc/stdio/snprintf_test.c | 2 +- test/libc/thread/sem_open_test.c | 6 +- test/libc/tinymath/complex_test.c | 2 +- test/libc/tinymath/strtod_test.c | 2 +- third_party/argon2/encoding.c | 3 +- third_party/finger/util.c | 2 +- third_party/lua/luaconf.h | 2 +- third_party/musl/strfmon.c | 2 +- third_party/regex/regerror.c | 2 +- third_party/zlib/gz/gzlib.c | 2 +- third_party/zlib/gz/gzwrite.c | 2 +- tool/build/lib/buffer.c | 2 +- tool/decode/lib/flagger.c | 2 +- 68 files changed, 580 insertions(+), 346 deletions(-) create mode 100644 examples/clear.c create mode 100644 libc/calls/fsync-fake.c create mode 100644 libc/isystem/linux/param.h create mode 100644 test/libc/calls/fchdir_test.c diff --git a/dsp/tty/ident.c b/dsp/tty/ident.c index 602a5212f..9ea569480 100644 --- a/dsp/tty/ident.c +++ b/dsp/tty/ident.c @@ -21,13 +21,13 @@ #include "libc/calls/termios.h" #include "libc/dce.h" #include "libc/errno.h" -#include "libc/fmt/fmt.h" #include "libc/intrin/safemacros.internal.h" #include "libc/intrin/weaken.h" #include "libc/mem/mem.h" #include "libc/runtime/runtime.h" #include "libc/sock/sock.h" #include "libc/sock/struct/pollfd.h" +#include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/sysv/consts/poll.h" #include "libc/sysv/errfuns.h" diff --git a/examples/clear.c b/examples/clear.c new file mode 100644 index 000000000..a21e05c06 --- /dev/null +++ b/examples/clear.c @@ -0,0 +1,16 @@ +#if 0 +/*─────────────────────────────────────────────────────────────────╗ +│ To the extent possible under law, Justine Tunney has waived │ +│ all copyright and related or neighboring rights to this file, │ +│ as it is written in the following disclaimers: │ +│ • http://unlicense.org/ │ +│ • http://creativecommons.org/publicdomain/zero/1.0/ │ +╚─────────────────────────────────────────────────────────────────*/ +#endif +#include "libc/calls/calls.h" + +// clears the teletypewriter display with empty cells + +int main(int argc, char *argv[]) { + write(1, "\e[H", 3); +} diff --git a/examples/ctrlc.c b/examples/ctrlc.c index 86e532714..c88e9835f 100644 --- a/examples/ctrlc.c +++ b/examples/ctrlc.c @@ -7,6 +7,7 @@ │ • http://creativecommons.org/publicdomain/zero/1.0/ │ ╚─────────────────────────────────────────────────────────────────*/ #endif +#include "libc/assert.h" #include "libc/calls/calls.h" #include "libc/calls/struct/sigaction.h" #include "libc/errno.h" @@ -15,7 +16,9 @@ #include "libc/sock/struct/pollfd.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" +#include "libc/sysv/consts/f.h" #include "libc/sysv/consts/limits.h" +#include "libc/sysv/consts/o.h" #include "libc/sysv/consts/poll.h" #include "libc/sysv/consts/sig.h" @@ -45,7 +48,7 @@ int main(int argc, char *argv[]) { // some programs are blocked on cpu rather than i/o // such programs shall rely on asynchronous signals printf("doing cpu task...\n"); - for (volatile int i = 0; i < INT_MAX / 20; ++i) { + for (volatile int i = 0; i < INT_MAX / 5; ++i) { if (gotsig) { printf("\rgot ctrl+c asynchronously\n"); exit(0); @@ -55,7 +58,7 @@ int main(int argc, char *argv[]) { // posix guarantees atomic i/o if you use pipe_buf sized buffers // that way we don't need to worry about things like looping and // we can also be assured that multiple actors wont have tearing - char buf[PIPE_BUF]; + char buf[4]; // read data from standard input // @@ -107,5 +110,6 @@ int main(int argc, char *argv[]) { // operating system will send SIGPIPE if there's any problem // which kills the process by default write(1, buf, got); + write(1, "\n", 1); } } diff --git a/examples/ttyinfo.c b/examples/ttyinfo.c index 1bfd871ab..11655a0c4 100644 --- a/examples/ttyinfo.c +++ b/examples/ttyinfo.c @@ -15,7 +15,9 @@ #include "libc/fmt/fmt.h" #include "libc/log/check.h" #include "libc/log/log.h" +#include "libc/nt/runtime.h" #include "libc/runtime/runtime.h" +#include "libc/sock/select.h" #include "libc/stdio/dprintf.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" @@ -26,6 +28,8 @@ #include "libc/sysv/consts/termios.h" #include "libc/x/xsigaction.h" +__static_yoink("WinMainStdin"); + #define CTRL(C) ((C) ^ 0b01000000) #define WRITE(FD, SLIT) write(FD, SLIT, strlen(SLIT)) #define ENABLE_SAFE_PASTE "\e[?2004h" diff --git a/libc/calls/calls.mk b/libc/calls/calls.mk index 7c6c3b583..2b49d09c2 100644 --- a/libc/calls/calls.mk +++ b/libc/calls/calls.mk @@ -93,6 +93,12 @@ o/$(MODE)/libc/calls/vdsofunc.greg.o: private \ -ffreestanding \ -fno-sanitize=address +# we can't use magic because: +# this code is called by WinMain +o/$(MODE)/libc/calls/winstdin1.o: private \ + COPTS += \ + $(NO_MAGIC) + # we can't use asan because: # ntspawn allocates 128kb of heap memory via win32 o/$(MODE)/libc/calls/ntspawn.o \ diff --git a/libc/calls/fdatasync-nt.c b/libc/calls/fdatasync-nt.c index 962d83d83..fa3c8ba04 100644 --- a/libc/calls/fdatasync-nt.c +++ b/libc/calls/fdatasync-nt.c @@ -17,12 +17,15 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" +#include "libc/nt/enum/filetype.h" #include "libc/nt/files.h" #include "libc/sysv/errfuns.h" -textwindows int sys_fdatasync_nt(int fd) { - // TODO(jart): what should we do with worker pipes? - if (!__isfdkind(fd, kFdFile)) return ebadf(); +textwindows int sys_fdatasync_nt(int fd, bool fake) { + if (!__isfdopen(fd)) return ebadf(); + if (!__isfdkind(fd, kFdFile)) return einval(); + if (GetFileType(g_fds.p[fd].handle) != kNtFileTypeDisk) return einval(); if (_check_interrupts(0)) return -1; + if (fake) return 0; return FlushFileBuffers(g_fds.p[fd].handle) ? 0 : -1; } diff --git a/libc/calls/fdatasync.c b/libc/calls/fdatasync.c index 68822f83a..633c7f780 100644 --- a/libc/calls/fdatasync.c +++ b/libc/calls/fdatasync.c @@ -18,39 +18,46 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/cp.internal.h" -#include "libc/calls/struct/stat.h" +#include "libc/calls/internal.h" #include "libc/calls/syscall-nt.internal.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/intrin/strace.internal.h" #include "libc/runtime/runtime.h" +#include "libc/sysv/errfuns.h" /** * Blocks until kernel flushes non-metadata buffers for fd to disk. * * @return 0 on success, or -1 w/ errno + * @raise ECANCELED if thread was cancelled in masked mode + * @raise EROFS if `fd` is on a read-only filesystem e.g. /zip + * @raise EINVAL if `fd` is a special file w/o synchronization + * @raise ENOSPC if disk space was exhausted + * @raise EBADF if `fd` isn't an open file + * @raise EINTR if signal was delivered + * @raise EIO if an i/o error happened * @see sync(), fsync(), sync_file_range() * @see __nosync to secretly disable - * @raise ECANCELED if thread was cancelled in masked mode - * @raise EINTR if signal was delivered * @cancellationpoint * @asyncsignalsafe */ int fdatasync(int fd) { int rc; - struct stat st; - if (__nosync != 0x5453455454534146) { - BEGIN_CANCELLATION_POINT; - if (!IsWindows()) { + bool fake = __nosync == 0x5453455454534146; + BEGIN_CANCELLATION_POINT; + if (__isfdkind(fd, kFdZip)) { + rc = erofs(); + } else if (!IsWindows()) { + if (!fake) { rc = sys_fdatasync(fd); } else { - rc = sys_fdatasync_nt(fd); + rc = sys_fsync_fake(fd); } - END_CANCELLATION_POINT; - STRACE("fdatasync(%d) → %d% m", fd, rc); } else { - rc = fstat(fd, &st); - STRACE("fdatasync_fake(%d) → %d% m", fd, rc); + rc = sys_fdatasync_nt(fd, fake); } + END_CANCELLATION_POINT; + STRACE("fdatasync%s(%d) → %d% m", fake ? "_fake" : "", fd, rc); return rc; } diff --git a/libc/calls/fsync-fake.c b/libc/calls/fsync-fake.c new file mode 100644 index 000000000..301269827 --- /dev/null +++ b/libc/calls/fsync-fake.c @@ -0,0 +1,36 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2023 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/calls/struct/stat.h" +#include "libc/calls/struct/stat.internal.h" +#include "libc/sysv/consts/s.h" +#include "libc/sysv/errfuns.h" + +int sys_fsync_fake(int fd) { + int rc; + struct stat st; + if (!(rc = sys_fstat(fd, &st))) { + if (S_ISSOCK(st.st_mode) || // + S_ISFIFO(st.st_mode) || // + S_ISLNK(st.st_mode)) { + rc = einval(); + } + } + return rc; +} diff --git a/libc/calls/fsync.c b/libc/calls/fsync.c index 9ff7df131..07554a3be 100644 --- a/libc/calls/fsync.c +++ b/libc/calls/fsync.c @@ -18,19 +18,25 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/cp.internal.h" -#include "libc/calls/struct/stat.h" +#include "libc/calls/internal.h" #include "libc/calls/syscall-nt.internal.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/intrin/strace.internal.h" #include "libc/runtime/runtime.h" +#include "libc/sysv/errfuns.h" /** * Blocks until kernel flushes buffers for fd to disk. * * @return 0 on success, or -1 w/ errno * @raise ECANCELED if thread was cancelled in masked mode + * @raise EROFS if `fd` is on a read-only filesystem e.g. /zip + * @raise EINVAL if `fd` is a special file w/o synchronization + * @raise ENOSPC if disk space was exhausted + * @raise EBADF if `fd` isn't an open file * @raise EINTR if signal was delivered + * @raise EIO if an i/o error happened * @see fdatasync(), sync_file_range() * @see __nosync to secretly disable * @cancellationpoint @@ -38,19 +44,20 @@ */ int fsync(int fd) { int rc; - struct stat st; - if (__nosync != 0x5453455454534146) { - BEGIN_CANCELLATION_POINT; - if (!IsWindows()) { + bool fake = __nosync == 0x5453455454534146; + BEGIN_CANCELLATION_POINT; + if (__isfdkind(fd, kFdZip)) { + rc = erofs(); + } else if (!IsWindows()) { + if (!fake) { rc = sys_fsync(fd); } else { - rc = sys_fdatasync_nt(fd); + rc = sys_fsync_fake(fd); } - END_CANCELLATION_POINT; - STRACE("fsync(%d) → %d% m", fd, rc); } else { - rc = fstat(fd, &st); - STRACE("fsync_fake(%d) → %d% m", fd, rc); + rc = sys_fdatasync_nt(fd, fake); } + END_CANCELLATION_POINT; + STRACE("fsync%s(%d) → %d% m", fake ? "_fake" : "", fd, rc); return rc; } diff --git a/libc/calls/open-nt.c b/libc/calls/open-nt.c index 119f1f6fc..df5314a4a 100644 --- a/libc/calls/open-nt.c +++ b/libc/calls/open-nt.c @@ -24,6 +24,7 @@ #include "libc/calls/syscall_support-nt.internal.h" #include "libc/errno.h" #include "libc/macros.internal.h" +#include "libc/nt/console.h" #include "libc/nt/createfile.h" #include "libc/nt/enum/accessmask.h" #include "libc/nt/enum/creationdisposition.h" @@ -144,14 +145,20 @@ static textwindows int sys_open_nt_console(int dirfd, const struct NtMagicPaths *mp, uint32_t flags, int32_t mode, size_t fd) { - if (GetFileType(g_fds.p[STDIN_FILENO].handle) == kNtFileTypeChar && - GetFileType(g_fds.p[STDOUT_FILENO].handle) == kNtFileTypeChar) { + uint32_t cm; + int input, output; + if ((__isfdopen((input = STDIN_FILENO)) && + GetConsoleMode(g_fds.p[input].handle, &cm)) && + ((__isfdopen((output = STDOUT_FILENO)) && + GetConsoleMode(g_fds.p[output].handle, &cm)) || + (__isfdopen((output = STDERR_FILENO)) && + GetConsoleMode(g_fds.p[output].handle, &cm)))) { // this is an ugly hack that works for observed usage patterns - g_fds.p[fd].handle = g_fds.p[STDIN_FILENO].handle; - g_fds.p[fd].extra = g_fds.p[STDOUT_FILENO].handle; - g_fds.p[STDOUT_FILENO].dontclose = true; - g_fds.p[STDIN_FILENO].dontclose = true; + g_fds.p[fd].handle = g_fds.p[input].handle; + g_fds.p[fd].extra = g_fds.p[output].handle; g_fds.p[fd].dontclose = true; + g_fds.p[input].dontclose = true; + g_fds.p[output].dontclose = true; } else if ((g_fds.p[fd].handle = sys_open_nt_impl( dirfd, mp->conin, (flags & ~O_ACCMODE) | O_RDONLY, mode, kNtFileFlagOverlapped)) != -1) { @@ -187,7 +194,7 @@ textwindows int sys_open_nt(int dirfd, const char *file, uint32_t flags, ssize_t rc; __fds_lock(); if ((rc = fd = __reservefd_unlocked(-1)) != -1) { - if ((flags & O_ACCMODE) == O_RDWR && !strcmp(file, kNtMagicPaths.devtty)) { + if (!strcmp(file, kNtMagicPaths.devtty)) { rc = sys_open_nt_console(dirfd, &kNtMagicPaths, flags, mode, fd); } else { rc = sys_open_nt_file(dirfd, file, flags, mode, fd); diff --git a/libc/calls/pread.c b/libc/calls/pread.c index 7a124eb94..c72c222ea 100644 --- a/libc/calls/pread.c +++ b/libc/calls/pread.c @@ -74,7 +74,7 @@ ssize_t pread(int fd, void *buf, size_t size, int64_t offset) { } else if (__isfdkind(fd, kFdSocket)) { rc = espipe(); } else if (__isfdkind(fd, kFdFile)) { - rc = sys_read_nt(&g_fds.p[fd], (struct iovec[]){{buf, size}}, 1, offset); + rc = sys_read_nt(fd, (struct iovec[]){{buf, size}}, 1, offset); } else { rc = ebadf(); } diff --git a/libc/calls/preadv.c b/libc/calls/preadv.c index ab64f22dc..4ceecd915 100644 --- a/libc/calls/preadv.c +++ b/libc/calls/preadv.c @@ -62,7 +62,7 @@ static ssize_t Preadv(int fd, struct iovec *iov, int iovlen, int64_t off) { if (g_fds.p[fd].kind == kFdSocket) { return espipe(); } else { - return sys_read_nt(g_fds.p + fd, iov, iovlen, off); + return sys_read_nt(fd, iov, iovlen, off); } } else { return ebadf(); diff --git a/libc/calls/read-nt.c b/libc/calls/read-nt.c index c34cc15ad..8d57784e8 100644 --- a/libc/calls/read-nt.c +++ b/libc/calls/read-nt.c @@ -41,6 +41,7 @@ #include "libc/nt/struct/overlapped.h" #include "libc/nt/synchronization.h" #include "libc/nt/thread.h" +#include "libc/nt/thunk/msabi.h" #include "libc/str/str.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/sicode.h" @@ -48,19 +49,24 @@ #include "libc/sysv/consts/termios.h" #include "libc/sysv/errfuns.h" +__static_yoink("WinMainStdin"); + #ifdef __x86_64__ +__msabi extern typeof(CloseHandle) *const __imp_CloseHandle; + static textwindows void sys_read_nt_abort(int64_t handle, struct NtOverlapped *overlapped) { unassert(CancelIoEx(handle, overlapped) || GetLastError() == kNtErrorNotFound); } -static textwindows ssize_t sys_read_nt_impl(struct Fd *fd, void *data, - size_t size, int64_t offset) { +textwindows ssize_t sys_read_nt_impl(int fd, void *data, size_t size, + int64_t offset) { // perform the read i/o operation bool32 ok; + struct Fd *f; uint32_t got; int filetype; int64_t handle; @@ -69,27 +75,28 @@ static textwindows ssize_t sys_read_nt_impl(struct Fd *fd, void *data, uint32_t targetsize; bool is_console_input; int abort_errno = EAGAIN; + f = g_fds.p + fd; StartOver: size = MIN(size, 0x7ffff000); - handle = __resolve_stdin_handle(fd->handle); + handle = __resolve_stdin_handle(f->handle); filetype = GetFileType(handle); - is_console_input = g_fds.stdin.handle ? fd->handle == g_fds.stdin.handle - : fd->handle == g_fds.p[0].handle; + is_console_input = g_fds.stdin.handle ? f->handle == g_fds.stdin.handle + : f->handle == g_fds.p[0].handle; // the caller might be reading a single byte at a time. but we need to // be able to munge three bytes into just 1 e.g. "\342\220\200" → "\0" - if (size && fd->buflen) { + if (size && f->buflen) { ReturnDataFromBuffer: - got = MIN(size, fd->buflen); - remain = fd->buflen - got; - if (got) memcpy(data, fd->buf, got); - if (remain) memmove(fd->buf, fd->buf + got, remain); - fd->buflen = remain; + got = MIN(size, f->buflen); + remain = f->buflen - got; + if (got) memcpy(data, f->buf, got); + if (remain) memmove(f->buf, f->buf + got, remain); + f->buflen = remain; return got; } if (is_console_input && size && size < 3 && (__ttymagic & kFdTtyMunging)) { - targetdata = fd->buf; - targetsize = sizeof(fd->buf); + targetdata = f->buf; + targetsize = sizeof(f->buf); } else { targetdata = data; targetsize = size; @@ -106,12 +113,11 @@ StartOver: // since for overlapped i/o, we always use GetOverlappedResult ok = ReadFile(handle, targetdata, targetsize, 0, &overlap); if (!ok && GetLastError() == kNtErrorIoPending) { - TryAgain: // the i/o operation is in flight; blocking is unavoidable // if we're in a non-blocking mode, then immediately abort // if an interrupt is pending then we abort before waiting // otherwise wait for i/o periodically checking interrupts - if (fd->flags & O_NONBLOCK) { + if (f->flags & O_NONBLOCK) { sys_read_nt_abort(handle, &overlap); } else if (_check_interrupts(kSigOpRestartable)) { Interrupted: @@ -120,6 +126,9 @@ StartOver: } else { for (;;) { uint32_t i; + if (g_fds.stdin.inisem) { + ReleaseSemaphore(g_fds.stdin.inisem, 1, 0); + } i = WaitForSingleObject(overlap.hEvent, __SIG_POLLING_INTERVAL_MS); if (i == kNtWaitTimeout) { if (_check_interrupts(kSigOpRestartable)) { @@ -138,10 +147,11 @@ StartOver: // for windows to acknowledge that it's done using that memory ok = GetOverlappedResult(handle, &overlap, &got, true); if (!ok && GetLastError() == kNtErrorIoIncomplete) { - goto TryAgain; + kprintf("you complete me\n"); + ok = true; } } - CloseHandle(overlap.hEvent); + __imp_CloseHandle(overlap.hEvent); } else { ok = false; } @@ -153,7 +163,7 @@ StartOver: int64_t position; // save file pointer which windows clobbers, even for overlapped i/o if (!SetFilePointerEx(handle, 0, &position, SEEK_CUR)) { - return __winerr(); // fd probably isn't seekable? + return __winerr(); // f probably isn't seekable? } struct NtOverlapped overlap = {0}; overlap.Pointer = (void *)(uintptr_t)offset; @@ -179,11 +189,11 @@ StartOver: } } if (__ttymagic & kFdTtyEchoing) { - _weaken(__echo_terminal_input)(fd, targetdata, got); + _weaken(__echo_terminal_input)(f, targetdata, got); } } if (targetdata != data) { - fd->buflen = got; + f->buflen = got; goto ReturnDataFromBuffer; } return got; @@ -204,8 +214,8 @@ StartOver: } } -textwindows ssize_t sys_read_nt(struct Fd *fd, const struct iovec *iov, - size_t iovlen, int64_t opt_offset) { +textwindows ssize_t sys_read_nt(int fd, const struct iovec *iov, size_t iovlen, + int64_t opt_offset) { ssize_t rc; size_t i, total; if (opt_offset < -1) return einval(); diff --git a/libc/calls/read.c b/libc/calls/read.c index e165256eb..441e6ded9 100644 --- a/libc/calls/read.c +++ b/libc/calls/read.c @@ -80,9 +80,9 @@ ssize_t read(int fd, void *buf, size_t size) { } else if (fd >= g_fds.n) { rc = ebadf(); } else if (IsMetal()) { - rc = sys_readv_metal(g_fds.p + fd, &(struct iovec){buf, size}, 1); + rc = sys_readv_metal(fd, &(struct iovec){buf, size}, 1); } else if (IsWindows()) { - rc = sys_readv_nt(g_fds.p + fd, &(struct iovec){buf, size}, 1); + rc = sys_readv_nt(fd, &(struct iovec){buf, size}, 1); } else { rc = enosys(); } diff --git a/libc/calls/readv-metal.c b/libc/calls/readv-metal.c index 46a6817e0..c4ffc3416 100644 --- a/libc/calls/readv-metal.c +++ b/libc/calls/readv-metal.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/internal.h" #include "libc/calls/metalfile.internal.h" #include "libc/calls/struct/fd.internal.h" #include "libc/calls/struct/iovec.h" @@ -28,11 +29,11 @@ #ifdef __x86_64__ -ssize_t sys_readv_metal(struct Fd *fd, const struct iovec *iov, int iovlen) { +ssize_t sys_readv_metal(int fd, const struct iovec *iov, int iovlen) { int i; size_t got, toto; struct MetalFile *file; - switch (fd->kind) { + switch (g_fds.p[fd].kind) { case kFdConsole: /* * The VGA teletypewriter code may wish to send out "status report" @@ -40,14 +41,14 @@ ssize_t sys_readv_metal(struct Fd *fd, const struct iovec *iov, int iovlen) { * Read & return these if they are available. */ if (_weaken(sys_readv_vga)) { - ssize_t res = _weaken(sys_readv_vga)(fd, iov, iovlen); + ssize_t res = _weaken(sys_readv_vga)(g_fds.p + fd, iov, iovlen); if (res > 0) return res; } /* fall through */ case kFdSerial: return sys_readv_serial(fd, iov, iovlen); case kFdFile: - file = (struct MetalFile *)fd->handle; + file = (struct MetalFile *)g_fds.p[fd].handle; for (toto = i = 0; i < iovlen && file->pos < file->size; ++i) { got = MIN(iov[i].iov_len, file->size - file->pos); if (got) memcpy(iov[i].iov_base, file->base, got); diff --git a/libc/calls/readv-nt.c b/libc/calls/readv-nt.c index c15301039..cc8cff12b 100644 --- a/libc/calls/readv-nt.c +++ b/libc/calls/readv-nt.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/internal.h" #include "libc/calls/struct/iovec.internal.h" #include "libc/intrin/weaken.h" #include "libc/sock/internal.h" @@ -24,9 +25,8 @@ #ifdef __x86_64__ -textwindows ssize_t sys_readv_nt(struct Fd *fd, const struct iovec *iov, - int iovlen) { - switch (fd->kind) { +textwindows ssize_t sys_readv_nt(int fd, const struct iovec *iov, int iovlen) { + switch (g_fds.p[fd].kind) { case kFdFile: case kFdConsole: return sys_read_nt(fd, iov, iovlen, -1); diff --git a/libc/calls/readv-serial.c b/libc/calls/readv-serial.c index b1f91ca78..d2bf92d57 100644 --- a/libc/calls/readv-serial.c +++ b/libc/calls/readv-serial.c @@ -16,14 +16,15 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/internal.h" #include "libc/calls/struct/fd.internal.h" #include "libc/calls/struct/iovec.internal.h" #include "libc/nexgen32e/uart.internal.h" #include "libc/runtime/pc.internal.h" #ifdef __x86_64__ -static bool IsDataAvailable(struct Fd *fd) { - return inb(fd->handle + UART_LSR) & UART_TTYDA; +static bool IsDataAvailable(int fd) { + return inb(g_fds.p[fd].handle + UART_LSR) & UART_TTYDA; } static int GetFirstIov(const struct iovec *iov, int iovlen) { @@ -36,13 +37,13 @@ static int GetFirstIov(const struct iovec *iov, int iovlen) { return -1; } -ssize_t sys_readv_serial(struct Fd *fd, const struct iovec *iov, int iovlen) { +ssize_t sys_readv_serial(int fd, const struct iovec *iov, int iovlen) { size_t i; if ((i = GetFirstIov(iov, iovlen)) != -1) { while (!IsDataAvailable(fd)) { __builtin_ia32_pause(); } - ((char *)iov[i].iov_base)[0] = inb(fd->handle); + ((char *)iov[i].iov_base)[0] = inb(g_fds.p[fd].handle); return 1; } else { return 0; diff --git a/libc/calls/readv.c b/libc/calls/readv.c index 67a9f0fb1..448b38bff 100644 --- a/libc/calls/readv.c +++ b/libc/calls/readv.c @@ -68,9 +68,9 @@ ssize_t readv(int fd, const struct iovec *iov, int iovlen) { } else if (fd >= g_fds.n) { rc = ebadf(); } else if (IsMetal()) { - rc = sys_readv_metal(g_fds.p + fd, iov, iovlen); + rc = sys_readv_metal(fd, iov, iovlen); } else if (IsWindows()) { - rc = sys_readv_nt(g_fds.p + fd, iov, iovlen); + rc = sys_readv_nt(fd, iov, iovlen); } else { rc = enosys(); } diff --git a/libc/calls/struct/iovec.internal.h b/libc/calls/struct/iovec.internal.h index e2b65790a..c39a3f56e 100644 --- a/libc/calls/struct/iovec.internal.h +++ b/libc/calls/struct/iovec.internal.h @@ -12,10 +12,10 @@ int64_t sys_pwritev(int, const struct iovec *, int, int64_t, int64_t); int64_t sys_readv(int32_t, const struct iovec *, int32_t); int64_t sys_vmsplice(int, const struct iovec *, int64_t, uint32_t); int64_t sys_writev(int32_t, const struct iovec *, int32_t); -ssize_t sys_read_nt(struct Fd *, const struct iovec *, size_t, int64_t); -ssize_t sys_readv_metal(struct Fd *, const struct iovec *, int); -ssize_t sys_readv_nt(struct Fd *, const struct iovec *, int); -ssize_t sys_readv_serial(struct Fd *, const struct iovec *, int); +ssize_t sys_read_nt(int, const struct iovec *, size_t, int64_t); +ssize_t sys_readv_metal(int, const struct iovec *, int); +ssize_t sys_readv_nt(int, const struct iovec *, int); +ssize_t sys_readv_serial(int, const struct iovec *, int); ssize_t sys_write_nt(int, const struct iovec *, size_t, ssize_t); ssize_t sys_writev_metal(struct Fd *, const struct iovec *, int); ssize_t sys_writev_nt(int, const struct iovec *, int); diff --git a/libc/calls/struct/timeval.h b/libc/calls/struct/timeval.h index 4adaa8207..0073e6002 100644 --- a/libc/calls/struct/timeval.h +++ b/libc/calls/struct/timeval.h @@ -31,6 +31,7 @@ struct timeval timeval_sub(struct timeval, struct timeval) pureconst; struct timeval timeval_subz(struct timeval, struct timeval) pureconst; int64_t timeval_toseconds(struct timeval); int64_t timeval_tomicros(struct timeval); +int64_t timeval_tomillis(struct timeval); struct timeval timespec_totimeval(struct timespec) pureconst; static inline struct timeval timeval_fromseconds(int64_t __x) { return (struct timeval){__x}; diff --git a/libc/calls/syscall-nt.internal.h b/libc/calls/syscall-nt.internal.h index 01c4aefff..f4a8071a9 100644 --- a/libc/calls/syscall-nt.internal.h +++ b/libc/calls/syscall-nt.internal.h @@ -14,9 +14,10 @@ int sys_fadvise_nt(int, uint64_t, uint64_t, int); int sys_fchdir_nt(int); int sys_fchmodat_nt(int, const char *, uint32_t, int); int sys_fcntl_nt(int, int, uintptr_t); -int sys_fdatasync_nt(int); +int sys_fdatasync_nt(int, bool); int sys_flock_nt(int, int); int sys_fork_nt(uint32_t); +int sys_fsync_fake(int); int sys_ftruncate_nt(int64_t, uint64_t); int sys_getloadavg_nt(double *, int); int sys_getppid_nt(void); @@ -36,6 +37,7 @@ int sys_sync_nt(void); int sys_truncate_nt(const char *, uint64_t); int sys_unlinkat_nt(int, const char *, int); int64_t sys_lseek_nt(int, int64_t, int); +ssize_t sys_read_nt_impl(int, void *, size_t, int64_t); ssize_t sys_readlinkat_nt(int, const char *, char *, size_t); COSMOPOLITAN_C_END_ diff --git a/libc/calls/tcflush.c b/libc/calls/tcflush.c index df32ce08c..2cea37d70 100644 --- a/libc/calls/tcflush.c +++ b/libc/calls/tcflush.c @@ -23,10 +23,14 @@ #include "libc/calls/syscall_support-nt.internal.h" #include "libc/calls/termios.h" #include "libc/dce.h" +#include "libc/errno.h" #include "libc/fmt/itoa.h" #include "libc/intrin/strace.internal.h" #include "libc/mem/alloca.h" #include "libc/nt/comms.h" +#include "libc/nt/console.h" +#include "libc/sysv/consts/fileno.h" +#include "libc/sysv/consts/o.h" #include "libc/sysv/consts/termios.h" #include "libc/sysv/errfuns.h" @@ -45,18 +49,48 @@ static const char *DescribeFlush(char buf[12], int action) { } static dontinline textwindows int sys_tcflush_nt(int fd, int queue) { - bool32 ok; - int64_t h; - if (!__isfdopen(fd)) return ebadf(); - ok = true; - h = g_fds.p[fd].handle; - if (queue == TCIFLUSH || queue == TCIOFLUSH) { - ok &= !!PurgeComm(h, kNtPurgeRxclear); + if (!__isfdopen(fd)) { + return ebadf(); } - if (queue == TCOFLUSH || queue == TCIOFLUSH) { - ok &= !!PurgeComm(h, kNtPurgeTxclear); + int64_t hConin; + if (__isfdkind(fd, kFdConsole)) { + hConin = g_fds.p[fd].handle; + } else if (fd == 0 || fd == 1 || fd == 2) { + hConin = g_fds.p[(fd = 0)].handle; + } else { + return enotty(); } - return ok ? 0 : __winerr(); + uint32_t inmode; + if (!GetConsoleMode(hConin, &inmode)) { + return enotty(); + } + if (queue == TCOFLUSH) { + return 0; // windows console output is never buffered + } + FlushConsoleInputBuffer(hConin); + int rc = 0; + int e = errno; + int oldflags = g_fds.p[fd].flags; + g_fds.p[fd].flags |= O_NONBLOCK; + for (;;) { + char buf[512]; + ssize_t got = sys_read_nt_impl(fd, buf, 512, -1); + if (!got) { + break; + } else if (got == -1) { + if (errno == EAGAIN) { + errno = e; + } else if (errno == EINTR) { + errno = e; + continue; + } else { + rc = -1; + } + break; + } + } + g_fds.p[fd].flags = oldflags; + return rc; } /** @@ -77,7 +111,9 @@ static dontinline textwindows int sys_tcflush_nt(int fd, int queue) { */ int tcflush(int fd, int queue) { int rc; - if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) { + if (queue != TCIFLUSH && queue != TCOFLUSH && queue != TCIOFLUSH) { + rc = einval(); + } else if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) { rc = enotty(); } else if (IsLinux()) { rc = sys_ioctl(fd, TCFLSH, queue); diff --git a/libc/calls/tcgetattr-nt.c b/libc/calls/tcgetattr-nt.c index 5607d4e15..966277efa 100644 --- a/libc/calls/tcgetattr-nt.c +++ b/libc/calls/tcgetattr-nt.c @@ -22,79 +22,87 @@ #include "libc/calls/struct/termios.h" #include "libc/calls/ttydefaults.h" #include "libc/intrin/nomultics.internal.h" +#include "libc/macros.internal.h" #include "libc/nt/console.h" #include "libc/nt/enum/consolemodeflags.h" #include "libc/nt/struct/consolescreenbufferinfoex.h" #include "libc/str/str.h" #include "libc/sysv/consts/baud.internal.h" +#include "libc/sysv/consts/fileno.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/termios.h" #include "libc/sysv/errfuns.h" -textwindows int tcgetattr_nt(int ignored, struct termios *tio) { - int64_t in, out; - bool32 inok, outok; - uint32_t inmode = 0, outmode = 0; - inok = GetConsoleMode((in = __getfdhandleactual(0)), &inmode); - outok = GetConsoleMode((out = __getfdhandleactual(1)), &outmode); - if (inok | outok) { - bzero(tio, sizeof(*tio)); +textwindows int tcgetattr_nt(int fd, struct termios *tio) { + int64_t hInput, hOutput; + uint32_t inmode, outmode; - tio->c_cc[VMIN] = 1; - tio->c_cc[VINTR] = __vintr; - tio->c_cc[VQUIT] = __vquit; - tio->c_cc[VERASE] = CTRL('?'); - tio->c_cc[VWERASE] = CTRL('W'); - tio->c_cc[VKILL] = CTRL('U'); - tio->c_cc[VEOF] = CTRL('D'); - tio->c_cc[VMIN] = CTRL('A'); - tio->c_cc[VSTART] = _POSIX_VDISABLE; - tio->c_cc[VSTOP] = _POSIX_VDISABLE; - tio->c_cc[VSUSP] = _POSIX_VDISABLE; - tio->c_cc[VREPRINT] = CTRL('R'); - tio->c_cc[VDISCARD] = CTRL('O'); - tio->c_cc[VLNEXT] = CTRL('V'); - - tio->c_iflag = IUTF8; - tio->c_lflag = ECHOE; - tio->c_cflag = CS8 | CREAD; - tio->_c_ispeed = B38400; - tio->_c_ospeed = B38400; - - if (inok) { - if (inmode & kNtEnableLineInput) { - tio->c_lflag |= ICANON; - } - // kNtEnableEchoInput only works with kNtEnableLineInput enabled. - if ((inmode & kNtEnableEchoInput) || (__ttymagic & kFdTtyEchoing)) { - tio->c_lflag |= ECHO; - } - // The Windows console itself always echos control codes as ASCII. - if ((inmode & kNtEnableEchoInput) || !(__ttymagic & kFdTtyEchoRaw)) { - tio->c_lflag |= ECHOCTL; - } - if (!(__ttymagic & kFdTtyNoCr2Nl)) { - tio->c_iflag |= ICRNL; - } - if (!(__ttymagic & kFdTtyNoIsigs)) { - tio->c_lflag |= ISIG; - } - if ((inmode & kNtEnableProcessedInput) || (__ttymagic & kFdTtyMunging)) { - tio->c_lflag |= IEXTEN; - } - } - - if (outok) { - if (outmode & kNtEnableProcessedOutput) { - tio->c_oflag |= OPOST; - } - if (!(outmode & kNtDisableNewlineAutoReturn)) { - tio->c_oflag |= OPOST | ONLCR; - } - } - - return 0; + if (__isfdkind(fd, kFdConsole)) { + hInput = g_fds.p[fd].handle; + hOutput = g_fds.p[fd].extra; + } else if (fd == STDIN_FILENO || // + fd == STDOUT_FILENO || // + fd == STDERR_FILENO) { + hInput = g_fds.p[STDIN_FILENO].handle; + hOutput = g_fds.p[MAX(STDOUT_FILENO, fd)].handle; } else { return enotty(); } + + if (!GetConsoleMode(hInput, &inmode) || !GetConsoleMode(hOutput, &outmode)) { + return enotty(); + } + + bzero(tio, sizeof(*tio)); + + tio->c_cc[VMIN] = 1; + tio->c_cc[VINTR] = __vintr; + tio->c_cc[VQUIT] = __vquit; + tio->c_cc[VERASE] = CTRL('?'); + tio->c_cc[VWERASE] = CTRL('W'); + tio->c_cc[VKILL] = CTRL('U'); + tio->c_cc[VEOF] = CTRL('D'); + tio->c_cc[VMIN] = CTRL('A'); + tio->c_cc[VSTART] = _POSIX_VDISABLE; + tio->c_cc[VSTOP] = _POSIX_VDISABLE; + tio->c_cc[VSUSP] = _POSIX_VDISABLE; + tio->c_cc[VREPRINT] = CTRL('R'); + tio->c_cc[VDISCARD] = CTRL('O'); + tio->c_cc[VLNEXT] = CTRL('V'); + + tio->c_iflag = IUTF8; + tio->c_lflag = ECHOE; + tio->c_cflag = CS8 | CREAD; + tio->_c_ispeed = B38400; + tio->_c_ospeed = B38400; + + if (inmode & kNtEnableLineInput) { + tio->c_lflag |= ICANON; + } + // kNtEnableEchoInput only works with kNtEnableLineInput enabled. + if ((inmode & kNtEnableEchoInput) || (__ttymagic & kFdTtyEchoing)) { + tio->c_lflag |= ECHO; + } + // The Windows console itself always echos control codes as ASCII. + if ((inmode & kNtEnableEchoInput) || !(__ttymagic & kFdTtyEchoRaw)) { + tio->c_lflag |= ECHOCTL; + } + if (!(__ttymagic & kFdTtyNoCr2Nl)) { + tio->c_iflag |= ICRNL; + } + if (!(__ttymagic & kFdTtyNoIsigs)) { + tio->c_lflag |= ISIG; + } + if ((inmode & kNtEnableProcessedInput) || (__ttymagic & kFdTtyMunging)) { + tio->c_lflag |= IEXTEN; + } + + if (outmode & kNtEnableProcessedOutput) { + tio->c_oflag |= OPOST; + } + if (!(outmode & kNtDisableNewlineAutoReturn)) { + tio->c_oflag |= OPOST | ONLCR; + } + + return 0; } diff --git a/libc/calls/tcsetattr-nt.c b/libc/calls/tcsetattr-nt.c index ddebd6d04..8d7acb02c 100644 --- a/libc/calls/tcsetattr-nt.c +++ b/libc/calls/tcsetattr-nt.c @@ -19,12 +19,15 @@ #include "libc/calls/internal.h" #include "libc/calls/sig.internal.h" #include "libc/calls/struct/metatermios.internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/termios.h" #include "libc/calls/termios.internal.h" #include "libc/calls/ttydefaults.h" #include "libc/dce.h" #include "libc/intrin/describeflags.internal.h" #include "libc/intrin/nomultics.internal.h" #include "libc/intrin/strace.internal.h" +#include "libc/macros.internal.h" #include "libc/nt/console.h" #include "libc/nt/enum/consolemodeflags.h" #include "libc/nt/enum/version.h" @@ -32,6 +35,7 @@ #include "libc/nt/version.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" +#include "libc/sysv/consts/fileno.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/sicode.h" #include "libc/sysv/consts/sig.h" @@ -166,94 +170,92 @@ textwindows int tcsetattr_nt(int fd, int opt, const struct termios *tio) { bool32 ok; int64_t hInput, hOutput; uint32_t inmode, outmode; + if (__isfdkind(fd, kFdConsole)) { - // program manually opened /dev/tty in O_RDWR mode for cmd.exe hInput = g_fds.p[fd].handle; hOutput = g_fds.p[fd].extra; - } else if (fd == 0 || fd == 1) { - // otherwise just assume cmd.exe console stdio - // there's no serial port support yet - hInput = g_fds.p[0].handle; - hOutput = g_fds.p[1].handle; - fd = 0; + } else if (fd == STDIN_FILENO || // + fd == STDOUT_FILENO || // + fd == STDERR_FILENO) { + hInput = g_fds.p[STDIN_FILENO].handle; + hOutput = g_fds.p[MAX(STDOUT_FILENO, fd)].handle; } else { - STRACE("tcsetattr(fd) must be 0, 1, or open'd /dev/tty"); return enotty(); } - if (GetConsoleMode(hInput, &inmode) && GetConsoleMode(hOutput, &outmode)) { - if (opt == TCSAFLUSH) { - FlushConsoleInputBuffer(hInput); - } - inmode &= ~(kNtEnableLineInput | kNtEnableEchoInput | - kNtEnableProcessedInput | kNtEnableVirtualTerminalInput); - inmode |= kNtEnableWindowInput; - __ttymagic = 0; - if (tio->c_lflag & ICANON) { - inmode |= kNtEnableLineInput | kNtEnableProcessedInput; - } else { - __ttymagic |= kFdTtyMunging; - if (tio->c_cc[VMIN] != 1) { - STRACE("tcsetattr c_cc[VMIN] must be 1 on Windows"); - return einval(); - } - if (IsAtLeastWindows10()) { - // - keys like f1, up, etc. get turned into \e ansi codes - // - totally destroys default console gui (e.g. up arrow) - inmode |= kNtEnableVirtualTerminalInput; - } - } - if (!(tio->c_iflag & ICRNL)) { - __ttymagic |= kFdTtyNoCr2Nl; - } - if (!(tio->c_lflag & ECHOCTL)) { - __ttymagic |= kFdTtyEchoRaw; - } - if (tio->c_lflag & ECHO) { - // "kNtEnableEchoInput can be used only if the - // kNtEnableLineInput mode is also enabled." -MSDN - if (tio->c_lflag & ICANON) { - inmode |= kNtEnableEchoInput; - } else { - // If ECHO is enabled in raw mode, then read(0) needs to - // magically write(1) to simulate echoing. This normally - // visualizes control codes, e.g. \r → ^M unless ECHOCTL - // hasn't been specified. - __ttymagic |= kFdTtyEchoing; - } - } - if (!(tio->c_lflag & ISIG)) { - __ttymagic |= kFdTtyNoIsigs; - } - __vintr = tio->c_cc[VINTR]; - __vquit = tio->c_cc[VQUIT]; - if ((tio->c_lflag & ISIG) && // - tio->c_cc[VINTR] == CTRL('C')) { - // allows ctrl-c to be delivered asynchronously via win32 - inmode |= kNtEnableProcessedInput; - } - ok = SetConsoleMode(hInput, inmode); - (void)ok; - NTTRACE("SetConsoleMode(%p, %s) → %hhhd", hInput, - DescribeNtConsoleInFlags(inmode), ok); + if (!GetConsoleMode(hInput, &inmode) || !GetConsoleMode(hOutput, &outmode)) { + return enotty(); + } - outmode &= ~kNtDisableNewlineAutoReturn; - outmode |= kNtEnableProcessedOutput; - if (!(tio->c_oflag & ONLCR)) { - outmode |= kNtDisableNewlineAutoReturn; + if (opt == TCSAFLUSH) { + tcflush(fd, TCIFLUSH); + } + inmode &= ~(kNtEnableLineInput | kNtEnableEchoInput | + kNtEnableProcessedInput | kNtEnableVirtualTerminalInput); + inmode |= kNtEnableWindowInput; + __ttymagic = 0; + if (tio->c_lflag & ICANON) { + inmode |= kNtEnableLineInput | kNtEnableProcessedInput; + } else { + __ttymagic |= kFdTtyMunging; + if (tio->c_cc[VMIN] != 1) { + STRACE("tcsetattr c_cc[VMIN] must be 1 on Windows"); + return einval(); } if (IsAtLeastWindows10()) { - outmode |= kNtEnableVirtualTerminalProcessing; + // - keys like f1, up, etc. get turned into \e ansi codes + // - totally destroys default console gui (e.g. up arrow) + inmode |= kNtEnableVirtualTerminalInput; } - ok = SetConsoleMode(hOutput, outmode); - (void)ok; - NTTRACE("SetConsoleMode(%p, %s) → %hhhd", hOutput, - DescribeNtConsoleOutFlags(outmode), ok); - - return 0; - } else { - return enotty(); } + if (!(tio->c_iflag & ICRNL)) { + __ttymagic |= kFdTtyNoCr2Nl; + } + if (!(tio->c_lflag & ECHOCTL)) { + __ttymagic |= kFdTtyEchoRaw; + } + if (tio->c_lflag & ECHO) { + // "kNtEnableEchoInput can be used only if the + // kNtEnableLineInput mode is also enabled." -MSDN + if (tio->c_lflag & ICANON) { + inmode |= kNtEnableEchoInput; + } else { + // If ECHO is enabled in raw mode, then read(0) needs to + // magically write(1) to simulate echoing. This normally + // visualizes control codes, e.g. \r → ^M unless ECHOCTL + // hasn't been specified. + __ttymagic |= kFdTtyEchoing; + } + } + if (!(tio->c_lflag & ISIG)) { + __ttymagic |= kFdTtyNoIsigs; + } + __vintr = tio->c_cc[VINTR]; + __vquit = tio->c_cc[VQUIT]; + if ((tio->c_lflag & ISIG) && // + tio->c_cc[VINTR] == CTRL('C')) { + // allows ctrl-c to be delivered asynchronously via win32 + inmode |= kNtEnableProcessedInput; + } + ok = SetConsoleMode(hInput, inmode); + (void)ok; + NTTRACE("SetConsoleMode(%p, %s) → %hhhd", hInput, + DescribeNtConsoleInFlags(inmode), ok); + + outmode &= ~kNtDisableNewlineAutoReturn; + outmode |= kNtEnableProcessedOutput; + if (!(tio->c_oflag & ONLCR)) { + outmode |= kNtDisableNewlineAutoReturn; + } + if (IsAtLeastWindows10()) { + outmode |= kNtEnableVirtualTerminalProcessing; + } + ok = SetConsoleMode(hOutput, outmode); + (void)ok; + NTTRACE("SetConsoleMode(%p, %s) → %hhhd", hOutput, + DescribeNtConsoleOutFlags(outmode), ok); + + return 0; } __attribute__((__constructor__)) static void tcsetattr_nt_init(void) { diff --git a/libc/calls/winstdin1.c b/libc/calls/winstdin1.c index c88a1b875..b0d569a5a 100644 --- a/libc/calls/winstdin1.c +++ b/libc/calls/winstdin1.c @@ -20,9 +20,11 @@ #include "libc/calls/internal.h" #include "libc/calls/syscall_support-nt.internal.h" #include "libc/dce.h" +#include "libc/fmt/itoa.h" #include "libc/intrin/atomic.h" #include "libc/intrin/strace.internal.h" #include "libc/log/libfatal.internal.h" +#include "libc/nt/console.h" #include "libc/nt/createfile.h" #include "libc/nt/enum/accessmask.h" #include "libc/nt/enum/creationdisposition.h" @@ -60,6 +62,7 @@ __msabi extern typeof(CreateFile) *const __imp_CreateFileW; __msabi extern typeof(CreateNamedPipe) *const __imp_CreateNamedPipeW; __msabi extern typeof(CreateSemaphore) *const __imp_CreateSemaphoreW; __msabi extern typeof(CreateThread) *const __imp_CreateThread; +__msabi extern typeof(GetConsoleMode) *const __imp_GetConsoleMode; __msabi extern typeof(GetCurrentThreadId) *const __imp_GetCurrentThreadId; __msabi extern typeof(GetStdHandle) *const __imp_GetStdHandle; __msabi extern typeof(ReadFile) *const __imp_ReadFile; @@ -78,78 +81,97 @@ static void Log(const char *s) { #endif } -__msabi static dontasan dontubsan dontinstrument textwindows uint32_t -WinStdinThread(void *lpParameter) { - char buf[4096]; +__msabi static textwindows uint32_t WinStdinThread(void *lpParameter) { + char buf[4]; uint32_t i, got, wrote; - - // wait forever for semaphore to exceed 0 - // - // this semaphore is unlocked the first time read or poll happens. we - // need it so the prog has time to call functions like SetConsoleMode - // before we begin performing i/o. - __imp_WaitForSingleObject(g_fds.stdin.inisem, -1u); - __imp_CloseHandle(g_fds.stdin.inisem); - - // relay stdin to process - Log(" activated\n"); + Log(" activated thread\n"); for (;;) { + Log(" wait\n"); + __imp_WaitForSingleObject(g_fds.stdin.inisem, -1u); + Log(" read\n"); if (!__imp_ReadFile(g_fds.stdin.handle, buf, sizeof(buf), &got, 0)) { Log(" read failed\n"); goto Finish; } if (!got) { - Log(" end of file\n"); + Log(" eof\n"); goto Finish; } for (i = 0; i < got; i += wrote) { + Log(" write"); if (!__imp_WriteFile(g_fds.stdin.writer, buf + i, got - i, &wrote, 0)) { - Log(" failed to write to pipe\n"); + Log(" write failed\n"); goto Finish; } } } - Finish: __imp_CloseHandle(g_fds.stdin.writer); return 0; } +textwindows static char16_t *FixCpy(char16_t p[17], uint64_t x, uint8_t k) { + while (k > 0) *p++ = "0123456789abcdef"[(x >> (k -= 4)) & 15]; + *p = '\0'; + return p; +} + +textwindows static char16_t *CreateStdinPipeName(char16_t *a, int64_t h) { + char16_t *p = a; + const char *q = "\\\\?\\pipe\\cosmo\\stdin\\"; + while (*q) *p++ = *q++; + p = FixCpy(p, h, 64); + *p = 0; + return a; +} + // this makes it possible for our read() implementation to periodically // poll for signals while performing a blocking overlapped io operation -dontasan dontubsan dontinstrument textwindows void WinMainStdin(void) { +textwindows void WinMainStdin(void) { char16_t pipename[64]; int64_t hStdin, hWriter, hReader, hThread, hSemaphore; if (!SupportsWindows()) return; + // handle numbers stay the same when inherited by the subprocesses hStdin = __imp_GetStdHandle(kNtStdInputHandle); if (hStdin == kNtInvalidHandleValue) { Log(" GetStdHandle failed\n"); return; } - // create non-inherited semaphore with initial value of 0 - hSemaphore = __imp_CreateSemaphoreW(0, 0, 1, 0); - if (!hSemaphore) { - Log(" CreateSemaphore failed\n"); - return; - } - __create_pipe_name(pipename); - hReader = __imp_CreateNamedPipeW( - pipename, kNtPipeAccessInbound | kNtFileFlagOverlapped, - kNtPipeTypeByte | kNtPipeReadmodeByte, 1, 4096, 4096, 0, 0); - if (hReader == kNtInvalidHandleValue) { - Log(" CreateNamedPipe failed\n"); - return; - } - hWriter = __imp_CreateFileW(pipename, kNtGenericWrite, 0, 0, kNtOpenExisting, + CreateStdinPipeName(pipename, hStdin); + hReader = __imp_CreateFileW(pipename, kNtGenericRead, 0, 0, kNtOpenExisting, kNtFileFlagOverlapped, 0); - if (hWriter == kNtInvalidHandleValue) { - Log(" CreateFile failed\n"); - return; - } - hThread = __imp_CreateThread(0, 65536, WinStdinThread, 0, 0, 0); - if (!hThread) { - Log(" CreateThread failed\n"); - return; + if (hReader == kNtInvalidHandleValue) { + // we are the init process; create pipe server to dole out stdin + hWriter = __imp_CreateNamedPipeW( + pipename, kNtPipeAccessOutbound | kNtFileFlagOverlapped, + kNtPipeTypeMessage | kNtPipeReadmodeMessage | kNtPipeNowait, + kNtPipeUnlimitedInstances, 4096, 4096, 0, 0); + if (hWriter == kNtInvalidHandleValue) { + Log(" CreateNamedPipe failed\n"); + return; + } + hReader = __imp_CreateFileW(pipename, kNtGenericRead, 0, 0, kNtOpenExisting, + kNtFileFlagOverlapped, 0); + if (hReader == kNtInvalidHandleValue) { + Log(" CreateFile failed\n"); + return; + } + // create non-inherited semaphore with initial value of 0 + hSemaphore = __imp_CreateSemaphoreW(0, 0, 1, 0); + if (!hSemaphore) { + Log(" CreateSemaphore failed\n"); + return; + } + hThread = __imp_CreateThread(0, 65536, WinStdinThread, 0, 0, 0); + if (!hThread) { + Log(" CreateThread failed\n"); + return; + } + } else { + // we are the child of a cosmo process with its own stdin thread + hWriter = 0; + hThread = 0; + hSemaphore = 0; } g_fds.stdin.handle = hStdin; g_fds.stdin.thread = hThread; diff --git a/libc/calls/winstdin2.c b/libc/calls/winstdin2.c index cfe091acc..45d6e74b5 100644 --- a/libc/calls/winstdin2.c +++ b/libc/calls/winstdin2.c @@ -23,7 +23,7 @@ textwindows int64_t __resolve_stdin_handle(int64_t handle) { if (handle == g_fds.stdin.handle) { - if (!atomic_exchange(&g_fds.stdin.once, 1)) { + if (g_fds.stdin.inisem) { ReleaseSemaphore(g_fds.stdin.inisem, 1, 0); } handle = g_fds.stdin.reader; diff --git a/libc/fmt/fmt.h b/libc/fmt/fmt.h index f7a65f4ed..704e5d27a 100644 --- a/libc/fmt/fmt.h +++ b/libc/fmt/fmt.h @@ -1,8 +1,5 @@ #ifndef COSMOPOLITAN_LIBC_FMT_FMT_H_ #define COSMOPOLITAN_LIBC_FMT_FMT_H_ -/*───────────────────────────────────────────────────────────────────────────│─╗ -│ cosmopolitan § string formatting ─╬─│┼ -╚────────────────────────────────────────────────────────────────────────────│*/ #if __SIZEOF_POINTER__ == 8 #define POINTER_XDIGITS 12 /* math.log(2**48-1,16) */ @@ -13,15 +10,6 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -int snprintf(char *, size_t, const char *, ...) - printfesque(3) dontthrow nocallback; -int vsnprintf(char *, size_t, const char *, va_list) -dontthrow nocallback; -int sprintf(char *, const char *, ...) dontthrow nocallback; -int vsprintf(char *, const char *, va_list) -dontthrow nocallback; -int sscanf(const char *, const char *, ...) scanfesque(2); -int vsscanf(const char *, const char *, va_list); char *fcvt(double, int, int *, int *); char *ecvt(double, int, int *, int *); char *gcvt(double, int, char *); diff --git a/libc/intrin/strace.internal.h b/libc/intrin/strace.internal.h index 141b50308..55455e4bc 100644 --- a/libc/intrin/strace.internal.h +++ b/libc/intrin/strace.internal.h @@ -4,11 +4,11 @@ #include "libc/runtime/runtime.h" #define _KERNTRACE 0 /* not configurable w/ flag yet */ -#define _POLLTRACE 0 /* not configurable w/ flag yet */ +#define _POLLTRACE 1 /* not configurable w/ flag yet */ #define _DATATRACE 1 /* not configurable w/ flag yet */ #define _STDIOTRACE 0 /* not configurable w/ flag yet */ #define _LOCKTRACE 0 /* not configurable w/ flag yet */ -#define _NTTRACE 0 /* not configurable w/ flag yet */ +#define _NTTRACE 1 /* not configurable w/ flag yet */ #define STRACE_PROLOGUE "%rSYS %6P %'18T " diff --git a/libc/isystem/linux/param.h b/libc/isystem/linux/param.h new file mode 100644 index 000000000..4fa289b1a --- /dev/null +++ b/libc/isystem/linux/param.h @@ -0,0 +1,19 @@ +#ifndef _LINUX_PARAM_H +#define _LINUX_PARAM_H +#include "libc/runtime/clktck.h" + +#ifndef HZ +#define HZ CLK_TCK +#endif + +#ifndef EXEC_PAGESIZE +#define EXEC_PAGESIZE 65536 +#endif + +#ifndef NOGROUP +#define NOGROUP (-1) +#endif + +#define MAXHOSTNAMELEN 64 + +#endif diff --git a/libc/isystem/stdarg.h b/libc/isystem/stdarg.h index 232d26075..f624adf10 100644 --- a/libc/isystem/stdarg.h +++ b/libc/isystem/stdarg.h @@ -1,3 +1,4 @@ #ifndef _STDARG_H #define _STDARG_H +#include "libc/stdio/stdio.h" #endif /* _STDARG_H */ diff --git a/libc/sock/gethostips.c b/libc/sock/gethostips.c index 8cd5e1874..7bd82696a 100644 --- a/libc/sock/gethostips.c +++ b/libc/sock/gethostips.c @@ -32,8 +32,6 @@ #include "libc/sysv/consts/sio.h" #include "libc/sysv/consts/sock.h" -/* TODO(jart): DELETE */ - static uint32_t *GetUnixIps(void) { int fd, n; uint64_t z; diff --git a/libc/sock/recv-nt.c b/libc/sock/recv-nt.c index 7705a51e7..a0a8a7a75 100644 --- a/libc/sock/recv-nt.c +++ b/libc/sock/recv-nt.c @@ -28,8 +28,8 @@ #include "libc/sock/syscall_fd.internal.h" #include "libc/sysv/errfuns.h" -textwindows ssize_t sys_recv_nt(struct Fd *fd, const struct iovec *iov, - size_t iovlen, uint32_t flags) { +textwindows ssize_t sys_recv_nt(int fd, const struct iovec *iov, size_t iovlen, + uint32_t flags) { int err; ssize_t rc; uint32_t got; @@ -37,17 +37,18 @@ textwindows ssize_t sys_recv_nt(struct Fd *fd, const struct iovec *iov, struct NtIovec iovnt[16]; struct NtOverlapped overlapped = {.hEvent = WSACreateEvent()}; err = errno; - if (!WSARecv(fd->handle, iovnt, __iovec2nt(iovnt, iov, iovlen), 0, &flags, - &overlapped, 0)) { - if (WSAGetOverlappedResult(fd->handle, &overlapped, &got, false, &flags)) { + if (!WSARecv(g_fds.p[fd].handle, iovnt, __iovec2nt(iovnt, iov, iovlen), 0, + &flags, &overlapped, 0)) { + if (WSAGetOverlappedResult(g_fds.p[fd].handle, &overlapped, &got, false, + &flags)) { rc = got; } else { rc = -1; } } else { errno = err; - sockfd = (struct SockFd *)fd->extra; - rc = __wsablock(fd, &overlapped, &flags, kSigOpRestartable, + sockfd = (struct SockFd *)g_fds.p[fd].extra; + rc = __wsablock(g_fds.p + fd, &overlapped, &flags, kSigOpRestartable, sockfd->rcvtimeo); } unassert(WSACloseEvent(overlapped.hEvent)); diff --git a/libc/sock/recv.c b/libc/sock/recv.c index 822f32e5a..7dbf79bb2 100644 --- a/libc/sock/recv.c +++ b/libc/sock/recv.c @@ -53,12 +53,12 @@ ssize_t recv(int fd, void *buf, size_t size, int flags) { rc = sys_recvfrom(fd, buf, size, flags, 0, 0); } else if (__isfdopen(fd)) { if (__isfdkind(fd, kFdSocket)) { - rc = sys_recv_nt(&g_fds.p[fd], (struct iovec[]){{buf, size}}, 1, flags); + rc = sys_recv_nt(fd, (struct iovec[]){{buf, size}}, 1, flags); } else if (__isfdkind(fd, kFdFile)) { if (flags) { rc = einval(); } else { - rc = sys_read_nt(&g_fds.p[fd], (struct iovec[]){{buf, size}}, 1, -1); + rc = sys_read_nt(fd, (struct iovec[]){{buf, size}}, 1, -1); } } else { rc = enotsock(); diff --git a/libc/sock/recvfrom-nt.c b/libc/sock/recvfrom-nt.c index 4c4e36b1a..847f400c9 100644 --- a/libc/sock/recvfrom-nt.c +++ b/libc/sock/recvfrom-nt.c @@ -25,7 +25,7 @@ #include "libc/sock/syscall_fd.internal.h" #include "libc/sysv/errfuns.h" -textwindows ssize_t sys_recvfrom_nt(struct Fd *fd, const struct iovec *iov, +textwindows ssize_t sys_recvfrom_nt(int fd, const struct iovec *iov, size_t iovlen, uint32_t flags, void *opt_out_srcaddr, uint32_t *opt_inout_srcaddrsize) { @@ -36,17 +36,19 @@ textwindows ssize_t sys_recvfrom_nt(struct Fd *fd, const struct iovec *iov, struct NtIovec iovnt[16]; struct NtOverlapped overlapped = {.hEvent = WSACreateEvent()}; err = errno; - if (!WSARecvFrom(fd->handle, iovnt, __iovec2nt(iovnt, iov, iovlen), 0, &flags, - opt_out_srcaddr, opt_inout_srcaddrsize, &overlapped, NULL)) { - if (WSAGetOverlappedResult(fd->handle, &overlapped, &got, false, &flags)) { + if (!WSARecvFrom(g_fds.p[fd].handle, iovnt, __iovec2nt(iovnt, iov, iovlen), 0, + &flags, opt_out_srcaddr, opt_inout_srcaddrsize, &overlapped, + NULL)) { + if (WSAGetOverlappedResult(g_fds.p[fd].handle, &overlapped, &got, false, + &flags)) { rc = got; } else { rc = -1; } } else { errno = err; - sockfd = (struct SockFd *)fd->extra; - rc = __wsablock(fd, &overlapped, &flags, kSigOpRestartable, + sockfd = (struct SockFd *)g_fds.p[fd].extra; + rc = __wsablock(g_fds.p + fd, &overlapped, &flags, kSigOpRestartable, sockfd->rcvtimeo); } WSACloseEvent(overlapped.hEvent); diff --git a/libc/sock/recvfrom.c b/libc/sock/recvfrom.c index ff8e95e3e..23fa7996d 100644 --- a/libc/sock/recvfrom.c +++ b/libc/sock/recvfrom.c @@ -68,11 +68,11 @@ ssize_t recvfrom(int fd, void *buf, size_t size, int flags, rc = sys_recvfrom(fd, buf, size, flags, &addr, &addrsize); } else if (__isfdopen(fd)) { if (__isfdkind(fd, kFdSocket)) { - rc = sys_recvfrom_nt(&g_fds.p[fd], (struct iovec[]){{buf, size}}, 1, - flags, &addr, &addrsize); + rc = sys_recvfrom_nt(fd, (struct iovec[]){{buf, size}}, 1, flags, &addr, + &addrsize); } else if (__isfdkind(fd, kFdFile) && !opt_out_srcaddr) { /* socketpair */ if (!flags) { - rc = sys_read_nt(&g_fds.p[fd], (struct iovec[]){{buf, size}}, 1, -1); + rc = sys_read_nt(fd, (struct iovec[]){{buf, size}}, 1, -1); } else { rc = einval(); } diff --git a/libc/sock/recvmsg.c b/libc/sock/recvmsg.c index 0a6fa8cc2..188310972 100644 --- a/libc/sock/recvmsg.c +++ b/libc/sock/recvmsg.c @@ -74,12 +74,12 @@ ssize_t recvmsg(int fd, struct msghdr *msg, int flags) { } else if (__isfdopen(fd)) { if (!msg->msg_control) { if (__isfdkind(fd, kFdSocket)) { - rc = sys_recvfrom_nt(&g_fds.p[fd], msg->msg_iov, msg->msg_iovlen, flags, + rc = sys_recvfrom_nt(fd, msg->msg_iov, msg->msg_iovlen, flags, msg->msg_name, &msg->msg_namelen); } else if (__isfdkind(fd, kFdFile) && !msg->msg_name) { /* socketpair */ if (!flags) { - if ((got = sys_read_nt(&g_fds.p[fd], msg->msg_iov, msg->msg_iovlen, - -1)) != -1) { + if ((got = sys_read_nt(fd, msg->msg_iov, msg->msg_iovlen, -1)) != + -1) { msg->msg_flags = 0; rc = got; } else { diff --git a/libc/sock/select-nt.c b/libc/sock/select-nt.c index 8f6a1c709..795427b63 100644 --- a/libc/sock/select-nt.c +++ b/libc/sock/select-nt.c @@ -56,8 +56,10 @@ int sys_select_nt(int nfds, fd_set *readfds, fd_set *writefds, } // convert the wait time to a word - if (!timeout || ckd_add(&millis, timeout->tv_sec, timeout->tv_usec / 1000)) { + if (!timeout) { millis = -1; + } else { + millis = timeval_tomillis(*timeout); } // call our nt poll implementation @@ -76,8 +78,7 @@ int sys_select_nt(int nfds, fd_set *readfds, fd_set *writefds, // store remaining time back in caller's timeval if (timeout) { - timeout->tv_sec = millis / 1000; - timeout->tv_usec = millis % 1000 * 1000; + *timeout = timeval_frommillis(millis); } return fdcount; diff --git a/libc/sock/syscall_fd.internal.h b/libc/sock/syscall_fd.internal.h index b6c66d039..dd7ba684e 100644 --- a/libc/sock/syscall_fd.internal.h +++ b/libc/sock/syscall_fd.internal.h @@ -17,9 +17,9 @@ int sys_getsockopt_nt(struct Fd *, int, int, void *, uint32_t *); int sys_listen_nt(struct Fd *, int); int sys_setsockopt_nt(struct Fd *, int, int, const void *, uint32_t); int sys_shutdown_nt(struct Fd *, int); -ssize_t sys_recv_nt(struct Fd *, const struct iovec *, size_t, uint32_t); -ssize_t sys_recvfrom_nt(struct Fd *, const struct iovec *, size_t, uint32_t, - void *, uint32_t *); +ssize_t sys_recv_nt(int, const struct iovec *, size_t, uint32_t); +ssize_t sys_recvfrom_nt(int, const struct iovec *, size_t, uint32_t, void *, + uint32_t *); int __wsablock(struct Fd *, struct NtOverlapped *, uint32_t *, int, uint32_t); COSMOPOLITAN_C_END_ diff --git a/libc/stdio/gcvt.c b/libc/stdio/gcvt.c index 633856e09..662b0df13 100644 --- a/libc/stdio/gcvt.c +++ b/libc/stdio/gcvt.c @@ -24,6 +24,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/fmt/fmt.h" #include "libc/mem/mem.h" +#include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/str/unicode.h" #include "third_party/gdtoa/gdtoa.h" diff --git a/libc/stdio/snprintf.c b/libc/stdio/snprintf.c index dff485baf..43bfd6e38 100644 --- a/libc/stdio/snprintf.c +++ b/libc/stdio/snprintf.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/fmt/fmt.h" +#include "libc/stdio/stdio.h" /** * Formats string to buffer. diff --git a/libc/stdio/sscanf.c b/libc/stdio/sscanf.c index cf2830305..0e688e327 100644 --- a/libc/stdio/sscanf.c +++ b/libc/stdio/sscanf.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/dce.h" #include "libc/fmt/fmt.h" +#include "libc/stdio/stdio.h" /** * String decoder. diff --git a/libc/stdio/stdio.h b/libc/stdio/stdio.h index 0aff90b24..3cad71ef4 100644 --- a/libc/stdio/stdio.h +++ b/libc/stdio/stdio.h @@ -108,6 +108,14 @@ int vscanf(const char *, va_list); int fscanf(FILE *, const char *, ...) scanfesque(2); int vfscanf(FILE *, const char *, va_list); +int snprintf(char *, size_t, const char *, ...) + printfesque(3) dontthrow nocallback; +int vsnprintf(char *, size_t, const char *, va_list) +dontthrow nocallback; +int sprintf(char *, const char *, ...) dontthrow nocallback; +int vsprintf(char *, const char *, va_list) +dontthrow nocallback; + int fwprintf(FILE *, const wchar_t *, ...); int fwscanf(FILE *, const wchar_t *, ...); int swprintf(wchar_t *, size_t, const wchar_t *, ...); @@ -122,6 +130,9 @@ int wprintf(const wchar_t *, ...); int wscanf(const wchar_t *, ...); int fwide(FILE *, int); +int sscanf(const char *, const char *, ...) scanfesque(2); +int vsscanf(const char *, const char *, va_list); + /*───────────────────────────────────────────────────────────────────────────│─╗ │ cosmopolitan § standard i/o » allocating ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│*/ diff --git a/libc/stdio/vappendf.c b/libc/stdio/vappendf.c index 492e0d676..746d5e97e 100644 --- a/libc/stdio/vappendf.c +++ b/libc/stdio/vappendf.c @@ -18,10 +18,10 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" #include "libc/dce.h" -#include "libc/fmt/fmt.h" #include "libc/macros.internal.h" #include "libc/mem/mem.h" #include "libc/stdio/append.h" +#include "libc/stdio/stdio.h" #define W sizeof(size_t) diff --git a/libc/stdio/vasprintf.c b/libc/stdio/vasprintf.c index cba7ae3b7..5e8a7035a 100644 --- a/libc/stdio/vasprintf.c +++ b/libc/stdio/vasprintf.c @@ -17,8 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" -#include "libc/fmt/fmt.h" #include "libc/mem/mem.h" +#include "libc/stdio/stdio.h" /** * Formats string w/ dynamic memory allocation. diff --git a/libc/stdio/vsprintf.c b/libc/stdio/vsprintf.c index cea4ef7c0..e50d6d731 100644 --- a/libc/stdio/vsprintf.c +++ b/libc/stdio/vsprintf.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/fmt/fmt.h" #include "libc/limits.h" +#include "libc/stdio/stdio.h" /** * Formats string to buffer hopefully large enough w/ vararg state. diff --git a/libc/testlib/comborunner.c b/libc/testlib/comborunner.c index d53656584..b0150ba4b 100644 --- a/libc/testlib/comborunner.c +++ b/libc/testlib/comborunner.c @@ -16,9 +16,9 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/fmt/fmt.h" #include "libc/intrin/safemacros.internal.h" #include "libc/mem/mem.h" +#include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/testlib/testlib.h" diff --git a/libc/testlib/fixturerunner.c b/libc/testlib/fixturerunner.c index 59071ef89..6d9205b33 100644 --- a/libc/testlib/fixturerunner.c +++ b/libc/testlib/fixturerunner.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/fmt/fmt.h" #include "libc/runtime/internal.h" +#include "libc/stdio/stdio.h" #include "libc/sysv/consts/prot.h" #include "libc/testlib/testlib.h" diff --git a/libc/testlib/formatint.c b/libc/testlib/formatint.c index ebbdfc90f..fdfc8b345 100644 --- a/libc/testlib/formatint.c +++ b/libc/testlib/formatint.c @@ -16,10 +16,10 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/fmt/fmt.h" #include "libc/intrin/bits.h" #include "libc/macros.internal.h" #include "libc/mem/mem.h" +#include "libc/stdio/stdio.h" #include "libc/testlib/testlib.h" static size_t sbufi_; diff --git a/test/libc/calls/fchdir_test.c b/test/libc/calls/fchdir_test.c new file mode 100644 index 000000000..f80d8c594 --- /dev/null +++ b/test/libc/calls/fchdir_test.c @@ -0,0 +1,37 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2023 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/mem/gc.internal.h" +#include "libc/sysv/consts/o.h" +#include "libc/testlib/testlib.h" + +char testlib_enable_tmp_setup_teardown; + +TEST(fchdir, test) { + const char *a, *b; + ASSERT_SYS(0, 0, mkdir("dog", 0755)); + ASSERT_SYS(0, 0, chdir("dog")); + ASSERT_NE(NULL, (a = gc(getcwd(0, 0)))); + ASSERT_SYS(0, 3, open(".", O_RDONLY)); + ASSERT_SYS(0, 0, chdir("/")); + ASSERT_SYS(0, 0, fchdir(3)); + ASSERT_NE(NULL, (b = gc(getcwd(0, 0)))); + ASSERT_STREQ(a, b); + ASSERT_SYS(0, 0, close(3)); +} diff --git a/test/libc/calls/setrlimit_test.c b/test/libc/calls/setrlimit_test.c index 637c49ed6..ed89b9c31 100644 --- a/test/libc/calls/setrlimit_test.c +++ b/test/libc/calls/setrlimit_test.c @@ -21,12 +21,12 @@ #include "libc/calls/struct/rlimit.h" #include "libc/dce.h" #include "libc/errno.h" -#include "libc/fmt/fmt.h" #include "libc/intrin/directmap.internal.h" #include "libc/intrin/safemacros.internal.h" #include "libc/limits.h" #include "libc/runtime/runtime.h" #include "libc/stdio/rand.h" +#include "libc/stdio/stdio.h" #include "libc/sysv/consts/auxv.h" #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/o.h" diff --git a/test/libc/calls/symlinkat_test.c b/test/libc/calls/symlinkat_test.c index 436920cf2..1c1ae1437 100644 --- a/test/libc/calls/symlinkat_test.c +++ b/test/libc/calls/symlinkat_test.c @@ -19,11 +19,11 @@ #include "libc/calls/calls.h" #include "libc/calls/struct/stat.h" #include "libc/errno.h" -#include "libc/fmt/fmt.h" #include "libc/fmt/itoa.h" #include "libc/limits.h" #include "libc/runtime/runtime.h" #include "libc/stdio/rand.h" +#include "libc/stdio/stdio.h" #include "libc/sysv/consts/at.h" #include "libc/sysv/consts/s.h" #include "libc/testlib/testlib.h" diff --git a/test/libc/intrin/kprintf_test.c b/test/libc/intrin/kprintf_test.c index 59f44e3f7..23d21e365 100644 --- a/test/libc/intrin/kprintf_test.c +++ b/test/libc/intrin/kprintf_test.c @@ -20,7 +20,6 @@ #include "libc/calls/calls.h" #include "libc/dce.h" #include "libc/errno.h" -#include "libc/fmt/fmt.h" #include "libc/intrin/bits.h" #include "libc/limits.h" #include "libc/log/libfatal.internal.h" @@ -29,6 +28,7 @@ #include "libc/runtime/runtime.h" #include "libc/runtime/symbols.internal.h" #include "libc/stdio/rand.h" +#include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/prot.h" diff --git a/test/libc/log/backtrace_test.c b/test/libc/log/backtrace_test.c index 9b541fe53..3b62b90fd 100644 --- a/test/libc/log/backtrace_test.c +++ b/test/libc/log/backtrace_test.c @@ -91,7 +91,7 @@ TEST(ShowCrashReports, testMemoryLeakCrash) { return; } ASSERT_NE(-1, pipe2(fds, O_CLOEXEC)); - ASSERT_NE(-1, (pid = vfork())); + ASSERT_NE(-1, (pid = fork())); if (!pid) { dup2(fds[1], 1); dup2(fds[1], 2); @@ -191,7 +191,7 @@ TEST(ShowCrashReports, testDivideByZero) { int ws, pid, fds[2]; char *output, buf[512]; ASSERT_NE(-1, pipe2(fds, O_CLOEXEC)); - ASSERT_NE(-1, (pid = vfork())); + ASSERT_NE(-1, (pid = fork())); if (!pid) { dup2(fds[1], 1); dup2(fds[1], 2); @@ -315,7 +315,7 @@ TEST(ShowCrashReports, testBssOverrunCrash) { int ws, pid, fds[2]; char *output, buf[512]; ASSERT_NE(-1, pipe2(fds, O_CLOEXEC)); - ASSERT_NE(-1, (pid = vfork())); + ASSERT_NE(-1, (pid = fork())); if (!pid) { dup2(fds[1], 1); dup2(fds[1], 2); @@ -393,7 +393,7 @@ TEST(ShowCrashReports, testNpeCrash) { int ws, pid, fds[2]; char *output, buf[512]; ASSERT_NE(-1, pipe2(fds, O_CLOEXEC)); - ASSERT_NE(-1, (pid = vfork())); + ASSERT_NE(-1, (pid = fork())); if (!pid) { dup2(fds[1], 1); dup2(fds[1], 2); @@ -452,7 +452,7 @@ TEST(ShowCrashReports, testDataOverrunCrash) { int ws, pid, fds[2]; char *output, buf[512]; ASSERT_NE(-1, pipe2(fds, O_CLOEXEC)); - ASSERT_NE(-1, (pid = vfork())); + ASSERT_NE(-1, (pid = fork())); if (!pid) { dup2(fds[1], 1); dup2(fds[1], 2); @@ -506,7 +506,7 @@ TEST(ShowCrashReports, testNpeCrashAfterFinalize) { int ws, pid, fds[2]; char *output, buf[512]; ASSERT_NE(-1, pipe2(fds, O_CLOEXEC)); - ASSERT_NE(-1, (pid = vfork())); + ASSERT_NE(-1, (pid = fork())); if (!pid) { dup2(fds[1], 1); dup2(fds[1], 2); diff --git a/test/libc/stdio/dirstream_test.c b/test/libc/stdio/dirstream_test.c index a6d9f4e49..24ec09821 100644 --- a/test/libc/stdio/dirstream_test.c +++ b/test/libc/stdio/dirstream_test.c @@ -22,7 +22,6 @@ #include "libc/dce.h" #include "libc/errno.h" #include "libc/fmt/conv.h" -#include "libc/fmt/fmt.h" #include "libc/fmt/itoa.h" #include "libc/intrin/kprintf.h" #include "libc/limits.h" @@ -34,6 +33,7 @@ #include "libc/stdio/append.h" #include "libc/stdio/ftw.h" #include "libc/stdio/rand.h" +#include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/sysv/consts/dt.h" #include "libc/sysv/consts/o.h" diff --git a/test/libc/stdio/palandprintf_test.c b/test/libc/stdio/palandprintf_test.c index 6ca7e26e5..456036b1d 100644 --- a/test/libc/stdio/palandprintf_test.c +++ b/test/libc/stdio/palandprintf_test.c @@ -25,7 +25,6 @@ │ THE SOFTWARE. │ └─────────────────────────────────────────────────────────────────────────────*/ #include "libc/errno.h" -#include "libc/fmt/fmt.h" #include "libc/fmt/itoa.h" #include "libc/intrin/pushpop.internal.h" #include "libc/intrin/safemacros.internal.h" @@ -33,6 +32,7 @@ #include "libc/math.h" #include "libc/mem/gc.h" #include "libc/mem/mem.h" +#include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" diff --git a/test/libc/stdio/snprintf_test.c b/test/libc/stdio/snprintf_test.c index aaaf7cb01..34f21c04d 100644 --- a/test/libc/stdio/snprintf_test.c +++ b/test/libc/stdio/snprintf_test.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/fmt/fmt.h" +#include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/testlib/testlib.h" diff --git a/test/libc/thread/sem_open_test.c b/test/libc/thread/sem_open_test.c index b2e30d7d7..9a2c99ee7 100644 --- a/test/libc/thread/sem_open_test.c +++ b/test/libc/thread/sem_open_test.c @@ -21,11 +21,11 @@ #include "libc/mem/gc.h" #include "libc/mem/mem.h" #include "libc/runtime/runtime.h" -#include "libc/temp.h" #include "libc/str/str.h" #include "libc/sysv/consts/clock.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/sig.h" +#include "libc/temp.h" #include "libc/testlib/subprocess.h" #include "libc/testlib/testlib.h" #include "libc/thread/semaphore.h" @@ -107,7 +107,7 @@ TEST(sem_close, withUnnamedSemaphore_isUndefinedBehavior) { SPAWN(fork); IgnoreStderr(); sem_close(&sem); - EXITS(128 + SIGABRT); // see __assert_fail + TERMS(SIGABRT); // see __assert_fail ASSERT_SYS(0, 0, sem_destroy(&sem)); } @@ -118,7 +118,7 @@ TEST(sem_destroy, withNamedSemaphore_isUndefinedBehavior) { SPAWN(fork); IgnoreStderr(); sem_destroy(sem); - EXITS(128 + SIGABRT); // see __assert_fail + TERMS(SIGABRT); // see __assert_fail ASSERT_SYS(0, 0, sem_unlink("/boop")); ASSERT_SYS(0, 0, sem_close(sem)); } diff --git a/test/libc/tinymath/complex_test.c b/test/libc/tinymath/complex_test.c index 70e6f493e..874292a17 100644 --- a/test/libc/tinymath/complex_test.c +++ b/test/libc/tinymath/complex_test.c @@ -17,7 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/complex.h" -#include "libc/fmt/fmt.h" +#include "libc/stdio/stdio.h" #include "libc/testlib/testlib.h" TEST(complex, test) { diff --git a/test/libc/tinymath/strtod_test.c b/test/libc/tinymath/strtod_test.c index bdb559108..74b0225b3 100644 --- a/test/libc/tinymath/strtod_test.c +++ b/test/libc/tinymath/strtod_test.c @@ -17,11 +17,11 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/fmt/conv.h" -#include "libc/fmt/fmt.h" #include "libc/macros.internal.h" #include "libc/mem/gc.h" #include "libc/mem/mem.h" #include "libc/runtime/fenv.h" +#include "libc/stdio/stdio.h" #include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" #include "libc/x/xasprintf.h" diff --git a/third_party/argon2/encoding.c b/third_party/argon2/encoding.c index cd9bff6a4..64d3bb3ed 100644 --- a/third_party/argon2/encoding.c +++ b/third_party/argon2/encoding.c @@ -15,11 +15,12 @@ │ - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 │ │ │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "third_party/argon2/encoding.h" #include "libc/fmt/fmt.h" #include "libc/limits.h" +#include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "third_party/argon2/core.h" -#include "third_party/argon2/encoding.h" asm(".ident\t\"\\n\\n\ argon2 (CC0 or Apache2)\\n\ diff --git a/third_party/finger/util.c b/third_party/finger/util.c index ffdfeb1bd..1478ccc4c 100644 --- a/third_party/finger/util.c +++ b/third_party/finger/util.c @@ -37,10 +37,10 @@ #include "libc/calls/struct/stat.h" #include "libc/calls/struct/stat.macros.h" #include "libc/errno.h" -#include "libc/fmt/fmt.h" #include "libc/mem/mem.h" #include "libc/paths.h" #include "libc/runtime/runtime.h" +#include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/sysv/consts/o.h" #include "third_party/finger/finger.h" diff --git a/third_party/lua/luaconf.h b/third_party/lua/luaconf.h index 74ef92328..4a30d889c 100644 --- a/third_party/lua/luaconf.h +++ b/third_party/lua/luaconf.h @@ -3,8 +3,8 @@ #include "libc/assert.h" #include "libc/dce.h" #include "libc/fmt/conv.h" -#include "libc/fmt/fmt.h" #include "libc/limits.h" +#include "libc/stdio/stdio.h" #include "libc/str/unicode.h" #define LUA_USE_POSIX diff --git a/third_party/musl/strfmon.c b/third_party/musl/strfmon.c index e11e6c265..fdcb98705 100644 --- a/third_party/musl/strfmon.c +++ b/third_party/musl/strfmon.c @@ -26,7 +26,7 @@ │ │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/errno.h" -#include "libc/fmt/fmt.h" +#include "libc/stdio/stdio.h" #include "libc/str/locale.h" #include "libc/str/str.h" #include "libc/thread/tls.h" diff --git a/third_party/regex/regerror.c b/third_party/regex/regerror.c index 9814e1adc..5e92e1185 100644 --- a/third_party/regex/regerror.c +++ b/third_party/regex/regerror.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/fmt/fmt.h" #include "libc/intrin/safemacros.internal.h" +#include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "third_party/regex/regex.h" diff --git a/third_party/zlib/gz/gzlib.c b/third_party/zlib/gz/gzlib.c index a1a98eee4..65ac20bbd 100644 --- a/third_party/zlib/gz/gzlib.c +++ b/third_party/zlib/gz/gzlib.c @@ -6,9 +6,9 @@ * For conditions of distribution and use, see copyright notice in zlib.h */ #include "libc/calls/calls.h" -#include "libc/fmt/fmt.h" #include "libc/limits.h" #include "libc/mem/mem.h" +#include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/sysv/consts/o.h" #include "third_party/zlib/gz/gzguts.inc" diff --git a/third_party/zlib/gz/gzwrite.c b/third_party/zlib/gz/gzwrite.c index 36d4b2f81..e91976b4b 100644 --- a/third_party/zlib/gz/gzwrite.c +++ b/third_party/zlib/gz/gzwrite.c @@ -6,8 +6,8 @@ * For conditions of distribution and use, see copyright notice in zlib.h */ #include "libc/calls/calls.h" -#include "libc/fmt/fmt.h" #include "libc/mem/mem.h" +#include "libc/stdio/stdio.h" #include "third_party/zlib/gz/gzguts.inc" // clang-format off diff --git a/tool/build/lib/buffer.c b/tool/build/lib/buffer.c index 41322d1d2..7c85f5215 100644 --- a/tool/build/lib/buffer.c +++ b/tool/build/lib/buffer.c @@ -19,10 +19,10 @@ #include "tool/build/lib/buffer.h" #include "libc/calls/calls.h" #include "libc/errno.h" -#include "libc/fmt/fmt.h" #include "libc/macros.internal.h" #include "libc/mem/arraylist2.internal.h" #include "libc/mem/mem.h" +#include "libc/stdio/stdio.h" #include "libc/str/str.h" /* TODO(jart): replace with new append*() library */ diff --git a/tool/decode/lib/flagger.c b/tool/decode/lib/flagger.c index 99d77e7b7..9ff26ad31 100644 --- a/tool/decode/lib/flagger.c +++ b/tool/decode/lib/flagger.c @@ -17,9 +17,9 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "tool/decode/lib/flagger.h" -#include "libc/fmt/fmt.h" #include "libc/mem/arraylist2.internal.h" #include "libc/mem/mem.h" +#include "libc/stdio/stdio.h" #include "libc/str/str.h" /**