Get Fat Emacs working in Windows Console

This commit is contained in:
Justine Tunney 2023-08-18 04:55:57 -07:00
parent bf835de612
commit 7100b1cf91
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
34 changed files with 479 additions and 168 deletions

View file

@ -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);

View file

@ -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;

View file

@ -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) &&

View file

@ -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) {

View file

@ -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();

View file

@ -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;
}
}

View file

@ -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) {

View file

@ -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__ */

View file

@ -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);

View file

@ -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__ */

View file

@ -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;

View file

@ -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;

View 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_ */

View file

@ -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;
}
}

View file

@ -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;
}

View file

@ -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,

View file

@ -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;
}

View file

@ -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;

View file

@ -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;

View 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;
}

View file

@ -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('\\');
}

View file

@ -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;

View file

@ -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) */

View file

@ -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);

View file

@ -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);

View file

@ -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__)

View file

@ -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) {

View file

@ -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");

View file

@ -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
#

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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) */

View file

@ -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);
}