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" /**