mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-07 06:53:33 +00:00
Fix some additional Windows TTY issues
This commit is contained in:
parent
f26a280cda
commit
1694edf85c
12 changed files with 107 additions and 43 deletions
|
@ -58,7 +58,16 @@ int main(int argc, char *argv[]) {
|
|||
// posix guarantees atomic i/o if you use pipe_buf sized buffers
|
||||
// that way we don't need to worry about things like looping and
|
||||
// we can also be assured that multiple actors wont have tearing
|
||||
char buf[4];
|
||||
// 512 is the minimum permissible value for PIPE_BUF for all the
|
||||
// platforms. when stdin is a terminal there are more guarantees
|
||||
// about exactly how many bytes will be returned. in ICANON mode
|
||||
// which is the default you can count on it returning one single
|
||||
// line, including its \n (or VEOL, or VEOL2) per read. if using
|
||||
// non-canonical raw mode, then a single logical keystroke shall
|
||||
// be returned per read, so long as has VMIN characters or more,
|
||||
// and the default VMIN is 1. you can also set VMIN w/ tcsetattr
|
||||
// to 0 for a special kind of non-blocking mode.
|
||||
char buf[512];
|
||||
|
||||
// read data from standard input
|
||||
//
|
||||
|
@ -109,7 +118,7 @@ int main(int argc, char *argv[]) {
|
|||
// it's usually safe to ignore the return code of write. the
|
||||
// operating system will send SIGPIPE if there's any problem
|
||||
// which kills the process by default
|
||||
write(1, "got: ", 5);
|
||||
write(1, buf, got);
|
||||
write(1, "\n", 1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,7 +92,7 @@ int EnableRawMode(void) {
|
|||
perror("tcsetattr");
|
||||
}
|
||||
|
||||
WRITE(outfd, ENABLE_MOUSE_TRACKING);
|
||||
/* WRITE(outfd, ENABLE_MOUSE_TRACKING); */
|
||||
/* WRITE(outfd, ENABLE_SAFE_PASTE); */
|
||||
/* WRITE(outfd, PROBE_DISPLAY_SIZE); */
|
||||
return 0;
|
||||
|
|
|
@ -71,6 +71,10 @@ textwindows int sys_dup_nt(int oldfd, int newfd, int flags, int start) {
|
|||
if (g_fds.p[oldfd].kind == kFdSocket && _weaken(_dupsockfd)) {
|
||||
g_fds.p[newfd].extra =
|
||||
(intptr_t)_weaken(_dupsockfd)((struct SockFd *)g_fds.p[oldfd].extra);
|
||||
} else if (g_fds.p[oldfd].kind == kFdConsole) {
|
||||
unassert(DuplicateHandle(proc, g_fds.p[oldfd].extra, proc,
|
||||
&g_fds.p[newfd].extra, 0, false,
|
||||
kNtDuplicateSameAccess));
|
||||
} else {
|
||||
g_fds.p[newfd].extra = g_fds.p[oldfd].extra;
|
||||
}
|
||||
|
|
|
@ -115,7 +115,8 @@ static int ioctl_fionread(int fd, uint32_t *arg) {
|
|||
}
|
||||
} else if (GetConsoleMode(handle, &cm)) {
|
||||
int bytes = CountConsoleInputBytes(g_fds.p + fd);
|
||||
return MAX(0, bytes);
|
||||
*arg = MAX(0, bytes);
|
||||
return 0;
|
||||
} else {
|
||||
return eopnotsupp();
|
||||
}
|
||||
|
|
|
@ -20,13 +20,17 @@
|
|||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nt/enum/fileflagandattributes.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/thunk/msabi.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/at.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
int __mkntpathat(int dirfd, const char *path, int flags,
|
||||
char16_t file[hasatleast PATH_MAX]) {
|
||||
__msabi extern typeof(GetFileAttributes) *const __imp_GetFileAttributesW;
|
||||
|
||||
static int __mkntpathat_impl(int dirfd, const char *path, int flags,
|
||||
char16_t file[hasatleast PATH_MAX]) {
|
||||
char16_t dir[PATH_MAX];
|
||||
uint32_t dirlen, filelen;
|
||||
if (!isutf8(path, -1)) return eilseq(); // thwart overlong nul in conversion
|
||||
|
@ -49,3 +53,30 @@ int __mkntpathat(int dirfd, const char *path, int flags,
|
|||
return filelen;
|
||||
}
|
||||
}
|
||||
|
||||
int __mkntpathat(int dirfd, const char *path, int flags,
|
||||
char16_t file[hasatleast PATH_MAX]) {
|
||||
|
||||
// convert the path.
|
||||
int len;
|
||||
if ((len = __mkntpathat_impl(dirfd, path, flags, file)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// if path ends with a slash, then we need to manually do what linux
|
||||
// does and check to make sure it's a directory, and return ENOTDIR,
|
||||
// since WIN32 will reject the path with EINVAL if we don't do this.
|
||||
if (len && file[len - 1] == '\\') {
|
||||
uint32_t fattr;
|
||||
if (len > 1 && !(len == 3 && file[1] == ':')) {
|
||||
file[--len] = 0;
|
||||
}
|
||||
if ((fattr = __imp_GetFileAttributesW(file)) != -1u &&
|
||||
!(fattr & kNtFileAttributeReparsePoint) &&
|
||||
!(fattr & kNtFileAttributeDirectory)) {
|
||||
return enotdir();
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
|
|
@ -53,16 +53,6 @@ static textwindows int64_t sys_open_nt_impl(int dirfd, const char *path,
|
|||
return kNtInvalidHandleValue;
|
||||
}
|
||||
|
||||
// strip trailing slash
|
||||
size_t n = strlen16(path16);
|
||||
if (n > 1 && path16[n - 1] == '\\') {
|
||||
// path denormalization only goes so far. when a trailing / or /.
|
||||
// exists the kernel interprets that as having O_DIRECTORY intent
|
||||
// furthermore, windows will throw an error on unc paths with it!
|
||||
flags |= O_DIRECTORY;
|
||||
path16[--n] = 0;
|
||||
}
|
||||
|
||||
// implement no follow flag
|
||||
// you can't open symlinks; use readlink
|
||||
// this flag only applies to the final path component
|
||||
|
|
|
@ -106,7 +106,7 @@ struct Keystrokes {
|
|||
unsigned char pc;
|
||||
uint16_t utf16hs;
|
||||
pthread_mutex_t lock;
|
||||
struct Keystroke pool[8];
|
||||
struct Keystroke pool[512];
|
||||
};
|
||||
|
||||
static struct Keystrokes __keystroke;
|
||||
|
@ -203,7 +203,7 @@ static textwindows int ProcessKeyEvent(const struct NtInputRecord *r, char *p) {
|
|||
}
|
||||
|
||||
// make it possible to distinguish ctrl-h (^H) from backspace (^?)
|
||||
if (c == kNtVkBack) {
|
||||
if (c == kNtVkBack && !(cks & (kNtLeftCtrlPressed | kNtRightCtrlPressed))) {
|
||||
c = 0177;
|
||||
}
|
||||
|
||||
|
@ -387,6 +387,25 @@ static textwindows void EchoTty(struct Fd *f, const char *p, size_t n) {
|
|||
}
|
||||
}
|
||||
|
||||
static textwindows bool EraseKeystroke(struct Fd *f) {
|
||||
struct Dll *e;
|
||||
if ((e = dll_last(__keystroke.line))) {
|
||||
struct Keystroke *k = KEYSTROKE_CONTAINER(e);
|
||||
dll_remove(&__keystroke.line, e);
|
||||
dll_make_first(&__keystroke.free, e);
|
||||
for (int i = k->buflen; i--;) {
|
||||
if ((k->buf[i] & 0300) == 0200) continue;
|
||||
WriteTty(f, "\b \b", 3);
|
||||
if (!(__ttyconf.magic & kTtyEchoRaw) && IsCtl(k->buf[i])) {
|
||||
WriteTty(f, "\b \b", 3);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static textwindows void IngestConsoleInputRecord(struct Fd *f,
|
||||
struct NtInputRecord *r) {
|
||||
|
||||
|
@ -401,18 +420,15 @@ static textwindows void IngestConsoleInputRecord(struct Fd *f,
|
|||
if (len == 1 && buf[0] && //
|
||||
(buf[0] & 255) == __ttyconf.verase && //
|
||||
!(__ttyconf.magic & kTtyUncanon)) {
|
||||
struct Dll *e;
|
||||
if ((e = dll_last(__keystroke.line))) {
|
||||
struct Keystroke *k = KEYSTROKE_CONTAINER(e);
|
||||
dll_remove(&__keystroke.line, e);
|
||||
dll_make_first(&__keystroke.free, e);
|
||||
for (int i = k->buflen; i--;) {
|
||||
if ((k->buf[i] & 0300) == 0200) continue;
|
||||
WriteTty(f, "\b \b", 3);
|
||||
if (!(__ttyconf.magic & kTtyEchoRaw) && IsCtl(k->buf[i])) {
|
||||
WriteTty(f, "\b \b", 3);
|
||||
}
|
||||
}
|
||||
EraseKeystroke(f);
|
||||
return;
|
||||
}
|
||||
|
||||
// handle kill in canonical mode
|
||||
if (len == 1 && buf[0] && //
|
||||
(buf[0] & 255) == __ttyconf.vkill && //
|
||||
!(__ttyconf.magic & kTtyUncanon)) {
|
||||
while (EraseKeystroke(f)) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -455,12 +471,21 @@ static textwindows void IngestConsoleInput(struct Fd *f) {
|
|||
struct NtInputRecord records[16];
|
||||
if (!__keystroke.end_of_file) {
|
||||
do {
|
||||
if (ReadConsoleInput(f->handle, records, ARRAYLEN(records), &n)) {
|
||||
for (i = 0; i < n && !__keystroke.end_of_file; ++i) {
|
||||
IngestConsoleInputRecord(f, records + i);
|
||||
if (GetNumberOfConsoleInputEvents(f->handle, &n)) {
|
||||
if (n) {
|
||||
n = MIN(ARRAYLEN(records), n);
|
||||
if (ReadConsoleInput(f->handle, records, n, &n)) {
|
||||
for (i = 0; i < n && !__keystroke.end_of_file; ++i) {
|
||||
IngestConsoleInputRecord(f, records + i);
|
||||
}
|
||||
} else {
|
||||
STRACE("ReadConsoleInput failed w/ %d", GetLastError());
|
||||
__keystroke.end_of_file = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
STRACE("ReadConsoleInput failed w/ %d", GetLastError());
|
||||
STRACE("GetNumberOfConsoleInputRecords failed w/ %d", GetLastError());
|
||||
__keystroke.end_of_file = true;
|
||||
break;
|
||||
}
|
||||
|
@ -513,7 +538,7 @@ static textwindows bool DigestConsoleInput(char *data, size_t size, int *rc) {
|
|||
// copy keystroke(s) into user buffer
|
||||
int toto = 0;
|
||||
struct Dll *e;
|
||||
while ((e = dll_first(__keystroke.list))) {
|
||||
while (size && (e = dll_first(__keystroke.list))) {
|
||||
struct Keystroke *k = KEYSTROKE_CONTAINER(e);
|
||||
uint32_t got = MIN(size, k->buflen);
|
||||
uint32_t remain = k->buflen - got;
|
||||
|
@ -567,7 +592,12 @@ static textwindows ssize_t ReadFromWindowsConsole(struct Fd *f, void *data,
|
|||
}
|
||||
}
|
||||
if (_check_interrupts(kSigOpRestartable)) return -1;
|
||||
if (__pause_thread(ms)) return -1;
|
||||
if (__pause_thread(ms)) {
|
||||
if (errno == EAGAIN) {
|
||||
errno = EINTR; // TODO(jart): Why do we need it?
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -34,7 +34,6 @@
|
|||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/describebacktrace.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/popcnt.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
|
|
|
@ -74,8 +74,7 @@ textwindows int tcsetattr_nt(int fd, int opt, const struct termios *tio) {
|
|||
inmode |= kNtEnableWindowInput;
|
||||
__ttyconf.magic = 0;
|
||||
if (tio->c_lflag & ICANON) {
|
||||
inmode |=
|
||||
kNtEnableLineInput | kNtEnableProcessedInput | kNtEnableQuickEditMode;
|
||||
inmode |= kNtEnableLineInput | kNtEnableQuickEditMode;
|
||||
} else {
|
||||
inmode &= ~kNtEnableQuickEditMode;
|
||||
__ttyconf.magic |= kTtyUncanon;
|
||||
|
@ -101,7 +100,8 @@ textwindows int tcsetattr_nt(int fd, int opt, const struct termios *tio) {
|
|||
memcpy(__ttyconf.c_cc, tio->c_cc, NCCS);
|
||||
if ((tio->c_lflag & ISIG) && __ttyconf.vintr == CTRL('C')) {
|
||||
// allows ctrl-c to be delivered asynchronously via win32
|
||||
inmode |= kNtEnableProcessedInput;
|
||||
// TODO(jart): Fix up sig.c more.
|
||||
// inmode |= kNtEnableProcessedInput;
|
||||
}
|
||||
ok = SetConsoleMode(hInput, inmode);
|
||||
(void)ok;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#include "libc/intrin/likely.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
|
||||
#define _NTTRACE 0 /* not configurable w/ flag yet */
|
||||
#define _NTTRACE 1 /* not configurable w/ flag yet */
|
||||
#define _POLLTRACE 0 /* not configurable w/ flag yet */
|
||||
#define _DATATRACE 1 /* not configurable w/ flag yet */
|
||||
#define _LOCKTRACE 0 /* not configurable w/ flag yet */
|
||||
|
|
|
@ -1045,7 +1045,7 @@ syscon mount MNT_SNAPSHOT 0 0 0x40000000 0x40000000 0x01000000 0 0
|
|||
# limits
|
||||
#
|
||||
# group name GNU/Systemd GNU/Systemd (Aarch64) XNU's Not UNIX! MacOS (Arm64) FreeBSD OpenBSD NetBSD The New Technology Commentary
|
||||
syscon limits PIPE_BUF 4096 4096 512 512 512 512 512 4096 # bsd consensus
|
||||
syscon limits PIPE_BUF 4096 4096 512 512 512 512 512 512 # bsd consensus
|
||||
syscon limits NGROUPS_MAX 65536 65536 16 16 1023 16 16 0 #
|
||||
syscon limits LINK_MAX 127 127 32767 32767 32767 32767 32767 64 # freebsd/windows are educated guesses
|
||||
syscon limits MAX_CANON 255 255 1024 1024 255 255 255 255 # windows is guessed
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
#include "libc/sysv/consts/syscon.internal.h"
|
||||
.syscon limits,PIPE_BUF,4096,4096,512,512,512,512,512,4096
|
||||
.syscon limits,PIPE_BUF,4096,4096,512,512,512,512,512,512
|
||||
|
|
Loading…
Reference in a new issue