diff --git a/examples/ttyinfo.c b/examples/ttyinfo.c index 801a5858a..ac86bab93 100644 --- a/examples/ttyinfo.c +++ b/examples/ttyinfo.c @@ -16,6 +16,7 @@ #include "libc/log/check.h" #include "libc/log/log.h" #include "libc/runtime/runtime.h" +#include "libc/stdio/dprintf.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/sysv/consts/exit.h" @@ -62,15 +63,25 @@ int rawmode(void) { once = true; } memcpy(&t, &oldterm, sizeof(t)); + t.c_cc[VMIN] = 1; t.c_cc[VTIME] = 1; + + // emacs does the following to remap ctrl-c to ctrl-g in termios + // t.c_cc[VINTR] = CTRL('G'); + // it can be restored using + // (set-quit-char (logxor ?C 0100)) + // but we are able to polyfill the remapping on windows + // please note this is a moot point b/c ISIG is cleared + t.c_iflag &= ~(INPCK | ISTRIP | PARMRK | INLCR | IGNCR | ICRNL | IXON | IGNBRK | BRKINT); t.c_lflag &= ~(IEXTEN | ICANON | ECHO | ECHONL | ISIG); t.c_cflag &= ~(CSIZE | PARENB); - t.c_oflag &= ~OPOST; + t.c_oflag |= OPOST | ONLCR; t.c_cflag |= CS8; t.c_iflag |= IUTF8; + tcsetattr(1, TCSANOW, &t); WRITE(1, ENABLE_SAFE_PASTE); WRITE(1, ENABLE_MOUSE_TRACKING); @@ -125,8 +136,16 @@ const char *describemouseevent(int e) { return buf + 1; } +// change the code above to enable ISIG if you want to trigger this +// then press ctrl-c or ctrl-\ in your pseudoteletypewriter console +void OnSignalThatWontEintrRead(int sig) { + dprintf(1, "got %s\n", strsignal(sig)); +} + int main(int argc, char *argv[]) { int e, c, y, x, n, yn, xn; + signal(SIGINT, OnSignalThatWontEintrRead); + signal(SIGQUIT, OnSignalThatWontEintrRead); xsigaction(SIGTERM, onkilled, 0, 0, NULL); xsigaction(SIGWINCH, onresize, 0, 0, NULL); xsigaction(SIGCONT, onresize, 0, 0, NULL); diff --git a/libc/calls/close-nt.c b/libc/calls/close-nt.c index 7076f1ec9..8f6d656b6 100644 --- a/libc/calls/close-nt.c +++ b/libc/calls/close-nt.c @@ -47,9 +47,11 @@ textwindows int sys_close_nt(struct Fd *fd, int fildes) { // if this file descriptor is wrapped in a named pipe worker thread // then we need to close our copy of the worker thread handle. it's // also required that whatever install a worker use malloc, so free - if (!CloseHandle(fd->handle)) ok = false; - if (fd->kind == kFdConsole && fd->extra && fd->extra != -1) { - if (!CloseHandle(fd->extra)) ok = false; + if (!fd->dontclose) { + if (!CloseHandle(fd->handle)) ok = false; + if (fd->kind == kFdConsole && fd->extra && fd->extra != -1) { + if (!CloseHandle(fd->extra)) ok = false; + } } return ok ? 0 : -1; diff --git a/libc/calls/faccessat.c b/libc/calls/faccessat.c index 2f20905a7..ae979fc21 100644 --- a/libc/calls/faccessat.c +++ b/libc/calls/faccessat.c @@ -28,6 +28,7 @@ #include "libc/intrin/weaken.h" #include "libc/runtime/zipos.internal.h" #include "libc/sysv/consts/at.h" +#include "libc/sysv/consts/ok.h" #include "libc/sysv/errfuns.h" /** @@ -39,9 +40,9 @@ * @param amode can be `R_OK`, `W_OK`, `X_OK`, or `F_OK` * @param flags can have `AT_EACCESS` and/or `AT_SYMLINK_NOFOLLOW` * @return 0 if ok, or -1 and sets errno - * @raise EINVAL if `mode` has bad value + * @raise EINVAL if `amode` or `flags` had invalid values * @raise EPERM if pledge() is in play without rpath promise - * @raise EACCES if access for requested `mode` would be denied + * @raise EACCES if access for requested `amode` would be denied * @raise ENOTDIR if a directory component in `path` exists as non-directory * @raise ENOENT if component of `path` doesn't exist or `path` is empty * @raise ENOTSUP if `path` is a zip file and `dirfd` isn't `AT_FDCWD` @@ -53,6 +54,9 @@ int faccessat(int dirfd, const char *path, int amode, int flags) { struct ZiposUri zipname; if (IsAsan() && !__asan_is_valid_str(path)) { rc = efault(); + } else if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_EACCESS)) || + !(amode == F_OK || !(amode & ~(R_OK | W_OK | X_OK)))) { + rc = einval(); } else if (__isfdkind(dirfd, kFdZip)) { rc = enotsup(); } else if (_weaken(__zipos_open) && diff --git a/libc/calls/fcntl-nt.c b/libc/calls/fcntl-nt.c index 0fbed453e..3866824ce 100644 --- a/libc/calls/fcntl-nt.c +++ b/libc/calls/fcntl-nt.c @@ -349,7 +349,9 @@ textwindows int sys_fcntl_nt(int fd, int cmd, uintptr_t arg) { uint32_t flags; int access_mode_flags = O_ACCMODE | O_APPEND | O_ASYNC | O_DIRECT | O_NOATIME | O_NONBLOCK | O_RANDOM | O_SEQUENTIAL; - if (__isfdkind(fd, kFdFile) || __isfdkind(fd, kFdSocket)) { + if (__isfdkind(fd, kFdFile) || // + __isfdkind(fd, kFdSocket) || // + __isfdkind(fd, kFdConsole)) { if (cmd == F_GETFL) { rc = g_fds.p[fd].flags & access_mode_flags; } else if (cmd == F_SETFL) { diff --git a/libc/calls/fstatat.c b/libc/calls/fstatat.c index 536db58e0..43b8a233a 100644 --- a/libc/calls/fstatat.c +++ b/libc/calls/fstatat.c @@ -58,6 +58,8 @@ int fstatat(int dirfd, const char *path, struct stat *st, int flags) { struct ZiposUri zipname; if (IsAsan() && !__asan_is_valid(st, sizeof(*st))) { rc = efault(); + } else if (flags & ~AT_SYMLINK_NOFOLLOW) { + return einval(); } else if (__isfdkind(dirfd, kFdZip)) { STRACE("zipos dirfd not supported yet"); rc = einval(); diff --git a/libc/calls/ntaccesscheck.c b/libc/calls/ntaccesscheck.c index 815437c1a..5f1d06ef7 100644 --- a/libc/calls/ntaccesscheck.c +++ b/libc/calls/ntaccesscheck.c @@ -39,13 +39,11 @@ #include "libc/sysv/consts/ok.h" #include "libc/sysv/errfuns.h" +// TODO: what does this code do with symlinks? + /** * Asks Microsoft if we're authorized to use a folder or file. * - * Implementation Details: MSDN documentation imposes no limit on the - * internal size of SECURITY_DESCRIPTOR, which we are responsible for - * allocating. We've selected 1024 which shall hopefully be adequate. - * * @param flags can have R_OK, W_OK, X_OK, etc. * @return 0 if authorized, or -1 w/ errno * @see https://blog.aaronballman.com/2011/08/how-to-check-access-rights/ @@ -86,25 +84,28 @@ TryAgain: &hToken)) { if (DuplicateToken(hToken, kNtSecurityImpersonation, &hImpersonatedToken)) { + if (flags == kNtGenericExecute) { // X_OK + flags |= kNtGenericRead; // R_OK + } if (AccessCheck(s, hImpersonatedToken, flags, &mapping, &privileges, &privsize, &granted, &result)) { if (result || flags == F_OK) { rc = 0; } else { - STRACE("ntaccesscheck finale failed %d %d", result, flags); + NTTRACE("ntaccesscheck finale failed %d %x", result, flags); rc = eacces(); } } else { rc = __winerr(); - STRACE("%s(%#hs) failed: %m", "AccessCheck", pathname); + NTTRACE("%s(%#hs) failed: %m", "AccessCheck", pathname); } } else { rc = __winerr(); - STRACE("%s(%#hs) failed: %m", "DuplicateToken", pathname); + NTTRACE("%s(%#hs) failed: %m", "DuplicateToken", pathname); } } else { rc = __winerr(); - STRACE("%s(%#hs) failed: %m", "OpenProcessToken", pathname); + NTTRACE("%s(%#hs) failed: %m", "OpenProcessToken", pathname); } } else { e = GetLastError(); @@ -114,11 +115,11 @@ TryAgain: goto TryAgain; } else { rc = enomem(); - STRACE("%s(%#hs) failed: %m", "GetFileSecurity", pathname); + NTTRACE("%s(%#hs) failed: %m", "GetFileSecurity", pathname); } } else { errno = e; - STRACE("%s(%#hs) failed: %m", "GetFileSecurity", pathname); + NTTRACE("%s(%#hs) failed: %m", "GetFileSecurity", pathname); rc = -1; } } diff --git a/libc/calls/open-nt.c b/libc/calls/open-nt.c index 66da47629..e70e31a85 100644 --- a/libc/calls/open-nt.c +++ b/libc/calls/open-nt.c @@ -22,7 +22,6 @@ #include "libc/calls/state.internal.h" #include "libc/calls/syscall-nt.internal.h" #include "libc/calls/syscall_support-nt.internal.h" -#include "libc/intrin/kprintf.h" #include "libc/nt/createfile.h" #include "libc/nt/enum/fileflagandattributes.h" #include "libc/nt/enum/filetype.h" @@ -31,6 +30,7 @@ #include "libc/str/str.h" #include "libc/sysv/consts/fileno.h" #include "libc/sysv/consts/o.h" +#include "libc/sysv/errfuns.h" static textwindows int64_t sys_open_nt_impl(int dirfd, const char *path, uint32_t flags, int32_t mode, @@ -54,8 +54,10 @@ static textwindows int sys_open_nt_console(int dirfd, size_t fd) { if (GetFileType(g_fds.p[STDIN_FILENO].handle) == kNtFileTypeChar && GetFileType(g_fds.p[STDOUT_FILENO].handle) == kNtFileTypeChar) { + // 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[fd].dontclose = true; // don't call CloseHandle() upon close() } else if ((g_fds.p[fd].handle = sys_open_nt_impl( dirfd, mp->conin, (flags & ~O_ACCMODE) | O_RDONLY, mode, kNtFileFlagOverlapped)) != -1) { diff --git a/libc/calls/read-nt.c b/libc/calls/read-nt.c index e3fdcbb4d..bb488fb19 100644 --- a/libc/calls/read-nt.c +++ b/libc/calls/read-nt.c @@ -26,6 +26,7 @@ #include "libc/calls/syscall_support-nt.internal.h" #include "libc/calls/wincrash.internal.h" #include "libc/errno.h" +#include "libc/intrin/nomultics.internal.h" #include "libc/intrin/strace.internal.h" #include "libc/macros.internal.h" #include "libc/nt/enum/filetype.h" @@ -40,23 +41,63 @@ #include "libc/nt/thread.h" #include "libc/str/str.h" #include "libc/sysv/consts/o.h" +#include "libc/sysv/consts/sicode.h" +#include "libc/sysv/consts/sig.h" +#include "libc/sysv/consts/termios.h" #include "libc/sysv/errfuns.h" +#ifdef __x86_64__ + +enum Action { + DO_NOTHING, + DO_RESTART, + DO_EINTR, +}; + static textwindows void sys_read_nt_abort(int64_t handle, struct NtOverlapped *overlapped) { unassert(CancelIoEx(handle, overlapped) || GetLastError() == kNtErrorNotFound); } -static textwindows void MungeTerminalInput(struct Fd *fd, char *p, size_t n) { - if (!(fd->ttymagic & kFdTtyNoCr2Nl)) { - size_t i; - for (i = 0; i < n; ++i) { +static textwindows int MungeTerminalInput(char *p, uint32_t *n) { + size_t i, j; + if (!(__ttymagic & kFdTtyNoCr2Nl)) { + for (i = 0; i < *n; ++i) { if (p[i] == '\r') { p[i] = '\n'; } } } + if (!(__ttymagic & kFdTtyNoIsigs)) { + bool delivered = false; + bool got_vintr = false; + bool got_vquit = false; + for (j = i = 0; i < *n; ++i) { + if (__vintr != _POSIX_VDISABLE && p[i] == __vintr) { + got_vintr = true; + } else if (__vquit != _POSIX_VDISABLE && p[i] == __vquit) { + got_vquit = true; + } else { + p[j++] = p[i]; + } + } + if (got_vintr) { + delivered |= __sig_handle(0, SIGINT, SI_KERNEL, 0); + } + if (got_vquit) { + delivered |= __sig_handle(0, SIGQUIT, SI_KERNEL, 0); + } + if (*n && !j) { + if (delivered) { + return DO_EINTR; + } else { + return DO_RESTART; + } + } + *n = j; + } + return DO_NOTHING; } // Manual CMD.EXE echoing for when !ICANON && ECHO is the case. @@ -67,7 +108,7 @@ static textwindows void EchoTerminalInput(struct Fd *fd, char *p, size_t n) { } else { hOutput = g_fds.p[1].handle; } - if (fd->ttymagic & kFdTtyEchoRaw) { + if (__ttymagic & kFdTtyEchoRaw) { WriteFile(hOutput, p, n, 0, 0); } else { size_t i; @@ -93,6 +134,7 @@ static textwindows ssize_t sys_read_nt_impl(struct Fd *fd, void *data, int filetype; int64_t handle; int abort_errno = EAGAIN; +StartOver: size = MIN(size, 0x7ffff000); handle = __resolve_stdin_handle(fd->handle); filetype = GetFileType(handle); @@ -107,9 +149,9 @@ static textwindows ssize_t sys_read_nt_impl(struct Fd *fd, void *data, // since for overlapped i/o, we always use GetOverlappedResult ok = ReadFile(handle, data, size, 0, &overlap); if (!ok && GetLastError() == kNtErrorIoPending) { - // i/o operation is in flight; blocking is unavoidable - // if we're in non-blocking mode, then immediately abort - // if an interrupt is pending, then abort before waiting + // 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) { sys_read_nt_abort(handle, &overlap); @@ -161,11 +203,23 @@ static textwindows ssize_t sys_read_nt_impl(struct Fd *fd, void *data, unassert(SetFilePointerEx(handle, position, 0, SEEK_SET)); } if (ok) { - if (fd->ttymagic & kFdTtyMunging) { - MungeTerminalInput(fd, data, got); - } - if (fd->ttymagic & kFdTtyEchoing) { - EchoTerminalInput(fd, data, got); + if (g_fds.stdin.handle ? fd->handle == g_fds.stdin.handle + : fd->handle == g_fds.p[0].handle) { + if (__ttymagic & kFdTtyMunging) { + switch (MungeTerminalInput(data, &got)) { + case DO_NOTHING: + break; + case DO_RESTART: + goto StartOver; + case DO_EINTR: + return eintr(); + default: + __builtin_unreachable(); + } + } + if (__ttymagic & kFdTtyEchoing) { + EchoTerminalInput(fd, data, got); + } } return got; } @@ -207,3 +261,5 @@ textwindows ssize_t sys_read_nt(struct Fd *fd, const struct iovec *iov, return sys_read_nt_impl(fd, NULL, 0, opt_offset); } } + +#endif /* __x86_64__ */ diff --git a/libc/calls/read.c b/libc/calls/read.c index fc2914b9c..e165256eb 100644 --- a/libc/calls/read.c +++ b/libc/calls/read.c @@ -66,25 +66,27 @@ ssize_t read(int fd, void *buf, size_t size) { ssize_t rc; BEGIN_CANCELLATION_POINT; - if (fd >= 0) { - if ((!buf && size) || (IsAsan() && !__asan_is_valid(buf, size))) { - rc = efault(); - } else if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) { - rc = _weaken(__zipos_read)( - (struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, - &(struct iovec){buf, size}, 1, -1); - } else if (!IsWindows() && !IsMetal()) { - rc = sys_read(fd, buf, 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); - } else { - rc = sys_readv_nt(g_fds.p + fd, &(struct iovec){buf, size}, 1); - } - } else { + + if (fd < 0) { rc = ebadf(); + } else if ((!buf && size) || (IsAsan() && !__asan_is_valid(buf, size))) { + rc = efault(); + } else if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) { + rc = _weaken(__zipos_read)( + (struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, + &(struct iovec){buf, size}, 1, -1); + } else if (IsLinux() || IsXnu() || IsFreebsd() || IsOpenbsd() || IsNetbsd()) { + rc = sys_read(fd, buf, 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); + } else if (IsWindows()) { + rc = sys_readv_nt(g_fds.p + fd, &(struct iovec){buf, size}, 1); + } else { + rc = enosys(); } + END_CANCELLATION_POINT; DATATRACE("read(%d, [%#.*hhs%s], %'zu) → %'zd% m", fd, (int)MAX(0, MIN(40, rc)), buf, rc > 40 ? "..." : "", size, rc); diff --git a/libc/calls/readv-nt.c b/libc/calls/readv-nt.c index aa98b04a3..c15301039 100644 --- a/libc/calls/readv-nt.c +++ b/libc/calls/readv-nt.c @@ -22,6 +22,8 @@ #include "libc/sock/syscall_fd.internal.h" #include "libc/sysv/errfuns.h" +#ifdef __x86_64__ + textwindows ssize_t sys_readv_nt(struct Fd *fd, const struct iovec *iov, int iovlen) { switch (fd->kind) { @@ -34,3 +36,5 @@ textwindows ssize_t sys_readv_nt(struct Fd *fd, const struct iovec *iov, return ebadf(); } } + +#endif /* __x86_64__ */ diff --git a/libc/calls/readv.c b/libc/calls/readv.c index 9c2d84d7a..67a9f0fb1 100644 --- a/libc/calls/readv.c +++ b/libc/calls/readv.c @@ -50,29 +50,29 @@ ssize_t readv(int fd, const struct iovec *iov, int iovlen) { ssize_t rc; BEGIN_CANCELLATION_POINT; - if (fd >= 0 && iovlen >= 0) { - if (IsAsan() && !__asan_is_valid_iov(iov, iovlen)) { - rc = efault(); - } else if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) { - rc = _weaken(__zipos_read)( - (struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, iov, iovlen, -1); - } else if (!IsWindows() && !IsMetal()) { - if (iovlen == 1) { - rc = sys_read(fd, iov[0].iov_base, iov[0].iov_len); - } else { - rc = sys_readv(fd, iov, iovlen); - } - } else if (fd >= g_fds.n) { - rc = ebadf(); - } else if (IsMetal()) { - rc = sys_readv_metal(g_fds.p + fd, iov, iovlen); - } else { - rc = sys_readv_nt(g_fds.p + fd, iov, iovlen); - } - } else if (fd < 0) { + if (fd < 0) { rc = ebadf(); - } else { + } else if (iovlen < 0) { rc = einval(); + } else if (IsAsan() && !__asan_is_valid_iov(iov, iovlen)) { + rc = efault(); + } else if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) { + rc = _weaken(__zipos_read)( + (struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, iov, iovlen, -1); + } else if (IsLinux() || IsXnu() || IsFreebsd() || IsOpenbsd() || IsNetbsd()) { + if (iovlen == 1) { + rc = sys_read(fd, iov[0].iov_base, iov[0].iov_len); + } else { + rc = sys_readv(fd, iov, iovlen); + } + } else if (fd >= g_fds.n) { + rc = ebadf(); + } else if (IsMetal()) { + rc = sys_readv_metal(g_fds.p + fd, iov, iovlen); + } else if (IsWindows()) { + rc = sys_readv_nt(g_fds.p + fd, iov, iovlen); + } else { + rc = enosys(); } END_CANCELLATION_POINT; diff --git a/libc/calls/struct/fd.internal.h b/libc/calls/struct/fd.internal.h index 126787c33..381c632f5 100644 --- a/libc/calls/struct/fd.internal.h +++ b/libc/calls/struct/fd.internal.h @@ -17,11 +17,12 @@ COSMOPOLITAN_C_START_ #define kFdTtyEchoRaw 2 /* don't ^X visualize control codes */ #define kFdTtyMunging 4 /* enable input / output remappings */ #define kFdTtyNoCr2Nl 8 /* don't map \r → \n (a.k.a !ICRNL) */ +#define kFdTtyNoIsigs 16 struct Fd { char kind; bool zombie; - char ttymagic; + bool dontclose; unsigned flags; unsigned mode; int64_t handle; diff --git a/libc/calls/struct/termios.internal.h b/libc/calls/struct/termios.internal.h new file mode 100644 index 000000000..244418323 --- /dev/null +++ b/libc/calls/struct/termios.internal.h @@ -0,0 +1,14 @@ +#ifndef COSMOPOLITAN_LIBC_CALLS_STRUCT_TERMIOS_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_CALLS_STRUCT_TERMIOS_INTERNAL_H_ +#include "libc/calls/struct/termios.h" +#include "libc/mem/alloca.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +const char *DescribeTermios(char[1024], ssize_t, struct termios *); + +#define DescribeTermios(rc, tio) DescribeTermios(alloca(1024), rc, tio) + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_CALLS_STRUCT_TERMIOS_INTERNAL_H_ */ diff --git a/libc/calls/tcgetattr-nt.c b/libc/calls/tcgetattr-nt.c index 18464cfd1..5607d4e15 100644 --- a/libc/calls/tcgetattr-nt.c +++ b/libc/calls/tcgetattr-nt.c @@ -21,6 +21,7 @@ #include "libc/calls/struct/fd.internal.h" #include "libc/calls/struct/termios.h" #include "libc/calls/ttydefaults.h" +#include "libc/intrin/nomultics.internal.h" #include "libc/nt/console.h" #include "libc/nt/enum/consolemodeflags.h" #include "libc/nt/struct/consolescreenbufferinfoex.h" @@ -31,20 +32,19 @@ #include "libc/sysv/errfuns.h" textwindows int tcgetattr_nt(int ignored, struct termios *tio) { - int ttymagic; int64_t in, out; bool32 inok, outok; - uint32_t inmode, outmode; + 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)); tio->c_cc[VMIN] = 1; - tio->c_cc[VINTR] = CTRL('C'); - tio->c_cc[VQUIT] = CTRL('\\'); - tio->c_cc[VWERASE] = CTRL('?'); // windows swaps these :'( - tio->c_cc[VERASE] = CTRL('H'); // windows swaps these :'( + 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'); @@ -57,26 +57,30 @@ textwindows int tcgetattr_nt(int ignored, struct termios *tio) { tio->c_iflag = IUTF8; tio->c_lflag = ECHOE; - tio->c_cflag = CS8; + tio->c_cflag = CS8 | CREAD; tio->_c_ispeed = B38400; tio->_c_ospeed = B38400; if (inok) { - ttymagic = g_fds.p[0].ttymagic; if (inmode & kNtEnableLineInput) { tio->c_lflag |= ICANON; } - if ((inmode & kNtEnableEchoInput) || (ttymagic & kFdTtyEchoing)) { + // kNtEnableEchoInput only works with kNtEnableLineInput enabled. + if ((inmode & kNtEnableEchoInput) || (__ttymagic & kFdTtyEchoing)) { tio->c_lflag |= ECHO; } - if (!(ttymagic & kFdTtyEchoRaw)) { + // The Windows console itself always echos control codes as ASCII. + if ((inmode & kNtEnableEchoInput) || !(__ttymagic & kFdTtyEchoRaw)) { tio->c_lflag |= ECHOCTL; } - if (!(ttymagic & kFdTtyNoCr2Nl)) { + if (!(__ttymagic & kFdTtyNoCr2Nl)) { tio->c_iflag |= ICRNL; } - if (inmode & kNtEnableProcessedInput) { - tio->c_lflag |= IEXTEN | ISIG; + if (!(__ttymagic & kFdTtyNoIsigs)) { + tio->c_lflag |= ISIG; + } + if ((inmode & kNtEnableProcessedInput) || (__ttymagic & kFdTtyMunging)) { + tio->c_lflag |= IEXTEN; } } diff --git a/libc/calls/tcgetattr.c b/libc/calls/tcgetattr.c index 54c3cdba6..52fcea9d2 100644 --- a/libc/calls/tcgetattr.c +++ b/libc/calls/tcgetattr.c @@ -19,6 +19,7 @@ #include "libc/calls/internal.h" #include "libc/calls/struct/metatermios.internal.h" #include "libc/calls/struct/termios.h" +#include "libc/calls/struct/termios.internal.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/termios.internal.h" #include "libc/calls/ttydefaults.h" @@ -105,6 +106,6 @@ int tcgetattr(int fd, struct termios *tio) { } else { rc = enosys(); } - STRACE("tcgetattr(%d, %p) → %d% m", fd, tio, rc); + STRACE("tcgetattr(%d, [%s]) → %d% m", fd, DescribeTermios(rc, tio), rc); return rc; } diff --git a/libc/calls/tcsetattr-nt.c b/libc/calls/tcsetattr-nt.c index fc40cae63..9a9363eed 100644 --- a/libc/calls/tcsetattr-nt.c +++ b/libc/calls/tcsetattr-nt.c @@ -19,7 +19,9 @@ #include "libc/calls/internal.h" #include "libc/calls/struct/metatermios.internal.h" #include "libc/calls/termios.internal.h" +#include "libc/calls/ttydefaults.h" #include "libc/intrin/describeflags.internal.h" +#include "libc/intrin/nomultics.internal.h" #include "libc/intrin/strace.internal.h" #include "libc/nt/console.h" #include "libc/nt/enum/consolemodeflags.h" @@ -31,7 +33,7 @@ textwindows int tcsetattr_nt(int fd, int opt, const struct termios *tio) { bool32 ok; - int ttymagic; + int infd; int64_t hInput, hOutput; uint32_t inmode, outmode; if (__isfdkind(fd, kFdConsole)) { @@ -56,21 +58,21 @@ textwindows int tcsetattr_nt(int fd, int opt, const struct termios *tio) { inmode &= ~(kNtEnableLineInput | kNtEnableEchoInput | kNtEnableProcessedInput); inmode |= kNtEnableWindowInput; - ttymagic = 0; + __ttymagic = 0; if (tio->c_lflag & ICANON) { inmode |= kNtEnableLineInput; } else { - ttymagic |= kFdTtyMunging; + __ttymagic |= kFdTtyMunging; if (tio->c_cc[VMIN] != 1) { STRACE("tcsetattr c_cc[VMIN] must be 1 on Windows"); return einval(); } } if (!(tio->c_iflag & ICRNL)) { - ttymagic |= kFdTtyNoCr2Nl; + __ttymagic |= kFdTtyNoCr2Nl; } if (!(tio->c_lflag & ECHOCTL)) { - ttymagic |= kFdTtyEchoRaw; + __ttymagic |= kFdTtyEchoRaw; } if (tio->c_lflag & ECHO) { // "kNtEnableEchoInput can be used only if the @@ -82,16 +84,21 @@ textwindows int tcsetattr_nt(int fd, int opt, const struct termios *tio) { // magically write(1) to simulate echoing. This normally // visualizes control codes, e.g. \r → ^M unless ECHOCTL // hasn't been specified. - ttymagic |= kFdTtyEchoing; + __ttymagic |= kFdTtyEchoing; } } - if (tio->c_lflag & (IEXTEN | ISIG)) { - inmode |= kNtEnableProcessedInput; + if (!(tio->c_lflag & ISIG)) { + __ttymagic |= kFdTtyNoIsigs; } if (IsAtLeastWindows10()) { inmode |= kNtEnableVirtualTerminalInput; } - g_fds.p[fd].ttymagic = ttymagic; + __vintr = tio->c_cc[VINTR]; + __vquit = tio->c_cc[VQUIT]; + if ((tio->c_lflag & ISIG) && // + tio->c_cc[VINTR] == CTRL('C')) { + inmode |= kNtEnableProcessedInput; + } ok = SetConsoleMode(hInput, inmode); (void)ok; NTTRACE("SetConsoleMode(%p, %s) → %hhhd", hInput, diff --git a/libc/calls/tcsetattr.c b/libc/calls/tcsetattr.c index f0f48275d..f68daab8f 100644 --- a/libc/calls/tcsetattr.c +++ b/libc/calls/tcsetattr.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" #include "libc/calls/struct/metatermios.internal.h" +#include "libc/calls/struct/termios.internal.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/termios.h" #include "libc/calls/termios.internal.h" @@ -92,7 +93,7 @@ static int tcsetattr_impl(int fd, int opt, const struct termios *tio) { int tcsetattr(int fd, int opt, const struct termios *tio) { int rc; rc = tcsetattr_impl(fd, opt, tio); - STRACE("tcsetattr(%d, %s, %p) → %d% m", fd, DescribeTcsa(alloca(12), opt), - tio, rc); + STRACE("tcsetattr(%d, %s, %s) → %d% m", fd, DescribeTcsa(alloca(12), opt), + DescribeTermios(0, tio), rc); return rc; } diff --git a/libc/calls/write.c b/libc/calls/write.c index 803b3aff6..504cc52cb 100644 --- a/libc/calls/write.c +++ b/libc/calls/write.c @@ -68,22 +68,22 @@ ssize_t write(int fd, const void *buf, size_t size) { ssize_t rc; BEGIN_CANCELLATION_POINT; - if (fd >= 0) { - if ((!buf && size) || (IsAsan() && !__asan_is_valid(buf, size))) { - rc = efault(); - } else if (__isfdkind(fd, kFdZip)) { - rc = ebadf(); - } else if (!IsWindows() && !IsMetal()) { - rc = sys_write(fd, buf, size); - } else if (fd >= g_fds.n) { - rc = ebadf(); - } else if (IsMetal()) { - rc = sys_writev_metal(g_fds.p + fd, &(struct iovec){buf, size}, 1); - } else { - rc = sys_writev_nt(fd, &(struct iovec){buf, size}, 1); - } - } else { + if (fd < 0) { rc = ebadf(); + } else if ((!buf && size) || (IsAsan() && !__asan_is_valid(buf, size))) { + rc = efault(); + } else if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) { + rc = ebadf(); // posix specifies this when not open()'d for writing + } else if (IsLinux() || IsXnu() || IsFreebsd() || IsOpenbsd() || IsNetbsd()) { + rc = sys_write(fd, buf, size); + } else if (fd >= g_fds.n) { + rc = ebadf(); + } else if (IsMetal()) { + rc = sys_writev_metal(g_fds.p + fd, &(struct iovec){buf, size}, 1); + } else if (IsWindows()) { + rc = sys_writev_nt(fd, &(struct iovec){buf, size}, 1); + } else { + rc = enosys(); } END_CANCELLATION_POINT; diff --git a/libc/calls/writev.c b/libc/calls/writev.c index 0c4cf128b..693f09e0a 100644 --- a/libc/calls/writev.c +++ b/libc/calls/writev.c @@ -53,28 +53,28 @@ ssize_t writev(int fd, const struct iovec *iov, int iovlen) { ssize_t rc; BEGIN_CANCELLATION_POINT; - if (fd >= 0 && iovlen >= 0) { - if (IsAsan() && !__asan_is_valid_iov(iov, iovlen)) { - rc = efault(); - } else if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) { - rc = ebadf(); - } else if (!IsWindows() && !IsMetal()) { - if (iovlen == 1) { - rc = sys_write(fd, iov[0].iov_base, iov[0].iov_len); - } else { - rc = sys_writev(fd, iov, iovlen); - } - } else if (fd >= g_fds.n) { - rc = ebadf(); - } else if (IsMetal()) { - rc = sys_writev_metal(g_fds.p + fd, iov, iovlen); - } else { - rc = sys_writev_nt(fd, iov, iovlen); - } - } else if (fd < 0) { + if (fd < 0) { rc = ebadf(); - } else { + } else if (iovlen < 0) { rc = einval(); + } else if (IsAsan() && !__asan_is_valid_iov(iov, iovlen)) { + rc = efault(); + } else if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) { + rc = ebadf(); // posix specifies this when not open()'d for writing + } else if (IsLinux() || IsXnu() || IsFreebsd() || IsOpenbsd() || IsNetbsd()) { + if (iovlen == 1) { + rc = sys_write(fd, iov[0].iov_base, iov[0].iov_len); + } else { + rc = sys_writev(fd, iov, iovlen); + } + } else if (fd >= g_fds.n) { + rc = ebadf(); + } else if (IsMetal()) { + rc = sys_writev_metal(g_fds.p + fd, iov, iovlen); + } else if (IsWindows()) { + rc = sys_writev_nt(fd, iov, iovlen); + } else { + rc = enosys(); } END_CANCELLATION_POINT; diff --git a/libc/intrin/describetermios.c b/libc/intrin/describetermios.c new file mode 100644 index 000000000..c7cb82bb5 --- /dev/null +++ b/libc/intrin/describetermios.c @@ -0,0 +1,130 @@ +/*-*- 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/struct/termios.h" +#include "libc/dce.h" +#include "libc/intrin/asan.internal.h" +#include "libc/intrin/describeflags.internal.h" +#include "libc/intrin/kprintf.h" +#include "libc/macros.internal.h" +#include "libc/sysv/consts/termios.h" + +#define N 1024 + +#define append(...) o += ksnprintf(buf + o, N - o, __VA_ARGS__) + +const char *(DescribeTermios)(char buf[N], ssize_t rc, struct termios *tio) { + int o = 0; + char b128[128]; + + if (!tio) return "NULL"; + if ((!IsAsan() && kisdangerous(tio)) || + (IsAsan() && !__asan_is_valid(tio, sizeof(*tio)))) { + ksnprintf(buf, N, "%p", tio); + return buf; + } + + append("{"); + + struct DescribeFlags kInput[] = { + {IGNBRK, "IGNBRK"}, // + {BRKINT, "BRKINT"}, // + {IGNPAR, "IGNPAR"}, // + {PARMRK, "PARMRK"}, // + {INPCK, "INPCK"}, // + {ISTRIP, "ISTRIP"}, // + {INLCR, "INLCR"}, // + {IGNCR, "IGNCR"}, // + {ICRNL, "ICRNL"}, // + {IUCLC, "IUCLC"}, // + {IXON, "IXON"}, // + {IXANY, "IXANY"}, // + {IXOFF, "IXOFF"}, // + {IMAXBEL, "IMAXBEL"}, // + {IUTF8, "IUTF8"}, // + }; + append(".c_iflag=%s", + DescribeFlags(b128, 128, kInput, ARRAYLEN(kInput), "", tio->c_iflag)); + + struct DescribeFlags kOutput[] = { + {OPOST, "OPOST"}, // + {OLCUC, "OLCUC"}, // + {ONLCR, "ONLCR"}, // + {OCRNL, "OCRNL"}, // + {ONOCR, "ONOCR"}, // + {ONLRET, "ONLRET"}, // + {OFILL, "OFILL"}, // + {OFDEL, "OFDEL"}, // + {NL1, "NL1"}, // + {CR3, "CR3"}, // + {CR2, "CR2"}, // + {CR1, "CR1"}, // + {TAB3, "TAB3"}, // + {TAB2, "TAB2"}, // + {TAB1, "TAB1"}, // + {BS1, "BS1"}, // + {VT1, "VT1"}, // + {FF1, "FF1"}, // + }; + append(", .c_oflag=%s", DescribeFlags(b128, 128, kOutput, ARRAYLEN(kOutput), + "", tio->c_oflag)); + + struct DescribeFlags kControl[] = { + {CS8, "CS8"}, // + {CS7, "CS7"}, // + {CS6, "CS6"}, // + {CSTOPB, "CSTOPB"}, // + {CREAD, "CREAD"}, // + {PARENB, "PARENB"}, // + {PARODD, "PARODD"}, // + {HUPCL, "HUPCL"}, // + {CLOCAL, "CLOCAL"}, // + {CRTSCTS, "CRTSCTS"}, // + }; + append(", .c_cflag=%s", DescribeFlags(b128, 128, kControl, ARRAYLEN(kControl), + "", tio->c_cflag)); + + struct DescribeFlags kLocal[] = { + {ISIG, "ISIG"}, // + {ICANON, "ICANON"}, // + {XCASE, "XCASE"}, // + {ECHO, "ECHO"}, // + {ECHOE, "ECHOE"}, // + {ECHOK, "ECHOK"}, // + {ECHONL, "ECHONL"}, // + {NOFLSH, "NOFLSH"}, // + {TOSTOP, "TOSTOP"}, // + {ECHOCTL, "ECHOCTL"}, // + {ECHOPRT, "ECHOPRT"}, // + {ECHOKE, "ECHOKE"}, // + {FLUSHO, "FLUSHO"}, // + {PENDIN, "PENDIN"}, // + {IEXTEN, "IEXTEN"}, // + {EXTPROC, "EXTPROC"}, // + }; + append(", .c_lflag=%s", + DescribeFlags(b128, 128, kLocal, ARRAYLEN(kLocal), "", tio->c_lflag)); + + append(", c_cc[VINTR]=%#o", tio->c_cc[VINTR]); + append(", c_cc[VERASE]=%#o", tio->c_cc[VERASE]); + append(", c_cc[VWERASE]=%#o", tio->c_cc[VWERASE]); + + append("}"); + + return buf; +} diff --git a/libc/intrin/g_fds.c b/libc/intrin/g_fds.c index 68d5ed9d2..17751152e 100644 --- a/libc/intrin/g_fds.c +++ b/libc/intrin/g_fds.c @@ -18,10 +18,12 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" #include "libc/calls/state.internal.h" +#include "libc/calls/ttydefaults.h" #include "libc/intrin/_getenv.internal.h" #include "libc/intrin/atomic.h" #include "libc/intrin/extend.internal.h" #include "libc/intrin/kprintf.h" +#include "libc/intrin/nomultics.internal.h" #include "libc/intrin/pushpop.internal.h" #include "libc/intrin/weaken.h" #include "libc/macros.internal.h" @@ -117,4 +119,6 @@ textstartup void __init_fds(int argc, char **argv, char **envp) { } fds->p[1].flags = O_WRONLY | O_APPEND; fds->p[2].flags = O_WRONLY | O_APPEND; + __vintr = CTRL('C'); + __vquit = CTRL('\\'); } diff --git a/libc/intrin/nomultics.c b/libc/intrin/nomultics.c index e0003b831..ee6e3815c 100644 --- a/libc/intrin/nomultics.c +++ b/libc/intrin/nomultics.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/intrin/nomultics.internal.h" /** * Controls ANSI prefix for log emissions. @@ -26,3 +27,6 @@ */ char __replmode; char __replstderr; +char __ttymagic; +char __vintr; +char __vquit; diff --git a/libc/intrin/nomultics.internal.h b/libc/intrin/nomultics.internal.h index ffc15900c..bda620d57 100644 --- a/libc/intrin/nomultics.internal.h +++ b/libc/intrin/nomultics.internal.h @@ -5,6 +5,9 @@ COSMOPOLITAN_C_START_ extern char __replmode; extern char __replstderr; +extern char __ttymagic; +extern char __vintr; +extern char __vquit; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/intrin/strchr.c b/libc/intrin/strchr.c index aa5fe133b..23a5af58a 100644 --- a/libc/intrin/strchr.c +++ b/libc/intrin/strchr.c @@ -94,11 +94,11 @@ static dontasan inline const char *strchr_x64(const char *p, uint64_t c) { * @asyncsignalsafe * @vforksafe */ -char *strchr(const char *s, int c) { +dontasan char *strchr(const char *s, int c) { + if (IsAsan()) __asan_verify_str(s); #if defined(__x86_64__) && !defined(__chibicc__) const char *r; if (X86_HAVE(SSE)) { - if (IsAsan()) __asan_verify(s, 1); r = strchr_sse(s, c); } else { r = strchr_pure(s, c); diff --git a/libc/intrin/strchrnul.c b/libc/intrin/strchrnul.c index aaca503cb..28d4662e4 100644 --- a/libc/intrin/strchrnul.c +++ b/libc/intrin/strchrnul.c @@ -94,10 +94,10 @@ dontasan static const char *strchrnul_x64(const char *p, uint64_t c) { * NUL terminator if c is not found */ char *strchrnul(const char *s, int c) { + if (IsAsan()) __asan_verify_str(s); #if defined(__x86_64__) && !defined(__chibicc__) const char *r; if (X86_HAVE(SSE)) { - if (IsAsan()) __asan_verify(s, 1); r = strchrnul_sse(s, c); } else { r = strchrnul_pure(s, c); diff --git a/libc/intrin/strcpy.c b/libc/intrin/strcpy.c index 237b84822..ad5986fed 100644 --- a/libc/intrin/strcpy.c +++ b/libc/intrin/strcpy.c @@ -36,6 +36,7 @@ typedef char xmm_t __attribute__((__vector_size__(16), __aligned__(16))); dontasan char *strcpy(char *d, const char *s) { size_t i = 0; if (IsAsan()) { + __asan_verify_str(s); __asan_verify(d, strlen(s) + 1); } #if defined(__x86_64__) && !defined(__chibicc__) diff --git a/libc/runtime/getdosargv.c b/libc/runtime/getdosargv.c index c5f9de975..cbb8a8d03 100644 --- a/libc/runtime/getdosargv.c +++ b/libc/runtime/getdosargv.c @@ -18,11 +18,15 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/intrin/bits.h" #include "libc/intrin/safemacros.internal.h" +#include "libc/nt/enum/fileflagandattributes.h" +#include "libc/nt/files.h" #include "libc/nt/thunk/msabi.h" #include "libc/runtime/internal.h" #include "libc/str/str.h" #include "libc/str/utf16.h" +__msabi extern typeof(GetFileAttributes) *const __imp_GetFileAttributesW; + struct DosArgv { const char16_t *s; char *p; @@ -102,6 +106,31 @@ textwindows dontasan int GetDosArgv(const char16_t *cmdline, char *buf, if (!st->wc) break; if (++argc < max) { argv[argc - 1] = st->p < st->pe ? st->p : NULL; + if (argc == 1) { + // windows lets you run "foo.com" without saying "./foo.com" + // which caused emacs to crash after searching for itself :( + char16_t cmd[256]; + uint32_t i, j, attr; + i = j = 0; + cmd[j++] = st->wc; + for (; st->s[i]; ++i) { + if (i == 255 || st->s[i] == '/' || st->s[i] == '\\') { + goto GiveUpAddingDotSlash; + } + if (st->s[i] == ' ' || st->s[i] == '\t') { + break; + } + cmd[j++] = st->s[i]; + } + cmd[j] = 0; + if ((attr = __imp_GetFileAttributesW(cmd)) != -1u && + !(attr & kNtFileAttributeDirectory)) { + AppendDosArgv('.', st); + AppendDosArgv('\\', st); + } + GiveUpAddingDotSlash: + donothing; + } } inquote = false; while (st->wc) { diff --git a/libc/runtime/printargs.c b/libc/runtime/printargs.c index b8b0eb16a..47f3d8e5f 100644 --- a/libc/runtime/printargs.c +++ b/libc/runtime/printargs.c @@ -284,9 +284,11 @@ dontasan textstartup void __printargs(const char *prologue) { } if ((n = poll(u.pfds, ARRAYLEN(u.pfds), 0)) != -1) { for (i = 0; i < ARRAYLEN(u.pfds); ++i) { + char oflagbuf[128]; if (i && (u.pfds[i].revents & POLLNVAL)) continue; - PRINT(" ☼ %d (revents=%#hx fcntl(F_GETFL)=%#x isatty()=%hhhd)", i, - u.pfds[i].revents, fcntl(i, F_GETFL), isatty(i)); + PRINT(" ☼ %d (revents=%#hx fcntl(F_GETFL)=%s isatty()=%hhhd)", i, + u.pfds[i].revents, (DescribeOpenFlags)(oflagbuf, fcntl(i, F_GETFL)), + isatty(i)); } } else { PRINT(" poll() returned %d %m", n); @@ -449,15 +451,18 @@ dontasan textstartup void __printargs(const char *prologue) { PRINT(" ☼ %s = %#s", "kNtSystemDirectory", kNtSystemDirectory); PRINT(" ☼ %s = %#s", "kNtWindowsDirectory", kNtWindowsDirectory); #endif + PRINT(" ☼ %s = %#s", "__argv[0]", __argv[0]); + PRINT(" ☼ %s = %#s", "getenv(\"_\")", getenv("_")); + PRINT(" ☼ %s = %#s", "getauxval(AT_EXECFN)", getauxval(AT_EXECFN)); PRINT(" ☼ %s = %#s", "GetProgramExecutableName", GetProgramExecutableName()); PRINT(" ☼ %s = %#s", "GetInterpreterExecutableName", GetInterpreterExecutableName(u.path, sizeof(u.path))); - PRINT(" ☼ %s = %p", "RSP", __builtin_frame_address(0)); PRINT(" ☼ %s = %p", "GetStackSize()", GetStackSize()); PRINT(" ☼ %s = %p", "GetGuardSize()", GetGuardSize()); PRINT(" ☼ %s = %p", "GetStackAddr()", GetStackAddr()); PRINT(" ☼ %s = %p", "GetStaticStackSize()", GetStaticStackSize()); PRINT(" ☼ %s = %p", "GetStaticStackAddr(0)", GetStaticStackAddr(0)); + PRINT(" ☼ %s = %p", "__builtin_frame_address(0)", __builtin_frame_address(0)); PRINT(""); PRINT("MEMTRACK"); @@ -509,46 +514,34 @@ dontasan textstartup void __printargs(const char *prologue) { if (termios.c_oflag & OFILL) kprintf(" OFILL"); if (termios.c_oflag & OFDEL) kprintf(" OFDEL"); if (termios.c_oflag & OLCUC) kprintf(" OLCUC"); - if ((termios.c_oflag & NLDLY) == NL0) { - kprintf(" NL0"); - } else if ((termios.c_oflag & NLDLY) == NL1) { + if ((termios.c_oflag & NLDLY) == NL1) { kprintf(" NL1"); } else if ((termios.c_oflag & NLDLY) == NL2) { kprintf(" NL2"); } else if ((termios.c_oflag & NLDLY) == NL3) { kprintf(" NL3"); } - if ((termios.c_oflag & CRDLY) == CR0) { - kprintf(" CR0"); - } else if ((termios.c_oflag & CRDLY) == CR1) { + if ((termios.c_oflag & CRDLY) == CR1) { kprintf(" CR1"); } else if ((termios.c_oflag & CRDLY) == CR2) { kprintf(" CR2"); } else if ((termios.c_oflag & CRDLY) == CR3) { kprintf(" CR3"); } - if ((termios.c_oflag & TABDLY) == TAB0) { - kprintf(" TAB0"); - } else if ((termios.c_oflag & TABDLY) == TAB1) { + if ((termios.c_oflag & TABDLY) == TAB1) { kprintf(" TAB1"); } else if ((termios.c_oflag & TABDLY) == TAB2) { kprintf(" TAB2"); } else if ((termios.c_oflag & TABDLY) == TAB3) { kprintf(" TAB3"); } - if ((termios.c_oflag & BSDLY) == BS0) { - kprintf(" BS0"); - } else if ((termios.c_oflag & BSDLY) == BS1) { + if ((termios.c_oflag & BSDLY) == BS1) { kprintf(" BS1"); } - if ((termios.c_oflag & VTDLY) == VT0) { - kprintf(" VT0"); - } else if ((termios.c_oflag & VTDLY) == VT1) { + if ((termios.c_oflag & VTDLY) == VT1) { kprintf(" VT1"); } - if ((termios.c_oflag & FFDLY) == FF0) { - kprintf(" FF0"); - } else if ((termios.c_oflag & FFDLY) == FF1) { + if ((termios.c_oflag & FFDLY) == FF1) { kprintf(" FF1"); } kprintf("\n"); diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index c3d56cd0a..0a4e0586b 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -312,10 +312,10 @@ syscon splice SPLICE_F_MORE 4 4 0 0 0 0 0 0 # can be safely i syscon splice SPLICE_F_GIFT 8 8 0 0 0 0 0 0 # can probably be ignored by polyfill # access() flags +# libc/sysv/consts/ok.h # # group name GNU/Systemd GNU/Systemd (Aarch64) XNU's Not UNIX! MacOS (Arm64) FreeBSD OpenBSD NetBSD The New Technology Commentary -syscon access F_OK 0 0 0 0 0 0 0 0 # consensus -syscon access X_OK 1 1 1 1 1 1 1 0xa0000000 # unix consensus and kNtGenericExecute | kNtGenericRead +syscon access X_OK 1 1 1 1 1 1 1 0x20000000 # unix consensus and kNtGenericExecute syscon access W_OK 2 2 2 2 2 2 2 0x40000000 # unix consensus and kNtGenericWrite syscon access R_OK 4 4 4 4 4 4 4 0x80000000 # unix consensus and kNtGenericRead @@ -403,10 +403,10 @@ syscon fcntl F_GETLEASE 0x0401 0x0401 -1 107 -1 -1 -1 -1 # group name GNU/Systemd GNU/Systemd (Aarch64) XNU's Not UNIX! MacOS (Arm64) FreeBSD OpenBSD NetBSD The New Technology Commentary syscon at AT_FDCWD -100 -100 -2 -2 -100 -100 -100 -100 # faked nt syscon at AT_SYMLINK_NOFOLLOW 0x0100 0x0100 0x20 0x20 0x0200 2 0x200 0x0100 # faked nt -syscon at AT_SYMLINK_FOLLOW 0x0400 0x0400 0x40 0x40 0x0400 4 0x400 0 # see linkat(2) +syscon at AT_SYMLINK_FOLLOW 0x0400 0x0400 0x40 0x40 0x0400 4 0x400 0x0400 # see linkat(2) syscon at AT_REMOVEDIR 0x0200 0x0200 0x80 0x80 0x0800 8 0x800 0x0200 # faked nt syscon at AT_EACCESS 0x0200 0x0200 0x10 0x10 0x0100 1 0x100 0 # performs check using effective uid/gid; unnecessary nt -syscon at AT_EMPTY_PATH 0x1000 0x1000 0 0 0 0 0 0 # linux 2.6.39+; see unlink, O_TMPFILE, etc. +syscon at AT_EMPTY_PATH 0x1000 0x1000 0 0 0 0 0 0x1000 # linux 2.6.39+; see unlink, O_TMPFILE, etc. # utimensat() special values # diff --git a/libc/sysv/consts/AT_EMPTY_PATH.S b/libc/sysv/consts/AT_EMPTY_PATH.S index 299b2d31d..931d5e6a5 100644 --- a/libc/sysv/consts/AT_EMPTY_PATH.S +++ b/libc/sysv/consts/AT_EMPTY_PATH.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon at,AT_EMPTY_PATH,0x1000,0x1000,0,0,0,0,0,0 +.syscon at,AT_EMPTY_PATH,0x1000,0x1000,0,0,0,0,0,0x1000 diff --git a/libc/sysv/consts/AT_SYMLINK_FOLLOW.S b/libc/sysv/consts/AT_SYMLINK_FOLLOW.S index 2d97235df..1920713d6 100644 --- a/libc/sysv/consts/AT_SYMLINK_FOLLOW.S +++ b/libc/sysv/consts/AT_SYMLINK_FOLLOW.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon at,AT_SYMLINK_FOLLOW,0x0400,0x0400,0x40,0x40,0x0400,4,0x400,0 +.syscon at,AT_SYMLINK_FOLLOW,0x0400,0x0400,0x40,0x40,0x0400,4,0x400,0x0400 diff --git a/libc/sysv/consts/X_OK.S b/libc/sysv/consts/X_OK.S index 096dc637f..4a737c421 100644 --- a/libc/sysv/consts/X_OK.S +++ b/libc/sysv/consts/X_OK.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon access,X_OK,1,1,1,1,1,1,1,0xa0000000 +.syscon access,X_OK,1,1,1,1,1,1,1,0x20000000 diff --git a/libc/sysv/consts/ok.h b/libc/sysv/consts/ok.h index 1e891b864..bfc398cfc 100644 --- a/libc/sysv/consts/ok.h +++ b/libc/sysv/consts/ok.h @@ -1,18 +1,17 @@ #ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS_OK_H_ #define COSMOPOLITAN_LIBC_SYSV_CONSTS_OK_H_ -#define F_OK F_OK -#define R_OK R_OK -#define W_OK W_OK +#define F_OK 0 #define X_OK X_OK +#define W_OK W_OK +#define R_OK R_OK #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -extern const int F_OK; -extern const int R_OK; -extern const int W_OK; -extern const int X_OK; +extern const unsigned X_OK; +extern const unsigned W_OK; +extern const unsigned R_OK; /* warning: is sign bit on windows */ COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/test/libc/runtime/getdosargv_test.c b/test/libc/runtime/getdosargv_test.c index 172b53af4..4c87f02f5 100644 --- a/test/libc/runtime/getdosargv_test.c +++ b/test/libc/runtime/getdosargv_test.c @@ -16,10 +16,23 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/errno.h" #include "libc/mem/mem.h" #include "libc/runtime/internal.h" +#include "libc/runtime/runtime.h" #include "libc/testlib/testlib.h" +char testlib_enable_tmp_setup_teardown; + +void SetUpOnce(void) { + if (!IsWindows()) { + // TODO(jart): mock out that win32 i/o call + tinyprint(2, program_invocation_name, ": skipping on non-windows\n", NULL); + exit(0); + } +} + TEST(GetDosArgv, empty) { size_t max = 4; size_t size = ARG_MAX / 2; @@ -184,3 +197,16 @@ TEST(GetDosArgv, cmdToil) { free(argv); free(buf); } + +TEST(GetDosArgv, canonicalizesCurrentDirectoryCommandPath) { + size_t max = 4; + size_t size = ARG_MAX / 2; + char *buf = malloc(size * sizeof(char)); + char **argv = malloc(max * sizeof(char *)); + ASSERT_SYS(0, 0, touch("emacs.com", 0755)); + EXPECT_EQ(1, GetDosArgv(u"emacs.com", buf, size, argv, max)); + EXPECT_STREQ(".\\emacs.com", argv[0]); + EXPECT_EQ(NULL, argv[1]); + free(argv); + free(buf); +}