diff --git a/examples/ctrlc.c b/examples/ctrlc.c index c88e9835f..ee1ca37fa 100644 --- a/examples/ctrlc.c +++ b/examples/ctrlc.c @@ -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); } } diff --git a/examples/ttyinfo.c b/examples/ttyinfo.c index dda2c7be0..40e08db78 100644 --- a/examples/ttyinfo.c +++ b/examples/ttyinfo.c @@ -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; diff --git a/libc/calls/dup-nt.c b/libc/calls/dup-nt.c index e81c2010b..cd1c515ff 100644 --- a/libc/calls/dup-nt.c +++ b/libc/calls/dup-nt.c @@ -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; } diff --git a/libc/calls/ioctl.c b/libc/calls/ioctl.c index 0713f9047..40aa02228 100644 --- a/libc/calls/ioctl.c +++ b/libc/calls/ioctl.c @@ -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(); } diff --git a/libc/calls/mkntpathat.c b/libc/calls/mkntpathat.c index 098320181..2d2ee7cd3 100644 --- a/libc/calls/mkntpathat.c +++ b/libc/calls/mkntpathat.c @@ -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; +} diff --git a/libc/calls/open-nt.c b/libc/calls/open-nt.c index 612a04f10..5fb6a31ae 100644 --- a/libc/calls/open-nt.c +++ b/libc/calls/open-nt.c @@ -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 diff --git a/libc/calls/read-nt.c b/libc/calls/read-nt.c index 69289896f..96ef64872 100644 --- a/libc/calls/read-nt.c +++ b/libc/calls/read-nt.c @@ -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; } diff --git a/libc/calls/sig.c b/libc/calls/sig.c index c4daee116..6cacfc783 100644 --- a/libc/calls/sig.c +++ b/libc/calls/sig.c @@ -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" diff --git a/libc/calls/tcsetattr-nt.c b/libc/calls/tcsetattr-nt.c index 60597ff9f..60c2acb89 100644 --- a/libc/calls/tcsetattr-nt.c +++ b/libc/calls/tcsetattr-nt.c @@ -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; diff --git a/libc/intrin/strace.internal.h b/libc/intrin/strace.internal.h index 92cc9101e..17ba1a1a5 100644 --- a/libc/intrin/strace.internal.h +++ b/libc/intrin/strace.internal.h @@ -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 */ diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index d32003295..539a205ec 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -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 diff --git a/libc/sysv/consts/PIPE_BUF.S b/libc/sysv/consts/PIPE_BUF.S index e367d95eb..35597d9bf 100644 --- a/libc/sysv/consts/PIPE_BUF.S +++ b/libc/sysv/consts/PIPE_BUF.S @@ -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