Add missing ICANON features

This commit is contained in:
Justine Tunney 2024-09-05 03:17:19 -07:00
parent dd8544c3bd
commit 03875beadb
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
22 changed files with 526 additions and 251 deletions

View file

@ -1,9 +1,10 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_INTERNAL_H_
#define COSMOPOLITAN_LIBC_CALLS_INTERNAL_H_
#include "libc/atomic.h"
#include "libc/intrin/fds.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/struct/sigval.h"
#include "libc/dce.h"
#include "libc/intrin/fds.h"
#include "libc/macros.h"
#include "libc/stdbool.h"
@ -25,6 +26,7 @@ uint32_t sys_getuid_nt(void);
int __ensurefds_unlocked(int);
void __printfds(struct Fd *, size_t);
int CountConsoleInputBytes(void);
int CountConsoleInputBytesBlocking(uint32_t, sigset_t);
int FlushConsoleInputBytes(void);
int64_t GetConsoleInputHandle(void);
int64_t GetConsoleOutputHandle(void);

View file

@ -34,6 +34,7 @@
*
* @param flags can have AT_EMPTY_PATH or AT_SYMLINK_NOFOLLOW
* @return 0 on success, or -1 w/ errno
* @raise EROFS if either path is under /zip/...
* @asyncsignalsafe
*/
int linkat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath,
@ -42,7 +43,7 @@ int linkat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath,
if (_weaken(__zipos_notat) &&
((rc = __zipos_notat(olddirfd, oldpath)) == -1 ||
(rc = __zipos_notat(newdirfd, newpath)) == -1)) {
STRACE("zipos fchownat not supported yet");
rc = erofs();
} else if (!IsWindows()) {
rc = sys_linkat(olddirfd, oldpath, newdirfd, newpath, flags);
} else {

View file

@ -53,7 +53,7 @@
int mkdirat(int dirfd, const char *path, unsigned mode) {
int rc;
if (_weaken(__zipos_notat) && (rc = __zipos_notat(dirfd, path)) == -1) {
STRACE("zipos mkdirat not supported yet");
rc = erofs();
} else if (!IsWindows()) {
rc = sys_mkdirat(dirfd, path, mode);
} else {

View file

@ -34,9 +34,8 @@ static textwindows int _park_thread(uint32_t msdelay, sigset_t waitmask,
int sig, handler_was_called;
if (_check_cancel() == -1)
return -1;
if (_weaken(__sig_get) && (sig = _weaken(__sig_get)(waitmask))) {
if (_weaken(__sig_get) && (sig = _weaken(__sig_get)(waitmask)))
goto HandleSignal;
}
int expect = 0;
atomic_int futex = 0;
struct PosixThread *pt = _pthread_self();
@ -49,9 +48,11 @@ static textwindows int _park_thread(uint32_t msdelay, sigset_t waitmask,
handler_was_called = _weaken(__sig_relay)(sig, SI_KERNEL, waitmask);
if (_check_cancel() == -1)
return -1;
if (!restartable || (handler_was_called & SIG_HANDLED_NO_RESTART)) {
if (handler_was_called & SIG_HANDLED_NO_RESTART)
return eintr();
}
if (handler_was_called & SIG_HANDLED_SA_RESTART)
if (!restartable)
return eintr();
}
return 0;
}

View file

@ -79,15 +79,15 @@ static textwindows int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
bool ok;
uint64_t millis;
uint32_t cm, avail, waitfor;
struct sys_pollfd_nt pipefds[8];
struct sys_pollfd_nt pipefds[64];
struct sys_pollfd_nt sockfds[64];
int pipeindices[ARRAYLEN(pipefds)];
int sockindices[ARRAYLEN(sockfds)];
struct timespec started, deadline, remain, now;
struct timespec deadline, remain, now;
int i, rc, sn, pn, gotinvals, gotpipes, gotsocks;
started = timespec_real();
deadline = timespec_add(started, timespec_frommillis(ms ? *ms : -1u));
waitfor = ms ? *ms : -1u;
deadline = timespec_add(timespec_mono(), timespec_frommillis(waitfor));
// do the planning
// we need to read static variables
@ -168,16 +168,39 @@ static textwindows int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
pipefds[i].revents |= POLLERR_;
}
} else if (GetConsoleMode(pipefds[i].handle, &cm)) {
switch (CountConsoleInputBytes()) {
case 0:
break;
case -1:
pipefds[i].revents &= ~POLLWRNORM_;
pipefds[i].revents |= POLLHUP_;
break;
default:
pipefds[i].revents |= POLLRDNORM_;
break;
// some programs like bash like to poll([stdin], 1, -1) so let's
// avoid busy looping in such cases. we could generalize this to
// always avoid busy loops, but we'd need poll to launch threads
if (pn == 1 && sn == 0 && (pipefds[i].events & POLLRDNORM_)) {
int err = errno;
switch (CountConsoleInputBytesBlocking(waitfor, sigmask)) {
case -1:
if (errno == EINTR || errno == ECANCELED)
return -1;
errno = err;
pipefds[i].revents &= ~POLLWRNORM_;
pipefds[i].revents |= POLLERR_;
break;
case 0:
pipefds[i].revents &= ~POLLWRNORM_;
pipefds[i].revents |= POLLHUP_;
break;
default:
pipefds[i].revents |= POLLRDNORM_;
break;
}
} else {
switch (CountConsoleInputBytes()) {
case 0:
break;
case -1:
pipefds[i].revents &= ~POLLWRNORM_;
pipefds[i].revents |= POLLHUP_;
break;
default:
pipefds[i].revents |= POLLRDNORM_;
break;
}
}
} else {
// we have no way of polling if a non-socket is readable yet
@ -202,7 +225,7 @@ static textwindows int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
// check for pending signals, thread cancelation, etc.
waitfor = 0;
if (!gotinvals && !gotsocks && !gotpipes) {
now = timespec_real();
now = timespec_mono();
if (timespec_cmp(now, deadline) < 0) {
remain = timespec_sub(deadline, now);
millis = timespec_tomillis(remain);
@ -211,7 +234,7 @@ static textwindows int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
if (waitfor) {
POLLTRACE("poll() sleeping for %'d out of %'lu ms", waitfor,
timespec_tomillis(remain));
if ((rc = _park_norestart(waitfor, sigmask)) == -1)
if (_park_norestart(waitfor, sigmask) == -1)
return -1; // eintr, ecanceled, etc.
}
}

View file

@ -23,6 +23,7 @@
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/iovec.h"
#include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/struct/timespec.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/cosmo.h"
#include "libc/ctype.h"
@ -31,7 +32,9 @@
#include "libc/intrin/describeflags.h"
#include "libc/intrin/dll.h"
#include "libc/intrin/fds.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/nomultics.h"
#include "libc/intrin/safemacros.h"
#include "libc/intrin/strace.h"
#include "libc/intrin/weaken.h"
#include "libc/macros.h"
@ -132,6 +135,7 @@ struct Keystrokes {
atomic_uint once;
bool end_of_file;
bool ohno_decckm;
bool bypass_mode;
uint16_t utf16hs;
int16_t freekeys;
int64_t cin, cot;
@ -149,12 +153,12 @@ textwindows void WipeKeystrokes(void) {
bzero(&__keystroke, sizeof(__keystroke));
}
static textwindows void FreeKeystrokeImpl(struct Dll *key) {
textwindows static void FreeKeystrokeImpl(struct Dll *key) {
dll_make_first(&__keystroke.free, key);
++__keystroke.freekeys;
}
static textwindows struct Keystroke *NewKeystroke(void) {
textwindows static struct Keystroke *NewKeystroke(void) {
struct Dll *e = dll_first(__keystroke.free);
if (!e) // See MIN(freekeys) before ReadConsoleInput()
__builtin_trap();
@ -165,18 +169,18 @@ static textwindows struct Keystroke *NewKeystroke(void) {
return k;
}
static textwindows void FreeKeystroke(struct Dll **list, struct Dll *key) {
textwindows static void FreeKeystroke(struct Dll **list, struct Dll *key) {
dll_remove(list, key);
FreeKeystrokeImpl(key);
}
static textwindows void FreeKeystrokes(struct Dll **list) {
textwindows static void FreeKeystrokes(struct Dll **list) {
struct Dll *key;
while ((key = dll_first(*list)))
FreeKeystroke(list, key);
}
static textwindows void OpenConsole(void) {
textwindows static void OpenConsole(void) {
__keystroke.vkt = kVirtualKey;
__keystroke.cin = CreateFile(u"CONIN$", kNtGenericRead | kNtGenericWrite,
kNtFileShareRead, 0, kNtOpenExisting, 0, 0);
@ -188,21 +192,21 @@ static textwindows void OpenConsole(void) {
}
}
static textwindows int AddSignal(int sig) {
textwindows static int AddSignal(int sig) {
atomic_fetch_or_explicit(&__get_tls()->tib_sigpending, 1ull << (sig - 1),
memory_order_relaxed);
return 0;
}
static textwindows void InitConsole(void) {
textwindows static void InitConsole(void) {
cosmo_once(&__keystroke.once, OpenConsole);
}
static textwindows void LockKeystrokes(void) {
textwindows static void LockKeystrokes(void) {
pthread_mutex_lock(&__keystroke.lock);
}
static textwindows void UnlockKeystrokes(void) {
textwindows static void UnlockKeystrokes(void) {
pthread_mutex_unlock(&__keystroke.lock);
}
@ -216,14 +220,14 @@ textwindows int64_t GetConsoleOutputHandle(void) {
return __keystroke.cot;
}
static textwindows bool IsMouseModeCommand(int x) {
textwindows static bool IsMouseModeCommand(int x) {
return x == 1000 || // SET_VT200_MOUSE
x == 1002 || // SET_BTN_EVENT_MOUSE
x == 1006 || // SET_SGR_EXT_MODE_MOUSE
x == 1015; // SET_URXVT_EXT_MODE_MOUSE
}
static textwindows int GetVirtualKey(uint16_t vk, bool shift, bool ctrl) {
textwindows static int GetVirtualKey(uint16_t vk, bool shift, bool ctrl) {
for (int i = 0; __keystroke.vkt[i].vk; ++i) {
if (__keystroke.vkt[i].vk == vk) {
if (shift && ctrl) {
@ -240,7 +244,7 @@ static textwindows int GetVirtualKey(uint16_t vk, bool shift, bool ctrl) {
return 0;
}
static textwindows int ProcessKeyEvent(const struct NtInputRecord *r, char *p) {
textwindows static int ProcessKeyEvent(const struct NtInputRecord *r, char *p) {
uint32_t c = r->Event.KeyEvent.uChar.UnicodeChar;
uint16_t vk = r->Event.KeyEvent.wVirtualKeyCode;
@ -327,7 +331,7 @@ static textwindows int ProcessKeyEvent(const struct NtInputRecord *r, char *p) {
// handle ctrl-\ and ctrl-c
// note we define _POSIX_VDISABLE as zero
// tcsetattr() lets anyone reconfigure these keybindings
if (c && !(__ttyconf.magic & kTtyNoIsigs)) {
if (c && !(__ttyconf.magic & kTtyNoIsigs) && !__keystroke.bypass_mode) {
if (c == __ttyconf.vintr) {
return AddSignal(SIGINT);
} else if (c == __ttyconf.vquit) {
@ -337,7 +341,9 @@ static textwindows int ProcessKeyEvent(const struct NtInputRecord *r, char *p) {
// handle ctrl-d which generates end-of-file, unless pending line data
// is present, in which case we flush that without the newline instead
if (c && c == __ttyconf.veof && !(__ttyconf.magic & kTtyUncanon)) {
if (c && c == __ttyconf.veof && //
!__keystroke.bypass_mode && //
!(__ttyconf.magic & kTtyUncanon)) {
if (dll_is_empty(__keystroke.line)) {
__keystroke.end_of_file = true;
} else {
@ -367,7 +373,7 @@ static textwindows int ProcessKeyEvent(const struct NtInputRecord *r, char *p) {
// - write(1, "\e[?1000;1002;1015;1006h") to enable
// - write(1, "\e[?1000;1002;1015;1006l") to disable
// See o//examples/ttyinfo and o//tool/viz/life
static textwindows int ProcessMouseEvent(const struct NtInputRecord *r,
textwindows static int ProcessMouseEvent(const struct NtInputRecord *r,
char *b) {
char *p = b;
unsigned char e = 0;
@ -424,7 +430,7 @@ static textwindows int ProcessMouseEvent(const struct NtInputRecord *r,
return p - b;
}
static textwindows int ConvertConsoleInputToAnsi(const struct NtInputRecord *r,
textwindows static int ConvertConsoleInputToAnsi(const struct NtInputRecord *r,
char p[hasatleast 23]) {
switch (r->EventType) {
case kNtKeyEvent:
@ -438,18 +444,19 @@ static textwindows int ConvertConsoleInputToAnsi(const struct NtInputRecord *r,
}
}
static textwindows void WriteTty(const char *p, size_t n) {
textwindows static void WriteTty(const char *p, size_t n) {
WriteFile(__keystroke.cot, p, n, 0, 0);
}
static textwindows bool IsCtl(int c) {
return isascii(c) && iscntrl(c) && c != '\n' && c != '\t';
textwindows static bool IsCtl(int c, bool escape_harder) {
return isascii(c) && iscntrl(c) &&
(escape_harder || (c != '\n' && c != '\t'));
}
static textwindows void WriteCtl(const char *p, size_t n) {
textwindows static void WriteCtl(const char *p, size_t n, bool escape_harder) {
size_t i;
for (i = 0; i < n; ++i) {
if (IsCtl(p[i])) {
if (IsCtl(p[i], escape_harder)) {
char ctl[2];
ctl[0] = '^';
ctl[1] = p[i] ^ 0100;
@ -460,19 +467,22 @@ static textwindows void WriteCtl(const char *p, size_t n) {
}
}
static textwindows void EchoTty(const char *p, size_t n) {
if (__ttyconf.magic & kTtyEchoRaw) {
WriteTty(p, n);
} else {
WriteCtl(p, n);
textwindows static void EchoTty(const char *p, size_t n, bool escape_harder) {
if (!(__ttyconf.magic & kTtySilence)) {
if (__ttyconf.magic & kTtyEchoRaw) {
WriteTty(p, n);
} else {
WriteCtl(p, n, escape_harder);
}
}
}
static textwindows void EraseCharacter(void) {
WriteTty("\b \b", 3);
textwindows static void EraseCharacter(bool should_echo) {
if (should_echo)
WriteTty("\b \b", 3);
}
static textwindows bool EraseKeystroke(void) {
textwindows static bool EraseKeystroke(bool should_echo) {
struct Dll *e;
if ((e = dll_last(__keystroke.line))) {
struct Keystroke *k = KEYSTROKE_CONTAINER(e);
@ -480,9 +490,9 @@ static textwindows bool EraseKeystroke(void) {
for (int i = k->buflen; i--;) {
if ((k->buf[i] & 0300) == 0200)
continue; // utf-8 cont
EraseCharacter();
if (!(__ttyconf.magic & kTtyEchoRaw) && IsCtl(k->buf[i]))
EraseCharacter();
EraseCharacter(should_echo);
if (!(__ttyconf.magic & kTtyEchoRaw) && IsCtl(k->buf[i], true))
EraseCharacter(should_echo);
}
return true;
} else {
@ -490,7 +500,17 @@ static textwindows bool EraseKeystroke(void) {
}
}
static textwindows void IngestConsoleInputRecord(struct NtInputRecord *r) {
textwindows static int IsLookingAtSpace(void) {
struct Dll *e;
if ((e = dll_last(__keystroke.line))) {
struct Keystroke *k = KEYSTROKE_CONTAINER(e);
return k->buflen == 1 && isascii(k->buf[0]) && isspace(k->buf[0]);
} else {
return -1;
}
}
textwindows static void IngestConsoleInputRecord(struct NtInputRecord *r) {
// convert win32 console event into ansi
int len;
@ -498,19 +518,103 @@ static textwindows void IngestConsoleInputRecord(struct NtInputRecord *r) {
if (!(len = ConvertConsoleInputToAnsi(r, buf)))
return;
// handle ctrl-v in canonical mode
// the next keystroke will bypass input processing
if (!(__ttyconf.magic & kTtyUncanon) && // ICANON
!(__ttyconf.magic & kTtyNoIexten)) { // IEXTEN
if (__keystroke.bypass_mode) {
struct Keystroke *k = NewKeystroke();
memcpy(k->buf, buf, sizeof(k->buf));
k->buflen = len;
dll_make_last(&__keystroke.line, &k->elem);
EchoTty(buf, len, true);
if (!__keystroke.freekeys) {
dll_make_last(&__keystroke.list, __keystroke.line);
__keystroke.line = 0;
}
__keystroke.bypass_mode = false;
return;
} else if (len == 1 && buf[0] && //
(buf[0] & 255) == __ttyconf.vlnext) {
__keystroke.bypass_mode = true;
if (!(__ttyconf.magic & kTtySilence) && // ECHO
!(__ttyconf.magic & kTtyEchoRaw)) // ECHOCTL
WriteTty("^\b", 2);
return;
}
}
// handle backspace in canonical mode
if (len == 1 && buf[0] && //
(buf[0] & 255) == __ttyconf.verase && //
!(__ttyconf.magic & kTtyUncanon)) {
EraseKeystroke();
!(__ttyconf.magic & kTtyUncanon) && //
!(__ttyconf.magic & kTtyNoIexten)) {
bool should_visually_erase = //
!(__ttyconf.magic & kTtySilence) && // ECHO
!(__ttyconf.magic & kTtyNoEchoe); // ECHOE
EraseKeystroke(should_visually_erase);
if (!(__ttyconf.magic & kTtySilence) && // ECHO
(__ttyconf.magic & kTtyNoEchoe) && // !ECHOE
!(__ttyconf.magic & kTtyEchoRaw)) // ECHOCTL
WriteCtl(buf, len, true);
return;
}
// handle ctrl-w in canonical mode
// this lets you erase the last word
if (len == 1 && buf[0] && //
(buf[0] & 255) == __ttyconf.vwerase && //
!(__ttyconf.magic & kTtyUncanon) && //
!(__ttyconf.magic & kTtyNoIexten)) {
bool should_visually_erase = //
!(__ttyconf.magic & kTtySilence) && // ECHO
!(__ttyconf.magic & kTtyNoEchoe); // ECHOE
while (IsLookingAtSpace() == 1)
EraseKeystroke(should_visually_erase);
while (IsLookingAtSpace() == 0)
EraseKeystroke(should_visually_erase);
if (!(__ttyconf.magic & kTtySilence) && // ECHO
(__ttyconf.magic & kTtyNoEchoe) && // !ECHOE
!(__ttyconf.magic & kTtyEchoRaw)) // ECHOCTL
WriteCtl(buf, len, true);
return;
}
// handle kill in canonical mode
// this clears the line you're editing
if (len == 1 && buf[0] && //
(buf[0] & 255) == __ttyconf.vkill && //
!(__ttyconf.magic & kTtyUncanon)) {
while (EraseKeystroke()) {
!(__ttyconf.magic & kTtyUncanon) && //
!(__ttyconf.magic & kTtyNoIexten)) {
bool should_visually_kill = //
!(__ttyconf.magic & kTtySilence) && // ECHO
!(__ttyconf.magic & kTtyNoEchok) && // ECHOK
!(__ttyconf.magic & kTtyNoEchoke); // ECHOKE
while (EraseKeystroke(should_visually_kill)) {
}
if (!(__ttyconf.magic & kTtySilence) && // ECHO
!(__ttyconf.magic & kTtyNoEchok) && // ECHOK
(__ttyconf.magic & kTtyNoEchoke) && // !ECHOKE
!(__ttyconf.magic & kTtyEchoRaw)) // ECHOCTL
WriteCtl(buf, len, true);
return;
}
// handle ctrl-r in canonical mode
// this reprints the line you're editing
if (len == 1 && buf[0] && //
(buf[0] & 255) == __ttyconf.vreprint && //
!(__ttyconf.magic & kTtyUncanon) && // ICANON
!(__ttyconf.magic & kTtyNoIexten) && // IEXTEN
!(__ttyconf.magic & kTtySilence)) { // ECHO
struct Dll *e;
if (!(__ttyconf.magic & kTtyEchoRaw))
WriteCtl(buf, len, true);
WriteTty("\r\n", 2);
for (e = dll_first(__keystroke.line); e;
e = dll_next(__keystroke.line, e)) {
struct Keystroke *k = KEYSTROKE_CONTAINER(e);
WriteCtl(k->buf, k->buflen, true);
}
return;
}
@ -522,8 +626,7 @@ static textwindows void IngestConsoleInputRecord(struct NtInputRecord *r) {
// echo input if it was successfully recorded
// assuming the win32 console isn't doing it already
if (!(__ttyconf.magic & kTtySilence))
EchoTty(buf, len);
EchoTty(buf, len, false);
// save keystroke to appropriate list
if (__ttyconf.magic & kTtyUncanon) {
@ -535,14 +638,15 @@ static textwindows void IngestConsoleInputRecord(struct NtInputRecord *r) {
if (!__keystroke.freekeys || (len == 1 && buf[0] &&
((buf[0] & 255) == '\n' || //
(buf[0] & 255) == __ttyconf.veol || //
(buf[0] & 255) == __ttyconf.veol2))) {
((buf[0] & 255) == __ttyconf.veol2 &&
!(__ttyconf.magic & kTtyNoIexten))))) {
dll_make_last(&__keystroke.list, __keystroke.line);
__keystroke.line = 0;
}
}
}
static textwindows void IngestConsoleInput(void) {
textwindows static void IngestConsoleInput(void) {
uint32_t i, n;
struct NtInputRecord records[16];
for (;;) {
@ -552,7 +656,7 @@ static textwindows void IngestConsoleInput(void) {
return;
if (!GetNumberOfConsoleInputEvents(__keystroke.cin, &n))
goto UnexpectedEof;
if (!n)
if (!n || !__keystroke.freekeys)
return;
n = MIN(__keystroke.freekeys, MIN(ARRAYLEN(records), n));
if (!ReadConsoleInput(__keystroke.cin, records, n, &n))
@ -657,12 +761,11 @@ textwindows void InterceptTerminalCommands(const char *data, size_t size) {
__builtin_unreachable();
}
}
if (cm2 != cm) {
if (cm2 != cm)
SetConsoleMode(GetConsoleInputHandle(), cm2);
}
}
static textwindows bool DigestConsoleInput(char *data, size_t size, int *rc) {
textwindows static bool DigestConsoleInput(char *data, size_t size, int *rc) {
// handle eof once available input is consumed
if (dll_is_empty(__keystroke.list) && __keystroke.end_of_file) {
@ -702,21 +805,42 @@ static textwindows bool DigestConsoleInput(char *data, size_t size, int *rc) {
}
}
static textwindows int WaitForConsole(struct Fd *f, sigset_t waitmask) {
textwindows static uint32_t DisableProcessedInput(void) {
// the time has come to ensure that ctrl-v ctrl-c works in icanon mode
// we're perfectly capable of generating a SIGINT or SIGQUIT ourselves
// while the cosmo termios driver is in control; so we disable windows
// console input processing for now; we'll turn it back on when we are
// done, since it's useful for ensuring asynchronous signal deliveries
uint32_t inmode = 0;
if (GetConsoleMode(__keystroke.cin, &inmode))
if (inmode & kNtEnableProcessedInput)
SetConsoleMode(__keystroke.cin, inmode & ~kNtEnableProcessedInput);
return inmode;
}
textwindows static void RestoreProcessedInput(uint32_t inmode) {
// re-enable win32 console input processing, if it was enabled when we
// started, and no signal handler callbacks changed things in-between.
if (inmode & kNtEnableProcessedInput) {
uint32_t inmode2;
if (GetConsoleMode(__keystroke.cin, &inmode2))
if (inmode2 == (inmode & ~kNtEnableProcessedInput))
SetConsoleMode(__keystroke.cin, inmode);
}
}
textwindows static int CountConsoleInputBytesBlockingImpl(uint32_t ms,
sigset_t waitmask,
bool restartable) {
int sig;
int64_t sem;
uint32_t wi, ms = -1;
if (!__ttyconf.vmin) {
if (!__ttyconf.vtime) {
return 0; // non-blocking w/o raising eagain
} else {
ms = __ttyconf.vtime * 100;
}
}
uint32_t wi;
struct timespec now, deadline;
InitConsole();
deadline = timespec_add(timespec_mono(), timespec_frommillis(ms));
RestartOperation:
if (_check_cancel() == -1)
return -1;
if (f->flags & _O_NONBLOCK)
return eagain();
if (_weaken(__sig_get) && (sig = _weaken(__sig_get)(waitmask)))
goto DeliverSignal;
struct PosixThread *pt = _pthread_self();
@ -726,12 +850,40 @@ static textwindows int WaitForConsole(struct Fd *f, sigset_t waitmask) {
wi = WaitForMultipleObjects(2, (int64_t[2]){__keystroke.cin, sem}, 0, ms);
atomic_store_explicit(&pt->pt_blocker, 0, memory_order_release);
CloseHandle(sem);
// check for wait timeout
if (wi == kNtWaitTimeout)
return 0; // vtime elapsed
if (wi == 0)
return -2; // console data
return etimedout();
// handle event on console handle. this means we can now read from the
// conosle without blocking. so the first thing we do is slurp up your
// keystroke data. some of those keystrokes might cause a signal to be
// raised. so we need to check for pending signals again and handle it
if (wi == 0) {
int got = CountConsoleInputBytes();
if (_weaken(__sig_get) && (sig = _weaken(__sig_get)(waitmask)))
goto DeliverSignal;
if (got == -1)
// this is a bona fide eof and console errors are logged to strace
return 0;
if (got == 0) {
// this can happen for multiple reasons. first our driver controls
// user interactions in canonical mode. secondly we could lose the
// race with another thread that's reading input.
now = timespec_mono();
if (timespec_cmp(now, deadline) >= 0)
return etimedout();
ms = min(-1u, timespec_tomillis(timespec_sub(deadline, now)));
goto RestartOperation;
}
return got;
}
// handle wait itself failing
if (wi != 1)
return __winerr(); // wait failed
return __winerr();
// handle event on throwaway semaphore, it is poked by signal delivery
if (_weaken(__sig_get)) {
if (!(sig = _weaken(__sig_get)(waitmask)))
return eintr();
@ -739,24 +891,57 @@ static textwindows int WaitForConsole(struct Fd *f, sigset_t waitmask) {
int handler_was_called = _weaken(__sig_relay)(sig, SI_KERNEL, waitmask);
if (_check_cancel() == -1)
return -1;
if (!(handler_was_called & SIG_HANDLED_NO_RESTART))
return -2;
if (handler_was_called & SIG_HANDLED_NO_RESTART)
return eintr();
if (handler_was_called & SIG_HANDLED_SA_RESTART)
if (!restartable)
return eintr();
}
return eintr();
goto RestartOperation;
}
static textwindows ssize_t ReadFromConsole(struct Fd *f, void *data,
textwindows int CountConsoleInputBytesBlocking(uint32_t ms, sigset_t waitmask) {
uint32_t inmode = DisableProcessedInput();
int rc = CountConsoleInputBytesBlockingImpl(ms, waitmask, false);
RestoreProcessedInput(inmode);
return rc;
}
textwindows static int WaitToReadFromConsole(struct Fd *f, sigset_t waitmask) {
uint32_t ms = -1;
if (!__ttyconf.vmin) {
if (!__ttyconf.vtime) {
return 0; // non-blocking w/o raising eagain
} else {
ms = __ttyconf.vtime * 100;
}
}
if (f->flags & _O_NONBLOCK)
return eagain();
int olderr = errno;
int rc = CountConsoleInputBytesBlockingImpl(ms, waitmask, true);
if (rc == -1 && errno == ETIMEDOUT) {
// read() never raises ETIMEDOUT so if vtime elapses we raise an EOF
errno = olderr;
rc = 0;
}
return rc;
}
textwindows static ssize_t ReadFromConsole(struct Fd *f, void *data,
size_t size, sigset_t waitmask) {
int rc;
InitConsole();
uint32_t inmode = DisableProcessedInput();
do {
LockKeystrokes();
IngestConsoleInput();
bool done = DigestConsoleInput(data, size, &rc);
UnlockKeystrokes();
if (done)
return rc;
} while ((rc = WaitForConsole(f, waitmask)) == -2);
break;
} while ((rc = WaitToReadFromConsole(f, waitmask)) > 0);
RestoreProcessedInput(inmode);
return rc;
}
@ -794,7 +979,7 @@ textwindows ssize_t ReadBuffer(int fd, void *data, size_t size, int64_t offset,
}
}
static textwindows ssize_t ReadIovecs(int fd, const struct iovec *iov,
textwindows static ssize_t ReadIovecs(int fd, const struct iovec *iov,
size_t iovlen, int64_t opt_offset,
sigset_t waitmask) {
ssize_t rc;

View file

@ -57,6 +57,7 @@ ssize_t readlinkat(int dirfd, const char *path, char *buf, size_t bufsiz) {
} else if (_weaken(__zipos_notat) &&
(bytes = __zipos_notat(dirfd, path)) == -1) {
STRACE("TODO: zipos support for readlinkat");
bytes = einval();
} else if (!IsWindows()) {
bytes = sys_readlinkat(dirfd, path, buf, bufsiz);
} else {

View file

@ -86,9 +86,8 @@ RestartOperation:
__cursor_unlock(f->cursor);
return -1; // ECANCELED
}
if (_weaken(__sig_get) && (sig = _weaken(__sig_get)(waitmask))) {
if (_weaken(__sig_get) && (sig = _weaken(__sig_get)(waitmask)))
goto HandleInterrupt;
}
// signals have already been fully blocked by caller
// perform i/o operation with atomic signal/cancel checking

View file

@ -41,6 +41,7 @@
* @param newdirfd is normally AT_FDCWD but if it's an open directory
* and newpath is relative, then newpath become relative to dirfd
* @return 0 on success, or -1 w/ errno
* @raise EROFS if either path is under /zip/...
*/
int renameat(int olddirfd, const char *oldpath, int newdirfd,
const char *newpath) {
@ -48,7 +49,7 @@ int renameat(int olddirfd, const char *oldpath, int newdirfd,
if (_weaken(__zipos_notat) &&
((rc = __zipos_notat(olddirfd, oldpath)) == -1 ||
(rc = __zipos_notat(newdirfd, newpath)) == -1)) {
STRACE("zipos renameat not supported yet");
rc = erofs();
} else if (!IsWindows()) {
rc = sys_renameat(olddirfd, oldpath, newdirfd, newpath);
} else {

View file

@ -18,9 +18,9 @@
*/
#include "libc/assert.h"
#include "libc/calls/internal.h"
#include "libc/intrin/fds.h"
#include "libc/calls/struct/termios.h"
#include "libc/calls/syscall-nt.internal.h"
#include "libc/intrin/fds.h"
#include "libc/intrin/nomultics.h"
#include "libc/nt/console.h"
#include "libc/nt/enum/consolemodeflags.h"
@ -58,32 +58,34 @@ textwindows int tcgetattr_nt(int fd, struct termios *tio) {
// kNtEnableLineInput and kNtEnableEchoInput only apply to programs
// that call ReadFile() or ReadConsole(). since we do not use them,
// the flags could serve the purpose of inter-process communication
if ((inmode & kNtEnableLineInput) || !(__ttyconf.magic & kTtyUncanon)) {
if ((inmode & kNtEnableLineInput) || !(__ttyconf.magic & kTtyUncanon))
tio->c_lflag |= ICANON;
}
// kNtEnableEchoInput only works with kNtEnableLineInput enabled.
if ((inmode & kNtEnableEchoInput) || !(__ttyconf.magic & kTtySilence)) {
if ((inmode & kNtEnableEchoInput) || !(__ttyconf.magic & kTtySilence))
tio->c_lflag |= ECHO;
}
// The Windows console itself always echos control codes as ASCII.
if ((inmode & kNtEnableEchoInput) || !(__ttyconf.magic & kTtyEchoRaw)) {
if (!(__ttyconf.magic & kTtyEchoRaw))
tio->c_lflag |= ECHOCTL;
}
if (!(__ttyconf.magic & kTtyNoCr2Nl)) {
if (!(__ttyconf.magic & kTtyNoEchoe))
tio->c_lflag |= ECHOE;
if (!(__ttyconf.magic & kTtyNoEchok))
tio->c_lflag |= ECHOK;
if (!(__ttyconf.magic & kTtyNoEchoke))
tio->c_lflag |= ECHOKE;
if (!(__ttyconf.magic & kTtyNoCr2Nl))
tio->c_iflag |= ICRNL;
}
if (!(__ttyconf.magic & kTtyNoIsigs)) {
if (!(__ttyconf.magic & kTtyNoIsigs))
tio->c_lflag |= ISIG;
}
if (inmode & kNtEnableProcessedInput) {
if (!(__ttyconf.magic & kTtyNoIexten))
tio->c_lflag |= IEXTEN;
}
if (outmode & kNtEnableProcessedOutput) {
if (outmode & kNtEnableProcessedOutput)
tio->c_oflag |= OPOST;
}
if (!(outmode & kNtDisableNewlineAutoReturn)) {
if (!(outmode & kNtDisableNewlineAutoReturn))
tio->c_oflag |= OPOST | ONLCR;
}
return 0;
}

View file

@ -18,10 +18,10 @@
*/
#include "libc/assert.h"
#include "libc/calls/internal.h"
#include "libc/intrin/fds.h"
#include "libc/calls/struct/termios.h"
#include "libc/calls/syscall-nt.internal.h"
#include "libc/calls/ttydefaults.h"
#include "libc/intrin/fds.h"
#include "libc/intrin/nomultics.h"
#include "libc/nt/console.h"
#include "libc/nt/enum/consolemodeflags.h"
@ -61,40 +61,50 @@ textwindows int tcsetattr_nt(int fd, int opt, const struct termios *tio) {
inmode &= ~kNtEnableQuickEditMode;
__ttyconf.magic |= kTtyUncanon;
}
if (!(tio->c_iflag & ICRNL)) {
if (!(tio->c_iflag & ICRNL))
__ttyconf.magic |= kTtyNoCr2Nl;
}
if (!(tio->c_lflag & ECHOCTL)) {
if (!(tio->c_lflag & ECHOE))
__ttyconf.magic |= kTtyNoEchoe;
if (!(tio->c_lflag & ECHOK))
__ttyconf.magic |= kTtyNoEchok;
if (!(tio->c_lflag & ECHOKE))
__ttyconf.magic |= kTtyNoEchoke;
if (!(tio->c_lflag & ECHOCTL))
__ttyconf.magic |= kTtyEchoRaw;
}
if (tio->c_lflag & ECHO) {
// "kNtEnableEchoInput can be used only if the
// kNtEnableLineInput mode is also enabled." -MSDN
if (tio->c_lflag & ICANON) {
if (tio->c_lflag & ICANON)
inmode |= kNtEnableEchoInput;
}
} else {
__ttyconf.magic |= kTtySilence;
}
if (!(tio->c_lflag & ISIG)) {
if (!(tio->c_lflag & ISIG))
__ttyconf.magic |= kTtyNoIsigs;
}
// IEXTEN enables implementation-defined input processing. This flag,
// as well as ICANON must be enabled for the special characters EOL2,
// LNEXT, REPRINT, WERASE to be interpreted.
if (!(tio->c_lflag & IEXTEN))
__ttyconf.magic |= kTtyNoIexten;
memcpy(__ttyconf.c_cc, tio->c_cc, NCCS);
if ((tio->c_lflag & ISIG) && //
!(tio->c_lflag & ICANON) && //
__ttyconf.vintr == CTRL('C')) {
if ((tio->c_lflag & ISIG) && __ttyconf.vintr == CTRL('C'))
// allows ctrl-c to be delivered asynchronously via win32
// we normally don't want win32 doing this 24/7 in the bg
// because we don't have job control, tcsetpgrp, etc. yet
// it's normally much better to let read-nt.c raise a sig
// because read-nt only manages your tty whilst it's used
// because read-nt only manages your tty while it is used
inmode |= kNtEnableProcessedInput;
}
outmode &= ~kNtDisableNewlineAutoReturn;
outmode |= kNtEnableProcessedOutput;
if (!(tio->c_oflag & ONLCR)) {
if (!(tio->c_oflag & ONLCR))
outmode |= kNtDisableNewlineAutoReturn;
}
outmode |= kNtEnableVirtualTerminalProcessing;
// tune the win32 configuration

View file

@ -29,6 +29,6 @@
*/
struct timespec timespec_mono(void) {
struct timespec ts;
npassert(!clock_gettime(CLOCK_MONOTONIC, &ts));
unassert(!clock_gettime(CLOCK_MONOTONIC, &ts));
return ts;
}

View file

@ -39,12 +39,13 @@
* @param path is the thing to delete
* @param flags can have AT_REMOVEDIR
* @return 0 on success, or -1 w/ errno
* @raise EROFS if either path is under /zip/...
*/
int unlinkat(int dirfd, const char *path, int flags) {
int rc;
if (_weaken(__zipos_notat) && (rc = __zipos_notat(dirfd, path)) == -1) {
STRACE("zipos unlinkat not supported yet");
rc = erofs();
} else if (!IsWindows()) {
rc = sys_unlinkat(dirfd, path, flags);
} else {

View file

@ -1,17 +1,21 @@
#ifndef COSMOPOLITAN_NOMULTICS_H_
#define COSMOPOLITAN_NOMULTICS_H_
#define kTtySilence 1 /* do not relay read() into write() */
#define kTtyEchoRaw 2 /* don't ^X visualize control codes */
#define kTtyUncanon 4 /* enables non-canonical (raw) mode */
#define kTtyNoCr2Nl 8 /* don't map \r → \n (a.k.a !ICRNL) */
#define kTtyNoIsigs 16 /* don't auto-raise signals on keys */
#define kTtyXtMouse 32 /* enables eXtreme Xterm mouse mode */
#define kTtySilence 1 /* do not relay read() into write() */
#define kTtyEchoRaw 2 /* don't ^X visualize control codes */
#define kTtyUncanon 4 /* enables non-canonical (raw) mode */
#define kTtyNoCr2Nl 8 /* don't map \r → \n (a.k.a !ICRNL) */
#define kTtyNoIsigs 16 /* don't auto-raise signals on keys */
#define kTtyXtMouse 32 /* enables eXtreme Xterm mouse mode */
#define kTtyNoIexten 64 /* disable various canon keystrokes */
#define kTtyNoEchoe 128
#define kTtyNoEchok 256
#define kTtyNoEchoke 512
COSMOPOLITAN_C_START_
struct TtyConf {
unsigned char magic;
unsigned magic;
unsigned char mousebs;
unsigned char replmode;
unsigned char replstderr;

View file

@ -98,14 +98,14 @@ static int _forker(uint32_t dwCreationFlags) {
struct timespec started;
int ax, dx, tid, parent;
parent = __pid;
started = timespec_real();
started = timespec_mono();
_onfork_prepare();
if (!IsWindows()) {
ax = sys_fork();
} else {
ax = sys_fork_nt(dwCreationFlags);
}
micros = timespec_tomicros(timespec_sub(timespec_real(), started));
micros = timespec_tomicros(timespec_sub(timespec_mono(), started));
if (!ax) {
// get new process id

View file

@ -74,17 +74,14 @@ static textwindows int __proc_wait(int pid, int *wstatus, int options,
// check for signals and cancelation
int sig, handler_was_called;
if (_check_cancel() == -1) {
if (_check_cancel() == -1)
return -1;
}
if (_weaken(__sig_get) && (sig = _weaken(__sig_get)(waitmask))) {
handler_was_called = _weaken(__sig_relay)(sig, SI_KERNEL, waitmask);
if (_check_cancel() == -1) {
if (_check_cancel() == -1)
return -1; // ECANCELED because SIGTHR was just handled
}
if (handler_was_called & SIG_HANDLED_NO_RESTART) {
if (handler_was_called & SIG_HANDLED_NO_RESTART)
return eintr(); // a non-SA_RESTART handler was called
}
}
// check for zombie to harvest

View file

@ -23,9 +23,8 @@
int __zipos_notat(int dirfd, const char *path) {
struct ZiposUri zipname;
if (!path)
return efault();
if (__isfdkind(dirfd, kFdZip) || __zipos_parseuri(path, &zipname) != -1) {
return einval();
}
return 0;
if (__isfdkind(dirfd, kFdZip) || __zipos_parseuri(path, &zipname) != -1)
return -1;
return 0;
}

View file

@ -6,14 +6,14 @@ COSMOPOLITAN_C_START_
#define BENCHMARK(ITERATIONS, WORK_PER_RUN, CODE) \
do { \
struct timespec start = timespec_real(); \
struct timespec start = timespec_mono(); \
for (int __i = 0; __i < ITERATIONS; ++__i) { \
asm volatile("" ::: "memory"); \
CODE; \
} \
long long work = ((WORK_PER_RUN) ? (WORK_PER_RUN) : 1) * (ITERATIONS); \
double nanos = \
(timespec_tonanos(timespec_sub(timespec_real(), start)) + work - 1) / \
(timespec_tonanos(timespec_sub(timespec_mono(), start)) + work - 1) / \
(double)work; \
if (nanos < 1000) { \
printf("%10g ns %2dx %s\n", nanos, (ITERATIONS), #CODE); \