Make improvements for Actually Portable Emacs

- Get SIGWINCH working again on the New Technology
- Correctly handle O_NOFOLLOW in open() on Windows
- Implement synthetic umask() functionality on Windows
- Do a better job managing file execute access on Windows
- Fill in `st_uid` and `st_gid` with username hash on Windows
- Munge UNICODE control pictures into control codes on Windows
- Do a better job ensuring Windows console settings are restored
- Introduce KPRINTF_LOG environment variable to log kprintf to a file
This commit is contained in:
Justine Tunney 2023-08-19 06:41:06 -07:00
parent 9c7b81ee0f
commit 965516e313
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
108 changed files with 1126 additions and 807 deletions

View file

@ -26,6 +26,7 @@
#include "libc/calls/struct/siginfo.h"
#include "libc/calls/struct/sigset.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/macros.internal.h"
#include "libc/nt/runtime.h"
#include "libc/runtime/internal.h"
@ -35,6 +36,7 @@
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/tls.h"
#ifdef __x86_64__
/**
@ -179,8 +181,16 @@ textwindows bool __sig_handle(int sigops, int sig, int si_code,
switch (__sighandrvas[sig]) {
case (intptr_t)SIG_DFL:
if (__sig_is_fatal(sig)) {
STRACE("terminating on %G", sig);
_restorewintty();
size_t len;
char name[16];
strsignal_r(sig, name);
len = strlen(name);
name[len++] = '\n';
WriteFile(GetStdHandle(kNtStdErrorHandle), name, len, 0, 0);
STRACE("terminating on %s", name);
if (_weaken(__restore_console_win32)) {
_weaken(__restore_console_win32)();
}
ExitProcess(sig);
}
// fallthrough

View file

@ -34,7 +34,7 @@ textwindows int sys_clock_nanosleep_nt(int clock, int flags,
for (;;) {
if (sys_clock_gettime_nt(clock, &now)) return -1;
if (timespec_cmp(now, abs) >= 0) return 0;
if (_check_interrupts(0, g_fds.p)) return -1;
if (_check_interrupts(0)) return -1;
SleepEx(MIN(__SIG_POLLING_INTERVAL_MS,
timespec_tomillis(timespec_sub(abs, now))),
false);
@ -45,7 +45,7 @@ textwindows int sys_clock_nanosleep_nt(int clock, int flags,
for (;;) {
sys_clock_gettime_nt(clock, &now);
if (timespec_cmp(now, abs) >= 0) return 0;
if (_check_interrupts(0, g_fds.p)) {
if (_check_interrupts(0)) {
if (rem) *rem = timespec_sub(abs, now);
return -1;
}

View file

@ -23,11 +23,12 @@
#include "libc/calls/syscall-nt.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/sock/syscall_fd.internal.h"
#include "libc/sysv/errfuns.h"
#include "libc/runtime/zipos.internal.h"
/**
* Closes file descriptor.
@ -59,6 +60,8 @@ int close(int fd) {
if (fd < 0) {
rc = ebadf();
} else {
// helps guarantee stderr log gets duplicated before user closes
if (_weaken(kloghandle)) _weaken(kloghandle)();
// for performance reasons we want to avoid holding __fds_lock()
// while sys_close() is happening. this leaves the kernel / libc
// having a temporarily inconsistent state. routines that obtain

View file

@ -24,6 +24,7 @@
#include "libc/nt/enum/fileflagandattributes.h"
#include "libc/nt/enum/filesharemode.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/errfuns.h"
// code size optimization
// <sync libc/sysv/consts.sh>
@ -34,69 +35,113 @@
#define _O_DIRECTORY 0x00010000 // kNtFileFlagBackupSemantics
#define _O_TMPFILE 0x00410000 // AttributeTemporary|FlagDeleteOnClose
#define _O_DIRECT 0x00004000 // kNtFileFlagNoBuffering
#define _O_NDELAY 0x00000800 // kNtFileFlagWriteThrough
#define _O_NONBLOCK 0x00000800 // kNtFileFlagWriteThrough
#define _O_RANDOM 0x80000000 // kNtFileFlagRandomAccess
#define _O_SEQUENTIAL 0x40000000 // kNtFileFlagSequentialScan
#define _O_COMPRESSED 0x20000000 // kNtFileAttributeCompressed
#define _O_INDEXED 0x10000000 // !kNtFileAttributeNotContentIndexed
#define _O_NONBLOCK 0x00000800
#define _O_CLOEXEC 0x00080000
// </sync libc/sysv/consts.sh>
textwindows int GetNtOpenFlags(int flags, int mode, uint32_t *out_perm,
uint32_t *out_share, uint32_t *out_disp,
uint32_t *out_attr) {
bool is_creating_file;
uint32_t perm, share, disp, attr;
if (flags &
~(O_ACCMODE | _O_APPEND | _O_CREAT | _O_EXCL | _O_TRUNC | _O_DIRECTORY |
_O_TMPFILE | _O_NONBLOCK | _O_RANDOM | _O_SEQUENTIAL | _O_COMPRESSED |
_O_INDEXED | _O_CLOEXEC | _O_DIRECT)) {
return einval();
}
switch (flags & O_ACCMODE) {
case O_RDONLY:
perm = kNtFileGenericRead | kNtGenericExecute;
perm = kNtFileGenericRead;
break;
case O_WRONLY:
perm = kNtFileGenericWrite | kNtGenericExecute;
perm = kNtFileGenericWrite;
break;
case O_RDWR:
perm = kNtFileGenericRead | kNtFileGenericWrite | kNtGenericExecute;
perm = kNtFileGenericRead | kNtFileGenericWrite;
break;
default:
return -1;
return einval();
}
if (flags & _O_APPEND) {
perm = kNtFileAppendData;
perm = kNtFileAppendData; // todo: this is part of generic write above
}
attr = 0;
is_creating_file = (flags & _O_CREAT) || (flags & _O_TMPFILE) == _O_TMPFILE;
// POSIX O_EXEC doesn't mean the same thing as kNtGenericExecute. We
// request execute access when we can determine it from mode's bits.
// When opening existing files we greedily request execute access so
// mmap() won't fail. If it causes CreateFile() to fail, our wrapper
// will try calling CreateFile a second time without execute access.
if (is_creating_file) {
if (mode & 0111) {
perm |= kNtGenericExecute;
}
if (~mode & 0200) {
attr |= kNtFileAttributeReadonly; // not sure why anyone would
}
} else {
perm |= kNtGenericExecute;
}
// Be as generous as possible in sharing file access. Not doing this
// you'll quickly find yourself no longer able to administer Windows
if (flags & _O_EXCL) {
share = kNtFileShareExclusive;
} else {
share = kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete;
}
if ((flags & _O_CREAT) && (flags & _O_EXCL)) {
disp = kNtCreateNew;
} else if ((flags & _O_CREAT) && (flags & _O_TRUNC)) {
disp = kNtCreateAlways;
} else if (flags & _O_CREAT) {
disp = kNtOpenAlways;
// These POSIX to WIN32 mappings are relatively straightforward.
if (flags & _O_EXCL) {
if (is_creating_file) {
disp = kNtCreateNew;
} else {
return einval();
}
} else if (is_creating_file) {
if (flags & _O_TRUNC) {
disp = kNtCreateAlways;
} else {
disp = kNtOpenAlways;
}
} else if (flags & _O_TRUNC) {
disp = kNtTruncateExisting;
} else {
disp = kNtOpenExisting;
}
// Please use tmpfd() or tmpfile() instead of O_TMPFILE.
if ((flags & _O_TMPFILE) == _O_TMPFILE) {
attr = kNtFileAttributeTemporary | kNtFileFlagDeleteOnClose;
attr |= kNtFileAttributeTemporary | kNtFileFlagDeleteOnClose;
} else {
attr = kNtFileAttributeNormal;
attr |= kNtFileAttributeNormal;
if (flags & _O_DIRECTORY) {
attr |= kNtFileFlagBackupSemantics;
}
if (~mode & 0200) {
attr |= kNtFileAttributeReadonly;
}
}
if (~flags & _O_INDEXED) attr |= kNtFileAttributeNotContentIndexed;
if (flags & _O_COMPRESSED) attr |= kNtFileAttributeCompressed;
// The Windows content indexing service ravages performance similar to
// Windows Defender. Please pass O_INDEXED to openat() to enable this.
if (~flags & _O_INDEXED) {
attr |= kNtFileAttributeNotContentIndexed;
}
// Windows' transparent filesystem compression is really cool, as such
// we've introduced a nonstandard O_COMPRESSED flag to help you use it
if (flags & _O_COMPRESSED) {
attr |= kNtFileAttributeCompressed;
}
// Not certain yet what benefit these flags offer.
if (flags & _O_SEQUENTIAL) attr |= kNtFileFlagSequentialScan;
if (flags & _O_RANDOM) attr |= kNtFileFlagRandomAccess;
if (flags & _O_DIRECT) attr |= kNtFileFlagNoBuffering;

View file

@ -21,7 +21,9 @@
#include "libc/calls/syscall-nt.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/sysv/errfuns.h"
/**
@ -60,6 +62,8 @@
*/
int dup2(int oldfd, int newfd) {
int rc;
// helps guarantee stderr log gets duplicated before user closes
if (_weaken(kloghandle)) _weaken(kloghandle)();
if (__isfdkind(oldfd, kFdZip)) {
rc = enotsup();
#ifdef __aarch64__

View file

@ -21,7 +21,9 @@
#include "libc/calls/syscall-nt.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/errfuns.h"
@ -59,6 +61,8 @@
*/
int dup3(int oldfd, int newfd, int flags) {
int rc;
// helps guarantee stderr log gets duplicated before user closes
if (_weaken(kloghandle)) _weaken(kloghandle)();
if (oldfd == newfd || (flags & ~O_CLOEXEC)) {
rc = einval(); // NetBSD doesn't do this
} else if (oldfd < 0 || newfd < 0) {

View file

@ -45,6 +45,8 @@
#include "libc/sysv/consts/ok.h"
#include "libc/sysv/errfuns.h"
extern long __klog_handle;
__msabi extern typeof(CloseHandle) *const __imp_CloseHandle;
__msabi extern typeof(WaitForSingleObject) *const __imp_WaitForSingleObject;
__msabi extern typeof(GetExitCodeProcess) *const __imp_GetExitCodeProcess;
@ -115,6 +117,12 @@ textwindows int sys_execve_nt(const char *program, char *const argv[],
startinfo.hStdOutput = g_fds.p[1].handle;
startinfo.hStdError = g_fds.p[2].handle;
if (_weaken(__klog_handle) && //
*_weaken(__klog_handle) != 0 && //
*_weaken(__klog_handle) != -1) {
__imp_CloseHandle(*_weaken(__klog_handle));
}
// spawn the process
rc = ntspawn(program, argv, envp, v, 0, 0, true, 0, 0, &startinfo, &procinfo);
if (rc == -1) {

View file

@ -23,6 +23,6 @@
textwindows int sys_fdatasync_nt(int fd) {
// TODO(jart): what should we do with worker pipes?
if (!__isfdkind(fd, kFdFile)) return ebadf();
if (_check_interrupts(0, 0)) return -1;
if (_check_interrupts(0)) return -1;
return FlushFileBuffers(g_fds.p[fd].handle) ? 0 : -1;
}

View file

@ -17,9 +17,11 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/stat.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/fmt/conv.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/bsr.h"
#include "libc/intrin/strace.internal.h"
#include "libc/macros.internal.h"
@ -70,25 +72,27 @@ static textwindows uint32_t GetSizeOfReparsePoint(int64_t fh) {
textwindows int sys_fstat_nt(int64_t handle, struct stat *st) {
int filetype;
uint32_t umask;
uint64_t actualsize;
struct NtFileCompressionInfo fci;
struct NtByHandleFileInformation wst;
if (!st) return efault();
if ((filetype = GetFileType(handle))) {
bzero(st, sizeof(*st));
umask = atomic_load_explicit(&__umask, memory_order_acquire);
switch (filetype) {
case kNtFileTypeChar:
st->st_mode = S_IFCHR | 0644;
st->st_mode = S_IFCHR | (0666 & ~umask);
break;
case kNtFileTypePipe:
st->st_mode = S_IFIFO | 0644;
st->st_mode = S_IFIFO | (0666 & ~umask);
break;
case kNtFileTypeDisk:
if (GetFileInformationByHandle(handle, &wst)) {
st->st_mode = 0555;
st->st_mode = 0555 & ~umask;
st->st_flags = wst.dwFileAttributes;
if (!(wst.dwFileAttributes & kNtFileAttributeReadonly)) {
st->st_mode |= 0200;
st->st_mode |= 0222 & ~umask;
}
if (wst.dwFileAttributes & kNtFileAttributeDirectory) {
st->st_mode |= S_IFDIR;
@ -101,6 +105,7 @@ textwindows int sys_fstat_nt(int64_t handle, struct stat *st) {
st->st_mtim = FileTimeToTimeSpec(wst.ftLastWriteFileTime);
st->st_ctim = FileTimeToTimeSpec(wst.ftCreationFileTime);
st->st_birthtim = st->st_ctim;
st->st_gid = st->st_uid = __synthesize_uid();
st->st_size = (uint64_t)wst.nFileSizeHigh << 32 | wst.nFileSizeLow;
st->st_blksize = 4096;
st->st_dev = wst.dwVolumeSerialNumber;

View file

@ -30,6 +30,10 @@
/**
* Returns information about file, via open()'d descriptor.
*
* On Windows, this implementation always sets `st_uid` and `st_gid` to
* `getuid()` and `getgid()`. The `st_mode` field is generated based on
* the current umask().
*
* @return 0 on success or -1 w/ errno
* @raise EBADF if `fd` isn't a valid file descriptor
* @raise EIO if an i/o error happens while reading from file system
@ -43,14 +47,14 @@ int fstat(int fd, struct stat *st) {
} else if (__isfdkind(fd, kFdZip)) {
rc = _weaken(__zipos_fstat)(
(struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, st);
} else if (!IsWindows() && !IsMetal()) {
} else if (IsLinux() || IsXnu() || IsFreebsd() || IsOpenbsd() || IsNetbsd()) {
rc = sys_fstat(fd, st);
} else if (IsMetal()) {
rc = sys_fstat_metal(fd, st);
} else if (!__isfdkind(fd, kFdFile)) {
rc = ebadf();
} else {
} else if (IsWindows()) {
rc = sys_fstat_nt(__getfdhandleactual(fd), st);
} else {
rc = enosys();
}
STRACE("fstat(%d, [%s]) → %d% m", fd, DescribeStat(rc, st), rc);
return rc;

View file

@ -43,10 +43,26 @@ static inline const char *__strace_fstatat_flags(char buf[12], int flags) {
/**
* Returns information about thing.
*
* @param dirfd is normally AT_FDCWD but if it's an open directory and
* file is a relative path, then file becomes relative to dirfd
* On Windows, this implementation always sets `st_uid` and `st_gid` to
* `getuid()` and `getgid()`. Anyone who relies upon the information to
* secure a multi-tenant personal computer should consider improving it
* and further note that the `st_mode` group / other bits will be clear
*
* @param dirfd is normally `AT_FDCWD` but if it's an open directory and
* file is a relative path, then `path` becomes relative to `dirfd`
* @param st is where the result is stored
* @param flags can have `AT_SYMLINK_NOFOLLOW`
* @param st is where result is stored
* @param flags can have AT_SYMLINK_NOFOLLOW
* @raise EACCES if denied access to component in path prefix
* @raise EIO if i/o error occurred while reading from filesystem
* @raise ELOOP if a symbolic link loop exists in `path`
* @raise ENAMETOOLONG if a component in `path` exceeds `NAME_MAX`
* @raise ENOENT on empty string or if component in path doesn't exist
* @raise ENOTDIR if a parent component existed that wasn't a directory
* @raise ENOTDIR if `path` is relative and `dirfd` isn't an open directory
* @raise EOVERFLOW shouldn't be possible on 64-bit systems
* @raise ELOOP may ahappen if `SYMLOOP_MAX` symlinks were dereferenced
* @raise ENAMETOOLONG may happen if `path` exceeded `PATH_MAX`
* @return 0 on success, or -1 w/ errno
* @see S_ISDIR(st.st_mode), S_ISREG()
* @asyncsignalsafe
@ -70,10 +86,12 @@ int fstatat(int dirfd, const char *path, struct stat *st, int flags) {
} else {
rc = enotsup();
}
} else if (!IsWindows()) {
} else if (IsLinux() || IsXnu() || IsFreebsd() || IsOpenbsd() || IsNetbsd()) {
rc = sys_fstatat(dirfd, path, st, flags);
} else {
} else if (IsWindows()) {
rc = sys_fstatat_nt(dirfd, path, st, flags);
} else {
rc = enosys();
}
STRACE("fstatat(%s, %#s, [%s], %s) → %d% m", DescribeDirfd(dirfd), path,
DescribeStat(rc, st), __strace_fstatat_flags(alloca(12), flags), rc);

View file

@ -17,9 +17,11 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/atomic.h"
#include "libc/calls/calls.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/strace.internal.h"
#include "libc/limits.h"
#include "libc/macros.internal.h"
@ -27,7 +29,7 @@
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
static uint32_t KnuthMultiplicativeHash32(const void *buf, size_t size) {
static uint32_t __kmp32(const void *buf, size_t size) {
size_t i;
uint32_t h;
const uint32_t kPhiPrime = 0x9e3779b1;
@ -36,18 +38,24 @@ static uint32_t KnuthMultiplicativeHash32(const void *buf, size_t size) {
return h;
}
static textwindows dontinline uint32_t GetUserNameHash(void) {
textwindows uint32_t __synthesize_uid(void) {
char16_t buf[257];
uint32_t size = ARRAYLEN(buf);
GetUserName(&buf, &size);
return KnuthMultiplicativeHash32(buf, size >> 1) & INT_MAX;
static atomic_uint uid;
uint32_t tmp, size = ARRAYLEN(buf);
if (!(tmp = atomic_load_explicit(&uid, memory_order_acquire))) {
GetUserName(&buf, &size);
tmp = __kmp32(buf, size >> 1) & INT_MAX;
if (!tmp) ++tmp;
atomic_store_explicit(&uid, tmp, memory_order_release);
}
return tmp;
}
/**
* Returns real user id of process.
*
* This never fails. On Windows, which doesn't really have this concept,
* we return a deterministic value that's likely to work.
* we return a hash of the username.
*
* @return user id (always successful)
* @asyncsignalsafe
@ -61,7 +69,7 @@ uint32_t getuid(void) {
} else if (!IsWindows()) {
rc = sys_getuid();
} else {
rc = GetUserNameHash();
rc = __synthesize_uid();
}
npassert(rc >= 0);
STRACE("%s() → %d", "getuid", rc);
@ -72,7 +80,7 @@ uint32_t getuid(void) {
* Returns real group id of process.
*
* This never fails. On Windows, which doesn't really have this concept,
* we return a deterministic value that's likely to work.
* we return a hash of the username.
*
* @return group id (always successful)
* @asyncsignalsafe
@ -86,7 +94,7 @@ uint32_t getgid(void) {
} else if (!IsWindows()) {
rc = sys_getgid();
} else {
rc = GetUserNameHash();
rc = __synthesize_uid();
}
npassert(rc >= 0);
STRACE("%s() → %d", "getgid", rc);

View file

@ -1,5 +1,6 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_INTERNAL_H_
#define COSMOPOLITAN_LIBC_CALLS_INTERNAL_H_
#include "libc/atomic.h"
#include "libc/calls/struct/fd.internal.h"
#include "libc/calls/struct/sigval.h"
#include "libc/dce.h"
@ -16,6 +17,7 @@ COSMOPOLITAN_C_START_
#define kIoMotion ((const int8_t[3]){1, 0, 0})
extern struct Fds g_fds;
extern atomic_int __umask;
extern const struct Fd kEmptyFd;
int __reservefd(int);
@ -24,6 +26,7 @@ void __releasefd(int);
int __ensurefds(int);
int __ensurefds_unlocked(int);
void __printfds(void);
uint32_t __synthesize_uid(void);
forceinline int64_t __getfdhandleactual(int fd) {
return g_fds.p[fd].handle;
@ -37,8 +40,8 @@ forceinline bool __isfdkind(int fd, int kind) {
return 0 <= fd && fd < g_fds.n && g_fds.p[fd].kind == kind;
}
int _check_interrupts(int);
int sys_close_nt(struct Fd *, int);
int _check_interrupts(int, struct Fd *);
int sys_openat_metal(int, const char *, int, unsigned);
COSMOPOLITAN_C_END_

View file

@ -33,8 +33,8 @@
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
textwindows int _check_interrupts(int sigops, struct Fd *fd) {
int e, rc;
textwindows int _check_interrupts(int sigops) {
int e, rc, flags = 0;
e = errno;
if (_weaken(pthread_testcancel_np) &&
(rc = _weaken(pthread_testcancel_np)())) {
@ -42,21 +42,22 @@ textwindows int _check_interrupts(int sigops, struct Fd *fd) {
return -1;
}
if (__tls_enabled) {
flags = __get_tls()->tib_flags;
__get_tls()->tib_flags |= TIB_FLAG_TIME_CRITICAL;
}
if (_weaken(_check_sigalrm)) {
_weaken(_check_sigalrm)();
}
if (__tls_enabled) {
__get_tls()->tib_flags &= ~TIB_FLAG_TIME_CRITICAL;
__get_tls()->tib_flags = flags;
}
if (_weaken(_check_sigwinch)) {
_weaken(_check_sigwinch)();
}
if (!__tls_enabled || !(__get_tls()->tib_flags & TIB_FLAG_TIME_CRITICAL)) {
if (!(sigops & kSigOpNochld) && _weaken(_check_sigchld)) {
_weaken(_check_sigchld)();
}
if (fd && _weaken(_check_sigwinch)) {
_weaken(_check_sigwinch)(fd);
}
}
if (_weaken(__sig_check) && _weaken(__sig_check)(sigops)) {
return eintr();

View file

@ -672,9 +672,9 @@ int ioctl(int fd, unsigned long request, ...) {
if (request == FIONREAD) {
rc = ioctl_fionread(fd, arg);
} else if (request == TIOCGWINSZ) {
rc = tcgetwinsize(fd, arg);
return tcgetwinsize(fd, arg);
} else if (request == TIOCSWINSZ) {
rc = tcsetwinsize(fd, arg);
return tcsetwinsize(fd, arg);
} else if (request == SIOCGIFCONF) {
rc = ioctl_siocgifconf(fd, arg);
} else if (request == SIOCGIFADDR) {

View file

@ -78,7 +78,7 @@
int64_t lseek(int fd, int64_t offset, int whence) {
int64_t rc;
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
rc = _weaken(__zipos_lseek)(
rc = _weaken(__zipos_seek)(
(struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, offset, whence);
} else if (IsLinux() || IsXnu() || IsFreebsd() || IsOpenbsd()) {
rc = sys_lseek(fd, offset, whence, 0);

View file

@ -92,20 +92,24 @@ TryAgain:
if (result || flags == F_OK) {
rc = 0;
} else {
NTTRACE("ntaccesscheck finale failed %d %x", result, flags);
NTTRACE("ntaccesscheck finale failed: result=%d flags=%x", result,
flags);
rc = eacces();
}
} else {
rc = __winerr();
NTTRACE("%s(%#hs) failed: %m", "AccessCheck", pathname);
NTTRACE("%s(%#hs) failed: %s", "AccessCheck", pathname,
strerror(errno));
}
} else {
rc = __winerr();
NTTRACE("%s(%#hs) failed: %m", "DuplicateToken", pathname);
NTTRACE("%s(%#hs) failed: %s", "DuplicateToken", pathname,
strerror(errno));
}
} else {
rc = __winerr();
NTTRACE("%s(%#hs) failed: %m", "OpenProcessToken", pathname);
NTTRACE("%s(%#hs) failed: %s", "OpenProcessToken", pathname,
strerror(errno));
}
} else {
e = GetLastError();
@ -115,11 +119,13 @@ TryAgain:
goto TryAgain;
} else {
rc = enomem();
NTTRACE("%s(%#hs) failed: %m", "GetFileSecurity", pathname);
NTTRACE("%s(%#hs) failed: %s", "GetFileSecurity", pathname,
strerror(errno));
}
} else {
errno = e;
NTTRACE("%s(%#hs) failed: %m", "GetFileSecurity", pathname);
NTTRACE("%s(%#hs) failed: %s", "GetFileSecurity", pathname,
strerror(errno));
rc = -1;
}
}

View file

@ -40,6 +40,13 @@ static textwindows int64_t sys_open_nt_impl(int dirfd, const char *path,
if (__mkntpathat(dirfd, path, flags, path16) == -1) {
return kNtInvalidHandleValue;
}
if (flags & O_NOFOLLOW) {
if ((attr = GetFileAttributes(path16)) != -1u && //
(attr & kNtFileAttributeReparsePoint)) {
return eloop();
}
flags &= ~O_NOFOLLOW;
}
if (GetNtOpenFlags(flags, mode, &perm, &share, &disp, &attr) == -1) {
return kNtInvalidHandleValue;
}
@ -57,7 +64,9 @@ static textwindows int sys_open_nt_console(int dirfd,
// 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()
g_fds.p[STDOUT_FILENO].dontclose = true;
g_fds.p[STDIN_FILENO].dontclose = true;
g_fds.p[fd].dontclose = true;
} else if ((g_fds.p[fd].handle = sys_open_nt_impl(
dirfd, mp->conin, (flags & ~O_ACCMODE) | O_RDONLY, mode,
kNtFileFlagOverlapped)) != -1) {

View file

@ -61,6 +61,19 @@
* // run `zip program.com hi.txt` beforehand
* openat(AT_FDCWD, "/zip/hi.txt", O_RDONLY);
*
* Cosmopolitan's general approach on Windows to path translation is to
*
* - replace `/' with `\`
* - translate utf-8 into utf-16
* - turn `"\X\foo"` into `"\\?\X:\foo"`
* - turn `"\X"` into `"\\?\X:\"`
* - turn `"X:\foo"` into `"\\?\X:\foo"`
*
* On Windows, opening files in `/tmp` will open them in GetTempPath(),
* which is a secure per-user directory. Opening `/dev/tty` will open a
* special console file descriptor holding both `CONIN$` and `CONOUT$`,
* which can't be fully closed. Opening `/dev/null` will open up `NUL`.
*
* @param dirfd is normally `AT_FDCWD` but if it's an open directory and
* `file` names a relative path then it's opened relative to `dirfd`
* @param file is a UTF-8 string naming filesystem entity, e.g. `foo/bar.txt`,
@ -81,12 +94,12 @@
* - `O_CLOEXEC` automatic close() upon execve()
* - `O_EXCL` exclusive access (see below)
* - `O_APPEND` open file for appending only
* - `O_NOFOLLOW` fail with ELOOP if it's a symlink
* - `O_NONBLOCK` asks read/write to fail with `EAGAIN` rather than block
* - `O_EXEC` open file for execution only; see fexecve()
* - `O_NOCTTY` prevents `file` possibly becoming controlling terminal
* - `O_NONBLOCK` asks read/write to fail with `EAGAIN` rather than block
* - `O_DIRECT` it's complicated (not supported on Apple and OpenBSD)
* - `O_DIRECTORY` useful for stat'ing (hint on UNIX but required on NT)
* - `O_NOFOLLOW` fail if it's a symlink (zero on Windows)
* - `O_DSYNC` it's complicated (zero on non-Linux/Apple)
* - `O_RSYNC` it's complicated (zero on non-Linux/Apple)
* - `O_VERIFY` it's complicated (zero on non-FreeBSD)

View file

@ -28,7 +28,7 @@
textwindows int sys_pause_nt(void) {
for (;;) {
if (_check_interrupts(0, g_fds.p)) {
if (_check_interrupts(0)) {
return -1;
}

View file

@ -69,7 +69,7 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms,
if (sigmask) {
__sig_mask(SIG_SETMASK, sigmask, &oldmask);
}
if ((rc = _check_interrupts(0, g_fds.p))) {
if ((rc = _check_interrupts(0))) {
goto ReturnPath;
}
@ -194,7 +194,7 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms,
}
// otherwise loop limitlessly for timeout to elapse while
// checking for signal delivery interrupts, along the way
if ((rc = _check_interrupts(0, g_fds.p))) {
if ((rc = _check_interrupts(0))) {
goto ReturnPath;
}
}

View file

@ -27,10 +27,6 @@
#include "libc/thread/tls.h"
#include "libc/thread/xnu.internal.h"
static textwindows inline bool HasWorkingConsole(void) {
return !!(__ntconsolemode[0] | __ntconsolemode[1] | __ntconsolemode[2]);
}
static dontubsan void RaiseSigFpe(void) {
volatile int x = 0;
x = 1 / x;

View file

@ -26,9 +26,12 @@
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/calls/wincrash.internal.h"
#include "libc/errno.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/nomultics.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/macros.internal.h"
#include "libc/nt/console.h"
#include "libc/nt/enum/filetype.h"
#include "libc/nt/enum/wait.h"
#include "libc/nt/errors.h"
@ -48,83 +51,12 @@
#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 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.
static textwindows void EchoTerminalInput(struct Fd *fd, char *p, size_t n) {
int64_t hOutput;
if (fd->kind == kFdConsole) {
hOutput = fd->extra;
} else {
hOutput = g_fds.p[1].handle;
}
if (__ttymagic & kFdTtyEchoRaw) {
WriteFile(hOutput, p, n, 0, 0);
} else {
size_t i;
for (i = 0; i < n; ++i) {
if (isascii(p[i]) && iscntrl(p[i]) && p[i] != '\n' && p[i] != '\t') {
char ctl[2];
ctl[0] = '^';
ctl[1] = p[i] ^ 0100;
WriteFile(hOutput, ctl, 2, 0, 0);
} else {
WriteFile(hOutput, p + i, 1, 0, 0);
}
}
}
}
static textwindows ssize_t sys_read_nt_impl(struct Fd *fd, void *data,
size_t size, int64_t offset) {
@ -133,11 +65,38 @@ static textwindows ssize_t sys_read_nt_impl(struct Fd *fd, void *data,
uint32_t got;
int filetype;
int64_t handle;
uint32_t remain;
uint32_t conmode;
char *targetdata;
uint32_t targetsize;
bool is_console_input;
int abort_errno = EAGAIN;
StartOver:
size = MIN(size, 0x7ffff000);
handle = __resolve_stdin_handle(fd->handle);
filetype = GetFileType(handle);
is_console_input = g_fds.stdin.handle ? fd->handle == g_fds.stdin.handle
: fd->handle == g_fds.p[0].handle;
// the caller might be reading a single byte at a time. but we need to
// be able to munge three bytes into just 1 e.g. "\342\220\200" → "\0"
if (size && fd->buflen) {
ReturnDataFromBuffer:
got = MIN(size, fd->buflen);
remain = fd->buflen - got;
if (got) memcpy(data, fd->buf, got);
if (remain) memmove(fd->buf, fd->buf + got, remain);
fd->buflen = remain;
return got;
}
if (is_console_input && size && size < 3 && (__ttymagic & kFdTtyMunging)) {
targetdata = fd->buf;
targetsize = sizeof(fd->buf);
} else {
targetdata = data;
targetsize = size;
}
if (filetype == kNtFileTypeChar || filetype == kNtFileTypePipe) {
struct NtOverlapped overlap = {0};
if (offset != -1) {
@ -147,7 +106,7 @@ StartOver:
if ((overlap.hEvent = CreateEvent(0, 0, 0, 0))) {
// the win32 manual says it's important to *not* put &got here
// since for overlapped i/o, we always use GetOverlappedResult
ok = ReadFile(handle, data, size, 0, &overlap);
ok = ReadFile(handle, targetdata, targetsize, 0, &overlap);
if (!ok && GetLastError() == kNtErrorIoPending) {
// the i/o operation is in flight; blocking is unavoidable
// if we're in a non-blocking mode, then immediately abort
@ -155,7 +114,7 @@ StartOver:
// otherwise wait for i/o periodically checking interrupts
if (fd->flags & O_NONBLOCK) {
sys_read_nt_abort(handle, &overlap);
} else if (_check_interrupts(kSigOpRestartable, g_fds.p)) {
} else if (_check_interrupts(kSigOpRestartable)) {
Interrupted:
abort_errno = errno;
sys_read_nt_abort(handle, &overlap);
@ -164,7 +123,7 @@ StartOver:
uint32_t i;
i = WaitForSingleObject(overlap.hEvent, __SIG_POLLING_INTERVAL_MS);
if (i == kNtWaitTimeout) {
if (_check_interrupts(kSigOpRestartable, g_fds.p)) {
if (_check_interrupts(kSigOpRestartable)) {
goto Interrupted;
}
} else {
@ -186,7 +145,7 @@ StartOver:
}
} else if (offset == -1) {
// perform simple blocking file read
ok = ReadFile(handle, data, size, &got, 0);
ok = ReadFile(handle, targetdata, targetsize, &got, 0);
} else {
// perform pread()-style file read at particular file offset
int64_t position;
@ -196,17 +155,17 @@ StartOver:
}
struct NtOverlapped overlap = {0};
overlap.Pointer = (void *)(uintptr_t)offset;
ok = ReadFile(handle, data, size, 0, &overlap);
ok = ReadFile(handle, targetdata, targetsize, 0, &overlap);
if (!ok && GetLastError() == kNtErrorIoPending) ok = true;
if (ok) ok = GetOverlappedResult(handle, &overlap, &got, true);
// restore file pointer which windows clobbers, even on error
unassert(SetFilePointerEx(handle, position, 0, SEEK_SET));
}
if (ok) {
if (g_fds.stdin.handle ? fd->handle == g_fds.stdin.handle
: fd->handle == g_fds.p[0].handle) {
if (is_console_input) {
if (__ttymagic & kFdTtyMunging) {
switch (MungeTerminalInput(data, &got)) {
switch (_weaken(__munge_terminal_input)(targetdata, &got)) {
case DO_NOTHING:
break;
case DO_RESTART:
@ -218,9 +177,13 @@ StartOver:
}
}
if (__ttymagic & kFdTtyEchoing) {
EchoTerminalInput(fd, data, got);
_weaken(__echo_terminal_input)(fd, targetdata, got);
}
}
if (targetdata != data) {
fd->buflen = got;
goto ReturnDataFromBuffer;
}
return got;
}
@ -245,7 +208,7 @@ textwindows ssize_t sys_read_nt(struct Fd *fd, const struct iovec *iov,
uint32_t size;
size_t i, total;
if (opt_offset < -1) return einval();
if (_check_interrupts(kSigOpRestartable, fd)) return -1;
if (_check_interrupts(kSigOpRestartable)) return -1;
while (iovlen && !iov[0].iov_len) iov++, iovlen--;
if (iovlen) {
for (total = i = 0; i < iovlen; ++i) {

View file

@ -5,7 +5,7 @@
#include "libc/calls/ucontext.h"
#define __SIG_QUEUE_LENGTH 32
#define __SIG_POLLING_INTERVAL_MS 50
#define __SIG_POLLING_INTERVAL_MS 20
#define __SIG_LOGGING_INTERVAL_MS 1700
#if !(__ASSEMBLER__ + __LINKER__ + 0)

View file

@ -77,7 +77,7 @@ int sigsuspend(const sigset_t *ignore) {
long totoms = 0;
#endif
do {
if ((rc = _check_interrupts(0, g_fds.p))) {
if ((rc = _check_interrupts(0))) {
break;
}
if (SleepEx(__SIG_POLLING_INTERVAL_MS, true) == kNtWaitIoCompletion) {

View file

@ -16,48 +16,57 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/atomic.h"
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/struct/fd.internal.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/winsize.h"
#include "libc/calls/struct/winsize.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/strace.internal.h"
#include "libc/nt/console.h"
#include "libc/nt/struct/consolescreenbufferinfoex.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/sicode.h"
#include "libc/sysv/consts/sig.h"
#include "libc/thread/tls.h"
#ifdef __x86_64__
static struct winsize __ws;
static atomic_uint __win_winsize;
textwindows void _check_sigwinch(struct Fd *fd) {
int e;
siginfo_t si;
struct winsize ws, old;
struct NtConsoleScreenBufferInfoEx sbinfo;
if (__tls_enabled && __threaded != gettid()) return;
old = __ws;
e = errno;
if (old.ws_row != 0xffff) {
if (tcgetwinsize_nt(fd, &ws) != -1) {
if (old.ws_col != ws.ws_col || old.ws_row != ws.ws_row) {
__ws = ws;
if (old.ws_col | old.ws_row) {
__sig_add(0, SIGWINCH, SI_KERNEL);
}
}
static textwindows unsigned __get_console_size(void) {
for (int fd = 1; fd < 10; ++fd) {
intptr_t hConsoleOutput;
if (g_fds.p[fd].kind == kFdConsole) {
hConsoleOutput = g_fds.p[fd].extra;
} else {
if (!old.ws_row && !old.ws_col) {
__ws.ws_row = 0xffff;
}
hConsoleOutput = g_fds.p[fd].handle;
}
struct NtConsoleScreenBufferInfoEx sr = {.cbSize = sizeof(sr)};
if (GetConsoleScreenBufferInfoEx(hConsoleOutput, &sr)) {
unsigned short yn = sr.srWindow.Bottom - sr.srWindow.Top + 1;
unsigned short xn = sr.srWindow.Right - sr.srWindow.Left + 1;
return (unsigned)yn << 16 | xn;
}
}
errno = e;
return -1u;
}
textwindows void _check_sigwinch(void) {
unsigned old = atomic_load_explicit(&__win_winsize, memory_order_acquire);
if (old == -1u) return;
unsigned neu = __get_console_size();
old = atomic_exchange(&__win_winsize, neu);
if (neu != old) {
__sig_add(0, SIGWINCH, SI_KERNEL);
}
}
__attribute__((__constructor__)) static void sigwinch_init(void) {
if (!IsWindows()) return;
unsigned ws = __get_console_size();
atomic_store_explicit(&__win_winsize, ws, memory_order_release);
STRACE("sigwinch_init() → %08x", ws);
}
#endif /* __x86_64__ */

View file

@ -20,19 +20,14 @@
#include "libc/sysv/consts/at.h"
/**
* Returns information about thing.
* Returns information about file.
*
* @param st is where result is stored
* @see S_ISDIR(st.st_mode), S_ISREG(), etc.
* @raise EACCES if denied access to component in path prefix
* @raise EIO if i/o error occurred while reading from filesystem
* @raise ELOOP if a symbolic link loop exists in `path`
* @raise ENAMETOOLONG if a component in `path` exceeds `NAME_MAX`
* @raise ENOENT on empty string or if component in path doesn't exist
* @raise ENOTDIR if a parent component existed that wasn't a directory
* @raise EOVERFLOW shouldn't be possible on 64-bit systems
* @raise ELOOP may ahappen if `SYMLOOP_MAX` symlinks were dereferenced
* @raise ENAMETOOLONG may happen if `path` exceeded `PATH_MAX`
* This function is equivalent to:
*
* struct stat st;
* fstatat(AT_FDCWD, path, &st, 0);
*
* @see fstatat() for further documentation
* @asyncsignalsafe
*/
int stat(const char *path, struct stat *st) {

View file

@ -3,6 +3,10 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
#define DO_NOTHING 0
#define DO_RESTART 1
#define DO_EINTR 2
#define kFdEmpty 0
#define kFdFile 1
#define kFdSocket 2
@ -23,6 +27,8 @@ struct Fd {
char kind;
bool zombie;
bool dontclose;
char buflen;
char buf[4];
unsigned flags;
unsigned mode;
int64_t handle;
@ -44,8 +50,10 @@ struct Fds {
struct StdinRelay stdin;
};
int64_t __resolve_stdin_handle(int64_t);
void WinMainStdin(void);
int64_t __resolve_stdin_handle(int64_t);
int __munge_terminal_input(char *, uint32_t *);
void __echo_terminal_input(struct Fd *, char *, size_t);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -65,7 +65,7 @@ const char *DescribeSigaction(char[256], int, const struct sigaction *);
void _init_onntconsoleevent(void);
void _init_wincrash(void);
void _check_sigwinch();
void _check_sigwinch(void);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -6,7 +6,7 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
int tcgetwinsize_nt(struct Fd *, struct winsize *);
int tcgetwinsize_nt(int, struct winsize *);
const char *DescribeWinsize(char[64], int, struct winsize *);
#define DescribeWinsize(rc, ws) DescribeWinsize(alloca(64), rc, ws)

View file

@ -33,8 +33,9 @@
static dontinline textwindows int sys_tcdrain_nt(int fd) {
if (!__isfdopen(fd)) return ebadf();
if (_check_interrupts(0, g_fds.p)) return -1;
if (!FlushFileBuffers(g_fds.p[fd].handle)) return __winerr();
if (_check_interrupts(0)) return -1;
// Tried FlushFileBuffers but it made Emacs hang when run in cmd.exe
// "Console output is not buffered." -Quoth MSDN on FlushFileBuffers
return 0;
}

View file

@ -17,67 +17,45 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/fd.internal.h"
#include "libc/calls/struct/winsize.h"
#include "libc/calls/struct/winsize.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/errno.h"
#include "libc/macros.internal.h"
#include "libc/nt/console.h"
#include "libc/nt/enum/startf.h"
#include "libc/nt/startupinfo.h"
#include "libc/nt/struct/consolescreenbufferinfoex.h"
#include "libc/nt/struct/startupinfo.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/fileno.h"
#include "libc/sysv/errfuns.h"
textwindows int tcgetwinsize_nt(struct Fd *fd, struct winsize *ws) {
int i, e, rc;
uint32_t mode;
struct Fd *fds[3];
struct NtStartupInfo startinfo;
struct NtConsoleScreenBufferInfoEx sbinfo;
rc = -1;
e = errno;
if (ws) {
__fds_lock();
fds[0] = fd, fds[1] = g_fds.p + 1, fds[2] = g_fds.p + 0;
GetStartupInfo(&startinfo);
for (i = 0; i < ARRAYLEN(fds); ++i) {
if (fds[i]->kind == kFdFile || fds[i]->kind == kFdConsole) {
if (GetConsoleMode(__getfdhandleactual(i), &mode)) {
bzero(&sbinfo, sizeof(sbinfo));
sbinfo.cbSize = sizeof(sbinfo);
if (GetConsoleScreenBufferInfoEx(__getfdhandleactual(i), &sbinfo)) {
ws->ws_col = sbinfo.srWindow.Right - sbinfo.srWindow.Left + 1;
ws->ws_row = sbinfo.srWindow.Bottom - sbinfo.srWindow.Top + 1;
ws->ws_xpixel = 0;
ws->ws_ypixel = 0;
errno = e;
rc = 0;
break;
} else if (startinfo.dwFlags & kNtStartfUsecountchars) {
ws->ws_col = startinfo.dwXCountChars;
ws->ws_row = startinfo.dwYCountChars;
ws->ws_xpixel = 0;
ws->ws_ypixel = 0;
errno = e;
rc = 0;
break;
} else {
__winerr();
}
} else {
enotty();
}
} else {
ebadf();
}
}
__fds_unlock();
} else {
efault();
textwindows int tcgetwinsize_nt(int fd, struct winsize *ws) {
// The Linux man page doesn't list EBADF as an errno for this.
if (!__isfdopen(fd)) {
return enotty();
}
// Unlike _check_sigwinch() this is an API so be stricter.
intptr_t hConsoleOutput;
if (fd == STDIN_FILENO) {
uint32_t dwMode;
// WIN32 doesn't allow GetConsoleScreenBufferInfoEx(stdin)
if (GetConsoleMode(g_fds.p[STDIN_FILENO].handle, &dwMode)) {
hConsoleOutput = g_fds.p[STDOUT_FILENO].handle;
if (GetConsoleMode(hConsoleOutput, &dwMode)) {
hConsoleOutput = g_fds.p[STDERR_FILENO].handle;
}
} else {
return enotty();
}
} else if (g_fds.p[fd].kind == kFdConsole) {
hConsoleOutput = g_fds.p[fd].extra;
} else {
hConsoleOutput = g_fds.p[fd].handle;
}
// Query the console.
struct NtConsoleScreenBufferInfoEx sr = {.cbSize = sizeof(sr)};
if (GetConsoleScreenBufferInfoEx(hConsoleOutput, &sr)) {
ws->ws_col = sr.srWindow.Right - sr.srWindow.Left + 1;
ws->ws_row = sr.srWindow.Bottom - sr.srWindow.Top + 1;
return 0;
} else {
return enotty();
}
return rc;
}

View file

@ -38,7 +38,7 @@ int tcgetwinsize(int fd, struct winsize *ws) {
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
rc = enotty();
} else if (IsWindows()) {
rc = tcgetwinsize_nt(g_fds.p + fd, ws);
rc = tcgetwinsize_nt(fd, ws);
} else {
rc = sys_ioctl(fd, TIOCGWINSZ, ws);
}

View file

@ -17,20 +17,151 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/struct/metatermios.internal.h"
#include "libc/calls/termios.internal.h"
#include "libc/calls/ttydefaults.h"
#include "libc/dce.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/nomultics.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/nt/console.h"
#include "libc/nt/enum/consolemodeflags.h"
#include "libc/nt/enum/version.h"
#include "libc/nt/runtime.h"
#include "libc/nt/version.h"
#include "libc/runtime/runtime.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__
textwindows int __munge_terminal_input(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';
}
}
}
bool delivered = false;
bool got_vintr = false;
bool got_vquit = false;
for (j = i = 0; i < *n; ++i) {
/*
It's not possible to type Ctrl+Space (aka ^@) into the CMD.EXE
console. Ctrl+Z is also problematic, since the Windows Console
processes that keystroke into an EOF event, even when we don't
enable input processing. These control codes are important for
Emacs users. One solution is to install the "Windows Terminal"
application from Microsoft Store, type Ctrl+Shift+, to get its
settings.json file opened, and add this code to its "actions":
"actions": [
{
"command": {
"action": "sendInput",
"input": ""
},
"keys": "ctrl+space"
},
{
"command": {
"action": "sendInput",
"input": ""
},
"keys": "ctrl+z"
},
{
"command": {
"action": "sendInput",
"input": "\u001f"
},
"keys": "ctrl+-"
}
],
Its not possible to configure Windows Terminal to output our
control codes. The workaround is to encode control sequences
using the "Control Pictures" UNICODE plane, which we'll then
translate back from UTF-8 glyphs, into actual control codes.
Another option Windows users can consider, particularly when
using CMD.EXE is installing Microsoft PowerTools whech makes
it possible to remap keys then configure ncurses to use them
https://github.com/microsoft/terminal/issues/2865
*/
int c;
if (i + 3 <= *n && // control pictures ascii nul ␀
(p[i + 0] & 255) == 0xe2 && // becomes ^@ a.k.a. ctrl-space
(p[i + 1] & 255) == 0x90 && // map the entire unicode plane
((p[i + 2] & 255) >= 0x80 && //
(p[i + 2] & 255) <= 0x9F)) {
c = (p[i + 2] & 255) - 0x80;
i += 2;
} else {
c = p[i] & 255;
}
if (!(__ttymagic & kFdTtyNoIsigs) && //
__vintr != _POSIX_VDISABLE && //
c == __vintr) {
got_vintr = true;
} else if (!(__ttymagic & kFdTtyNoIsigs) && //
__vquit != _POSIX_VDISABLE && //
c == __vquit) {
got_vquit = true;
} else {
p[j++] = c;
}
}
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.
textwindows void __echo_terminal_input(struct Fd *fd, char *p, size_t n) {
int64_t hOutput;
if (fd->kind == kFdConsole) {
hOutput = fd->extra;
} else {
hOutput = g_fds.p[1].handle;
}
if (__ttymagic & kFdTtyEchoRaw) {
WriteFile(hOutput, p, n, 0, 0);
} else {
size_t i;
for (i = 0; i < n; ++i) {
if (isascii(p[i]) && iscntrl(p[i]) && p[i] != '\n' && p[i] != '\t') {
char ctl[2];
ctl[0] = '^';
ctl[1] = p[i] ^ 0100;
WriteFile(hOutput, ctl, 2, 0, 0);
} else {
WriteFile(hOutput, p + i, 1, 0, 0);
}
}
}
}
textwindows int tcsetattr_nt(int fd, int opt, const struct termios *tio) {
bool32 ok;
int infd;
@ -122,3 +253,11 @@ textwindows int tcsetattr_nt(int fd, int opt, const struct termios *tio) {
return enotty();
}
}
__attribute__((__constructor__)) static void tcsetattr_nt_init(void) {
if (!getenv("TERM")) {
setenv("TERM", "xterm-256color", true);
}
}
#endif /* __x86_64__ */

View file

@ -17,14 +17,22 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/strace.internal.h"
#include "libc/sysv/consts/nr.h"
/**
* Sets file mode creation mask.
*
* On Windows, the value of umask() determines how Cosmopolitan goes
* about creating synthetic `st_mode` bits. The default umask is 077
* which is based on the assumption that Windows is being used for a
* single user. Don't assume that Cosmopolitan Libc will help you to
* secure a multitenant Windows computer.
*
* @return previous mask
* @note always succeeds
*/
@ -33,8 +41,7 @@ unsigned umask(unsigned newmask) {
if (!IsWindows()) {
oldmask = sys_umask(newmask);
} else {
// TODO(jart): what should we do with this?
oldmask = newmask;
oldmask = atomic_exchange(&__umask, newmask);
}
STRACE("umask(%#o) → %#o", oldmask);
return oldmask;

View file

@ -155,7 +155,7 @@ textwindows int sys_wait4_nt(int pid, int *opt_out_wstatus, int options,
sigaddset(&mask, SIGCHLD);
__sig_mask(SIG_BLOCK, &mask, &oldmask);
do {
rc = _check_interrupts(kSigOpRestartable | kSigOpNochld, 0);
rc = _check_interrupts(kSigOpRestartable | kSigOpNochld);
if (rc == -1) break;
__fds_lock();
rc = sys_wait4_nt_impl(&pid, opt_out_wstatus, options, opt_out_rusage);