mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-03-03 07:29:23 +00:00
Get Fat Emacs working in Windows Console
This commit is contained in:
parent
bf835de612
commit
7100b1cf91
34 changed files with 479 additions and 168 deletions
|
@ -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);
|
||||
|
|
|
@ -47,10 +47,12 @@ 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 (!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;
|
||||
}
|
||||
|
|
|
@ -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) &&
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,12 +203,24 @@ 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 (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 (fd->ttymagic & kFdTtyEchoing) {
|
||||
}
|
||||
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__ */
|
||||
|
|
|
@ -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))) {
|
||||
|
||||
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 (!IsWindows() && !IsMetal()) {
|
||||
} 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 {
|
||||
} else if (IsWindows()) {
|
||||
rc = sys_readv_nt(g_fds.p + fd, &(struct iovec){buf, size}, 1);
|
||||
}
|
||||
} else {
|
||||
rc = ebadf();
|
||||
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);
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -50,13 +50,16 @@ 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)) {
|
||||
if (fd < 0) {
|
||||
rc = ebadf();
|
||||
} 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 (!IsWindows() && !IsMetal()) {
|
||||
} else if (IsLinux() || IsXnu() || IsFreebsd() || IsOpenbsd() || IsNetbsd()) {
|
||||
if (iovlen == 1) {
|
||||
rc = sys_read(fd, iov[0].iov_base, iov[0].iov_len);
|
||||
} else {
|
||||
|
@ -66,13 +69,10 @@ ssize_t readv(int fd, const struct iovec *iov, int iovlen) {
|
|||
rc = ebadf();
|
||||
} else if (IsMetal()) {
|
||||
rc = sys_readv_metal(g_fds.p + fd, iov, iovlen);
|
||||
} else {
|
||||
} else if (IsWindows()) {
|
||||
rc = sys_readv_nt(g_fds.p + fd, iov, iovlen);
|
||||
}
|
||||
} else if (fd < 0) {
|
||||
rc = ebadf();
|
||||
} else {
|
||||
rc = einval();
|
||||
rc = enosys();
|
||||
}
|
||||
|
||||
END_CANCELLATION_POINT;
|
||||
|
|
|
@ -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;
|
||||
|
|
14
libc/calls/struct/termios.internal.h
Normal file
14
libc/calls/struct/termios.internal.h
Normal file
|
@ -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_ */
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)) {
|
||||
if (fd < 0) {
|
||||
rc = ebadf();
|
||||
} else if (!IsWindows() && !IsMetal()) {
|
||||
} 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 {
|
||||
} else if (IsWindows()) {
|
||||
rc = sys_writev_nt(fd, &(struct iovec){buf, size}, 1);
|
||||
}
|
||||
} else {
|
||||
rc = ebadf();
|
||||
rc = enosys();
|
||||
}
|
||||
|
||||
END_CANCELLATION_POINT;
|
||||
|
|
|
@ -53,12 +53,15 @@ 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)) {
|
||||
if (fd < 0) {
|
||||
rc = ebadf();
|
||||
} 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();
|
||||
} else if (!IsWindows() && !IsMetal()) {
|
||||
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 {
|
||||
|
@ -68,13 +71,10 @@ ssize_t writev(int fd, const struct iovec *iov, int iovlen) {
|
|||
rc = ebadf();
|
||||
} else if (IsMetal()) {
|
||||
rc = sys_writev_metal(g_fds.p + fd, iov, iovlen);
|
||||
} else {
|
||||
} else if (IsWindows()) {
|
||||
rc = sys_writev_nt(fd, iov, iovlen);
|
||||
}
|
||||
} else if (fd < 0) {
|
||||
rc = ebadf();
|
||||
} else {
|
||||
rc = einval();
|
||||
rc = enosys();
|
||||
}
|
||||
|
||||
END_CANCELLATION_POINT;
|
||||
|
|
130
libc/intrin/describetermios.c
Normal file
130
libc/intrin/describetermios.c
Normal file
|
@ -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;
|
||||
}
|
|
@ -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('\\');
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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__)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
#
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue